This commit is contained in:
2022-02-09 23:04:36 +08:00
parent b4a6750921
commit c51dac9b7e
12 changed files with 413 additions and 69 deletions

117
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,117 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

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

View File

@@ -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<String, MemberHookCreater>()
/**
* 得到当前被 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 {

View File

@@ -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<out Class<*>>? = 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 消息日志

View File

@@ -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 查找结果实现类
*

View File

@@ -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<out Class<*>>? = 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 消息日志

View File

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

View File

@@ -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 <reified T> instance() = instance as? T? ?: error("HookParam object cannot cast to ${T::class.java.name}")
inline fun <reified T> 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 <T> 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)
}
/**

View File

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

View File

@@ -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<Any?>? 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
}
}

View File

@@ -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] 键值
*