From c51dac9b7ea33b923be848bc7d3987b52ec88a65 Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Wed, 9 Feb 2022 23:04:36 +0800 Subject: [PATCH] ... --- .idea/codeStyles/Project.xml | 117 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .../yukihookapi/hook/bean/HookClass.kt | 40 ++++++ .../yukihookapi/hook/core/YukiHookCreater.kt | 48 +++++-- .../hook/core/finder/ConstructorFinder.kt | 12 +- .../hook/core/finder/FieldFinder.kt | 12 +- .../hook/core/finder/MethodFinder.kt | 12 +- .../hook/factory/ReflectionFactory.kt | 13 ++ .../yukihookapi/hook/param/HookParam.kt | 48 +++---- .../yukihookapi/hook/param/PackageParam.kt | 73 +++++++---- .../hook/param/wrapper/HookParamWrapper.kt | 76 ++++++++++++ .../hook/xposed/prefs/YukiHookModulePrefs.kt | 26 ++-- 12 files changed, 413 insertions(+), 69 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookClass.kt create mode 100644 yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/HookParamWrapper.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..4bec4ea8 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,117 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..a55e7a17 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookClass.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookClass.kt new file mode 100644 index 00000000..922f8441 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookClass.kt @@ -0,0 +1,40 @@ +/** + * MIT License + * + * Copyright (C) 2022 HighCapable + * + * This file is part of YukiHookAPI. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is Created by fankes on 2022/2/9. + */ +package com.highcapable.yukihookapi.hook.bean + +/** + * 自适应异常 [Class] 接管类 + * @param instance 实例 + * @param name 完整名称 + * @param throwable 异常 + */ +class HookClass( + var instance: Class<*>? = null, + var name: String, + var throwable: Throwable? = null +) \ No newline at end of file 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 dc0c4361..b649fd70 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 @@ -31,6 +31,7 @@ package com.highcapable.yukihookapi.hook.core import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.DoNotUseMethod +import com.highcapable.yukihookapi.hook.bean.HookClass import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder import com.highcapable.yukihookapi.hook.core.finder.FieldFinder import com.highcapable.yukihookapi.hook.core.finder.MethodFinder @@ -38,6 +39,7 @@ 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.param.wrapper.HookParamWrapper import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XC_MethodReplacement import de.robv.android.xposed.XposedBridge @@ -49,9 +51,9 @@ import java.lang.reflect.Member * * 这是一个 API 对接类 - 实现原生对接 [XposedBridge] * @param packageParam 需要传入 [PackageParam] 实现方法调用 - * @param hookClass 要 Hook 的 [Class] - 必须使用当前 Hook APP 的 [ClassLoader] 装载 + * @param hookClass 要 Hook 的 [HookClass] 实例 */ -class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Class<*>) { +class YukiHookCreater(private val packageParam: PackageParam, private val hookClass: HookClass) { /** * Hook 全部方法的标识 @@ -61,6 +63,16 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla /** 设置要 Hook 的方法、构造类 */ private var hookMembers = HashMap() + /** + * 得到当前被 Hook 的 [Class] + * + * - ❗不推荐直接使用 - 万一得不到 [Class] 对象则会无法处理异常导致崩溃 + * @return [Class] + * @throws IllegalStateException 如果当前 [Class] 未被正确装载 + */ + val thisClass + get() = hookClass.instance ?: error("Cannot get hook class \"${hookClass.name}\" cause ${hookClass.throwable?.message}") + /** * 注入要 Hook 的方法、构造类 * @param tag 可设置标签 - 在发生错误时方便进行调试 @@ -164,9 +176,10 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * @return [MethodFinder.Result] */ fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result { + if (hookClass.instance == null) return MethodFinder(hookInstance = this).failure(hookClass.throwable) hookAllMembers = HookAllMembers.HOOK_NONE isHookMemberSetup = true - return MethodFinder(hookInstance = this, hookClass).apply(initiate).build() + return MethodFinder(hookInstance = this, hookClass.instance).apply(initiate).build() } /** @@ -177,9 +190,10 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * @return [ConstructorFinder.Result] */ fun constructor(initiate: ConstructorFinder.() -> Unit = {}): ConstructorFinder.Result { + if (hookClass.instance == null) return ConstructorFinder(hookInstance = this).failure(hookClass.throwable) hookAllMembers = HookAllMembers.HOOK_NONE isHookMemberSetup = true - return ConstructorFinder(hookInstance = this, hookClass).apply(initiate).build() + return ConstructorFinder(hookInstance = this, hookClass.instance).apply(initiate).build() } /** @@ -188,7 +202,8 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla * @return [FieldFinder.Result] */ fun HookParam.field(initiate: FieldFinder.() -> Unit) = - FieldFinder(hookInstance = this@MemberHookCreater, hookClass).apply(initiate).build() + if (hookClass.instance == null) FieldFinder(hookInstance = this@MemberHookCreater).failure(hookClass.throwable) + else FieldFinder(hookInstance = this@MemberHookCreater, hookClass.instance).apply(initiate).build() /** * 在方法执行完成前 Hook @@ -298,11 +313,20 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla @DoNotUseMethod fun hook() { if (!YukiHookAPI.hasXposedBridge) return + if (hookClass.instance == null) { + (hookClass.throwable ?: Throwable("Failed Hooked Class [${hookClass.name}]")).also { + onHookingFailureCallback?.invoke(it) + onAllFailureCallback?.invoke(it) + if (onHookingFailureCallback == null && onAllFailureCallback == null) + onHookFailureMsg(it) + } + return + } /** 定义替换 Hook 回调方法体 */ val replaceMent = object : XC_MethodReplacement() { override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? { if (baseParam == null) return null - return HookParam(baseParam).let { param -> + return HookParam(HookParamWrapper(baseParam)).let { param -> try { if (replaceHookCallback != null) onHookLogMsg(msg = "Replace Hook Member [${member ?: "All Member $allMethodsName"}] done [$tag]") @@ -322,7 +346,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla val beforeAfterHook = object : XC_MethodHook() { override fun beforeHookedMethod(baseParam: MethodHookParam?) { if (baseParam == null) return - HookParam(baseParam).also { param -> + HookParam(HookParamWrapper(baseParam)).also { param -> runCatching { beforeHookCallback?.invoke(param) if (beforeHookCallback != null) @@ -338,7 +362,7 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla override fun afterHookedMethod(baseParam: MethodHookParam?) { if (baseParam == null) return - HookParam(baseParam).also { param -> + HookParam(HookParamWrapper(baseParam)).also { param -> runCatching { afterHookCallback?.invoke(param) if (afterHookCallback != null) @@ -379,12 +403,12 @@ class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Cla when (hookAllMembers) { HookAllMembers.HOOK_ALL_METHODS -> if (isReplaceHookMode) - XposedBridge.hookAllMethods(hookClass, allMethodsName, replaceMent) - else XposedBridge.hookAllMethods(hookClass, allMethodsName, beforeAfterHook) + XposedBridge.hookAllMethods(hookClass.instance, allMethodsName, replaceMent) + else XposedBridge.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook) HookAllMembers.HOOK_ALL_CONSTRUCTORS -> if (isReplaceHookMode) - XposedBridge.hookAllConstructors(hookClass, replaceMent) - else XposedBridge.hookAllConstructors(hookClass, beforeAfterHook) + XposedBridge.hookAllConstructors(hookClass.instance, replaceMent) + else XposedBridge.hookAllConstructors(hookClass.instance, beforeAfterHook) else -> error("Hooked got a no error possible") } }.onFailure { 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 24f29e9b..5aaf83ea 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 @@ -44,7 +44,7 @@ import java.lang.reflect.Constructor * @param hookInstance 当前 Hook 实例 * @param hookClass 当前被 Hook 的 [Class] */ -class ConstructorFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { +class ConstructorFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>? = null) { /** [Constructor] 参数数组 */ private var params: Array>? = null @@ -91,6 +91,16 @@ class ConstructorFinder(private val hookInstance: YukiHookCreater.MemberHookCrea Result(isNoSuch = true, e) } + /** + * 创建一个异常结果 + * + * - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法 + * @param throwable 异常 + * @return [Result] + */ + @DoNotUseMethod + fun failure(throwable: Throwable?) = Result(isNoSuch = true, throwable) + /** * 发生错误时输出日志 * @param msg 消息日志 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 1473e865..91ddfa72 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 @@ -43,7 +43,7 @@ import java.lang.reflect.Field * @param hookInstance 当前 Hook 实例 * @param hookClass 当前被 Hook 的 [Class] */ -class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { +class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>? = null) { /** 当前找到的 [Field] */ private var fieldInstance: Field? = null @@ -85,6 +85,16 @@ class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, p Result(isNoSuch = true, e) } + /** + * 创建一个异常结果 + * + * - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法 + * @param throwable 异常 + * @return [Result] + */ + @DoNotUseMethod + fun failure(throwable: Throwable?) = Result(isNoSuch = true, throwable) + /** * Field 查找结果实现类 * 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 d47351d2..764cc269 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 @@ -44,7 +44,7 @@ import java.lang.reflect.Method * @param hookInstance 当前 Hook 实例 * @param hookClass 当前 Hook 的 Class */ -class MethodFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { +class MethodFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>? = null) { /** [Method] 参数数组 */ private var params: Array>? = null @@ -109,6 +109,16 @@ class MethodFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, Result(isNoSuch = true, e) } + /** + * 创建一个异常结果 + * + * - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法 + * @param throwable 异常 + * @return [Result] + */ + @DoNotUseMethod + fun failure(throwable: Throwable?) = Result(isNoSuch = true, throwable) + /** * 发生错误时输出日志 * @param msg 消息日志 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt index 23fcc560..cb1320cc 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt @@ -29,6 +29,7 @@ package com.highcapable.yukihookapi.hook.factory +import com.highcapable.yukihookapi.hook.bean.HookClass import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Method @@ -52,6 +53,18 @@ val String.hasClass false } +/** + * [Class] 转换为 [HookClass] + * @return [HookClass] + */ +val Class<*>.hookClass get() = HookClass(instance = this, name) + +/** + * [HookClass] 转换为 [Class] + * @return [Class] or null + */ +val HookClass.clazz get() = instance + /** * 查找方法是否存在 * @param name 名称 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt index 1a52905d..c60eecb6 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt @@ -29,42 +29,46 @@ package com.highcapable.yukihookapi.hook.param -import de.robv.android.xposed.XC_MethodHook +import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper import java.lang.reflect.Constructor -import java.lang.reflect.Method +import java.lang.reflect.Member /** * Hook 方法、构造类的目标对象实现类 - * @param baseParam 对接 Xposed API 的 [XC_MethodHook.MethodHookParam] + * @param wrapper [HookParam] 的参数包装类实例 */ -class HookParam(private val baseParam: XC_MethodHook.MethodHookParam) { +class HookParam(private val wrapper: HookParamWrapper) { /** - * 获取当前 [method] or [constructor] 的参数对象数组 + * 获取当前 [member] or [constructor] 的参数对象数组 * @return [Array] */ - val args get() = baseParam.args ?: arrayOf(0) + val args get() = wrapper.args ?: arrayOf(0) /** - * 获取当前 [method] or [constructor] 的参数对象数组第一位 + * 获取当前 [member] or [constructor] 的参数对象数组第一位 * @return [Array] * @throws IllegalStateException 如果数组为空或对象为空 */ - val firstArgs get() = args[0] ?: error("HookParam args[0] with a non-null object") + val firstArgs + get() = if (args.isNotEmpty()) args[0] ?: error("HookParam args[0] with a non-null object") + else error("HookParam args is empty") /** - * 获取当前 [method] or [constructor] 的参数对象数组最后一位 + * 获取当前 [member] or [constructor] 的参数对象数组最后一位 * @return [Array] * @throws IllegalStateException 如果数组为空或对象为空 */ - val lastArgs get() = args[args.lastIndex] ?: error("HookParam args[lastIndex] with a non-null object") + val lastArgs + get() = if (args.isNotEmpty()) args[args.lastIndex] ?: error("HookParam args[lastIndex] with a non-null object") + else error("HookParam args is empty") /** * 获取当前 Hook 实例的对象 * @return [Any] * @throws IllegalStateException 如果对象为空 */ - val instance get() = baseParam.thisObject ?: error("HookParam must with a non-null object") + val instance get() = wrapper.instance ?: error("HookParam must with a non-null instance") /** * 获取当前 Hook 实例的类对象 @@ -73,27 +77,27 @@ class HookParam(private val baseParam: XC_MethodHook.MethodHookParam) { val instanceClass get() = instance.javaClass /** - * 获取当前 Hook 的方法 - * @return [Method] - * @throws IllegalStateException 如果方法为空或方法类型不是 [Method] + * 获取当前 Hook 的方法、构造方法 + * @return [Member] + * @throws IllegalStateException 如果 [Member] 是空的 */ - val method get() = baseParam.method as? Method? ?: error("Current hook method type is wrong or null") + val member get() = wrapper.member ?: error("Current hook member type is wrong or null") /** * 获取当前 Hook 的构造方法 * @return [Constructor] * @throws IllegalStateException 如果方法为空或方法类型不是 [Constructor] */ - val constructor get() = baseParam.method as? Constructor<*>? ?: error("Current hook constructor type is wrong or null") + val constructor get() = wrapper.member as? Constructor<*>? ?: error("Current hook constructor type is wrong or null") /** - * 获取、设置当前 [method] or [constructor] 的返回值 + * 获取、设置当前 [member] or [constructor] 的返回值 * @return [Any] or null */ var result: Any? - get() = baseParam.result + get() = wrapper.result set(value) { - baseParam.result = value + wrapper.result = value } /** @@ -101,10 +105,10 @@ class HookParam(private val baseParam: XC_MethodHook.MethodHookParam) { * @return [T] * @throws IllegalStateException 如果对象为空或对象类型不是 [T] */ - inline fun instance() = instance as? T? ?: error("HookParam object cannot cast to ${T::class.java.name}") + inline fun instance() = instance as? T? ?: error("HookParam instance cannot cast to ${T::class.java.name}") /** - * 获取当前 [method] or [constructor] 的参数实例化对象类 + * 获取当前 [member] or [constructor] 的参数实例化对象类 * @param index 参数对象数组下标 - 默认是 0 * @return [ArgsModifyer] */ @@ -151,7 +155,7 @@ class HookParam(private val baseParam: XC_MethodHook.MethodHookParam) { fun set(any: T?) { if (args.isEmpty()) error("HookParam method args is empty,mabe not has args") if (index > args.lastIndex) error("HookParam method args index out of bounds,max is ${args.lastIndex}") - baseParam.args[index] = any + wrapper.setArgs(index, any) } /** 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 7dad5b5e..cc3b1091 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 @@ -31,16 +31,18 @@ package com.highcapable.yukihookapi.hook.param import android.content.pm.ApplicationInfo import com.highcapable.yukihookapi.annotation.DoNotUseMethod +import com.highcapable.yukihookapi.hook.bean.HookClass import com.highcapable.yukihookapi.hook.core.YukiHookCreater import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker +import com.highcapable.yukihookapi.hook.factory.hookClass import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs /** * 装载 Hook 的目标 APP 入口对象实现类 - * @param baseParam [PackageParam] 的参数包装类实例 - 默认是空的 + * @param wrapper [PackageParam] 的参数包装类实例 - 默认是空的 */ -open class PackageParam(private var baseParam: PackageParamWrapper? = null) { +open class PackageParam(private var wrapper: PackageParamWrapper? = null) { /** * 获取当前 APP 的 [ClassLoader] @@ -48,13 +50,13 @@ open class PackageParam(private var baseParam: PackageParamWrapper? = null) { * @throws IllegalStateException 如果 [ClassLoader] 是空的 */ val appClassLoader - get() = baseParam?.appClassLoader ?: javaClass.classLoader ?: error("PackageParam got null ClassLoader") + get() = wrapper?.appClassLoader ?: javaClass.classLoader ?: error("PackageParam got null ClassLoader") /** * 获取当前 APP 的 [ApplicationInfo] * @return [ApplicationInfo] */ - val appInfo get() = baseParam?.appInfo ?: ApplicationInfo() + val appInfo get() = wrapper?.appInfo ?: ApplicationInfo() /** * 获取当前 APP 的进程名称 @@ -62,13 +64,13 @@ open class PackageParam(private var baseParam: PackageParamWrapper? = null) { * 默认的进程名称是 [packageName] * @return [String] */ - val processName get() = baseParam?.processName ?: packageName + val processName get() = wrapper?.processName ?: packageName /** * 获取当前 APP 的包名 * @return [String] */ - val packageName get() = baseParam?.packageName ?: "" + val packageName get() = wrapper?.packageName ?: "" /** * 获取当前 APP 是否为第一个 Application @@ -78,20 +80,26 @@ open class PackageParam(private var baseParam: PackageParamWrapper? = null) { /** * 获得当前使用的存取数据对象缓存实例 - * * @return [YukiHookModulePrefs] */ val prefs by lazy { YukiHookModulePrefs() } + /** + * 获得当前使用的存取数据对象缓存实例 + * @param name 自定义 Sp 存储名称 + * @return [YukiHookModulePrefs] + */ + fun prefs(name: String) = prefs.name(name) + /** * 赋值并克隆另一个 [PackageParam] * * - ❗此方法为私有功能性 API - 你不应该手动调用此方法 - * @param another 另一个 [PackageParam] + * @param anotherParam 另一个 [PackageParam] */ @DoNotUseMethod - internal fun baseAssignInstance(another: PackageParam) { - this.baseParam = another.baseParam + internal fun baseAssignInstance(anotherParam: PackageParam) { + thisParam.wrapper = anotherParam.wrapper } /** @@ -111,27 +119,46 @@ open class PackageParam(private var baseParam: PackageParamWrapper? = null) { */ fun loadHooker(hooker: YukiBaseHooker) = hooker.assignInstance(packageParam = this) - /** - * 将目标 [Class] 绑定到 [appClassLoader] - * - * - ❗请注意未绑定到 [appClassLoader] 的 [Class] 不能被装载 - 调用 [hook] 方法会自动绑定 - * @return [Class] - * @throws NoClassDefFoundError 如果找不到类会报错 - */ - fun Class<*>.bind(): Class<*> = appClassLoader.loadClass(name) - /** * 通过 [appClassLoader] 查询并装载 [Class] * @param name 类名 - * @return [Class] - * @throws NoClassDefFoundError 如果找不到类会报错 + * @return [HookClass] */ - fun findClass(name: String): Class<*> = appClassLoader.loadClass(name) + fun findClass(name: String) = try { + appClassLoader.loadClass(name).hookClass + } catch (e: Throwable) { + HookClass(name = name, throwable = e) + } /** * Hook 方法、构造类 * @param initiate 方法体 */ fun Class<*>.hook(initiate: YukiHookCreater.() -> Unit) = - YukiHookCreater(packageParam = this@PackageParam, hookClass = bind()).apply(initiate).hook() + YukiHookCreater(packageParam = thisParam, hookClass = hookClass.bind()).apply(initiate).hook() + + /** + * Hook 方法、构造类 + * @param initiate 方法体 + */ + fun HookClass.hook(initiate: YukiHookCreater.() -> Unit) = + YukiHookCreater(packageParam = thisParam, hookClass = bind()).apply(initiate).hook() + + /** + * 将目标 [Class] 绑定到 [appClassLoader] + * + * - ❗请注意未绑定到 [appClassLoader] 的 [Class] 不能被装载 - 调用 [hook] 方法会自动绑定 + * @return [HookClass] + */ + private fun HookClass.bind() = try { + appClassLoader.loadClass(name).hookClass + } catch (e: Throwable) { + HookClass(name = name, throwable = e) + } + + /** + * 返回自身实例 + * @return [PackageParam] + */ + private val thisParam get() = this } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/HookParamWrapper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/HookParamWrapper.kt new file mode 100644 index 00000000..8f3f19ee --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/HookParamWrapper.kt @@ -0,0 +1,76 @@ +/** + * MIT License + * + * Copyright (C) 2022 HighCapable + * + * This file is part of YukiHookAPI. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is Created by fankes on 2022/2/9. + */ +package com.highcapable.yukihookapi.hook.param.wrapper + +import com.highcapable.yukihookapi.hook.param.HookParam +import de.robv.android.xposed.XC_MethodHook +import java.lang.reflect.Member + +/** + * 用于包装 [HookParam] + * @param baseParam 对接 [XC_MethodHook.MethodHookParam] + */ +class HookParamWrapper(private val baseParam: XC_MethodHook.MethodHookParam) { + + /** + * [Member] 实例 + * @return [Member] or null + */ + val member: Member? get() = baseParam.method + + /** + * 当前实例对象 + * @return [Any] or null + */ + val instance: Any? get() = baseParam.thisObject + + /** + * 方法、构造方法数组 + * @return [Array] or null + */ + val args: Array? get() = baseParam.args + + /** + * 方法、设置方法结果 + * @return [Any] or null + */ + var result: Any? + get() = baseParam.result + set(value) { + baseParam.result = value + } + + /** + * 设置方法参数 + * @param index 数组下标 + * @param any 参数对象实例 + */ + fun setArgs(index: Int, any: Any?) { + baseParam.args[index] = any + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt index a1610543..6c5dfa33 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt @@ -51,15 +51,15 @@ import java.io.File * * - 详见 [New XSharedPreferences](https://github.com/LSPosed/LSPosed/wiki/New-XSharedPreferences#for-the-module) * - * - 未使用 LSposed 环境请将你的模块 API 降至 26 以下 - YukiHookAPI 将会尝试使用 [makeWorldReadable] 但仍有可能不成功 + * - 未使用 LSPosed 环境请将你的模块 API 降至 26 以下 - YukiHookAPI 将会尝试使用 [makeWorldReadable] 但仍有可能不成功 * * - ❗当你在模块中存取数据的时候 [context] 必须不能是空的 * @param context 上下文实例 - 默认空 */ class YukiHookModulePrefs(private val context: Context? = null) { - /** 存储名称 - 包名 + _preferences */ - private val prefsName get() = "${YukiHookAPI.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences" + /** 存储名称 - 默认包名 + _preferences */ + private var prefsName = "${YukiHookAPI.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences" /** 是否为 Xposed 环境 */ private val isXposedEnvironment = YukiHookAPI.hasXposedBridge @@ -83,26 +83,24 @@ class YukiHookModulePrefs(private val context: Context? = null) { * 获得 [XSharedPreferences] 对象 * @return [XSharedPreferences] */ - private val xPref by lazy { - XSharedPreferences(YukiHookAPI.modulePackageName, prefsName).apply { + private val xPref + get() = XSharedPreferences(YukiHookAPI.modulePackageName, prefsName).apply { makeWorldReadable() reload() } - } /** * 获得 [SharedPreferences] 对象 * @return [SharedPreferences] */ - private val sPref by lazy { - try { + private val sPref + get() = try { context?.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE) ?: error("If you want to use module prefs,you must set the context instance first") } catch (_: Throwable) { context?.getSharedPreferences(prefsName, Context.MODE_PRIVATE) ?: error("If you want to use module prefs,you must set the context instance first") } - } /** 设置全局可读可写 */ private fun makeWorldReadable() = runCatching { @@ -112,6 +110,16 @@ class YukiHookModulePrefs(private val context: Context? = null) { } } + /** + * 自定义 Sp 存储名称 + * @param name 自定义的 Sp 存储名称 + * @return [YukiHookModulePrefs] + */ + fun name(name: String): YukiHookModulePrefs { + prefsName = name + return this + } + /** * 获取 [String] 键值 *