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

View File

@@ -302,6 +302,48 @@ loadZygote {
更多功能请参考 [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 进程“死掉”。

View File

@@ -139,11 +139,12 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
* @param packageName 当前 Hook 的 APP 包名
*/
inner class MemberHookCreater @PublishedApi internal constructor(
private val priority: Int,
internal val tag: String,
internal val packageName: String
private val priority: Int, internal val tag: String, internal val packageName: String
) {
/** Hook 结果实例 */
private var result: Result? = null
/** 是否已经执行 Hook */
private var isHooked = false
@@ -206,6 +207,9 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
/** 全部方法的名称 */
private var allMethodsName = ""
/** 当前被 Hook 的方法、构造方法实例 */
private var hookedMember: YukiHookBridge.Hooker.YukiHookedMember? = null
/**
* 手动指定要 Hook 的方法、构造方法
*
@@ -421,12 +425,20 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
replaceHookResult = null
}
/**
* 移除当前注入的 Hook 方法、构造方法 (解除 Hook)
*
* - ❗你只能在 Hook 回调方法中使用此功能
* @param result 回调是否成功
*/
fun removeSelf(result: (Boolean) -> Unit = {}) = this.result?.remove(result) ?: result(false)
/**
* Hook 创建入口
* @return [Result]
*/
@PublishedApi
internal fun build() = Result()
internal fun build() = Result().apply { result = this }
/** Hook 执行入口 */
@PublishedApi
@@ -508,9 +520,12 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent)
else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also {
when {
it.first == null -> error("Hook Member [$member] failed")
it.second -> onAlreadyHookedCallback?.invoke(it.first!!)
else -> onHookedCallback?.invoke(it.first!!)
it.first.member == null -> error("Hook Member [$member] failed")
it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!)
else -> {
hookedMember = it.first
onHookedCallback?.invoke(it.first.member!!)
}
}
}
}.onFailure {
@@ -537,8 +552,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also {
when {
it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
it.second -> it.first.forEach { e -> onAlreadyHookedCallback?.invoke(e) }
else -> it.first.forEach { e -> onHookedCallback?.invoke(e) }
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
else -> {
hookedMember = it.first.first()
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
}
}
}
HookMemberMode.HOOK_ALL_CONSTRUCTORS ->
@@ -546,8 +564,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also {
when {
it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched")
it.second -> it.first.forEach { e -> onAlreadyHookedCallback?.invoke(e) }
else -> it.first.forEach { e -> onHookedCallback?.invoke(e) }
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
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")
@@ -707,6 +728,19 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
* @return [Result] 可继续向下监听
*/
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 com.highcapable.yukihookapi.YukiHookAPI
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.param.PackageParam
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
@@ -441,13 +439,13 @@ object YukiHookBridge {
internal object Hooker {
/** 已经 Hook 的 [Member] 数组 */
private val hookedMembers = HashSet<String>()
private val hookedMembers = HashSet<YukiHookedMember>()
/** 已经 Hook 的全部 [Method] 数组 */
private val hookedAllMethods = HashSet<String>()
private val hookedAllMethods = HashMap<String, HashSet<YukiHookedMember>>()
/** 已经 Hook 的全部 [Constructor] 数组 */
private val hookedAllConstructors = HashSet<String>()
private val hookedAllConstructors = HashMap<String, HashSet<YukiHookedMember>>()
/** 默认 Hook 回调优先级 */
internal const val PRIORITY_DEFAULT = 50
@@ -494,12 +492,18 @@ object YukiHookBridge {
* 对接 [XposedBridge.hookMethod]
* @param hookMethod 需要 Hook 的方法、构造方法
* @param callback 回调
* @return [Pair] - ([Member] or null,[Boolean] 是否已经 Hook)
* @return [Pair] - ([YukiHookedMember],[Boolean] 是否已经 Hook)
*/
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<Member?, Boolean> {
if (hookedMembers.contains(hookMethod.toString())) return Pair(hookMethod, true)
hookedMembers.add(hookMethod.toString())
return Pair(XposedBridge.hookMethod(hookMethod, compatCallback(callback))?.hookedMethod, false)
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<YukiHookedMember, Boolean> {
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)
}
}
/**
@@ -509,20 +513,22 @@ object YukiHookBridge {
* @param hookClass 当前 Hook 的 [Class]
* @param methodName 方法名
* @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
val hookedMembers = HashSet<Member>().also {
val hookedMembers = HashSet<YukiHookedMember>().also {
val allMethodsName = "$hookClass$methodName"
if (hookedAllMethods.contains(allMethodsName)) {
isAlreadyHook = true
hookClass?.allMethods { _, method -> if (method.name == methodName) it.add(method) }
hookedAllMethods[allMethodsName]?.forEach { e -> it.add(e) }
return@also
}
hookedAllMethods.add(allMethodsName)
XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { it.isNotEmpty() }
?.forEach { e -> it.add(e.hookedMethod) }
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)
}
@@ -533,20 +539,20 @@ object YukiHookBridge {
* 对接 [XposedBridge.hookAllConstructors]
* @param hookClass 当前 Hook 的 [Class]
* @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
val hookedMembers = HashSet<Member>().also {
val hookedMembers = HashSet<YukiHookedMember>().also {
val allConstructorsName = "$hookClass<init>"
if (hookedAllConstructors.contains(allConstructorsName)) {
isAlreadyHook = true
hookClass?.allConstructors { _, constructor -> it.add(constructor) }
hookedAllConstructors[allConstructorsName]?.forEach { e -> it.add(e) }
return@also
}
hookedAllConstructors.add(allConstructorsName)
XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { it.isNotEmpty() }
?.forEach { e -> it.add(e.hookedMethod) }
XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { e -> e.isNotEmpty() }
?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allConstructorsName)) }
hookedAllConstructors[allConstructorsName] = it
}
return Pair(hookedMembers, isAlreadyHook)
}
@@ -626,5 +632,41 @@ object YukiHookBridge {
* @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() {
instance.unhook()
runCatching {
if (tag.isNotBlank()) {
hookedAllMethods.remove(tag)
hookedAllConstructors.remove(tag)
} else hookedMembers.remove(this)
}
}
}
}
}