Merge findMethod/findField/findClass function and hookMember function to self api

This commit is contained in:
2022-08-09 02:29:48 +08:00
parent 4a3d5b093e
commit ff05e3cd5e
2 changed files with 109 additions and 120 deletions

View File

@@ -40,7 +40,10 @@ import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.YukiGenerateApi import com.highcapable.yukihookapi.annotation.YukiGenerateApi
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
@@ -144,10 +147,9 @@ object YukiHookBridge {
* @throws IllegalStateException 如果获取不到系统框架的 [Context] * @throws IllegalStateException 如果获取不到系统框架的 [Context]
*/ */
internal val systemContext internal val systemContext
get() = runCatching { get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let {
YukiHookHelper.findMethod(ActivityThreadClass, name = "getSystemContext") ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
.invoke(YukiHookHelper.findMethod(ActivityThreadClass, name = "currentActivityThread").invoke(null)) as? Context? } ?: error("Failed to got SystemContext")
}.getOrNull() ?: error("Failed to got SystemContext")
/** /**
* 模块是否装载了 Xposed 回调方法 * 模块是否装载了 Xposed 回调方法
@@ -175,7 +177,7 @@ object YukiHookBridge {
*/ */
internal val executorName internal val executorName
get() = runCatching { get() = runCatching {
(YukiHookHelper.findField(XposedBridge::class.java, name = "TAG").get(null) as? String?) classOf<XposedBridge>().field { name = "TAG" }.ignored().get().string().takeIf { it.isNotBlank() }
?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown" ?.replace(oldValue = "Bridge", newValue = "")?.replace(oldValue = "-", newValue = "")?.trim() ?: "unknown"
}.getOrNull() ?: "invalid" }.getOrNull() ?: "invalid"
@@ -200,10 +202,11 @@ object YukiHookBridge {
* @param packageName 当前包名 * @param packageName 当前包名
* @return [Int] * @return [Int]
*/ */
internal fun findUserId(packageName: String) = runCatching { internal fun findUserId(packageName: String) =
YukiHookHelper.findMethod(UserHandleClass, name = "getUserId", IntType) UserHandleClass.method {
.invoke(null, systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) as? Int ?: 0 name = "getUserId"
}.getOrNull() ?: 0 param(IntType)
}.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid)
/** /**
* 自动忽略 MIUI 系统可能出现的日志收集注入实例 * 自动忽略 MIUI 系统可能出现的日志收集注入实例
@@ -275,7 +278,7 @@ object YukiHookBridge {
/** Hook [Application] 装载方法 */ /** Hook [Application] 装载方法 */
runCatching { runCatching {
if (AppLifecycleCallback.isCallbackSetUp) { if (AppLifecycleCallback.isCallbackSetUp) {
YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "attach", ContextClass), object : YukiMemberHook() { YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(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) }
} }
@@ -284,56 +287,51 @@ 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) }
} }
}) })
YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onTerminate"), object : YukiMemberHook() { YukiHookHelper.hook(ApplicationClass.method { 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) }
} }
}) })
YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onLowMemory"), object : YukiMemberHook() { YukiHookHelper.hook(ApplicationClass.method { 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) }
} }
}) })
YukiHookHelper.hookMember( YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() {
YukiHookHelper.findMethod(ApplicationClass, name = "onTrimMemory", IntType), override fun afterHookedMember(wrapper: HookParamWrapper) {
object : YukiMemberHook() { val self = wrapper.instance as? Application? ?: return
override fun afterHookedMember(wrapper: HookParamWrapper) { val type = wrapper.args?.get(0) as? Int? ?: return
val self = wrapper.instance as? Application? ?: return AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
val type = wrapper.args?.get(0) as? Int? ?: return }
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type) })
} YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() {
}) override fun afterHookedMember(wrapper: HookParamWrapper) {
YukiHookHelper.hookMember(YukiHookHelper.findMethod(ApplicationClass, name = "onConfigurationChanged", ConfigurationClass), val self = wrapper.instance as? Application? ?: return
object : YukiMemberHook() { val config = wrapper.args?.get(0) as? Configuration? ?: return
override fun afterHookedMember(wrapper: HookParamWrapper) { AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
val self = wrapper.instance as? Application? ?: return }
val config = wrapper.args?.get(0) as? Configuration? ?: return })
AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
}
})
} }
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp) if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
YukiHookHelper.hookMember( YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() {
YukiHookHelper.findMethod(InstrumentationClass, name = "callApplicationOnCreate", ApplicationClass), override fun afterHookedMember(wrapper: HookParamWrapper) {
object : YukiMemberHook() { (wrapper.args?.get(0) as? Application?)?.also {
override fun afterHookedMember(wrapper: HookParamWrapper) { hostApplication = it
(wrapper.args?.get(0) as? Application?)?.also { AppLifecycleCallback.onCreateCallback?.invoke(it)
hostApplication = it AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) ->
AppLifecycleCallback.onCreateCallback?.invoke(it) if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() {
AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) -> override fun onReceive(context: Context?, intent: Intent?) {
if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() { if (context == null || intent == null) return
override fun onReceive(context: Context?, intent: Intent?) { if (e.first.any { e -> e == intent.action }) e.second(context, intent)
if (context == null || intent == null) return }
if (e.first.any { a -> a == intent.action }) e.second(context, intent) }, IntentFilter().apply { e.first.forEach { e -> addAction(e) } })
}
}, IntentFilter().apply { e.first.forEach { a -> addAction(a) } })
}
if (isDataChannelRegister) return
isDataChannelRegister = true
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
} }
if (isDataChannelRegister) return
isDataChannelRegister = true
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
} }
}) }
})
} }
} }
@@ -343,12 +341,15 @@ object YukiHookBridge {
*/ */
internal fun injectModuleAppResources(context: Context) { internal fun injectModuleAppResources(context: Context) {
if (injectedHostContextHashCodes.contains(context.hashCode())) return if (injectedHostContextHashCodes.contains(context.hashCode())) return
injectedHostContextHashCodes.add(context.hashCode())
if (hasXposedBridge) if (hasXposedBridge)
runCatching { AssetManagerClass.method {
YukiHookHelper.findMethod(AssetManagerClass, name = "addAssetPath", StringType) name = "addAssetPath"
.invoke(context.resources.assets, moduleAppFilePath) param(StringType)
injectedHostContextHashCodes.add(context.hashCode()) }.ignored().onNoSuchMethod {
}.onFailure { yLoggerE(msg = "Failed to inject module resources in context [$context]", e = it) } runCatching { injectedHostContextHashCodes.remove(context.hashCode()) }
yLoggerE(msg = "Failed to inject module resources in context [$context]", e = it)
}.get(context.resources.assets).call(moduleAppFilePath)
else yLoggerW(msg = "You can only inject module resources in Xposed Environment") else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
} }
@@ -364,14 +365,12 @@ object YukiHookBridge {
*/ */
internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) { internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) {
runCatching { runCatching {
YukiHookHelper.hookMember( YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() {
YukiHookHelper.findMethod(JavaClassLoader, name = "loadClass", StringType, BooleanType), override fun afterHookedMember(wrapper: HookParamWrapper) {
object : YukiMemberHook() { if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name)
override fun afterHookedMember(wrapper: HookParamWrapper) { (wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) }
if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name) }
(wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) } })
}
})
}.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") } }.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") }
} }
@@ -384,27 +383,23 @@ object YukiHookBridge {
*/ */
@YukiGenerateApi @YukiGenerateApi
fun hookModuleAppStatus(loader: ClassLoader?, isHookResourcesStatus: Boolean = false) { fun hookModuleAppStatus(loader: ClassLoader?, isHookResourcesStatus: Boolean = false) {
if (YukiHookAPI.Configs.isEnableHookModuleStatus) if (YukiHookAPI.Configs.isEnableHookModuleStatus.not()) return
YukiHookHelper.findClass(loader, YukiHookModuleStatus::class.java).also { statusClass -> classOf<YukiHookModuleStatus>(loader).apply {
if (isHookResourcesStatus.not()) { if (isHookResourcesStatus.not()) {
YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME), YukiHookHelper.hook(method { name = YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME }, object : YukiMemberReplacement() {
object : YukiMemberReplacement() { override fun replaceHookedMember(wrapper: HookParamWrapper) = true
override fun replaceHookedMember(wrapper: HookParamWrapper) = true })
}) YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME }, object : YukiMemberReplacement() {
YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME), override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName
object : YukiMemberReplacement() { })
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorName YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME }, object : YukiMemberReplacement() {
}) override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion
YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME), })
object : YukiMemberReplacement() { } else
override fun replaceHookedMember(wrapper: HookParamWrapper) = executorVersion YukiHookHelper.hook(method { name = YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME }, object : YukiMemberReplacement() {
}) override fun replaceHookedMember(wrapper: HookParamWrapper) = true
} else })
YukiHookHelper.hookMember(YukiHookHelper.findMethod(statusClass, YukiHookModuleStatus.HAS_RESOURCES_HOOK_METHOD_NAME), }
object : YukiMemberReplacement() {
override fun replaceHookedMember(wrapper: HookParamWrapper) = true
})
}
} }
/** /**

View File

@@ -25,15 +25,19 @@
* *
* This file is Created by fankes on 2022/7/28. * This file is Created by fankes on 2022/7/28.
*/ */
@file:Suppress("NewApi")
package com.highcapable.yukihookapi.hook.xposed.bridge.factory package com.highcapable.yukihookapi.hook.xposed.bridge.factory
import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.log.yLoggerE
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 de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import java.lang.reflect.Field
import java.lang.reflect.Member import java.lang.reflect.Member
import java.lang.reflect.Method
/** /**
* Hook 回调优先级配置类 * Hook 回调优先级配置类
@@ -67,33 +71,20 @@ internal object YukiHookedMembers {
internal object YukiHookHelper { internal object YukiHookHelper {
/** /**
* 查找 [Class] * Hook [BaseFinder.BaseResult]
* @param loader 当前 [ClassLoader] * @param traction 直接调用 [BaseFinder.BaseResult]
* @param baseClass 当前类 * @param callback 回调
* @return [Field] * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook)
* @throws IllegalStateException 如果 [ClassLoader] 为空
*/ */
internal fun findClass(loader: ClassLoader?, baseClass: Class<*>) = loader?.loadClass(baseClass.name) ?: error("ClassLoader is null") internal fun hook(traction: BaseFinder.BaseResult, callback: YukiHookCallback) = runCatching {
hookMember(
/** when (traction) {
* 查找 [Field] is MethodFinder.Result -> traction.ignored().give()
* @param baseClass 所在类 is ConstructorFinder.Result -> traction.ignored().give()
* @param name 变量名称 else -> error("Unexpected BaseFinder result interface type")
* @return [Field] }, callback
* @throws NoSuchFieldError 如果找不到变量 )
*/ }.onFailure { yLoggerE(msg = "Hooking Process exception occurred", e = it) }.getOrNull() ?: Pair(null, false)
internal fun findField(baseClass: Class<*>, name: String) = baseClass.getDeclaredField(name).apply { isAccessible = true }
/**
* 查找 [Method]
* @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 [Member] * Hook [Member]
@@ -103,18 +94,21 @@ internal object YukiHookHelper {
* @param callback 回调 * @param callback 回调
* @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook)
*/ */
internal fun hookMember(member: Member, callback: YukiHookCallback): Pair<YukiMemberHook.Unhook?, Boolean> { internal fun hookMember(member: Member?, callback: YukiHookCallback): Pair<YukiMemberHook.Unhook?, Boolean> {
runCatching { runCatching {
YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach { YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach {
if (it.member.toString() == member.toString()) return@runCatching it if (it.member.toString() == member?.toString()) return@runCatching it
} }
} }
return if (YukiHookBridge.hasXposedBridge) return when {
YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(member, callback.compat())).let { member == null -> Pair(null, false)
YukiHookedMembers.hookedMembers.add(it) YukiHookBridge.hasXposedBridge ->
Pair(it, false) YukiMemberHook.Unhook.wrapper(XposedBridge.hookMethod(member, callback.compat())).let {
} YukiHookedMembers.hookedMembers.add(it)
else Pair(null, false) Pair(it, false)
}
else -> Pair(null, false)
}
} }
/** /**
@@ -125,9 +119,9 @@ internal object YukiHookHelper {
* @param args 参数实例 * @param args 参数实例
* @return [Any] or null * @return [Any] or null
*/ */
internal fun invokeOriginalMember(member: Member, instance: Any?, vararg args: Any?) = internal fun invokeOriginalMember(member: Member?, instance: Any?, vararg args: Any?) =
if (YukiHookBridge.hasXposedBridge && YukiHookedMembers.hookedMembers.any { it.member.toString() == member.toString() }) if (YukiHookBridge.hasXposedBridge && YukiHookedMembers.hookedMembers.any { it.member.toString() == member.toString() })
XposedBridge.invokeOriginalMethod(member, instance, args) member?.let { XposedBridge.invokeOriginalMethod(it, instance, args) }
else null else null
/** /**