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