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() {
// 设置模式
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()
}
}

View File

@@ -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

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.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 {

View File

@@ -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<Long, ConstructorFinder>()
/** 构造方法参数 */
private var params: Array<out Class<*>>? = 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<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
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
}
}
}

View File

@@ -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<out Class<*>>? = 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<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]
* @param packageParam 需要使用的 [PackageParam]
* - 此方法为私有功能性 API - 你不应该手动调用此方法
*/
@DoNotUseMethod
internal fun assignInstance(packageParam: PackageParam) {

View File

@@ -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) }

View File

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

View File

@@ -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] 类型

View File

@@ -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 {