Modify move YukiHookBridge.Hooker to YukiHookFactory and fix some bug / add some feature

This commit is contained in:
2022-07-29 00:45:22 +08:00
parent eac27cf36a
commit d2f529bbe4
3 changed files with 364 additions and 295 deletions

View File

@@ -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.type.HookEntryType
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge 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.Field
import java.lang.reflect.Member import java.lang.reflect.Member
/** /**
* [YukiHookAPI] 的 [Member] 核心 Hook 实现类 * [YukiHookAPI] 的 [Member] 核心 Hook 实现类
* *
* 核心 API 对接 [YukiHookBridge.Hooker] 实现 * 核心 API 对接 [YukiHookHelper] 实现
* @param packageParam 需要传入 [PackageParam] 实现方法调用 * @param packageParam 需要传入 [PackageParam] 实现方法调用
* @param hookClass 要 Hook 的 [HookClass] 实例 * @param hookClass 要 Hook 的 [HookClass] 实例
*/ */
class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) { class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackageParam, @PublishedApi internal val hookClass: HookClass) {
/** 默认 Hook 回调优先级 */ /** 默认 Hook 回调优先级 */
val PRIORITY_DEFAULT = YukiHookBridge.Hooker.PRIORITY_DEFAULT val PRIORITY_DEFAULT = YukiHookPriority.PRIORITY_DEFAULT
/** 延迟回调 Hook 方法结果 */ /** 延迟回调 Hook 方法结果 */
val PRIORITY_LOWEST = YukiHookBridge.Hooker.PRIORITY_LOWEST val PRIORITY_LOWEST = YukiHookPriority.PRIORITY_LOWEST
/** 更快回调 Hook 方法结果 */ /** 更快回调 Hook 方法结果 */
val PRIORITY_HIGHEST = YukiHookBridge.Hooker.PRIORITY_HIGHEST val PRIORITY_HIGHEST = YukiHookPriority.PRIORITY_HIGHEST
/** /**
* Hook 模式定义 * Hook 模式定义
@@ -208,7 +212,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
private var allMethodsName = "" private var allMethodsName = ""
/** 当前被 Hook 的方法、构造方法实例 */ /** 当前被 Hook 的方法、构造方法实例 */
private var hookedMember: YukiHookBridge.Hooker.YukiHookedMember? = null private var memberUnhook: YukiMemberHook.Unhook? = null
/** /**
* 手动指定要 Hook 的方法、构造方法 * 手动指定要 Hook 的方法、构造方法
@@ -458,7 +462,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
val replaceHookParam = HookParam(createrInstance = this@YukiMemberHookCreater) val replaceHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
/** 定义替换 Hook 回调方法体 */ /** 定义替换 Hook 回调方法体 */
val replaceMent = object : YukiHookBridge.Hooker.YukiMemberReplacement(priority) { val replaceMent = object : YukiMemberReplacement(priority) {
override fun replaceHookedMember(wrapper: HookParamWrapper): Any? { override fun replaceHookedMember(wrapper: HookParamWrapper): Any? {
return replaceHookParam.assign(wrapper).let { param -> return replaceHookParam.assign(wrapper).let { param ->
try { try {
@@ -482,7 +486,7 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
val afterHookParam = HookParam(createrInstance = this@YukiMemberHookCreater) val afterHookParam = HookParam(createrInstance = this@YukiMemberHookCreater)
/** 定义前后 Hook 回调方法体 */ /** 定义前后 Hook 回调方法体 */
val beforeAfterHook = object : YukiHookBridge.Hooker.YukiMemberHook(priority) { val beforeAfterHook = object : YukiMemberHook(priority) {
override fun beforeHookedMember(wrapper: HookParamWrapper) { override fun beforeHookedMember(wrapper: HookParamWrapper) {
beforeHookParam.assign(wrapper).also { param -> beforeHookParam.assign(wrapper).also { param ->
runCatching { runCatching {
@@ -517,13 +521,13 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
if (member != null) if (member != null)
member.also { member -> member.also { member ->
runCatching { runCatching {
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookMethod(member, replaceMent) (if (isReplaceHookMode) YukiHookHelper.hookMethod(member, replaceMent)
else YukiHookBridge.Hooker.hookMethod(member, beforeAfterHook)).also { else YukiHookHelper.hookMethod(member, beforeAfterHook)).also {
when { when {
it.first.member == null -> error("Hook Member [$member] failed") it.first.member == null -> error("Hook Member [$member] failed")
it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!) it.second -> onAlreadyHookedCallback?.invoke(it.first.member!!)
else -> { else -> {
hookedMember = it.first memberUnhook = it.first
onHookedCallback?.invoke(it.first.member!!) onHookedCallback?.invoke(it.first.member!!)
} }
} }
@@ -548,25 +552,25 @@ class YukiMemberHookCreater(@PublishedApi internal val packageParam: PackagePara
else runCatching { else runCatching {
when (hookMemberMode) { when (hookMemberMode) {
HookMemberMode.HOOK_ALL_METHODS -> HookMemberMode.HOOK_ALL_METHODS ->
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, replaceMent) (if (isReplaceHookMode) YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, replaceMent)
else YukiHookBridge.Hooker.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also { else YukiHookHelper.hookAllMethods(hookClass.instance, allMethodsName, beforeAfterHook)).also {
when { when {
it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched") it.first.isEmpty() -> throw NoSuchMethodError("No Method name \"$allMethodsName\" matched")
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) } it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
else -> { else -> {
hookedMember = it.first.first() memberUnhook = it.first.first()
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) } it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) }
} }
} }
} }
HookMemberMode.HOOK_ALL_CONSTRUCTORS -> HookMemberMode.HOOK_ALL_CONSTRUCTORS ->
(if (isReplaceHookMode) YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, replaceMent) (if (isReplaceHookMode) YukiHookHelper.hookAllConstructors(hookClass.instance, replaceMent)
else YukiHookBridge.Hooker.hookAllConstructors(hookClass.instance, beforeAfterHook)).also { else YukiHookHelper.hookAllConstructors(hookClass.instance, beforeAfterHook)).also {
when { when {
it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched") it.first.isEmpty() -> throw NoSuchMethodError("No Constructor matched")
it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) } it.second -> it.first.forEach { e -> if (e.member != null) onAlreadyHookedCallback?.invoke(e.member!!) }
else -> { else -> {
hookedMember = it.first.first() memberUnhook = it.first.first()
it.first.forEach { e -> if (e.member != null) onHookedCallback?.invoke(e.member!!) } 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 回调是否成功 * @param result 回调是否成功
*/ */
fun remove(result: (Boolean) -> Unit = {}) { fun remove(result: (Boolean) -> Unit = {}) {
hookedMember?.unhook() memberUnhook?.remove()
runCatching { preHookMembers.remove(this@MemberHookCreater.toString()) } runCatching { preHookMembers.remove(this@MemberHookCreater.toString()) }
result(hookedMember != null) if (memberUnhook != null) onHookLogMsg(msg = "Remove Hooked Member [${member ?: "All of \"$allMethodsName\""}] done [$tag]")
hookedMember = null result(memberUnhook != null)
memberUnhook = null
} }
} }
} }

View File

@@ -48,17 +48,19 @@ import com.highcapable.yukihookapi.hook.type.android.*
import com.highcapable.yukihookapi.hook.type.java.IntType 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.YukiModuleResources
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources 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.inject.YukiHookBridge_Injector
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
import dalvik.system.PathClassLoader 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_InitPackageResources
import de.robv.android.xposed.callbacks.XC_LoadPackage 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] 的装载类实现桥 * 这是一个对接 Xposed Hook 入口与 [XposedBridge] 的装载类实现桥
@@ -124,8 +126,8 @@ object YukiHookBridge {
*/ */
internal val systemContext internal val systemContext
get() = runCatching { get() = runCatching {
Hooker.findMethod(ActivityThreadClass, name = "getSystemContext") YukiHookHelper.findMethod(ActivityThreadClass, name = "getSystemContext")
.invoke(Hooker.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context? .invoke(YukiHookHelper.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context?
}.getOrNull() ?: error("Failed to got SystemContext") }.getOrNull() ?: error("Failed to got SystemContext")
/** /**
@@ -154,7 +156,7 @@ object YukiHookBridge {
*/ */
internal val executorName internal val executorName
get() = runCatching { 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" ?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown"
}.getOrNull() ?: "invalid" }.getOrNull() ?: "invalid"
@@ -241,7 +243,7 @@ object YukiHookBridge {
/** Hook [Application] 装载方法 */ /** Hook [Application] 装载方法 */
runCatching { runCatching {
if (AppLifecycleCallback.isCallbackSetUp) { 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) { override fun beforeHookedMember(wrapper: HookParamWrapper) {
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) } (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) } (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) { override fun afterHookedMember(wrapper: HookParamWrapper) {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) } (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) { override fun afterHookedMember(wrapper: HookParamWrapper) {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) } (wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
} }
}) })
Hooker.hookMethod(Hooker.findMethod(ApplicationClass, name = "onTrimMemory", IntType), object : Hooker.YukiMemberHook() { YukiHookHelper.hookMethod(
override fun afterHookedMember(wrapper: HookParamWrapper) { YukiHookHelper.findMethod(ApplicationClass, name = "onTrimMemory", IntType),
val self = wrapper.instance as? Application? ?: return object : YukiMemberHook() {
val type = wrapper.args?.get(0) as? Int? ?: return override fun afterHookedMember(wrapper: HookParamWrapper) {
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) 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 = "onConfigurationChanged", ConfigurationClass),
object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) { override fun afterHookedMember(wrapper: HookParamWrapper) {
val self = wrapper.instance as? Application? ?: return val self = wrapper.instance as? Application? ?: return
val config = wrapper.args?.get(0) as? Configuration? ?: return val config = wrapper.args?.get(0) as? Configuration? ?: return
@@ -278,9 +281,9 @@ object YukiHookBridge {
}) })
} }
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
Hooker.hookMethod( YukiHookHelper.hookMethod(
Hooker.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass), YukiHookHelper.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass),
object : Hooker.YukiMemberHook() { object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) { override fun afterHookedMember(wrapper: HookParamWrapper) {
(wrapper.args?.get(0) as? Application?)?.also { (wrapper.args?.get(0) as? Application?)?.also {
hostApplication = it hostApplication = it
@@ -317,23 +320,23 @@ object YukiHookBridge {
@YukiGenerateApi @YukiGenerateApi
fun hookModuleAppStatus(classLoader: ClassLoader?, isHookResourcesStatus: Boolean = false) { fun hookModuleAppStatus(classLoader: ClassLoader?, isHookResourcesStatus: Boolean = false) {
if (YukiHookAPI.Configs.isEnableHookModuleStatus) if (YukiHookAPI.Configs.isEnableHookModuleStatus)
Hooker.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass -> YukiHookHelper.findClass(classLoader, YukiHookModuleStatus::class.java).also { statusClass ->
if (isHookResourcesStatus.not()) { if (isHookResourcesStatus.not()) {
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME), YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME),
object : Hooker.YukiMemberReplacement() { object : YukiMemberReplacement() {
override fun replaceHookedMember(wrapper: HookParamWrapper) = true override fun replaceHookedMember(wrapper: HookParamWrapper) = true
}) })
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME), YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME),
object : Hooker.YukiMemberReplacement() { object : YukiMemberReplacement() {
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName
}) })
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME), YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME),
object : Hooker.YukiMemberReplacement() { object : YukiMemberReplacement() {
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion
}) })
} else } else
Hooker.hookMethod(Hooker.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME), YukiHookHelper.hookMethod(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME),
object : Hooker.YukiMemberReplacement() { object : YukiMemberReplacement() {
override fun replaceHookedMember(wrapper: HookParamWrapper) = true override fun replaceHookedMember(wrapper: HookParamWrapper) = true
}) })
} }
@@ -430,247 +433,4 @@ object YukiHookBridge {
/** 系统广播监听回调 */ /** 系统广播监听回调 */
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>() 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) }
}
}
}
}
} }

View File

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