From cf614eee15fa588726f6584f1840279f2f2ac1dd Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Sun, 14 Aug 2022 23:57:40 +0800 Subject: [PATCH] Moved YukiHookBridge some apps and host function into AppParasitics --- .../hook/factory/YukiHookFactory.kt | 4 +- .../yukihookapi/hook/log/LoggerFactory.kt | 3 +- .../yukihookapi/hook/param/PackageParam.kt | 38 +-- .../hook/xposed/bridge/YukiHookBridge.kt | 270 ++--------------- .../hook/xposed/parasitic/AppParasitics.kt | 274 ++++++++++++++++++ 5 files changed, 320 insertions(+), 269 deletions(-) create mode 100644 yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt index 4fbf1579..822a3da9 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt @@ -39,8 +39,8 @@ import android.widget.ImageView import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit import java.io.BufferedReader @@ -124,7 +124,7 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources() * * - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息 */ -fun Resources.injectModuleAppResources() = YukiHookBridge.injectModuleAppResources(hostResources = this) +fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this) /** * 仅判断模块是否在太极、无极中激活 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt index 9c8fd236..20d6cf01 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt @@ -32,6 +32,7 @@ package com.highcapable.yukihookapi.hook.log import android.util.Log import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import de.robv.android.xposed.XposedBridge /** @@ -91,7 +92,7 @@ private fun baseLogger(format: String, type: LoggerType, tag: String, msg: Strin /** 打印到 [XposedBridge.log] */ fun loggerInXposed() = runCatching { YukiHookBridge.hostProcessName.also { - val appUserId = YukiHookBridge.findUserId(it) + val appUserId = AppParasitics.findUserId(it) XposedBridge.log("[$tag][$format]${if (isShowProcessName) (if (appUserId != 0) "[$it][$appUserId]" else "[$it]") else ""}--> $msg") e?.also { e -> XposedBridge.log(e) } } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt index 286a3055..cfbc4b22 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt @@ -48,11 +48,11 @@ import com.highcapable.yukihookapi.hook.factory.hasClass import com.highcapable.yukihookapi.hook.param.type.HookEntryType import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper import com.highcapable.yukihookapi.hook.utils.value -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs /** @@ -82,7 +82,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同 * @return [Int] */ - val appUserId get() = YukiHookBridge.findUserId(packageName) + val appUserId get() = AppParasitics.findUserId(packageName) /** * 获取当前 Hook APP 的 [Application] 实例 @@ -91,7 +91,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @return [Application] * @throws IllegalStateException 如果 [Application] 是空的 */ - val appContext get() = YukiHookBridge.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext") + val appContext get() = AppParasitics.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext") /** * 获取当前 Hook APP 的 Resources @@ -107,7 +107,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @return [Context] ContextImpl 实例对象 * @throws IllegalStateException 如果获取不到系统框架的 [Context] */ - val systemContext get() = YukiHookBridge.systemContext + val systemContext get() = AppParasitics.systemContext /** * 获取当前 Hook APP 的进程名称 @@ -143,7 +143,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * - ❗作为 Hook API 装载时无法使用 - 会获取到空字符串 * @return [String] */ - val moduleAppFilePath get() = YukiHookBridge.moduleAppFilePath + val moduleAppFilePath get() = AppParasitics.moduleAppFilePath /** * 获取当前 Xposed 模块自身 [Resources] @@ -153,8 +153,8 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @throws IllegalStateException 如果当前 Hook Framework 不支持此功能 */ val moduleAppResources - get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) YukiHookBridge.moduleAppResources - else YukiHookBridge.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources") + get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) AppParasitics.moduleAppResources + else AppParasitics.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources") /** * 获得当前使用的存取数据对象缓存实例 @@ -202,7 +202,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: fun resources() = HookResources(wrapper?.appResources) /** 刷新当前 Xposed 模块自身 [Resources] */ - fun refreshModuleAppResources() = YukiHookBridge.refreshModuleAppResources() + fun refreshModuleAppResources() = AppParasitics.refreshModuleAppResources() /** * 监听当前 Hook APP 生命周期装载事件 @@ -244,13 +244,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * 装载并 Hook 系统框架 * @param initiate 方法体 */ - inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, initiate) + inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, initiate) /** * 装载并 Hook 系统框架 * @param hooker Hook 子类 */ - fun loadSystem(hooker: YukiBaseHooker) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, hooker) + fun loadSystem(hooker: YukiBaseHooker) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, hooker) /** * 装载 APP Zygote 事件 @@ -356,7 +356,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * - ❗这是一个实验性功能 - 一般情况下不会用到此方法 - 不保证不会发生错误 * @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve) */ - fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = YukiHookBridge.hookClassLoader(loader = this, result) + fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = AppParasitics.hookClassLoader(loader = this, result) /** * Hook 方法、构造方法 @@ -439,7 +439,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param result 回调 - ([Context] baseContext,[Boolean] 是否已执行 super) */ fun attachBaseContext(result: (baseContext: Context, hasCalledSuper: Boolean) -> Unit) { - YukiHookBridge.AppLifecycleCallback.attachBaseContextCallback = result + AppParasitics.AppLifecycleCallback.attachBaseContextCallback = result } /** @@ -447,7 +447,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param initiate 方法体 */ fun onCreate(initiate: Application.() -> Unit) { - YukiHookBridge.AppLifecycleCallback.onCreateCallback = initiate + AppParasitics.AppLifecycleCallback.onCreateCallback = initiate } /** @@ -455,7 +455,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param initiate 方法体 */ fun onTerminate(initiate: Application.() -> Unit) { - YukiHookBridge.AppLifecycleCallback.onTerminateCallback = initiate + AppParasitics.AppLifecycleCallback.onTerminateCallback = initiate } /** @@ -463,7 +463,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param initiate 方法体 */ fun onLowMemory(initiate: Application.() -> Unit) { - YukiHookBridge.AppLifecycleCallback.onLowMemoryCallback = initiate + AppParasitics.AppLifecycleCallback.onLowMemoryCallback = initiate } /** @@ -471,7 +471,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param result 回调 - ([Application] 当前实例,[Int] 类型) */ fun onTrimMemory(result: (self: Application, level: Int) -> Unit) { - YukiHookBridge.AppLifecycleCallback.onTrimMemoryCallback = result + AppParasitics.AppLifecycleCallback.onTrimMemoryCallback = result } /** @@ -479,7 +479,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param result 回调 - ([Application] 当前实例,[Configuration] 配置实例) */ fun onConfigurationChanged(result: (self: Application, config: Configuration) -> Unit) { - YukiHookBridge.AppLifecycleCallback.onConfigurationChangedCallback = result + AppParasitics.AppLifecycleCallback.onConfigurationChangedCallback = result } /** @@ -488,13 +488,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @param result 回调 - ([Context] 当前上下文,[Intent] 当前 Intent) */ fun registerReceiver(vararg action: String, result: (context: Context, intent: Intent) -> Unit) { - if (action.isNotEmpty()) YukiHookBridge.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result) + if (action.isNotEmpty()) AppParasitics.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result) } /** 设置创建生命周期监听回调 */ @PublishedApi internal fun build() { - YukiHookBridge.AppLifecycleCallback.isCallbackSetUp = true + AppParasitics.AppLifecycleCallback.isCallbackSetUp = true } } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt index 325d0783..338181ae 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt @@ -29,38 +29,24 @@ package com.highcapable.yukihookapi.hook.xposed.bridge -import android.app.Application -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.content.res.Configuration -import android.content.res.Resources import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.YukiGenerateApi -import com.highcapable.yukihookapi.hook.factory.* -import com.highcapable.yukihookapi.hook.log.yLoggerE -import com.highcapable.yukihookapi.hook.log.yLoggerW +import com.highcapable.yukihookapi.hook.factory.classOf +import com.highcapable.yukihookapi.hook.factory.field +import com.highcapable.yukihookapi.hook.factory.hasClass +import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.type.HookEntryType import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper -import com.highcapable.yukihookapi.hook.type.android.* -import com.highcapable.yukihookapi.hook.type.java.BooleanType -import com.highcapable.yukihookapi.hook.type.java.IntType -import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader -import com.highcapable.yukihookapi.hook.type.java.StringType -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement import com.highcapable.yukihookapi.hook.xposed.bridge.inject.YukiHookBridge_Injector import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus -import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import dalvik.system.PathClassLoader import de.robv.android.xposed.IXposedHookInitPackageResources import de.robv.android.xposed.IXposedHookLoadPackage @@ -79,16 +65,9 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage @YukiGenerateApi object YukiHookBridge { - /** Android 系统框架名称 */ - @PublishedApi - internal const val SYSTEM_FRAMEWORK_NAME = "android" - /** Xposed 是否装载完成 */ private var isXposedInitialized = false - /** [YukiHookDataChannel] 是否已经注册 */ - private var isDataChannelRegister = false - /** 当前 Hook 进程是否正处于 [IXposedHookZygoteInit.initZygote] */ private var isInitializingZygote = false @@ -98,49 +77,12 @@ object YukiHookBridge { /** 当前 [PackageParamWrapper] 实例数组 */ private val packageParamWrappers = HashMap() - /** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */ - private val injectedHostResourcesHashCodes = HashSet() - /** 当前 [PackageParam] 方法体回调 */ internal var packageParamCallback: (PackageParam.() -> Unit)? = null /** 当前 Hook Framework 是否支持 Resources Hook */ internal var isSupportResourcesHook = false - /** - * 当前 Hook APP (宿主) 的全局生命周期 [Application] - * - * 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效 - */ - internal var hostApplication: Application? = null - - /** 当前 Xposed 模块自身 APK 路径 */ - internal var moduleAppFilePath = "" - - /** 当前 Xposed 模块自身 [Resources] */ - internal var moduleAppResources: YukiModuleResources? = null - - /** - * 当前环境中使用的 [ClassLoader] - * - * 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader] - * @return [ClassLoader] - * @throws IllegalStateException 如果 [ClassLoader] 为空 - */ - internal val baseClassLoader get() = classOf().classLoader ?: error("Operating system not supported") - - /** - * 获取当前 Xposed 模块自身动态 [Resources] - * @return [YukiModuleResources] or null - */ - internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull() - - /** - * 自动生成的 Xposed 模块构建版本号 - * @return [String] - */ - internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion() - /** * 当前宿主正在进行的 Hook 进程标识名称 * @return [String] @@ -148,14 +90,18 @@ object YukiHookBridge { internal val hostProcessName get() = if (isInitializingZygote) "android-zygote" else YukiHookAppHelper.currentPackageName() ?: "unknown" /** - * 获取当前系统框架的 [Context] - * @return [Context] ContextImpl 实例对象 - * @throws IllegalStateException 如果获取不到系统框架的 [Context] + * 自动生成的 Xposed 模块构建版本号 + * @return [String] */ - internal val systemContext - get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let { - ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke() - } ?: error("Failed to got SystemContext") + internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion() + + /** + * 预设的 Xposed 模块包名 + * + * - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常 + */ + @YukiGenerateApi + var modulePackageName = "" /** * 模块是否装载了 Xposed 回调方法 @@ -167,14 +113,6 @@ object YukiHookBridge { val isXposedCallbackSetUp get() = isXposedInitialized.not() && packageParamCallback != null - /** - * 预设的 Xposed 模块包名 - * - * - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常 - */ - @YukiGenerateApi - var modulePackageName = "" - /** * 获取当前 Hook 框架的名称 * @@ -201,19 +139,6 @@ object YukiHookBridge { */ internal val hasXposedBridge get() = executorVersion >= 0 - /** - * 获取指定 [packageName] 的用户 ID - * - * 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同 - * @param packageName 当前包名 - * @return [Int] - */ - internal fun findUserId(packageName: String) = - UserHandleClass.method { - name = "getUserId" - param(IntType) - }.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) - /** * 自动忽略 MIUI 系统可能出现的日志收集注入实例 * @param packageName 当前包名 @@ -259,12 +184,12 @@ object YukiHookBridge { if (type == HookEntryType.ZYGOTE || appClassLoader != null) PackageParamWrapper( type = type, - packageName = packageName ?: SYSTEM_FRAMEWORK_NAME, - processName = processName ?: SYSTEM_FRAMEWORK_NAME, + packageName = packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME, + processName = processName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME, appClassLoader = appClassLoader ?: XposedBridge.BOOTCLASSLOADER, appInfo = appInfo, appResources = appResources - ).also { packageParamWrappers[packageName ?: SYSTEM_FRAMEWORK_NAME] = it } + ).also { packageParamWrappers[packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME] = it } else null else packageParamWrappers[packageName]?.also { it.type = type @@ -276,125 +201,6 @@ object YukiHookBridge { } } - /** - * 注入当前 Hook APP (宿主) 全局生命周期 - * @param packageName 包名 - */ - private fun registerToAppLifecycle(packageName: String) { - /** Hook [Application] 装载方法 */ - runCatching { - if (AppLifecycleCallback.isCallbackSetUp) { - YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() { - override fun beforeHookedMember(wrapper: HookParamWrapper) { - runCatching { - (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) } - }.onFailure { wrapper.throwable = it } - } - - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) } - }.onFailure { wrapper.throwable = it } - } - }) - YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) } - }.onFailure { wrapper.throwable = it } - } - }) - YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) } - }.onFailure { wrapper.throwable = it } - } - }) - YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - val self = wrapper.instance as? Application? ?: return - val type = wrapper.args?.get(0) as? Int? ?: return - AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) - }.onFailure { wrapper.throwable = it } - } - }) - YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - val self = wrapper.instance as? Application? ?: return - val config = wrapper.args?.get(0) as? Configuration? ?: return - AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config) - }.onFailure { wrapper.throwable = it } - } - }) - } - if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) - YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - runCatching { - (wrapper.args?.get(0) as? Application?)?.also { - hostApplication = it - AppLifecycleCallback.onCreateCallback?.invoke(it) - AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) -> - if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - if (context == null || intent == null) return - if (e.first.any { e -> e == intent.action }) e.second(context, intent) - } - }, IntentFilter().apply { e.first.forEach { e -> addAction(e) } }) - } - if (isDataChannelRegister) return - isDataChannelRegister = true - runCatching { YukiHookDataChannel.instance().register(it, packageName) } - } - }.onFailure { wrapper.throwable = it } - } - }) - } - } - - /** - * 向 Hook APP (宿主) 注入当前 Xposed 模块的资源 - * @param hostResources 需要注入的宿主 [Resources] - */ - internal fun injectModuleAppResources(hostResources: Resources) { - if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return - if (hasXposedBridge) runCatching { - hostResources.assets.current { - method { - name = "addAssetPath" - param(StringType) - }.call(moduleAppFilePath) - } - injectedHostResourcesHashCodes.add(hostResources.hashCode()) - }.onFailure { - yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it) - } else yLoggerW(msg = "You can only inject module resources in Xposed Environment") - } - - /** 刷新当前 Xposed 模块自身 [Resources] */ - internal fun refreshModuleAppResources() { - dynamicModuleAppResources?.let { moduleAppResources = it } - } - - /** - * 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法 - * @param loader 当前 [ClassLoader] - * @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve) - */ - internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) { - runCatching { - YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name) - (wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) } - } - }) - }.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") } - } - /** * Hook 模块自身激活状态和 Resources Hook 支持状态 * @@ -441,8 +247,8 @@ object YukiHookBridge { */ @YukiGenerateApi fun callXposedZygoteLoaded(sparam: IXposedHookZygoteInit.StartupParam) { - moduleAppFilePath = sparam.modulePath - refreshModuleAppResources() + AppParasitics.moduleAppFilePath = sparam.modulePath + AppParasitics.refreshModuleAppResources() } /** @@ -468,7 +274,7 @@ object YukiHookBridge { resparam: XC_InitPackageResources.InitPackageResourcesParam? = null ) { if (isMiuiCatcherPatch(packageName = lpparam?.packageName ?: resparam?.packageName).not()) when { - isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, SYSTEM_FRAMEWORK_NAME, SYSTEM_FRAMEWORK_NAME) + isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, AppParasitics.SYSTEM_FRAMEWORK_NAME, AppParasitics.SYSTEM_FRAMEWORK_NAME) lpparam != null -> if (isPackageLoaded(lpparam.packageName, HookEntryType.PACKAGE).not()) assignWrapper(HookEntryType.PACKAGE, lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo) @@ -480,38 +286,8 @@ object YukiHookBridge { else -> null }?.also { YukiHookAPI.onXposedLoaded(it) - if (it.type == HookEntryType.PACKAGE) registerToAppLifecycle(it.packageName) + if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName) if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true } } - - /** - * 当前 Hook APP (宿主) 的生命周期回调处理类 - */ - internal object AppLifecycleCallback { - - /** 是否已设置回调 */ - internal var isCallbackSetUp = false - - /** [Application.attachBaseContext] 回调 */ - internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null - - /** [Application.onCreate] 回调 */ - internal var onCreateCallback: (Application.() -> Unit)? = null - - /** [Application.onTerminate] 回调 */ - internal var onTerminateCallback: (Application.() -> Unit)? = null - - /** [Application.onLowMemory] 回调 */ - internal var onLowMemoryCallback: (Application.() -> Unit)? = null - - /** [Application.onTrimMemory] 回调 */ - internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null - - /** [Application.onConfigurationChanged] 回调 */ - internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null - - /** 系统广播监听回调 */ - internal val onReceiversCallback = HashMap, (Context, Intent) -> Unit>>() - } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt new file mode 100644 index 00000000..14c07097 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt @@ -0,0 +1,274 @@ +/* + * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. + * Copyright (C) 2019-2022 HighCapable + * https://github.com/fankes/YukiHookAPI + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is Created by fankes on 2022/8/14. + */ +package com.highcapable.yukihookapi.hook.xposed.parasitic + +import android.app.Activity +import android.app.Application +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.content.res.Resources +import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.factory.classOf +import com.highcapable.yukihookapi.hook.factory.current +import com.highcapable.yukihookapi.hook.factory.method +import com.highcapable.yukihookapi.hook.log.yLoggerE +import com.highcapable.yukihookapi.hook.log.yLoggerW +import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper +import com.highcapable.yukihookapi.hook.type.android.* +import com.highcapable.yukihookapi.hook.type.java.BooleanType +import com.highcapable.yukihookapi.hook.type.java.IntType +import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader +import com.highcapable.yukihookapi.hook.type.java.StringType +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook +import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel + +/** + * 这是一个管理 APP 寄生功能的控制类 + * + * 通过这些功能即可轻松实现对 (Xposed) 宿主环境的 [Resources] 注入以及 [Activity] 代理 + */ +@PublishedApi +internal object AppParasitics { + + /** Android 系统框架名称 */ + @PublishedApi + internal const val SYSTEM_FRAMEWORK_NAME = "android" + + /** [YukiHookDataChannel] 是否已经注册 */ + private var isDataChannelRegister = false + + /** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */ + private val injectedHostResourcesHashCodes = HashSet() + + /** + * 当前 Hook APP (宿主) 的全局生命周期 [Application] + * + * 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效 + */ + internal var hostApplication: Application? = null + + /** 当前 Xposed 模块自身 APK 路径 */ + internal var moduleAppFilePath = "" + + /** 当前 Xposed 模块自身 [Resources] */ + internal var moduleAppResources: YukiModuleResources? = null + + /** + * 当前环境中使用的 [ClassLoader] + * + * 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader] + * @return [ClassLoader] + * @throws IllegalStateException 如果 [ClassLoader] 为空 + */ + internal val baseClassLoader get() = classOf().classLoader ?: error("Operating system not supported") + + /** + * 获取当前 Xposed 模块自身动态 [Resources] + * @return [YukiModuleResources] or null + */ + internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull() + + /** + * 获取当前系统框架的 [Context] + * @return [Context] ContextImpl 实例对象 + * @throws IllegalStateException 如果获取不到系统框架的 [Context] + */ + internal val systemContext + get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let { + ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke() + } ?: error("Failed to got SystemContext") + + /** + * 获取指定 [packageName] 的用户 ID + * + * 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同 + * @param packageName 当前包名 + * @return [Int] + */ + internal fun findUserId(packageName: String) = + UserHandleClass.method { + name = "getUserId" + param(IntType) + }.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) + + /** + * 注入当前 Hook APP (宿主) 全局生命周期 + * @param packageName 包名 + */ + internal fun registerToAppLifecycle(packageName: String) { + /** Hook [Application] 装载方法 */ + runCatching { + if (AppLifecycleCallback.isCallbackSetUp) { + YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() { + override fun beforeHookedMember(wrapper: HookParamWrapper) { + runCatching { + (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) } + }.onFailure { wrapper.throwable = it } + } + + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) } + }.onFailure { wrapper.throwable = it } + } + }) + YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) } + }.onFailure { wrapper.throwable = it } + } + }) + YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) } + }.onFailure { wrapper.throwable = it } + } + }) + YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + val self = wrapper.instance as? Application? ?: return + val type = wrapper.args?.get(0) as? Int? ?: return + AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) + }.onFailure { wrapper.throwable = it } + } + }) + YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + val self = wrapper.instance as? Application? ?: return + val config = wrapper.args?.get(0) as? Configuration? ?: return + AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config) + }.onFailure { wrapper.throwable = it } + } + }) + } + if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) + YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + runCatching { + (wrapper.args?.get(0) as? Application?)?.also { + hostApplication = it + AppLifecycleCallback.onCreateCallback?.invoke(it) + AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) -> + if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (context == null || intent == null) return + if (e.first.any { e -> e == intent.action }) e.second(context, intent) + } + }, IntentFilter().apply { e.first.forEach { e -> addAction(e) } }) + } + if (isDataChannelRegister) return + isDataChannelRegister = true + runCatching { YukiHookDataChannel.instance().register(it, packageName) } + } + }.onFailure { wrapper.throwable = it } + } + }) + } + } + + /** + * 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法 + * @param loader 当前 [ClassLoader] + * @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve) + */ + internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) { + runCatching { + YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name) + (wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) } + } + }) + }.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") } + } + + /** + * 向 Hook APP (宿主) 注入当前 Xposed 模块的资源 + * @param hostResources 需要注入的宿主 [Resources] + */ + internal fun injectModuleAppResources(hostResources: Resources) { + if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return + if (YukiHookBridge.hasXposedBridge) runCatching { + hostResources.assets.current { + method { + name = "addAssetPath" + param(StringType) + }.call(moduleAppFilePath) + } + injectedHostResourcesHashCodes.add(hostResources.hashCode()) + }.onFailure { + yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it) + } else yLoggerW(msg = "You can only inject module resources in Xposed Environment") + } + + /** 刷新当前 Xposed 模块自身 [Resources] */ + internal fun refreshModuleAppResources() { + dynamicModuleAppResources?.let { moduleAppResources = it } + } + + /** + * 当前 Hook APP (宿主) 的生命周期回调处理类 + */ + internal object AppLifecycleCallback { + + /** 是否已设置回调 */ + internal var isCallbackSetUp = false + + /** [Application.attachBaseContext] 回调 */ + internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null + + /** [Application.onCreate] 回调 */ + internal var onCreateCallback: (Application.() -> Unit)? = null + + /** [Application.onTerminate] 回调 */ + internal var onTerminateCallback: (Application.() -> Unit)? = null + + /** [Application.onLowMemory] 回调 */ + internal var onLowMemoryCallback: (Application.() -> Unit)? = null + + /** [Application.onTrimMemory] 回调 */ + internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null + + /** [Application.onConfigurationChanged] 回调 */ + internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null + + /** 系统广播监听回调 */ + internal val onReceiversCallback = HashMap, (Context, Intent) -> Unit>>() + } +} \ No newline at end of file