diff --git a/README.md b/README.md index b0c15c66..2ee8a73f 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,6 @@ ⛱️ An efficient Kotlin version of the Xposed Hook API.
-## Plan is in progress - -> `IXposedHookInitPackageResources` and `IXposedHookZygoteInit` are being adapted recently,let's stay tuned - -**计划进行中** - -> 近期正在适配 `IXposedHookInitPackageResources`、`IXposedHookZygoteInit` 敬请期待 - ## What's this - This is an efficient Xposed Hook API rebuilt in Kotlin @@ -31,6 +23,18 @@ - 名称取自 [《ももくり》女主 栗原 雪(Yuki)](https://www.bilibili.com/bangumi/play/ss5016) - 前身为 [开发学习项目](https://github.com/fankes/TMore) 中使用的 Innocent Xposed API,现在重新命名并开源 +## Supports + +- Standard Hook +- Zygote Hook +- Resources Hook + +**支持的功能** + +- 标准 Hook +- Zygote Hook +- 资源钩子(Resources Hook) + ## Get Started - [Click here](https://fankes.github.io/YukiHookAPI) go to the documentation page for more detailed tutorials and content. diff --git a/docs/about/future.md b/docs/about/future.md index a3b8d3b0..7b511902 100644 --- a/docs/about/future.md +++ b/docs/about/future.md @@ -26,15 +26,7 @@ 目前 API 只支持通过自动处理程序绑定到 `xposed_init`,若您不喜欢自动处理程序,一定要自己实现模块装载入口,未来会按照需求人数推出仅有 API 功能的 Lite 版本。 -### 支持资源 Hook 和注入系统框架 - -**计划状态:近期** - -目前的 API 仅支持 APP 内的功能 Hook,并不支持 `Resource` 的替换以及 Hook 系统框架。 - -API 还未实现对 `handleInitPackageResources` 和 `initZygote` 的调用。 - -在未来会根据使用和需求人数加上这个功能,如有需求你也可以向我们提交 Pull Request 来贡献你的代码。 +API 已经提供了 Xposed 原生 API 监听接口,你可以 [在这里](config/xposed-using?id=原生-xposed-api-事件) 找到或查看 Demo 的实现方法。 ### 支持更多 Hook Framework diff --git a/docs/api/document.md b/docs/api/document.md index 481232f2..0d277858 100644 --- a/docs/api/document.md +++ b/docs/api/document.md @@ -22,6 +22,14 @@ [filename](public/ModuleApplication.md ':include') +[filename](public/YukiModuleResources.md ':include') + +[filename](public/YukiResources.md ':include') + +[filename](public/YukiResForwarder.md ':include') + +[filename](public/YukiXposedEvent.md ':include') + [filename](public/ComponentTypeFactory.md ':include') [filename](public/GraphicsTypeFactory.md ':include') @@ -38,7 +46,9 @@ [filename](public/YukiBaseHooker.md ':include') -[filename](public/YukiHookCreater.md ':include') +[filename](public/YukiMemberHookCreater.md ':include') + +[filename](public/YukiResourcesHookCreater.md ':include') [filename](public/MethodFinder.md ':include') @@ -54,4 +64,6 @@ [filename](public/VariousClass.md ':include') -[filename](public/CurrentClass.md ':include') \ No newline at end of file +[filename](public/CurrentClass.md ':include') + +[filename](public/HookResources.md ':include') \ No newline at end of file diff --git a/docs/api/public/ConstructorFinder.md b/docs/api/public/ConstructorFinder.md index c1135f40..a2a6837f 100644 --- a/docs/api/public/ConstructorFinder.md +++ b/docs/api/public/ConstructorFinder.md @@ -1,7 +1,7 @@ ## ConstructorFinder [class] ```kotlin -class ConstructorFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder() +class ConstructorFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder() ``` **变更记录** @@ -112,6 +112,22 @@ fun paramCount(num: Int): IndexTypeCondition !> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。 +### superClass [method] + +```kotlin +fun superClass(isOnlySuperClass: Boolean) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置在 `classSet` 的所有父类中查找当前 `Constructor`。 + +!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。 + ### RemedyPlan [class] ```kotlin diff --git a/docs/api/public/CurrentClass.md b/docs/api/public/CurrentClass.md index d9b4b398..68ded49c 100644 --- a/docs/api/public/CurrentClass.md +++ b/docs/api/public/CurrentClass.md @@ -1,7 +1,7 @@ ## CurrentClass [class] ```kotlin -class CurrentClass(private val instance: Class<*>, private val self: Any) +class CurrentClass(internal val instance: Class<*>, internal val self: Any) ``` **变更记录** @@ -12,10 +12,24 @@ class CurrentClass(private val instance: Class<*>, private val self: Any) > 当前实例的类操作对象。 +### superClass [method] + +```kotlin +fun superClass(): SuperClass +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 调用父类实例。 + ### field [method] ```kotlin -fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance +inline fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance ``` **变更记录** @@ -29,7 +43,7 @@ fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance ### method [method] ```kotlin -fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance +inline fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance ``` **变更记录** @@ -38,4 +52,46 @@ fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance **功能描述** -> 调用当前实例中的方法。 \ No newline at end of file +> 调用当前实例中的方法。 + +### SuperClass [class] + +```kotlin +inner class SuperClass +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 当前类的父类实例的类操作对象。 + +#### field [method] + +```kotlin +inline fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 调用父类实例中的变量。 + +#### method [method] + +```kotlin +inline fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 调用父类实例中的方法。 \ No newline at end of file diff --git a/docs/api/public/FieldFinder.md b/docs/api/public/FieldFinder.md index 3e276e6a..ce105c9f 100644 --- a/docs/api/public/FieldFinder.md +++ b/docs/api/public/FieldFinder.md @@ -1,7 +1,7 @@ ## FieldFinder [class] ```kotlin -class FieldFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>?) : BaseFinder() +class FieldFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>?) : BaseFinder() ``` **变更记录** @@ -132,6 +132,22 @@ fun type(value: Any): IndexTypeCondition !> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。 +### superClass [method] + +```kotlin +fun superClass(isOnlySuperClass: Boolean) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置在 `classSet` 的所有父类中查找当前 `Field`。 + +!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。 + ### Result [class] ```kotlin diff --git a/docs/api/public/HookParam.md b/docs/api/public/HookParam.md index d11c8785..4d0e842b 100644 --- a/docs/api/public/HookParam.md +++ b/docs/api/public/HookParam.md @@ -1,7 +1,7 @@ ## HookParam [class] ```kotlin -class HookParam(private​ ​val​ ​createrInstance​:​ ​YukiHookCreater​, private val wrapper: HookParamWrapper) +class HookParam(private val createrInstance: YukiMemberHookCreater, private var wrapper: HookParamWrapper?) ``` **变更记录** diff --git a/docs/api/public/HookResources.md b/docs/api/public/HookResources.md new file mode 100644 index 00000000..2fcfb700 --- /dev/null +++ b/docs/api/public/HookResources.md @@ -0,0 +1,13 @@ +## HookResources [class] + +```kotlin +class HookResources(var instance: YukiResources?) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 创建一个当前 Hook 的 `YukiResources` 接管类。 \ No newline at end of file diff --git a/docs/api/public/IYukiHookXposedInit.md b/docs/api/public/IYukiHookXposedInit.md index 86d820ff..099a054d 100644 --- a/docs/api/public/IYukiHookXposedInit.md +++ b/docs/api/public/IYukiHookXposedInit.md @@ -48,4 +48,42 @@ fun onHook() **功能描述** -> Xposed API 的模块装载调用入口方法。 \ No newline at end of file +> Xposed API 的模块装载调用入口方法。 + +### onXposedEvent [method] + +```kotlin +fun onXposedEvent() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 监听 Xposed 原生装载事件。 + +若你的 Hook 事件中存在需要兼容的原生 Xposed 功能,可在这里实现。 + +请在这里使用 `YukiXposedEvent` 创建回调事件监听。 + +可监听的事件如下: + +`YukiXposedEvent.onInitZygote` + +`YukiXposedEvent.onHandleLoadPackage` + +`YukiXposedEvent.onHandleInitPackageResources` + +!> 此接口仅供监听和实现原生 Xposed API 的功能,请不要在这里操作 `YukiHookAPI`。 + +## ~~YukiHookXposedInitProxy [interface]~~ + +**变更记录** + +`v1.0` `添加` + +`v1.0.80` `作废` + +请转移到 `IYukiHookXposedInit` \ No newline at end of file diff --git a/docs/api/public/MethodFinder.md b/docs/api/public/MethodFinder.md index 5dee8715..38f254f8 100644 --- a/docs/api/public/MethodFinder.md +++ b/docs/api/public/MethodFinder.md @@ -1,7 +1,7 @@ ## MethodFinder [class] ```kotlin -class MethodFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder() +class MethodFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder() ``` **变更记录** @@ -196,6 +196,22 @@ fun returnType(value: Any): IndexTypeCondition !> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。 +### superClass [method] + +```kotlin +fun superClass(isOnlySuperClass: Boolean) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置在 `classSet` 的所有父类中查找当前 `Method`。 + +!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。 + ### RemedyPlan [class] ```kotlin diff --git a/docs/api/public/PackageParam.md b/docs/api/public/PackageParam.md index 501d0b83..6d3fad33 100644 --- a/docs/api/public/PackageParam.md +++ b/docs/api/public/PackageParam.md @@ -1,7 +1,7 @@ ## PackageParam [class] ```kotlin -open class PackageParam(private var wrapper: PackageParamWrapper?) +open class PackageParam(internal var wrapper: PackageParamWrapper?) ``` **变更记录** @@ -54,6 +54,24 @@ val appContext: Application > 获取当前 Hook APP 的 `Application`。 +!> 首次装载可能是空的,请延迟一段时间再获取。 + +### appResources [field] + +```kotlin +val appResources:Resources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获取当前 Hook APP 的 Resources。 + +!> 你只能在 `HookResources.hook` 方法体内或 `appContext` 装载完毕时进行调用。 + ### processName [field] ```kotlin @@ -112,6 +130,38 @@ val mainProcessName: String 其对应的就是 `packageName`。 +### moduleAppFilePath [field] + +```kotlin +val moduleAppFilePath: String +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获取当前 Xposed 模块自身 APK 文件路径。 + +!> 作为 Hook API 装载时无法使用,会获取到空字符串。 + +### moduleAppResources [field] + +```kotlin +val moduleAppResources: YukiModuleResources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获取当前 Xposed 模块自身 `Resources`。 + +!> 作为 Hook API 或不支持的 Hook Framework 装载时无法使用,会抛出异常。 + ### prefs [field] ```kotlin @@ -126,6 +176,8 @@ val prefs: YukiHookModulePrefs > 获得当前使用的存取数据对象缓存实例。 +!> 作为 Hook API 装载时无法使用,会抛出异常。 + ### prefs [method] ```kotlin @@ -146,6 +198,24 @@ fun prefs(name: String): YukiHookModulePrefs 你可以通过 `name` 来自定义 Sp 存储的名称。 +!> 作为 Hook API 装载时无法使用,会抛出异常。 + +### resources [method] + +```kotlin +fun resources(): HookResources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获得当前 Hook APP 的 `YukiResources` 对象。 + +请调用 `HookResources.hook` 方法开始 Hook。 + ### loadApp [method] ```kotlin @@ -170,6 +240,28 @@ fun loadApp(name: String, hooker: YukiBaseHooker) `name` 为 APP 的包名,后方的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。 +装载并 Hook 指定、全部包名的 APP,若要 Hook 系统框架,请使用 `loadZygote`。 + +### loadZygote [method] + +```kotlin +inline fun loadZygote(initiate: PackageParam.() -> Unit) +``` + +```kotlin +fun loadZygote(hooker: YukiBaseHooker) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 装载并 Hook 系统框架。 + +方法中的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。 + ### withProcess [method] ```kotlin @@ -331,19 +423,19 @@ val variousClass = VariousClass("com.example.demo.DemoClass1", "com.example.demo ### hook [method] ```kotlin -inline fun String.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result +inline fun String.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result ``` ```kotlin -inline fun Class<*>.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result +inline fun Class<*>.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result ``` ```kotlin -inline fun VariousClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result +inline fun VariousClass.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result ``` ```kotlin -inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result +inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result ``` **变更记录** @@ -360,7 +452,7 @@ inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreate `v1.0.3` `修改` -新增 `YukiHookCreater.Result` 返回值 +新增 `YukiMemberHookCreater.Result` 返回值 `v1.0.70` `修改` @@ -443,4 +535,34 @@ findClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").hook { YourClass.hook(isUseAppClassLoader = false) { // Your code here. } -``` \ No newline at end of file +``` + +### hook [method] + +```kotlin +inline fun HookResources.hook(initiate: YukiResourcesHookCreater.() -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增 + +**功能描述** + +> Hook APP 的 Resources。 + +**功能示例** + +Resources Hook 为固定用法,获取 `resources` 对象,然后调用 `hook` 方法开始 Hook。 + +> 示例如下 + +```kotlin +resources().hook { + // Your code here. +} +``` + +!> 这是固定用法,为了防止发生问题,你不可手动实现任何 `HookResources` 实例执行 `hook` 调用。 + +将 Resources 的 Hook 设置为这样是为了与 `findClass(...).hook` 做到统一,使得调用起来逻辑不会混乱。 \ No newline at end of file diff --git a/docs/api/public/ReflectionFactory.md b/docs/api/public/ReflectionFactory.md index 38abe3c4..62e1ffcb 100644 --- a/docs/api/public/ReflectionFactory.md +++ b/docs/api/public/ReflectionFactory.md @@ -8,6 +8,34 @@ > 这是自定义 `Member` 和 `Class` 相关功能的查找匹配以及 `invoke` 的封装类。 +### hookClass [field] + +```kotlin +val Class<*>.hookClass: HookClass +``` + +**变更记录** + +`v1.0` `添加` + +**功能描述** + +> 将 `Class` 转换为 `HookClass`。 + +### normalClass [field] + +```kotlin +val HookClass.normalClass: Class<*>? +``` + +**变更记录** + +`v1.0` `添加` + +**功能描述** + +> 将 `HookClass` 转换为 `Class`。 + ### hasClass [field] ```kotlin @@ -36,33 +64,19 @@ if("com.example.demo.DemoClass".hasClass) { } ``` -### hookClass [field] +### hasExtends [field] ```kotlin -val Class<*>.hookClass: HookClass +val Class<*>.hasExtends: Boolean ``` **变更记录** -`v1.0` `添加` +`v1.0.80` `新增` **功能描述** -> 将 `Class` 转换为 `HookClass`。 - -### normalClass [field] - -```kotlin -val HookClass.normalClass: Class<*>? -``` - -**变更记录** - -`v1.0` `添加` - -**功能描述** - -> 将 `HookClass` 转换为 `Class`。 +> 当前 `Class` 是否有继承关系,父类是 `Any` 将被认为没有继承关系。 ### classOf [method] diff --git a/docs/api/public/YukiHookFactory.md b/docs/api/public/YukiHookFactory.md index d60426b9..eeabe2c0 100644 --- a/docs/api/public/YukiHookFactory.md +++ b/docs/api/public/YukiHookFactory.md @@ -50,7 +50,21 @@ fun IYukiHookXposedInit.encase(vararg hooker: YukiBaseHooker) **功能描述** -> 在 `IYukiHookXposedInit` 中装载 `YukiHookAPI`。 +> 在 `IYukiHookXposedInit` 中调用 `YukiHookAPI`。 + +### resources [method] + +```kotlin +fun IYukiHookXposedInit.resources(initiate: ResourcesParam.() -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 在 `IYukiHookXposedInit` 中调用 `YukiHookAPI.resources`。 ### modulePrefs [field] @@ -94,6 +108,20 @@ val Context.processName: String > 获取当前进程名称。 +### isSupportResourcesHook [field] + +```kotlin +val Any?.isSupportResourcesHook: Boolean +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)。 + ### isModuleActive [field] ```kotlin diff --git a/docs/api/public/YukiHookCreater.md b/docs/api/public/YukiMemberHookCreater.md similarity index 89% rename from docs/api/public/YukiHookCreater.md rename to docs/api/public/YukiMemberHookCreater.md index f790d06f..3ac812b5 100644 --- a/docs/api/public/YukiHookCreater.md +++ b/docs/api/public/YukiMemberHookCreater.md @@ -1,7 +1,7 @@ -## YukiHookCreater [class] +## YukiMemberHookCreater [class] ```kotlin -class YukiHookCreater(private val packageParam: PackageParam, internal val hookClass: HookClass) +class YukiMemberHookCreater(private val packageParam: PackageParam, internal val hookClass: HookClass) ``` **变更记录** @@ -14,7 +14,49 @@ class YukiHookCreater(private val packageParam: PackageParam, internal val hookC **功能描述** -> `YukiHookAPI` 核心 Hook 实现类。 +> `YukiHookAPI` 的 `Member` 核心 Hook 实现类。 + +### PRIORITY_DEFAULT [field] + +```kotlin +val PRIORITY_DEFAULT: Int +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 默认 Hook 回调优先级。 + +### PRIORITY_LOWEST [field] + +```kotlin +val PRIORITY_LOWEST: Int +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 延迟回调 Hook 方法结果。 + +### PRIORITY_HIGHEST [field] + +```kotlin +val PRIORITY_HIGHEST: Int +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 更快回调 Hook 方法结果。 ### instanceClass [field] @@ -39,7 +81,7 @@ val instanceClass: Class<*> ### injectMember [method] ```kotlin -inline fun injectMember(tag: String, initiate: MemberHookCreater.() -> Unit): MemberHookCreater.Result +inline fun injectMember(priority: Int, tag: String, initiate: MemberHookCreater.() -> Unit): MemberHookCreater.Result ``` **变更记录** @@ -50,6 +92,8 @@ inline fun injectMember(tag: String, initiate: MemberHookCreater.() -> Unit): Me 将方法体进行 inline +增加 `priority` Hook 优先级 + **功能描述** > 注入要 Hook 的方法、构造类。 @@ -76,16 +120,30 @@ injectMember(tag = "KuriharaYuki") { } ``` +你还可以自定义 `priority`,以控制当前 Hook 对象并列执行的优先级速度。 + +> 示例如下 + +```kotlin +injectMember(priority = PRIORITY_HIGHEST) { + // Your code here. +} +``` + ### MemberHookCreater [class] ```kotlin -inner class MemberHookCreater(var tag: String) +inner class MemberHookCreater(private val priority: Int, internal val tag: String) ``` **变更记录** `v1.0` `添加` +`v1.0.80` `修改` + +增加 `priority` Hook 优先级 + **功能描述** > Hook 核心功能实现类,查找和处理需要 Hook 的方法、构造类。 diff --git a/docs/api/public/YukiModuleResources.md b/docs/api/public/YukiModuleResources.md new file mode 100644 index 00000000..f9cbbf28 --- /dev/null +++ b/docs/api/public/YukiModuleResources.md @@ -0,0 +1,29 @@ +## YukiModuleResources [class] + +```kotlin +class YukiModuleResources(private val baseInstance: XModuleResources) : Resources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 对接 `XModuleResources` 的中间层实例。 + +### fwd [method] + +```kotlin +fun fwd(resId: Int): YukiResForwarder +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 对接 `XModuleResources.fwd` 方法。 + +创建 `YukiResForwarder` 与 `XResForwarder` 实例。 \ No newline at end of file diff --git a/docs/api/public/YukiResForwarder.md b/docs/api/public/YukiResForwarder.md new file mode 100644 index 00000000..01e1490b --- /dev/null +++ b/docs/api/public/YukiResForwarder.md @@ -0,0 +1,55 @@ +## YukiResForwarder [class] + +```kotlin +class YukiResForwarder(private val baseInstance: XResForwarder) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 对接 `XResForwarder` 的中间层实例。 + +### instance [field] + +```kotlin +val instance: XResForwarder +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获得 `XResForwarder` 实例。 + +### id [field] + +```kotlin +val id: Int +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获得当前 APP 的 Resources Id。 + +### resources [field] + +```kotlin +val resources: Resources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获得当前 APP 的 Resources。 \ No newline at end of file diff --git a/docs/api/public/YukiResources.md b/docs/api/public/YukiResources.md new file mode 100644 index 00000000..5cb0ba4f --- /dev/null +++ b/docs/api/public/YukiResources.md @@ -0,0 +1,77 @@ +## YukiResources [class] + +```kotlin +class YukiResources(private val baseInstance: XResources) : Resources +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 对接 `XResources` 的中间层实例。 + +### LayoutInflatedParam [class] + +```kotlin +class LayoutInflatedParam(internal val baseParam: XC_LayoutInflated.LayoutInflatedParam) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 装载 Hook APP 的目标布局 Resources 实现类。 + +#### variantName [field] + +```kotlin +val variantName: String +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获取当前被 Hook 的布局装载目录名称。 + +例如:`layout`、`layout-land`、`layout-sw600dp`。 + +#### currentView [field] + +```kotlin +val currentView: View +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 获取当前被 Hook 的布局实例。 + +#### findViewByIdentifier [method] + +```kotlin +inline fun View.findViewByIdentifier(name: String): T? +``` + +```kotlin +inline fun findViewByIdentifier(name: String): T? +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 使用 Identifier 查找 Hook APP 指定 Id 的 `View`。 + +扩展方法可以使用 Identifier 查找 Hook APP 当前装载布局中指定 Id 的 `View`。 \ No newline at end of file diff --git a/docs/api/public/YukiResourcesHookCreater.md b/docs/api/public/YukiResourcesHookCreater.md new file mode 100644 index 00000000..7e719d16 --- /dev/null +++ b/docs/api/public/YukiResourcesHookCreater.md @@ -0,0 +1,569 @@ +## YukiResourcesHookCreater [class] + +```kotlin +class YukiResourcesHookCreater(private val packageParam: PackageParam, internal val hookResources: HookResources) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> `YukiHookAPI` 的 `Resources` 核心 Hook 实现类。 + +### injectResource [method] + +```kotlin +inline fun injectResource(tag: String, initiate: ResourceHookCreater.() -> Unit): ResourceHookCreater.Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 注入要 Hook 的 Resources。 + +**功能示例** + +你可以注入任意 Resources,使用 `injectResource` 即可创建一个 `Hook` 对象。 + +> 示例如下 + +```kotlin +injectResource { + // Your code here. +} +``` + +你还可以自定义 `tag`,方便你在调试的时候能够区分你的 `Hook` 对象。 + +> 示例如下 + +```kotlin +injectResource(tag = "KuriharaYuki") { + // Your code here. +} +``` + +### ResourcesHookCreater [class] + +```kotlin +inner class ResourcesHookCreater(private val tag: String) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> Hook 核心功能实现类。 + +查找和处理需要 Hook 的 Resources。 + +#### resourceId [field] + +```kotlin +var resourceId: Int +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 直接设置需要替换的 Resources Id。 + +!> 不建议使用此方法设置目标需要 Hook 的 Resources Id,你可以使用 `conditions` 方法。 + +**功能示例** + +你可以直接设置并指定目标 Hook APP 的 Resources Id。 + +> 示例如下 + +```kotlin +injectResource { + resourceId = 0x7f060001.toInt() + replaceTo(...) +} +``` + +#### conditions [method] + +```kotlin +inline fun conditions(initiate: ConditionFinder.() -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 查找条件。 + +若你设置了 `resourceId` 则此方法将不会被使用。 + +**功能示例** + +你可参考 [ConditionFinder](#conditionfinder-class) 查看详细用法。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "test_string" + string() + } + replaceTo(...) +} +``` + +#### replaceTo [method] + +```kotlin +fun replaceTo(any: Any) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 替换指定 Resources 为指定的值。 + +**功能示例** + +你可以替换找到的 Resources 为你想要的值,可以是 `String`、`Drawable` 等。 + +比如我们要替换一个找到的字符串 Resources。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "test_string" + string() + } + replaceTo("replace string") +} +``` + +或是替换为一个 `Drawable`,你无需对目标 Resources 实现 `fwd` 方法或 `DrawableLoader`。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "test_drawable" + drawable() + } + replaceTo(ColorDrawable(Color.RED)) +} +``` + +#### replaceToTrue [method] + +```kotlin +fun replaceToTrue() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 替换指定 Resources 为 `true`。 + +!> 确保目标替换 Resources 的类型为 `Boolean`。 + +#### replaceToFalse [method] + +```kotlin +fun replaceToFalse() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 替换指定 Resources 为 `false`。 + +!> 确保目标替换 Resources 的类型为 `Boolean`。 + +#### replaceToModuleResource [method] + +```kotlin +fun replaceToModuleResource(resId: Int) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 替换为当前 Xposed 模块的 Resources。 + +你可以直接使用模块的 `R.string.xxx`、`R.mipmap.xxx`、`R.drawable.xxx` 替换 Hook APP 的 Resources。 + +**功能示例** + +使用此方法可非常方便地使用当前模块的 Resources 去替换目标 Hook APP 的 Resources。 + +这个过程你无需对目标 Resources 实现 `fwd` 方法。 + +比如我们要替换一个字符串。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "test_string" + string() + } + replaceToModuleResource(R.id.module_string) +} +``` + +还可以替换一些复杂的 Resources,比如 `xml` 创建的 `drawable`。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "test_drawable" + drawable() + } + replaceToModuleResource(R.drawable.module_drawable) +} +``` + +#### injectAsLayout [method] + +```kotlin +fun injectAsLayout(initiate: YukiResources.LayoutInflatedParam.() -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 作为装载的布局注入。 + +**功能示例** + +你可以直接注入一个布局监听并修改它的内部 `View`。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "activity_main" + layout() + } + injectAsLayout { + findViewByIdentifier(name = "test_view")?.isVisible = false + findViewByIdentifier(name = "test_text_view")?.text = "Hook this" + } +} +``` + +你还可以通过 `currentView` 拿到 `Context`。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "activity_main" + layout() + } + injectAsLayout { + Toast.makeText(currentView.context, "Hook this", Toast.LENGTH_SHORT).show() + } +} +``` + +#### ConditionFinder [class] + +```kotlin +inner class ConditionFinder +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> Resources 查找条件实现类。 + +##### name [field] + +```kotlin +var name: String +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 名称。 + +##### anim [method] + +```kotlin +fun anim() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为动画。 + +##### animator [method] + +```kotlin +fun animator() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为属性动画。 + +##### bool [method] + +```kotlin +fun bool() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为布朗(Boolean)。 + +##### color [method] + +```kotlin +fun color() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为颜色(Color)。 + +##### dimen [method] + +```kotlin +fun dimen() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为尺寸(Dimention)。 + +##### drawable [method] + +```kotlin +fun drawable() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为 Drawable。 + +##### integer [method] + +```kotlin +fun integer() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为整型(Integer)。 + +##### layout [method] + +```kotlin +fun layout() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为布局(Layout)。 + +##### plurals [method] + +```kotlin +fun plurals() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为 Plurals。 + +##### string [method] + +```kotlin +fun string() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为字符串(String)。 + +##### xml [method] + +```kotlin +fun xml() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为 Xml。 + +##### mipmap [method] + +```kotlin +fun mipmap() +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 Resources 类型为位图(Mipmap)。 + +#### Result [class] + +```kotlin +inner class Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 监听全部 Hook 结果实现类,可在这里处理失败事件监听。 + +##### result [method] + +```kotlin +inline fun result(initiate: Result.() -> Unit): Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 创建监听事件方法体。 + +##### by [method] + +```kotlin +inline fun by(initiate: () -> Boolean): Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 添加执行 Hook 需要满足的条件,不满足条件将直接停止 Hook。 + +#### onHookingFailure [method] + +```kotlin +fun onHookingFailure(initiate: (Throwable) -> Unit): Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 监听 Hook 过程发生错误的回调方法。 + +#### ignoredHookingFailure [method] + +```kotlin +fun ignoredHookingFailure(): Result +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 忽略 Hook 过程出现的错误。 \ No newline at end of file diff --git a/docs/api/public/YukiXposedEvent.md b/docs/api/public/YukiXposedEvent.md new file mode 100644 index 00000000..55e25561 --- /dev/null +++ b/docs/api/public/YukiXposedEvent.md @@ -0,0 +1,69 @@ +## YukiXposedEvent [object] + +```kotlin +object YukiXposedEvent +``` + +**变更记录** + +`v1.0.80` `添加` + +**功能描述** + +> 实现对原生 Xposed API 的装载事件监听。 + +### events [method] + +```kotlin +inline fun events(initiate: YukiXposedEvent.() -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 对 `YukiXposedEvent` 创建一个方法体。 + +### onInitZygote [method] + +```kotlin +fun onInitZygote(initiate: (StartupParam) -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 initZygote 事件监听。 + +### onHandleLoadPackage [method] + +```kotlin +fun onHandleLoadPackage(initiate: (LoadPackageParam) -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 handleLoadPackage 事件监听。 + +### onHandleInitPackageResources [method] + +```kotlin +fun onHandleInitPackageResources(initiate: (InitPackageResourcesParam) -> Unit) +``` + +**变更记录** + +`v1.0.80` `新增` + +**功能描述** + +> 设置 handleInitPackageResources 事件监听。 \ No newline at end of file diff --git a/docs/config/api-example.md b/docs/config/api-example.md index a136de10..3e3f32e5 100644 --- a/docs/config/api-example.md +++ b/docs/config/api-example.md @@ -177,6 +177,57 @@ class HookEntryClass : IYukiHookXposedInit { } ``` +### 扩展特性 + +如果你当前使用的 Hook Framework 支持并启用了资源钩子(Resources Hook)功能,你现在可以直接在 `encase` 中创建 Resources Hook。 + +你完全不需要与之前在使用 Xposed API 那样区分 `initZygote`、`handleLoadPackage`、`handleInitPackageResources` 方法来执行不同的功能。 + +在 `YukiHookAPI` 中,这些功能**是无缝的**。 + +> 示例如下 + +```kotlin +encase { + loadApp(name = "com.example.demo") { + findClass(name = "$packageName.DemoClass").hook { + // Your code here. + } + // 创建一个 Resources Hook (固定用法) + resources().hook { + // Your code here. + } + } +} +``` + +你还可以同时使用 `loadZygote` 方法来装载系统框架。 + +> 示例如下 + +```kotlin +encase { + loadZygote { + ActivityClass.hook { + // Your code here. + } + // 在 Zygote 中创建 Resources Hook + resources().hook { + // Your code here. + } + } + loadApp(name = "com.example.demo") { + findClass(name = "$packageName.DemoClass").hook { + // Your code here. + } + // 在 APP 中创建 Resources Hook + resources().hook { + // Your code here. + } + } +} +``` + ## 作为 Hook API 使用需要注意的地方 若你作为 Hook API 使用,那么你只需要在入口处对 `encase` 方法进行区分。 @@ -196,4 +247,6 @@ fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker) 此处的 `baseContext` 只需填入你在 `attachBaseContext` 处得到的 `Context` 即可,其它用法与上述内容完全一致。 -!> 切勿以 Xposed 方式使用 `encase` 方法而漏掉 `baseContext` 参数,否则你的 Hook 将完全不工作。 \ No newline at end of file +!> 切勿以 Xposed 方式使用 `encase` 方法而漏掉 `baseContext` 参数,否则你的 Hook 将完全不工作。 + +!> Resources Hook 功能不支持作为 Hook API 使用。 \ No newline at end of file diff --git a/docs/config/api-exception.md b/docs/config/api-exception.md index b9c08495..ed600518 100644 --- a/docs/config/api-exception.md +++ b/docs/config/api-exception.md @@ -16,11 +16,11 @@ 请确认你在正确的地方装载了 `YukiHookAPI` 的 `encase` 方法,详情请参考 [作为 Xposed 模块使用的相关配置](config/xposed-using) 以及 [作为 Hook API 使用的相关配置](config/api-using)。 -!> `loggerE` You cannot load a hooker in "onInit" method! Aborted +!> `loggerE` You cannot load a hooker in "onInit" or "onXposedEvent" method! Aborted **异常原因** -你尝试在继承 `IYukiHookXposedInit` 的 Hook 入口类的 `onInit` 方法中装载了 `encase` 方法。 +你尝试在继承 `IYukiHookXposedInit` 的 Hook 入口类的 `onInit` 或 `onXposedEvent` 方法中装载了 `encase` 或 `resources` 方法。 > 示例如下 @@ -34,6 +34,13 @@ class HookEntry : IYukiHookXposedInit { } } + override fun onXposedEvent() { + // ❗错误的使用方法 + YukiHookAPI.encase { + // Your code here. + } + } + override fun onHook() { // Your code here. } @@ -75,6 +82,16 @@ class HookEntry : IYukiHookXposedInit { 通常情况下这种错误不会轻易发生,若一旦发生此错误,请自行查看控制台打印的日志定位问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。 +!> `loggerE` YukiHookAPI bind initZygote failed + +**异常原因** + +`YukiHookAPI` 在尝试装载 Xposed 原生接口 `initZygote` 方法时发生了不能处理的异常。 + +**解决方案** + +通常情况下这种错误不会轻易发生,若一旦发生此错误,请自行查看控制台打印的日志定位问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。 + !> `loggerE` HookClass \[**NAME**\] not found **异常原因** @@ -361,6 +378,50 @@ method { 请检查查询条件中 `param` 的 `index` 号下标的 `Class` 是否存在,然后再试一次。 +!> `loggerE` Resources Hook condition name/type cannot be empty \[**TAG**\] + +**异常原因** + +在查找 Resources 时并未设置任何条件。 + +> 示例如下 + +```kotlin +// 情况 1 +conditions { + // 这里没有设置任何条件 +} +// 情况 2 +conditions { + name = "test" + // 这里缺少了 type 条件 +} +``` + +**解决方案** + +Resources 的 Hook 并非类似方法的 Hook,其必须拥有完整的名称和类型描述才能查询成功,请将查询条件补充完整并再试一次。 + +!> `loggerE` Resources Hook type is invalid \[**TAG**\] + +**异常原因** + +在 Hook Resources 时发生了类型错误的异常。 + +**解决方案** + +`YukiHookAPI` 会尝试在 `initZygote` 与 `handleInitPackageResources` 中装载 Resources Hook,若全部装载失败可能会发生此异常,当前 Hook Framework 需要支持并启用资源钩子(Resources Hook)功能,请检查后再试一次。 + +!> `loggerE` Resources Hook got an Exception \[**TAG**\] + +**异常原因** + +在 Hook Resources 时发生了任意的异常。 + +**解决方案** + +这是一个异常汇总,请自行向下查看日志具体的异常是什么,例如找不到 Resources Id 的问题。 + ## 阻断异常 > 这些异常会直接导致 APP 停止运行(FC),同时会在控制台打印 `E` 级别的日志,还会造成 Hook 进程“死掉”。 @@ -682,6 +743,42 @@ encase { `appContext` 在宿主环境初始化完成之前有大的概率可能是空的,请延迟获取或在宿主的 Hook 方法回调方法体内再使用此变量。 +!> `IllegalStateException` Current Hook Framework not support moduleAppResources + +**异常原因** + +在 `PackageParam` 中调用了 `moduleAppResources` 变量但是无法获取到实例对象。 + +> 示例如下 + +```kotlin +encase { + // 调用了此变量 + moduleAppResources... +} +``` + +**解决方案** + +`moduleAppResources` 需要当前 Hook Framework 支持 `initZygote` 功能,请检查后再试一次。 + +!> `IllegalStateException` You cannot call to appResources in this time + +在 `PackageParam` 中调用了 `appResources` 变量但是无法获取到实例对象。 + +> 示例如下 + +```kotlin +encase { + // 调用了此变量 + appResources... +} +``` + +**解决方案** + +`appResources` 不会在 `initZygote` 中装载,另外,这个功能需要当前 Hook Framework 支持并启用资源钩子(Resources Hook)功能,请检查后再试一次。 + !> `IllegalStateException` VariousClass match failed of those **CLASSES** **异常原因** @@ -713,6 +810,48 @@ TargetClass.hook { 详情请参考 [状态监听](guide/example?id=状态监听)。 +!> `IllegalStateException` LayoutInflatedParam View instance got null + +**异常原因** + +在布局 Hook 回调中调用了 `currentView` 但没取到实例对象。 + +> 示例如下 + +```kotlin +injectResource { + conditions { + name = "activity_main" + layout() + } + injectAsLayout { + // 调用了此变量 + currentView... + } +} +``` + +**解决方案** + +这种情况基本上不存在,除非被 Hook 的宿主当前 `Activity` 已经销毁或 Hook Framework 自身存在问题。 + +!> `IllegalStateException` XResForwarder is invalid + +**异常原因** + +在 `YukiResForwarder` 中调用了 `resources` 但没取到实例对象。 + +> 示例如下 + +```kotlin +// 调用了此变量 +moduleAppResources.fwd(...).resources +``` + +**解决方案** + +这种情况基本上不存在,除非 Hook Framework 自身存在问题。 + !> `IllegalStateException` Hook Members is empty, hook aborted **异常原因** @@ -731,6 +870,24 @@ TargetClass.hook { 你必须在 `hook` 方法体内加入至少一个 `injectMember` 方法。 +!> `IllegalStateException` Hook Resources is empty, hook aborted + +**异常原因** + +使用了 `hook` 方法体但其中并没有填写内容。 + +> 示例如下 + +```kotlin +resources().hook { + // 这里没有填写任何内容 +} +``` + +**解决方案** + +你必须在 `hook` 方法体内加入至少一个 `injectResources` 方法。 + !> `IllegalStateException` paramTypes is empty, please use emptyParam() instead **异常原因** @@ -771,4 +928,14 @@ method { name = "test" paramCount = 0 } -``` \ No newline at end of file +``` + +!> `IllegalStateException` Invalid YukiHookCallback type + +**异常原因** + +`YukiHookAPI` 的核心 Hook 功能发生故障。 + +**解决方案** + +这种情况基本上不存在,若发生上述问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。 \ No newline at end of file diff --git a/docs/config/r8-proguard.md b/docs/config/r8-proguard.md index d0753c38..f5c3c6fa 100644 --- a/docs/config/r8-proguard.md +++ b/docs/config/r8-proguard.md @@ -10,16 +10,7 @@ > ~~如果你仍然在使用 `Proguard`,你需要做一些规则配置。~~ -~~在 `proguard-rules.pro` 添加如下代码即可。~~ - -> ~~示例如下~~ - -```proguard --keep class com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus {*;} --keep class 这里填你的 HookEntryClass 入口类完整包名_YukiHookXposedInit {*;} -``` - -!> Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 `R8`,基本可以不需要考虑混淆的问题。 +!> Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 `R8`,不再需要考虑混淆的问题。 若要在任何版本下启用 `R8`,请在 `gradle.properties` 文件中加入如下规则,Android Gradle Plugin 7.0 及以上版本无需任何配置。 diff --git a/docs/config/xposed-using.md b/docs/config/xposed-using.md index de752367..6d9de824 100644 --- a/docs/config/xposed-using.md +++ b/docs/config/xposed-using.md @@ -96,7 +96,7 @@ Xposed 入口类处理如下。 > 示例如下 ```kotlin -class HookEntry_YukiHookXposedInit: IXposedHookLoadPackage, ... +class HookEntry_YukiHookXposedInit: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources ``` 编译后的类名结构如下。 @@ -105,6 +105,7 @@ class HookEntry_YukiHookXposedInit: IXposedHookLoadPackage, ... ``` ...hook.HookEntry ← 你的入口类 +...hook.HookEntry_Impl ← 自动生成的 Impl 类 ...hook.HookEntry_YukiHookXposedInit ← 自动生成的 Xposed 入口类 ``` @@ -122,7 +123,7 @@ Xposed 入口类处理如下。 > 示例如下 ```kotlin -class HookXposedEntry: IXposedHookLoadPackage, ... +class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources ``` 编译后的类名结构如下。 @@ -131,6 +132,7 @@ class HookXposedEntry: IXposedHookLoadPackage, ... ``` ...hook.HookEntry ← 你的入口类 +...hook.HookEntry_Impl ← 自动生成的 Impl 类 ...hook.HookXposedEntry ← 自动生成的 Xposed 入口类 ``` @@ -138,21 +140,47 @@ class HookXposedEntry: IXposedHookLoadPackage, ... ### IYukiHookXposedInit 接口 -```kotlin -interface IYukiHookXposedInit { - - fun onInit() - - fun onHook() -} -``` - `IYukiHookXposedInit` 接口为你的 `HookEntryClass` 必须实现的接口,这是你的模块开始 Hook 的起点。 若要了解更多可 [点击这里](api/document?id=iyukihookxposedinit-interface) 进行查看。 当你的模块被 Xposed 装载后,`onHook` 方法将会被回调,你需要在此方法中开始使用 `YukiHookAPI`。 -> 基本的调用流程为 `YukiHookInjectXposedInitClass.handleLoadPackage` → `HookEntryClass.onInit` → `HookEntryClass.onHook` → `YukiHookAPI.onXposedLoaded` +> 基本的调用流程为 `_YukiHookXposedInit` → `IYukiHookXposedInit.onXposedEvent` → `IYukiHookXposedInit.onInit` → `IYukiHookXposedInit.onHook` -详情请参考 [API 基本配置](config/api-example)。 \ No newline at end of file +详情请参考 [API 基本配置](config/api-example)。 + +## 原生 Xposed API 事件 + +若你当前的 Xposed 模块使用了第三方的资源,但是短时间内可能无法转移它们,此时,你可以使用 `onXposedEvent` 实现监听原生 Xposed API 的全部装载事件。 + +> 示例如下 + +```kotlin +@InjectYukiHookWithXposed +class HookEntry: IYukiHookXposedInit { + + override fun onHook() { + // Your code here. + } + + override fun onXposedEvent() { + // 监听原生 Xposed API 的装载事件 + YukiXposedEvent.events { + onInitZygote { + // it 对象即 [StartupParam] + } + onHandleLoadPackage { + // it 对象即 [LoadPackageParam] + } + onHandleInitPackageResources { + // it 对象即 [InitPackageResourcesParam] + } + } + } +} +``` + +`onXposedEvent` 与 `onHook` 方法完全独立存在,互不影响,你可以继续在 `onHook` 方法中使用 `YukiHookAPI`。 + +若要了解更多可 [点击这里](api/document?id=onxposedevent-method) 进行查看。 \ No newline at end of file diff --git a/docs/guide/example.md b/docs/guide/example.md index e9042051..45ead4cb 100644 --- a/docs/guide/example.md +++ b/docs/guide/example.md @@ -8,7 +8,7 @@ ``` Host Environment -└── YukiHookCreater +└── YukiMemberHookCreater └── Class └── MemberHookCreater └── Member @@ -19,6 +19,15 @@ Host Environment ├── Before └── After ... + YukiResourcesHookCreater + └── Resources + └── ResourcesHookCreater + └── Drawable + └── Replace + ResourcesHookCreater + └── Layout + └── Inject + ... ``` > 上方的结构换做代码将可写为如下形式。 @@ -37,6 +46,14 @@ TargetClass.hook { } } } +resources().hook { + injectResource { + conditions { + // Your code here. + } + replaceTo(...) + } +} ``` ## Demo @@ -51,6 +68,10 @@ TargetClass.hook { ## 一个简单的 Hook 例子 +> 这里给出了 Hook APP、Hook 系统框架与 Hook Resources 的例子,可供参考。 + +### Hook APP + 假设,我们要 Hook `com.android.browser` 中的 `onCreate` 方法并弹出一个对话框。 在 `encase` 方法体中添加代码。 @@ -171,6 +192,97 @@ TestClass.hook { } ``` +### Hook 系统框架 + +在 `YukiHookAPI` 中,Hook 系统框架的实现非常简单。 + +假设我们要全局 Hook 一个 `Activity` 的 `onCreate` 事件 + +在 `encase` 方法体中添加代码。 + +> 示例如下 + +```kotlin +loadZygote { + ActivityClass.hook { + injectMember { + method { + name = "onCreate" + param(BundleClass) + returnType = UnitType + } + afterHook { + // Your code here. + } + } + } +} +``` + +这样就实现了上述的 Hook 功能。 + +!> `loadZygote` 与 `loadApp(name = "android")` 有直接性区别,`loadZygote` 会在 `initZygote` 中装载,若要 Hook 系统框架,建议使用 `loadZygote`。 + +### Hook Resources + +假设,我们要 Hook `com.android.browser` 中 `string` 类型的 `app_name` 内容替换为 `123`。 + +在 `encase` 方法体中添加代码。 + +> 示例如下 + +```kotlin +loadApp(name = "com.android.browser") { + resources().hook { + injectResource { + conditions { + name = "app_name" + string() + } + replaceTo("123") + } + } +} +``` + +若当前 APP 使用 `app_name` 设置了标题栏文本,则它就会变成我们的 `123`。 + +你还可以使用当前 Xposed 模块的 Resources 替换 Hook APP 的 Resources。 + +假设,我们要继续 Hook `com.android.browser` 中 `mipmap` 类型的 `ic_launcher`。 + +> 示例如下 + +```kotlin +loadApp(name = "com.android.browser") { + resources().hook { + injectResource { + conditions { + name = "ic_launcher" + mipmap() + } + replaceToModuleResource(R.mipmap.ic_launcher) + } + } +} +``` + +至此目标 APP 的图标将会被替换为我们设置的图标。 + +若你想替换系统框架的资源,同样也可以这样实现,只需要把 `loadApp` 换成 `loadZygote` 即可。 + +> 示例如下 + +```kotlin +loadZygote { + resources().hook { + // Your code here. + } +} +``` + +更多功能请参考 [ResourcesHookCreater](api/document?id=resourceshookcreater-class)。 + ## 异常处理 > `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。 @@ -193,6 +305,20 @@ injectMember { } ``` +在 Resources Hook 时此方法同样适用。 + +> 示例如下 + +```kotlin +injectResource { + // Your code here. +}.result { + // 处理 Hook 时的任意异常 + onHookingFailure {} + // ... +} +``` + 你还可以处理 Hook 的 `Class` 不存在时发生的异常。 > 示例如下 diff --git a/docs/guide/home.md b/docs/guide/home.md index 316f7087..97779d5f 100644 --- a/docs/guide/home.md +++ b/docs/guide/home.md @@ -62,6 +62,8 @@ 现在,我们只需要编写少量的代码,一切时间开销和花费交给自动化处理。 +借助 `Kotlin` 优雅的 `lambda` 写法以及 `YukiHookAPI`,可以让你的 Hook 逻辑更加美观清晰。 + > 示例如下 @@ -70,9 +72,34 @@ ```kotlin @InjectYukiHookWithXposed -class MainHook : IYukiHookXposedInit { +class HookEntry : IYukiHookXposedInit { override fun onHook() = encase { + loadZygote { + ActivityClass.hook { + injectMember { + method { + name = "onCreate" + param(BundleClass) + } + beforeHook { + // Your code here. + } + afterHook { + // Your code here. + } + } + } + resources().hook { + injectResource { + conditions { + name = "sym_def_app_icon" + mipmap() + } + replaceToModuleResource(R.mipmap.ic_launcher) + } + } + } loadApp(name = "com.android.browser") { ActivityClass.hook { injectMember { @@ -88,6 +115,15 @@ class MainHook : IYukiHookXposedInit { } } } + resources().hook { + injectResource { + conditions { + name = "ic_launcher" + mipmap() + } + replaceToModuleResource(R.mipmap.ic_launcher) + } + } } } } @@ -96,14 +132,16 @@ class MainHook : IYukiHookXposedInit { #### **Xposed API** ```kotlin -class MainHook : IXposedHookLoadPackage { +class HookEntry : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources { - override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { - if (lpparam.packageName == "com.android.browser") - XposedHelpers.findAndHookMethod( + private lateinit var moduleResources: XModuleResources + + override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) { + moduleResources = XModuleResources.createInstance(sparam.modulePath, null) + XResources.setSystemWideReplacement("android", "mipmap", "sym_def_app_icon", moduleResources.fwd(R.mipmap.ic_launcher)) + XposedHelpers.findAndHookMethod( Activity::class.java.name, - lpparam.classLoader, - "onCreate", + null, "onCreate", Bundle::class.java, object : XC_MethodHook() { override fun beforeHookedMethod(param: MethodHookParam?) { @@ -115,14 +153,36 @@ class MainHook : IXposedHookLoadPackage { } }) } + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + if (lpparam.packageName == "com.android.browser") + XposedHelpers.findAndHookMethod( + Activity::class.java.name, + lpparam.classLoader, "onCreate", + Bundle::class.java, + object : XC_MethodHook() { + override fun beforeHookedMethod(param: MethodHookParam?) { + // Your code here. + } + + override fun afterHookedMethod(param: MethodHookParam?) { + // Your code here. + } + }) + } + + override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) { + if (resparam.packageName == "com.android.browser") + resparam.res.setReplacement("com.android.browser", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher)) + } } ``` -是的,你没有看错,仅仅就需要这几行代码,就一切安排妥当。 +是的,你没有看错,仅仅就需要这些代码,就能完全取代 Xposed API 实现同样的功能。 -代码量少,逻辑清晰,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。 +现在,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。 ## 支持的 Hook 框架 @@ -131,7 +191,7 @@ class MainHook : IXposedHookLoadPackage { | Hook Framework | ST | Describe | | --------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------- | | [LSPosed](https://github.com/LSPosed/LSPosed) | ✅ | 多场景下稳定使用 | -| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ✅ | 部分兼容 | +| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ❎ | 已停止维护,不再推荐使用 | | [Pine](https://github.com/canyie/pine) | ⭕ | 可以使用 | | [SandHook](https://github.com/asLody/SandHook) | ⭕ | 可以使用 | | [Whale](https://github.com/asLody/whale) | ⭕ | 需要 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale) | diff --git a/docs/guide/move-to-new-api.md b/docs/guide/move-to-new-api.md index 775c3742..7feac1ae 100644 --- a/docs/guide/move-to-new-api.md +++ b/docs/guide/move-to-new-api.md @@ -22,15 +22,28 @@ override fun onHook() = encase { appInfo // 得到宿主 Application 生命周期 appContext - // 创建 Hook - findClass("com.demo.Test").hook { - injectMember { - method { - name = "test" - param(BooleanType) + // Hook 指定的 APP + loadApp(name = "com.demo.test") { + // Class Hook + findClass("com.demo.test.TestClass").hook { + injectMember { + method { + name = "test" + param(BooleanType) + } + afterHook { + // ... + } } - afterHook { - // ... + } + // Resources Hook (固定用法) + resources().hook { + injectResource { + conditions { + name = "ic_launcher" + mipmap() + } + replaceToModuleResource(R.mipmap.ic_launcher) } } } @@ -40,6 +53,12 @@ override fun onHook() = encase { #### **Xposed API** ```kotlin +private lateinit var moduleResources: XModuleResources + +override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) { + moduleResources = XModuleResources.createInstance(sparam.modulePath, null) +} + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { // 得到当前 Hook 的包名 lpparam.packageName @@ -47,12 +66,20 @@ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { lpparam.applicationInfo // 得到宿主 Application 生命周期 AndroidAppHelper.currentApplication() + // Class Hook + if(lpparam.packageName == "com.demo.test") + XposedHelpers.findAndHookMethod("com.demo.test.TestClass", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() { + override fun afterHookedMethod(param: MethodHookParam) { + // ... + } + }) +} + +override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) { + // 得到当前 Hook 的包名 + resparam.packageName // 创建 Hook - XposedHelpers.findAndHookMethod("com.demo.Test", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() { - override fun afterHookedMethod(param: MethodHookParam) { - // ... - } - }) + resparam.res.setReplacement("com.demo.test", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher)) } ``` diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index eac71914..27b8377a 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -101,7 +101,7 @@ dependencies { ```kotlin @InjectYukiHookWithXposed -class MainHook : IYukiHookXposedInit { +class HookEntry : IYukiHookXposedInit { override fun onHook() = YukiHookAPI.encase { // Your code here. @@ -153,4 +153,4 @@ override fun attachBaseContext(base: Context?) { ### 特别说明 -!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 功能将失效。 \ No newline at end of file +!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 以及 Resources Hook 功能将失效。 \ No newline at end of file diff --git a/docs/guide/special-feature.md b/docs/guide/special-feature.md index 32859e15..a82f3d4c 100644 --- a/docs/guide/special-feature.md +++ b/docs/guide/special-feature.md @@ -11,7 +11,26 @@ ```java package com.demo; -public class Test { +public class BaseTest { + + public BaseTest() { + // ... + } + + public BaseTest(boolean isInit) { + // ... + } + + private void doBaseTask(String taskName) { + // ... + } +} +``` + +```java +package com.demo; + +public class Test extends BaseTest { public Test() { // ... @@ -61,7 +80,7 @@ public class Test { ### 查询与反射调用 -假设我们要得到 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。 +假设我们要得到 `Test`(以下统称“当前 `Class`”)的 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。 > 示例如下 @@ -178,6 +197,54 @@ Test::class.java.method { }.get(instance) // 得到这个方法 ``` +### 在父类查询 + +你会注意到 `Test` 继承于 `BaseTest`,现在我们想得到 `BaseTest` 的 `doBaseTask` 方法,在不知道父类名称的情况下,要怎么做呢? + +参照上面的查询条件,我们只需要在查询条件中加入一个 `superClass` 即可实现这个功能。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val instance = Test() +// 使用 YukiHookAPI 调用并执行 +Test::class.java.method { + name = "doBaseTask" + param(StringType) + // 只需要添加这个条件 + superClass() +}.get(instance).call("task_name") +``` + +这个时候我们就可以在父类中取到这个方法了。 + +`superClass` 有一个参数为 `isOnlySuperClass`,设置为 `true` 后,可以跳过当前 `Class` 仅查询当前 `Class` 的父类。 + +由于我们现在已知 `doBaseTask` 方法只存在于父类,可以加上这个条件节省查询时间。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val instance = Test() +// 使用 YukiHookAPI 调用并执行 +Test::class.java.method { + name = "doBaseTask" + param(StringType) + // 加入一个查询条件 + superClass(isOnlySuperClass = true) +}.get(instance).call("task_name") +``` + +这个时候我们同样可以得到父类中的这个方法。 + +`superClass` 一旦设置就会自动循环向后查找全部继承的父类中是否有这个方法,直到查询到目标没有父类(继承关系为 `java.lang.Object`)为止。 + +更多用法可参考 [superClass 方法](api/document?id=superclass-method)。 + +!> 当前查询的 `Method` 除非指定 `superClass` 条件,否则只能查询到当前 `Class` 的 `Method`。 + ### 静态字节码 有些方法和变量在 `Class` 中是静态的实现,这个时候,我们不需要传入实例就可以调用它们。 @@ -347,6 +414,23 @@ instance.current { } ``` +我们还可以用 `superClass` 调用当前 `Class` 父类的方法。 + +> 示例如下 + +```kotlin +// 假设这就是这个 Class 的实例 +val instance = Test() +// 假设这个 Class 是不能被直接得到的 +instance.current { + // 执行父类的 doBaseTask 方法 + superClass().method { + name = "doBaseTask" + param(StringType) + }.call("task_name") +} +``` + 问题又来了,我想使用反射的方式创建如下的实例并调用其中的方法,该怎么做呢? > 示例如下 @@ -768,9 +852,9 @@ loggerE(msg = "This is an error") ```kotlin // 假设这就是被抛出的异常 -val e = Throwable(...) +val throwable = Throwable(...) // 打印日志 -loggerE(msg = "This is an error", e = e) +loggerE(msg = "This is an error", e = throwable) ``` 打印的结果为如下所示。 diff --git a/docs/index.html b/docs/index.html index 8205148d..b2c46f49 100644 --- a/docs/index.html +++ b/docs/index.html @@ -44,7 +44,7 @@ coverpage: true, loadNavbar: true, loadSidebar: true, - subMaxLevel: 3, + subMaxLevel: 4, auto2top: true, mergeNavbar: false, notFoundPage: '_404.md', @@ -60,7 +60,7 @@ ], placeholder: '搜索文档', noData: '噫,什么都没找到~', - depth: 5, + depth: 7, hideOtherSidebarContent: true }, tabs: { 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 b42b50f6..f6c0105e 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 @@ -31,13 +31,16 @@ import java.text.SimpleDateFormat import java.util.* /** - * 代码文件注入器 + * 代码文件注入模板类 */ object CodeSourceFileTemplate { /** 定义 Jvm 方法名 */ private const val IS_ACTIVE_METHOD_NAME = "__--" + /** 定义 Jvm 方法名 */ + private const val HAS_RESOURCES_HOOK_METHOD_NAME = "_--_" + /** 定义 Jvm 方法名 */ private const val GET_XPOSED_VERSION_METHOD_NAME = "--__" @@ -92,80 +95,150 @@ object CodeSourceFileTemplate { /** * 获得 xposed_init 注入文件 * @param packageName 包名 - * @param modulePackageName 模块包名 * @param entryClassName 入口类名 * @param xInitClassName xposed_init 入口类名 * @return [ByteArray] */ - fun getXposedInitFileByteArray(packageName: String, modulePackageName: String, entryClassName: String, xInitClassName: String) = - ("package $packageName\n" + + fun getXposedInitFileByteArray(packageName: String, entryClassName: String, xInitClassName: String) = + ("@file:Suppress(\"ClassName\")\n" + + "\n" + + "package $packageName\n" + "\n" + "import androidx.annotation.Keep\n" + - "import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge\n" + - "import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus\n" + - "import com.highcapable.yukihookapi.hook.log.loggerE\n" + - "import de.robv.android.xposed.IXposedHookLoadPackage\n" + - "import de.robv.android.xposed.XC_MethodReplacement\n" + - "import de.robv.android.xposed.XposedHelpers\n" + - "import de.robv.android.xposed.XposedBridge\n" + - "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" + "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" + - "import $packageName.$entryClassName\n" + + "import de.robv.android.xposed.IXposedHookInitPackageResources\n" + + "import de.robv.android.xposed.IXposedHookLoadPackage\n" + + "import de.robv.android.xposed.IXposedHookZygoteInit\n" + + "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" + + "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" + "\n" + - getCommentContent(entryClassName, currrentClassTag = "XposedInit") + + getCommentContent(entryClassName, currrentClassTag = "Xposed Init") + "@Keep\n" + "@YukiGenerateApi\n" + - "class $xInitClassName : IXposedHookLoadPackage {\n" + + "class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {\n" + + "\n" + + " override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" + + " ${entryClassName}_Impl.callInitZygote(sparam)\n" + + " YukiXposedEvent.EventHandler.callInitZygote(sparam)\n" + + " }\n" + "\n" + " override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + - " if (lpparam == null) return\n" + - " try {\n" + - " $entryClassName().apply {\n" + - " onInit()\n" + - " if (YukiHookXposedBridge.isXposedCallbackSetUp) {\n" + - " loggerE(tag = \"YukiHookAPI\", msg = \"You cannot loading a hooker in \\\"onInit\\\" method! Aborted\")\n" + - " return\n" + - " }\n" + - " onHook()\n" + + " ${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" + + "}").toByteArray() + + /** + * 获得 xposed_init_Impl 注入文件 + * @param packageName 包名 + * @param modulePackageName 模块包名 + * @param entryClassName 入口类名 + * @return [ByteArray] + */ + fun getXposedInitImplFileByteArray(packageName: String, modulePackageName: String, entryClassName: String) = + ("@file:Suppress(\"ClassName\")\n" + + "\n" + + "package $packageName\n" + + "\n" + + "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" + + "import com.highcapable.yukihookapi.hook.log.loggerE\n" + + "import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge\n" + + "import de.robv.android.xposed.IXposedHookZygoteInit\n" + + "import de.robv.android.xposed.XC_MethodReplacement\n" + + "import de.robv.android.xposed.XposedHelpers\n" + + "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" + + "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" + + "\n" + + getCommentContent(entryClassName, currrentClassTag = "Xposed Init Impl") + + "@YukiGenerateApi\n" + + "object ${entryClassName}_Impl {\n" + + "\n" + + " private const val modulePackageName = \"$modulePackageName\"\n" + + " private val hookEntry = $entryClassName()\n" + + " private var isZygoteBinded = false\n" + + " private var lpparam: XC_LoadPackage.LoadPackageParam? = null\n" + + "\n" + + " private fun callXposedLoaded(\n" + + " isZygoteLoaded: Boolean = false,\n" + + " lpparam: XC_LoadPackage.LoadPackageParam? = null,\n" + + " resparam: XC_InitPackageResources.InitPackageResourcesParam? = null\n" + + " ) {\n" + + " if (isZygoteBinded.not()) runCatching {\n" + + " hookEntry.onXposedEvent()\n" + + " hookEntry.onInit()\n" + + " if (YukiHookBridge.isXposedCallbackSetUp) {\n" + + " loggerE(tag = \"YukiHookAPI\", msg = \"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" + + " return\n" + " }\n" + - " YukiHookXposedBridge.callXposedInitialized()\n" + - " } catch (e: Throwable) {\n" + - " loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI try to load HookEntryClass failed\", e = e)\n" + - " }\n" + - " if (lpparam.packageName == \"$modulePackageName\") {\n" + + " hookEntry.onHook()\n" + + " YukiHookBridge.callXposedInitialized()\n" + + " YukiHookBridge.modulePackageName = modulePackageName\n" + + " }.onFailure { loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI try to load HookEntryClass failed\", e = it) }\n" + + " YukiHookBridge.callXposedLoaded(isZygoteLoaded, lpparam, resparam)\n" + + " }\n" + + "\n" + + " private fun hookModuleAppStatus(lpparam: XC_LoadPackage.LoadPackageParam? = this.lpparam, isHookResourcesStatus: Boolean = false) {\n" + + " lpparam?.let { this.lpparam = it }\n" + + " if (isHookResourcesStatus.not()) {\n" + " XposedHelpers.findAndHookMethod(\n" + " YukiHookModuleStatus::class.java.name,\n" + - " lpparam.classLoader,\n" + + " this.lpparam?.classLoader,\n" + " \"$IS_ACTIVE_METHOD_NAME\",\n" + " object : XC_MethodReplacement() {\n" + " override fun replaceHookedMethod(param: MethodHookParam?) = true\n" + " })\n" + " XposedHelpers.findAndHookMethod(\n" + " YukiHookModuleStatus::class.java.name,\n" + - " lpparam.classLoader,\n" + + " this.lpparam?.classLoader,\n" + " \"$GET_XPOSED_TAG_METHOD_NAME\",\n" + " object : XC_MethodReplacement() {\n" + - " override fun replaceHookedMethod(param: MethodHookParam?) = try {\n" + - " XposedBridge::class.java.getDeclaredField(\"TAG\").apply { isAccessible = true }.get(null) as String\n" + - " } catch (_: Throwable) {\n" + - " \"invalid\"\n" + - " }\n" + + " override fun replaceHookedMethod(param: MethodHookParam?) = YukiHookBridge.executorName\n" + " })\n" + " XposedHelpers.findAndHookMethod(\n" + " YukiHookModuleStatus::class.java.name,\n" + - " lpparam.classLoader,\n" + + " this.lpparam?.classLoader,\n" + " \"$GET_XPOSED_VERSION_METHOD_NAME\",\n" + " object : XC_MethodReplacement() {\n" + - " override fun replaceHookedMethod(param: MethodHookParam?) = try {\n" + - " XposedBridge.getXposedVersion()\n" + - " } catch (_: Throwable) {\n" + - " -1\n" + - " }\n" + + " override fun replaceHookedMethod(param: MethodHookParam?) = YukiHookBridge.executorVersion\n" + " })\n" + - " YukiHookXposedBridge.isModulePackageXposedEnv = true\n" + - " }\n" + - " YukiHookXposedBridge.modulePackageName = \"$modulePackageName\"\n" + - " YukiHookXposedBridge.callXposedLoaded(lpparam)\n" + + " } else XposedHelpers.findAndHookMethod(\n" + + " YukiHookModuleStatus::class.java.name,\n" + + " this.lpparam?.classLoader,\n" + + " \"$HAS_RESOURCES_HOOK_METHOD_NAME\",\n" + + " object : XC_MethodReplacement() {\n" + + " override fun replaceHookedMethod(param: MethodHookParam?) = true\n" + + " })\n" + + " }\n" + + "\n" + + " @YukiGenerateApi\n" + + " fun callInitZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" + + " if (sparam == null) return\n" + + " runCatching {\n" + + " YukiHookBridge.callXposedZygoteLoaded(sparam)\n" + + " }.onFailure { loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI bind initZygote failed\", e = it) }\n" + + " callXposedLoaded(isZygoteLoaded = true)\n" + + " isZygoteBinded = true\n" + + " }\n" + + "\n" + + " @YukiGenerateApi\n" + + " fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + + " if (lpparam == null) return\n" + + " if (lpparam.packageName == modulePackageName) hookModuleAppStatus(lpparam)\n" + + " callXposedLoaded(lpparam = lpparam)\n" + + " }\n" + + "\n" + + " @YukiGenerateApi\n" + + " fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" + + " if (resparam == null) return\n" + + " if (resparam.packageName == modulePackageName) hookModuleAppStatus(isHookResourcesStatus = true)\n" + + " callXposedLoaded(resparam = resparam)\n" + " }\n" + "}").toByteArray() } \ No newline at end of file