diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt index 581514f4..a139e8fb 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt @@ -50,6 +50,7 @@ class MainInjecter : YukiHookXposedInitProxy { override fun onHook() { // 设置模式 + YukiHookAPI.debugTag = "YukiSuki" YukiHookAPI.isDebug = true // 方案 1 // encase(MainHooker(), SecondHooker()) @@ -80,11 +81,11 @@ class MainInjecter : YukiHookXposedInitProxy { method { name = "a" param(StringType, StringType) - returnType = StringType + returnType = UnitType } beforeHook { - args(index = 0).set("改了前面的") - args(index = 1).set("改了后面的") + args(index = 0).set("✌️改了前面的") + args(index = 1).set("✌️改了后面的") } } injectMember { @@ -141,11 +142,9 @@ class MainInjecter : YukiHookXposedInitProxy { }.failures { onConductFailure { _, _ -> } onHookingFailure {} - onNoSuchMemberFailure {} onAllFailure {} ignoredConductFailure() ignoredHookingFailure() - ignoredNoSuchMemberFailure() ignoredAllFailure() } } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index a7ee8aef..b6fcbf3b 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -49,15 +49,23 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage @Keep object YukiHookAPI { - /** 全局标识 */ - const val TAG = "YukiHookAPI" + /** + * 这是一个调试日志的全局标识 + * + * 默认文案为 YukiHookAPI + * + * 你可以修改为你自己的文案 + */ + var debugTag = "YukiHookAPI" /** * 是否开启调试模式 - 默认启用 * * 启用后将交由日志输出管理器打印详细 Hook 日志到控制台 * - * 请过滤 [TAG] (YukiHookAPI) 即可找到每条日志 + * 关闭后将只输出 Error 级别的日志 + * + * 请过滤 [debugTag] 即可找到每条日志 */ var isDebug = true diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt index 89697599..16fe7c45 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt @@ -38,7 +38,6 @@ import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.param.HookParam import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.utils.runBlocking import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XC_MethodReplacement import de.robv.android.xposed.XposedBridge @@ -59,15 +58,17 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla /** * 注入要 Hook 的方法、构造类 + * @param tag 可设置标签 - 在发生错误时方便进行调试 * @param initiate 方法体 */ - fun injectMember(initiate: MemberHookCreater.() -> Unit) = - MemberHookCreater().apply(initiate).apply { + fun injectMember(tag: String = "Default", initiate: MemberHookCreater.() -> Unit) = + MemberHookCreater(tag).apply(initiate).apply { hookMembers[toString()] = this - }.create() + }.build() /** - * Hook 执行入口 - 不可在外部调用 + * Hook 执行入口 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 * @throws IllegalStateException 如果必要参数没有被设置 */ @DoNotUseMethod @@ -80,8 +81,9 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * 智能全局方法、构造类查找类实现方法 * * 处理需要 Hook 的方法 + * @param tag 当前设置的标签 */ - inner class MemberHookCreater { + inner class MemberHookCreater(var tag: String) { /** [beforeHook] 回调 */ private var beforeHookCallback: (HookParam.() -> Unit)? = null @@ -98,17 +100,14 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla /** Hook 开始时出现错误回调 */ private var onHookingFailureCallback: ((Throwable) -> Unit)? = null - /** 当找不到方法、变量时错误回调 */ - private var onNoSuchMemberCallback: ((Throwable) -> Unit)? = null - /** 全部错误回调 */ private var onAllFailureCallback: ((Throwable) -> Unit)? = null /** 是否为替换 Hook 模式 */ private var isReplaceHookMode = false - /** 是否停止 Hook */ - private var isStopHookMode = false + /** 标识是否已经设置了要 Hook 的 [member] */ + private var isHookMemberSetup = false /** * 手动指定要 Hook 的方法、构造类 @@ -122,17 +121,12 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * * 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换 * @param initiate 方法体 + * @return [MethodFinder.Result] */ - fun method(initiate: MethodFinder.() -> Unit) = runBlocking { - runCatching { - member = MethodFinder(hookClass).apply(initiate).find() - }.onFailure { - isStopHookMode = true - onNoSuchMemberCallback?.invoke(it) - onAllFailureCallback?.invoke(it) - if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(it) - } - }.result { onHookLogMsg(msg = "Find Method [$member] takes ${it}ms") } + fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result { + isHookMemberSetup = true + return MethodFinder(hookInstance = this, hookClass).apply(initiate).build() + } /** * 查找需要 Hook 的构造类 @@ -140,16 +134,10 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换 * @param initiate 方法体 */ - fun constructor(initiate: ConstructorFinder.() -> Unit) = runBlocking { - runCatching { - member = ConstructorFinder(hookClass).apply(initiate).find() - }.onFailure { - isStopHookMode = true - onNoSuchMemberCallback?.invoke(it) - onAllFailureCallback?.invoke(it) - if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(it) - } - }.result { onHookLogMsg(msg = "Find Constructor [$member] takes ${it}ms") } + fun constructor(initiate: ConstructorFinder.() -> Unit): ConstructorFinder.Result { + isHookMemberSetup = true + return ConstructorFinder(hookInstance = this, hookClass).apply(initiate).build() + } /** * 查找 [Field] @@ -157,19 +145,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * @return [FieldFinder.Result] */ fun HookParam.field(initiate: FieldFinder.() -> Unit) = - try { - var result: FieldFinder.Result? = null - runBlocking { - result = FieldFinder(hookClass).apply(initiate).find() - }.result { onHookLogMsg(msg = "Find Field [${result?.give()}] takes ${it}ms") } - result!! - } catch (e: Throwable) { - isStopHookMode = true - onNoSuchMemberCallback?.invoke(e) - onAllFailureCallback?.invoke(e) - if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(e) - FieldFinder(hookClass).Result() - } + FieldFinder(hookInstance = this@MemberHookCreater, hookClass).apply(initiate).build() /** * 在方法执行完成前 Hook @@ -263,80 +239,99 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla } /** - * Hook 创建入口 - 不可在外部调用 + * Hook 创建入口 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 * @return [Result] */ @DoNotUseMethod - fun create() = Result() + fun build() = Result() /** - * Hook 执行入口 - 不可在外部调用 - * @throws IllegalStateException 如果必要参数没有被设置 + * Hook 执行入口 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 */ @DoNotUseMethod fun hook() { - if (isStopHookMode) return - member?.also { member -> - runCatching { - if (isReplaceHookMode) - XposedBridge.hookMethod(member, object : XC_MethodReplacement() { - override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? { - if (baseParam == null) return null - return HookParam(baseParam).let { param -> - try { - if (replaceHookCallback != null) - onHookLogMsg(msg = "Replace Hook Member [${member}] done") - replaceHookCallback?.invoke(param) - } catch (e: Throwable) { - onConductFailureCallback?.invoke(param, e) - onAllFailureCallback?.invoke(e) - if (onConductFailureCallback == null && onAllFailureCallback == null) - onHookFailureMsg(e) - null + if (member != null) + member.also { member -> + runCatching { + if (isReplaceHookMode) + XposedBridge.hookMethod(member, object : XC_MethodReplacement() { + override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? { + if (baseParam == null) return null + return HookParam(baseParam).let { param -> + try { + if (replaceHookCallback != null) + onHookLogMsg(msg = "Replace Hook Member [${member}] done [$tag]") + replaceHookCallback?.invoke(param) + } catch (e: Throwable) { + onConductFailureCallback?.invoke(param, e) + onAllFailureCallback?.invoke(e) + if (onConductFailureCallback == null && onAllFailureCallback == null) + onHookFailureMsg(e) + null + } } } - } - }) - else - XposedBridge.hookMethod(member, object : XC_MethodHook() { - override fun beforeHookedMethod(baseParam: MethodHookParam?) { - if (baseParam == null) return - HookParam(baseParam).also { param -> - runCatching { - beforeHookCallback?.invoke(param) - if (beforeHookCallback != null) - onHookLogMsg(msg = "Before Hook Member [${member}] done") - }.onFailure { - onConductFailureCallback?.invoke(param, it) - onAllFailureCallback?.invoke(it) - if (onConductFailureCallback == null && onAllFailureCallback == null) - onHookFailureMsg(it) + }) + else + XposedBridge.hookMethod(member, object : XC_MethodHook() { + override fun beforeHookedMethod(baseParam: MethodHookParam?) { + if (baseParam == null) return + HookParam(baseParam).also { param -> + runCatching { + beforeHookCallback?.invoke(param) + if (beforeHookCallback != null) + onHookLogMsg(msg = "Before Hook Member [${member}] done [$tag]") + }.onFailure { + onConductFailureCallback?.invoke(param, it) + onAllFailureCallback?.invoke(it) + if (onConductFailureCallback == null && onAllFailureCallback == null) + onHookFailureMsg(it) + } } } - } - override fun afterHookedMethod(baseParam: MethodHookParam?) { - if (baseParam == null) return - HookParam(baseParam).also { param -> - runCatching { - afterHookCallback?.invoke(param) - if (afterHookCallback != null) - onHookLogMsg(msg = "After Hook Member [${member}] done") - }.onFailure { - onConductFailureCallback?.invoke(param, it) - onAllFailureCallback?.invoke(it) - if (onConductFailureCallback == null && onAllFailureCallback == null) - onHookFailureMsg(it) + override fun afterHookedMethod(baseParam: MethodHookParam?) { + if (baseParam == null) return + HookParam(baseParam).also { param -> + runCatching { + afterHookCallback?.invoke(param) + if (afterHookCallback != null) + onHookLogMsg(msg = "After Hook Member [${member}] done [$tag]") + }.onFailure { + onConductFailureCallback?.invoke(param, it) + onAllFailureCallback?.invoke(it) + if (onConductFailureCallback == null && onAllFailureCallback == null) + onHookFailureMsg(it) + } } } - } - }) - }.onFailure { - onHookingFailureCallback?.invoke(it) - onAllFailureCallback?.invoke(it) - if (onHookingFailureCallback == null && onAllFailureCallback == null) onHookFailureMsg(it) + }) + }.onFailure { + onHookingFailureCallback?.invoke(it) + onAllFailureCallback?.invoke(it) + if (onHookingFailureCallback == null && onAllFailureCallback == null) onHookFailureMsg(it) + } } - } ?: error("Hook Member cannot be null") + else { + onHookingFailureCallback?.invoke(Throwable()) + onAllFailureCallback?.invoke(Throwable()) + if (onHookingFailureCallback == null && onAllFailureCallback == null) + loggerE( + msg = if (isHookMemberSetup) + "Hooked Member with a finding error in Class [$hookClass] [$tag]" + else "Hooked Member cannot be non-null in Class [$hookClass] [$tag]" + ) + } + } + + /** + * Hook 过程中开启了 [YukiHookAPI.isDebug] 输出调试信息 + * @param msg 调试日志内容 + */ + internal fun onHookLogMsg(msg: String) { + if (YukiHookAPI.isDebug) loggerI(msg = msg) } /** @@ -344,17 +339,9 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * @param throwable 异常信息 */ private fun onHookFailureMsg(throwable: Throwable) = - loggerE(msg = "Try to hook $hookClass[$member] got an Exception", e = throwable) + loggerE(msg = "Try to hook $hookClass[$member] got an Exception [$tag]", e = throwable) - /** - * Hook 过程中开启了 [YukiHookAPI.isDebug] 输出调试信息 - * @param msg 调试日志内容 - */ - private fun onHookLogMsg(msg: String) { - if (YukiHookAPI.isDebug) loggerI(msg = msg) - } - - override fun toString() = "$member#YukiHook" + override fun toString() = "$member$tag#YukiHook" /** * 监听 Hook 结果实现类 @@ -388,7 +375,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla /** * 监听 Hook 开始时发生错误的回调方法 - * @param initiate 回调错误 - ([Throwable] 异常) + * @param initiate 回调错误 * @return [Result] 可继续向下监听 */ fun onHookingFailure(initiate: (Throwable) -> Unit): Result { @@ -402,25 +389,9 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla */ fun ignoredHookingFailure() = onHookingFailure {} - /** - * 监听 Hook 过程发生找不到方法、变量错误的回调方法 - * @param initiate 回调错误 - ([Throwable] 异常) - * @return [Result] 可继续向下监听 - */ - fun onNoSuchMemberFailure(initiate: (Throwable) -> Unit): Result { - onNoSuchMemberCallback = initiate - return this - } - - /** - * 忽略 Hook 过程发生找不到方法、变量的错误 - * @return [Result] 可继续向下监听 - */ - fun ignoredNoSuchMemberFailure() = onNoSuchMemberFailure {} - /** * 监听全部 Hook 过程发生错误的回调方法 - * @param initiate 回调错误 - ([Throwable] 异常) + * @param initiate 回调错误 * @return [Result] 可继续向下监听 */ fun onAllFailure(initiate: (Throwable) -> Unit): Result { diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt index f1c0f260..9d1ed721 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt @@ -25,27 +25,35 @@ * * This file is Created by fankes on 2022/2/4. */ -@file:Suppress("unused") +@file:Suppress("unused", "EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate") package com.highcapable.yukihookapi.hook.core.finder import com.highcapable.yukihookapi.annotation.DoNotUseMethod +import com.highcapable.yukihookapi.hook.core.YukiHookCreater +import com.highcapable.yukihookapi.hook.log.loggerE +import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.utils.ReflectionUtils +import com.highcapable.yukihookapi.hook.utils.runBlocking import java.lang.reflect.Constructor /** * [Constructor] 查找类 * - * 可通过指定类型查找指定构造类 + * 可通过指定类型查找指定构造方法 + * @param hookInstance 当前 Hook 实例 * @param hookClass 当前被 Hook 的 [Class] */ -class ConstructorFinder(private val hookClass: Class<*>) { +class ConstructorFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { - /** 方法参数 */ + /** 失败尝试次数数组 */ + private val remedyPlan = HashMap() + + /** 构造方法参数 */ private var params: Array>? = null /** - * 方法参数 + * 构造方法参数 * @param param 参数数组 */ fun param(vararg param: Class<*>) { @@ -53,13 +61,135 @@ class ConstructorFinder(private val hookClass: Class<*>) { } /** - * 得到构造类 - 不能在外部调用 + * 得到构造方法 * @return [Constructor] - * @throws NoSuchMethodError 如果找不到构造类 + * @throws NoSuchMethodError 如果找不到构造方法 */ - @DoNotUseMethod - fun find(): Constructor<*> = - if (params != null) + private val result + get() = if (params != null) ReflectionUtils.findConstructorExact(hookClass, *params!!) else ReflectionUtils.findConstructorExact(hookClass) + + /** + * 得到构造方法结果 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 + * @return [Result] + */ + @DoNotUseMethod + fun build() = try { + runBlocking { + hookInstance.member = result + }.result { + hookInstance.onHookLogMsg(msg = "Find Constructor [${hookInstance.member}] takes ${it}ms [${hookInstance.tag}]") + } + Result() + } catch (e: Throwable) { + onFailureMsg(throwable = e) + Result(isNoSuch = true, e) + } + + /** + * 发生错误时输出日志 + * @param msg 消息日志 + * @param throwable 错误 + */ + private fun onFailureMsg(msg: String = "", throwable: Throwable? = null) = + loggerE(msg = "NoSuchConstructor happend in [$hookClass] $msg [${hookInstance.tag}]", e = throwable) + + /** + * [Constructor] 重查找实现类 + * + * 可累计失败次数直到查找成功 + */ + inner class RemedyPlan { + + /** 失败尝试次数数组 */ + private val remedyPlan = HashSet() + + /** + * 创建需要重新查找的 [Constructor] + * + * 你可以添加多个备选构造方法 - 直到成功为止 + * + * 若最后依然失败 - 将停止查找并输出错误日志 + * @param initiate 方法体 + */ + fun constructor(initiate: ConstructorFinder.() -> Unit) = + remedyPlan.add(ConstructorFinder(hookInstance, hookClass).apply(initiate)) + + /** + * 开始重查找 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 + */ + @DoNotUseMethod + internal fun build() { + if (remedyPlan.isNotEmpty()) run { + var isFindSuccess = false + var lastError: Throwable? = null + remedyPlan.forEachIndexed { p, it -> + runCatching { + runBlocking { + hookInstance.member = it.result + }.result { + hookInstance.onHookLogMsg(msg = "Find Constructor [${hookInstance.member}] takes ${it}ms [${hookInstance.tag}]") + } + isFindSuccess = true + hookInstance.onHookLogMsg(msg = "Constructor [${hookInstance.member}] trying ${p + 1} times success by RemedyPlan [${hookInstance.tag}]") + return@run + }.onFailure { + lastError = it + onFailureMsg(msg = "trying ${p + 1} times by RemedyPlan --> $it") + } + } + if (!isFindSuccess) { + onFailureMsg( + msg = "trying ${remedyPlan.size} times and all failure by RemedyPlan", + throwable = lastError + ) + remedyPlan.clear() + } + } else loggerW(msg = "RemedyPlan is empty,forgot it? [${hookInstance.tag}]") + } + } + + /** + * [Constructor] 查找结果实现类 + * @param isNoSuch 是否没有找到构造方法 - 默认否 + * @param e 错误信息 + */ + inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) { + + /** + * 创建监听结果事件方法体 + * @param initiate 方法体 + * @return [Result] 可继续向下监听 + */ + fun result(initiate: Result.() -> Unit) = apply(initiate) + + /** + * 创建构造方法重查找功能 + * + * 当你遇到一种构造方法可能存在不同形式的存在时 + * + * 可以使用 [RemedyPlan] 重新查找它 - 而没有必要使用 [onNoSuchConstructor] 捕获异常二次查找构造方法 + * @param initiate 方法体 + * @return [Result] 可继续向下监听 + */ + fun remedys(initiate: RemedyPlan.() -> Unit): Result { + if (isNoSuch) RemedyPlan().apply(initiate).build() + return this + } + + /** + * 监听找不到构造方法时 + * + * 只会返回第一次的错误信息 - 不会返回 [RemedyPlan] 的错误信息 + * @param initiate 回调错误 + * @return [Result] 可继续向下监听 + */ + fun onNoSuchConstructor(initiate: (Throwable) -> Unit): Result { + if (isNoSuch) initiate(e ?: Throwable()) + return this + } + } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt index b876db18..4c12e9f9 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt @@ -30,16 +30,20 @@ package com.highcapable.yukihookapi.hook.core.finder import com.highcapable.yukihookapi.annotation.DoNotUseMethod +import com.highcapable.yukihookapi.hook.core.YukiHookCreater +import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.utils.ReflectionUtils +import com.highcapable.yukihookapi.hook.utils.runBlocking import java.lang.reflect.Field /** * Field 查找结果实现类 * * 可在这里处理找到的 [fieldInstance] + * @param hookInstance 当前 Hook 实例 * @param hookClass 当前被 Hook 的 [Class] */ -class FieldFinder(private val hookClass: Class<*>) { +class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { /** 当前找到的 [Field] */ private var fieldInstance: Field? = null @@ -51,25 +55,42 @@ class FieldFinder(private val hookClass: Class<*>) { var type: Class<*>? = null /** - * 得到变量处理结果 - 不能在外部调用 + * 得到变量处理结果 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 * @return [Result] - * @throws NoSuchFieldError 如果找不到变量 + * @throws IllegalStateException 如果 [name] 没有被设置 */ @DoNotUseMethod - fun find(): Result { - fieldInstance = when { - name.isBlank() -> error("Field name cannot be empty") - else -> ReflectionUtils.findFieldIfExists(hookClass, type?.name, name) + fun build() = if (name.isBlank()) { + loggerE(msg = "Field name cannot be empty in Class [$hookClass] [${hookInstance.tag}]") + Result(isNoSuch = true) + } else try { + runBlocking { + fieldInstance = ReflectionUtils.findFieldIfExists(hookClass, type?.name, name) + }.result { + hookInstance.onHookLogMsg(msg = "Find Field [${fieldInstance}] takes ${it}ms [${hookInstance.tag}]") } - return Result() + Result() + } catch (e: Throwable) { + loggerE(msg = "NoSuchField happend in [$hookClass] [${hookInstance.tag}]", e = e) + Result(isNoSuch = true, e) } /** * Field 查找结果实现类 * * 可在这里处理找到的 [fieldInstance] + * @param isNoSuch 是否没有找到变量 - 默认否 + * @param e 错误信息 */ - inner class Result { + inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) { + + /** + * 创建监听结果事件方法体 + * @param initiate 方法体 + * @return [Result] 可继续向下监听 + */ + fun result(initiate: Result.() -> Unit) = apply(initiate) /** * 设置变量实例 @@ -90,5 +111,17 @@ class FieldFinder(private val hookClass: Class<*>) { * @return [Field] or null */ fun give() = fieldInstance + + /** + * 监听找不到变量时 + * + * 只会返回第一次和最后一次的错误信息 + * @param initiate 回调错误 + * @return [Result] 可继续向下监听 + */ + fun onNoSuchField(initiate: (Throwable) -> Unit): Result { + if (isNoSuch) initiate(e ?: Throwable()) + return this + } } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt index 22b04d61..f1d1b651 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt @@ -25,21 +25,26 @@ * * This file is Created by fankes on 2022/2/4. */ -@file:Suppress("unused", "MemberVisibilityCanBePrivate") +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_API_USAGE") package com.highcapable.yukihookapi.hook.core.finder import com.highcapable.yukihookapi.annotation.DoNotUseMethod +import com.highcapable.yukihookapi.hook.core.YukiHookCreater +import com.highcapable.yukihookapi.hook.log.loggerE +import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.utils.ReflectionUtils +import com.highcapable.yukihookapi.hook.utils.runBlocking import java.lang.reflect.Method /** * [Method] 查找类 * * 可通过指定类型查找指定方法 - * @param hookClass 当前被 Hook 的 [Class] + * @param hookInstance 当前 Hook 实例 + * @param hookClass 当前 Hook 的 Class */ -class MethodFinder(private val hookClass: Class<*>) { +class MethodFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { /** 方法参数 */ private var params: Array>? = null @@ -59,17 +64,140 @@ class MethodFinder(private val hookClass: Class<*>) { } /** - * 得到方法 - 不能在外部调用 + * 得到方法 * @return [Method] + * @throws IllegalStateException 如果 [name] 未设置 * @throws NoSuchMethodError 如果找不到方法 */ + private val result + get() = if (params != null) + ReflectionUtils.findMethodBestMatch(hookClass, returnType, name, *params!!) + else ReflectionUtils.findMethodNoParam(hookClass, returnType, name) + + /** + * 得到方法结果 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 + * @return [Result] + */ @DoNotUseMethod - fun find(): Method = - when { - name.isBlank() -> error("Method name cannot be empty") - else -> - if (params != null) - ReflectionUtils.findMethodBestMatch(hookClass, returnType, name, *params!!) - else ReflectionUtils.findMethodNoParam(hookClass, returnType, name) + fun build() = if (name.isBlank()) { + loggerE(msg = "Method name cannot be empty in Class [$hookClass] [${hookInstance.tag}]") + Result(isNoSuch = true) + } else try { + runBlocking { + hookInstance.member = result + }.result { + hookInstance.onHookLogMsg(msg = "Find Method [${hookInstance.member}] takes ${it}ms [${hookInstance.tag}]") } + Result() + } catch (e: Throwable) { + onFailureMsg(throwable = e) + Result(isNoSuch = true, e) + } + + /** + * 发生错误时输出日志 + * @param msg 消息日志 + * @param throwable 错误 + */ + private fun onFailureMsg(msg: String = "", throwable: Throwable? = null) = + loggerE(msg = "NoSuchMethod happend in [$hookClass] $msg [${hookInstance.tag}]", e = throwable) + + /** + * [Method] 重查找实现类 + * + * 可累计失败次数直到查找成功 + */ + inner class RemedyPlan { + + /** 失败尝试次数数组 */ + private val remedyPlan = HashSet() + + /** + * 创建需要重新查找的 [Method] + * + * 你可以添加多个备选方法 - 直到成功为止 + * + * 若最后依然失败 - 将停止查找并输出错误日志 + * @param initiate 方法体 + */ + fun method(initiate: MethodFinder.() -> Unit) = remedyPlan.add(MethodFinder(hookInstance, hookClass).apply(initiate)) + + /** + * 开始重查找 + * - 此功能交由方法体自动完成 - 你不应该手动调用此方法 + */ + @DoNotUseMethod + internal fun build() { + if (remedyPlan.isNotEmpty()) run { + var isFindSuccess = false + var lastError: Throwable? = null + remedyPlan.forEachIndexed { p, it -> + runCatching { + runBlocking { + hookInstance.member = it.result + }.result { + hookInstance.onHookLogMsg(msg = "Find Method [${hookInstance.member}] takes ${it}ms [${hookInstance.tag}]") + } + isFindSuccess = true + hookInstance.onHookLogMsg(msg = "Method [${hookInstance.member}] trying ${p + 1} times success by RemedyPlan [${hookInstance.tag}]") + return@run + }.onFailure { + lastError = it + onFailureMsg(msg = "trying ${p + 1} times by RemedyPlan --> $it") + } + } + if (!isFindSuccess) { + onFailureMsg( + msg = "trying ${remedyPlan.size} times and all failure by RemedyPlan", + throwable = lastError + ) + remedyPlan.clear() + } + } else loggerW(msg = "RemedyPlan is empty,forgot it? [${hookInstance.tag}]") + } + } + + /** + * [Method] 查找结果实现类 + * @param isNoSuch 是否没有找到方法 - 默认否 + * @param e 错误信息 + */ + inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) { + + /** + * 创建监听结果事件方法体 + * @param initiate 方法体 + * @return [Result] 可继续向下监听 + */ + fun result(initiate: Result.() -> Unit) = apply(initiate) + + /** + * 创建方法重查找功能 + * + * 当你遇到一种方法可能存在不同形式的存在时 + * + * 可以使用 [RemedyPlan] 重新查找它 - 而没有必要使用 [onNoSuchMethod] 捕获异常二次查找方法 + * + * 若第一次查找失败了 - 你还可以在这里继续添加此方法体直到成功为止 + * @param initiate 方法体 + * @return [Result] 可继续向下监听 + */ + fun remedys(initiate: RemedyPlan.() -> Unit): Result { + if (isNoSuch) RemedyPlan().apply(initiate).build() + return this + } + + /** + * 监听找不到方法时 + * + * 只会返回第一次的错误信息 - 不会返回 [RemedyPlan] 的错误信息 + * @param initiate 回调错误 + * @return [Result] 可继续向下监听 + */ + fun onNoSuchMethod(initiate: (Throwable) -> Unit): Result { + if (isNoSuch) initiate(e ?: Throwable()) + return this + } + } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt index f498ebbf..2a9e82b6 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt @@ -75,6 +75,7 @@ abstract class YukiBaseHooker : PackageParam() { /** * 赋值并克隆一个 [PackageParam] * @param packageParam 需要使用的 [PackageParam] + * - 此方法为私有功能性 API - 你不应该手动调用此方法 */ @DoNotUseMethod internal fun assignInstance(packageParam: PackageParam) { diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt index 0fc1331b..26b403ec 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt @@ -36,13 +36,11 @@ import de.robv.android.xposed.XposedBridge /** * 向控制台和 [XposedBridge] 打印日志 - D * - * 你可以对此方法进行二次封装 - * * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] - * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] + * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag] * @param msg 日志打印的内容 */ -fun loggerD(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { +fun loggerD(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching { Log.d(tag, msg) XposedBridge.log("[$tag][D]--> $msg") } @@ -50,13 +48,11 @@ fun loggerD(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { /** * 向控制台和 [XposedBridge] 打印日志 - I * - * 你可以对此方法进行二次封装 - * * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] - * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] + * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag] * @param msg 日志打印的内容 */ -fun loggerI(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { +fun loggerI(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching { Log.i(tag, msg) XposedBridge.log("[$tag][I]--> $msg") } @@ -64,13 +60,11 @@ fun loggerI(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { /** * 向控制台和 [XposedBridge] 打印日志 - W * - * 你可以对此方法进行二次封装 - * * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] - * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] + * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag] * @param msg 日志打印的内容 */ -fun loggerW(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { +fun loggerW(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching { Log.w(tag, msg) XposedBridge.log("[$tag][W]--> $msg") } @@ -78,14 +72,12 @@ fun loggerW(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { /** * 向控制台和 [XposedBridge] 打印日志 - E * - * 你可以对此方法进行二次封装 - * * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] - * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] + * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag] * @param msg 日志打印的内容 * @param e 可填入异常堆栈信息 - 将自动完整打印到控制台 */ -fun loggerE(tag: String = YukiHookAPI.TAG, msg: String, e: Throwable? = null) = runCatching { +fun loggerE(tag: String = YukiHookAPI.debugTag, msg: String, e: Throwable? = null) = runCatching { Log.e(tag, msg, e) XposedBridge.log("[$tag][E]--> $msg") e?.also { XposedBridge.log(it) } 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 6ec12d2d..d00cca4a 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 @@ -89,6 +89,7 @@ open class PackageParam( /** * 赋值并克隆另一个 [PackageParam] * @param another 另一个 [PackageParam] + * - 此方法为私有功能性 API - 你不应该手动调用此方法 */ @DoNotUseMethod internal fun baseAssignInstance(another: PackageParam) { diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt index 131a5c43..bbe65679 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt @@ -35,7 +35,7 @@ import java.io.Serializable * 获得 [Any] 类型 * @return [Class] */ -val AnyType get() = Any::class.java +val AnyType get() = Any::class.javaPrimitiveType /** * 获得 [Unit] 类型 @@ -47,7 +47,7 @@ val UnitType get() = Unit::class.javaPrimitiveType * 获得 [Boolean] 类型 * @return [Class] */ -val BooleanType get() = Boolean::class.javaPrimitiveType +val BooleanType get() = Boolean::class.java /** * 获得 [Int] 类型 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java index 2e201185..25ff11d7 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java @@ -107,18 +107,8 @@ public class ReflectionUtils { return false; } - public static Field findField(Class clazz, String typeName, String fieldName) { - try { - return findFieldIfExists(clazz, typeName, fieldName); - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Can't find field <" + clazz.getName() + "#" + typeName + "#" + fieldName + ">"); - } - } - - public static Field findFieldIfExists(Class clazz, String typeName, String fieldName) - throws NoSuchFieldException { - String fullFieldName = clazz.getName() + "#" + fieldName + "#" + typeName; - + public static Field findFieldIfExists(Class clazz, String typeName, String fieldName) throws NoSuchFieldException { + String fullFieldName = "name:[" + fieldName + "] type:[" + typeName + "] in Class [" + clazz.getName() + "] by YukiHook#finder"; if (!fieldCache.containsKey(fullFieldName)) { if (clazz != null && !TextUtils.isEmpty(typeName) && !TextUtils.isEmpty(fieldName)) { Class clz = clazz; @@ -134,7 +124,7 @@ public class ReflectionUtils { } } } while ((clz = clz.getSuperclass()) != null); - throw new NoSuchFieldException(clazz.getName() + "#" + typeName + " " + fieldName); + throw new NoSuchFieldException("Can't find this field --> " + fullFieldName); } return null; } else {