diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreater.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreater.kt index 4e28b907..1155464f 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreater.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreater.kt @@ -43,26 +43,30 @@ 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.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookPriority +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook +import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement import java.lang.reflect.Field import java.lang.reflect.Member /** * [YukiHookAPI] 的 [Member] 核心 Hook 实现类 * - * 核心 API 对接 [YukiHookBridge.Hooker] 实现 + * 核心 API 对接 [YukiHookHelper] 实现 * @param packageParam 需要传入 [PackageParam] 实现方法调用 * @param hookClass 要 Hook 的 [HookClass] 实例 */ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) { /** 默认 Hook 回调优先级 */ - val PRIORITY_DEFAULT = YukiHookBridge.Hooker.PRIORITY_DEFAULT + val PRIORITY_DEFAULT = YukiHookPriority.PRIORITY_DEFAULT /** 延迟回调 Hook 方法结果 */ - val PRIORITY_LOWEST = YukiHookBridge.Hooker.PRIORITY_LOWEST + val PRIORITY_LOWEST = YukiHookPriority.PRIORITY_LOWEST /** 更快回调 Hook 方法结果 */ - val PRIORITY_HIGHEST = YukiHookBridge.Hooker.PRIORITY_HIGHEST + val PRIORITY_HIGHEST = YukiHookPriority.PRIORITY_HIGHEST /** * Hook 模式定义 @@ -208,7 +212,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara private var allMethodsName = "" /** 当前被 Hook 的方法、构造方法实例 */ - private var hookedMember: YukiHookBridge.Hooker.YukiHookedMember? = null + private var memberUnhook: YukiMemberHook.Unhook? = null /** * 手动指定要 Hook 的方法、构造方法 @@ -458,7 +462,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara val replaceHookParam = HookParam(createrInstance = this@YukiMemberHookCreater) /** 定义替换 Hook 回调方法体 */ - val replaceMent = object : YukiHookBridge.Hooker.YukiMemberReplacement(priority) { + val replaceMent = object : YukiMemberReplacement(priority) { override fun replaceHookedMember(wrapper: HookParamWrapper): Any? { return replaceHookParam.assign(wrapper).let { param -> try { @@ -482,7 +486,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara val afterHookParam = HookParam(createrInstance = this@YukiMemberHookCreater) /** 定义前后 Hook 回调方法体 */ - val beforeAfterHook = object : YukiHookBridge.Hooker.YukiMemberHook(priority) { + val beforeAfterHook = object : YukiMemberHook(priority) { override fun beforeHookedMember(wrapper: HookParamWrapper) { beforeHookParam.assign(wrapper).also { param -> runCatching { @@ -517,13 +521,13 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara if (member != null) member.also { member -> runCatching { - (if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent) - else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also { + (if (isReplaceHookMode) YukiHookHelper.hookMethod(member, replaceMent) + else YukiHookHelper.hookMethod(member, beforeAfterHook)).also { when { it.first.member == null -> error("Hook Member [$member] failed") it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!) else -> { - hookedMember = it.first + memberUnhook = it.first onHookedCallback?.invoke(it.first.member!!) } } @@ -548,25 +552,25 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara else runCatching { when (hookMemberMode) { HookMemberMode.HOOK_ALL_METHODS -> - (if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, replaceMent) - else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also { + (if (isReplaceHookMode) YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, replaceMent) + else YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also { when { it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched") it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) } else -> { - hookedMember = it.first.first() + memberUnhook = it.first.first() it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) } } } } HookMemberMode.HOOK_ALL_CONSTRUCTORS -> - (if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, replaceMent) - else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also { + (if (isReplaceHookMode) YukiHookHelper.hookAllConstructors(hookClass.instance, replaceMent) + else YukiHookHelper.hookAllConstructors(hookClass.instance, beforeAfterHook)).also { when { it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched") it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) } else -> { - hookedMember = it.first.first() + memberUnhook = it.first.first() it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) } } } @@ -736,10 +740,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara * @param result 回调是否成功 */ fun remove(result: (Boolean) -> Unit = {}) { - hookedMember?.unhook() + memberUnhook?.remove() runCatching { preHookMembers.remove(this@MemberHookCreater.toString()) } - result(hookedMember != null) - hookedMember = null + if (memberUnhook != null) onHookLogMsg(msg = "Remove Hooked Member [${member ?: "All of \"$allMethodsName\""}] done [$tag]") + result(memberUnhook != null) + memberUnhook = null } } } 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 973ef9b6..9eb67390 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 @@ -48,17 +48,19 @@ import com.highcapable.yukihookapi.hook.type.android.* import com.highcapable.yukihookapi.hook.type.java.IntType 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 dalvik.system.PathClassLoader -import de.robv.android.xposed.* +import de.robv.android.xposed.IXposedHookInitPackageResources +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.IXposedHookZygoteInit +import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_InitPackageResources import de.robv.android.xposed.callbacks.XC_LoadPackage -import java.lang.reflect.Constructor -import java.lang.reflect.Field -import java.lang.reflect.Member -import java.lang.reflect.Method /** * 这是一个对接 Xposed Hook 入口与 [XposedBridge] 的装载类实现桥 @@ -124,8 +126,8 @@ object YukiHookBridge { */ internal val systemContext get() = runCatching { - Hooker.findMethod(ActivityThreadClass, name = "getSystemContext") - .invoke(Hooker.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context? + YukiHookHelper.findMethod(ActivityThreadClass, name = "getSystemContext") + .invoke(YukiHookHelper.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context? }.getOrNull() ?: error("Failed to got SystemContext") /** @@ -154,7 +156,7 @@ object YukiHookBridge { */ internal val executorName get() = runCatching { - (Hooker.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?) + (YukiHookHelper.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?) ?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown" }.getOrNull() ?: "invalid" @@ -241,7 +243,7 @@ object YukiHookBridge { /** Hook [Application] 装载方法 */ runCatching { if (AppLifecycleCallback.isCallbackSetUp) { - Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "attach", ContextClass), object : Hooker.YukiMemberHook() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "attach", ContextClass), object : YukiMemberHook() { override fun beforeHookedMember(wrapper: HookParamWrapper) { (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) } } @@ -250,26 +252,27 @@ object YukiHookBridge { (wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) } } }) - Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onTerminate"), object : Hooker.YukiMemberHook() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "onTerminate"), object : YukiMemberHook() { override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) } } }) - Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onLowMemory"), object : Hooker.YukiMemberHook() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "onLowMemory"), object : YukiMemberHook() { override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) } } }) - Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onTrimMemory", IntType), object : Hooker.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) - } - }) - Hooker.hookMethod( - Hooker.findMethod(ApplicationClass, name = "onConfigurationChanged", ConfigurationClass), - object : Hooker.YukiMemberHook() { + YukiHookHelper.hookMethod( + 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.hookMethod(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 @@ -278,9 +281,9 @@ object YukiHookBridge { }) } if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) - Hooker.hookMethod( - Hooker.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass), - object : Hooker.YukiMemberHook() { + YukiHookHelper.hookMethod( + YukiHookHelper.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass), + object : YukiMemberHook() { override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.args?.get(0) as? Application?)?.also { hostApplication = it @@ -317,23 +320,23 @@ object YukiHookBridge { @YukiGenerateApi fun hookModuleAppStatus(classLoader: ClassLoader?, isHookResourcesStatus: Boolean = false) { if (YukiHookAPI.Configs.isEnableHookModuleStatus) - Hooker.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass -> + YukiHookHelper.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass -> if (isHookResourcesStatus.not()) { - Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME), - object : Hooker.YukiMemberReplacement() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME), + object : YukiMemberReplacement() { override fun replaceHookedMember(wrapper: HookParamWrapper) = true }) - Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME), - object : Hooker.YukiMemberReplacement() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME), + object : YukiMemberReplacement() { override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName }) - Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME), - object : Hooker.YukiMemberReplacement() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME), + object : YukiMemberReplacement() { override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion }) } else - Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME), - object : Hooker.YukiMemberReplacement() { + YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME), + object : YukiMemberReplacement() { override fun replaceHookedMember(wrapper: HookParamWrapper) = true }) } @@ -430,247 +433,4 @@ object YukiHookBridge { /** 系统广播监听回调 */ internal val onReceiversCallback = HashMap, (Context, Intent) -> Unit>>() } - - /** - * Hook 核心功能实现实例 - * - * 对接 [XposedBridge] 实现 Hook 功能 - */ - internal object Hooker { - - /** 已经 Hook 的 [Member] 数组 */ - private val hookedMembers = HashSet() - - /** 已经 Hook 的全部 [Method] 数组 */ - private val hookedAllMethods = HashMap>() - - /** 已经 Hook 的全部 [Constructor] 数组 */ - private val hookedAllConstructors = HashMap>() - - /** 默认 Hook 回调优先级 */ - internal const val PRIORITY_DEFAULT = 50 - - /** 延迟回调 Hook 方法结果 */ - internal const val PRIORITY_LOWEST = -10000 - - /** 更快回调 Hook 方法结果 */ - internal const val PRIORITY_HIGHEST = 10000 - - /** - * 查找 [Class] - * @param classLoader 当前 [ClassLoader] - * @param baseClass 当前类 - * @return [Field] - * @throws IllegalStateException 如果 [ClassLoader] 为空 - */ - internal fun findClass(classLoader: ClassLoader?, baseClass: Class<*>) = - classLoader?.loadClass(baseClass.name) ?: error("ClassLoader is null") - - /** - * 查找变量 - * @param baseClass 所在类 - * @param name 变量名称 - * @return [Field] - * @throws NoSuchFieldError 如果找不到变量 - */ - internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true } - - /** - * 查找方法 - * @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 } - - /** - * Hook 方法 - * - * 对接 [XposedBridge.hookMethod] - * @param hookMethod 需要 Hook 的方法、构造方法 - * @param callback 回调 - * @return [Pair] - ([YukiHookedMember],[Boolean] 是否已经 Hook) - */ - internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair { - runCatching { - hookedMembers.takeIf { it.isNotEmpty() }?.forEach { - if (it.member.toString() == hookMethod.toString()) return@runCatching it - } - } - return YukiHookedMember.wrapper(XposedBridge.hookMethod(hookMethod, compatCallback(callback))).let { - hookedMembers.add(it) - Pair(it, false) - } - } - - /** - * Hook 当前 [hookClass] 所有 [methodName] 的方法 - * - * 对接 [XposedBridge.hookAllMethods] - * @param hookClass 当前 Hook 的 [Class] - * @param methodName 方法名 - * @param callback 回调 - * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook) - */ - internal fun hookAllMethods( - hookClass: Class<*>?, methodName: String, callback: YukiHookCallback - ): Pair, Boolean> { - var isAlreadyHook = false - val hookedMembers = HashSet().also { - val allMethodsName = "$hookClass$methodName" - if (hookedAllMethods.contains(allMethodsName)) { - isAlreadyHook = true - hookedAllMethods[allMethodsName]?.forEach { e -> it.add(e) } - return@also - } - XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { e -> e.isNotEmpty() } - ?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allMethodsName)) } - hookedAllMethods[allMethodsName] = it - } - return Pair(hookedMembers, isAlreadyHook) - } - - /** - * Hook 当前 [hookClass] 所有构造方法 - * - * 对接 [XposedBridge.hookAllConstructors] - * @param hookClass 当前 Hook 的 [Class] - * @param callback 回调 - * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook) - */ - internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair, Boolean> { - var isAlreadyHook = false - val hookedMembers = HashSet().also { - val allConstructorsName = "$hookClass" - if (hookedAllConstructors.contains(allConstructorsName)) { - isAlreadyHook = true - hookedAllConstructors[allConstructorsName]?.forEach { e -> it.add(e) } - return@also - } - XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { e -> e.isNotEmpty() } - ?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allConstructorsName)) } - hookedAllConstructors[allConstructorsName] = it - } - return Pair(hookedMembers, isAlreadyHook) - } - - /** - * 兼容对接 Hook 回调接口 - * @param callback [YukiHookCallback] 接口 - * @return [XC_MethodHook] 原始接口 - */ - private fun compatCallback(callback: YukiHookCallback) = when (callback) { - is YukiMemberHook -> object : XC_MethodHook(callback.priority) { - - /** 创建 Hook 前 [HookParamWrapper] */ - val beforeHookWrapper = HookParamWrapper() - - /** 创建 Hook 后 [HookParamWrapper] */ - val afterHookWrapper = HookParamWrapper() - - override fun beforeHookedMethod(param: MethodHookParam?) { - if (param == null) return - callback.beforeHookedMember(beforeHookWrapper.assign(param)) - } - - override fun afterHookedMethod(param: MethodHookParam?) { - if (param == null) return - callback.afterHookedMember(afterHookWrapper.assign(param)) - } - } - is YukiMemberReplacement -> object : XC_MethodReplacement(callback.priority) { - - /** 创建替换 Hook [HookParamWrapper] */ - val replaceHookWrapper = HookParamWrapper() - - override fun replaceHookedMethod(param: MethodHookParam?): Any? { - if (param == null) return null - return callback.replaceHookedMember(replaceHookWrapper.assign(param)) - } - } - else -> error("Invalid YukiHookCallback type") - } - - /** - * Hook 方法回调接口 - * @param priority Hook 优先级 - 默认 [PRIORITY_DEFAULT] - */ - internal abstract class YukiMemberHook(override val priority: Int = PRIORITY_DEFAULT) : YukiHookCallback(priority) { - - /** - * 在方法执行之前注入 - * @param wrapper 包装实例 - */ - open fun beforeHookedMember(wrapper: HookParamWrapper) {} - - /** - * 在方法执行之后注入 - * @param wrapper 包装实例 - */ - open fun afterHookedMember(wrapper: HookParamWrapper) {} - } - - /** - * Hook 替换方法回调接口 - * @param priority Hook 优先级- 默认 [PRIORITY_DEFAULT] - */ - internal abstract class YukiMemberReplacement(override val priority: Int = PRIORITY_DEFAULT) : YukiHookCallback(priority) { - - /** - * 拦截替换为指定结果 - * @param wrapper 包装实例 - * @return [Any] or null - */ - abstract fun replaceHookedMember(wrapper: HookParamWrapper): Any? - } - - /** - * Hook 回调接口父类 - * @param priority Hook 优先级 - */ - internal abstract class YukiHookCallback(open val priority: Int) - - /** - * 已经 Hook 的 [Member] 实现类 - * @param instance 对接 [XC_MethodHook.Unhook] - * @param tag 标识多个 [Member] Hook 的标签 - */ - internal class YukiHookedMember private constructor(private val instance: XC_MethodHook.Unhook, private val tag: String) { - - companion object { - - /** - * 从 [XC_MethodHook.Unhook] 创建 [YukiHookedMember] 实例 - * @param instance [XC_MethodHook.Unhook] 实例 - * @param tag 标识多个 [Member] Hook 的标签 - 默认空 - * @return [YukiHookedMember] - */ - internal fun wrapper(instance: XC_MethodHook.Unhook, tag: String = "") = YukiHookedMember(instance, tag) - } - - /** - * 当前被 Hook 的 [Member] - * @return [Member] or null - */ - internal val member: Member? get() = instance.hookedMethod - - /** 解除 Hook */ - internal fun unhook() { - if (tag.isNotBlank()) runCatching { - if (hookedAllMethods.contains(tag)) - hookedAllMethods[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.instance.unhook() } - if (hookedAllConstructors.contains(tag)) - hookedAllConstructors[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.instance.unhook() } - hookedAllMethods.remove(tag) - hookedAllConstructors.remove(tag) - } else { - instance.unhook() - runCatching { hookedMembers.remove(this) } - } - } - } - } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiHookFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiHookFactory.kt new file mode 100644 index 00000000..2d13a461 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiHookFactory.kt @@ -0,0 +1,304 @@ +/* + * 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/7/28. + */ +package com.highcapable.yukihookapi.hook.xposed.bridge.factory + +import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XposedBridge +import java.lang.reflect.Constructor +import java.lang.reflect.Field +import java.lang.reflect.Member +import java.lang.reflect.Method + +/** + * Hook 回调优先级配置类 + */ +internal object YukiHookPriority { + + /** 默认 Hook 回调优先级 */ + internal const val PRIORITY_DEFAULT = 50 + + /** 延迟回调 Hook 方法结果 */ + internal const val PRIORITY_LOWEST = -10000 + + /** 更快回调 Hook 方法结果 */ + internal const val PRIORITY_HIGHEST = 10000 +} + +/** + * 已经 Hook 的方法、构造方法缓存数组 + */ +internal object YukiHookedMembers { + + /** 已经 Hook 的 [Member] 数组 */ + internal val hookedMembers = HashSet() + + /** 已经 Hook 的成组 [Method] 数组 */ + internal val hookedQueueMethods = HashMap>() + + /** 已经 Hook 的成组 [Constructor] 数组 */ + internal val hookedQueueConstructors = HashMap>() +} + +/** + * Hook 核心功能实现实例 + * + * 对接 [XposedBridge] 实现 Hook 功能 + */ +internal object YukiHookHelper { + + /** + * 查找 [Class] + * @param classLoader 当前 [ClassLoader] + * @param baseClass 当前类 + * @return [Field] + * @throws IllegalStateException 如果 [ClassLoader] 为空 + */ + internal fun findClass(classLoader: ClassLoader?, baseClass: Class<*>) = + classLoader?.loadClass(baseClass.name) ?: error("ClassLoader is null") + + /** + * 查找变量 + * @param baseClass 所在类 + * @param name 变量名称 + * @return [Field] + * @throws NoSuchFieldError 如果找不到变量 + */ + internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true } + + /** + * 查找方法 + * @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 } + + /** + * Hook 方法 + * + * 对接 [XposedBridge.hookMethod] + * @param hookMethod 需要 Hook 的方法、构造方法 + * @param callback 回调 + * @return [Pair] - ([YukiMemberHook.Unhook],[Boolean] 是否已经 Hook) + */ + internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair { + runCatching { + YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach { + if (it.member.toString() == hookMethod.toString()) return@runCatching it + } + } + return YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(hookMethod, callback.compat())).let { + YukiHookedMembers.hookedMembers.add(it) + Pair(it, false) + } + } + + /** + * Hook 当前 [hookClass] 所有 [methodName] 的方法 + * + * 对接 [XposedBridge.hookAllMethods] + * @param hookClass 当前 Hook 的 [Class] + * @param methodName 方法名 + * @param callback 回调 + * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiMemberHook.Unhook] 数组,[Boolean] 是否已经 Hook) + */ + internal fun hookAllMethods( + hookClass: Class<*>?, methodName: String, callback: YukiHookCallback + ): Pair, Boolean> { + var isAlreadyHook = false + val hookedMembers = HashSet().also { + val allMethodsName = "$hookClass$methodName" + if (YukiHookedMembers.hookedQueueMethods.contains(allMethodsName)) { + isAlreadyHook = true + YukiHookedMembers.hookedQueueMethods[allMethodsName]?.forEach { e -> it.add(e) } + return@also + } + XposedBridge.hookAllMethods(hookClass, methodName, callback.compat()).takeIf { e -> e.isNotEmpty() } + ?.forEach { e -> it.add(YukiMemberHook.Unhook.wrapper(e, allMethodsName)) } + YukiHookedMembers.hookedQueueMethods[allMethodsName] = it + } + return Pair(hookedMembers, isAlreadyHook) + } + + /** + * Hook 当前 [hookClass] 所有构造方法 + * + * 对接 [XposedBridge.hookAllConstructors] + * @param hookClass 当前 Hook 的 [Class] + * @param callback 回调 + * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiMemberHook.Unhook] 数组,[Boolean] 是否已经 Hook) + */ + internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair, Boolean> { + var isAlreadyHook = false + val hookedMembers = HashSet().also { + val allConstructorsName = "$hookClass" + if (YukiHookedMembers.hookedQueueConstructors.contains(allConstructorsName)) { + isAlreadyHook = true + YukiHookedMembers.hookedQueueConstructors[allConstructorsName]?.forEach { e -> it.add(e) } + return@also + } + XposedBridge.hookAllConstructors(hookClass, callback.compat()).takeIf { e -> e.isNotEmpty() } + ?.forEach { e -> it.add(YukiMemberHook.Unhook.wrapper(e, allConstructorsName)) } + YukiHookedMembers.hookedQueueConstructors[allConstructorsName] = it + } + return Pair(hookedMembers, isAlreadyHook) + } + + /** + * 兼容对接 Hook 回调接口 + * @return [XC_MethodHook] 原始接口 + */ + private fun YukiHookCallback.compat() = object : XC_MethodHook(priority) { + + /** 创建 Hook 前 [HookParamWrapper] */ + private val beforeHookWrapper = HookParamWrapper() + + /** 创建 Hook 后 [HookParamWrapper] */ + private val afterHookWrapper = HookParamWrapper() + + override fun beforeHookedMethod(param: MethodHookParam?) { + if (param == null) return + if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type") + if (this@compat is YukiMemberReplacement) + param.result = replaceHookedMember(beforeHookWrapper.assign(param)) + else beforeHookedMember(beforeHookWrapper.assign(param)) + } + + override fun afterHookedMethod(param: MethodHookParam?) { + if (param == null) return + if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type") + afterHookedMember(afterHookWrapper.assign(param)) + } + } +} + +/** + * Hook 替换方法回调接口 + * @param priority Hook 优先级- 默认 [YukiHookPriority.PRIORITY_DEFAULT] + */ +internal abstract class YukiMemberReplacement(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiMemberHook(priority) { + + override fun beforeHookedMember(wrapper: HookParamWrapper) { + wrapper.result = replaceHookedMember(wrapper) + } + + override fun afterHookedMember(wrapper: HookParamWrapper) {} + + /** + * 拦截替换为指定结果 + * @param wrapper 包装实例 + * @return [Any] or null + */ + abstract fun replaceHookedMember(wrapper: HookParamWrapper): Any? +} + +/** + * Hook 方法回调接口 + * @param priority Hook 优先级 - 默认 [YukiHookPriority.PRIORITY_DEFAULT] + */ +internal abstract class YukiMemberHook(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiHookCallback(priority) { + + /** + * 在方法执行之前注入 + * @param wrapper 包装实例 + */ + open fun beforeHookedMember(wrapper: HookParamWrapper) {} + + /** + * 在方法执行之后注入 + * @param wrapper 包装实例 + */ + open fun afterHookedMember(wrapper: HookParamWrapper) {} + + /** + * 已经 Hook 且可被解除 Hook 的 [Member] 实现类 + * @param instance 对接 [XC_MethodHook.Unhook] + * @param tag 标识多个 [Member] Hook 的标签 - 例如 [YukiHookHelper.hookAllMethods]、[YukiHookHelper.hookAllConstructors] + */ + internal class Unhook private constructor(private val instance: XC_MethodHook.Unhook, private val tag: String) { + + companion object { + + /** + * 从 [XC_MethodHook.Unhook] 创建 [Unhook] 实例 + * @param instance [XC_MethodHook.Unhook] 实例 + * @param tag 标识多个 [Member] Hook 的标签 - 默认空 + * @return [Unhook] + */ + internal fun wrapper(instance: XC_MethodHook.Unhook, tag: String = "") = Unhook(instance, tag) + } + + /** + * 当前被 Hook 的 [Member] + * @return [Member] or null + */ + internal val member: Member? get() = instance.hookedMethod + + /** + * 解除 Hook 并从 + * [YukiHookedMembers.hookedMembers]、 + * [YukiHookedMembers.hookedQueueMethods]、 + * [YukiHookedMembers.hookedQueueConstructors] + * 缓存数组中移除 + */ + internal fun remove() { + if (tag.isNotBlank()) runCatching { + when { + YukiHookedMembers.hookedQueueMethods.contains(tag) -> { + YukiHookedMembers.hookedQueueMethods[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.unhook(isNeedRemove = false) } + YukiHookedMembers.hookedQueueMethods.remove(tag) + } + YukiHookedMembers.hookedQueueConstructors.contains(tag) -> { + YukiHookedMembers.hookedQueueConstructors[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.unhook(isNeedRemove = false) } + YukiHookedMembers.hookedQueueConstructors.remove(tag) + } + else -> unhook() + } + } else unhook() + } + + /** + * 解除 [instance] 的 Hook + * @param isNeedRemove 是否需要从 [YukiHookedMembers.hookedMembers] 中移除 + */ + private fun unhook(isNeedRemove: Boolean = true) { + instance.unhook() + if (isNeedRemove) runCatching { YukiHookedMembers.hookedMembers.remove(this) } + } + } +} + +/** + * Hook 回调接口父类 + * @param priority Hook 优先级 + */ +internal abstract class YukiHookCallback(open val priority: Int) \ No newline at end of file