mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 09:45:19 +08:00
Added initZygote、Resources Hook function and fix more bugs
This commit is contained in:
@@ -234,7 +234,17 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
|
||||
packageName = packageName,
|
||||
fileName = xInitClassName
|
||||
).apply {
|
||||
write(CodeSourceFileTemplate.getXposedInitFileByteArray(packageName, fModulePackageName, entryClassName, xInitClassName))
|
||||
write(CodeSourceFileTemplate.getXposedInitFileByteArray(packageName, entryClassName, xInitClassName))
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
/** 插入 xposed_init_Impl 代码 */
|
||||
codeGenerator.createNewFile(
|
||||
dependencies = Dependencies.ALL_FILES,
|
||||
packageName = packageName,
|
||||
fileName = "${entryClassName}_Impl"
|
||||
).apply {
|
||||
write(CodeSourceFileTemplate.getXposedInitImplFileByteArray(packageName, fModulePackageName, entryClassName))
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
|
@@ -40,9 +40,10 @@ import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.factory.processName
|
||||
import com.highcapable.yukihookapi.hook.log.*
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import java.lang.reflect.Constructor
|
||||
@@ -77,13 +78,9 @@ object YukiHookAPI {
|
||||
* 获取当前 Hook 框架的名称
|
||||
*
|
||||
* 从 [XposedBridge] 获取 TAG
|
||||
* @return [String] 无法获取会返回 unknown - [hasXposedBridge] 不存在会返回 invalid
|
||||
* @return [String] 无法获取会返回 unknown - [YukiHookBridge.hasXposedBridge] 不存在会返回 invalid
|
||||
*/
|
||||
val executorName
|
||||
get() = runCatching {
|
||||
(XposedBridge::class.java.getDeclaredField("TAG").apply { isAccessible = true }.get(null) as? String?)
|
||||
?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown"
|
||||
}.getOrNull() ?: "invalid"
|
||||
val executorName get() = YukiHookBridge.executorName
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 框架的版本
|
||||
@@ -91,7 +88,7 @@ object YukiHookAPI {
|
||||
* 获取 [XposedBridge.getXposedVersion]
|
||||
* @return [Int] 无法获取会返回 -1
|
||||
*/
|
||||
val executorVersion get() = runCatching { XposedBridge.getXposedVersion() }.getOrNull() ?: -1
|
||||
val executorVersion get() = YukiHookBridge.executorVersion
|
||||
|
||||
/**
|
||||
* 配置 YukiHookAPI
|
||||
@@ -169,7 +166,7 @@ object YukiHookAPI {
|
||||
* @param wrapper 代理包装 [PackageParamWrapper]
|
||||
*/
|
||||
internal fun onXposedLoaded(wrapper: PackageParamWrapper) =
|
||||
YukiHookXposedBridge.packageParamCallback?.invoke(PackageParam(wrapper).apply { printSplashLog() })
|
||||
YukiHookBridge.packageParamCallback?.invoke(PackageParam(wrapper).apply { printSplashLog() })
|
||||
|
||||
/**
|
||||
* 配置 [YukiHookAPI] 相关参数
|
||||
@@ -189,8 +186,8 @@ object YukiHookAPI {
|
||||
*/
|
||||
fun encase(initiate: PackageParam.() -> Unit) {
|
||||
isLoadedFromBaseContext = false
|
||||
if (hasXposedBridge)
|
||||
YukiHookXposedBridge.packageParamCallback = initiate
|
||||
if (YukiHookBridge.hasXposedBridge)
|
||||
YukiHookBridge.packageParamCallback = initiate
|
||||
else printNoXposedEnvLog()
|
||||
}
|
||||
|
||||
@@ -205,8 +202,8 @@ object YukiHookAPI {
|
||||
*/
|
||||
fun encase(vararg hooker: YukiBaseHooker) {
|
||||
isLoadedFromBaseContext = false
|
||||
if (hasXposedBridge)
|
||||
YukiHookXposedBridge.packageParamCallback = {
|
||||
if (YukiHookBridge.hasXposedBridge)
|
||||
YukiHookBridge.packageParamCallback = {
|
||||
if (hooker.isNotEmpty())
|
||||
hooker.forEach { it.assignInstance(packageParam = this) }
|
||||
else yLoggerE(msg = "Failed to passing \"encase\" method because your hooker param is empty")
|
||||
@@ -230,7 +227,7 @@ object YukiHookAPI {
|
||||
fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit) {
|
||||
isLoadedFromBaseContext = true
|
||||
when {
|
||||
hasXposedBridge && baseContext != null -> initiate.invoke(baseContext.packageParam.apply { printSplashLog() })
|
||||
YukiHookBridge.hasXposedBridge && baseContext != null -> initiate.invoke(baseContext.packageParam.apply { printSplashLog() })
|
||||
else -> printNoXposedEnvLog()
|
||||
}
|
||||
}
|
||||
@@ -251,7 +248,7 @@ object YukiHookAPI {
|
||||
*/
|
||||
fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker) {
|
||||
isLoadedFromBaseContext = true
|
||||
if (hasXposedBridge)
|
||||
if (YukiHookBridge.hasXposedBridge)
|
||||
(if (baseContext != null)
|
||||
if (hooker.isNotEmpty()) {
|
||||
printSplashLog()
|
||||
@@ -262,7 +259,7 @@ object YukiHookAPI {
|
||||
|
||||
/** 输出欢迎信息调试日志 */
|
||||
private fun printSplashLog() {
|
||||
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not() || YukiHookXposedBridge.isModulePackageXposedEnv) return
|
||||
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not()) return
|
||||
isShowSplashLogOnceTime = false
|
||||
yLoggerI(msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using $executorName API $executorVersion")
|
||||
}
|
||||
@@ -274,11 +271,6 @@ object YukiHookAPI {
|
||||
* 通过 baseContext 创建 Hook 入口类
|
||||
* @return [PackageParam]
|
||||
*/
|
||||
private val Context.packageParam get() = PackageParam(PackageParamWrapper(packageName, processName, classLoader, applicationInfo))
|
||||
|
||||
/**
|
||||
* 是否存在 [XposedBridge]
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal val hasXposedBridge get() = executorVersion >= 0
|
||||
private val Context.packageParam
|
||||
get() = PackageParam(PackageParamWrapper(HookEntryType.PACKAGE, packageName, processName, classLoader, applicationInfo))
|
||||
}
|
@@ -41,7 +41,7 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
*
|
||||
* 例子:com.example.module.hook.MainHook、com.example.module.hook.inject.MainInject、com.example.module.hook.custom.CustomClass
|
||||
*
|
||||
* 你的 xposed_init 入口将被自动生成为 --> 你的模块APP包名/hook/...可允许子包名存在.../你的入口类_YukiHookXposedInit 或自定义 [entryClassName]
|
||||
* 你的 xposed_init 入口将被自动生成为 --> 你的模块 APP 包名/hook/...可允许子包名存在.../你的入口类_YukiHookXposedInit 或自定义 [entryClassName]
|
||||
*
|
||||
* 例子:com.example.module.hook.MainHook_YukiHookXposedInit
|
||||
*
|
||||
|
@@ -37,19 +37,47 @@ import com.highcapable.yukihookapi.hook.factory.method
|
||||
* @param instance 当前实例的 [Class]
|
||||
* @param self 当前实例本身
|
||||
*/
|
||||
class CurrentClass(private val instance: Class<*>, private val self: Any) {
|
||||
class CurrentClass(@PublishedApi internal val instance: Class<*>, @PublishedApi internal val self: Any) {
|
||||
|
||||
/**
|
||||
* 调用父类实例
|
||||
* @return [SuperClass]
|
||||
*/
|
||||
fun superClass() = SuperClass()
|
||||
|
||||
/**
|
||||
* 调用当前实例中的变量
|
||||
* @param initiate 查找方法体
|
||||
* @return [FieldFinder.Result.Instance]
|
||||
*/
|
||||
fun field(initiate: FieldFinder.() -> Unit) = instance.field(initiate).get(self)
|
||||
inline fun field(initiate: FieldFinder.() -> Unit) = instance.field(initiate).get(self)
|
||||
|
||||
/**
|
||||
* 调用当前实例中的方法
|
||||
* @param initiate 查找方法体
|
||||
* @return [MethodFinder.Result.Instance]
|
||||
*/
|
||||
fun method(initiate: MethodFinder.() -> Unit) = instance.method(initiate).get(self)
|
||||
inline fun method(initiate: MethodFinder.() -> Unit) = instance.method(initiate).get(self)
|
||||
|
||||
/**
|
||||
* 当前类的父类实例的类操作对象
|
||||
*
|
||||
* - ❗请使用 [superClass] 方法来获取 [SuperClass]
|
||||
*/
|
||||
inner class SuperClass {
|
||||
|
||||
/**
|
||||
* 调用父类实例中的变量
|
||||
* @param initiate 查找方法体
|
||||
* @return [FieldFinder.Result.Instance]
|
||||
*/
|
||||
inline fun field(initiate: FieldFinder.() -> Unit) = instance.superclass.field(initiate).get(self)
|
||||
|
||||
/**
|
||||
* 调用父类实例中的方法
|
||||
* @param initiate 查找方法体
|
||||
* @return [MethodFinder.Result.Instance]
|
||||
*/
|
||||
inline fun method(initiate: MethodFinder.() -> Unit) = instance.superclass.method(initiate).get(self)
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/5/1.
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.bean
|
||||
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
|
||||
/**
|
||||
* 创建一个当前 Hook 的 [YukiResources] 接管类
|
||||
* @param instance 实例
|
||||
*/
|
||||
class HookResources(var instance: YukiResources? = null) {
|
||||
|
||||
override fun toString() = "[instance] $instance"
|
||||
}
|
@@ -25,13 +25,12 @@
|
||||
*
|
||||
* This file is Created by fankes on 2022/2/2.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "PropertyName")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core
|
||||
|
||||
import android.os.SystemClock
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.bean.HookClass
|
||||
import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
|
||||
@@ -40,21 +39,29 @@ import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerI
|
||||
import com.highcapable.yukihookapi.hook.param.HookParam
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
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
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Member
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 核心 Hook 实现类
|
||||
* [YukiHookAPI] 的 [Member] 核心 Hook 实现类
|
||||
*
|
||||
* 这是一个 API 对接类 - 实现原生对接 [XposedBridge]
|
||||
* 核心 API 对接 [YukiHookBridge.Hooker] 实现
|
||||
* @param packageParam 需要传入 [PackageParam] 实现方法调用
|
||||
* @param hookClass 要 Hook 的 [HookClass] 实例
|
||||
*/
|
||||
class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) {
|
||||
class YukiMemberHookCreater(private val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) {
|
||||
|
||||
/** 默认 Hook 回调优先级 */
|
||||
val PRIORITY_DEFAULT = 50
|
||||
|
||||
/** 延迟回调 Hook 方法结果 */
|
||||
val PRIORITY_LOWEST = -10000
|
||||
|
||||
/** 更快回调 Hook 方法结果 */
|
||||
val PRIORITY_HIGHEST = 10000
|
||||
|
||||
/**
|
||||
* Hook 模式定义
|
||||
@@ -65,13 +72,13 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
/** [hookClass] 找不到时出现的错误回调 */
|
||||
private var onHookClassNotFoundFailureCallback: ((Throwable) -> Unit)? = null
|
||||
|
||||
/** 是否对当前 [YukiHookCreater] 禁止执行 Hook 操作 */
|
||||
/** 是否对当前 [YukiMemberHookCreater] 禁止执行 Hook 操作 */
|
||||
@PublishedApi
|
||||
internal var isDisableCreaterRunHook = false
|
||||
|
||||
/** 设置要 Hook 的方法、构造类 */
|
||||
@PublishedApi
|
||||
internal var hookMembers = HashSet<MemberHookCreater>()
|
||||
internal var preHookMembers = HashSet<MemberHookCreater>()
|
||||
|
||||
/**
|
||||
* 得到当前被 Hook 的 [Class]
|
||||
@@ -85,25 +92,25 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
|
||||
/**
|
||||
* 注入要 Hook 的方法、构造类
|
||||
* @param priority Hook 优先级 - 默认 [PRIORITY_DEFAULT]
|
||||
* @param tag 可设置标签 - 在发生错误时方便进行调试
|
||||
* @param initiate 方法体
|
||||
* @return [MemberHookCreater.Result]
|
||||
*/
|
||||
inline fun injectMember(tag: String = "Default", initiate: MemberHookCreater.() -> Unit) =
|
||||
MemberHookCreater(tag).apply(initiate).apply { hookMembers.add(this) }.build()
|
||||
inline fun injectMember(priority: Int = PRIORITY_DEFAULT, tag: String = "Default", initiate: MemberHookCreater.() -> Unit) =
|
||||
MemberHookCreater(priority, tag).apply(initiate).apply { preHookMembers.add(this) }.build()
|
||||
|
||||
/**
|
||||
* Hook 执行入口
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
* @throws IllegalStateException 如果必要参数没有被设置
|
||||
* @return [Result]
|
||||
*/
|
||||
@PublishedApi
|
||||
@YukiPrivateApi
|
||||
internal fun hook(): Result {
|
||||
if (YukiHookAPI.hasXposedBridge.not()) return Result()
|
||||
return if (hookMembers.isEmpty()) error("Hook Members is empty, hook aborted")
|
||||
if (YukiHookBridge.hasXposedBridge.not()) return Result()
|
||||
/** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.PACKAGE] */
|
||||
if (packageParam.wrapper?.type == HookEntryType.RESOURCES) return Result()
|
||||
return if (preHookMembers.isEmpty()) error("Hook Members is empty, hook aborted")
|
||||
else Result().also {
|
||||
Thread {
|
||||
/** 延迟使得方法取到返回值 */
|
||||
@@ -111,7 +118,7 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
when {
|
||||
isDisableCreaterRunHook.not() && hookClass.instance != null -> {
|
||||
it.onPrepareHook?.invoke()
|
||||
hookMembers.forEach { m -> m.hook() }
|
||||
preHookMembers.forEach { m -> m.hook() }
|
||||
}
|
||||
isDisableCreaterRunHook.not() && hookClass.instance == null ->
|
||||
if (onHookClassNotFoundFailureCallback == null)
|
||||
@@ -126,9 +133,10 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
* Hook 核心功能实现类
|
||||
*
|
||||
* 查找和处理需要 Hook 的方法、构造类
|
||||
* @param priority Hook 优先级
|
||||
* @param tag 当前设置的标签
|
||||
*/
|
||||
inner class MemberHookCreater(var tag: String) {
|
||||
inner class MemberHookCreater(private val priority: Int, internal val tag: String) {
|
||||
|
||||
/** [beforeHook] 回调 */
|
||||
private var beforeHookCallback: (HookParam.() -> Unit)? = null
|
||||
@@ -386,23 +394,15 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
|
||||
/**
|
||||
* Hook 创建入口
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
* @return [Result]
|
||||
*/
|
||||
@PublishedApi
|
||||
@YukiPrivateApi
|
||||
internal fun build() = Result()
|
||||
|
||||
/**
|
||||
* Hook 执行入口
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
*/
|
||||
/** Hook 执行入口 */
|
||||
@PublishedApi
|
||||
@YukiPrivateApi
|
||||
internal fun hook() {
|
||||
if (YukiHookAPI.hasXposedBridge.not() || isDisableMemberRunHook) return
|
||||
if (YukiHookBridge.hasXposedBridge.not() || isDisableMemberRunHook) return
|
||||
if (hookClass.instance == null) {
|
||||
(hookClass.throwable ?: Throwable("HookClass [${hookClass.name}] not found")).also {
|
||||
onHookingFailureCallback?.invoke(it)
|
||||
@@ -411,11 +411,13 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
}
|
||||
return
|
||||
}
|
||||
/** 定义替换 Hook 的 [HookParam] */
|
||||
val replaceHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
|
||||
|
||||
/** 定义替换 Hook 回调方法体 */
|
||||
val replaceMent = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? {
|
||||
if (baseParam == null) return null
|
||||
return HookParam(createrInstance = this@YukiHookCreater, HookParamWrapper(baseParam)).let { param ->
|
||||
val replaceMent = object : YukiHookBridge.Hooker.YukiMemberReplacement(priority) {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper): Any? {
|
||||
return replaceHookParam.assign(wrapper).let { param ->
|
||||
try {
|
||||
if (replaceHookCallback != null || isReplaceHookOnlyResultMode)
|
||||
onHookLogMsg(msg = "Replace Hook Member [${member ?: "All Member $allMethodsName"}] done [$tag]")
|
||||
@@ -430,11 +432,16 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
}
|
||||
}
|
||||
|
||||
/** 定义前 Hook 的 [HookParam] */
|
||||
val beforeHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
|
||||
|
||||
/** 定义后 Hook 的 [HookParam] */
|
||||
val afterHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
|
||||
|
||||
/** 定义前后 Hook 回调方法体 */
|
||||
val beforeAfterHook = object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(baseParam: MethodHookParam?) {
|
||||
if (baseParam == null) return
|
||||
HookParam(createrInstance = this@YukiHookCreater, HookParamWrapper(baseParam)).also { param ->
|
||||
val beforeAfterHook = object : YukiHookBridge.Hooker.YukiMemberHook(priority) {
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
beforeHookParam.assign(wrapper).also { param ->
|
||||
runCatching {
|
||||
beforeHookCallback?.invoke(param)
|
||||
if (beforeHookCallback != null)
|
||||
@@ -447,9 +454,8 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(baseParam: MethodHookParam?) {
|
||||
if (baseParam == null) return
|
||||
HookParam(createrInstance = this@YukiHookCreater, HookParamWrapper(baseParam)).also { param ->
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
afterHookParam.assign(wrapper).also { param ->
|
||||
runCatching {
|
||||
afterHookCallback?.invoke(param)
|
||||
if (afterHookCallback != null)
|
||||
@@ -467,9 +473,9 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
member.also { member ->
|
||||
runCatching {
|
||||
if (isReplaceHookMode)
|
||||
XposedBridge.hookMethod(member, replaceMent)?.hookedMethod?.also { onHookedCallback?.invoke(it) }
|
||||
YukiHookBridge.Hooker.hookMethod(member, replaceMent)?.also { onHookedCallback?.invoke(it) }
|
||||
?: error("Hook Member [$member] failed")
|
||||
else XposedBridge.hookMethod(member, beforeAfterHook)?.hookedMethod?.also { onHookedCallback?.invoke(it) }
|
||||
else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)?.also { onHookedCallback?.invoke(it) }
|
||||
?: error("Hook Member [$member] failed")
|
||||
}.onFailure {
|
||||
onHookingFailureCallback?.invoke(it)
|
||||
@@ -493,23 +499,23 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
when (hookMemberMode) {
|
||||
HookMemberMode.HOOK_ALL_METHODS ->
|
||||
if (isReplaceHookMode)
|
||||
XposedBridge.hookAllMethods(hookClass.instance, allMethodsName, replaceMent).also {
|
||||
YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, replaceMent).also {
|
||||
if (it.isEmpty()) throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e.hookedMethod) }
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e) }
|
||||
}
|
||||
else XposedBridge.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook).also {
|
||||
else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook).also {
|
||||
if (it.isEmpty()) throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e.hookedMethod) }
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e) }
|
||||
}
|
||||
HookMemberMode.HOOK_ALL_CONSTRUCTORS ->
|
||||
if (isReplaceHookMode)
|
||||
XposedBridge.hookAllConstructors(hookClass.instance, replaceMent).also {
|
||||
YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, replaceMent).also {
|
||||
if (it.isEmpty()) throw NoSuchMethodError("No Constructor matched")
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e.hookedMethod) }
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e) }
|
||||
}
|
||||
else XposedBridge.hookAllConstructors(hookClass.instance, beforeAfterHook).also {
|
||||
else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook).also {
|
||||
if (it.isEmpty()) throw NoSuchMethodError("No Constructor matched")
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e.hookedMethod) }
|
||||
else it.forEach { e -> onHookedCallback?.invoke(e) }
|
||||
}
|
||||
else -> error("Hooked got a no error possible")
|
||||
}
|
||||
@@ -550,7 +556,7 @@ class YukiHookCreater(private val packageParam: PackageParam, @PublishedApi inte
|
||||
*/
|
||||
internal val isNotIgnoredNoSuchMemberFailure get() = onNoSuchMemberFailureCallback == null && isNotIgnoredHookingFailure
|
||||
|
||||
override fun toString() = "[tag] $tag [class] $hookClass [member] $member $allMethodsName [mode] $hookMemberMode"
|
||||
override fun toString() = "[tag] $tag [priority] $priority [class] $hookClass [member] $member $allMethodsName [mode] $hookMemberMode"
|
||||
|
||||
/**
|
||||
* 监听 Hook 结果实现类
|
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/5/1.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.bean.HookResources
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerI
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 的 [Resources] 核心 Hook 实现类
|
||||
*
|
||||
* @param packageParam 需要传入 [PackageParam] 实现方法调用
|
||||
* @param hookResources 要 Hook 的 [HookResources] 实例
|
||||
*/
|
||||
class YukiResourcesHookCreater(private val packageParam: PackageParam, @PublishedApi internal val hookResources: HookResources) {
|
||||
|
||||
/** 设置要 Hook 的 Resources */
|
||||
@PublishedApi
|
||||
internal var preHookResources = HashSet<ResourcesHookCreater>()
|
||||
|
||||
/**
|
||||
* 注入要 Hook 的 Resources
|
||||
* @param tag 可设置标签 - 在发生错误时方便进行调试
|
||||
* @param initiate 方法体
|
||||
* @return [ResourcesHookCreater.Result]
|
||||
*/
|
||||
inline fun injectResource(tag: String = "Default", initiate: ResourcesHookCreater.() -> Unit) =
|
||||
ResourcesHookCreater(tag).apply(initiate).apply { preHookResources.add(this) }.build()
|
||||
|
||||
/**
|
||||
* Hook 执行入口
|
||||
* @throws IllegalStateException 如果必要参数没有被设置
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun hook() {
|
||||
if (YukiHookBridge.hasXposedBridge.not()) return
|
||||
/** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.RESOURCES] */
|
||||
if (packageParam.wrapper?.type == HookEntryType.PACKAGE) return
|
||||
if (preHookResources.isEmpty()) error("Hook Resources is empty, hook aborted")
|
||||
preHookResources.forEach { it.hook() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 核心功能实现类
|
||||
*
|
||||
* 查找和处理需要 Hook 的 Resources
|
||||
* @param tag 当前设置的标签
|
||||
*/
|
||||
inner class ResourcesHookCreater(private val tag: String) {
|
||||
|
||||
/**
|
||||
* 模块 APP Resources 替换实例
|
||||
* @param resId Resources Id
|
||||
*/
|
||||
private inner class ModuleResFwd(var resId: Int)
|
||||
|
||||
/** 是否对当前 [ResourcesHookCreater] 禁止执行 Hook 操作 */
|
||||
@PublishedApi
|
||||
internal var isDisableCreaterRunHook = false
|
||||
|
||||
/** 当前的查找条件 */
|
||||
@PublishedApi
|
||||
internal var conditions: ConditionFinder? = null
|
||||
|
||||
/** Hook 出现错误回调 */
|
||||
private var onHookFailureCallback: ((Throwable) -> Unit)? = null
|
||||
|
||||
/** 当前的替换值实例 */
|
||||
private var replaceInstance: Any? = null
|
||||
|
||||
/** 当前的布局注入实例 */
|
||||
private var layoutInstance: (YukiResources.LayoutInflatedParam.() -> Unit)? = null
|
||||
|
||||
/** 直接设置需要替换的 Resources Id */
|
||||
var resourceId = -1
|
||||
|
||||
/**
|
||||
* 设置 Resources 查找条件
|
||||
*
|
||||
* 若你设置了 [resourceId] 则此方法将不会被使用
|
||||
* @param initiate 条件方法体
|
||||
*/
|
||||
inline fun conditions(initiate: ConditionFinder.() -> Unit) {
|
||||
conditions = ConditionFinder().apply(initiate).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换指定 Resources 为指定的值
|
||||
* @param any 可以是任何你想替换的类型 - 但要注意若当前类型不支持可能会报错
|
||||
*/
|
||||
fun replaceTo(any: Any) {
|
||||
replaceInstance = any
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换指定 Resources 为 true
|
||||
*
|
||||
* - ❗确保目标替换 Resources 的类型为 [Boolean]
|
||||
*/
|
||||
fun replaceToTrue() = replaceTo(any = true)
|
||||
|
||||
/**
|
||||
* 替换指定 Resources 为 false
|
||||
*
|
||||
* - ❗确保目标替换 Resources 的类型为 [Boolean]
|
||||
*/
|
||||
fun replaceToFalse() = replaceTo(any = false)
|
||||
|
||||
/**
|
||||
* 替换为当前 Xposed 模块的 Resources
|
||||
*
|
||||
* 你可以直接使用模块的 R.string.xxx、R.mipmap.xxx、R.drawable.xxx 替换 Hook APP 的 Resources
|
||||
* @param resId 当前 Xposed 模块的 Resources Id
|
||||
*/
|
||||
fun replaceToModuleResource(resId: Int) {
|
||||
replaceInstance = ModuleResFwd(resId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 作为装载的布局注入
|
||||
* @param initiate [YukiResources.LayoutInflatedParam] 方法体
|
||||
*/
|
||||
fun injectAsLayout(initiate: YukiResources.LayoutInflatedParam.() -> Unit) {
|
||||
layoutInstance = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动兼容当前替换的 Resources 类型
|
||||
* @param any 替换的任意类型
|
||||
* @return [Any]
|
||||
*/
|
||||
private fun compat(any: Any?) = if (any is ModuleResFwd) packageParam.moduleAppResources.fwd(any.resId) else any
|
||||
|
||||
/**
|
||||
* Hook 创建入口
|
||||
* @return [Result]
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build() = Result()
|
||||
|
||||
/** Hook 执行入口 */
|
||||
@PublishedApi
|
||||
internal fun hook() {
|
||||
if (isDisableCreaterRunHook.not()) runCatching {
|
||||
when {
|
||||
conditions == null -> yLoggerE(msg = "You must set the conditions before hook a Resources [$tag]")
|
||||
replaceInstance == null && layoutInstance == null -> yLoggerE(msg = "Resources Hook got null replaceInstance [$tag]")
|
||||
packageParam.wrapper?.type == HookEntryType.RESOURCES && hookResources.instance != null ->
|
||||
if (resourceId == -1) when {
|
||||
layoutInstance != null ->
|
||||
hookResources.instance?.hookLayout(
|
||||
packageParam.packageName, conditions!!.type,
|
||||
conditions!!.name, layoutInstance!!
|
||||
).run { onHookLogMsg(msg = "Hook Resources Layout $conditions done [$tag]") }
|
||||
else -> hookResources.instance?.setReplacement(
|
||||
packageParam.packageName, conditions!!.type,
|
||||
conditions!!.name, compat(replaceInstance)
|
||||
).run { onHookLogMsg(msg = "Hook Resources Value $conditions done [$tag]") }
|
||||
} else when {
|
||||
layoutInstance != null -> hookResources.instance?.hookLayout(resourceId, layoutInstance!!)
|
||||
.run { onHookLogMsg(msg = "Hook Resources Layout Id $resourceId done [$tag]") }
|
||||
else -> hookResources.instance?.setReplacement(resourceId, compat(replaceInstance))
|
||||
.run { onHookLogMsg(msg = "Hook Resources Value Id $resourceId done [$tag]") }
|
||||
}
|
||||
packageParam.wrapper?.type == HookEntryType.ZYGOTE ->
|
||||
if (resourceId == -1) when {
|
||||
layoutInstance != null ->
|
||||
YukiResources.hookSystemWideLayout(
|
||||
packageParam.packageName, conditions!!.type,
|
||||
conditions!!.name, layoutInstance!!
|
||||
).run { onHookLogMsg(msg = "Hook Wide Resources Layout $conditions done [$tag]") }
|
||||
else -> YukiResources.setSystemWideReplacement(
|
||||
packageParam.packageName, conditions!!.type,
|
||||
conditions!!.name, compat(replaceInstance)
|
||||
).run { onHookLogMsg(msg = "Hook Wide Resources Value $conditions done [$tag]") }
|
||||
} else when {
|
||||
layoutInstance != null -> YukiResources.hookSystemWideLayout(resourceId, layoutInstance!!)
|
||||
.run { onHookLogMsg(msg = "Hook Wide Resources Layout Id $resourceId done [$tag]") }
|
||||
else -> YukiResources.setSystemWideReplacement(resourceId, compat(replaceInstance))
|
||||
.run { onHookLogMsg(msg = "Hook Wide Resources Value Id $resourceId done [$tag]") }
|
||||
}
|
||||
else -> yLoggerE(msg = "Resources Hook type is invalid [$tag]")
|
||||
}
|
||||
}.onFailure {
|
||||
if (onHookFailureCallback == null)
|
||||
yLoggerE(msg = "Resources Hook got an Exception [$tag]", e = it)
|
||||
else onHookFailureCallback?.invoke(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 过程中开启了 [YukiHookAPI.Configs.isDebug] 输出调试信息
|
||||
* @param msg 调试日志内容
|
||||
*/
|
||||
private fun onHookLogMsg(msg: String) {
|
||||
if (YukiHookAPI.Configs.isDebug) yLoggerI(msg = msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resources 查找条件实现类
|
||||
*/
|
||||
inner class ConditionFinder {
|
||||
|
||||
/** Resources 类型 */
|
||||
internal var type = ""
|
||||
|
||||
/** 设置 Resources 名称 */
|
||||
var name = ""
|
||||
|
||||
/** 设置 Resources 类型为动画 */
|
||||
fun anim() {
|
||||
type = "anim"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为属性动画 */
|
||||
fun animator() {
|
||||
type = "animator"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为布朗(Boolean) */
|
||||
fun bool() {
|
||||
type = "bool"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为颜色(Color) */
|
||||
fun color() {
|
||||
type = "color"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为尺寸(Dimention) */
|
||||
fun dimen() {
|
||||
type = "dimen"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为 Drawable */
|
||||
fun drawable() {
|
||||
type = "drawable"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为整型(Integer) */
|
||||
fun integer() {
|
||||
type = "integer"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为布局(Layout) */
|
||||
fun layout() {
|
||||
type = "layout"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为 Plurals */
|
||||
fun plurals() {
|
||||
type = "plurals"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为字符串(String) */
|
||||
fun string() {
|
||||
type = "string"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为 Xml */
|
||||
fun xml() {
|
||||
type = "xml"
|
||||
}
|
||||
|
||||
/** 设置 Resources 类型为位图(Mipmap) */
|
||||
fun mipmap() {
|
||||
type = "mipmap"
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查找对象实例
|
||||
* @return [ConditionFinder]
|
||||
* @throws IllegalStateException 如果没有设置 [name] 或 [type]
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): ConditionFinder {
|
||||
when {
|
||||
name.isBlank() -> error("Resources Hook condition name cannot be empty [$tag]")
|
||||
type.isBlank() -> error("Resources Hook condition type cannot be empty [$tag]")
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun toString() = "[${if (packageParam.wrapper?.type == HookEntryType.ZYGOTE) "android." else ""}R.$type.$name]"
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听全部 Hook 结果实现类
|
||||
*
|
||||
* 可在这里处理失败事件监听
|
||||
*/
|
||||
inner class Result {
|
||||
|
||||
/**
|
||||
* 创建监听事件方法体
|
||||
* @param initiate 方法体
|
||||
* @return [Result] 可继续向下监听
|
||||
*/
|
||||
inline fun result(initiate: Result.() -> Unit) = apply(initiate)
|
||||
|
||||
/**
|
||||
* 添加执行 Hook 需要满足的条件
|
||||
*
|
||||
* 不满足条件将直接停止 Hook
|
||||
* @param initiate 条件方法体
|
||||
* @return [Result] 可继续向下监听
|
||||
*/
|
||||
inline fun by(initiate: () -> Boolean): Result {
|
||||
isDisableCreaterRunHook = (runCatching { initiate() }.getOrNull() ?: false).not()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听 Hook 过程发生错误的回调方法
|
||||
* @param initiate 回调错误
|
||||
* @return [Result] 可继续向下监听
|
||||
*/
|
||||
fun onHookingFailure(initiate: (Throwable) -> Unit): Result {
|
||||
onHookFailureCallback = initiate
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略 Hook 过程出现的错误
|
||||
* @return [Result] 可继续向下监听
|
||||
*/
|
||||
fun ignoredHookingFailure(): Result {
|
||||
onHookingFailure {}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "[tag] $tag [conditions] $conditions [replaceInstance] $replaceInstance [layoutInstance] $layoutInstance"
|
||||
}
|
||||
}
|
@@ -31,9 +31,10 @@ package com.highcapable.yukihookapi.hook.core.finder
|
||||
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
|
||||
@@ -44,17 +45,23 @@ import java.lang.reflect.Constructor
|
||||
* [Constructor] 查找类
|
||||
*
|
||||
* 可通过指定类型查找指定构造方法
|
||||
* @param hookInstance 当前 Hook 实例 - 填写后将自动设置 [YukiHookCreater.MemberHookCreater.member]
|
||||
* @param hookInstance 当前 Hook 实例 - 填写后将自动设置 [YukiMemberHookCreater.MemberHookCreater.member]
|
||||
* @param classSet 当前需要查找的 [Class] 实例
|
||||
*/
|
||||
class ConstructorFinder(
|
||||
@property:YukiPrivateApi
|
||||
override val hookInstance: YukiHookCreater.MemberHookCreater? = null,
|
||||
override val hookInstance: YukiMemberHookCreater.MemberHookCreater? = null,
|
||||
@property:YukiPrivateApi
|
||||
override val classSet: Class<*>? = null
|
||||
) : BaseFinder(tag = "Constructor", hookInstance, classSet) {
|
||||
|
||||
/** 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater] */
|
||||
/** 当前使用的 [classSet] */
|
||||
private var usedClassSet = classSet
|
||||
|
||||
/** 是否在未找到后继续在当前 [classSet] 的父类中查找 */
|
||||
private var isFindInSuperClass = false
|
||||
|
||||
/** 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater] */
|
||||
private var isBindToHooker = false
|
||||
|
||||
/** 当前重查找结果回调 */
|
||||
@@ -136,16 +143,31 @@ class ConstructorFinder(
|
||||
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置在 [classSet] 的所有父类中查找当前 [Constructor]
|
||||
*
|
||||
* - ❗若当前 [classSet] 的父类较多可能会耗时 - API 会自动循环到父类继承是 [Any] 前的最后一个类
|
||||
* @param isOnlySuperClass 是否仅在当前 [classSet] 的父类中查找 - 若父类是 [Any] 则不会生效
|
||||
*/
|
||||
fun superClass(isOnlySuperClass: Boolean = false) {
|
||||
isFindInSuperClass = true
|
||||
if (isOnlySuperClass && classSet?.hasExtends == true) usedClassSet = classSet.superclass
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到构造方法
|
||||
* @return [Constructor]
|
||||
* @throws NoSuchMethodError 如果找不到构造方法
|
||||
*/
|
||||
private val result get() = ReflectionTool.findConstructor(classSet, orderIndex, matchIndex, modifiers, paramCount, paramTypes)
|
||||
private val result
|
||||
get() = ReflectionTool.findConstructor(
|
||||
usedClassSet, orderIndex, matchIndex, modifiers,
|
||||
paramCount, paramTypes, isFindInSuperClass
|
||||
)
|
||||
|
||||
/**
|
||||
* 设置实例
|
||||
* @param isBind 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater]
|
||||
* @param isBind 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater]
|
||||
* @param constructor 当前找到的 [Constructor]
|
||||
*/
|
||||
private fun setInstance(isBind: Boolean, constructor: Constructor<*>) {
|
||||
@@ -205,13 +227,8 @@ class ConstructorFinder(
|
||||
inline fun constructor(initiate: ConstructorFinder.() -> Unit) =
|
||||
Result().apply { remedyPlans.add(Pair(ConstructorFinder(hookInstance, classSet).apply(initiate), this)) }
|
||||
|
||||
/**
|
||||
* 开始重查找
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
*/
|
||||
/** 开始重查找 */
|
||||
@PublishedApi
|
||||
@YukiPrivateApi
|
||||
internal fun build() {
|
||||
if (classSet == null) return
|
||||
if (remedyPlans.isNotEmpty()) run {
|
||||
|
@@ -32,9 +32,10 @@ package com.highcapable.yukihookapi.hook.core.finder
|
||||
import android.os.SystemClock
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
|
||||
import com.highcapable.yukihookapi.hook.utils.runBlocking
|
||||
import java.lang.reflect.Field
|
||||
@@ -48,11 +49,17 @@ import java.lang.reflect.Field
|
||||
*/
|
||||
class FieldFinder(
|
||||
@property:YukiPrivateApi
|
||||
override val hookInstance: YukiHookCreater.MemberHookCreater? = null,
|
||||
override val hookInstance: YukiMemberHookCreater.MemberHookCreater? = null,
|
||||
@property:YukiPrivateApi
|
||||
override val classSet: Class<*>? = null
|
||||
) : BaseFinder(tag = "Field", hookInstance, classSet) {
|
||||
|
||||
/** 当前使用的 [classSet] */
|
||||
private var usedClassSet = classSet
|
||||
|
||||
/** 是否在未找到后继续在当前 [classSet] 的父类中查找 */
|
||||
private var isFindInSuperClass = false
|
||||
|
||||
/** [ModifierRules] 实例 */
|
||||
@PublishedApi
|
||||
internal var modifiers: ModifierRules? = null
|
||||
@@ -121,11 +128,22 @@ class FieldFinder(
|
||||
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置在 [classSet] 的所有父类中查找当前 [Field]
|
||||
*
|
||||
* - ❗若当前 [classSet] 的父类较多可能会耗时 - API 会自动循环到父类继承是 [Any] 前的最后一个类
|
||||
* @param isOnlySuperClass 是否仅在当前 [classSet] 的父类中查找 - 若父类是 [Any] 则不会生效
|
||||
*/
|
||||
fun superClass(isOnlySuperClass: Boolean = false) {
|
||||
isFindInSuperClass = true
|
||||
if (isOnlySuperClass && classSet?.hasExtends == true) usedClassSet = classSet.superclass
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到变量处理结果
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
* @param isBind 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater]
|
||||
* @param isBind 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater]
|
||||
* @return [Result]
|
||||
* @throws IllegalStateException 如果 [name] 没有被设置
|
||||
*/
|
||||
@@ -133,7 +151,8 @@ class FieldFinder(
|
||||
override fun build(isBind: Boolean) = try {
|
||||
if (classSet != null) {
|
||||
runBlocking {
|
||||
memberInstance = ReflectionTool.findField(classSet, orderIndex, matchIndex, name, modifiers, type.compat())
|
||||
memberInstance =
|
||||
ReflectionTool.findField(usedClassSet, orderIndex, matchIndex, name, modifiers, type.compat(), isFindInSuperClass)
|
||||
}.result { onHookLogMsg(msg = "Find Field [${memberInstance}] takes ${it}ms [${hookTag}]") }
|
||||
Result()
|
||||
} else Result(isNoSuch = true, Throwable("classSet is null"))
|
||||
|
@@ -31,9 +31,10 @@ package com.highcapable.yukihookapi.hook.core.finder
|
||||
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
|
||||
@@ -44,17 +45,23 @@ import java.lang.reflect.Method
|
||||
* [Method] 查找类
|
||||
*
|
||||
* 可通过指定类型查找指定方法
|
||||
* @param hookInstance 当前 Hook 实例 - 填写后将自动设置 [YukiHookCreater.MemberHookCreater.member]
|
||||
* @param hookInstance 当前 Hook 实例 - 填写后将自动设置 [YukiMemberHookCreater.MemberHookCreater.member]
|
||||
* @param classSet 当前需要查找的 [Class] 实例
|
||||
*/
|
||||
class MethodFinder(
|
||||
@property:YukiPrivateApi
|
||||
override val hookInstance: YukiHookCreater.MemberHookCreater? = null,
|
||||
override val hookInstance: YukiMemberHookCreater.MemberHookCreater? = null,
|
||||
@property:YukiPrivateApi
|
||||
override val classSet: Class<*>? = null
|
||||
) : BaseFinder(tag = "Method", hookInstance, classSet) {
|
||||
|
||||
/** 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater] */
|
||||
/** 当前使用的 [classSet] */
|
||||
private var usedClassSet = classSet
|
||||
|
||||
/** 是否在未找到后继续在当前 [classSet] 的父类中查找 */
|
||||
private var isFindInSuperClass = false
|
||||
|
||||
/** 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater] */
|
||||
private var isBindToHooker = false
|
||||
|
||||
/** 当前重查找结果回调 */
|
||||
@@ -182,17 +189,32 @@ class MethodFinder(
|
||||
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置在 [classSet] 的所有父类中查找当前 [Method]
|
||||
*
|
||||
* - ❗若当前 [classSet] 的父类较多可能会耗时 - API 会自动循环到父类继承是 [Any] 前的最后一个类
|
||||
* @param isOnlySuperClass 是否仅在当前 [classSet] 的父类中查找 - 若父类是 [Any] 则不会生效
|
||||
*/
|
||||
fun superClass(isOnlySuperClass: Boolean = false) {
|
||||
isFindInSuperClass = true
|
||||
if (isOnlySuperClass && classSet?.hasExtends == true) usedClassSet = classSet.superclass
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到方法
|
||||
* @return [Method]
|
||||
* @throws NoSuchMethodError 如果找不到方法
|
||||
*/
|
||||
private val result
|
||||
get() = ReflectionTool.findMethod(classSet, orderIndex, matchIndex, name, modifiers, returnType.compat(), paramCount, paramTypes)
|
||||
get() = ReflectionTool.findMethod(
|
||||
usedClassSet, orderIndex, matchIndex,
|
||||
name, modifiers, returnType.compat(),
|
||||
paramCount, paramTypes, isFindInSuperClass
|
||||
)
|
||||
|
||||
/**
|
||||
* 设置实例
|
||||
* @param isBind 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater]
|
||||
* @param isBind 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater]
|
||||
* @param method 当前找到的 [Method]
|
||||
*/
|
||||
private fun setInstance(isBind: Boolean, method: Method) {
|
||||
@@ -204,7 +226,7 @@ class MethodFinder(
|
||||
* 得到方法结果
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
* @param isBind 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater]
|
||||
* @param isBind 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater]
|
||||
* @return [Result]
|
||||
*/
|
||||
@YukiPrivateApi
|
||||
@@ -254,13 +276,8 @@ class MethodFinder(
|
||||
inline fun method(initiate: MethodFinder.() -> Unit) =
|
||||
Result().apply { remedyPlans.add(Pair(MethodFinder(hookInstance, classSet).apply(initiate), this)) }
|
||||
|
||||
/**
|
||||
* 开始重查找
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
*/
|
||||
/** 开始重查找 */
|
||||
@PublishedApi
|
||||
@YukiPrivateApi
|
||||
internal fun build() {
|
||||
if (classSet == null) return
|
||||
if (remedyPlans.isNotEmpty()) run {
|
||||
|
@@ -31,11 +31,12 @@ import android.os.SystemClock
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerI
|
||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import java.lang.reflect.Member
|
||||
import kotlin.math.abs
|
||||
|
||||
@@ -47,7 +48,7 @@ import kotlin.math.abs
|
||||
*/
|
||||
abstract class BaseFinder(
|
||||
private val tag: String,
|
||||
open val hookInstance: YukiHookCreater.MemberHookCreater? = null,
|
||||
open val hookInstance: YukiMemberHookCreater.MemberHookCreater? = null,
|
||||
open val classSet: Class<*>? = null
|
||||
) {
|
||||
|
||||
@@ -170,14 +171,14 @@ abstract class BaseFinder(
|
||||
* @param msg 调试日志内容
|
||||
*/
|
||||
internal fun onHookLogMsg(msg: String) {
|
||||
if (YukiHookAPI.Configs.isDebug && YukiHookAPI.hasXposedBridge) yLoggerI(msg = msg)
|
||||
if (YukiHookAPI.Configs.isDebug && YukiHookBridge.hasXposedBridge) yLoggerI(msg = msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到结果
|
||||
*
|
||||
* - ❗此功能交由方法体自动完成 - 你不应该手动调用此方法
|
||||
* @param isBind 是否将结果设置到目标 [YukiHookCreater.MemberHookCreater]
|
||||
* @param isBind 是否将结果设置到目标 [YukiMemberHookCreater.MemberHookCreater]
|
||||
* @return [Any]
|
||||
*/
|
||||
@YukiPrivateApi
|
||||
|
@@ -29,7 +29,6 @@
|
||||
|
||||
package com.highcapable.yukihookapi.hook.factory
|
||||
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.bean.CurrentClass
|
||||
import com.highcapable.yukihookapi.hook.bean.HookClass
|
||||
import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
|
||||
@@ -37,6 +36,7 @@ import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
|
||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
@@ -63,6 +63,12 @@ val HookClass.normalClass get() = instance
|
||||
*/
|
||||
val String.hasClass get() = hasClass(loader = null)
|
||||
|
||||
/**
|
||||
* 当前 [Class] 是否有继承关系 - 父类是 [Any] 将被认为没有继承关系
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val Class<*>.hasExtends get() = superclass.name != "java.lang.Object"
|
||||
|
||||
/**
|
||||
* 通过字符串转换为实体类
|
||||
* @param name [Class] 的完整包名+名称
|
||||
@@ -74,7 +80,7 @@ fun classOf(name: String, loader: ClassLoader? = null): Class<*> {
|
||||
val hashCode = ("[$name][$loader]").hashCode()
|
||||
return MemberCacheStore.findClass(hashCode) ?: run {
|
||||
when {
|
||||
YukiHookAPI.hasXposedBridge ->
|
||||
YukiHookBridge.hasXposedBridge ->
|
||||
runCatching { XposedHelpers.findClassIfExists(name, loader) }.getOrNull()
|
||||
?: when (loader) {
|
||||
null -> Class.forName(name)
|
||||
|
@@ -46,13 +46,13 @@ import java.io.File
|
||||
import java.io.FileReader
|
||||
|
||||
/**
|
||||
* 在 [IYukiHookXposedInit] 中装载 [YukiHookAPI.Configs]
|
||||
* @param initiate Hook 方法体
|
||||
* 在 [IYukiHookXposedInit] 中调用 [YukiHookAPI.configs]
|
||||
* @param initiate 配置方法体
|
||||
*/
|
||||
inline fun IYukiHookXposedInit.configs(initiate: YukiHookAPI.Configs.() -> Unit) = YukiHookAPI.configs(initiate)
|
||||
|
||||
/**
|
||||
* 在 [IYukiHookXposedInit] 中装载 [YukiHookAPI]
|
||||
* 在 [IYukiHookXposedInit] 中调用 [YukiHookAPI.encase]
|
||||
* @param initiate Hook 方法体
|
||||
*/
|
||||
fun IYukiHookXposedInit.encase(initiate: PackageParam.() -> Unit) = YukiHookAPI.encase(initiate)
|
||||
@@ -102,6 +102,12 @@ val Context.processName
|
||||
packageName ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)
|
||||
* @return [Boolean] 是否支持
|
||||
*/
|
||||
val Any?.isSupportResourcesHook get() = YukiHookModuleStatus.hasResourcesHook()
|
||||
|
||||
/**
|
||||
* 判断模块是否在 Xposed 或太极、无极中激活
|
||||
* @return [Boolean] 是否激活
|
||||
|
@@ -35,8 +35,6 @@ import de.robv.android.xposed.XposedBridge
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - D
|
||||
*
|
||||
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
|
||||
* @param msg 日志打印的内容
|
||||
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
|
||||
*/
|
||||
@@ -46,8 +44,6 @@ internal fun yLoggerD(msg: String, isDisableLog: Boolean = false) {
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - I
|
||||
*
|
||||
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
|
||||
* @param msg 日志打印的内容
|
||||
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
|
||||
*/
|
||||
@@ -57,8 +53,6 @@ internal fun yLoggerI(msg: String, isDisableLog: Boolean = false) {
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - W
|
||||
*
|
||||
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
|
||||
* @param msg 日志打印的内容
|
||||
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
|
||||
*/
|
||||
@@ -68,8 +62,6 @@ internal fun yLoggerW(msg: String, isDisableLog: Boolean = false) {
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - E
|
||||
*
|
||||
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
|
||||
* @param msg 日志打印的内容
|
||||
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
|
||||
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
|
||||
|
@@ -29,7 +29,7 @@
|
||||
|
||||
package com.highcapable.yukihookapi.hook.param
|
||||
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Member
|
||||
@@ -37,16 +37,26 @@ import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* Hook 方法、构造类的目标对象实现类
|
||||
* @param createrInstance [YukiHookCreater] 的实例对象
|
||||
* @param createrInstance [YukiMemberHookCreater] 的实例对象
|
||||
* @param wrapper [HookParam] 的参数包装类实例
|
||||
*/
|
||||
class HookParam(private val createrInstance: YukiHookCreater, private val wrapper: HookParamWrapper) {
|
||||
class HookParam(private val createrInstance: YukiMemberHookCreater, private var wrapper: HookParamWrapper? = null) {
|
||||
|
||||
/**
|
||||
* 在回调中设置 [HookParam] 使用的 [HookParamWrapper]
|
||||
* @param wrapper [HookParamWrapper] 实例
|
||||
* @return [HookParam]
|
||||
*/
|
||||
internal fun assign(wrapper: HookParamWrapper): HookParam {
|
||||
this.wrapper = wrapper
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 对象 [method] or [constructor] 的参数对象数组
|
||||
* @return [Array]
|
||||
*/
|
||||
val args get() = wrapper.args ?: arrayOf(0)
|
||||
val args get() = wrapper?.args ?: arrayOf(0)
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 实例的对象
|
||||
@@ -55,36 +65,36 @@ class HookParam(private val createrInstance: YukiHookCreater, private val wrappe
|
||||
* @return [Any]
|
||||
* @throws IllegalStateException 如果对象为空
|
||||
*/
|
||||
val instance get() = wrapper.instance ?: error("HookParam instance got null! Is this a static member?")
|
||||
val instance get() = wrapper?.instance ?: error("HookParam instance got null! Is this a static member?")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 实例的类对象
|
||||
* @return [Class]
|
||||
*/
|
||||
val instanceClass get() = wrapper.instance?.javaClass ?: createrInstance.instanceClass
|
||||
val instanceClass get() = wrapper?.instance?.javaClass ?: createrInstance.instanceClass
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 对象的方法
|
||||
* @return [Method]
|
||||
* @throws IllegalStateException 如果 [Method] 为空或方法类型不是 [Method]
|
||||
*/
|
||||
val method get() = wrapper.member as? Method? ?: error("Current hook Method type is wrong or null")
|
||||
val method get() = wrapper?.member as? Method? ?: error("Current hook Method type is wrong or null")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 对象的构造方法
|
||||
* @return [Constructor]
|
||||
* @throws IllegalStateException 如果 [Constructor] 为空或方法类型不是 [Constructor]
|
||||
*/
|
||||
val constructor get() = wrapper.member 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")
|
||||
|
||||
/**
|
||||
* 获取、设置当前 Hook 对象的 [method] or [constructor] 的返回值
|
||||
* @return [Any] or null
|
||||
*/
|
||||
var result: Any?
|
||||
get() = wrapper.result
|
||||
get() = wrapper?.result
|
||||
set(value) {
|
||||
wrapper.result = value
|
||||
wrapper?.result = value
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +130,7 @@ class HookParam(private val createrInstance: YukiHookCreater, private val wrappe
|
||||
* @param args 参数实例
|
||||
* @return [T]
|
||||
*/
|
||||
fun <T> Member.invokeOriginal(vararg args: Any?) = wrapper.invokeOriginalMember(member = this, *args) as? T?
|
||||
fun <T> Member.invokeOriginal(vararg args: Any?) = wrapper?.invokeOriginalMember(member = this, *args) as? T?
|
||||
|
||||
/**
|
||||
* 设置当前 Hook 对象方法的 [result] 返回值为 true
|
||||
@@ -286,7 +296,7 @@ class HookParam(private val createrInstance: YukiHookCreater, private val wrappe
|
||||
if (index < 0) error("HookParam Method args index must be >= 0")
|
||||
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}")
|
||||
wrapper.setArgs(index, any)
|
||||
wrapper?.setArgs(index, any)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -31,14 +31,21 @@ package com.highcapable.yukihookapi.hook.param
|
||||
|
||||
import android.app.Application
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.res.Resources
|
||||
import com.highcapable.yukihookapi.hook.bean.HookClass
|
||||
import com.highcapable.yukihookapi.hook.bean.HookResources
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||
import com.highcapable.yukihookapi.hook.core.YukiResourcesHookCreater
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.hasClass
|
||||
import com.highcapable.yukihookapi.hook.factory.hookClass
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
|
||||
@@ -46,7 +53,7 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
* 装载 Hook 的目标 APP 入口对象实现类
|
||||
* @param wrapper [PackageParam] 的参数包装类实例 - 默认是空的
|
||||
*/
|
||||
open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
open class PackageParam(@PublishedApi internal var wrapper: PackageParamWrapper? = null) {
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 [ClassLoader]
|
||||
@@ -63,13 +70,6 @@ open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
*/
|
||||
val appInfo get() = wrapper?.appInfo ?: YukiHookAppHelper.currentApplicationInfo() ?: ApplicationInfo()
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 [Application] 实例
|
||||
* @return [Application]
|
||||
* @throws IllegalStateException 如果 [Application] 是空的
|
||||
*/
|
||||
val appContext get() = YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的进程名称
|
||||
*
|
||||
@@ -84,11 +84,29 @@ open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
*/
|
||||
val packageName get() = wrapper?.packageName ?: YukiHookAppHelper.currentPackageName() ?: ""
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 [Application] 实例
|
||||
*
|
||||
* - ❗首次装载可能是空的 - 请延迟一段时间再获取
|
||||
* @return [Application]
|
||||
* @throws IllegalStateException 如果 [Application] 是空的
|
||||
*/
|
||||
val appContext get() = YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 Resources
|
||||
*
|
||||
* - ❗你只能在 [HookResources.hook] 方法体内或 [appContext] 装载完毕时进行调用
|
||||
* @return [Resources]
|
||||
* @throws IllegalStateException 如果当前处于 [loadZygote] 或 [appContext] 尚未加载
|
||||
*/
|
||||
val appResources get() = wrapper?.appResources ?: appContext.resources ?: error("You cannot call to appResources in this time")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 是否为第一个 [Application]
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isFirstApplication get() = packageName == processName
|
||||
val isFirstApplication get() = packageName.trim() == processName.trim()
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的主进程名称
|
||||
@@ -96,21 +114,50 @@ open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
* 其对应的就是 [packageName]
|
||||
* @return [String]
|
||||
*/
|
||||
val mainProcessName get() = packageName
|
||||
val mainProcessName get() = packageName.trim()
|
||||
|
||||
/**
|
||||
* 获取当前 Xposed 模块自身 APK 文件路径
|
||||
*
|
||||
* - ❗作为 Hook API 装载时无法使用 - 会获取到空字符串
|
||||
* @return [String]
|
||||
*/
|
||||
val moduleAppFilePath get() = YukiHookBridge.moduleAppFilePath
|
||||
|
||||
/**
|
||||
* 获取当前 Xposed 模块自身 [Resources]
|
||||
*
|
||||
* - ❗作为 Hook API 或不支持的 Hook Framework 装载时无法使用 - 会抛出异常
|
||||
* @return [YukiModuleResources]
|
||||
* @throws IllegalStateException 如果当前 Hook Framework 不支持此功能
|
||||
*/
|
||||
val moduleAppResources get() = YukiHookBridge.moduleAppResources ?: error("Current Hook Framework not support moduleAppResources")
|
||||
|
||||
/**
|
||||
* 获得当前使用的存取数据对象缓存实例
|
||||
*
|
||||
* - ❗作为 Hook API 装载时无法使用 - 会抛出异常
|
||||
* @return [YukiHookModulePrefs]
|
||||
*/
|
||||
val prefs by lazy { YukiHookModulePrefs() }
|
||||
|
||||
/**
|
||||
* 获得当前使用的存取数据对象缓存实例
|
||||
*
|
||||
* - ❗作为 Hook API 装载时无法使用 - 会抛出异常
|
||||
* @param name 自定义 Sp 存储名称
|
||||
* @return [YukiHookModulePrefs]
|
||||
*/
|
||||
fun prefs(name: String) = prefs.name(name)
|
||||
|
||||
/**
|
||||
* 获得当前 Hook APP 的 [YukiResources] 对象
|
||||
*
|
||||
* 请调用 [HookResources.hook] 方法开始 Hook
|
||||
* @return [HookResources]
|
||||
*/
|
||||
fun resources() = HookResources(wrapper?.appResources)
|
||||
|
||||
/**
|
||||
* 赋值并克隆另一个 [PackageParam]
|
||||
* @param anotherParam 另一个 [PackageParam]
|
||||
@@ -120,21 +167,41 @@ open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载并 Hook 指定包名的 APP
|
||||
* @param name 包名
|
||||
* 装载并 Hook 指定、全部包名的 APP
|
||||
*
|
||||
* 若要 Hook 系统框架 - 请使用 [loadZygote]
|
||||
* @param name 包名 - 不填将过滤除了系统框架的全部 APP
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
inline fun loadApp(name: String, initiate: PackageParam.() -> Unit) {
|
||||
if (packageName == name) initiate(this)
|
||||
inline fun loadApp(name: String = "", initiate: PackageParam.() -> Unit) {
|
||||
if (wrapper?.type != HookEntryType.ZYGOTE && (packageName == name || name.isBlank())) initiate(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载并 Hook 指定包名的 APP
|
||||
* @param name 包名
|
||||
* 装载并 Hook 指定、全部包名的 APP
|
||||
*
|
||||
* 若要 Hook 系统框架 - 请使用 [loadZygote]
|
||||
* @param name 包名 - 不填将过滤除了系统框架的全部 APP
|
||||
* @param hooker Hook 子类
|
||||
*/
|
||||
fun loadApp(name: String, hooker: YukiBaseHooker) {
|
||||
if (packageName == name) loadHooker(hooker)
|
||||
fun loadApp(name: String = "", hooker: YukiBaseHooker) {
|
||||
if (wrapper?.type != HookEntryType.ZYGOTE && (packageName == name || name.isBlank())) loadHooker(hooker)
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载并 Hook 系统框架
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
inline fun loadZygote(initiate: PackageParam.() -> Unit) {
|
||||
if (wrapper?.type == HookEntryType.ZYGOTE) initiate(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载并 Hook 系统框架
|
||||
* @param hooker Hook 子类
|
||||
*/
|
||||
fun loadZygote(hooker: YukiBaseHooker) {
|
||||
if (wrapper?.type == HookEntryType.ZYGOTE) loadHooker(hooker)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,37 +284,44 @@ open class PackageParam(private var wrapper: PackageParamWrapper? = null) {
|
||||
* - ❗为防止任何字符串都被当做 [Class] 进行 Hook - 推荐优先使用 [findClass]
|
||||
* @param isUseAppClassLoader 是否使用 [appClassLoader] 重新绑定当前 [Class] - 默认启用
|
||||
* @param initiate 方法体
|
||||
* @return [YukiHookCreater.Result]
|
||||
* @return [YukiMemberHookCreater.Result]
|
||||
*/
|
||||
inline fun String.hook(isUseAppClassLoader: Boolean = true, initiate: YukiHookCreater.() -> Unit) =
|
||||
inline fun String.hook(isUseAppClassLoader: Boolean = true, initiate: YukiMemberHookCreater.() -> Unit) =
|
||||
findClass(name = this).hook(isUseAppClassLoader, initiate)
|
||||
|
||||
/**
|
||||
* Hook 方法、构造类
|
||||
* @param isUseAppClassLoader 是否使用 [appClassLoader] 重新绑定当前 [Class] - 默认启用
|
||||
* @param initiate 方法体
|
||||
* @return [YukiHookCreater.Result]
|
||||
* @return [YukiMemberHookCreater.Result]
|
||||
*/
|
||||
inline fun Class<*>.hook(isUseAppClassLoader: Boolean = true, initiate: YukiHookCreater.() -> Unit) =
|
||||
inline fun Class<*>.hook(isUseAppClassLoader: Boolean = true, initiate: YukiMemberHookCreater.() -> Unit) =
|
||||
hookClass.hook(isUseAppClassLoader, initiate)
|
||||
|
||||
/**
|
||||
* Hook 方法、构造类
|
||||
* @param isUseAppClassLoader 是否使用 [appClassLoader] 重新绑定当前 [Class] - 默认启用
|
||||
* @param initiate 方法体
|
||||
* @return [YukiHookCreater.Result]
|
||||
* @return [YukiMemberHookCreater.Result]
|
||||
*/
|
||||
inline fun VariousClass.hook(isUseAppClassLoader: Boolean = true, initiate: YukiHookCreater.() -> Unit) =
|
||||
inline fun VariousClass.hook(isUseAppClassLoader: Boolean = true, initiate: YukiMemberHookCreater.() -> Unit) =
|
||||
hookClass(if (isUseAppClassLoader) appClassLoader else null).hook(isUseAppClassLoader, initiate)
|
||||
|
||||
/**
|
||||
* Hook 方法、构造类
|
||||
* @param isUseAppClassLoader 是否使用 [appClassLoader] 重新绑定当前 [Class] - 默认启用
|
||||
* @param initiate 方法体
|
||||
* @return [YukiHookCreater.Result]
|
||||
* @return [YukiMemberHookCreater.Result]
|
||||
*/
|
||||
inline fun HookClass.hook(isUseAppClassLoader: Boolean = true, initiate: YukiHookCreater.() -> Unit) =
|
||||
YukiHookCreater(packageParam = this@PackageParam, hookClass = if (isUseAppClassLoader) bind() else this).apply(initiate).hook()
|
||||
inline fun HookClass.hook(isUseAppClassLoader: Boolean = true, initiate: YukiMemberHookCreater.() -> Unit) =
|
||||
YukiMemberHookCreater(packageParam = this@PackageParam, hookClass = if (isUseAppClassLoader) bind() else this).apply(initiate).hook()
|
||||
|
||||
/**
|
||||
* Hook APP 的 Resources
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
inline fun HookResources.hook(initiate: YukiResourcesHookCreater.() -> Unit) =
|
||||
YukiResourcesHookCreater(packageParam = this@PackageParam, hookResources = this).apply(initiate).hook()
|
||||
|
||||
/**
|
||||
* [VariousClass] 转换为 [HookClass] 并绑定到 [appClassLoader]
|
||||
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/26.
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.param.type
|
||||
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType.*
|
||||
import de.robv.android.xposed.IXposedHookInitPackageResources
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.IXposedHookZygoteInit
|
||||
|
||||
/**
|
||||
* 当前正在进行的 Hook 类型
|
||||
*
|
||||
* [ZYGOTE] 为 [IXposedHookZygoteInit.initZygote]
|
||||
*
|
||||
* [PACKAGE] 为 [IXposedHookLoadPackage.handleLoadPackage]
|
||||
*
|
||||
* [RESOURCES] 为 [IXposedHookInitPackageResources.handleInitPackageResources]
|
||||
*/
|
||||
@YukiPrivateApi
|
||||
enum class HookEntryType {
|
||||
|
||||
/** initZygote */
|
||||
ZYGOTE,
|
||||
|
||||
/** handleLoadPackage */
|
||||
PACKAGE,
|
||||
|
||||
/** handleInitPackageResources */
|
||||
RESOURCES
|
||||
}
|
@@ -40,34 +40,44 @@ import java.lang.reflect.Member
|
||||
* @param baseParam 对接 [XC_MethodHook.MethodHookParam]
|
||||
*/
|
||||
@YukiPrivateApi
|
||||
class HookParamWrapper(private val baseParam: XC_MethodHook.MethodHookParam) {
|
||||
class HookParamWrapper(private var baseParam: XC_MethodHook.MethodHookParam? = null) {
|
||||
|
||||
/**
|
||||
* 在回调中设置 [HookParamWrapper] 使用的 [XC_MethodHook.MethodHookParam]
|
||||
* @param baseParam 对接 [XC_MethodHook.MethodHookParam]
|
||||
* @return [HookParamWrapper]
|
||||
*/
|
||||
internal fun assign(baseParam: XC_MethodHook.MethodHookParam): HookParamWrapper {
|
||||
this.baseParam = baseParam
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* [Member] 实例
|
||||
* @return [Member] or null
|
||||
*/
|
||||
val member: Member? get() = baseParam.method
|
||||
val member: Member? get() = baseParam?.method
|
||||
|
||||
/**
|
||||
* 当前实例对象
|
||||
* @return [Any] or null
|
||||
*/
|
||||
val instance: Any? get() = baseParam.thisObject
|
||||
val instance: Any? get() = baseParam?.thisObject
|
||||
|
||||
/**
|
||||
* 方法、构造方法数组
|
||||
* @return [Array] or null
|
||||
*/
|
||||
val args: Array<Any?>? get() = baseParam.args
|
||||
val args: Array<Any?>? get() = baseParam?.args
|
||||
|
||||
/**
|
||||
* 方法、设置方法结果
|
||||
* @return [Any] or null
|
||||
*/
|
||||
var result: Any?
|
||||
get() = baseParam.result
|
||||
get() = baseParam?.result
|
||||
set(value) {
|
||||
baseParam.result = value
|
||||
baseParam?.result = value
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +85,7 @@ class HookParamWrapper(private val baseParam: XC_MethodHook.MethodHookParam) {
|
||||
* @param index 数组下标
|
||||
* @param any 参数对象实例
|
||||
*/
|
||||
fun setArgs(index: Int, any: Any?) {
|
||||
baseParam.args[index] = any
|
||||
}
|
||||
fun setArgs(index: Int, any: Any?) = baseParam?.args?.set(index, any)
|
||||
|
||||
/**
|
||||
* 执行原始 [Member]
|
||||
@@ -87,8 +95,7 @@ class HookParamWrapper(private val baseParam: XC_MethodHook.MethodHookParam) {
|
||||
* @param args 参数实例
|
||||
* @return [Any] or null
|
||||
*/
|
||||
fun invokeOriginalMember(member: Member, vararg args: Any?): Any? =
|
||||
XposedBridge.invokeOriginalMethod(member, instance, args)
|
||||
fun invokeOriginalMember(member: Member, vararg args: Any?): Any? = XposedBridge.invokeOriginalMethod(member, instance, args)
|
||||
|
||||
override fun toString() = "HookParamWrapper[$baseParam]"
|
||||
}
|
@@ -32,23 +32,30 @@ package com.highcapable.yukihookapi.hook.param.wrapper
|
||||
import android.content.pm.ApplicationInfo
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
|
||||
/**
|
||||
* 用于包装 [PackageParam]
|
||||
*
|
||||
* - ❗这是一个私有 API - 请不要在外部使用
|
||||
* @param type 当前正在进行的 Hook 类型
|
||||
* @param packageName 包名
|
||||
* @param processName 当前进程名
|
||||
* @param appClassLoader APP [ClassLoader]
|
||||
* @param appInfo APP [ApplicationInfo]
|
||||
* @param appResources APP [YukiResources]
|
||||
*/
|
||||
@YukiPrivateApi
|
||||
class PackageParamWrapper(
|
||||
var type: HookEntryType,
|
||||
var packageName: String,
|
||||
var processName: String,
|
||||
var appClassLoader: ClassLoader,
|
||||
var appInfo: ApplicationInfo
|
||||
var appInfo: ApplicationInfo? = null,
|
||||
var appResources: YukiResources? = null
|
||||
) {
|
||||
|
||||
override fun toString() = "PackageParamWrapper [packageName] $packageName [processName] $processName [appInfo] $appInfo"
|
||||
override fun toString() =
|
||||
"PackageParamWrapper [type] $type [packageName] $packageName [processName] $processName [appInfo] $appInfo [appResources] $appResources"
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
package com.highcapable.yukihookapi.hook.utils
|
||||
|
||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||
import java.lang.reflect.Constructor
|
||||
@@ -52,6 +53,7 @@ internal object ReflectionTool {
|
||||
* @param name 变量名称
|
||||
* @param modifiers 变量描述
|
||||
* @param type 变量类型
|
||||
* @param isFindInSuperClass 是否在未找到后继续在当前 [classSet] 的父类中查找
|
||||
* @return [Field]
|
||||
* @throws IllegalStateException 如果 [classSet] 为 null 或未设置任何条件或 [type] 目标类不存在
|
||||
* @throws NoSuchFieldError 如果找不到变量
|
||||
@@ -62,7 +64,8 @@ internal object ReflectionTool {
|
||||
matchIndex: Pair<Int, Boolean>?,
|
||||
name: String,
|
||||
modifiers: ModifierRules?,
|
||||
type: Class<*>?
|
||||
type: Class<*>?,
|
||||
isFindInSuperClass: Boolean
|
||||
): Field {
|
||||
if (type == UndefinedType) error("Field match type class is not found")
|
||||
if (orderIndex == null && matchIndex == null && name.isBlank() && modifiers == null && type == null)
|
||||
@@ -121,22 +124,25 @@ internal object ReflectionTool {
|
||||
}
|
||||
} ?: error("Can't find this Field [$name] because classSet is null")
|
||||
field?.also { MemberCacheStore.putField(hashCode, field) }
|
||||
?: throw NoSuchFieldError(
|
||||
?: if (isFindInSuperClass && classSet.hasExtends)
|
||||
findField(
|
||||
classSet.superclass,
|
||||
orderIndex, matchIndex,
|
||||
name, modifiers,
|
||||
type, isFindInSuperClass = true
|
||||
)
|
||||
else throw NoSuchFieldError(
|
||||
"Can't find this Field --> " +
|
||||
"orderIndex:[${
|
||||
when {
|
||||
orderIndex == null -> "unspecified"
|
||||
orderIndex.second.not() -> "last"
|
||||
else -> orderIndex.first
|
||||
}
|
||||
}] " +
|
||||
"matchIndex:[${
|
||||
when {
|
||||
matchIndex == null -> "unspecified"
|
||||
matchIndex.second.not() -> "last"
|
||||
else -> matchIndex.first
|
||||
}
|
||||
}] " +
|
||||
when {
|
||||
orderIndex == null -> ""
|
||||
orderIndex.second.not() -> "orderIndex:[last] "
|
||||
else -> "orderIndex:[${orderIndex.first}] "
|
||||
} +
|
||||
when {
|
||||
matchIndex == null -> ""
|
||||
matchIndex.second.not() -> "matchIndex:[last] "
|
||||
else -> "matchIndex:[${matchIndex.first}] "
|
||||
} +
|
||||
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
||||
"type:[${type ?: "unspecified"}] " +
|
||||
"modifiers:${modifiers ?: "[]"} " +
|
||||
@@ -156,6 +162,7 @@ internal object ReflectionTool {
|
||||
* @param returnType 方法返回值
|
||||
* @param paramCount 方法参数个数
|
||||
* @param paramTypes 方法参数类型
|
||||
* @param isFindInSuperClass 是否在未找到后继续在当前 [classSet] 的父类中查找
|
||||
* @return [Method]
|
||||
* @throws IllegalStateException 如果 [classSet] 为 null 或未设置任何条件或 [paramTypes] 以及 [returnType] 目标类不存在
|
||||
* @throws NoSuchMethodError 如果找不到方法
|
||||
@@ -168,7 +175,8 @@ internal object ReflectionTool {
|
||||
modifiers: ModifierRules?,
|
||||
returnType: Class<*>?,
|
||||
paramCount: Int,
|
||||
paramTypes: Array<out Class<*>>?
|
||||
paramTypes: Array<out Class<*>>?,
|
||||
isFindInSuperClass: Boolean
|
||||
): Method {
|
||||
if (returnType == UndefinedType) error("Method match returnType class is not found")
|
||||
paramTypes?.takeIf { it.isNotEmpty() }
|
||||
@@ -257,22 +265,26 @@ internal object ReflectionTool {
|
||||
}
|
||||
} ?: error("Can't find this Method [$name] because classSet is null")
|
||||
method?.also { MemberCacheStore.putMethod(hashCode, method) }
|
||||
?: throw NoSuchMethodError(
|
||||
?: if (isFindInSuperClass && classSet.hasExtends)
|
||||
findMethod(
|
||||
classSet.superclass,
|
||||
orderIndex, matchIndex,
|
||||
name, modifiers,
|
||||
returnType, paramCount,
|
||||
paramTypes, isFindInSuperClass = true
|
||||
)
|
||||
else throw NoSuchMethodError(
|
||||
"Can't find this Method --> " +
|
||||
"orderIndex:[${
|
||||
when {
|
||||
orderIndex == null -> "unspecified"
|
||||
orderIndex.second.not() -> "last"
|
||||
else -> orderIndex.first
|
||||
}
|
||||
}] " +
|
||||
"matchIndex:[${
|
||||
when {
|
||||
matchIndex == null -> "unspecified"
|
||||
matchIndex.second.not() -> "last"
|
||||
else -> matchIndex.first
|
||||
}
|
||||
}] " +
|
||||
when {
|
||||
orderIndex == null -> ""
|
||||
orderIndex.second.not() -> "orderIndex:[last] "
|
||||
else -> "orderIndex:[${orderIndex.first}] "
|
||||
} +
|
||||
when {
|
||||
matchIndex == null -> ""
|
||||
matchIndex.second.not() -> "matchIndex:[last] "
|
||||
else -> "matchIndex:[${matchIndex.first}] "
|
||||
} +
|
||||
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
||||
"paramCount:[${paramCount.takeIf { it >= 0 } ?: "unspecified"}] " +
|
||||
"paramTypes:[${paramTypes.typeOfString()}] " +
|
||||
@@ -292,6 +304,7 @@ internal object ReflectionTool {
|
||||
* @param modifiers 构造方法描述
|
||||
* @param paramCount 构造方法参数个数
|
||||
* @param paramTypes 构造方法参数类型
|
||||
* @param isFindInSuperClass 是否在未找到后继续在当前 [classSet] 的父类中查找
|
||||
* @return [Constructor]
|
||||
* @throws IllegalStateException 如果 [classSet] 为 null 或未设置任何条件或 [paramTypes] 目标类不存在
|
||||
* @throws NoSuchMethodError 如果找不到构造方法
|
||||
@@ -302,7 +315,8 @@ internal object ReflectionTool {
|
||||
matchIndex: Pair<Int, Boolean>?,
|
||||
modifiers: ModifierRules?,
|
||||
paramCount: Int,
|
||||
paramTypes: Array<out Class<*>>?
|
||||
paramTypes: Array<out Class<*>>?,
|
||||
isFindInSuperClass: Boolean
|
||||
): Constructor<*> {
|
||||
paramTypes?.takeIf { it.isNotEmpty() }
|
||||
?.forEachIndexed { p, it -> if (it == UndefinedType) error("Constructor match paramType[$p] class is not found") }
|
||||
@@ -364,22 +378,25 @@ internal object ReflectionTool {
|
||||
}
|
||||
} ?: error("Can't find this Constructor because classSet is null")
|
||||
return constructor?.also { MemberCacheStore.putConstructor(hashCode, constructor) }
|
||||
?: throw NoSuchMethodError(
|
||||
?: if (isFindInSuperClass && classSet.hasExtends)
|
||||
findConstructor(
|
||||
classSet.superclass,
|
||||
orderIndex, matchIndex,
|
||||
modifiers, paramCount,
|
||||
paramTypes, isFindInSuperClass = true
|
||||
)
|
||||
else throw NoSuchMethodError(
|
||||
"Can't find this Constructor --> " +
|
||||
"orderIndex:[${
|
||||
when {
|
||||
orderIndex == null -> "unspecified"
|
||||
orderIndex.second.not() -> "last"
|
||||
else -> orderIndex.first
|
||||
}
|
||||
}] " +
|
||||
"matchIndex:[${
|
||||
when {
|
||||
matchIndex == null -> "unspecified"
|
||||
matchIndex.second.not() -> "last"
|
||||
else -> matchIndex.first
|
||||
}
|
||||
}] " +
|
||||
when {
|
||||
orderIndex == null -> ""
|
||||
orderIndex.second.not() -> "orderIndex:[last] "
|
||||
else -> "orderIndex:[${orderIndex.first}] "
|
||||
} +
|
||||
when {
|
||||
matchIndex == null -> ""
|
||||
matchIndex.second.not() -> "matchIndex:[last] "
|
||||
else -> "matchIndex:[${matchIndex.first}] "
|
||||
} +
|
||||
"paramCount:[${
|
||||
paramCountR.takeIf { it >= 0 || it == -2 }
|
||||
?.toString()?.replace(oldValue = "-2", newValue = "last") ?: "unspecified"
|
||||
|
@@ -29,8 +29,8 @@ package com.highcapable.yukihookapi.hook.xposed
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.annotation.Keep
|
||||
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
|
||||
import com.highcapable.yukihookapi.hook.factory.isModuleActive
|
||||
import com.highcapable.yukihookapi.hook.factory.isSupportResourcesHook
|
||||
import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive
|
||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerD
|
||||
@@ -60,6 +60,9 @@ object YukiHookModuleStatus {
|
||||
/** 定义 Jvm 方法名 */
|
||||
private const val IS_ACTIVE_METHOD_NAME = "__--"
|
||||
|
||||
/** 定义 Jvm 方法名 */
|
||||
private const val HAS_RESOURCES_HOOK_METHOD_NAME = "_--_"
|
||||
|
||||
/** 定义 Jvm 方法名 */
|
||||
private const val GET_XPOSED_VERSION_METHOD_NAME = "--__"
|
||||
|
||||
@@ -87,18 +90,28 @@ object YukiHookModuleStatus {
|
||||
* 此方法经过 Hook 后返回 true 即模块已激活
|
||||
*
|
||||
* 请使用 [isModuleActive]、[isXposedModuleActive]、[isTaiChiModuleActive] 判断模块激活状态
|
||||
*
|
||||
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
|
||||
* @return [Boolean]
|
||||
*/
|
||||
@Keep
|
||||
@YukiPrivateApi
|
||||
@JvmName(IS_ACTIVE_METHOD_NAME)
|
||||
internal fun isActive(): Boolean {
|
||||
yLoggerD(msg = IS_ACTIVE_METHOD_NAME, isDisableLog = true)
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法经过 Hook 后返回 true 即当前 Hook Framework 支持资源钩子(Resources Hook)
|
||||
*
|
||||
* 请使用 [isSupportResourcesHook] 判断支持状态
|
||||
* @return [Boolean]
|
||||
*/
|
||||
@Keep
|
||||
@JvmName(HAS_RESOURCES_HOOK_METHOD_NAME)
|
||||
internal fun hasResourcesHook(): Boolean {
|
||||
yLoggerD(msg = HAS_RESOURCES_HOOK_METHOD_NAME, isDisableLog = true)
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法经过 Hook 后返回 [XposedBridge.getXposedVersion]
|
||||
* @return [Int]
|
||||
|
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/3.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.res.Resources
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.YukiGenerateApi
|
||||
import com.highcapable.yukihookapi.hook.factory.hasClass
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
import de.robv.android.xposed.*
|
||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import java.lang.reflect.Member
|
||||
|
||||
/**
|
||||
* 这是一个对接 Xposed Hook 入口与 [XposedBridge] 的装载类实现桥
|
||||
*
|
||||
* 实现与 [IXposedHookZygoteInit]、[IXposedHookLoadPackage]、[IXposedHookInitPackageResources] 接口通讯
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿修改或移动以及重命名此类的任何方法与变量
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
object YukiHookBridge {
|
||||
|
||||
/** Android 系统框架名称 */
|
||||
private const val SYSTEM_FRAMEWORK_NAME = "android"
|
||||
|
||||
/** Xposed 是否装载完成 */
|
||||
private var isXposedInitialized = false
|
||||
|
||||
/** 已在 [PackageParam] 中被装载的 APP 包名 */
|
||||
private val loadedPackageNames = HashSet<String>()
|
||||
|
||||
/** 当前 [PackageParamWrapper] 实例数组 */
|
||||
private var packageParamWrappers = HashMap<String, PackageParamWrapper>()
|
||||
|
||||
/** 当前 [PackageParam] 方法体回调 */
|
||||
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
|
||||
|
||||
/** 当前 Xposed 模块自身 APK 路径 */
|
||||
internal var moduleAppFilePath = ""
|
||||
|
||||
/** 当前 Xposed 模块自身 [Resources] */
|
||||
internal var moduleAppResources: YukiModuleResources? = null
|
||||
|
||||
/**
|
||||
* 模块是否装载了 Xposed 回调方法
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
* @return [Boolean]
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
val isXposedCallbackSetUp
|
||||
get() = isXposedInitialized.not() && packageParamCallback != null
|
||||
|
||||
/**
|
||||
* 预设的 Xposed 模块包名
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
var modulePackageName = ""
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 框架的名称
|
||||
*
|
||||
* 从 [XposedBridge] 获取 TAG
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 若要调用请使用 [YukiHookAPI.executorName]
|
||||
* @return [String] 无法获取会返回 unknown - [hasXposedBridge] 不存在会返回 invalid
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
val executorName
|
||||
get() = runCatching {
|
||||
(XposedBridge::class.java.getDeclaredField("TAG").apply { isAccessible = true }.get(null) as? String?)
|
||||
?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown"
|
||||
}.getOrNull() ?: "invalid"
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 框架的版本
|
||||
*
|
||||
* 获取 [XposedBridge.getXposedVersion]
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 若要调用请使用 [YukiHookAPI.executorVersion]
|
||||
* @return [Int] 无法获取会返回 -1
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
val executorVersion
|
||||
get() = runCatching { XposedBridge.getXposedVersion() }.getOrNull() ?: -1
|
||||
|
||||
/**
|
||||
* 是否存在 [XposedBridge]
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal val hasXposedBridge get() = executorVersion >= 0
|
||||
|
||||
/**
|
||||
* 自动忽略 MIUI 系统可能出现的日志收集注入实例
|
||||
* @param packageName 当前包名
|
||||
* @return [Boolean] 是否存在
|
||||
*/
|
||||
private fun isMiuiCatcherPatch(packageName: String?) =
|
||||
(packageName == "com.miui.contentcatcher" || packageName == "com.miui.catcherpatch") && ("android.miui.R").hasClass
|
||||
|
||||
/**
|
||||
* 当前包名是否已在指定的 [HookEntryType] 被装载
|
||||
* @param packageName 包名
|
||||
* @param type 当前 Hook 类型
|
||||
* @return [Boolean] 是否已被装载
|
||||
*/
|
||||
private fun isPackageLoaded(packageName: String, type: HookEntryType): Boolean {
|
||||
if (loadedPackageNames.contains("$packageName:$type")) return true
|
||||
loadedPackageNames.add("$packageName:$type")
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建、修改 [PackageParamWrapper]
|
||||
* @param type 当前正在进行的 Hook 类型
|
||||
* @param packageName 包名
|
||||
* @param processName 当前进程名
|
||||
* @param appClassLoader APP [ClassLoader]
|
||||
* @param appInfo APP [ApplicationInfo]
|
||||
* @param appResources APP [YukiResources]
|
||||
* @return [PackageParamWrapper] or null
|
||||
*/
|
||||
private fun assignWrapper(
|
||||
type: HookEntryType,
|
||||
packageName: String,
|
||||
processName: String = "",
|
||||
appClassLoader: ClassLoader? = null,
|
||||
appInfo: ApplicationInfo? = null,
|
||||
appResources: YukiResources? = null
|
||||
) = run {
|
||||
if (packageParamWrappers[packageName] == null)
|
||||
PackageParamWrapper(
|
||||
type = type,
|
||||
packageName = packageName,
|
||||
processName = processName,
|
||||
appClassLoader = appClassLoader ?: XposedBridge.BOOTCLASSLOADER,
|
||||
appInfo = appInfo,
|
||||
appResources = appResources
|
||||
).also { packageParamWrappers[packageName] = it }
|
||||
else packageParamWrappers[packageName]?.also {
|
||||
it.type = type
|
||||
if (packageName.isNotBlank()) it.packageName = packageName
|
||||
if (processName.isNotBlank()) it.processName = processName
|
||||
if (appClassLoader != null) it.appClassLoader = appClassLoader
|
||||
if (appInfo != null) it.appInfo = appInfo
|
||||
if (appResources != null) it.appResources = appResources
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标识 Xposed API 装载完成
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedInitialized() {
|
||||
isXposedInitialized = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载 Xposed API Zygote 回调
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件
|
||||
* @param sparam Xposed [IXposedHookZygoteInit.StartupParam]
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedZygoteLoaded(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleAppFilePath = sparam.modulePath
|
||||
moduleAppResources = YukiModuleResources.createInstance(moduleAppFilePath)
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载 Xposed API 回调
|
||||
*
|
||||
* 这里的入口会装载三次
|
||||
*
|
||||
* - 在 [IXposedHookZygoteInit.initZygote] 时装载
|
||||
*
|
||||
* - 在 [IXposedHookLoadPackage.handleLoadPackage] 时装载
|
||||
*
|
||||
* - 在 [IXposedHookInitPackageResources.handleInitPackageResources] 时装载
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed API 事件
|
||||
* @param isZygoteLoaded 是否为 Xposed [IXposedHookZygoteInit.initZygote]
|
||||
* @param lpparam Xposed [XC_LoadPackage.LoadPackageParam]
|
||||
* @param resparam Xposed [XC_InitPackageResources.InitPackageResourcesParam]
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedLoaded(
|
||||
isZygoteLoaded: Boolean,
|
||||
lpparam: XC_LoadPackage.LoadPackageParam? = null,
|
||||
resparam: XC_InitPackageResources.InitPackageResourcesParam? = null
|
||||
) {
|
||||
if (isMiuiCatcherPatch(packageName = lpparam?.packageName ?: resparam?.packageName).not()) when {
|
||||
isZygoteLoaded -> {
|
||||
if (isPackageLoaded(SYSTEM_FRAMEWORK_NAME, HookEntryType.ZYGOTE).not())
|
||||
assignWrapper(HookEntryType.ZYGOTE, SYSTEM_FRAMEWORK_NAME, SYSTEM_FRAMEWORK_NAME)
|
||||
else null
|
||||
}
|
||||
lpparam != null -> {
|
||||
if (lpparam.packageName != null && lpparam.processName != null && lpparam.appInfo != null && lpparam.classLoader != null &&
|
||||
isPackageLoaded(lpparam.packageName, HookEntryType.PACKAGE).not()
|
||||
) assignWrapper(HookEntryType.PACKAGE, lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo)
|
||||
else null
|
||||
}
|
||||
resparam != null -> {
|
||||
if (isPackageLoaded(resparam.packageName, HookEntryType.RESOURCES).not())
|
||||
assignWrapper(HookEntryType.RESOURCES, resparam.packageName, appResources = YukiResources.createFromXResources(resparam.res))
|
||||
else null
|
||||
}
|
||||
else -> null
|
||||
}?.let { YukiHookAPI.onXposedLoaded(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 核心功能实现实例
|
||||
*
|
||||
* 对接 [XposedBridge] 实现 Hook 功能
|
||||
*/
|
||||
internal object Hooker {
|
||||
|
||||
/**
|
||||
* Hook 方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookMethod]
|
||||
* @param hookMethod 需要 Hook 的方法、构造方法
|
||||
* @param callback 回调
|
||||
* @return [Member] or null
|
||||
*/
|
||||
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback) =
|
||||
XposedBridge.hookMethod(hookMethod, compatCallback(callback))?.hookedMethod
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有 [methodName] 的方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllMethods]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param methodName 方法名
|
||||
* @param callback 回调
|
||||
* @return [HashSet] 成功 Hook 的方法数组
|
||||
*/
|
||||
internal fun hookAllMethods(hookClass: Class<*>?, methodName: String, callback: YukiHookCallback) = HashSet<Member>().also {
|
||||
XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { it.isNotEmpty() }
|
||||
?.forEach { e -> it.add(e.hookedMethod) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有构造方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllConstructors]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param callback 回调
|
||||
* @return [HashSet] 成功 Hook 的构造方法数组
|
||||
*/
|
||||
internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback) = HashSet<Member>().also {
|
||||
XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { it.isNotEmpty() }
|
||||
?.forEach { e -> it.add(e.hookedMethod) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容对接 Hook 回调接口
|
||||
* @param callback [YukiHookCallback] 接口
|
||||
* @return [XC_MethodHook] 原始接口
|
||||
*/
|
||||
private fun compatCallback(callback: YukiHookCallback) = when (callback) {
|
||||
is YukiMemberHook -> object : XC_MethodHook(callback.priority) {
|
||||
|
||||
/** 创建 Hook 前 [HookParamWrapper] */
|
||||
val beforeHookWrapper = HookParamWrapper()
|
||||
|
||||
/** 创建 Hook 后 [HookParamWrapper] */
|
||||
val afterHookWrapper = HookParamWrapper()
|
||||
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
if (param == null) return
|
||||
callback.beforeHookedMember(beforeHookWrapper.assign(param))
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
if (param == null) return
|
||||
callback.afterHookedMember(afterHookWrapper.assign(param))
|
||||
}
|
||||
}
|
||||
is YukiMemberReplacement -> object : XC_MethodReplacement(callback.priority) {
|
||||
|
||||
/** 创建替换 Hook [HookParamWrapper] */
|
||||
val replaceHookWrapper = HookParamWrapper()
|
||||
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any? {
|
||||
if (param == null) return null
|
||||
return callback.replaceHookedMember(replaceHookWrapper.assign(param))
|
||||
}
|
||||
}
|
||||
else -> error("Invalid YukiHookCallback type")
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 方法回调接口
|
||||
* @param priority Hook 优先级
|
||||
*/
|
||||
internal abstract class YukiMemberHook(override val priority: Int) : YukiHookCallback(priority) {
|
||||
|
||||
/**
|
||||
* 在方法执行之前注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
abstract fun beforeHookedMember(wrapper: HookParamWrapper)
|
||||
|
||||
/**
|
||||
* 在方法执行之后注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
abstract fun afterHookedMember(wrapper: HookParamWrapper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 替换方法回调接口
|
||||
* @param priority Hook 优先级
|
||||
*/
|
||||
internal abstract class YukiMemberReplacement(override val priority: Int) : YukiHookCallback(priority) {
|
||||
|
||||
/**
|
||||
* 拦截替换为指定结果
|
||||
* @param wrapper 包装实例
|
||||
* @return [Any] or null
|
||||
*/
|
||||
abstract fun replaceHookedMember(wrapper: HookParamWrapper): Any?
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 回调接口父类
|
||||
* @param priority Hook 优先级
|
||||
*/
|
||||
internal abstract class YukiHookCallback(open val priority: Int)
|
||||
}
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/3.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge
|
||||
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.YukiGenerateApi
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
|
||||
/**
|
||||
* 这是一个 Xposed 模块的入口装载类实现桥
|
||||
*
|
||||
* 实现与 [IXposedHookLoadPackage] 接口通讯
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿修改或移动以及重命名此类的任何方法与变量
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
object YukiHookXposedBridge {
|
||||
|
||||
/** Xposed 是否装载完成 */
|
||||
private var isXposedInitialized = false
|
||||
|
||||
/** Xposed Hook API 方法体回调 */
|
||||
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
|
||||
|
||||
/**
|
||||
* 模块是否装载了 Xposed 回调方法
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
* @return [Boolean]
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
val isXposedCallbackSetUp
|
||||
get() = isXposedInitialized.not() && packageParamCallback != null
|
||||
|
||||
/**
|
||||
* 当前 Hook 的对象是模块自身
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
var isModulePackageXposedEnv = false
|
||||
|
||||
/**
|
||||
* 预设的 Xposed 模块包名
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
var modulePackageName = ""
|
||||
|
||||
/**
|
||||
* 标识 Xposed API 装载完成
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedInitialized() {
|
||||
isXposedInitialized = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载 Xposed API 回调
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件
|
||||
* @param lpparam Xposed [XC_LoadPackage.LoadPackageParam]
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedLoaded(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
/** 判断基础 API 可能为空的情况 */
|
||||
if (lpparam.packageName == null || lpparam.processName == null || lpparam.appInfo == null || lpparam.classLoader == null) return
|
||||
/** 回调 Xposed API 装载 */
|
||||
YukiHookAPI.onXposedLoaded(PackageParamWrapper(lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo))
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/29.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge.dummy
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.content.res.XModuleResources
|
||||
import android.content.res.XResForwarder
|
||||
|
||||
/**
|
||||
* 对接 [XModuleResources] 的中间层实例
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
class YukiModuleResources(private val baseInstance: XModuleResources) :
|
||||
Resources(baseInstance.assets, baseInstance.displayMetrics, baseInstance.configuration) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 对接 [XModuleResources.createInstance] 方法
|
||||
*
|
||||
* 创建 [YukiModuleResources] 与 [XModuleResources] 实例
|
||||
* @param path Xposed 模块 APK 路径
|
||||
* @return [YukiModuleResources]
|
||||
*/
|
||||
fun createInstance(path: String) = YukiModuleResources(XModuleResources.createInstance(path, null))
|
||||
}
|
||||
|
||||
/**
|
||||
* 对接 [XModuleResources.fwd] 方法
|
||||
*
|
||||
* 创建 [YukiResForwarder] 与 [XResForwarder] 实例
|
||||
* @param resId Resources Id
|
||||
* @return [YukiResForwarder]
|
||||
*/
|
||||
fun fwd(resId: Int) = YukiResForwarder(baseInstance.fwd(resId))
|
||||
|
||||
override fun toString() = "YukiModuleResources by $baseInstance"
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/29.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge.dummy
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.content.res.XResForwarder
|
||||
|
||||
/**
|
||||
* 对接 [XResForwarder] 的中间层实例
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
class YukiResForwarder(private val baseInstance: XResForwarder) {
|
||||
|
||||
/**
|
||||
* 获得 [XResForwarder] 实例
|
||||
* @return [XResForwarder]
|
||||
*/
|
||||
val instance get() = baseInstance
|
||||
|
||||
/**
|
||||
* 获得当前 Resources Id
|
||||
* @return [Int]
|
||||
*/
|
||||
val id get() = baseInstance.id
|
||||
|
||||
/**
|
||||
* 获得当前 Resources
|
||||
* @return [Resources]
|
||||
* @throws IllegalStateException 如果 [XResForwarder] 出现问题
|
||||
*/
|
||||
val resources get() = baseInstance.resources ?: error("XResForwarder is invalid")
|
||||
|
||||
override fun toString() = "YukiResForwarder by $baseInstance"
|
||||
}
|
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/29.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge.dummy
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.content.res.XResources
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources.LayoutInflatedParam
|
||||
import de.robv.android.xposed.callbacks.XC_LayoutInflated
|
||||
|
||||
/**
|
||||
* 对接 [XResources] 的中间层实例
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
class YukiResources(private val baseInstance: XResources) :
|
||||
Resources(baseInstance.assets, baseInstance.displayMetrics, baseInstance.configuration) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 从 [XResources] 创建 [YukiResources] 实例
|
||||
* @param baseInstance [XResources] 实例
|
||||
* @return [YukiResources]
|
||||
*/
|
||||
internal fun createFromXResources(baseInstance: XResources) = YukiResources(baseInstance)
|
||||
|
||||
/**
|
||||
* 兼容对接替换 Resources
|
||||
* @param replacement 替换 Resources
|
||||
* @return [Any] 兼容后的替换 Resources
|
||||
*/
|
||||
private fun compat(replacement: Any?) = when (replacement) {
|
||||
is YukiResForwarder -> replacement.instance
|
||||
is Drawable -> object : XResources.DrawableLoader() {
|
||||
override fun newDrawable(res: XResources?, id: Int): Drawable = replacement
|
||||
}
|
||||
else -> replacement
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Zygote 中替换 Resources
|
||||
*
|
||||
* 对接 [XResources.setSystemWideReplacement]
|
||||
* @param resId Resources Id
|
||||
* @param replacement 替换 Resources
|
||||
*/
|
||||
internal fun setSystemWideReplacement(resId: Int, replacement: Any?) = XResources.setSystemWideReplacement(resId, compat(replacement))
|
||||
|
||||
/**
|
||||
* 在 Zygote 中替换 Resources
|
||||
*
|
||||
* 对接 [XResources.setSystemWideReplacement]
|
||||
* @param packageName 包名
|
||||
* @param type Resources 类型
|
||||
* @param name Resources 名称
|
||||
* @param replacement 替换 Resources
|
||||
*/
|
||||
internal fun setSystemWideReplacement(packageName: String, type: String, name: String, replacement: Any?) =
|
||||
XResources.setSystemWideReplacement(packageName, type, name, compat(replacement))
|
||||
|
||||
/**
|
||||
* 在 Zygote 中注入布局 Resources
|
||||
*
|
||||
* 对接 [XResources.hookSystemWideLayout]
|
||||
* @param resId Resources Id
|
||||
* @param initiate 注入方法体
|
||||
*/
|
||||
internal fun hookSystemWideLayout(resId: Int, initiate: LayoutInflatedParam.() -> Unit) {
|
||||
XResources.hookSystemWideLayout(resId, object : XC_LayoutInflated() {
|
||||
override fun handleLayoutInflated(liparam: LayoutInflatedParam?) {
|
||||
if (liparam == null) return
|
||||
initiate(LayoutInflatedParam(liparam))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Zygote 中注入布局 Resources
|
||||
*
|
||||
* 对接 [XResources.hookSystemWideLayout]
|
||||
* @param packageName 包名
|
||||
* @param type Resources 类型
|
||||
* @param name Resources 名称
|
||||
* @param initiate 注入方法体
|
||||
*/
|
||||
internal fun hookSystemWideLayout(packageName: String, type: String, name: String, initiate: LayoutInflatedParam.() -> Unit) {
|
||||
XResources.hookSystemWideLayout(packageName, type, name, object : XC_LayoutInflated() {
|
||||
override fun handleLayoutInflated(liparam: LayoutInflatedParam?) {
|
||||
if (liparam == null) return
|
||||
initiate(LayoutInflatedParam(liparam))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行替换 Resources
|
||||
*
|
||||
* 对接 [XResources.setReplacement]
|
||||
* @param resId Resources Id
|
||||
* @param replacement 替换 Resources
|
||||
*/
|
||||
internal fun setReplacement(resId: Int, replacement: Any?) = baseInstance.setReplacement(resId, compat(replacement))
|
||||
|
||||
/**
|
||||
* 执行替换 Resources
|
||||
*
|
||||
* 对接 [XResources.setReplacement]
|
||||
* @param packageName 包名
|
||||
* @param type Resources 类型
|
||||
* @param name Resources 名称
|
||||
* @param replacement 替换 Resources
|
||||
*/
|
||||
internal fun setReplacement(packageName: String, type: String, name: String, replacement: Any?) =
|
||||
baseInstance.setReplacement(packageName, type, name, compat(replacement))
|
||||
|
||||
/**
|
||||
* 执行注入布局 Resources
|
||||
*
|
||||
* 对接 [XResources.hookLayout]
|
||||
* @param resId Resources Id
|
||||
* @param initiate 注入方法体
|
||||
*/
|
||||
internal fun hookLayout(resId: Int, initiate: LayoutInflatedParam.() -> Unit) {
|
||||
baseInstance.hookLayout(resId, object : XC_LayoutInflated() {
|
||||
override fun handleLayoutInflated(liparam: LayoutInflatedParam?) {
|
||||
if (liparam == null) return
|
||||
initiate(LayoutInflatedParam(liparam))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行注入布局 Resources
|
||||
*
|
||||
* 对接 [XResources.hookLayout]
|
||||
* @param packageName 包名
|
||||
* @param type Resources 类型
|
||||
* @param name Resources 名称
|
||||
* @param initiate 注入方法体
|
||||
*/
|
||||
internal fun hookLayout(packageName: String, type: String, name: String, initiate: LayoutInflatedParam.() -> Unit) {
|
||||
baseInstance.hookLayout(packageName, type, name, object : XC_LayoutInflated() {
|
||||
override fun handleLayoutInflated(liparam: LayoutInflatedParam?) {
|
||||
if (liparam == null) return
|
||||
initiate(LayoutInflatedParam(liparam))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载 Hook APP 的目标布局 Resources 实现类
|
||||
* @param baseParam 对接 [XC_LayoutInflated.LayoutInflatedParam]
|
||||
*/
|
||||
class LayoutInflatedParam(@PublishedApi internal val baseParam: XC_LayoutInflated.LayoutInflatedParam) {
|
||||
|
||||
/**
|
||||
* 获取当前被 Hook 的布局装载目录名称
|
||||
*
|
||||
* 例如:layout、layout-land、layout-sw600dp
|
||||
* @return [String]
|
||||
*/
|
||||
val variantName get() = baseParam.variant ?: ""
|
||||
|
||||
/**
|
||||
* 获取当前被 Hook 的布局实例
|
||||
* @return [View]
|
||||
*/
|
||||
val currentView get() = baseParam.view ?: error("LayoutInflatedParam View instance got null")
|
||||
|
||||
/**
|
||||
* 使用 Identifier 查找 Hook APP 指定 Id 的 [View]
|
||||
* @param name Id 的 Identifier 名称
|
||||
* @return [T] or null
|
||||
*/
|
||||
inline fun <reified T : View> View.findViewByIdentifier(name: String): T? =
|
||||
findViewById(baseParam.res.getIdentifier(name, "id", baseParam.resNames.pkg))
|
||||
|
||||
/**
|
||||
* 使用 Identifier 查找 Hook APP 当前装载布局中指定 Id 的 [View]
|
||||
* @param name Id 的 Identifier 名称
|
||||
* @return [T] or null
|
||||
*/
|
||||
inline fun <reified T : View> findViewByIdentifier(name: String) = currentView.findViewByIdentifier<T>(name)
|
||||
|
||||
override fun toString() = "LayoutInflatedParam by $baseParam"
|
||||
}
|
||||
|
||||
override fun toString() = "YukiResources by $baseInstance"
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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/4/30.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge.event
|
||||
|
||||
import com.highcapable.yukihookapi.annotation.YukiGenerateApi
|
||||
import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam
|
||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
|
||||
|
||||
/**
|
||||
* 实现对原生 Xposed API 的装载事件监听
|
||||
*/
|
||||
object YukiXposedEvent {
|
||||
|
||||
/** 监听 initZygote 开始的回调方法 */
|
||||
private var initZygoteCallback: ((StartupParam) -> Unit)? = null
|
||||
|
||||
/** 监听 handleLoadPackage 开始的回调方法 */
|
||||
private var handleLoadPackageCallback: ((LoadPackageParam) -> Unit)? = null
|
||||
|
||||
/** 监听 handleInitPackageResources 开始的回调方法 */
|
||||
private var handleInitPackageResourcesCallback: ((InitPackageResourcesParam) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* 对 [YukiXposedEvent] 创建一个方法体
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
inline fun events(initiate: YukiXposedEvent.() -> Unit) {
|
||||
YukiXposedEvent.apply(initiate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 initZygote 事件监听
|
||||
* @param initiate 回调方法体
|
||||
*/
|
||||
fun onInitZygote(initiate: (StartupParam) -> Unit) {
|
||||
initZygoteCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 handleLoadPackage 事件监听
|
||||
* @param initiate 回调方法体
|
||||
*/
|
||||
fun onHandleLoadPackage(initiate: (LoadPackageParam) -> Unit) {
|
||||
handleLoadPackageCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 handleInitPackageResources 事件监听
|
||||
* @param initiate 回调方法体
|
||||
*/
|
||||
fun onHandleInitPackageResources(initiate: (InitPackageResourcesParam) -> Unit) {
|
||||
handleInitPackageResourcesCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调监听事件处理类
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动调用
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
object EventHandler {
|
||||
|
||||
/**
|
||||
* 回调 initZygote 事件监听
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动调用
|
||||
* @param sparam Xposed API 实例
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callInitZygote(sparam: StartupParam?) {
|
||||
if (sparam == null) return
|
||||
initZygoteCallback?.invoke(sparam)
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调 handleLoadPackage 事件监听
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动调用
|
||||
* @param lpparam Xposed API 实例
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callHandleLoadPackage(lpparam: LoadPackageParam?) {
|
||||
if (lpparam == null) return
|
||||
handleLoadPackageCallback?.invoke(lpparam)
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调 handleInitPackageResources 事件监听
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动调用
|
||||
* @param resparam Xposed API 实例
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callHandleInitPackageResources(resparam: InitPackageResourcesParam?) {
|
||||
if (resparam == null) return
|
||||
handleInitPackageResourcesCallback?.invoke(resparam)
|
||||
}
|
||||
}
|
||||
}
|
@@ -38,7 +38,7 @@ import androidx.preference.PreferenceFragmentCompat
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.log.loggerW
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.ui.ModulePreferenceFragment
|
||||
import de.robv.android.xposed.XSharedPreferences
|
||||
@@ -82,10 +82,10 @@ class YukiHookModulePrefs(private val context: Context? = null) {
|
||||
}
|
||||
|
||||
/** 存储名称 - 默认包名 + _preferences */
|
||||
private var prefsName = "${YukiHookXposedBridge.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences"
|
||||
private var prefsName = "${YukiHookBridge.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences"
|
||||
|
||||
/** 是否为 Xposed 环境 */
|
||||
private val isXposedEnvironment = YukiHookAPI.hasXposedBridge
|
||||
private val isXposedEnvironment = YukiHookBridge.hasXposedBridge
|
||||
|
||||
/** [XSharedPreferences] 缓存的 [String] 键值数据 */
|
||||
private var xPrefCacheKeyValueStrings = HashMap<String, String>()
|
||||
@@ -114,7 +114,7 @@ class YukiHookModulePrefs(private val context: Context? = null) {
|
||||
/** 检查 API 装载状态 */
|
||||
private fun checkApi() {
|
||||
if (YukiHookAPI.isLoadedFromBaseContext) error("YukiHookModulePrefs not allowed in Custom Hook API")
|
||||
if (YukiHookAPI.hasXposedBridge && YukiHookXposedBridge.modulePackageName.isBlank())
|
||||
if (YukiHookBridge.hasXposedBridge && YukiHookBridge.modulePackageName.isBlank())
|
||||
error("Xposed modulePackageName load failed, please reset and rebuild it")
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ class YukiHookModulePrefs(private val context: Context? = null) {
|
||||
* @return [XSharedPreferences]
|
||||
*/
|
||||
private val xPref
|
||||
get() = XSharedPreferences(YukiHookXposedBridge.modulePackageName, prefsName).apply {
|
||||
get() = XSharedPreferences(YukiHookBridge.modulePackageName, prefsName).apply {
|
||||
checkApi()
|
||||
makeWorldReadable()
|
||||
reload()
|
||||
|
@@ -34,6 +34,7 @@ import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
|
||||
import com.highcapable.yukihookapi.hook.factory.configs
|
||||
import com.highcapable.yukihookapi.hook.factory.encase
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 的 Xposed 装载 API 调用接口
|
||||
@@ -48,6 +49,8 @@ import com.highcapable.yukihookapi.hook.factory.encase
|
||||
*
|
||||
* 请在 [onHook] 中调用 [YukiHookAPI.encase] 或直接调用 [encase]
|
||||
*
|
||||
* 你还可以实现监听原生 Xposed API 功能 - 重写 [onXposedEvent] 方法即可
|
||||
*
|
||||
* 详情请参考 [IYukiHookXposedInit 接口](https://fankes.github.io/YukiHookAPI/#/config/xposed-using?id=iyukihookxposedinit-%e6%8e%a5%e5%8f%a3)
|
||||
*/
|
||||
interface IYukiHookXposedInit {
|
||||
@@ -69,4 +72,23 @@ interface IYukiHookXposedInit {
|
||||
* 调用 [YukiHookAPI.encase] 或直接调用 [encase] 开始 Hook
|
||||
*/
|
||||
fun onHook()
|
||||
|
||||
/**
|
||||
* 监听 Xposed 原生装载事件
|
||||
*
|
||||
* 若你的 Hook 事件中存在需要兼容的原生 Xposed 功能 - 可在这里实现
|
||||
*
|
||||
* 请在这里使用 [YukiXposedEvent] 创建回调事件监听
|
||||
*
|
||||
* 可监听的事件如下:
|
||||
*
|
||||
* [YukiXposedEvent.onInitZygote]
|
||||
*
|
||||
* [YukiXposedEvent.onHandleLoadPackage]
|
||||
*
|
||||
* [YukiXposedEvent.onHandleInitPackageResources]
|
||||
*
|
||||
* - ❗此接口仅供监听和实现原生 Xposed API 的功能 - 请不要在这里操作 [YukiHookAPI]
|
||||
*/
|
||||
fun onXposedEvent() {}
|
||||
}
|
Reference in New Issue
Block a user