mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 18:55:35 +08:00
Modify move YukiHookBridge.Hooker to YukiHookFactory and fix some bug / add some feature
This commit is contained in:
@@ -43,26 +43,30 @@ 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.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookPriority
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Member
|
||||
|
||||
/**
|
||||
* [YukiHookAPI] 的 [Member] 核心 Hook 实现类
|
||||
*
|
||||
* 核心 API 对接 [YukiHookBridge.Hooker] 实现
|
||||
* 核心 API 对接 [YukiHookHelper] 实现
|
||||
* @param packageParam 需要传入 [PackageParam] 实现方法调用
|
||||
* @param hookClass 要 Hook 的 [HookClass] 实例
|
||||
*/
|
||||
class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) {
|
||||
|
||||
/** 默认 Hook 回调优先级 */
|
||||
val PRIORITY_DEFAULT = YukiHookBridge.Hooker.PRIORITY_DEFAULT
|
||||
val PRIORITY_DEFAULT = YukiHookPriority.PRIORITY_DEFAULT
|
||||
|
||||
/** 延迟回调 Hook 方法结果 */
|
||||
val PRIORITY_LOWEST = YukiHookBridge.Hooker.PRIORITY_LOWEST
|
||||
val PRIORITY_LOWEST = YukiHookPriority.PRIORITY_LOWEST
|
||||
|
||||
/** 更快回调 Hook 方法结果 */
|
||||
val PRIORITY_HIGHEST = YukiHookBridge.Hooker.PRIORITY_HIGHEST
|
||||
val PRIORITY_HIGHEST = YukiHookPriority.PRIORITY_HIGHEST
|
||||
|
||||
/**
|
||||
* Hook 模式定义
|
||||
@@ -208,7 +212,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
private var allMethodsName = ""
|
||||
|
||||
/** 当前被 Hook 的方法、构造方法实例 */
|
||||
private var hookedMember: YukiHookBridge.Hooker.YukiHookedMember? = null
|
||||
private var memberUnhook: YukiMemberHook.Unhook? = null
|
||||
|
||||
/**
|
||||
* 手动指定要 Hook 的方法、构造方法
|
||||
@@ -458,7 +462,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
val replaceHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
|
||||
|
||||
/** 定义替换 Hook 回调方法体 */
|
||||
val replaceMent = object : YukiHookBridge.Hooker.YukiMemberReplacement(priority) {
|
||||
val replaceMent = object : YukiMemberReplacement(priority) {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper): Any? {
|
||||
return replaceHookParam.assign(wrapper).let { param ->
|
||||
try {
|
||||
@@ -482,7 +486,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
val afterHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
|
||||
|
||||
/** 定义前后 Hook 回调方法体 */
|
||||
val beforeAfterHook = object : YukiHookBridge.Hooker.YukiMemberHook(priority) {
|
||||
val beforeAfterHook = object : YukiMemberHook(priority) {
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
beforeHookParam.assign(wrapper).also { param ->
|
||||
runCatching {
|
||||
@@ -517,13 +521,13 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
if (member != null)
|
||||
member.also { member ->
|
||||
runCatching {
|
||||
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent)
|
||||
else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also {
|
||||
(if (isReplaceHookMode) YukiHookHelper.hookMethod(member, replaceMent)
|
||||
else YukiHookHelper.hookMethod(member, beforeAfterHook)).also {
|
||||
when {
|
||||
it.first.member == null -> error("Hook Member [$member] failed")
|
||||
it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!)
|
||||
else -> {
|
||||
hookedMember = it.first
|
||||
memberUnhook = it.first
|
||||
onHookedCallback?.invoke(it.first.member!!)
|
||||
}
|
||||
}
|
||||
@@ -548,25 +552,25 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
else runCatching {
|
||||
when (hookMemberMode) {
|
||||
HookMemberMode.HOOK_ALL_METHODS ->
|
||||
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, replaceMent)
|
||||
else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also {
|
||||
(if (isReplaceHookMode) YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, replaceMent)
|
||||
else YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also {
|
||||
when {
|
||||
it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
|
||||
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
|
||||
else -> {
|
||||
hookedMember = it.first.first()
|
||||
memberUnhook = it.first.first()
|
||||
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
|
||||
}
|
||||
}
|
||||
}
|
||||
HookMemberMode.HOOK_ALL_CONSTRUCTORS ->
|
||||
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, replaceMent)
|
||||
else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also {
|
||||
(if (isReplaceHookMode) YukiHookHelper.hookAllConstructors(hookClass.instance, replaceMent)
|
||||
else YukiHookHelper.hookAllConstructors(hookClass.instance, beforeAfterHook)).also {
|
||||
when {
|
||||
it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched")
|
||||
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
|
||||
else -> {
|
||||
hookedMember = it.first.first()
|
||||
memberUnhook = it.first.first()
|
||||
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
|
||||
}
|
||||
}
|
||||
@@ -736,10 +740,11 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
|
||||
* @param result 回调是否成功
|
||||
*/
|
||||
fun remove(result: (Boolean) -> Unit = {}) {
|
||||
hookedMember?.unhook()
|
||||
memberUnhook?.remove()
|
||||
runCatching { preHookMembers.remove(this@MemberHookCreater.toString()) }
|
||||
result(hookedMember != null)
|
||||
hookedMember = null
|
||||
if (memberUnhook != null) onHookLogMsg(msg = "Remove Hooked Member [${member ?: "All of \"$allMethodsName\""}] done [$tag]")
|
||||
result(memberUnhook != null)
|
||||
memberUnhook = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,17 +48,19 @@ import com.highcapable.yukihookapi.hook.type.android.*
|
||||
import com.highcapable.yukihookapi.hook.type.java.IntType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.inject.YukiHookBridge_Injector
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import dalvik.system.PathClassLoader
|
||||
import de.robv.android.xposed.*
|
||||
import de.robv.android.xposed.IXposedHookInitPackageResources
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.IXposedHookZygoteInit
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Member
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* 这是一个对接 Xposed Hook 入口与 [XposedBridge] 的装载类实现桥
|
||||
@@ -124,8 +126,8 @@ object YukiHookBridge {
|
||||
*/
|
||||
internal val systemContext
|
||||
get() = runCatching {
|
||||
Hooker.findMethod(ActivityThreadClass, name = "getSystemContext")
|
||||
.invoke(Hooker.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context?
|
||||
YukiHookHelper.findMethod(ActivityThreadClass, name = "getSystemContext")
|
||||
.invoke(YukiHookHelper.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context?
|
||||
}.getOrNull() ?: error("Failed to got SystemContext")
|
||||
|
||||
/**
|
||||
@@ -154,7 +156,7 @@ object YukiHookBridge {
|
||||
*/
|
||||
internal val executorName
|
||||
get() = runCatching {
|
||||
(Hooker.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?)
|
||||
(YukiHookHelper.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?)
|
||||
?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown"
|
||||
}.getOrNull() ?: "invalid"
|
||||
|
||||
@@ -241,7 +243,7 @@ object YukiHookBridge {
|
||||
/** Hook [Application] 装载方法 */
|
||||
runCatching {
|
||||
if (AppLifecycleCallback.isCallbackSetUp) {
|
||||
Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "attach", ContextClass), object : Hooker.YukiMemberHook() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "attach", ContextClass), object : YukiMemberHook() {
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) }
|
||||
}
|
||||
@@ -250,26 +252,27 @@ object YukiHookBridge {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) }
|
||||
}
|
||||
})
|
||||
Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onTerminate"), object : Hooker.YukiMemberHook() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "onTerminate"), object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) }
|
||||
}
|
||||
})
|
||||
Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onLowMemory"), object : Hooker.YukiMemberHook() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "onLowMemory"), object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
|
||||
}
|
||||
})
|
||||
Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onTrimMemory", IntType), object : Hooker.YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val type = wrapper.args?.get(0) as? Int? ?: return
|
||||
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
|
||||
}
|
||||
})
|
||||
Hooker.hookMethod(
|
||||
Hooker.findMethod(ApplicationClass, name = "onConfigurationChanged", ConfigurationClass),
|
||||
object : Hooker.YukiMemberHook() {
|
||||
YukiHookHelper.hookMethod(
|
||||
YukiHookHelper.findMethod(ApplicationClass, name = "onTrimMemory", IntType),
|
||||
object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val type = wrapper.args?.get(0) as? Int? ?: return
|
||||
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(ApplicationClass, name = "onConfigurationChanged", ConfigurationClass),
|
||||
object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val config = wrapper.args?.get(0) as? Configuration? ?: return
|
||||
@@ -278,9 +281,9 @@ object YukiHookBridge {
|
||||
})
|
||||
}
|
||||
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
|
||||
Hooker.hookMethod(
|
||||
Hooker.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass),
|
||||
object : Hooker.YukiMemberHook() {
|
||||
YukiHookHelper.hookMethod(
|
||||
YukiHookHelper.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass),
|
||||
object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
(wrapper.args?.get(0) as? Application?)?.also {
|
||||
hostApplication = it
|
||||
@@ -317,23 +320,23 @@ object YukiHookBridge {
|
||||
@YukiGenerateApi
|
||||
fun hookModuleAppStatus(classLoader: ClassLoader?, isHookResourcesStatus: Boolean = false) {
|
||||
if (YukiHookAPI.Configs.isEnableHookModuleStatus)
|
||||
Hooker.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass ->
|
||||
YukiHookHelper.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass ->
|
||||
if (isHookResourcesStatus.not()) {
|
||||
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME),
|
||||
object : Hooker.YukiMemberReplacement() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME),
|
||||
object : YukiMemberReplacement() {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper) = true
|
||||
})
|
||||
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME),
|
||||
object : Hooker.YukiMemberReplacement() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME),
|
||||
object : YukiMemberReplacement() {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName
|
||||
})
|
||||
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME),
|
||||
object : Hooker.YukiMemberReplacement() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME),
|
||||
object : YukiMemberReplacement() {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion
|
||||
})
|
||||
} else
|
||||
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME),
|
||||
object : Hooker.YukiMemberReplacement() {
|
||||
YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME),
|
||||
object : YukiMemberReplacement() {
|
||||
override fun replaceHookedMember(wrapper: HookParamWrapper) = true
|
||||
})
|
||||
}
|
||||
@@ -430,247 +433,4 @@ object YukiHookBridge {
|
||||
/** 系统广播监听回调 */
|
||||
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 核心功能实现实例
|
||||
*
|
||||
* 对接 [XposedBridge] 实现 Hook 功能
|
||||
*/
|
||||
internal object Hooker {
|
||||
|
||||
/** 已经 Hook 的 [Member] 数组 */
|
||||
private val hookedMembers = HashSet<YukiHookedMember>()
|
||||
|
||||
/** 已经 Hook 的全部 [Method] 数组 */
|
||||
private val hookedAllMethods = HashMap<String, HashSet<YukiHookedMember>>()
|
||||
|
||||
/** 已经 Hook 的全部 [Constructor] 数组 */
|
||||
private val hookedAllConstructors = HashMap<String, HashSet<YukiHookedMember>>()
|
||||
|
||||
/** 默认 Hook 回调优先级 */
|
||||
internal const val PRIORITY_DEFAULT = 50
|
||||
|
||||
/** 延迟回调 Hook 方法结果 */
|
||||
internal const val PRIORITY_LOWEST = -10000
|
||||
|
||||
/** 更快回调 Hook 方法结果 */
|
||||
internal const val PRIORITY_HIGHEST = 10000
|
||||
|
||||
/**
|
||||
* 查找 [Class]
|
||||
* @param classLoader 当前 [ClassLoader]
|
||||
* @param baseClass 当前类
|
||||
* @return [Field]
|
||||
* @throws IllegalStateException 如果 [ClassLoader] 为空
|
||||
*/
|
||||
internal fun findClass(classLoader: ClassLoader?, baseClass: Class<*>) =
|
||||
classLoader?.loadClass(baseClass.name) ?: error("ClassLoader is null")
|
||||
|
||||
/**
|
||||
* 查找变量
|
||||
* @param baseClass 所在类
|
||||
* @param name 变量名称
|
||||
* @return [Field]
|
||||
* @throws NoSuchFieldError 如果找不到变量
|
||||
*/
|
||||
internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true }
|
||||
|
||||
/**
|
||||
* 查找方法
|
||||
* @param baseClass 所在类
|
||||
* @param name 方法名称
|
||||
* @param paramTypes 方法参数
|
||||
* @return [Method]
|
||||
* @throws NoSuchMethodError 如果找不到方法
|
||||
*/
|
||||
internal fun findMethod(baseClass: Class<*>, name: String, vararg paramTypes: Class<*>) =
|
||||
baseClass.getDeclaredMethod(name, *paramTypes).apply { isAccessible = true }
|
||||
|
||||
/**
|
||||
* Hook 方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookMethod]
|
||||
* @param hookMethod 需要 Hook 的方法、构造方法
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([YukiHookedMember],[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<YukiHookedMember, Boolean> {
|
||||
runCatching {
|
||||
hookedMembers.takeIf { it.isNotEmpty() }?.forEach {
|
||||
if (it.member.toString() == hookMethod.toString()) return@runCatching it
|
||||
}
|
||||
}
|
||||
return YukiHookedMember.wrapper(XposedBridge.hookMethod(hookMethod, compatCallback(callback))).let {
|
||||
hookedMembers.add(it)
|
||||
Pair(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有 [methodName] 的方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllMethods]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param methodName 方法名
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookAllMethods(
|
||||
hookClass: Class<*>?, methodName: String, callback: YukiHookCallback
|
||||
): Pair<HashSet<YukiHookedMember>, Boolean> {
|
||||
var isAlreadyHook = false
|
||||
val hookedMembers = HashSet<YukiHookedMember>().also {
|
||||
val allMethodsName = "$hookClass$methodName"
|
||||
if (hookedAllMethods.contains(allMethodsName)) {
|
||||
isAlreadyHook = true
|
||||
hookedAllMethods[allMethodsName]?.forEach { e -> it.add(e) }
|
||||
return@also
|
||||
}
|
||||
XposedBridge.hookAllMethods(hookClass, methodName, compatCallback(callback)).takeIf { e -> e.isNotEmpty() }
|
||||
?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allMethodsName)) }
|
||||
hookedAllMethods[allMethodsName] = it
|
||||
}
|
||||
return Pair(hookedMembers, isAlreadyHook)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有构造方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllConstructors]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([HashSet] 成功 Hook 的 [YukiHookedMember] 数组,[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair<HashSet<YukiHookedMember>, Boolean> {
|
||||
var isAlreadyHook = false
|
||||
val hookedMembers = HashSet<YukiHookedMember>().also {
|
||||
val allConstructorsName = "$hookClass<init>"
|
||||
if (hookedAllConstructors.contains(allConstructorsName)) {
|
||||
isAlreadyHook = true
|
||||
hookedAllConstructors[allConstructorsName]?.forEach { e -> it.add(e) }
|
||||
return@also
|
||||
}
|
||||
XposedBridge.hookAllConstructors(hookClass, compatCallback(callback)).takeIf { e -> e.isNotEmpty() }
|
||||
?.forEach { e -> it.add(YukiHookedMember.wrapper(e, allConstructorsName)) }
|
||||
hookedAllConstructors[allConstructorsName] = it
|
||||
}
|
||||
return Pair(hookedMembers, isAlreadyHook)
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容对接 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 优先级 - 默认 [PRIORITY_DEFAULT]
|
||||
*/
|
||||
internal abstract class YukiMemberHook(override val priority: Int = PRIORITY_DEFAULT) : YukiHookCallback(priority) {
|
||||
|
||||
/**
|
||||
* 在方法执行之前注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
open fun beforeHookedMember(wrapper: HookParamWrapper) {}
|
||||
|
||||
/**
|
||||
* 在方法执行之后注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
open fun afterHookedMember(wrapper: HookParamWrapper) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 替换方法回调接口
|
||||
* @param priority Hook 优先级- 默认 [PRIORITY_DEFAULT]
|
||||
*/
|
||||
internal abstract class YukiMemberReplacement(override val priority: Int = PRIORITY_DEFAULT) : 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)
|
||||
|
||||
/**
|
||||
* 已经 Hook 的 [Member] 实现类
|
||||
* @param instance 对接 [XC_MethodHook.Unhook]
|
||||
* @param tag 标识多个 [Member] Hook 的标签
|
||||
*/
|
||||
internal class YukiHookedMember private constructor(private val instance: XC_MethodHook.Unhook, private val tag: String) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 从 [XC_MethodHook.Unhook] 创建 [YukiHookedMember] 实例
|
||||
* @param instance [XC_MethodHook.Unhook] 实例
|
||||
* @param tag 标识多个 [Member] Hook 的标签 - 默认空
|
||||
* @return [YukiHookedMember]
|
||||
*/
|
||||
internal fun wrapper(instance: XC_MethodHook.Unhook, tag: String = "") = YukiHookedMember(instance, tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前被 Hook 的 [Member]
|
||||
* @return [Member] or null
|
||||
*/
|
||||
internal val member: Member? get() = instance.hookedMethod
|
||||
|
||||
/** 解除 Hook */
|
||||
internal fun unhook() {
|
||||
if (tag.isNotBlank()) runCatching {
|
||||
if (hookedAllMethods.contains(tag))
|
||||
hookedAllMethods[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.instance.unhook() }
|
||||
if (hookedAllConstructors.contains(tag))
|
||||
hookedAllConstructors[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.instance.unhook() }
|
||||
hookedAllMethods.remove(tag)
|
||||
hookedAllConstructors.remove(tag)
|
||||
} else {
|
||||
instance.unhook()
|
||||
runCatching { hookedMembers.remove(this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* 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/7/28.
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge.factory
|
||||
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Member
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* Hook 回调优先级配置类
|
||||
*/
|
||||
internal object YukiHookPriority {
|
||||
|
||||
/** 默认 Hook 回调优先级 */
|
||||
internal const val PRIORITY_DEFAULT = 50
|
||||
|
||||
/** 延迟回调 Hook 方法结果 */
|
||||
internal const val PRIORITY_LOWEST = -10000
|
||||
|
||||
/** 更快回调 Hook 方法结果 */
|
||||
internal const val PRIORITY_HIGHEST = 10000
|
||||
}
|
||||
|
||||
/**
|
||||
* 已经 Hook 的方法、构造方法缓存数组
|
||||
*/
|
||||
internal object YukiHookedMembers {
|
||||
|
||||
/** 已经 Hook 的 [Member] 数组 */
|
||||
internal val hookedMembers = HashSet<YukiMemberHook.Unhook>()
|
||||
|
||||
/** 已经 Hook 的成组 [Method] 数组 */
|
||||
internal val hookedQueueMethods = HashMap<String, HashSet<YukiMemberHook.Unhook>>()
|
||||
|
||||
/** 已经 Hook 的成组 [Constructor] 数组 */
|
||||
internal val hookedQueueConstructors = HashMap<String, HashSet<YukiMemberHook.Unhook>>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 核心功能实现实例
|
||||
*
|
||||
* 对接 [XposedBridge] 实现 Hook 功能
|
||||
*/
|
||||
internal object YukiHookHelper {
|
||||
|
||||
/**
|
||||
* 查找 [Class]
|
||||
* @param classLoader 当前 [ClassLoader]
|
||||
* @param baseClass 当前类
|
||||
* @return [Field]
|
||||
* @throws IllegalStateException 如果 [ClassLoader] 为空
|
||||
*/
|
||||
internal fun findClass(classLoader: ClassLoader?, baseClass: Class<*>) =
|
||||
classLoader?.loadClass(baseClass.name) ?: error("ClassLoader is null")
|
||||
|
||||
/**
|
||||
* 查找变量
|
||||
* @param baseClass 所在类
|
||||
* @param name 变量名称
|
||||
* @return [Field]
|
||||
* @throws NoSuchFieldError 如果找不到变量
|
||||
*/
|
||||
internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true }
|
||||
|
||||
/**
|
||||
* 查找方法
|
||||
* @param baseClass 所在类
|
||||
* @param name 方法名称
|
||||
* @param paramTypes 方法参数
|
||||
* @return [Method]
|
||||
* @throws NoSuchMethodError 如果找不到方法
|
||||
*/
|
||||
internal fun findMethod(baseClass: Class<*>, name: String, vararg paramTypes: Class<*>) =
|
||||
baseClass.getDeclaredMethod(name, *paramTypes).apply { isAccessible = true }
|
||||
|
||||
/**
|
||||
* Hook 方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookMethod]
|
||||
* @param hookMethod 需要 Hook 的方法、构造方法
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([YukiMemberHook.Unhook],[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookMethod(hookMethod: Member?, callback: YukiHookCallback): Pair<YukiMemberHook.Unhook, Boolean> {
|
||||
runCatching {
|
||||
YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach {
|
||||
if (it.member.toString() == hookMethod.toString()) return@runCatching it
|
||||
}
|
||||
}
|
||||
return YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(hookMethod, callback.compat())).let {
|
||||
YukiHookedMembers.hookedMembers.add(it)
|
||||
Pair(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有 [methodName] 的方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllMethods]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param methodName 方法名
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([HashSet] 成功 Hook 的 [YukiMemberHook.Unhook] 数组,[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookAllMethods(
|
||||
hookClass: Class<*>?, methodName: String, callback: YukiHookCallback
|
||||
): Pair<HashSet<YukiMemberHook.Unhook>, Boolean> {
|
||||
var isAlreadyHook = false
|
||||
val hookedMembers = HashSet<YukiMemberHook.Unhook>().also {
|
||||
val allMethodsName = "$hookClass$methodName"
|
||||
if (YukiHookedMembers.hookedQueueMethods.contains(allMethodsName)) {
|
||||
isAlreadyHook = true
|
||||
YukiHookedMembers.hookedQueueMethods[allMethodsName]?.forEach { e -> it.add(e) }
|
||||
return@also
|
||||
}
|
||||
XposedBridge.hookAllMethods(hookClass, methodName, callback.compat()).takeIf { e -> e.isNotEmpty() }
|
||||
?.forEach { e -> it.add(YukiMemberHook.Unhook.wrapper(e, allMethodsName)) }
|
||||
YukiHookedMembers.hookedQueueMethods[allMethodsName] = it
|
||||
}
|
||||
return Pair(hookedMembers, isAlreadyHook)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 当前 [hookClass] 所有构造方法
|
||||
*
|
||||
* 对接 [XposedBridge.hookAllConstructors]
|
||||
* @param hookClass 当前 Hook 的 [Class]
|
||||
* @param callback 回调
|
||||
* @return [Pair] - ([HashSet] 成功 Hook 的 [YukiMemberHook.Unhook] 数组,[Boolean] 是否已经 Hook)
|
||||
*/
|
||||
internal fun hookAllConstructors(hookClass: Class<*>?, callback: YukiHookCallback): Pair<HashSet<YukiMemberHook.Unhook>, Boolean> {
|
||||
var isAlreadyHook = false
|
||||
val hookedMembers = HashSet<YukiMemberHook.Unhook>().also {
|
||||
val allConstructorsName = "$hookClass<init>"
|
||||
if (YukiHookedMembers.hookedQueueConstructors.contains(allConstructorsName)) {
|
||||
isAlreadyHook = true
|
||||
YukiHookedMembers.hookedQueueConstructors[allConstructorsName]?.forEach { e -> it.add(e) }
|
||||
return@also
|
||||
}
|
||||
XposedBridge.hookAllConstructors(hookClass, callback.compat()).takeIf { e -> e.isNotEmpty() }
|
||||
?.forEach { e -> it.add(YukiMemberHook.Unhook.wrapper(e, allConstructorsName)) }
|
||||
YukiHookedMembers.hookedQueueConstructors[allConstructorsName] = it
|
||||
}
|
||||
return Pair(hookedMembers, isAlreadyHook)
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容对接 Hook 回调接口
|
||||
* @return [XC_MethodHook] 原始接口
|
||||
*/
|
||||
private fun YukiHookCallback.compat() = object : XC_MethodHook(priority) {
|
||||
|
||||
/** 创建 Hook 前 [HookParamWrapper] */
|
||||
private val beforeHookWrapper = HookParamWrapper()
|
||||
|
||||
/** 创建 Hook 后 [HookParamWrapper] */
|
||||
private val afterHookWrapper = HookParamWrapper()
|
||||
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
if (param == null) return
|
||||
if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type")
|
||||
if (this@compat is YukiMemberReplacement)
|
||||
param.result = replaceHookedMember(beforeHookWrapper.assign(param))
|
||||
else beforeHookedMember(beforeHookWrapper.assign(param))
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
if (param == null) return
|
||||
if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type")
|
||||
afterHookedMember(afterHookWrapper.assign(param))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 替换方法回调接口
|
||||
* @param priority Hook 优先级- 默认 [YukiHookPriority.PRIORITY_DEFAULT]
|
||||
*/
|
||||
internal abstract class YukiMemberReplacement(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiMemberHook(priority) {
|
||||
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
wrapper.result = replaceHookedMember(wrapper)
|
||||
}
|
||||
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {}
|
||||
|
||||
/**
|
||||
* 拦截替换为指定结果
|
||||
* @param wrapper 包装实例
|
||||
* @return [Any] or null
|
||||
*/
|
||||
abstract fun replaceHookedMember(wrapper: HookParamWrapper): Any?
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 方法回调接口
|
||||
* @param priority Hook 优先级 - 默认 [YukiHookPriority.PRIORITY_DEFAULT]
|
||||
*/
|
||||
internal abstract class YukiMemberHook(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiHookCallback(priority) {
|
||||
|
||||
/**
|
||||
* 在方法执行之前注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
open fun beforeHookedMember(wrapper: HookParamWrapper) {}
|
||||
|
||||
/**
|
||||
* 在方法执行之后注入
|
||||
* @param wrapper 包装实例
|
||||
*/
|
||||
open fun afterHookedMember(wrapper: HookParamWrapper) {}
|
||||
|
||||
/**
|
||||
* 已经 Hook 且可被解除 Hook 的 [Member] 实现类
|
||||
* @param instance 对接 [XC_MethodHook.Unhook]
|
||||
* @param tag 标识多个 [Member] Hook 的标签 - 例如 [YukiHookHelper.hookAllMethods]、[YukiHookHelper.hookAllConstructors]
|
||||
*/
|
||||
internal class Unhook private constructor(private val instance: XC_MethodHook.Unhook, private val tag: String) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 从 [XC_MethodHook.Unhook] 创建 [Unhook] 实例
|
||||
* @param instance [XC_MethodHook.Unhook] 实例
|
||||
* @param tag 标识多个 [Member] Hook 的标签 - 默认空
|
||||
* @return [Unhook]
|
||||
*/
|
||||
internal fun wrapper(instance: XC_MethodHook.Unhook, tag: String = "") = Unhook(instance, tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前被 Hook 的 [Member]
|
||||
* @return [Member] or null
|
||||
*/
|
||||
internal val member: Member? get() = instance.hookedMethod
|
||||
|
||||
/**
|
||||
* 解除 Hook 并从
|
||||
* [YukiHookedMembers.hookedMembers]、
|
||||
* [YukiHookedMembers.hookedQueueMethods]、
|
||||
* [YukiHookedMembers.hookedQueueConstructors]
|
||||
* 缓存数组中移除
|
||||
*/
|
||||
internal fun remove() {
|
||||
if (tag.isNotBlank()) runCatching {
|
||||
when {
|
||||
YukiHookedMembers.hookedQueueMethods.contains(tag) -> {
|
||||
YukiHookedMembers.hookedQueueMethods[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.unhook(isNeedRemove = false) }
|
||||
YukiHookedMembers.hookedQueueMethods.remove(tag)
|
||||
}
|
||||
YukiHookedMembers.hookedQueueConstructors.contains(tag) -> {
|
||||
YukiHookedMembers.hookedQueueConstructors[tag]?.takeIf { it.isNotEmpty() }?.forEach { it.unhook(isNeedRemove = false) }
|
||||
YukiHookedMembers.hookedQueueConstructors.remove(tag)
|
||||
}
|
||||
else -> unhook()
|
||||
}
|
||||
} else unhook()
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除 [instance] 的 Hook
|
||||
* @param isNeedRemove 是否需要从 [YukiHookedMembers.hookedMembers] 中移除
|
||||
*/
|
||||
private fun unhook(isNeedRemove: Boolean = true) {
|
||||
instance.unhook()
|
||||
if (isNeedRemove) runCatching { YukiHookedMembers.hookedMembers.remove(this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 回调接口父类
|
||||
* @param priority Hook 优先级
|
||||
*/
|
||||
internal abstract class YukiHookCallback(open val priority: Int)
|
Reference in New Issue
Block a user