From 3a1f6e6cb797985b58730562255d6f6aa1cc235d Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Mon, 16 May 2022 03:57:46 +0800 Subject: [PATCH] Fix inner Hook not invoking and fix maybe created duplicate hook --- docs/api/public/YukiMemberHookCreater.md | 14 +++++++ .../hook/core/YukiMemberHookCreater.kt | 37 +++++++++++++++---- .../hook/core/YukiResourcesHookCreater.kt | 12 ++++-- .../yukihookapi/hook/param/PackageParam.kt | 3 ++ .../yukihookapi/hook/utils/UtilsFactory.kt | 15 ++++++++ 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/docs/api/public/YukiMemberHookCreater.md b/docs/api/public/YukiMemberHookCreater.md index f159c59c..7ca36976 100644 --- a/docs/api/public/YukiMemberHookCreater.md +++ b/docs/api/public/YukiMemberHookCreater.md @@ -386,6 +386,20 @@ inline fun HookParam.constructor(initiate: ConstructorFinder.() -> Unit): Constr > 使用当前 `hookClass` 查找并得到构造方法。 +#### injectMember [method] + +```kotlin +inline fun HookParam.injectMember(priority: Int, tag: String, initiate: MemberHookCreater.() -> Unit): MemberHookCreater.Result +``` + +**变更记录** + +`v1.0.88` `新增` + +**功能描述** + +> 注入要 Hook 的方法、构造类 (嵌套 Hook)。 + #### beforeHook [method] ```kotlin 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 0a4c484a..3abc6481 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 @@ -42,6 +42,7 @@ import com.highcapable.yukihookapi.hook.param.HookParam 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.utils.putIfAbsentCompat import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import java.lang.reflect.Field import java.lang.reflect.Member @@ -79,7 +80,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara /** 设置要 Hook 的方法、构造类 */ @PublishedApi - internal var preHookMembers = HashSet() + internal var preHookMembers = HashMap() /** * 得到当前被 Hook 的 [Class] @@ -99,7 +100,8 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara * @return [MemberHookCreater.Result] */ inline fun injectMember(priority: Int = PRIORITY_DEFAULT, tag: String = "Default", initiate: MemberHookCreater.() -> Unit) = - MemberHookCreater(priority, tag, packageParam.exhibitName).apply(initiate).apply { preHookMembers.add(this) }.build() + MemberHookCreater(priority, tag, packageParam.exhibitName) + .apply(initiate).apply { preHookMembers.putIfAbsentCompat(toString(), this) }.build() /** * Hook 执行入口 @@ -109,8 +111,8 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara @PublishedApi internal fun hook(): Result { if (YukiHookBridge.hasXposedBridge.not()) return Result() - /** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.PACKAGE] */ - if (packageParam.wrapper?.type == HookEntryType.RESOURCES) return Result() + /** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.PACKAGE] 或 [PackageParam.isHookParamCallback] 已被执行 */ + if (packageParam.wrapper?.type == HookEntryType.RESOURCES && packageParam.isHookParamCallback.not()) return Result() return if (preHookMembers.isEmpty()) error("Hook Members is empty, hook aborted") else Result().also { Thread { @@ -119,7 +121,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara when { isDisableCreaterRunHook.not() && hookClass.instance != null -> { it.onPrepareHook?.invoke() - preHookMembers.forEach { m -> m.hook() } + preHookMembers.forEach { (_, m) -> m.hook() } } isDisableCreaterRunHook.not() && hookClass.instance == null -> if (onHookClassNotFoundFailureCallback == null) @@ -140,6 +142,9 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara */ inner class MemberHookCreater(private val priority: Int, internal val tag: String, internal val packageName: String) { + /** 是否已经执行 Hook */ + private var isHooked = false + /** [beforeHook] 回调 */ private var beforeHookCallback: (HookParam.() -> Unit)? = null @@ -301,6 +306,19 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara if (hookClass.instance == null) ConstructorFinder(hookInstance = this@MemberHookCreater).failure(hookClass.throwable) else ConstructorFinder(hookInstance = this@MemberHookCreater, hookClass.instance).apply(initiate).build() + /** + * 注入要 Hook 的方法、构造类 (嵌套 Hook) + * @param priority Hook 优先级 - 默认 [PRIORITY_DEFAULT] + * @param tag 可设置标签 - 在发生错误时方便进行调试 + * @param initiate 方法体 + * @return [MemberHookCreater.Result] + */ + inline fun HookParam.injectMember( + priority: Int = PRIORITY_DEFAULT, + tag: String = "InnerDefault", + initiate: MemberHookCreater.() -> Unit + ) = this@YukiMemberHookCreater.injectMember(priority, tag, initiate).also { this@YukiMemberHookCreater.hook() } + /** * 在方法执行完成前 Hook * @@ -408,7 +426,8 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara /** Hook 执行入口 */ @PublishedApi internal fun hook() { - if (YukiHookBridge.hasXposedBridge.not() || isDisableMemberRunHook) return + if (YukiHookBridge.hasXposedBridge.not() || isHooked || isDisableMemberRunHook) return + isHooked = true finder?.printLogIfExist() if (hookClass.instance == null) { (hookClass.throwable ?: Throwable("HookClass [${hookClass.name}] not found")).also { @@ -453,6 +472,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara beforeHookCallback?.invoke(param) if (beforeHookCallback != null) onHookLogMsg(msg = "Before Hook Member [${member ?: "All of \"$allMethodsName\""}] done [$tag]") + packageParam.isHookParamCallback = true }.onFailure { onConductFailureCallback?.invoke(param, it) onAllFailureCallback?.invoke(it) @@ -467,6 +487,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara afterHookCallback?.invoke(param) if (afterHookCallback != null) onHookLogMsg(msg = "After Hook Member [${member ?: "All of \"$allMethodsName\""}] done [$tag]") + packageParam.isHookParamCallback = true }.onFailure { onConductFailureCallback?.invoke(param, it) onAllFailureCallback?.invoke(it) @@ -479,11 +500,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara if (member != null) member.also { member -> runCatching { - if (isReplaceHookMode) + (if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent)?.also { onHookedCallback?.invoke(it) } ?: error("Hook Member [$member] failed") else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)?.also { onHookedCallback?.invoke(it) } - ?: error("Hook Member [$member] failed") + ?: error("Hook Member [$member] failed")).run { packageParam.isHookParamCallback = true } }.onFailure { onHookingFailureCallback?.invoke(it) onAllFailureCallback?.invoke(it) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreater.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreater.kt index a07687d4..63bed040 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreater.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreater.kt @@ -36,6 +36,7 @@ import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerI import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.type.HookEntryType +import com.highcapable.yukihookapi.hook.utils.putIfAbsentCompat import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources @@ -49,7 +50,7 @@ class YukiResourcesHookCreater(private val packageParam: PackageParam, @Publishe /** 设置要 Hook 的 Resources */ @PublishedApi - internal var preHookResources = HashSet() + internal var preHookResources = HashMap() /** * 注入要 Hook 的 Resources @@ -58,7 +59,7 @@ class YukiResourcesHookCreater(private val packageParam: PackageParam, @Publishe * @return [ResourcesHookCreater.Result] */ inline fun injectResource(tag: String = "Default", initiate: ResourcesHookCreater.() -> Unit) = - ResourcesHookCreater(tag).apply(initiate).apply { preHookResources.add(this) }.build() + ResourcesHookCreater(tag).apply(initiate).apply { preHookResources.putIfAbsentCompat(toString(), this) }.build() /** * Hook 执行入口 @@ -70,7 +71,7 @@ class YukiResourcesHookCreater(private val packageParam: PackageParam, @Publishe /** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.RESOURCES] */ if (packageParam.wrapper?.type == HookEntryType.PACKAGE) return if (preHookResources.isEmpty()) error("Hook Resources is empty, hook aborted") - preHookResources.forEach { it.hook() } + preHookResources.forEach { (_, r) -> r.hook() } } /** @@ -81,6 +82,9 @@ class YukiResourcesHookCreater(private val packageParam: PackageParam, @Publishe */ inner class ResourcesHookCreater(private val tag: String) { + /** 是否已经执行 Hook */ + private var isHooked = false + /** * 模块 APP Resources 替换实例 * @param resId Resources Id @@ -174,6 +178,8 @@ class YukiResourcesHookCreater(private val packageParam: PackageParam, @Publishe /** Hook 执行入口 */ @PublishedApi internal fun hook() { + if (isHooked) return + isHooked = true if (isDisableCreaterRunHook.not()) runCatching { when { conditions == null -> yLoggerE(msg = "You must set the conditions before hook a Resources [$tag]") 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 4b448d37..e3c964d2 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 @@ -56,6 +56,9 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs */ open class PackageParam(@PublishedApi internal var wrapper: PackageParamWrapper? = null) { + /** [HookParam] 是否已经执行回调事件 */ + internal var isHookParamCallback = false + /** * 用于展示的 APP 包名 * @return [String] diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/UtilsFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/UtilsFactory.kt index d71b48ed..da4c1d88 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/UtilsFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/UtilsFactory.kt @@ -27,8 +27,23 @@ */ package com.highcapable.yukihookapi.hook.utils +import android.os.Build import com.highcapable.yukihookapi.annotation.YukiPrivateApi +/** + * 不重复写入 [HashMap] + * + * 兼容旧版本 Android + * @param key Key + * @param value Value + */ +@YukiPrivateApi +inline fun HashMap.putIfAbsentCompat(key: K, value: V) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + putIfAbsent(key, value) + else get(key) ?: put(key, value) +} + /** * 计算方法执行耗时 * @param block 方法块