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 31d09da3..b9db76d0 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 @@ -40,7 +40,10 @@ 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.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.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.param.PackageParam @@ -144,10 +147,9 @@ object YukiHookBridge { * @throws IllegalStateException 如果获取不到系统框架的 [Context] */ internal val systemContext - get() = runCatching { - YukiHookHelper.findMethod(ActivityThreadClass, name = "getSystemContext") - .invoke(YukiHookHelper.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context? - }.getOrNull() ?: error("Failed to got SystemContext") + get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let { + ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke() + } ?: error("Failed to got SystemContext") /** * 模块是否装载了 Xposed 回调方法 @@ -175,7 +177,7 @@ object YukiHookBridge { */ internal val executorName get() = runCatching { - (YukiHookHelper.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?) + classOf().field { name = "TAG" }.ignored().get().string().takeIf { it.isNotBlank() } ?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown" }.getOrNull() ?: "invalid" @@ -200,10 +202,11 @@ object YukiHookBridge { * @param packageName 当前包名 * @return [Int] */ - internal fun findUserId(packageName: String) = runCatching { - YukiHookHelper.findMethod(UserHandleClass, name = "getUserId", IntType) - .invoke(null, systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) as? Int ?: 0 - }.getOrNull() ?: 0 + internal fun findUserId(packageName: String) = + UserHandleClass.method { + name = "getUserId" + param(IntType) + }.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) /** * 自动忽略 MIUI 系统可能出现的日志收集注入实例 @@ -275,7 +278,7 @@ object YukiHookBridge { /** Hook [Application] 装载方法 */ runCatching { if (AppLifecycleCallback.isCallbackSetUp) { - YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "attach", ContextClass), object : YukiMemberHook() { + YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() { override fun beforeHookedMember(wrapper: HookParamWrapper) { (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) } } @@ -284,56 +287,51 @@ object YukiHookBridge { (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) } } }) - YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onTerminate"), object : YukiMemberHook() { + YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() { override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) } } }) - YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onLowMemory"), object : YukiMemberHook() { + YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() { override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) } } }) - YukiHookHelper.hookMember( - YukiHookHelper.findMethod(ApplicationClass, name = "onTrimMemory", IntType), - object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - val self = wrapper.instance as? Application? ?: return - val type = wrapper.args?.get(0) as? Int? ?: return - AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) - } - }) - YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onConfigurationChanged", ConfigurationClass), - object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - val self = wrapper.instance as? Application? ?: return - val config = wrapper.args?.get(0) as? Configuration? ?: return - AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config) - } - }) + YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + val self = wrapper.instance as? Application? ?: return + val type = wrapper.args?.get(0) as? Int? ?: return + AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) + } + }) + YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + val self = wrapper.instance as? Application? ?: return + val config = wrapper.args?.get(0) as? Configuration? ?: return + AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config) + } + }) } if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) - YukiHookHelper.hookMember( - YukiHookHelper.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass), - object : YukiMemberHook() { - override fun afterHookedMember(wrapper: HookParamWrapper) { - (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 { a -> a == intent.action }) e.second(context, intent) - } - }, IntentFilter().apply { e.first.forEach { a -> addAction(a) } }) - } - if (isDataChannelRegister) return - isDataChannelRegister = true - runCatching { YukiHookDataChannel.instance().register(it, packageName) } + YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() { + override fun afterHookedMember(wrapper: HookParamWrapper) { + (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) } } - }) + } + }) } } @@ -343,12 +341,15 @@ object YukiHookBridge { */ internal fun injectModuleAppResources(context: Context) { if (injectedHostContextHashCodes.contains(context.hashCode())) return + injectedHostContextHashCodes.add(context.hashCode()) if (hasXposedBridge) - runCatching { - YukiHookHelper.findMethod(AssetManagerClass, name = "addAssetPath", StringType) - .invoke(context.resources.assets, moduleAppFilePath) - injectedHostContextHashCodes.add(context.hashCode()) - }.onFailure { yLoggerE(msg = "Failed to inject module resources in context [$context]", e = it) } + AssetManagerClass.method { + name = "addAssetPath" + param(StringType) + }.ignored().onNoSuchMethod { + runCatching { injectedHostContextHashCodes.remove(context.hashCode()) } + yLoggerE(msg = "Failed to inject module resources in context [$context]", e = it) + }.get(context.resources.assets).call(moduleAppFilePath) else yLoggerW(msg = "You can only inject module resources in Xposed Environment") } @@ -364,14 +365,12 @@ object YukiHookBridge { */ internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) { runCatching { - YukiHookHelper.hookMember( - YukiHookHelper.findMethod(JavaClassLoader, name = "loadClass", 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) } - } - }) + 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") } } @@ -384,27 +383,23 @@ object YukiHookBridge { */ @YukiGenerateApi fun hookModuleAppStatus(loader: ClassLoader?, isHookResourcesStatus: Boolean = false) { - if (YukiHookAPI.Configs.isEnableHookModuleStatus) - YukiHookHelper.findClass(loader, YukiHookModuleStatus::class.java).also { statusClass -> - if (isHookResourcesStatus.not()) { - YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME), - object : YukiMemberReplacement() { - override fun replaceHookedMember(wrapper: HookParamWrapper) = true - }) - YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME), - object : YukiMemberReplacement() { - override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName - }) - YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME), - object : YukiMemberReplacement() { - override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion - }) - } else - YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME), - object : YukiMemberReplacement() { - override fun replaceHookedMember(wrapper: HookParamWrapper) = true - }) - } + if (YukiHookAPI.Configs.isEnableHookModuleStatus.not()) return + classOf(loader).apply { + if (isHookResourcesStatus.not()) { + YukiHookHelper.hook(method { name = YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME }, object : YukiMemberReplacement() { + override fun replaceHookedMember(wrapper: HookParamWrapper) = true + }) + YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME }, object : YukiMemberReplacement() { + override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName + }) + YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME }, object : YukiMemberReplacement() { + override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion + }) + } else + YukiHookHelper.hook(method { name = YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME }, object : YukiMemberReplacement() { + override fun replaceHookedMember(wrapper: HookParamWrapper) = true + }) + } } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt index 0c877435..6d574e65 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt @@ -25,15 +25,19 @@ * * This file is Created by fankes on 2022/7/28. */ +@file:Suppress("NewApi") + package com.highcapable.yukihookapi.hook.xposed.bridge.factory +import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder +import com.highcapable.yukihookapi.hook.core.finder.MethodFinder +import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder +import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedBridge -import java.lang.reflect.Field import java.lang.reflect.Member -import java.lang.reflect.Method /** * Hook 回调优先级配置类 @@ -67,33 +71,20 @@ internal object YukiHookedMembers { internal object YukiHookHelper { /** - * 查找 [Class] - * @param loader 当前 [ClassLoader] - * @param baseClass 当前类 - * @return [Field] - * @throws IllegalStateException 如果 [ClassLoader] 为空 + * Hook [BaseFinder.BaseResult] + * @param traction 直接调用 [BaseFinder.BaseResult] + * @param callback 回调 + * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) */ - internal fun findClass(loader: ClassLoader?, baseClass: Class<*>) = loader?.loadClass(baseClass.name) ?: error("ClassLoader is null") - - /** - * 查找 [Field] - * @param baseClass 所在类 - * @param name 变量名称 - * @return [Field] - * @throws NoSuchFieldError 如果找不到变量 - */ - internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true } - - /** - * 查找 [Method] - * @param baseClass 所在类 - * @param name 方法名称 - * @param paramTypes 方法参数 - * @return [Method] - * @throws NoSuchMethodError 如果找不到方法 - */ - internal fun findMethod(baseClass: Class<*>, name: String, vararg paramTypes: Class<*>) = - baseClass.getDeclaredMethod(name, *paramTypes).apply { isAccessible = true } + internal fun hook(traction: BaseFinder.BaseResult, callback: YukiHookCallback) = runCatching { + hookMember( + when (traction) { + is MethodFinder.Result -> traction.ignored().give() + is ConstructorFinder.Result -> traction.ignored().give() + else -> error("Unexpected BaseFinder result interface type") + }, callback + ) + }.onFailure { yLoggerE(msg = "Hooking Process exception occurred", e = it) }.getOrNull() ?: Pair(null, false) /** * Hook [Member] @@ -103,18 +94,21 @@ internal object YukiHookHelper { * @param callback 回调 * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) */ - internal fun hookMember(member: Member, callback: YukiHookCallback): Pair { + internal fun hookMember(member: Member?, callback: YukiHookCallback): Pair { runCatching { YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach { - if (it.member.toString() == member.toString()) return@runCatching it + if (it.member.toString() == member?.toString()) return@runCatching it } } - return if (YukiHookBridge.hasXposedBridge) - YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(member, callback.compat())).let { - YukiHookedMembers.hookedMembers.add(it) - Pair(it, false) - } - else Pair(null, false) + return when { + member == null -> Pair(null, false) + YukiHookBridge.hasXposedBridge -> + YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(member, callback.compat())).let { + YukiHookedMembers.hookedMembers.add(it) + Pair(it, false) + } + else -> Pair(null, false) + } } /** @@ -125,9 +119,9 @@ internal object YukiHookHelper { * @param args 参数实例 * @return [Any] or null */ - internal fun invokeOriginalMember(member: Member, instance: Any?, vararg args: Any?) = + internal fun invokeOriginalMember(member: Member?, instance: Any?, vararg args: Any?) = if (YukiHookBridge.hasXposedBridge && YukiHookedMembers.hookedMembers.any { it.member.toString() == member.toString() }) - XposedBridge.invokeOriginalMethod(member, instance, args) + member?.let { XposedBridge.invokeOriginalMethod(it, instance, args) } else null /**