This commit is contained in:
2022-02-06 02:58:26 +08:00
parent 7fcfdb3bd4
commit 64243d4164
11 changed files with 452 additions and 199 deletions

View File

@@ -50,6 +50,7 @@ class MainInjecter : YukiHookXposedInitProxy {
override fun onHook() { override fun onHook() {
// 设置模式 // 设置模式
YukiHookAPI.debugTag = "YukiSuki"
YukiHookAPI.isDebug = true YukiHookAPI.isDebug = true
// 方案 1 // 方案 1
// encase(MainHooker(), SecondHooker()) // encase(MainHooker(), SecondHooker())
@@ -80,11 +81,11 @@ class MainInjecter : YukiHookXposedInitProxy {
method { method {
name = "a" name = "a"
param(StringType, StringType) param(StringType, StringType)
returnType = StringType returnType = UnitType
} }
beforeHook { beforeHook {
args(index = 0).set("改了前面的") args(index = 0).set("✌️改了前面的")
args(index = 1).set("改了后面的") args(index = 1).set("✌️改了后面的")
} }
} }
injectMember { injectMember {
@@ -141,11 +142,9 @@ class MainInjecter : YukiHookXposedInitProxy {
}.failures { }.failures {
onConductFailure { _, _ -> } onConductFailure { _, _ -> }
onHookingFailure {} onHookingFailure {}
onNoSuchMemberFailure {}
onAllFailure {} onAllFailure {}
ignoredConductFailure() ignoredConductFailure()
ignoredHookingFailure() ignoredHookingFailure()
ignoredNoSuchMemberFailure()
ignoredAllFailure() ignoredAllFailure()
} }
} }

View File

@@ -49,15 +49,23 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage
@Keep @Keep
object YukiHookAPI { object YukiHookAPI {
/** 全局标识 */ /**
const val TAG = "YukiHookAPI" * 这是一个调试日志的全局标识
*
* 默认文案为 YukiHookAPI
*
* 你可以修改为你自己的文案
*/
var debugTag = "YukiHookAPI"
/** /**
* 是否开启调试模式 - 默认启用 * 是否开启调试模式 - 默认启用
* *
* 启用后将交由日志输出管理器打印详细 Hook 日志到控制台 * 启用后将交由日志输出管理器打印详细 Hook 日志到控制台
* *
* 请过滤 [TAG] (YukiHookAPI) 即可找到每条日志 * 关闭后将只输出 Error 级别的日志
*
* 请过滤 [debugTag] 即可找到每条日志
*/ */
var isDebug = true var isDebug = true

View File

@@ -38,7 +38,6 @@ import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.param.HookParam import com.highcapable.yukihookapi.hook.param.HookParam
import com.highcapable.yukihookapi.hook.param.PackageParam 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_MethodHook
import de.robv.android.xposed.XC_MethodReplacement import de.robv.android.xposed.XC_MethodReplacement
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
@@ -59,15 +58,17 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
/** /**
* 注入要 Hook 的方法、构造类 * 注入要 Hook 的方法、构造类
* @param tag 可设置标签 - 在发生错误时方便进行调试
* @param initiate 方法体 * @param initiate 方法体
*/ */
fun injectMember(initiate: MemberHookCreater.() -> Unit) = fun injectMember(tag: String = "Default", initiate: MemberHookCreater.() -> Unit) =
MemberHookCreater().apply(initiate).apply { MemberHookCreater(tag).apply(initiate).apply {
hookMembers[toString()] = this hookMembers[toString()] = this
}.create() }.build()
/** /**
* Hook 执行入口 - 不可在外部调用 * Hook 执行入口
* - 此功能交由方法体自动完成 - 你不应该手动调用此方法
* @throws IllegalStateException 如果必要参数没有被设置 * @throws IllegalStateException 如果必要参数没有被设置
*/ */
@DoNotUseMethod @DoNotUseMethod
@@ -80,8 +81,9 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
* 智能全局方法、构造类查找类实现方法 * 智能全局方法、构造类查找类实现方法
* *
* 处理需要 Hook 的方法 * 处理需要 Hook 的方法
* @param tag 当前设置的标签
*/ */
inner class MemberHookCreater { inner class MemberHookCreater(var tag: String) {
/** [beforeHook] 回调 */ /** [beforeHook] 回调 */
private var beforeHookCallback: (HookParam.() -> Unit)? = null private var beforeHookCallback: (HookParam.() -> Unit)? = null
@@ -98,17 +100,14 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
/** Hook 开始时出现错误回调 */ /** Hook 开始时出现错误回调 */
private var onHookingFailureCallback: ((Throwable) -> Unit)? = null private var onHookingFailureCallback: ((Throwable) -> Unit)? = null
/** 当找不到方法、变量时错误回调 */
private var onNoSuchMemberCallback: ((Throwable) -> Unit)? = null
/** 全部错误回调 */ /** 全部错误回调 */
private var onAllFailureCallback: ((Throwable) -> Unit)? = null private var onAllFailureCallback: ((Throwable) -> Unit)? = null
/** 是否为替换 Hook 模式 */ /** 是否为替换 Hook 模式 */
private var isReplaceHookMode = false private var isReplaceHookMode = false
/** 是否停止 Hook */ /** 标识是否已经设置了要 Hook 的 [member] */
private var isStopHookMode = false private var isHookMemberSetup = false
/** /**
* 手动指定要 Hook 的方法、构造类 * 手动指定要 Hook 的方法、构造类
@@ -122,17 +121,12 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
* *
* 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换 * 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换
* @param initiate 方法体 * @param initiate 方法体
* @return [MethodFinder.Result]
*/ */
fun method(initiate: MethodFinder.() -> Unit) = runBlocking { fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result {
runCatching { isHookMemberSetup = true
member = MethodFinder(hookClass).apply(initiate).find() return MethodFinder(hookInstance = this, hookClass).apply(initiate).build()
}.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") }
/** /**
* 查找需要 Hook 的构造类 * 查找需要 Hook 的构造类
@@ -140,16 +134,10 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
* 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换 * 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换
* @param initiate 方法体 * @param initiate 方法体
*/ */
fun constructor(initiate: ConstructorFinder.() -> Unit) = runBlocking { fun constructor(initiate: ConstructorFinder.() -> Unit): ConstructorFinder.Result {
runCatching { isHookMemberSetup = true
member = ConstructorFinder(hookClass).apply(initiate).find() return ConstructorFinder(hookInstance = this, hookClass).apply(initiate).build()
}.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") }
/** /**
* 查找 [Field] * 查找 [Field]
@@ -157,19 +145,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
* @return [FieldFinder.Result] * @return [FieldFinder.Result]
*/ */
fun HookParam.field(initiate: FieldFinder.() -> Unit) = fun HookParam.field(initiate: FieldFinder.() -> Unit) =
try { FieldFinder(hookInstance = this@MemberHookCreater, hookClass).apply(initiate).build()
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()
}
/** /**
* 在方法执行完成前 Hook * 在方法执行完成前 Hook
@@ -263,80 +239,99 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
} }
/** /**
* Hook 创建入口 - 不可在外部调用 * Hook 创建入口
* - 此功能交由方法体自动完成 - 你不应该手动调用此方法
* @return [Result] * @return [Result]
*/ */
@DoNotUseMethod @DoNotUseMethod
fun create() = Result() fun build() = Result()
/** /**
* Hook 执行入口 - 不可在外部调用 * Hook 执行入口
* @throws IllegalStateException 如果必要参数没有被设置 * - 此功能交由方法体自动完成 - 你不应该手动调用此方法
*/ */
@DoNotUseMethod @DoNotUseMethod
fun hook() { fun hook() {
if (isStopHookMode) return if (member != null)
member?.also { member -> member.also { member ->
runCatching { runCatching {
if (isReplaceHookMode) if (isReplaceHookMode)
XposedBridge.hookMethod(member, object : XC_MethodReplacement() { XposedBridge.hookMethod(member, object : XC_MethodReplacement() {
override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? { override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? {
if (baseParam == null) return null if (baseParam == null) return null
return HookParam(baseParam).let { param -> return HookParam(baseParam).let { param ->
try { try {
if (replaceHookCallback != null) if (replaceHookCallback != null)
onHookLogMsg(msg = "Replace Hook Member [${member}] done") onHookLogMsg(msg = "Replace Hook Member [${member}] done [$tag]")
replaceHookCallback?.invoke(param) replaceHookCallback?.invoke(param)
} catch (e: Throwable) { } catch (e: Throwable) {
onConductFailureCallback?.invoke(param, e) onConductFailureCallback?.invoke(param, e)
onAllFailureCallback?.invoke(e) onAllFailureCallback?.invoke(e)
if (onConductFailureCallback == null && onAllFailureCallback == null) if (onConductFailureCallback == null && onAllFailureCallback == null)
onHookFailureMsg(e) onHookFailureMsg(e)
null null
}
} }
} }
} })
}) else
else XposedBridge.hookMethod(member, object : XC_MethodHook() {
XposedBridge.hookMethod(member, object : XC_MethodHook() { override fun beforeHookedMethod(baseParam: MethodHookParam?) {
override fun beforeHookedMethod(baseParam: MethodHookParam?) { if (baseParam == null) return
if (baseParam == null) return HookParam(baseParam).also { param ->
HookParam(baseParam).also { param -> runCatching {
runCatching { beforeHookCallback?.invoke(param)
beforeHookCallback?.invoke(param) if (beforeHookCallback != null)
if (beforeHookCallback != null) onHookLogMsg(msg = "Before Hook Member [${member}] done [$tag]")
onHookLogMsg(msg = "Before Hook Member [${member}] done") }.onFailure {
}.onFailure { onConductFailureCallback?.invoke(param, it)
onConductFailureCallback?.invoke(param, it) onAllFailureCallback?.invoke(it)
onAllFailureCallback?.invoke(it) if (onConductFailureCallback == null && onAllFailureCallback == null)
if (onConductFailureCallback == null && onAllFailureCallback == null) onHookFailureMsg(it)
onHookFailureMsg(it) }
} }
} }
}
override fun afterHookedMethod(baseParam: MethodHookParam?) { override fun afterHookedMethod(baseParam: MethodHookParam?) {
if (baseParam == null) return if (baseParam == null) return
HookParam(baseParam).also { param -> HookParam(baseParam).also { param ->
runCatching { runCatching {
afterHookCallback?.invoke(param) afterHookCallback?.invoke(param)
if (afterHookCallback != null) if (afterHookCallback != null)
onHookLogMsg(msg = "After Hook Member [${member}] done") onHookLogMsg(msg = "After Hook Member [${member}] done [$tag]")
}.onFailure { }.onFailure {
onConductFailureCallback?.invoke(param, it) onConductFailureCallback?.invoke(param, it)
onAllFailureCallback?.invoke(it) onAllFailureCallback?.invoke(it)
if (onConductFailureCallback == null && onAllFailureCallback == null) if (onConductFailureCallback == null && onAllFailureCallback == null)
onHookFailureMsg(it) onHookFailureMsg(it)
}
} }
} }
} })
}) }.onFailure {
}.onFailure { onHookingFailureCallback?.invoke(it)
onHookingFailureCallback?.invoke(it) onAllFailureCallback?.invoke(it)
onAllFailureCallback?.invoke(it) if (onHookingFailureCallback == null && onAllFailureCallback == null) onHookFailureMsg(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 异常信息 * @param throwable 异常信息
*/ */
private fun onHookFailureMsg(throwable: 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)
/** override fun toString() = "$member$tag#YukiHook"
* Hook 过程中开启了 [YukiHookAPI.isDebug] 输出调试信息
* @param msg 调试日志内容
*/
private fun onHookLogMsg(msg: String) {
if (YukiHookAPI.isDebug) loggerI(msg = msg)
}
override fun toString() = "$member#YukiHook"
/** /**
* 监听 Hook 结果实现类 * 监听 Hook 结果实现类
@@ -388,7 +375,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
/** /**
* 监听 Hook 开始时发生错误的回调方法 * 监听 Hook 开始时发生错误的回调方法
* @param initiate 回调错误 - ([Throwable] 异常) * @param initiate 回调错误
* @return [Result] 可继续向下监听 * @return [Result] 可继续向下监听
*/ */
fun onHookingFailure(initiate: (Throwable) -> Unit): Result { fun onHookingFailure(initiate: (Throwable) -> Unit): Result {
@@ -402,25 +389,9 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla
*/ */
fun ignoredHookingFailure() = onHookingFailure {} 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 过程发生错误的回调方法 * 监听全部 Hook 过程发生错误的回调方法
* @param initiate 回调错误 - ([Throwable] 异常) * @param initiate 回调错误
* @return [Result] 可继续向下监听 * @return [Result] 可继续向下监听
*/ */
fun onAllFailure(initiate: (Throwable) -> Unit): Result { fun onAllFailure(initiate: (Throwable) -> Unit): Result {

View File

@@ -25,27 +25,35 @@
* *
* This file is Created by fankes on 2022/2/4. * 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 package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod 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.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Constructor import java.lang.reflect.Constructor
/** /**
* [Constructor] 查找类 * [Constructor] 查找类
* *
* 可通过指定类型查找指定构造 * 可通过指定类型查找指定构造方法
* @param hookInstance 当前 Hook 实例
* @param hookClass 当前被 Hook 的 [Class] * @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<Long, ConstructorFinder>()
/** 构造方法参数 */
private var params: Array<out Class<*>>? = null private var params: Array<out Class<*>>? = null
/** /**
* 方法参数 * 构造方法参数
* @param param 参数数组 * @param param 参数数组
*/ */
fun param(vararg param: Class<*>) { fun param(vararg param: Class<*>) {
@@ -53,13 +61,135 @@ class ConstructorFinder(private val hookClass: Class<*>) {
} }
/** /**
* 得到构造类 - 不能在外部调用 * 得到构造方法
* @return [Constructor] * @return [Constructor]
* @throws NoSuchMethodError 如果找不到构造 * @throws NoSuchMethodError 如果找不到构造方法
*/ */
@DoNotUseMethod private val result
fun find(): Constructor<*> = get() = if (params != null)
if (params != null)
ReflectionUtils.findConstructorExact(hookClass, *params!!) ReflectionUtils.findConstructorExact(hookClass, *params!!)
else ReflectionUtils.findConstructorExact(hookClass) 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<ConstructorFinder>()
/**
* 创建需要重新查找的 [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
}
}
} }

View File

@@ -30,16 +30,20 @@
package com.highcapable.yukihookapi.hook.core.finder package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod 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.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Field import java.lang.reflect.Field
/** /**
* Field 查找结果实现类 * Field 查找结果实现类
* *
* 可在这里处理找到的 [fieldInstance] * 可在这里处理找到的 [fieldInstance]
* @param hookInstance 当前 Hook 实例
* @param hookClass 当前被 Hook 的 [Class] * @param hookClass 当前被 Hook 的 [Class]
*/ */
class FieldFinder(private val hookClass: Class<*>) { class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) {
/** 当前找到的 [Field] */ /** 当前找到的 [Field] */
private var fieldInstance: Field? = null private var fieldInstance: Field? = null
@@ -51,25 +55,42 @@ class FieldFinder(private val hookClass: Class<*>) {
var type: Class<*>? = null var type: Class<*>? = null
/** /**
* 得到变量处理结果 - 不能在外部调用 * 得到变量处理结果
* - 此功能交由方法体自动完成 - 你不应该手动调用此方法
* @return [Result] * @return [Result]
* @throws NoSuchFieldError 如果找不到变量 * @throws IllegalStateException 如果 [name] 没有被设置
*/ */
@DoNotUseMethod @DoNotUseMethod
fun find(): Result { fun build() = if (name.isBlank()) {
fieldInstance = when { loggerE(msg = "Field name cannot be empty in Class [$hookClass] [${hookInstance.tag}]")
name.isBlank() -> error("Field name cannot be empty") Result(isNoSuch = true)
else -> ReflectionUtils.findFieldIfExists(hookClass, type?.name, name) } 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 查找结果实现类 * Field 查找结果实现类
* *
* 可在这里处理找到的 [fieldInstance] * 可在这里处理找到的 [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 * @return [Field] or null
*/ */
fun give() = fieldInstance fun give() = fieldInstance
/**
* 监听找不到变量时
*
* 只会返回第一次和最后一次的错误信息
* @param initiate 回调错误
* @return [Result] 可继续向下监听
*/
fun onNoSuchField(initiate: (Throwable) -> Unit): Result {
if (isNoSuch) initiate(e ?: Throwable())
return this
}
} }
} }

View File

@@ -25,21 +25,26 @@
* *
* This file is Created by fankes on 2022/2/4. * 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 package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod 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.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Method import java.lang.reflect.Method
/** /**
* [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<out Class<*>>? = null private var params: Array<out Class<*>>? = null
@@ -59,17 +64,140 @@ class MethodFinder(private val hookClass: Class<*>) {
} }
/** /**
* 得到方法 - 不能在外部调用 * 得到方法
* @return [Method] * @return [Method]
* @throws IllegalStateException 如果 [name] 未设置
* @throws NoSuchMethodError 如果找不到方法 * @throws NoSuchMethodError 如果找不到方法
*/ */
private val result
get() = if (params != null)
ReflectionUtils.findMethodBestMatch(hookClass, returnType, name, *params!!)
else ReflectionUtils.findMethodNoParam(hookClass, returnType, name)
/**
* 得到方法结果
* - 此功能交由方法体自动完成 - 你不应该手动调用此方法
* @return [Result]
*/
@DoNotUseMethod @DoNotUseMethod
fun find(): Method = fun build() = if (name.isBlank()) {
when { loggerE(msg = "Method name cannot be empty in Class [$hookClass] [${hookInstance.tag}]")
name.isBlank() -> error("Method name cannot be empty") Result(isNoSuch = true)
else -> } else try {
if (params != null) runBlocking {
ReflectionUtils.findMethodBestMatch(hookClass, returnType, name, *params!!) hookInstance.member = result
else ReflectionUtils.findMethodNoParam(hookClass, returnType, name) }.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<MethodFinder>()
/**
* 创建需要重新查找的 [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
}
}
} }

View File

@@ -75,6 +75,7 @@ abstract class YukiBaseHooker : PackageParam() {
/** /**
* 赋值并克隆一个 [PackageParam] * 赋值并克隆一个 [PackageParam]
* @param packageParam 需要使用的 [PackageParam] * @param packageParam 需要使用的 [PackageParam]
* - 此方法为私有功能性 API - 你不应该手动调用此方法
*/ */
@DoNotUseMethod @DoNotUseMethod
internal fun assignInstance(packageParam: PackageParam) { internal fun assignInstance(packageParam: PackageParam) {

View File

@@ -36,13 +36,11 @@ import de.robv.android.xposed.XposedBridge
/** /**
* 向控制台和 [XposedBridge] 打印日志 - D * 向控制台和 [XposedBridge] 打印日志 - D
* *
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag]
* @param msg 日志打印的内容 * @param msg 日志打印的内容
*/ */
fun loggerD(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { fun loggerD(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching {
Log.d(tag, msg) Log.d(tag, msg)
XposedBridge.log("[$tag][D]--> $msg") XposedBridge.log("[$tag][D]--> $msg")
} }
@@ -50,13 +48,11 @@ fun loggerD(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
/** /**
* 向控制台和 [XposedBridge] 打印日志 - I * 向控制台和 [XposedBridge] 打印日志 - I
* *
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag]
* @param msg 日志打印的内容 * @param msg 日志打印的内容
*/ */
fun loggerI(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { fun loggerI(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching {
Log.i(tag, msg) Log.i(tag, msg)
XposedBridge.log("[$tag][I]--> $msg") XposedBridge.log("[$tag][I]--> $msg")
} }
@@ -64,13 +60,11 @@ fun loggerI(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
/** /**
* 向控制台和 [XposedBridge] 打印日志 - W * 向控制台和 [XposedBridge] 打印日志 - W
* *
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag]
* @param msg 日志打印的内容 * @param msg 日志打印的内容
*/ */
fun loggerW(tag: String = YukiHookAPI.TAG, msg: String) = runCatching { fun loggerW(tag: String = YukiHookAPI.debugTag, msg: String) = runCatching {
Log.w(tag, msg) Log.w(tag, msg)
XposedBridge.log("[$tag][W]--> $msg") XposedBridge.log("[$tag][W]--> $msg")
} }
@@ -78,14 +72,12 @@ fun loggerW(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
/** /**
* 向控制台和 [XposedBridge] 打印日志 - E * 向控制台和 [XposedBridge] 打印日志 - E
* *
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.debugTag]
* @param msg 日志打印的内容 * @param msg 日志打印的内容
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台 * @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) Log.e(tag, msg, e)
XposedBridge.log("[$tag][E]--> $msg") XposedBridge.log("[$tag][E]--> $msg")
e?.also { XposedBridge.log(it) } e?.also { XposedBridge.log(it) }

View File

@@ -89,6 +89,7 @@ open class PackageParam(
/** /**
* 赋值并克隆另一个 [PackageParam] * 赋值并克隆另一个 [PackageParam]
* @param another 另一个 [PackageParam] * @param another 另一个 [PackageParam]
* - 此方法为私有功能性 API - 你不应该手动调用此方法
*/ */
@DoNotUseMethod @DoNotUseMethod
internal fun baseAssignInstance(another: PackageParam) { internal fun baseAssignInstance(another: PackageParam) {

View File

@@ -35,7 +35,7 @@ import java.io.Serializable
* 获得 [Any] 类型 * 获得 [Any] 类型
* @return [Class] * @return [Class]
*/ */
val AnyType get() = Any::class.java val AnyType get() = Any::class.javaPrimitiveType
/** /**
* 获得 [Unit] 类型 * 获得 [Unit] 类型
@@ -47,7 +47,7 @@ val UnitType get() = Unit::class.javaPrimitiveType
* 获得 [Boolean] 类型 * 获得 [Boolean] 类型
* @return [Class] * @return [Class]
*/ */
val BooleanType get() = Boolean::class.javaPrimitiveType val BooleanType get() = Boolean::class.java
/** /**
* 获得 [Int] 类型 * 获得 [Int] 类型

View File

@@ -107,18 +107,8 @@ public class ReflectionUtils {
return false; return false;
} }
public static Field findField(Class<?> clazz, String typeName, String fieldName) { public static Field findFieldIfExists(Class<?> clazz, String typeName, String fieldName) throws NoSuchFieldException {
try { String fullFieldName = "name:[" + fieldName + "] type:[" + typeName + "] in Class [" + clazz.getName() + "] by YukiHook#finder";
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;
if (!fieldCache.containsKey(fullFieldName)) { if (!fieldCache.containsKey(fullFieldName)) {
if (clazz != null && !TextUtils.isEmpty(typeName) && !TextUtils.isEmpty(fieldName)) { if (clazz != null && !TextUtils.isEmpty(typeName) && !TextUtils.isEmpty(fieldName)) {
Class<?> clz = clazz; Class<?> clz = clazz;
@@ -134,7 +124,7 @@ public class ReflectionUtils {
} }
} }
} while ((clz = clz.getSuperclass()) != null); } while ((clz = clz.getSuperclass()) != null);
throw new NoSuchFieldException(clazz.getName() + "#" + typeName + " " + fieldName); throw new NoSuchFieldException("Can't find this field --> " + fullFieldName);
} }
return null; return null;
} else { } else {