Moved YukiHookBridge some apps and host function into AppParasitics

This commit is contained in:
2022-08-14 23:57:40 +08:00
parent 8683045790
commit cf614eee15
5 changed files with 320 additions and 269 deletions

View File

@@ -39,8 +39,8 @@ import android.widget.ImageView
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
import java.io.BufferedReader
@@ -124,7 +124,7 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources()
*
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
*/
fun Resources.injectModuleAppResources() = YukiHookBridge.injectModuleAppResources(hostResources = this)
fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this)
/**
* 仅判断模块是否在太极、无极中激活

View File

@@ -32,6 +32,7 @@ package com.highcapable.yukihookapi.hook.log
import android.util.Log
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import de.robv.android.xposed.XposedBridge
/**
@@ -91,7 +92,7 @@ private fun baseLogger(format: String, type: LoggerType, tag: String, msg: Strin
/** 打印到 [XposedBridge.log] */
fun loggerInXposed() = runCatching {
YukiHookBridge.hostProcessName.also {
val appUserId = YukiHookBridge.findUserId(it)
val appUserId = AppParasitics.findUserId(it)
XposedBridge.log("[$tag][$format]${if (isShowProcessName) (if (appUserId != 0) "[$it][$appUserId]" else "[$it]") else ""}--> $msg")
e?.also { e -> XposedBridge.log(e) }
}

View File

@@ -48,11 +48,11 @@ import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
import com.highcapable.yukihookapi.hook.utils.value
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.channel.YukiHookDataChannel
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
/**
@@ -82,7 +82,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
* @return [Int]
*/
val appUserId get() = YukiHookBridge.findUserId(packageName)
val appUserId get() = AppParasitics.findUserId(packageName)
/**
* 获取当前 Hook APP 的 [Application] 实例
@@ -91,7 +91,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @return [Application]
* @throws IllegalStateException 如果 [Application] 是空的
*/
val appContext get() = YukiHookBridge.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
val appContext get() = AppParasitics.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
/**
* 获取当前 Hook APP 的 Resources
@@ -107,7 +107,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @return [Context] ContextImpl 实例对象
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
*/
val systemContext get() = YukiHookBridge.systemContext
val systemContext get() = AppParasitics.systemContext
/**
* 获取当前 Hook APP 的进程名称
@@ -143,7 +143,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* - ❗作为 Hook API 装载时无法使用 - 会获取到空字符串
* @return [String]
*/
val moduleAppFilePath get() = YukiHookBridge.moduleAppFilePath
val moduleAppFilePath get() = AppParasitics.moduleAppFilePath
/**
* 获取当前 Xposed 模块自身 [Resources]
@@ -153,8 +153,8 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @throws IllegalStateException 如果当前 Hook Framework 不支持此功能
*/
val moduleAppResources
get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) YukiHookBridge.moduleAppResources
else YukiHookBridge.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources")
get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) AppParasitics.moduleAppResources
else AppParasitics.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources")
/**
* 获得当前使用的存取数据对象缓存实例
@@ -202,7 +202,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
fun resources() = HookResources(wrapper?.appResources)
/** 刷新当前 Xposed 模块自身 [Resources] */
fun refreshModuleAppResources() = YukiHookBridge.refreshModuleAppResources()
fun refreshModuleAppResources() = AppParasitics.refreshModuleAppResources()
/**
* 监听当前 Hook APP 生命周期装载事件
@@ -244,13 +244,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* 装载并 Hook 系统框架
* @param initiate 方法体
*/
inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, initiate)
inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, initiate)
/**
* 装载并 Hook 系统框架
* @param hooker Hook 子类
*/
fun loadSystem(hooker: YukiBaseHooker) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, hooker)
fun loadSystem(hooker: YukiBaseHooker) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, hooker)
/**
* 装载 APP Zygote 事件
@@ -356,7 +356,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* - ❗这是一个实验性功能 - 一般情况下不会用到此方法 - 不保证不会发生错误
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
*/
fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = YukiHookBridge.hookClassLoader(loader = this, result)
fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = AppParasitics.hookClassLoader(loader = this, result)
/**
* Hook 方法、构造方法
@@ -439,7 +439,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param result 回调 - ([Context] baseContext,[Boolean] 是否已执行 super)
*/
fun attachBaseContext(result: (baseContext: Context, hasCalledSuper: Boolean) -> Unit) {
YukiHookBridge.AppLifecycleCallback.attachBaseContextCallback = result
AppParasitics.AppLifecycleCallback.attachBaseContextCallback = result
}
/**
@@ -447,7 +447,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param initiate 方法体
*/
fun onCreate(initiate: Application.() -> Unit) {
YukiHookBridge.AppLifecycleCallback.onCreateCallback = initiate
AppParasitics.AppLifecycleCallback.onCreateCallback = initiate
}
/**
@@ -455,7 +455,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param initiate 方法体
*/
fun onTerminate(initiate: Application.() -> Unit) {
YukiHookBridge.AppLifecycleCallback.onTerminateCallback = initiate
AppParasitics.AppLifecycleCallback.onTerminateCallback = initiate
}
/**
@@ -463,7 +463,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param initiate 方法体
*/
fun onLowMemory(initiate: Application.() -> Unit) {
YukiHookBridge.AppLifecycleCallback.onLowMemoryCallback = initiate
AppParasitics.AppLifecycleCallback.onLowMemoryCallback = initiate
}
/**
@@ -471,7 +471,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param result 回调 - ([Application] 当前实例,[Int] 类型)
*/
fun onTrimMemory(result: (self: Application, level: Int) -> Unit) {
YukiHookBridge.AppLifecycleCallback.onTrimMemoryCallback = result
AppParasitics.AppLifecycleCallback.onTrimMemoryCallback = result
}
/**
@@ -479,7 +479,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param result 回调 - ([Application] 当前实例,[Configuration] 配置实例)
*/
fun onConfigurationChanged(result: (self: Application, config: Configuration) -> Unit) {
YukiHookBridge.AppLifecycleCallback.onConfigurationChangedCallback = result
AppParasitics.AppLifecycleCallback.onConfigurationChangedCallback = result
}
/**
@@ -488,13 +488,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
* @param result 回调 - ([Context] 当前上下文,[Intent] 当前 Intent)
*/
fun registerReceiver(vararg action: String, result: (context: Context, intent: Intent) -> Unit) {
if (action.isNotEmpty()) YukiHookBridge.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result)
if (action.isNotEmpty()) AppParasitics.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result)
}
/** 设置创建生命周期监听回调 */
@PublishedApi
internal fun build() {
YukiHookBridge.AppLifecycleCallback.isCallbackSetUp = true
AppParasitics.AppLifecycleCallback.isCallbackSetUp = true
}
}

View File

@@ -29,38 +29,24 @@
package com.highcapable.yukihookapi.hook.xposed.bridge
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.YukiGenerateApi
import com.highcapable.yukihookapi.hook.factory.*
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
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.method
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.type.android.*
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader
import com.highcapable.yukihookapi.hook.type.java.StringType
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 com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import dalvik.system.PathClassLoader
import de.robv.android.xposed.IXposedHookInitPackageResources
import de.robv.android.xposed.IXposedHookLoadPackage
@@ -79,16 +65,9 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage
@YukiGenerateApi
object YukiHookBridge {
/** Android 系统框架名称 */
@PublishedApi
internal const val SYSTEM_FRAMEWORK_NAME = "android"
/** Xposed 是否装载完成 */
private var isXposedInitialized = false
/** [YukiHookDataChannel] 是否已经注册 */
private var isDataChannelRegister = false
/** 当前 Hook 进程是否正处于 [IXposedHookZygoteInit.initZygote] */
private var isInitializingZygote = false
@@ -98,49 +77,12 @@ object YukiHookBridge {
/** 当前 [PackageParamWrapper] 实例数组 */
private val packageParamWrappers = HashMap<String, PackageParamWrapper>()
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
private val injectedHostResourcesHashCodes = HashSet<Int>()
/** 当前 [PackageParam] 方法体回调 */
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
/** 当前 Hook Framework 是否支持 Resources Hook */
internal var isSupportResourcesHook = false
/**
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
*
* 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效
*/
internal var hostApplication: Application? = null
/** 当前 Xposed 模块自身 APK 路径 */
internal var moduleAppFilePath = ""
/** 当前 Xposed 模块自身 [Resources] */
internal var moduleAppResources: YukiModuleResources? = null
/**
* 当前环境中使用的 [ClassLoader]
*
* 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader]
* @return [ClassLoader]
* @throws IllegalStateException 如果 [ClassLoader] 为空
*/
internal val baseClassLoader get() = classOf<YukiHookAPI>().classLoader ?: error("Operating system not supported")
/**
* 获取当前 Xposed 模块自身动态 [Resources]
* @return [YukiModuleResources] or null
*/
internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull()
/**
* 自动生成的 Xposed 模块构建版本号
* @return [String]
*/
internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion()
/**
* 当前宿主正在进行的 Hook 进程标识名称
* @return [String]
@@ -148,14 +90,18 @@ object YukiHookBridge {
internal val hostProcessName get() = if (isInitializingZygote) "android-zygote" else YukiHookAppHelper.currentPackageName() ?: "unknown"
/**
* 获取当前系统框架的 [Context]
* @return [Context] ContextImpl 实例对象
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
* 自动生成的 Xposed 模块构建版本号
* @return [String]
*/
internal val systemContext
get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let {
ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
} ?: error("Failed to got SystemContext")
internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion()
/**
* 预设的 Xposed 模块包名
*
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
*/
@YukiGenerateApi
var modulePackageName = ""
/**
* 模块是否装载了 Xposed 回调方法
@@ -167,14 +113,6 @@ object YukiHookBridge {
val isXposedCallbackSetUp
get() = isXposedInitialized.not() && packageParamCallback != null
/**
* 预设的 Xposed 模块包名
*
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
*/
@YukiGenerateApi
var modulePackageName = ""
/**
* 获取当前 Hook 框架的名称
*
@@ -201,19 +139,6 @@ object YukiHookBridge {
*/
internal val hasXposedBridge get() = executorVersion >= 0
/**
* 获取指定 [packageName] 的用户 ID
*
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
* @param packageName 当前包名
* @return [Int]
*/
internal fun findUserId(packageName: String) =
UserHandleClass.method {
name = "getUserId"
param(IntType)
}.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid)
/**
* 自动忽略 MIUI 系统可能出现的日志收集注入实例
* @param packageName 当前包名
@@ -259,12 +184,12 @@ object YukiHookBridge {
if (type == HookEntryType.ZYGOTE || appClassLoader != null)
PackageParamWrapper(
type = type,
packageName = packageName ?: SYSTEM_FRAMEWORK_NAME,
processName = processName ?: SYSTEM_FRAMEWORK_NAME,
packageName = packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME,
processName = processName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME,
appClassLoader = appClassLoader ?: XposedBridge.BOOTCLASSLOADER,
appInfo = appInfo,
appResources = appResources
).also { packageParamWrappers[packageName ?: SYSTEM_FRAMEWORK_NAME] = it }
).also { packageParamWrappers[packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME] = it }
else null
else packageParamWrappers[packageName]?.also {
it.type = type
@@ -276,125 +201,6 @@ object YukiHookBridge {
}
}
/**
* 注入当前 Hook APP (宿主) 全局生命周期
* @param packageName 包名
*/
private fun registerToAppLifecycle(packageName: String) {
/** Hook [Application] 装载方法 */
runCatching {
if (AppLifecycleCallback.isCallbackSetUp) {
YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() {
override fun beforeHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) }
}.onFailure { wrapper.throwable = it }
}
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
val self = wrapper.instance as? Application? ?: return
val type = wrapper.args?.get(0) as? Int? ?: return
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
val self = wrapper.instance as? Application? ?: return
val config = wrapper.args?.get(0) as? Configuration? ?: return
AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
}.onFailure { wrapper.throwable = it }
}
})
}
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Application?)?.also {
hostApplication = it
AppLifecycleCallback.onCreateCallback?.invoke(it)
AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) ->
if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) return
if (e.first.any { e -> e == intent.action }) e.second(context, intent)
}
}, IntentFilter().apply { e.first.forEach { e -> addAction(e) } })
}
if (isDataChannelRegister) return
isDataChannelRegister = true
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
}
}.onFailure { wrapper.throwable = it }
}
})
}
}
/**
* 向 Hook APP (宿主) 注入当前 Xposed 模块的资源
* @param hostResources 需要注入的宿主 [Resources]
*/
internal fun injectModuleAppResources(hostResources: Resources) {
if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return
if (hasXposedBridge) runCatching {
hostResources.assets.current {
method {
name = "addAssetPath"
param(StringType)
}.call(moduleAppFilePath)
}
injectedHostResourcesHashCodes.add(hostResources.hashCode())
}.onFailure {
yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it)
} else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
}
/** 刷新当前 Xposed 模块自身 [Resources] */
internal fun refreshModuleAppResources() {
dynamicModuleAppResources?.let { moduleAppResources = it }
}
/**
* 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法
* @param loader 当前 [ClassLoader]
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
*/
internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) {
runCatching {
YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
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") }
}
/**
* Hook 模块自身激活状态和 Resources Hook 支持状态
*
@@ -441,8 +247,8 @@ object YukiHookBridge {
*/
@YukiGenerateApi
fun callXposedZygoteLoaded(sparam: IXposedHookZygoteInit.StartupParam) {
moduleAppFilePath = sparam.modulePath
refreshModuleAppResources()
AppParasitics.moduleAppFilePath = sparam.modulePath
AppParasitics.refreshModuleAppResources()
}
/**
@@ -468,7 +274,7 @@ object YukiHookBridge {
resparam: XC_InitPackageResources.InitPackageResourcesParam? = null
) {
if (isMiuiCatcherPatch(packageName = lpparam?.packageName ?: resparam?.packageName).not()) when {
isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, SYSTEM_FRAMEWORK_NAME, SYSTEM_FRAMEWORK_NAME)
isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, AppParasitics.SYSTEM_FRAMEWORK_NAME, AppParasitics.SYSTEM_FRAMEWORK_NAME)
lpparam != null ->
if (isPackageLoaded(lpparam.packageName, HookEntryType.PACKAGE).not())
assignWrapper(HookEntryType.PACKAGE, lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo)
@@ -480,38 +286,8 @@ object YukiHookBridge {
else -> null
}?.also {
YukiHookAPI.onXposedLoaded(it)
if (it.type == HookEntryType.PACKAGE) registerToAppLifecycle(it.packageName)
if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName)
if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true
}
}
/**
* 当前 Hook APP (宿主) 的生命周期回调处理类
*/
internal object AppLifecycleCallback {
/** 是否已设置回调 */
internal var isCallbackSetUp = false
/** [Application.attachBaseContext] 回调 */
internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null
/** [Application.onCreate] 回调 */
internal var onCreateCallback: (Application.() -> Unit)? = null
/** [Application.onTerminate] 回调 */
internal var onTerminateCallback: (Application.() -> Unit)? = null
/** [Application.onLowMemory] 回调 */
internal var onLowMemoryCallback: (Application.() -> Unit)? = null
/** [Application.onTrimMemory] 回调 */
internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null
/** [Application.onConfigurationChanged] 回调 */
internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null
/** 系统广播监听回调 */
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>()
}
}

View File

@@ -0,0 +1,274 @@
/*
* 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/8/14.
*/
package com.highcapable.yukihookapi.hook.xposed.parasitic
import android.app.Activity
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
import com.highcapable.yukihookapi.hook.type.android.*
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader
import com.highcapable.yukihookapi.hook.type.java.StringType
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
/**
* 这是一个管理 APP 寄生功能的控制类
*
* 通过这些功能即可轻松实现对 (Xposed) 宿主环境的 [Resources] 注入以及 [Activity] 代理
*/
@PublishedApi
internal object AppParasitics {
/** Android 系统框架名称 */
@PublishedApi
internal const val SYSTEM_FRAMEWORK_NAME = "android"
/** [YukiHookDataChannel] 是否已经注册 */
private var isDataChannelRegister = false
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
private val injectedHostResourcesHashCodes = HashSet<Int>()
/**
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
*
* 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效
*/
internal var hostApplication: Application? = null
/** 当前 Xposed 模块自身 APK 路径 */
internal var moduleAppFilePath = ""
/** 当前 Xposed 模块自身 [Resources] */
internal var moduleAppResources: YukiModuleResources? = null
/**
* 当前环境中使用的 [ClassLoader]
*
* 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader]
* @return [ClassLoader]
* @throws IllegalStateException 如果 [ClassLoader] 为空
*/
internal val baseClassLoader get() = classOf<YukiHookAPI>().classLoader ?: error("Operating system not supported")
/**
* 获取当前 Xposed 模块自身动态 [Resources]
* @return [YukiModuleResources] or null
*/
internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull()
/**
* 获取当前系统框架的 [Context]
* @return [Context] ContextImpl 实例对象
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
*/
internal val systemContext
get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let {
ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
} ?: error("Failed to got SystemContext")
/**
* 获取指定 [packageName] 的用户 ID
*
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
* @param packageName 当前包名
* @return [Int]
*/
internal fun findUserId(packageName: String) =
UserHandleClass.method {
name = "getUserId"
param(IntType)
}.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid)
/**
* 注入当前 Hook APP (宿主) 全局生命周期
* @param packageName 包名
*/
internal fun registerToAppLifecycle(packageName: String) {
/** Hook [Application] 装载方法 */
runCatching {
if (AppLifecycleCallback.isCallbackSetUp) {
YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() {
override fun beforeHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) }
}.onFailure { wrapper.throwable = it }
}
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
val self = wrapper.instance as? Application? ?: return
val type = wrapper.args?.get(0) as? Int? ?: return
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
}.onFailure { wrapper.throwable = it }
}
})
YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
val self = wrapper.instance as? Application? ?: return
val config = wrapper.args?.get(0) as? Configuration? ?: return
AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
}.onFailure { wrapper.throwable = it }
}
})
}
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
runCatching {
(wrapper.args?.get(0) as? Application?)?.also {
hostApplication = it
AppLifecycleCallback.onCreateCallback?.invoke(it)
AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) ->
if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) return
if (e.first.any { e -> e == intent.action }) e.second(context, intent)
}
}, IntentFilter().apply { e.first.forEach { e -> addAction(e) } })
}
if (isDataChannelRegister) return
isDataChannelRegister = true
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
}
}.onFailure { wrapper.throwable = it }
}
})
}
}
/**
* 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法
* @param loader 当前 [ClassLoader]
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
*/
internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) {
runCatching {
YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() {
override fun afterHookedMember(wrapper: HookParamWrapper) {
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") }
}
/**
* 向 Hook APP (宿主) 注入当前 Xposed 模块的资源
* @param hostResources 需要注入的宿主 [Resources]
*/
internal fun injectModuleAppResources(hostResources: Resources) {
if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return
if (YukiHookBridge.hasXposedBridge) runCatching {
hostResources.assets.current {
method {
name = "addAssetPath"
param(StringType)
}.call(moduleAppFilePath)
}
injectedHostResourcesHashCodes.add(hostResources.hashCode())
}.onFailure {
yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it)
} else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
}
/** 刷新当前 Xposed 模块自身 [Resources] */
internal fun refreshModuleAppResources() {
dynamicModuleAppResources?.let { moduleAppResources = it }
}
/**
* 当前 Hook APP (宿主) 的生命周期回调处理类
*/
internal object AppLifecycleCallback {
/** 是否已设置回调 */
internal var isCallbackSetUp = false
/** [Application.attachBaseContext] 回调 */
internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null
/** [Application.onCreate] 回调 */
internal var onCreateCallback: (Application.() -> Unit)? = null
/** [Application.onTerminate] 回调 */
internal var onTerminateCallback: (Application.() -> Unit)? = null
/** [Application.onLowMemory] 回调 */
internal var onLowMemoryCallback: (Application.() -> Unit)? = null
/** [Application.onTrimMemory] 回调 */
internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null
/** [Application.onConfigurationChanged] 回调 */
internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null
/** 系统广播监听回调 */
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>()
}
}