mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 09:45:19 +08:00
Added unhook function in YukiHookBridge and YukiMemberHookCreater
This commit is contained in:
@@ -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
|
||||
|
@@ -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 进程“死掉”。
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user