Added unhook function in YukiHookBridge and YukiMemberHookCreater

This commit is contained in:
2022-07-28 03:11:27 +08:00
parent 4f8c2759c0
commit c410608690
4 changed files with 185 additions and 35 deletions

View File

@@ -518,6 +518,22 @@ fun intercept()
!> 这将会禁止此方法执行并返回 `null` !> 这将会禁止此方法执行并返回 `null`
#### removeSelf [method]
```kotlin
fun removeSelf(result: (Boolean) -> Unit)
```
**变更记录**
`v1.0.93` `新增`
**功能描述**
> 移除当前注入的 Hook 方法、构造方法 (解除 Hook)。
!> 你只能在 Hook 回调方法中使用此功能。
#### Result [class] #### Result [class]
```kotlin ```kotlin
@@ -736,6 +752,22 @@ fun ignoredAllFailure(): Result
> 忽略全部 Hook 过程发生的错误。 > 忽略全部 Hook 过程发生的错误。
##### remove [method]
```kotlin
fun remove(result: (Boolean) -> Unit)
```
**变更记录**
`v1.0.93` `新增`
**功能描述**
> 移除当前注入的 Hook 方法、构造方法 (解除 Hook)。
!> 你只能在 Hook 成功后才能解除 Hook可监听 `onHooked` 事件。
### Result [class] ### Result [class]
```kotlin ```kotlin

View File

@@ -302,6 +302,48 @@ loadZygote {
更多功能请参考 [ResourcesHookCreater](api/document?id=resourceshookcreater-class)。 更多功能请参考 [ResourcesHookCreater](api/document?id=resourceshookcreater-class)。
### 解除 Hook
原生的 Xposed 为我们提供了一个 `XC_MethodHook.Unhook` 功能,可以从 Hook 队列中将当前 Hook 移除,`YukiHookAPI` 同样可以实现此功能。
第一种方法,保存当前注入对象的 `Result` 实例,在适当的时候和地方调用 `remove` 即可解除该注入对象。
> 示例如下
```kotlin
// 设置一个变量保存当前实例
val hookResult = injectMember {
method {
name = "test"
returnType = UnitType
}
afterHook {
// ...
}
}
// 在适当的时候调用如下方法即可
hookResult.remove()
```
第二种方法,在 Hook 回调方法中调用 `removeSelf` 移除自身。
> 示例如下
```kotlin
injectMember {
method {
name = "test"
returnType = UnitType
}
afterHook {
// 直接调用如下方法即可
removeSelf()
}
}
```
更多功能请参考 [MemberHookCreater](api/document?id=memberhookcreater-class)。
## 异常处理 ## 异常处理
> `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。 > `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。

View File

@@ -139,11 +139,12 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
* @param packageName 当前 Hook 的 APP 包名 * @param packageName 当前 Hook 的 APP 包名
*/ */
inner class MemberHookCreater @PublishedApi internal constructor( inner class MemberHookCreater @PublishedApi internal constructor(
private val priority: Int, private val priority: Int, internal val tag: String, internal val packageName: String
internal val tag: String,
internal val packageName: String
) { ) {
/** Hook 结果实例 */
private var result: Result? = null
/** 是否已经执行 Hook */ /** 是否已经执行 Hook */
private var isHooked = false private var isHooked = false
@@ -206,6 +207,9 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
/** 全部方法的名称 */ /** 全部方法的名称 */
private var allMethodsName = "" private var allMethodsName = ""
/** 当前被 Hook 的方法、构造方法实例 */
private var hookedMember: YukiHookBridge.Hooker.YukiHookedMember? = null
/** /**
* 手动指定要 Hook 的方法、构造方法 * 手动指定要 Hook 的方法、构造方法
* *
@@ -421,12 +425,20 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
replaceHookResult = null replaceHookResult = null
} }
/**
* 移除当前注入的 Hook 方法、构造方法 (解除 Hook)
*
* - ❗你只能在 Hook 回调方法中使用此功能
* @param result 回调是否成功
*/
fun removeSelf(result: (Boolean) -> Unit = {}) = this.result?.remove(result) ?: result(false)
/** /**
* Hook 创建入口 * Hook 创建入口
* @return [Result] * @return [Result]
*/ */
@PublishedApi @PublishedApi
internal fun build() = Result() internal fun build() = Result().apply { result = this }
/** Hook 执行入口 */ /** Hook 执行入口 */
@PublishedApi @PublishedApi
@@ -508,9 +520,12 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent) (if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent)
else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also { else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also {
when { when {
it.first == null -> error("Hook Member [$member] failed") it.first.member == null -> error("Hook Member [$member] failed")
it.second -> onAlreadyHookedCallback?.invoke(it.first!!) it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!)
else -> onHookedCallback?.invoke(it.first!!) else -> {
hookedMember = it.first
onHookedCallback?.invoke(it.first.member!!)
}
} }
} }
}.onFailure { }.onFailure {
@@ -537,8 +552,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also { else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also {
when { when {
it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched") it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
it.second -> it.first.forEach { e -> onAlreadyHookedCallback?.invoke(e) } it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
else -> it.first.forEach { e -> onHookedCallback?.invoke(e) } else -> {
hookedMember = it.first.first()
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
}
} }
} }
HookMemberMode.HOOK_ALL_CONSTRUCTORS -> HookMemberMode.HOOK_ALL_CONSTRUCTORS ->
@@ -546,8 +564,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also { else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also {
when { when {
it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched") it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched")
it.second -> it.first.forEach { e -> onAlreadyHookedCallback?.invoke(e) } it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
else -> it.first.forEach { e -> onHookedCallback?.invoke(e) } else -> {
hookedMember = it.first.first()
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
}
} }
} }
else -> error("Hooked got a no error possible") else -> error("Hooked got a no error possible")
@@ -707,6 +728,19 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
* @return [Result] 可继续向下监听 * @return [Result] 可继续向下监听
*/ */
fun ignoredAllFailure() = onAllFailure {} fun ignoredAllFailure() = onAllFailure {}
/**
* 移除当前注入的 Hook 方法、构造方法 (解除 Hook)
*
* - ❗你只能在 Hook 成功后才能解除 Hook - 可监听 [onHooked] 事件
* @param result 回调是否成功
*/
fun remove(result: (Boolean) -> Unit = {}) {
hookedMember?.unhook()
runCatching { preHookMembers.remove(this@MemberHookCreater.toString()) }
result(hookedMember != null)
hookedMember = null
}
} }
} }

View File

@@ -39,8 +39,6 @@ import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.YukiGenerateApi import com.highcapable.yukihookapi.annotation.YukiGenerateApi
import com.highcapable.yukihookapi.hook.factory.allConstructors
import com.highcapable.yukihookapi.hook.factory.allMethods
import com.highcapable.yukihookapi.hook.factory.hasClass import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.param.type.HookEntryType import com.highcapable.yukihookapi.hook.param.type.HookEntryType
@@ -441,13 +439,13 @@ object YukiHookBridge {
internal object Hooker { internal object Hooker {
/** 已经 Hook 的 [Member] 数组 */ /** 已经 Hook 的 [Member] 数组 */
private val hookedMembers = HashSet<String>() private val hookedMembers = HashSet<YukiHookedMember>()
/** 已经 Hook 的全部 [Method] 数组 */ /** 已经 Hook 的全部 [Method] 数组 */
private val hookedAllMethods = HashSet<String>() private val hookedAllMethods = HashMap<String, HashSet<YukiHookedMember>>()
/** 已经 Hook 的全部 [Constructor] 数组 */ /** 已经 Hook 的全部 [Constructor] 数组 */
private val hookedAllConstructors = HashSet<String>() private val hookedAllConstructors = HashMap<String, HashSet<YukiHookedMember>>()
/** 默认 Hook 回调优先级 */ /** 默认 Hook 回调优先级 */
internal const val PRIORITY_DEFAULT = 50 internal const val PRIORITY_DEFAULT = 50
@@ -494,12 +492,18 @@ object YukiHookBridge {
* 对接 [XposedBridge.hookMethod] * 对接 [XposedBridge.hookMethod]
* @param hookMethod 需要 Hook 的方法、构造方法 * @param hookMethod 需要 Hook 的方法、构造方法
* @param callback 回调 * @param callback 回调
* @return [Pair] - ([Member] or null,[Boolean] 是否已经 Hook) * @return [Pair] - ([YukiHookedMember],[Boolean] 是否已经 Hook)
*/ */
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<Member?, Boolean> { internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<YukiHookedMember, Boolean> {
if (hookedMembers.contains(hookMethod.toString())) return Pair(hookMethod, true) runCatching {
hookedMembers.add(hookMethod.toString()) hookedMembers.takeIf { it.isNotEmpty() }?.forEach {
return Pair(XposedBridge.hookMethod(hookMethod, compatCallback(callback))?.hookedMethod, false) if (it.member.toString() == hookMethod.toString()) return@runCatching it
}
}
return YukiHookedMember.wrapper(XposedBridge.hookMethod(hookMethod, compatCallback(callback))).let {
hookedMembers.add(it)
Pair(it, false)
}
} }
/** /**
@@ -509,20 +513,22 @@ object YukiHookBridge {
* @param hookClass 当前 Hook 的 [Class] * @param hookClass 当前 Hook 的 [Class]
* @param methodName 方法名 * @param methodName 方法名
* @param callback 回调 * @param callback 回调
* @return [Pair] - ([HashSet] 成功 Hook 的方法数组,[Boolean] 是否已经 Hook) * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook)
*/ */
internal fun hookAllMethods(hookClass: Class<*>?, methodName: String, callback: YukiHookCallback): Pair<HashSet<Member>, Boolean> { internal fun hookAllMethods(
hookClass: Class<*>?, methodName: String, callback: YukiHookCallback
): Pair<HashSet<YukiHookedMember>, Boolean> {
var isAlreadyHook = false var isAlreadyHook = false
val hookedMembers = HashSet<Member>().also { val hookedMembers = HashSet<YukiHookedMember>().also {
val allMethodsName = "$hookClass$methodName" val allMethodsName = "$hookClass$methodName"
if (hookedAllMethods.contains(allMethodsName)) { if (hookedAllMethods.contains(allMethodsName)) {
isAlreadyHook = true isAlreadyHook = true
hookClass?.allMethods { _, method -> if (method.name == methodName) it.add(method) } hookedAllMethods[allMethodsName]?.forEach { e -> it.add(e) }
return@also return@also
} }
hookedAllMethods.add(allMethodsName) XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { e -> e.isNotEmpty() }
XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { it.isNotEmpty() } ?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allMethodsName)) }
?.forEach { e -> it.add(e.hookedMethod) } hookedAllMethods[allMethodsName] = it
} }
return Pair(hookedMembers, isAlreadyHook) return Pair(hookedMembers, isAlreadyHook)
} }
@@ -533,20 +539,20 @@ object YukiHookBridge {
* 对接 [XposedBridge.hookAllConstructors] * 对接 [XposedBridge.hookAllConstructors]
* @param hookClass 当前 Hook 的 [Class] * @param hookClass 当前 Hook 的 [Class]
* @param callback 回调 * @param callback 回调
* @return [Pair] - ([HashSet] 成功 Hook 的构造方法数组,[Boolean] 是否已经 Hook) * @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook)
*/ */
internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair<HashSet<Member>, Boolean> { internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair<HashSet<YukiHookedMember>, Boolean> {
var isAlreadyHook = false var isAlreadyHook = false
val hookedMembers = HashSet<Member>().also { val hookedMembers = HashSet<YukiHookedMember>().also {
val allConstructorsName = "$hookClass<init>" val allConstructorsName = "$hookClass<init>"
if (hookedAllConstructors.contains(allConstructorsName)) { if (hookedAllConstructors.contains(allConstructorsName)) {
isAlreadyHook = true isAlreadyHook = true
hookClass?.allConstructors { _, constructor -> it.add(constructor) } hookedAllConstructors[allConstructorsName]?.forEach { e -> it.add(e) }
return@also return@also
} }
hookedAllConstructors.add(allConstructorsName) XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { e -> e.isNotEmpty() }
XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { it.isNotEmpty() } ?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allConstructorsName)) }
?.forEach { e -> it.add(e.hookedMethod) } hookedAllConstructors[allConstructorsName] = it
} }
return Pair(hookedMembers, isAlreadyHook) return Pair(hookedMembers, isAlreadyHook)
} }
@@ -626,5 +632,41 @@ object YukiHookBridge {
* @param priority Hook 优先级 * @param priority Hook 优先级
*/ */
internal abstract class YukiHookCallback(open val priority: Int) 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() {
instance.unhook()
runCatching {
if (tag.isNotBlank()) {
hookedAllMethods.remove(tag)
hookedAllConstructors.remove(tag)
} else hookedMembers.remove(this)
}
}
}
} }
} }