diff --git a/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt b/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt index 992ff8db..4bec8b72 100644 --- a/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt +++ b/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt @@ -110,6 +110,7 @@ class HookEntry : IYukiHookXposedInit { } // 得到需要 Hook 的 Resources // 需要 Hook Framework 支持 Resources Hook(资源钩子) 才能成功 + // 在 Zygote 中的 Resources Hook 只需要 Hook Framework 支持 - 无需启用此功能 resources().hook { // 注入要 Hook 的 Resources injectResource { @@ -260,7 +261,7 @@ class HookEntry : IYukiHookXposedInit { } } // 得到需要 Hook 的 Resources - // 需要 Hook Framework 支持 Resources Hook(资源钩子) 才能成功 + // 需要 Hook Framework 支持 Resources Hook(资源钩子) 且启用此功能才能成功 resources().hook { // 注入要 Hook 的 Resources injectResource { diff --git a/docs/api/public/InjectYukiHookWithXposed.md b/docs/api/public/InjectYukiHookWithXposed.md index f3f5cc33..db0c73e4 100644 --- a/docs/api/public/InjectYukiHookWithXposed.md +++ b/docs/api/public/InjectYukiHookWithXposed.md @@ -4,7 +4,8 @@ annotation class InjectYukiHookWithXposed( val sourcePath: String, val modulePackageName: String, - val entryClassName: String + val entryClassName: String, + val isUsingResourcesHook: Boolean ) ``` @@ -16,6 +17,10 @@ annotation class InjectYukiHookWithXposed( 新增 `entryClassName` 参数 +`v1.0.92` `修改` + +新增 `isUsingResourcesHook` 参数 + **功能描述** > 标识 `YukiHookAPI` 注入 Xposed 入口的类注解。 diff --git a/docs/api/public/PackageParam.md b/docs/api/public/PackageParam.md index 3ed84dc2..99377efb 100644 --- a/docs/api/public/PackageParam.md +++ b/docs/api/public/PackageParam.md @@ -623,6 +623,8 @@ inline fun HookResources.hook(initiate: YukiResourcesHookCreater.() -> Unit) > Hook APP 的 Resources。 +!> 请注意你需要确保当前 Hook Framework 支持且 `InjectYukiHookWithXposed.isUsingResourcesHook` 已启用。 + **功能示例** Resources Hook 为固定用法,获取 `resources` 对象,然后调用 `hook` 方法开始 Hook。 diff --git a/docs/api/public/YukiHookAPI.md b/docs/api/public/YukiHookAPI.md index b78cfb72..77ac5c21 100644 --- a/docs/api/public/YukiHookAPI.md +++ b/docs/api/public/YukiHookAPI.md @@ -184,6 +184,8 @@ val isSupportResourcesHook: Boolean !> 在 Xposed 环境中可能会延迟等待事件回调后才会返回 true。 +!> 请注意你需要确保 `InjectYukiHookWithXposed.isUsingResourcesHook` 已启用,否则始终返回 false。 + ### Configs [object] ```kotlin diff --git a/docs/config/xposed-using.md b/docs/config/xposed-using.md index 6d9de824..a4d40dc5 100644 --- a/docs/config/xposed-using.md +++ b/docs/config/xposed-using.md @@ -18,7 +18,8 @@ annotation class InjectYukiHookWithXposed( val sourcePath: String, val modulePackageName: String, - val entryClassName: String + val entryClassName: String, + val isUsingResourcesHook: Boolean ) ``` @@ -96,7 +97,7 @@ Xposed 入口类处理如下。 > 示例如下 ```kotlin -class HookEntry_YukiHookXposedInit: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources +class HookEntry_YukiHookXposedInit: IXposedHookZygoteInit, IXposedHookLoadPackage, ... ``` 编译后的类名结构如下。 @@ -123,7 +124,7 @@ Xposed 入口类处理如下。 > 示例如下 ```kotlin -class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources +class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, ... ``` 编译后的类名结构如下。 @@ -138,6 +139,56 @@ class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHoo !> 你定义的 `entryClassName` 不可与 `xposed_init` 中的类名相同,否则自动处理程序会在编译时抛出异常。 +#### isUsingResourcesHook 参数 + +`isUsingResourcesHook` 决定了自动处理程序是否生成针对 Resources Hook 的相关代码,此功能默认是启用的。 + +启用后生成的入口类将为如下所示。 + +> 示例如下 + +```kotlin +class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources { + + override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) { + // ... + } + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { + // ... + } + + override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) { + // ... + } +} +``` + +若你当前的项目并不需要用到 Reources Hook,可以设置 `isUsingResourcesHook = false` 来关闭自动生成。 + +> 示例如下 + +```kotlin +@InjectYukiHookWithXposed(isUsingResourcesHook = false) +``` + +关闭后生成的入口类将为如下所示。 + +> 示例如下 + +```kotlin +class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage { + + override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) { + // ... + } + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { + // ... + } +} +``` + ### IYukiHookXposedInit 接口 `IYukiHookXposedInit` 接口为你的 `HookEntryClass` 必须实现的接口,这是你的模块开始 Hook 的起点。 diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt index 001ae879..22e229db 100644 --- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt +++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt @@ -103,8 +103,14 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { * @param sourcePath 指定的 source 路径 * @param modulePackageName 模块包名 * @param xInitClassName xposed_init 入口类名 + * @param isUsingResourcesHook 是否启用 Resources Hook */ - fun fetchKSClassDeclaration(sourcePath: String, modulePackageName: String, xInitClassName: String) { + fun fetchKSClassDeclaration( + sourcePath: String, + modulePackageName: String, + xInitClassName: String, + isUsingResourcesHook: Boolean + ) { asSequence().filterIsInstance().forEach { if (isInjectOnce) when { it.superTypes.any { type -> type.element.toString() == "IYukiHookXposedInit" } -> { @@ -117,7 +123,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { entryClassName = it.simpleName.asString(), xInitClassName = xcName ) - injectClass(it.packageName.asString(), modulePackageName, it.simpleName.asString(), xcName) + injectClass(it.packageName.asString(), modulePackageName, it.simpleName.asString(), xcName, isUsingResourcesHook) } it.superTypes.any { type -> type.element.toString() == "YukiHookXposedInitProxy" } -> problem(msg = "\"YukiHookXposedInitProxy\" was deprecated, please replace to \"IYukiHookXposedInit\"") @@ -133,6 +139,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { var sourcePath = "" // 项目相对路径 var modulePackageName = "" // 模块包名 var entryClassName = "" // xposed_init 入口类名 + var isUsingResourcesHook = false // 是否启用 Resources Hook e.arguments.forEach { pease -> if (pease.name?.asString() == "sourcePath") sourcePath = pease.value.toString().trim() @@ -140,6 +147,8 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { modulePackageName = pease.value.toString().trim() if (pease.name?.asString() == "entryClassName") entryClassName = pease.value.toString().trim() + if (pease.name?.asString() == "isUsingResourcesHook") + isUsingResourcesHook = pease.value as Boolean } if ((modulePackageName.startsWith(".") || modulePackageName.endsWith(".") || @@ -151,7 +160,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { true.let { for (i in 0..9) if (entryClassName.startsWith(i.toString())) return@let true;false }) && entryClassName.isNotEmpty() ) problem(msg = "Invalid entryClassName \"$entryClassName\"") - else fetchKSClassDeclaration(sourcePath, modulePackageName, entryClassName) + else fetchKSClassDeclaration(sourcePath, modulePackageName, entryClassName, isUsingResourcesHook) } } } @@ -207,58 +216,64 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { * @param modulePackageName 模块包名 * @param entryClassName 入口类名 * @param xInitClassName xposed_init 入口类名 + * @param isUsingResourcesHook 是否启用 Resources Hook */ - private fun injectClass(packageName: String, modulePackageName: String, entryClassName: String, xInitClassName: String) = - environment(ignoredError = true) { - if (modulePackageName.isNotBlank()) - warn(msg = "You set the customize module package name to \"$modulePackageName\", please check for yourself if it is correct") - val fModulePackageName = modulePackageName.ifBlank { - if (packageName.contains(other = ".hook.") || packageName.endsWith(suffix = ".hook")) - packageName.split(".hook")[0] - else error("Cannot identify your Module App's package name, please manually configure the package name") - } - val mdAppInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.application.inject" - val ykBridgeInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.bridge.inject" - /** 插入 ModuleApplication_Injector 代码 */ - codeGenerator.createNewFile( - dependencies = Dependencies.ALL_FILES, - packageName = mdAppInjectPackageName, - fileName = "ModuleApplication_Injector" - ).apply { - write(CodeSourceFileTemplate.getModuleApplicationInjectorFileByteArray(mdAppInjectPackageName, packageName, entryClassName)) - flush() - close() - } - /** 插入 YukiHookBridge_Injector 代码 */ - codeGenerator.createNewFile( - dependencies = Dependencies.ALL_FILES, - packageName = ykBridgeInjectPackageName, - fileName = "YukiHookBridge_Injector" - ).apply { - write(CodeSourceFileTemplate.getYukiHookBridgeInjectorFileByteArray(ykBridgeInjectPackageName)) - flush() - close() - } - /** 插入 xposed_init 代码 */ - codeGenerator.createNewFile( - dependencies = Dependencies.ALL_FILES, - packageName = packageName, - fileName = xInitClassName - ).apply { - write(CodeSourceFileTemplate.getXposedInitFileByteArray(packageName, entryClassName, xInitClassName)) - flush() - close() - } - /** 插入 xposed_init_Impl 代码 */ - codeGenerator.createNewFile( - dependencies = Dependencies.ALL_FILES, - packageName = packageName, - fileName = "${entryClassName}_Impl" - ).apply { - write(CodeSourceFileTemplate.getXposedInitImplFileByteArray(packageName, fModulePackageName, entryClassName)) - flush() - close() - } + private fun injectClass( + packageName: String, + modulePackageName: String, + entryClassName: String, + xInitClassName: String, + isUsingResourcesHook: Boolean + ) = environment(ignoredError = true) { + if (modulePackageName.isNotBlank()) + warn(msg = "You set the customize module package name to \"$modulePackageName\", please check for yourself if it is correct") + val fModulePackageName = modulePackageName.ifBlank { + if (packageName.contains(other = ".hook.") || packageName.endsWith(suffix = ".hook")) + packageName.split(".hook")[0] + else error("Cannot identify your Module App's package name, please manually configure the package name") } + val mdAppInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.application.inject" + val ykBridgeInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.bridge.inject" + /** 插入 ModuleApplication_Injector 代码 */ + codeGenerator.createNewFile( + dependencies = Dependencies.ALL_FILES, + packageName = mdAppInjectPackageName, + fileName = "ModuleApplication_Injector" + ).apply { + write(CodeSourceFileTemplate.getModuleApplicationInjectorFileByteArray(mdAppInjectPackageName, packageName, entryClassName)) + flush() + close() + } + /** 插入 YukiHookBridge_Injector 代码 */ + codeGenerator.createNewFile( + dependencies = Dependencies.ALL_FILES, + packageName = ykBridgeInjectPackageName, + fileName = "YukiHookBridge_Injector" + ).apply { + write(CodeSourceFileTemplate.getYukiHookBridgeInjectorFileByteArray(ykBridgeInjectPackageName)) + flush() + close() + } + /** 插入 xposed_init 代码 */ + codeGenerator.createNewFile( + dependencies = Dependencies.ALL_FILES, + packageName = packageName, + fileName = xInitClassName + ).apply { + write(CodeSourceFileTemplate.getXposedInitFileByteArray(packageName, entryClassName, xInitClassName, isUsingResourcesHook)) + flush() + close() + } + /** 插入 xposed_init_Impl 代码 */ + codeGenerator.createNewFile( + dependencies = Dependencies.ALL_FILES, + packageName = packageName, + fileName = "${entryClassName}_Impl" + ).apply { + write(CodeSourceFileTemplate.getXposedInitImplFileByteArray(packageName, fModulePackageName, entryClassName)) + flush() + close() + } + } } } \ No newline at end of file diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt index 4f77c7ef..07be7c48 100644 --- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt +++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt @@ -101,9 +101,10 @@ object CodeSourceFileTemplate { * @param packageName 包名 * @param entryClassName 入口类名 * @param xInitClassName xposed_init 入口类名 + * @param isUsingResourcesHook 是否启用 Resources Hook * @return [ByteArray] */ - fun getXposedInitFileByteArray(packageName: String, entryClassName: String, xInitClassName: String) = + fun getXposedInitFileByteArray(packageName: String, entryClassName: String, xInitClassName: String, isUsingResourcesHook: Boolean) = ("@file:Suppress(\"ClassName\")\n" + "\n" + "package $packageName\n" + @@ -111,16 +112,17 @@ object CodeSourceFileTemplate { "import androidx.annotation.Keep\n" + "import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" + "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" + - "import de.robv.android.xposed.IXposedHookInitPackageResources\n" + + (if (isUsingResourcesHook) "import de.robv.android.xposed.IXposedHookInitPackageResources\n" else "") + "import de.robv.android.xposed.IXposedHookLoadPackage\n" + "import de.robv.android.xposed.IXposedHookZygoteInit\n" + - "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" + + (if (isUsingResourcesHook) "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" else "") + "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" + "\n" + getCommentContent(entryClassName, currrentClassTag = "Xposed Init") + "@Keep\n" + "@YukiGenerateApi\n" + - "class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {\n" + + "class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage" + + "${if (isUsingResourcesHook) ", IXposedHookInitPackageResources" else ""} {\n" + "\n" + " override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" + " ${entryClassName}_Impl.callInitZygote(sparam)\n" + @@ -131,11 +133,11 @@ object CodeSourceFileTemplate { " ${entryClassName}_Impl.callHandleLoadPackage(lpparam)\n" + " YukiXposedEvent.EventHandler.callHandleLoadPackage(lpparam)\n" + " }\n" + - "\n" + - " override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" + - " ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" + - " YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" + - " }\n" + + (if (isUsingResourcesHook) + ("\n override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" + + " ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" + + " YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" + + " }\n") else "") + "}").toByteArray() /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index d25125c0..a3864b7f 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -34,6 +34,7 @@ import android.content.Context import android.content.res.Resources import com.highcapable.yukihookapi.YukiHookAPI.configs import com.highcapable.yukihookapi.YukiHookAPI.encase +import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder import com.highcapable.yukihookapi.hook.core.finder.FieldFinder import com.highcapable.yukihookapi.hook.core.finder.MethodFinder @@ -143,6 +144,8 @@ object YukiHookAPI { * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] * * - ❗在 Xposed 环境中可能会延迟等待事件回调后才会返回 true + * + * - ❗请注意你需要确保 [InjectYukiHookWithXposed.isUsingResourcesHook] 已启用 - 否则始终返回 false * @return [Boolean] 是否支持 */ val isSupportResourcesHook diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/annotation/xposed/InjectYukiHookWithXposed.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/annotation/xposed/InjectYukiHookWithXposed.kt index 86701af3..ddff268f 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/annotation/xposed/InjectYukiHookWithXposed.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/annotation/xposed/InjectYukiHookWithXposed.kt @@ -29,6 +29,7 @@ package com.highcapable.yukihookapi.annotation.xposed import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit +import de.robv.android.xposed.IXposedHookInitPackageResources /** * 标识 [YukiHookAPI] 注入 Xposed 入口的类注解 @@ -61,10 +62,12 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit * @param sourcePath 你的项目 source 相对路径 - 默认为 ..src/main.. * @param modulePackageName 模块包名 - 使用标准路径可不填会自动生成 * @param entryClassName 定义 [YukiHookAPI] 自动生成 Xposed 模块入口类的名称 - 不填默认使用 HookEntryClass_YukiHookXposedInit 进行生成 + * @param isUsingResourcesHook 是否启用 Resources Hook (资源钩子) - 启用后将自动注入 [IXposedHookInitPackageResources] - 默认是 */ @Target(AnnotationTarget.CLASS) annotation class InjectYukiHookWithXposed( val sourcePath: String = "src/main", val modulePackageName: String = "", - val entryClassName: String = "" + val entryClassName: String = "", + val isUsingResourcesHook: Boolean = true ) \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt index 5bc67c84..f4f087df 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt @@ -36,6 +36,7 @@ import android.content.pm.ApplicationInfo import android.content.res.Configuration import android.content.res.Resources import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.hook.bean.HookClass import com.highcapable.yukihookapi.hook.bean.HookResources import com.highcapable.yukihookapi.hook.bean.VariousClass @@ -375,6 +376,8 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: /** * Hook APP 的 Resources + * + * - ❗请注意你需要确保当前 Hook Framework 支持且 [InjectYukiHookWithXposed.isUsingResourcesHook] 已启用 * @param initiate 方法体 */ inline fun HookResources.hook(initiate: YukiResourcesHookCreater.() -> Unit) =