diff --git a/docs-source/src/en/api/public/com/highcapable/yukihookapi/YukiHookAPI.md b/docs-source/src/en/api/public/com/highcapable/yukihookapi/YukiHookAPI.md index c3c7d9b8..96f0a6ac 100644 --- a/docs-source/src/en/api/public/com/highcapable/yukihookapi/YukiHookAPI.md +++ b/docs-source/src/en/api/public/com/highcapable/yukihookapi/YukiHookAPI.md @@ -60,7 +60,7 @@ const val API_VERSION_CODE: Int `v1.0.91` `removed` -请转移到 `Status.executorName` +请转移到 `Status.Executor.name`

executorVersion - field

@@ -70,7 +70,7 @@ const val API_VERSION_CODE: Int `v1.0.91` `removed` -请转移到 `Status.executorVersion` +请转移到 `Status.Executor.apiLevel`、`Status.Executor.versionName`、`Status.Executor.versionCode` ## Status - object @@ -114,49 +114,25 @@ val isXposedEnvironment: Boolean > 获取当前是否为 (Xposed) 宿主环境。 -### executorName - field - -```kotlin:no-line-numbers -val executorName: String -``` +

executorName - field

**Change Records** `v1.0.91` `added` -**Function Illustrate** +`v1.1.5` `deprecated` -> 获取当前 Hook 框架的名称。 +请转移到 `Executor.name` -无法获取会返回 `unknown`,`XposedBridge` 不存在会返回 `invalid`。 - -::: warning - -在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 - -::: - -### executorVersion - field - -```kotlin:no-line-numbers -val executorVersion: Int -``` +

executorVersion - field

**Change Records** `v1.0.91` `added` -**Function Illustrate** +`v1.1.5` `deprecated` -> 获取当前 Hook 框架的版本。 - -无法获取会返回 `-1`。 - -::: warning - -在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 - -::: +请转移到 `Executor.apiLevel`、`Executor.versionName`、`Executor.versionCode` ### isModuleActive - field @@ -250,6 +226,100 @@ val isSupportResourcesHook: Boolean ::: +### Executor - object + +```kotlin:no-line-numbers +object Executor +``` + +**Change Records** + +`v1.1.5` `added` + +**Function Illustrate** + +> 当前 `YukiHookAPI` 使用的 Hook 框架相关信息。 + +#### name - field + +```kotlin:no-line-numbers +val name: String +``` + +**Change Records** + +`v1.1.5` `added` + +**Function Illustrate** + +> 获取当前 Hook 框架的名称。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### apiLevel - field + +```kotlin:no-line-numbers +val apiLevel: Int +``` + +**Change Records** + +`v1.1.5` `added` + +**Function Illustrate** + +> 获取当前 Hook 框架的 API 版本。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### versionName - field + +```kotlin:no-line-numbers +val versionName: String +``` + +**Change Records** + +`v1.1.5` `added` + +**Function Illustrate** + +> 获取当前 Hook 框架的版本名称。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### versionCode - field + +```kotlin:no-line-numbers +val versionCode: Int +``` + +**Change Records** + +`v1.1.5` `added` + +**Function Illustrate** + +> 获取当前 Hook 框架的版本号。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + ## Configs - object ```kotlin:no-line-numbers diff --git a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md index 609b0ca7..498aaed7 100644 --- a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md +++ b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md @@ -18,7 +18,7 @@ You can use the **Chrome Translation Plugin** to translate entire pages for refe **Function Illustrate** -> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 `XposedBridge.log` 打印日志的功能。 +> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 (Xposed) 宿主环境打印日志的功能。 ## LoggerType - class @@ -50,19 +50,29 @@ LOGD > 仅使用 `android.util.Log`。 -### XPOSEDBRIDGE - enum - -```kotlin:no-line-numbers -XPOSEDBRIDGE -``` +

XPOSEDBRIDGE - enum

**Change Records** `v1.1.0` `added` +`v1.1.5` `deprecated` + +请转移到 `XPOSED_ENVIRONMENT` + +### XPOSED_ENVIRONMENT - enum + +```kotlin:no-line-numbers +XPOSED_ENVIRONMENT +``` + +**Change Records** + +`v1.1.5` `added` + **Function Illustrate** -> 仅使用 `XposedBridge.log`。 +> 仅在 (Xposed) 宿主环境使用。 ::: danger @@ -84,7 +94,7 @@ SCOPE > 分区使用。 -(Xposed) 宿主环境仅使用 `XPOSEDBRIDGE`。 +(Xposed) 宿主环境仅使用 `XPOSED_ENVIRONMENT`。 模块环境仅使用 `LOGD`。 @@ -102,7 +112,7 @@ BOTH > 同时使用。 -(Xposed) 宿主环境使用 `LOGD` 与 `XPOSEDBRIDGE`。 +(Xposed) 宿主环境使用 `LOGD` 与 `XPOSED_ENVIRONMENT`。 模块环境仅使用 `LOGD`。 @@ -415,7 +425,7 @@ fun elements(vararg item: Int) > 自定义调试日志对外显示的元素。 -只对日志记录和 `XposedBridge.log` 生效。 +只对日志记录和 (Xposed) 宿主环境的日志生效。 日志元素的排列将按照你在 `item` 中设置的顺序进行显示。 @@ -473,7 +483,7 @@ fun loggerD(tag: String, msg: String, type: LoggerType) **Function Illustrate** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `D`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `D`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -493,7 +503,7 @@ fun loggerI(tag: String, msg: String, type: LoggerType) **Function Illustrate** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `I`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `I`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -513,7 +523,7 @@ fun loggerW(tag: String, msg: String, type: LoggerType) **Function Illustrate** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `W`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `W`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -533,6 +543,6 @@ fun loggerE(tag: String, msg: String, e: Throwable?, type: LoggerType) **Function Illustrate** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `E`,可携带 `e` 异常信息,将打印异常堆栈。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `E`,可携带 `e` 异常信息,将打印异常堆栈。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 \ No newline at end of file diff --git a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/YukiHookAPI.md b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/YukiHookAPI.md index c10bd281..30517fe0 100644 --- a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/YukiHookAPI.md +++ b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/YukiHookAPI.md @@ -52,7 +52,7 @@ const val API_VERSION_CODE: Int `v1.0.91` `移除` -请转移到 `Status.executorName` +请转移到 `Status.Executor.name`

executorVersion - field

@@ -62,7 +62,7 @@ const val API_VERSION_CODE: Int `v1.0.91` `移除` -请转移到 `Status.executorVersion` +请转移到 `Status.Executor.apiLevel`、`Status.Executor.versionName`、`Status.Executor.versionCode` ## Status - object @@ -106,49 +106,25 @@ val isXposedEnvironment: Boolean > 获取当前是否为 (Xposed) 宿主环境。 -### executorName - field - -```kotlin:no-line-numbers -val executorName: String -``` +

executorName - field

**变更记录** `v1.0.91` `新增` -**功能描述** +`v1.1.5` `作废` -> 获取当前 Hook 框架的名称。 +请转移到 `Executor.name` -无法获取会返回 `unknown`,`XposedBridge` 不存在会返回 `invalid`。 - -::: warning - -在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 - -::: - -### executorVersion - field - -```kotlin:no-line-numbers -val executorVersion: Int -``` +

executorVersion - field

**变更记录** `v1.0.91` `新增` -**功能描述** +`v1.1.5` `作废` -> 获取当前 Hook 框架的版本。 - -无法获取会返回 `-1`。 - -::: warning - -在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 - -::: +请转移到 `Executor.apiLevel`、`Executor.versionName`、`Executor.versionCode` ### isModuleActive - field @@ -242,6 +218,100 @@ val isSupportResourcesHook: Boolean ::: +### Executor - object + +```kotlin:no-line-numbers +object Executor +``` + +**变更记录** + +`v1.1.5` `新增` + +**功能描述** + +> 当前 `YukiHookAPI` 使用的 Hook 框架相关信息。 + +#### name - field + +```kotlin:no-line-numbers +val name: String +``` + +**变更记录** + +`v1.1.5` `新增` + +**功能描述** + +> 获取当前 Hook 框架的名称。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### apiLevel - field + +```kotlin:no-line-numbers +val apiLevel: Int +``` + +**变更记录** + +`v1.1.5` `新增` + +**功能描述** + +> 获取当前 Hook 框架的 API 版本。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### versionName - field + +```kotlin:no-line-numbers +val versionName: String +``` + +**变更记录** + +`v1.1.5` `新增` + +**功能描述** + +> 获取当前 Hook 框架的版本名称。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + +#### versionCode - field + +```kotlin:no-line-numbers +val versionCode: Int +``` + +**变更记录** + +`v1.1.5` `新增` + +**功能描述** + +> 获取当前 Hook 框架的版本号。 + +::: warning + +在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。 + +::: + ## Configs - object ```kotlin:no-line-numbers diff --git a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md index 8c2eb1f0..461f9ac2 100644 --- a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md +++ b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/log/LoggerFactory.md @@ -10,7 +10,7 @@ pageClass: code-page **功能描述** -> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 `XposedBridge.log` 打印日志的功能。 +> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 (Xposed) 宿主环境打印日志的功能。 ## LoggerType - class @@ -42,19 +42,29 @@ LOGD > 仅使用 `android.util.Log`。 -### XPOSEDBRIDGE - enum - -```kotlin:no-line-numbers -XPOSEDBRIDGE -``` +

XPOSEDBRIDGE - enum

**变更记录** `v1.1.0` `新增` +`v1.1.5` `作废` + +请转移到 `XPOSED_ENVIRONMENT` + +### XPOSED_ENVIRONMENT - enum + +```kotlin:no-line-numbers +XPOSED_ENVIRONMENT +``` + +**变更记录** + +`v1.1.5` `新增` + **功能描述** -> 仅使用 `XposedBridge.log`。 +> 仅在 (Xposed) 宿主环境使用。 ::: danger @@ -76,7 +86,7 @@ SCOPE > 分区使用。 -(Xposed) 宿主环境仅使用 `XPOSEDBRIDGE`。 +(Xposed) 宿主环境仅使用 `XPOSED_ENVIRONMENT`。 模块环境仅使用 `LOGD`。 @@ -94,7 +104,7 @@ BOTH > 同时使用。 -(Xposed) 宿主环境使用 `LOGD` 与 `XPOSEDBRIDGE`。 +(Xposed) 宿主环境使用 `LOGD` 与 `XPOSED_ENVIRONMENT`。 模块环境仅使用 `LOGD`。 @@ -407,7 +417,7 @@ fun elements(vararg item: Int) > 自定义调试日志对外显示的元素。 -只对日志记录和 `XposedBridge.log` 生效。 +只对日志记录和 (Xposed) 宿主环境的日志生效。 日志元素的排列将按照你在 `item` 中设置的顺序进行显示。 @@ -465,7 +475,7 @@ fun loggerD(tag: String, msg: String, type: LoggerType) **功能描述** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `D`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `D`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -485,7 +495,7 @@ fun loggerI(tag: String, msg: String, type: LoggerType) **功能描述** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `I`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `I`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -505,7 +515,7 @@ fun loggerW(tag: String, msg: String, type: LoggerType) **功能描述** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `W`。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `W`。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 @@ -525,6 +535,6 @@ fun loggerE(tag: String, msg: String, e: Throwable?, type: LoggerType) **功能描述** -> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `E`,可携带 `e` 异常信息,将打印异常堆栈。 +> 向 `Logcat` 和 (Xposed) 宿主环境打印日志,级别 `E`,可携带 `e` 异常信息,将打印异常堆栈。 `tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。 \ No newline at end of file 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 25612577..327bc34b 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 @@ -251,23 +251,23 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { msg = "You set the customize module package name to \"${data.customMPackageName}\", " + "please check for yourself if it is correct" ) + /** 插入 YukiHookAPI_Impl 代码 */ + createCodeFile( + fileName = ClassName.YukiHookAPI_Impl, + packageName = PackageName.YukiHookAPI_Impl, + content = data.sources()[ClassName.YukiHookAPI_Impl] + ) /** 插入 ModuleApplication_Impl 代码 */ createCodeFile( fileName = ClassName.ModuleApplication_Impl, packageName = PackageName.ModuleApplication_Impl, content = data.sources()[ClassName.ModuleApplication_Impl] ) - /** 插入 YukiHookBridge_Impl 代码 */ + /** 插入 YukiXposedModuleStatus_Impl 代码 */ createCodeFile( - fileName = ClassName.YukiHookBridge_Impl, - packageName = PackageName.YukiHookBridge_Impl, - content = data.sources()[ClassName.YukiHookBridge_Impl] - ) - /** 插入 YukiHookModuleStatus_Impl 代码 */ - createCodeFile( - fileName = ClassName.YukiHookModuleStatus_Impl, - packageName = PackageName.YukiHookModuleStatus_Impl, - content = data.sources()[ClassName.YukiHookModuleStatus_Impl] + fileName = ClassName.YukiXposedModuleStatus_Impl, + packageName = PackageName.YukiXposedModuleStatus_Impl, + content = data.sources()[ClassName.YukiXposedModuleStatus_Impl] ) /** 插入 xposed_init 代码 */ createCodeFile( diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/factory/CodeSourceFileFactory.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/factory/CodeSourceFileFactory.kt index 1fee751c..03d3b74b 100644 --- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/factory/CodeSourceFileFactory.kt +++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/factory/CodeSourceFileFactory.kt @@ -35,9 +35,9 @@ import java.util.* * 包名常量定义类 */ object PackageName { + const val YukiHookAPI_Impl = "com.highcapable.yukihookapi" const val ModuleApplication_Impl = "com.highcapable.yukihookapi.hook.xposed.application" - const val YukiHookBridge_Impl = "com.highcapable.yukihookapi.hook.xposed.bridge" - const val YukiHookModuleStatus_Impl = "com.highcapable.yukihookapi.hook.xposed.bridge.status" + const val YukiXposedModuleStatus_Impl = "com.highcapable.yukihookapi.hook.xposed.bridge.status" const val BootstrapReflectionClass = "com.highcapable.yukihookapi.thirdparty.me.weishu.reflection" } @@ -45,9 +45,9 @@ object PackageName { * 类名常量定义类 */ object ClassName { + const val YukiHookAPI_Impl = "YukiHookAPI_Impl" const val ModuleApplication_Impl = "ModuleApplication_Impl" - const val YukiHookBridge_Impl = "YukiHookBridge_Impl" - const val YukiHookModuleStatus_Impl = "YukiHookModuleStatus_Impl" + const val YukiXposedModuleStatus_Impl = "YukiXposedModuleStatus_Impl" const val XposedInit = "xposed_init" const val XposedInit_Impl = "xposed_init_Impl" const val BootstrapClass = "BootstrapClass" @@ -55,13 +55,15 @@ object ClassName { } /** - * YukiHookModuleStatus 方法名称定义类 + * YukiXposedModuleStatus 方法名称定义类 */ -object YukiHookModuleStatusJvmName { +object YukiXposedModuleStatusJvmName { const val IS_ACTIVE_METHOD_NAME = "__--" const val IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME = "_--_" - const val GET_XPOSED_VERSION_METHOD_NAME = "--__" - const val GET_XPOSED_TAG_METHOD_NAME = "_-_-" + const val GET_EXECUTOR_NAME_METHOD_NAME = "_-_-" + const val GET_EXECUTOR_API_LEVEL_METHOD_NAME = "-__-" + const val GET_EXECUTOR_VERSION_NAME_METHOD_NAME = "-_-_" + const val GET_EXECUTOR_VERSION_CODE_METHOD_NAME = "___-" } /** @@ -89,6 +91,15 @@ private fun createCommentContent(entryClassName: String = "", currrentClassTag: * @return [Map]<[String],[String]> */ fun GenerateData.sources() = mapOf( + ClassName.YukiHookAPI_Impl to ("@file:Suppress(\"ClassName\")\n" + + "\n" + + "package ${PackageName.YukiHookAPI_Impl}\n" + + "\n" + + createCommentContent(currrentClassTag = ClassName.YukiHookAPI_Impl) + + "object ${ClassName.YukiHookAPI_Impl} {\n" + + "\n" + + " val compiledTimestamp get() = ${System.currentTimeMillis()}\n" + + "}"), ClassName.ModuleApplication_Impl to ("@file:Suppress(\"ClassName\")\n" + "\n" + "package ${PackageName.ModuleApplication_Impl}\n" + @@ -103,52 +114,56 @@ fun GenerateData.sources() = mapOf( " } catch (_: Throwable) {\n" + " }\n" + "}"), - ClassName.YukiHookBridge_Impl to ("@file:Suppress(\"ClassName\")\n" + + ClassName.YukiXposedModuleStatus_Impl to ("@file:Suppress(\"ClassName\")\n" + "\n" + - "package ${PackageName.YukiHookBridge_Impl}\n" + - "\n" + - createCommentContent(currrentClassTag = ClassName.YukiHookBridge_Impl) + - "object ${ClassName.YukiHookBridge_Impl} {\n" + - "\n" + - " val compiledTimestamp get() = ${System.currentTimeMillis()}\n" + - "}"), - ClassName.YukiHookModuleStatus_Impl to ("@file:Suppress(\"ClassName\")\n" + - "\n" + - "package com.highcapable.yukihookapi.hook.xposed.bridge.status\n" + + "package ${PackageName.YukiXposedModuleStatus_Impl}\n" + "\n" + "import android.util.Log\n" + "import androidx.annotation.Keep\n" + "\n" + - createCommentContent(currrentClassTag = ClassName.YukiHookModuleStatus_Impl) + + createCommentContent(currrentClassTag = ClassName.YukiXposedModuleStatus_Impl) + "@Keep\n" + - "object YukiHookModuleStatus_Impl {\n" + + "object ${ClassName.YukiXposedModuleStatus_Impl} {\n" + "\n" + - " @JvmName(\"${YukiHookModuleStatusJvmName.IS_ACTIVE_METHOD_NAME}\")\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.IS_ACTIVE_METHOD_NAME}\")\n" + " fun isActive(): Boolean {\n" + " placeholderExecution()\n" + " return false\n" + " }\n" + "\n" + - " @JvmName(\"${YukiHookModuleStatusJvmName.IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME}\")\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME}\")\n" + " fun isSupportResourcesHook(): Boolean {\n" + " placeholderExecution()\n" + " return false\n" + " }\n" + "\n" + - " @JvmName(\"${YukiHookModuleStatusJvmName.GET_XPOSED_VERSION_METHOD_NAME}\")\n" + - " fun getXposedVersion(): Int {\n" + - " placeholderExecution()\n" + - " return -1\n" + - " }\n" + - "\n" + - " @JvmName(\"${YukiHookModuleStatusJvmName.GET_XPOSED_TAG_METHOD_NAME}\")\n" + - " fun getXposedBridgeTag(): String {\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.GET_EXECUTOR_NAME_METHOD_NAME}\")\n" + + " fun getExecutorName(): String {\n" + " placeholderExecution()\n" + " return \"unknown\"\n" + " }\n" + "\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.GET_EXECUTOR_API_LEVEL_METHOD_NAME}\")\n" + + " fun getExecutorApiLevel(): Int {\n" + + " placeholderExecution()\n" + + " return -1\n" + + " }\n" + + "\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.GET_EXECUTOR_VERSION_NAME_METHOD_NAME}\")\n" + + " fun getExecutorVersionName(): String {\n" + + " placeholderExecution()\n" + + " return \"unknown\"\n" + + " }\n" + + "\n" + + " @JvmName(\"${YukiXposedModuleStatusJvmName.GET_EXECUTOR_VERSION_CODE_METHOD_NAME}\")\n" + + " fun getExecutorVersionCode(): Int {\n" + + " placeholderExecution()\n" + + " return -1\n" + + " }\n" + + "\n" + " private fun placeholderExecution() {\n" + - " Log.d(\"YukiHookAPI-Placeholder\", \"No trace of hook in this time\")\n" + + " /** Consume a long method body */\n" + + " if (System.currentTimeMillis() == 0L) Log.d(\"${(1000..9999).random()}\", \"${(100000..999999).random()}\")\n" + " }\n" + "}"), ClassName.XposedInit to ("@file:Suppress(\"ClassName\")\n" + @@ -156,7 +171,7 @@ fun GenerateData.sources() = mapOf( "package $entryPackageName\n" + "\n" + "import androidx.annotation.Keep\n" + - "import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.event.caller.YukiXposedEventCaller\n" + "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" + (if (isUsingResourcesHook) "import de.robv.android.xposed.IXposedHookInitPackageResources\n" else "") + "import de.robv.android.xposed.IXposedHookLoadPackage\n" + @@ -172,17 +187,17 @@ fun GenerateData.sources() = mapOf( "\n" + " override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" + " ${entryClassName}_Impl.callInitZygote(sparam)\n" + - " YukiXposedEvent.EventHandler.callInitZygote(sparam)\n" + + " YukiXposedEventCaller.callInitZygote(sparam)\n" + " }\n" + "\n" + " override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + " ${entryClassName}_Impl.callHandleLoadPackage(lpparam)\n" + - " YukiXposedEvent.EventHandler.callHandleLoadPackage(lpparam)\n" + + " YukiXposedEventCaller.callHandleLoadPackage(lpparam)\n" + " }\n" + (if (isUsingResourcesHook) ("\n override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" + " ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" + - " YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" + + " YukiXposedEventCaller.callHandleInitPackageResources(resparam)\n" + " }\n") else "") + "}"), ClassName.XposedInit_Impl to ("@file:Suppress(\"ClassName\")\n" + @@ -190,9 +205,11 @@ fun GenerateData.sources() = mapOf( "package $entryPackageName\n" + "\n" + "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" + - "import com.highcapable.yukihookapi.hook.log.loggerE\n" + - "import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.caller.YukiXposedModuleCaller\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.resources.caller.YukiXposedResourcesCaller\n" + + "import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType\n" + "import de.robv.android.xposed.IXposedHookZygoteInit\n" + + "import de.robv.android.xposed.XposedBridge\n" + "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" + "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" + (if (customMPackageName.isBlank()) "import $modulePackageName.BuildConfig\n" else "") + @@ -203,48 +220,54 @@ fun GenerateData.sources() = mapOf( "\n" + " private const val modulePackageName = " + (if (customMPackageName.isNotBlank()) "\"$customMPackageName\"" else "BuildConfig.APPLICATION_ID") + "\n" + - "\n" + - " private var isZygoteBinded = false\n" + - "\n" + + " private var isZygoteCalled = false\n" + " private val hookEntry = " + (if (isEntryClassKindOfObject) "$entryClassName\n" else "$entryClassName()\n") + "\n" + - " private fun callXposedLoaded(\n" + + " private fun callOnXposedModuleLoaded(\n" + " isZygoteLoaded: Boolean = false,\n" + " lpparam: XC_LoadPackage.LoadPackageParam? = null,\n" + " resparam: XC_InitPackageResources.InitPackageResourcesParam? = null\n" + " ) {\n" + - " if (isZygoteBinded.not()) runCatching {\n" + + " if (isZygoteCalled.not()) runCatching {\n" + " hookEntry.onXposedEvent()\n" + " hookEntry.onInit()\n" + - " if (YukiHookBridge.isXposedCallbackSetUp) {\n" + - " loggerE(msg = \"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" + + " if (YukiXposedModuleCaller.isXposedCallbackSetUp) {\n" + + " YukiXposedModuleCaller.internalLoggerE(\"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" + " return\n" + " }\n" + " hookEntry.onHook()\n" + - " YukiHookBridge.callXposedInitialized()\n" + - " YukiHookBridge.modulePackageName = modulePackageName\n" + - " }.onFailure { loggerE(msg = \"YukiHookAPI try to load HookEntryClass failed\", e = it) }\n" + - " YukiHookBridge.callXposedLoaded(isZygoteLoaded, lpparam, resparam)\n" + + " YukiXposedModuleCaller.callOnFinishLoadModule()\n" + + " }.onFailure { YukiXposedModuleCaller.internalLoggerE(\"YukiHookAPI try to load HookEntryClass failed\", it) }\n" + + " YukiXposedModuleCaller.callOnPackageLoaded(\n" + + " type = when {\n" + + " isZygoteLoaded -> HookEntryType.ZYGOTE\n" + + " lpparam != null -> HookEntryType.PACKAGE\n" + + " resparam != null -> HookEntryType.RESOURCES\n" + + " else -> HookEntryType.ZYGOTE\n" + + " },\n" + + " packageName = lpparam?.packageName ?: resparam?.packageName,\n" + + " processName = lpparam?.processName,\n" + + " appClassLoader = lpparam?.classLoader ?: runCatching { XposedBridge.BOOTCLASSLOADER }.getOrNull(),\n" + + " appInfo = lpparam?.appInfo,\n" + + " appResources = YukiXposedResourcesCaller.createYukiResourcesFromXResources(resparam?.res)\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(msg = \"YukiHookAPI bind initZygote failed\", e = it) }\n" + - " callXposedLoaded(isZygoteLoaded = true)\n" + - " isZygoteBinded = true\n" + + " YukiXposedModuleCaller.callOnStartLoadModule(modulePackageName, sparam.modulePath)\n" + + " callOnXposedModuleLoaded(isZygoteLoaded = true)\n" + + " isZygoteCalled = true\n" + + " }.onFailure { YukiXposedModuleCaller.internalLoggerE(\"An exception occurred when YukiHookAPI loading Xposed Module\", it) }\n" + " }\n" + "\n" + - " @YukiGenerateApi\n" + " fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + - " if (lpparam != null) callXposedLoaded(lpparam = lpparam)\n" + + " if (lpparam != null && isZygoteCalled) callOnXposedModuleLoaded(lpparam = lpparam)\n" + " }\n" + "\n" + - " @YukiGenerateApi\n" + " fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" + - " if (resparam != null) callXposedLoaded(resparam = resparam)\n" + + " if (resparam != null && isZygoteCalled) callOnXposedModuleLoaded(resparam = resparam)\n" + " }\n" + "}"), ClassName.BootstrapClass to ("package ${PackageName.BootstrapReflectionClass};\n" + @@ -263,7 +286,6 @@ fun GenerateData.sources() = mapOf( "public final class BootstrapClass {\n" + "\n" + " private static final String TAG = \"BootstrapClass\";\n" + - "\n" + " private static Object sVmRuntime;\n" + " private static Method setHiddenApiExemptions;\n" + "\n" + @@ -290,7 +312,6 @@ fun GenerateData.sources() = mapOf( " if (sVmRuntime == null || setHiddenApiExemptions == null) {\n" + " return false;\n" + " }\n" + - "\n" + " try {\n" + " setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods});\n" + " return true;\n" + @@ -323,8 +344,8 @@ fun GenerateData.sources() = mapOf( createCommentContent(currrentClassTag = ClassName.Reflection) + "@Keep\n" + "public class Reflection {\n" + - " private static final String TAG = \"Reflection\";\n" + "\n" + + " private static final String TAG = \"Reflection\";\n" + " private static final String DEX = \"ZGV4CjAzNQCXDT0vQ44GJqsrjm32y0qlQmxUevbk56r0CwAAcAAAAHhWNBIAAAAAAAAAADwLAABDAAAAcAAAABMAAAB8AQAACwAAAMgBAAAMAAAATAIAAA8AAACsAgAAAwAAACQDAABwCAAAhAMAAIQDAACGAwAAiwMAAJUDAACdAwAArQMAALkDAADJAwAA3gMAAPADAAD3AwAA/wMAAAIEAAAGBAAACgQAABAEAAATBAAAGAQAADMEAABZBAAAdQQAAIkEAADYBAAAJgUAAHAFAACDBQAAmQUAAK0FAADBBQAA1QUAAOwFAAAIBgAAFAYAACUGAAAuBgAAMwYAADYGAABEBgAAUgYAAFYGAABZBgAAXQYAAHEGAACGBgAAmwYAAKQGAAC9BgAAwAYAAMgGAADTBgAA3AYAAO0GAAABBwAAFAcAACAHAAAoBwAANQcAAE8HAABXBwAAYAcAAHsHAACEBwAAkAcAAKgHAAC6BwAAwgcAANAHAAALAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAaAAAAGwAAABwAAAAdAAAAHgAAACMAAAAnAAAAKQAAACoAAAArAAAADAAAAAAAAAD4BwAADQAAAAAAAAAMCAAADgAAAAAAAAAACAAADwAAAAIAAAAAAAAAEAAAAAkAAAAUCAAAEAAAAA0AAADoBwAAIwAAAA4AAAAAAAAAJgAAAA4AAADgBwAAJwAAAA8AAAAAAAAAKAAAAA8AAADgBwAAKAAAAA8AAADwBwAAAgAAAD8AAAADAAAAIQAAAAUACgAEAAAABQAKAAUAAAAFAA8ACQAAAAUACgAKAAAABQAAACQAAAAFAAoAJQAAAAYACgAiAAAABgAJAD0AAAAGAA0APgAAAAcACgAiAAAAAQADADMAAAAEAAIALgAAAAUABgADAAAABgAGAAIAAAAGAAYAAwAAAAYACQAvAAAABgAKAC8AAAAGAAgAMAAAAAcABgADAAAABwABAEAAAAAHAAAAQQAAAAgABQA0AAAACQAGAAMAAAALAAcANwAAAA0ABAA2AAAABQAAABEAAAAJAAAAAAAAAAgAAAAAAAAA7AoAAB8IAAAGAAAAEQAAAAkAAAAAAAAABwAAAAAAAAACCwAAHAgAAAcAAAABAAAACQAAAAAAAAAgAAAAAAAAACULAAArCAAAAAADMS4wAAg8Y2xpbml0PgAGPGluaXQ+AA5BUFBMSUNBVElPTl9JRAAKQlVJTERfVFlQRQAOQm9vdHN0cmFwQ2xhc3MAE0Jvb3RzdHJhcENsYXNzLmphdmEAEEJ1aWxkQ29uZmlnLmphdmEABURFQlVHAAZGTEFWT1IAAUkAAklJAAJJTAAESUxMTAABTAADTExMABlMYW5kcm9pZC9jb250ZW50L0NvbnRleHQ7ACRMYW5kcm9pZC9jb250ZW50L3BtL0FwcGxpY2F0aW9uSW5mbzsAGkxhbmRyb2lkL29zL0J1aWxkJFZFUlNJT047ABJMYW5kcm9pZC91dGlsL0xvZzsATUxjb20vaGlnaGNhcGFibGUveXVraWhvb2thcGkvdGhpcmRwYXJ0eS9tZS93ZWlzaHUvZnJlZXJlZmxlY3Rpb24vQnVpbGRDb25maWc7AExMY29tL2hpZ2hjYXBhYmxlL3l1a2lob29rYXBpL3RoaXJkcGFydHkvbWUvd2Vpc2h1L3JlZmxlY3Rpb24vQm9vdHN0cmFwQ2xhc3M7AEhMY29tL2hpZ2hjYXBhYmxlL3l1a2lob29rYXBpL3RoaXJkcGFydHkvbWUvd2Vpc2h1L3JlZmxlY3Rpb24vUmVmbGVjdGlvbjsAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwAKUmVmbGVjdGlvbgAPUmVmbGVjdGlvbi5qYXZhAAdTREtfSU5UAANUQUcAAVYADFZFUlNJT05fQ09ERQAMVkVSU0lPTl9OQU1FAAJWTAABWgACWkwAEltMamF2YS9sYW5nL0NsYXNzOwATW0xqYXZhL2xhbmcvT2JqZWN0OwATW0xqYXZhL2xhbmcvU3RyaW5nOwAHY29udGV4dAAXZGFsdmlrLnN5c3RlbS5WTVJ1bnRpbWUAAWUABmV4ZW1wdAAJZXhlbXB0QWxsAAdmb3JOYW1lAA9mcmVlLXJlZmxlY3Rpb24AEmdldEFwcGxpY2F0aW9uSW5mbwARZ2V0RGVjbGFyZWRNZXRob2QACmdldFJ1bnRpbWUABmludm9rZQALbG9hZExpYnJhcnkAGG1lLndlaXNodS5mcmVlcmVmbGVjdGlvbgAGbWV0aG9kAAdtZXRob2RzABlyZWZsZWN0IGJvb3RzdHJhcCBmYWlsZWQ6AAdyZWxlYXNlAApzVm1SdW50aW1lABZzZXRIaWRkZW5BcGlFeGVtcHRpb25zABB0YXJnZXRTZGtWZXJzaW9uAAZ1bnNlYWwADHVuc2VhbE5hdGl2ZQAOdm1SdW50aW1lQ2xhc3MAAQAAAAoAAAACAAAACgAQAAEAAAASAAAAAQAAAAAAAAADAAAACgAKAAwAAAABAAAAAQAAAAIAAAAJABEAARcGBhc4FzwfFwAEARcBARcfAAAAAAAABgAHDgAWAAcOav8DATIOARUQAwI1DvAEBEMJGgESDwMDNg4BGw+pBQIFAwUEGR4DAC8NAA4ABw4ALAE6Bw4ANgE7ByydGuIBAQMALw0eAEgABw4ADQAHDgATAS0HHXIZa1oAAAEAAQABAAAANAgAAAQAAABwEAwAAAAOAAoAAAADAAEAOQgAAHsAAABgBQEAEwYcADRlbQAcBQgAGgYxABIXI3cQABIIHAkKAE0JBwhuMAsAZQcMARwFCAAaBjQAEicjdxAAEggcCQoATQkHCBIYHAkQAE0JBwhuMAsAZQcMAhIFEhYjZhEAEgcaCC0ATQgGB24wDgBRBgwEHwQIABIlI1URABIGGgc1AE0HBQYSFhIHTQcFBm4wDgBCBQwDHwMNABIlI1URABIGGgc+AE0HBQYSFhIXI3cQABIIHAkSAE0JBwhNBwUGbjAOAEIFDAUfBQ0AaQUKABIFEgYjZhEAbjAOAFMGDAVpBQkADgANABoFBgAaBjsAcTABAGUAKPcAAAYAAABrAAEAAQEMcgEAAQABAAAAaAgAAAQAAABwEAwAAAAOAAMAAQABAAAAbQgAAAsAAAASECMAEgASAU0CAAFxEAYAAAAKAA8AAAAIAAEAAwABAHMIAAAdAAAAEhESAmIDCQA4AwYAYgMKADkDBAABIQ8BYgMKAGIECQASFSNVEQASBk0HBQZuMA4AQwUo8g0AASEo7wAADAAAAA0AAQABAQwaAwAAAAEAAACDCAAADQAAABIQIwASABIBGgIPAE0CAAFxEAYAAAAKAA8AAAABAAEAAQAAAIgIAAAEAAAAcBAMAAAADgAEAAEAAQAAAI0IAAAeAAAAEgBgAQEAEwIcADUhAwAPAHEABwAAAAoBOQH7/xoAMgBxEA0AAABuEAAAAwAMAFIAAABxEAoAAAAKACjqBgABAAIZARkBGQEZARkBGQKBgASYEQMABQAIGgEKAQoDiIAEsBEBgYAExBMBCdwTAYkBhBQBCdwUAQADAAsaCIGABIgVAQmgFQGKAgAAAAAPAAAAAAAAAAEAAAAAAAAAAQAAAEMAAABwAAAAAgAAABMAAAB8AQAAAwAAAAsAAADIAQAABAAAAAwAAABMAgAABQAAAA8AAACsAgAABgAAAAMAAAAkAwAAAiAAAEMAAACEAwAAARAAAAcAAADgBwAABSAAAAMAAAAcCAAAAxAAAAEAAAAwCAAAAyAAAAgAAAA0CAAAASAAAAgAAACYCAAAACAAAAMAAADsCgAAABAAAAEAAAA8CwAA\";\n" + "\n" + " private static native int unsealNative(int targetSdkVersion);\n" + @@ -334,7 +355,6 @@ fun GenerateData.sources() = mapOf( " // Below Android P, ignore\n" + " return 0;\n" + " }\n" + - "\n" + " // try exempt API first.\n" + " if (exemptAll()) {\n" + " return 0;\n" + @@ -342,7 +362,6 @@ fun GenerateData.sources() = mapOf( " if (unsealByDexFile(context)) {\n" + " return 0;\n" + " }\n" + - "\n" + " return -1;\n" + " }\n" + "\n" + @@ -355,11 +374,9 @@ fun GenerateData.sources() = mapOf( " }\n" + " File code = new File(codeCacheDir, \"__temp_\" + System.currentTimeMillis() + \".dex\");\n" + " try {\n" + - "\n" + " try (FileOutputStream fos = new FileOutputStream(code)) {\n" + " fos.write(bytes);\n" + " }\n" + - "\n" + " DexFile dexFile = new DexFile(code);\n" + " // This class is hardcoded in the dex, Don't use BootstrapClass.class to reference it\n" + " // it maybe obfuscated!!\n" + diff --git a/yukihookapi-stub/build.gradle b/yukihookapi-stub/build.gradle index ff154fb9..cdb7fb5f 100644 --- a/yukihookapi-stub/build.gradle +++ b/yukihookapi-stub/build.gradle @@ -8,8 +8,17 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + kotlinOptions { + jvmTarget = 11 + freeCompilerArgs = [ + '-Xno-param-assertions', + '-Xno-call-assertions', + '-Xno-receiver-assertions' + ] + } +} + dependencies { - // Used 82 API Version - compileOnly 'de.robv.android.xposed:api:82' compileOnly fileTree(include: ['*.jar'], dir: '../yukihookapi/libs') } \ No newline at end of file diff --git a/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge_Impl.kt b/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/YukiHookAPI_Impl.kt similarity index 91% rename from yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge_Impl.kt rename to yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/YukiHookAPI_Impl.kt index 6b0d6cb2..6bbfc9ce 100644 --- a/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge_Impl.kt +++ b/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/YukiHookAPI_Impl.kt @@ -24,15 +24,16 @@ * SOFTWARE. * * This file is Created by fankes on 2022/9/26. + * This file is Modified by fankes on 2023/1/9. */ @file:Suppress("ClassName") -package com.highcapable.yukihookapi.hook.xposed.bridge +package com.highcapable.yukihookapi /** - * YukiHookBridge 注入 Stub + * YukiHookAPI 注入 Stub */ -object YukiHookBridge_Impl { +object YukiHookAPI_Impl { /** * 获取项目编译完成的时间戳 (当前本地时间) diff --git a/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus_Impl.kt b/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus_Impl.kt similarity index 64% rename from yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus_Impl.kt rename to yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus_Impl.kt index f87912ac..74bb794c 100644 --- a/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus_Impl.kt +++ b/yukihookapi-stub/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus_Impl.kt @@ -24,29 +24,23 @@ * SOFTWARE. * * This file is Created by fankes on 2023/1/1. + * This file is Modified by fankes on 2023/1/9. */ @file:Suppress("ClassName") package com.highcapable.yukihookapi.hook.xposed.bridge.status -import de.robv.android.xposed.XposedBridge - /** - * YukiHookModuleStatus 注入 Stub + * YukiXposedModuleStatus 注入 Stub */ -object YukiHookModuleStatus_Impl { +object YukiXposedModuleStatus_Impl { - /** 定义 Jvm 方法名 */ private const val IS_ACTIVE_METHOD_NAME = "__--" - - /** 定义 Jvm 方法名 */ private const val IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME = "_--_" - - /** 定义 Jvm 方法名 */ - private const val GET_XPOSED_VERSION_METHOD_NAME = "--__" - - /** 定义 Jvm 方法名 */ - private const val GET_XPOSED_TAG_METHOD_NAME = "_-_-" + private const val GET_EXECUTOR_NAME_METHOD_NAME = "_-_-" + private const val GET_EXECUTOR_API_LEVEL_METHOD_NAME = "-__-" + private const val GET_EXECUTOR_VERSION_NAME_METHOD_NAME = "-_-_" + private const val GET_EXECUTOR_VERSION_CODE_METHOD_NAME = "___-" /** * 此方法经过 Hook 后返回 true 即模块已激活 @@ -67,20 +61,38 @@ object YukiHookModuleStatus_Impl { fun isSupportResourcesHook(): Boolean = error("Stub!") /** - * 此方法经过 Hook 后返回 [XposedBridge.getXposedVersion] - * - * 返回值将在每次编译时自动生成 - * @return [Int] - */ - @JvmName(GET_XPOSED_VERSION_METHOD_NAME) - fun getXposedVersion(): Int = error("Stub!") - - /** - * 此方法经过 Hook 后返回 [XposedBridge] 的 TAG + * 此方法经过 Hook 后返回当前 Hook 框架的名称 * * 返回值将在每次编译时自动生成 * @return [String] */ - @JvmName(GET_XPOSED_TAG_METHOD_NAME) - fun getXposedBridgeTag(): String = error("Stub!") + @JvmName(GET_EXECUTOR_NAME_METHOD_NAME) + fun getExecutorName(): String = error("Stub!") + + /** + * 此方法经过 Hook 后返回当前 Hook 框架的 API 版本 + * + * 返回值将在每次编译时自动生成 + * @return [Int] + */ + @JvmName(GET_EXECUTOR_API_LEVEL_METHOD_NAME) + fun getExecutorApiLevel(): Int = error("Stub!") + + /** + * 此方法经过 Hook 后返回当前 Hook 框架的版本名称 + * + * 返回值将在每次编译时自动生成 + * @return [String] + */ + @JvmName(GET_EXECUTOR_VERSION_NAME_METHOD_NAME) + fun getExecutorVersionName(): String = error("Stub!") + + /** + * 此方法经过 Hook 后返回当前 Hook 框架的版本号 + * + * 返回值将在每次编译时自动生成 + * @return [Int] + */ + @JvmName(GET_EXECUTOR_VERSION_CODE_METHOD_NAME) + fun getExecutorVersionCode(): Int = error("Stub!") } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index 72206adf..08604082 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -33,9 +33,12 @@ import android.app.Application import android.content.Context import android.content.SharedPreferences import android.content.res.Resources +import com.highcapable.yukihookapi.YukiHookAPI.Configs.debugLog 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.api.compat.HookApiCategoryHelper +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiProperty import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder import com.highcapable.yukihookapi.hook.core.finder.members.FieldFinder import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder @@ -47,14 +50,13 @@ import com.highcapable.yukihookapi.hook.log.YukiHookLogger import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerI import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.param.type.HookEntryType import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule +import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiXposedModuleStatus +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs -import de.robv.android.xposed.XposedBridge import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Member @@ -65,7 +67,7 @@ import java.lang.reflect.Method * * 可以实现作为模块装载和自定义 Hook 装载两种方式 * - * 模块装载方式已经自动对接 Xposed API - 可直接调用 [encase] 完成操作 + * Xposed 模块装载方式已经自动对接相关 API - 可直接调用 [encase] 完成操作 * * 你可以调用 [configs] 对 [YukiHookAPI] 进行配置 */ @@ -92,38 +94,41 @@ object YukiHookAPI { * 获取项目编译完成的时间戳 (当前本地时间) * @return [Long] */ - val compiledTimestamp get() = YukiHookBridge.compiledTimestamp + val compiledTimestamp get() = runCatching { YukiHookAPI_Impl.compiledTimestamp }.getOrNull() ?: 0L /** * 获取当前是否为 (Xposed) 宿主环境 * @return [Boolean] */ - val isXposedEnvironment get() = YukiHookBridge.hasXposedBridge + val isXposedEnvironment get() = YukiXposedModule.isXposedEnvironment /** * 获取当前 Hook 框架的名称 * - * 从 [XposedBridge] 获取 TAG + * - ❗此方法已弃用 - 在之后的版本中将直接被删除 * - * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] - * @return [String] 无法获取会返回 unknown - [YukiHookBridge.hasXposedBridge] 不存在会返回 invalid + * - ❗请现在转移到 [Executor.name] + * @return [String] */ - val executorName - get() = YukiHookBridge.executorName.takeIf { isXposedEnvironment } ?: when { - isXposedModuleActive -> YukiHookModuleStatus.executorName - isTaiChiModuleActive -> YukiHookModuleStatus.TAICHI_XPOSED_NAME - else -> YukiHookModuleStatus.executorName - } + @Deprecated( + message = "请使用新方式来实现此功能", + ReplaceWith("Executor.name", "com.highcapable.yukihookapi.YukiHookAPI.Status.Executor") + ) + val executorName get() = Executor.name /** * 获取当前 Hook 框架的版本 * - * 获取 [XposedBridge.getXposedVersion] + * - ❗此方法已弃用 - 在之后的版本中将直接被删除 * - * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] - * @return [Int] 无法获取会返回 -1 + * - ❗请现在转移到 [Executor.apiLevel]、[Executor.versionName]、[Executor.versionCode] + * @return [Int] */ - val executorVersion get() = YukiHookBridge.executorVersion.takeIf { isXposedEnvironment } ?: YukiHookModuleStatus.executorVersion + @Deprecated( + message = "请使用新方式来实现此功能", + ReplaceWith("Executor.apiLevel", "com.highcapable.yukihookapi.YukiHookAPI.Status.Executor") + ) + val executorVersion get() = Executor.apiLevel /** * 判断模块是否在 Xposed 或太极、无极中激活 @@ -135,7 +140,7 @@ object YukiHookAPI { * - ❗在 (Xposed) 宿主环境中仅返回非 [isTaiChiModuleActive] 的激活状态 * @return [Boolean] 是否激活 */ - val isModuleActive get() = isXposedEnvironment || YukiHookModuleStatus.isActive || isTaiChiModuleActive + val isModuleActive get() = isXposedEnvironment || YukiXposedModuleStatus.isActive || isTaiChiModuleActive /** * 仅判断模块是否在 Xposed 中激活 @@ -145,7 +150,7 @@ object YukiHookAPI { * - ❗在 (Xposed) 宿主环境中始终返回 true * @return [Boolean] 是否激活 */ - val isXposedModuleActive get() = isXposedEnvironment || YukiHookModuleStatus.isActive + val isXposedModuleActive get() = isXposedEnvironment || YukiXposedModuleStatus.isActive /** * 仅判断模块是否在太极、无极中激活 @@ -168,7 +173,50 @@ object YukiHookAPI { * @return [Boolean] 是否支持 */ val isSupportResourcesHook - get() = YukiHookBridge.isSupportResourcesHook.takeIf { isXposedEnvironment } ?: YukiHookModuleStatus.isSupportResourcesHook + get() = YukiXposedModule.isSupportResourcesHook.takeIf { isXposedEnvironment } ?: YukiXposedModuleStatus.isSupportResourcesHook + + /** + * 当前 [YukiHookAPI] 使用的 Hook 框架相关信息 + */ + object Executor { + + /** + * 获取当前 Hook 框架的名称 + * + * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] + * @return [String] 无法获取会返回 unknown - 获取失败会返回 invalid + */ + val name + get() = HookApiProperty.name.takeIf { isXposedEnvironment } ?: when { + isXposedModuleActive -> YukiXposedModuleStatus.executorName + isTaiChiModuleActive -> HookApiProperty.TAICHI_XPOSED_NAME + else -> YukiXposedModuleStatus.executorName + } + + /** + * 获取当前 Hook 框架的 API 版本 + * + * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] + * @return [Int] 无法获取会返回 -1 + */ + val apiLevel get() = HookApiProperty.apiLevel.takeIf { isXposedEnvironment } ?: YukiXposedModuleStatus.executorApiLevel + + /** + * 获取当前 Hook 框架的版本名称 + * + * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] + * @return [String] 无法获取会返回 unknown - 不支持会返回 unsupported + */ + val versionName get() = HookApiProperty.versionName.takeIf { isXposedEnvironment } ?: YukiXposedModuleStatus.executorVersionName + + /** + * 获取当前 Hook 框架的版本号 + * + * - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus] + * @return [Int] 无法获取会返回 -1 - 不支持会返回 0 + */ + val versionCode get() = HookApiProperty.versionCode.takeIf { isXposedEnvironment } ?: YukiXposedModuleStatus.executorVersionCode + } } /** @@ -296,13 +344,6 @@ object YukiHookAPI { internal fun build() = Unit } - /** - * 装载 Xposed API 回调核心实现方法 - * @param wrapper 代理包装 [PackageParamWrapper] - */ - internal fun onXposedLoaded(wrapper: PackageParamWrapper) = - YukiHookBridge.packageParamCallback?.invoke(PackageParam(wrapper).apply { printSplashLog() }) - /** * 配置 [YukiHookAPI] 相关参数 * @@ -314,7 +355,7 @@ object YukiHookAPI { inline fun configs(initiate: Configs.() -> Unit) = Configs.apply(initiate).build() /** - * 作为模块装载调用入口方法 - Xposed API + * 作为 Xposed 模块装载调用入口方法 * * 用法请参考 [API 文档](https://fankes.github.io/YukiHookAPI/zh-cn/api/home) * @@ -327,13 +368,13 @@ object YukiHookAPI { */ fun encase(initiate: PackageParam.() -> Unit) { isLoadedFromBaseContext = false - if (YukiHookBridge.hasXposedBridge) - YukiHookBridge.packageParamCallback = initiate - else printNoXposedEnvLog() + if (YukiXposedModule.isXposedEnvironment) + YukiXposedModule.packageParamCallback = initiate + else printNotFoundHookApiError() } /** - * 作为模块装载调用入口方法 - Xposed API + * 作为 Xposed 模块装载调用入口方法 * * 用法请参考 [API 文档](https://fankes.github.io/YukiHookAPI/zh-cn/api/home) * @@ -347,13 +388,13 @@ object YukiHookAPI { */ fun encase(vararg hooker: YukiBaseHooker) { isLoadedFromBaseContext = false - if (YukiHookBridge.hasXposedBridge) - YukiHookBridge.packageParamCallback = { + if (YukiXposedModule.isXposedEnvironment) + YukiXposedModule.packageParamCallback = { if (hooker.isNotEmpty()) hooker.forEach { it.assignInstance(packageParam = this) } else yLoggerE(msg = "Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true) } - else printNoXposedEnvLog() + else printNotFoundHookApiError() } /** @@ -378,8 +419,9 @@ object YukiHookAPI { fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit) { isLoadedFromBaseContext = true when { - YukiHookBridge.hasXposedBridge && baseContext != null -> initiate(baseContext.packageParam.apply { printSplashLog() }) - else -> printNoXposedEnvLog() + HookApiCategoryHelper.hasAvailableHookApi && baseContext != null -> + initiate(baseContext.createPackageParam().apply { printSplashInfo() }) + else -> printNotFoundHookApiError() } } @@ -405,32 +447,33 @@ object YukiHookAPI { */ fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker) { isLoadedFromBaseContext = true - if (YukiHookBridge.hasXposedBridge) + if (HookApiCategoryHelper.hasAvailableHookApi) (if (baseContext != null) if (hooker.isNotEmpty()) { - printSplashLog() - hooker.forEach { it.assignInstance(packageParam = baseContext.packageParam) } + printSplashInfo() + hooker.forEach { it.assignInstance(packageParam = baseContext.createPackageParam()) } } else yLoggerE(msg = "Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true)) - else printNoXposedEnvLog() + else printNotFoundHookApiError() } /** 输出欢迎信息调试日志 */ - private fun printSplashLog() { + internal fun printSplashInfo() { if (Configs.isDebug.not() || isShowSplashLogOnceTime.not()) return isShowSplashLogOnceTime = false yLoggerI( - msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using ${Status.executorName} API ${Status.executorVersion}", + msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using ${Status.Executor.name} API ${Status.Executor.apiLevel}", isImplicit = true ) } - /** 输出找不到 [XposedBridge] 的错误日志 */ - private fun printNoXposedEnvLog() = yLoggerE(msg = "Could not found XposedBridge in current space! Aborted", isImplicit = true) + /** 输出找不到 Hook API 的错误日志 */ + private fun printNotFoundHookApiError() = + yLoggerE(msg = "Could not found any available Hook APIs in current environment! Aborted", isImplicit = true) /** * 通过 baseContext 创建 Hook 入口类 * @return [PackageParam] */ - private val Context.packageParam - get() = PackageParam(PackageParamWrapper(HookEntryType.PACKAGE, packageName, processName, classLoader, applicationInfo)) + private fun Context.createPackageParam() = + PackageParam(PackageParamWrapper(HookEntryType.PACKAGE, packageName, processName, classLoader, applicationInfo)) } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookResources.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookResources.kt index 649266aa..ef0fc508 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookResources.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/bean/HookResources.kt @@ -27,7 +27,7 @@ */ package com.highcapable.yukihookapi.hook.bean -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources /** * 创建一个当前 Hook 的 [YukiResources] 接管类 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator.kt index f71c2aac..84e632f4 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator.kt @@ -32,6 +32,12 @@ package com.highcapable.yukihookapi.hook.core import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.CauseProblemsApi import com.highcapable.yukihookapi.hook.bean.HookClass +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper +import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper +import com.highcapable.yukihookapi.hook.core.api.priority.YukiHookPriority +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberReplacement +import com.highcapable.yukihookapi.hook.core.api.result.YukiHookResult import com.highcapable.yukihookapi.hook.core.finder.base.MemberBaseFinder import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder import com.highcapable.yukihookapi.hook.core.finder.members.FieldFinder @@ -45,14 +51,9 @@ import com.highcapable.yukihookapi.hook.log.yLoggerI import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.param.HookParam import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.param.type.HookEntryType import com.highcapable.yukihookapi.hook.type.java.* import com.highcapable.yukihookapi.hook.utils.await -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookPriority -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Member @@ -71,13 +72,13 @@ class YukiMemberHookCreator @PublishedApi internal constructor( ) { /** 默认 Hook 回调优先级 */ - val PRIORITY_DEFAULT = YukiHookPriority.PRIORITY_DEFAULT + val PRIORITY_DEFAULT = 0x0 /** 延迟回调 Hook 方法结果 */ - val PRIORITY_LOWEST = YukiHookPriority.PRIORITY_LOWEST + val PRIORITY_LOWEST = 0x1 /** 更快回调 Hook 方法结果 */ - val PRIORITY_HIGHEST = YukiHookPriority.PRIORITY_HIGHEST + val PRIORITY_HIGHEST = 0x2 /** Hook 操作选项内容 */ private var hookOption = "" @@ -134,7 +135,7 @@ class YukiMemberHookCreator @PublishedApi internal constructor( */ @PublishedApi internal fun hook() = when { - YukiHookBridge.hasXposedBridge.not() -> Result() + HookApiCategoryHelper.hasAvailableHookApi.not() -> Result() /** 过滤 [HookEntryType.ZYGOTE] and [HookEntryType.PACKAGE] or [HookParam.isCallbackCalled] 已被执行 */ packageParam.wrapper?.type == HookEntryType.RESOURCES && HookParam.isCallbackCalled.not() -> Result() preHookMembers.isEmpty() -> Result().also { yLoggerW(msg = "Hook Members is empty in [${hookClass.name}], hook aborted") } @@ -192,6 +193,18 @@ class YukiMemberHookCreator @PublishedApi internal constructor( } } + /** + * 转换到 [YukiHookPriority] 优先级 + * @return [YukiHookPriority] + * @throws IllegalStateException 如果优先级不为 [PRIORITY_DEFAULT]、[PRIORITY_LOWEST]、[PRIORITY_HIGHEST] + */ + private fun Int.toPriority() = when (this) { + PRIORITY_DEFAULT -> YukiHookPriority.DEFAULT + PRIORITY_LOWEST -> YukiHookPriority.LOWEST + PRIORITY_HIGHEST -> YukiHookPriority.HIGHEST + else -> error("Invalid Hook Priority $this") + } + /** * Hook 核心功能实现类 * @@ -257,7 +270,7 @@ class YukiMemberHookCreator @PublishedApi internal constructor( internal var finder: MemberBaseFinder? = null /** 当前被 Hook 的 [Method]、[Constructor] 实例数组 */ - private val memberUnhooks = HashSet() + private val hookedMembers = HashSet() /** 当前需要 Hook 的 [Method]、[Constructor] */ internal val members = HashSet() @@ -509,7 +522,7 @@ class YukiMemberHookCreator @PublishedApi internal constructor( /** Hook 执行入口 */ @PublishedApi internal fun hook() { - if (YukiHookBridge.hasXposedBridge.not() || isHooked || isDisableMemberRunHook) return + if (HookApiCategoryHelper.hasAvailableHookApi.not() || isHooked || isDisableMemberRunHook) return isHooked = true finder?.printLogIfExist() if (hookClass.instance == null) { @@ -524,11 +537,11 @@ class YukiMemberHookCreator @PublishedApi internal constructor( runCatching { member.hook().also { when { - it.first?.member == null -> error("Hook Member [$member] failed") - it.second -> onAlreadyHookedCallback?.invoke(it.first?.member!!) - else -> it.first?.also { e -> - memberUnhooks.add(e) - onHookedCallback?.invoke(e.member!!) + it.hookedMember?.member == null -> error("Hook Member [$member] failed") + it.isAlreadyHooked -> onAlreadyHookedCallback?.invoke(it.hookedMember.member!!) + else -> { + hookedMembers.add(it.hookedMember) + onHookedCallback?.invoke(it.hookedMember.member!!) } } } @@ -552,14 +565,14 @@ class YukiMemberHookCreator @PublishedApi internal constructor( /** * Hook [Method]、[Constructor] - * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) + * @return [YukiHookResult] */ - private fun Member.hook(): Pair { + private fun Member.hook(): YukiHookResult { /** 定义替换 Hook 的 [HookParam] */ val replaceHookParam = HookParam(creatorInstance = this@YukiMemberHookCreator) /** 定义替换 Hook 回调方法体 */ - val replaceMent = object : YukiMemberReplacement(priority) { + val replaceMent = object : YukiMemberReplacement(priority.toPriority()) { override fun replaceHookedMember(param: Param) = replaceHookParam.assign(param).let { assign -> runCatching { @@ -585,7 +598,7 @@ class YukiMemberHookCreator @PublishedApi internal constructor( val afterHookParam = HookParam(creatorInstance = this@YukiMemberHookCreator) /** 定义前后 Hook 回调方法体 */ - val beforeAfterHook = object : YukiMemberHook(priority) { + val beforeAfterHook = object : YukiMemberHook(priority.toPriority()) { override fun beforeHookedMember(param: Param) { beforeHookParam.assign(param).also { assign -> runCatching { @@ -803,7 +816,7 @@ class YukiMemberHookCreator @PublishedApi internal constructor( * @param result 回调是否成功 */ fun remove(result: (Boolean) -> Unit = {}) { - memberUnhooks.takeIf { it.isNotEmpty() }?.apply { + hookedMembers.takeIf { it.isNotEmpty() }?.apply { forEach { it.remove() onHookLogMsg(msg = "Remove Hooked Member [${it.member}] done [$tag]") diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator.kt index 1c8409e9..db775f16 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator.kt @@ -32,13 +32,13 @@ package com.highcapable.yukihookapi.hook.core import android.content.res.Resources import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.bean.HookResources +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerI import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.param.type.HookEntryType -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType /** * [YukiHookAPI] 的 [Resources] 核心 Hook 实现类 @@ -67,7 +67,7 @@ class YukiResourcesHookCreator @PublishedApi internal constructor( /** Hook 执行入口 */ @PublishedApi internal fun hook() { - if (YukiHookBridge.hasXposedBridge.not()) return + if (HookApiCategoryHelper.hasAvailableHookApi.not()) return /** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.RESOURCES] */ if (packageParam.wrapper?.type == HookEntryType.PACKAGE) return if (preHookResources.isEmpty()) return yLoggerW(msg = "Hook Resources is empty, hook aborted") diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategory.kt new file mode 100644 index 00000000..1a440b34 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategory.kt @@ -0,0 +1,39 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.compat + +/** + * Hook API 类型定义类 + */ +internal enum class HookApiCategory { + /** 原版 Xposed API */ + ROVO89_XPOSED, + + /** 未知类型的 API */ + UNKNOWN +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategoryHelper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategoryHelper.kt new file mode 100644 index 00000000..a6101405 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiCategoryHelper.kt @@ -0,0 +1,64 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.compat + +import de.robv.android.xposed.XposedBridge + +/** + * Hook API 类型工具类 + */ +internal object HookApiCategoryHelper { + + /** 目前支持的 API 类型定义 - 按优先级正序排列 */ + private val supportedCategories = arrayOf(HookApiCategory.ROVO89_XPOSED) + + /** + * 获取当前支持的 API 类型 + * @return [HookApiCategory] + */ + internal val currentCategory + get() = supportedCategories.let { categories -> + categories.forEach { if (hasCategory(it)) return@let it } + HookApiCategory.UNKNOWN + } + + /** + * 获取当前环境是否存在可用的 Hook API + * @return [Boolean] + */ + internal val hasAvailableHookApi get() = currentCategory != HookApiCategory.UNKNOWN + + /** + * 判断当前运行环境是否存在当前 Hook API 类型 + * @return [Boolean] + */ + private fun hasCategory(category: HookApiCategory) = when (category) { + HookApiCategory.ROVO89_XPOSED -> runCatching { XposedBridge.getXposedVersion(); true }.getOrNull() ?: false + else -> false + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiProperty.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiProperty.kt new file mode 100644 index 00000000..0a72f39d --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookApiProperty.kt @@ -0,0 +1,101 @@ +/* + * 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 2023/1/9. + */ +@file:Suppress("MemberVisibilityCanBePrivate") + +package com.highcapable.yukihookapi.hook.core.api.compat + +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.xposed.parasitic.AppParasitics +import de.robv.android.xposed.XposedBridge + +/** + * Hook API 相关属性 + */ +internal object HookApiProperty { + + /** TaiChi (太极) Xposed 框架名称 */ + internal const val TAICHI_XPOSED_NAME = "TaiChi" + + /** BugXposed (应用转生) Xposed 框架名称 */ + internal const val BUG_XPOSED_NAME = "BugXposed" + + /** TaiChi (太极) ExposedBridge 完整类名 */ + internal const val EXPOSED_BRIDGE_CLASS_NAME = "me.weishu.exposed.ExposedBridge" + + /** BugXposed (应用转生) BugLoad 完整类名 */ + internal const val BUG_LOAD_CLASS_NAME = "com.bug.load.BugLoad" + + /** + * 获取当前 Hook 框架的名称 + * @return [String] 无法获取会返回 unknown - 获取失败会返回 invalid + */ + internal val name + get() = when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> when { + EXPOSED_BRIDGE_CLASS_NAME.hasClass(AppParasitics.currentApplication?.classLoader) -> TAICHI_XPOSED_NAME + BUG_LOAD_CLASS_NAME.hasClass(AppParasitics.currentApplication?.classLoader) -> BUG_XPOSED_NAME + else -> runCatching { + classOf().field { name = "TAG" }.ignored().get().string().takeIf { it.isNotBlank() } + ?.replace("Bridge", "")?.replace("-", "")?.trim() ?: "unknown" + }.getOrNull() ?: "invalid" + } + HookApiCategory.UNKNOWN -> "unknown" + } + + /** + * 获取当前 Hook 框架的 API 版本 + * @return [Int] 无法获取会返回 -1 + */ + internal val apiLevel + get() = when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> runCatching { XposedBridge.getXposedVersion() }.getOrNull() ?: -1 + HookApiCategory.UNKNOWN -> -1 + } + + /** + * 获取当前 Hook 框架的版本名称 + * @return [String] 无法获取会返回 unknown - 不支持会返回 unsupported + */ + internal val versionName + get() = when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> "unsupported" + HookApiCategory.UNKNOWN -> "unknown" + } + + /** + * 获取当前 Hook 框架的版本号 + * @return [Int] 无法获取会返回 -1 - 不支持会返回 0 + */ + internal val versionCode + get() = when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> 0 + HookApiCategory.UNKNOWN -> -1 + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookCompatHelper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookCompatHelper.kt new file mode 100644 index 00000000..0b325c28 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/compat/HookCompatHelper.kt @@ -0,0 +1,143 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.compat + +import com.highcapable.yukihookapi.hook.core.api.factory.YukiHookCallbackDelegate +import com.highcapable.yukihookapi.hook.core.api.factory.callAfterHookedMember +import com.highcapable.yukihookapi.hook.core.api.factory.callBeforeHookedMember +import com.highcapable.yukihookapi.hook.core.api.priority.YukiHookPriority +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XposedBridge +import java.lang.reflect.Member + +/** + * Hook API 兼容层处理工具类 + */ +internal object HookCompatHelper { + + /** + * [HookApiCategory.ROVO89_XPOSED] + * + * 兼容对接已 Hook 的 [Member] 接口 + * @return [YukiMemberHook.HookedMember] + */ + private fun XC_MethodHook.Unhook.compat() = YukiHookCallbackDelegate.createHookedMemberCallback(hookedMethod) { unhook() } + + /** + * [HookApiCategory.ROVO89_XPOSED] + * + * 兼容对接 Hook 结果回调接口 + * @return [YukiHookCallback.Param] + */ + private fun XC_MethodHook.MethodHookParam.compat() = + YukiHookCallbackDelegate.createParamCallback( + dataExtra = extra, + member = method, + instance = thisObject, + args = args, + hasThrowable = hasThrowable(), + resultCallback = { result = it }, + throwableCallback = { throwable = it }, + result = result, + throwable = throwable + ) + + /** + * 兼容对接 Hook 回调接口 + * @return [Any] 原始接口 + */ + private fun YukiHookCallback.compat() = when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> object : XC_MethodHook( + when (priority) { + YukiHookPriority.DEFAULT -> 50 + YukiHookPriority.LOWEST -> -10000 + YukiHookPriority.HIGHEST -> 10000 + } + ) { + override fun beforeHookedMethod(param: MethodHookParam?) { + if (param == null) return + this@compat.callBeforeHookedMember(param.compat()) + } + + override fun afterHookedMethod(param: MethodHookParam?) { + if (param == null) return + this@compat.callAfterHookedMember(param.compat()) + } + } + HookApiCategory.UNKNOWN -> throwUnsupportedHookApiError() + } + + /** + * Hook [Member] + * @param member 需要 Hook 的方法、构造方法 + * @param callback 回调 + * @return [YukiMemberHook.HookedMember] or null + */ + internal fun hookMember(member: Member?, callback: YukiHookCallback): YukiMemberHook.HookedMember? { + if (member == null) return null + return when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> XposedBridge.hookMethod(member, callback.compat()).compat() + HookApiCategory.UNKNOWN -> throwUnsupportedHookApiError() + } + } + + /** + * 执行未进行 Hook 的原始 [Member] + * @param member 实例 + * @param args 参数实例 + * @return [Any] or null + */ + internal fun invokeOriginalMember(member: Member?, instance: Any?, vararg args: Any?): Any? { + if (member == null) return null + return when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> XposedBridge.invokeOriginalMethod(member, instance, args) + HookApiCategory.UNKNOWN -> throwUnsupportedHookApiError() + } + } + + /** + * 使用当前 Hook API 自带的日志功能打印日志 + * @param msg 日志打印的内容 + * @param e 异常堆栈信息 - 默认空 + */ + internal fun logByHooker(msg: String, e: Throwable? = null) { + when (HookApiCategoryHelper.currentCategory) { + HookApiCategory.ROVO89_XPOSED -> { + XposedBridge.log(msg) + e?.also { XposedBridge.log(it) } + } + HookApiCategory.UNKNOWN -> throwUnsupportedHookApiError() + } + } + + /** 抛出不支持的 API 类型异常 */ + private fun throwUnsupportedHookApiError(): Nothing = + error("YukiHookAPI cannot support current Hook API or cannot found any available Hook APIs in current environment") +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/factory/YukiHookDelegateFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/factory/YukiHookDelegateFactory.kt new file mode 100644 index 00000000..eabe1e1d --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/factory/YukiHookDelegateFactory.kt @@ -0,0 +1,117 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.factory + +import android.os.Bundle +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberReplacement +import com.highcapable.yukihookapi.hook.core.api.store.YukiHookCacheStore +import java.lang.reflect.Member + +/** + * Hook API 回调事件代理类 + */ +internal object YukiHookCallbackDelegate { + + /** + * 创建 [YukiMemberHook.HookedMember] 实例 + * @param member 当前 [Member] + * @param removeCallback 回调解除 Hook 事件 + * @return [YukiMemberHook.HookedMember] + */ + internal fun createHookedMemberCallback(member: Member, removeCallback: () -> Unit) = + object : YukiMemberHook.HookedMember() { + override val member get() = member + override fun remove() { + removeCallback() + runCatching { YukiHookCacheStore.hookedMembers.remove(this) } + } + } + + /** + * 创建 [YukiHookCallback.Param] 实例 + * @param dataExtra 当前回调范围内的数据存储实例 + * @param member [Member] 实例 + * @param instance 当前实例对象 + * @param args 方法、构造方法数组 + * @param hasThrowable 是否存在设置过的方法调用抛出异常 + * @param resultCallback 回调设置当前 Hook 方法的返回值 (结果) + * @param throwableCallback 回调设置当前 Hook 方法调用抛出的异常 + * @param result 当前 Hook 方法返回值 (结果) + * @param throwable 当前 Hook 方法调用抛出的异常 + * @return [YukiHookCallback.Param] + */ + internal fun createParamCallback( + dataExtra: Bundle, + member: Member?, + instance: Any?, + args: Array?, + hasThrowable: Boolean, + resultCallback: (Any?) -> Unit, + throwableCallback: (Throwable?) -> Unit, + result: Any?, + throwable: Throwable? + ) = object : YukiHookCallback.Param { + override val dataExtra get() = dataExtra + override val member get() = member + override val instance get() = instance + override val args get() = args + override val hasThrowable get() = hasThrowable + override var result + get() = result + set(value) { + resultCallback(value) + } + override var throwable + get() = throwable + set(value) { + throwableCallback(value) + } + } +} + +/** + * 调用 [YukiMemberHook.beforeHookedMember] 事件 + * @param param Hook 结果回调接口 + */ +internal fun YukiHookCallback.callBeforeHookedMember(param: YukiHookCallback.Param) { + if (this !is YukiMemberHook) error("Invalid YukiHookCallback type") + if (this is YukiMemberReplacement) + param.result = replaceHookedMember(param) + else beforeHookedMember(param) +} + +/** + * 调用 [YukiMemberHook.afterHookedMember] 事件 + * @param param Hook 结果回调接口 + */ +internal fun YukiHookCallback.callAfterHookedMember(param: YukiHookCallback.Param) { + if (this !is YukiMemberHook) error("Invalid YukiHookCallback type") + afterHookedMember(param) +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/helper/YukiHookHelper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/helper/YukiHookHelper.kt new file mode 100644 index 00000000..4eb574a6 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/helper/YukiHookHelper.kt @@ -0,0 +1,104 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.helper + +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper +import com.highcapable.yukihookapi.hook.core.api.compat.HookCompatHelper +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback +import com.highcapable.yukihookapi.hook.core.api.result.YukiHookResult +import com.highcapable.yukihookapi.hook.core.api.store.YukiHookCacheStore +import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder +import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder +import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder +import com.highcapable.yukihookapi.hook.log.yLoggerE +import java.lang.reflect.Member + +/** + * Hook 核心功能实现工具类 + */ +internal object YukiHookHelper { + + /** + * Hook [BaseFinder.BaseResult] + * @param traction 直接调用 [BaseFinder.BaseResult] + * @param callback 回调 + * @return [YukiHookResult] + */ + internal fun hook(traction: BaseFinder.BaseResult, callback: YukiHookCallback) = runCatching { + val member: Member? = when (traction) { + is MethodFinder.Result -> traction.ignored().give() + is ConstructorFinder.Result -> traction.ignored().give() + else -> error("Unexpected BaseFinder result interface type") + } + hookMember(member, callback) + }.onFailure { yLoggerE(msg = "An exception occurred when hooking internal function", e = it) }.getOrNull() ?: YukiHookResult() + + /** + * Hook [Member] + * @param member 需要 Hook 的方法、构造方法 + * @param callback 回调 + * @return [YukiHookResult] + */ + internal fun hookMember(member: Member?, callback: YukiHookCallback): YukiHookResult { + runCatching { + YukiHookCacheStore.hookedMembers.takeIf { it.isNotEmpty() }?.forEach { + if (it.member.toString() == member?.toString()) return YukiHookResult(isAlreadyHooked = true, it) + } + } + return HookCompatHelper.hookMember(member, callback).let { + if (it != null) YukiHookCacheStore.hookedMembers.add(it) + YukiHookResult(hookedMember = it) + } + } + + /** + * 执行原始 [Member] + * + * 未进行 Hook 的 [Member] + * @param member 实例 + * @param args 参数实例 + * @return [Any] or null + */ + internal fun invokeOriginalMember(member: Member?, instance: Any?, vararg args: Any?) = + if (HookApiCategoryHelper.hasAvailableHookApi && YukiHookCacheStore.hookedMembers.any { it.member.toString() == member.toString() }) + member?.let { + runCatching { HookCompatHelper.invokeOriginalMember(member, instance, args) } + .onFailure { yLoggerE(msg = "Invoke original Member [$member] failed", e = it) } + .getOrNull() + } + else null + + /** + * 使用当前 Hook API 自带的日志功能打印日志 + * @param msg 日志打印的内容 + * @param e 异常堆栈信息 - 默认空 + */ + internal fun logByHooker(msg: String, e: Throwable? = null) { + if (HookApiCategoryHelper.hasAvailableHookApi) HookCompatHelper.logByHooker(msg, e) + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/priority/YukiHookPriority.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/priority/YukiHookPriority.kt new file mode 100644 index 00000000..e9ee95e2 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/priority/YukiHookPriority.kt @@ -0,0 +1,43 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.priority + +/** + * Hook 回调优先级配置类 + */ +internal enum class YukiHookPriority { + + /** 默认 Hook 回调优先级 */ + DEFAULT, + + /** 延迟回调 Hook 方法结果 */ + LOWEST, + + /** 更快回调 Hook 方法结果 */ + HIGHEST +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiHookCallback.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiHookCallback.kt new file mode 100644 index 00000000..2a4a011a --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiHookCallback.kt @@ -0,0 +1,89 @@ +/* + * 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/4/9. + * This file is Modified by fankes on 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.proxy + +import android.os.Bundle +import com.highcapable.yukihookapi.hook.core.api.priority.YukiHookPriority +import java.lang.reflect.Member + +/** + * Hook 回调接口抽象类 + * @param priority Hook 优先级 + */ +internal abstract class YukiHookCallback(internal open val priority: YukiHookPriority) { + + /** + * Hook 结果回调接口 + */ + internal interface Param { + + /** + * 当前回调范围内的数据存储实例 + * @return [Bundle] + */ + val dataExtra: Bundle + + /** + * [Member] 实例 + * @return [Member] or null + */ + val member: Member? + + /** + * 当前实例对象 + * @return [Any] or null + */ + val instance: Any? + + /** + * 方法、构造方法数组 + * @return [Array] or null + */ + val args: Array? + + /** + * 获取、设置方法返回值 (结果) + * @return [Any] or null + */ + var result: Any? + + /** + * 是否存在设置过的方法调用抛出异常 + * @return [Boolean] + */ + val hasThrowable: Boolean + + /** + * 获取、设置方法调用抛出的异常 + * @return [Throwable] or null + * @throws Throwable + */ + var throwable: Throwable? + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberHook.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberHook.kt new file mode 100644 index 00000000..34274b69 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberHook.kt @@ -0,0 +1,66 @@ +/* + * 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/4/9. + * This file is Modified by fankes on 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.proxy + +import com.highcapable.yukihookapi.hook.core.api.priority.YukiHookPriority +import java.lang.reflect.Member + +/** + * Hook 方法回调接口抽象类 + * @param priority Hook 优先级 - 默认 [YukiHookPriority.DEFAULT] + */ +internal abstract class YukiMemberHook(override val priority: YukiHookPriority = YukiHookPriority.DEFAULT) : YukiHookCallback(priority) { + + /** + * 在方法执行之前注入 + * @param param Hook 结果回调接口 + */ + internal open fun beforeHookedMember(param: Param) {} + + /** + * 在方法执行之后注入 + * @param param Hook 结果回调接口 + */ + internal open fun afterHookedMember(param: Param) {} + + /** + * 已经 Hook 且可被解除 Hook 的 [Member] 实现接口抽象类 + */ + internal abstract class HookedMember internal constructor() { + + /** + * 当前被 Hook 的 [Member] + * @return [Member] or null + */ + internal abstract val member: Member? + + /** 解除 Hook */ + internal abstract fun remove() + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/helper/YukiHookAppHelper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberReplacement.kt similarity index 55% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/helper/YukiHookAppHelper.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberReplacement.kt index 50c3da14..9acbc07d 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/helper/YukiHookAppHelper.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/proxy/YukiMemberReplacement.kt @@ -24,41 +24,28 @@ * SOFTWARE. * * This file is Created by fankes on 2022/4/9. + * This file is Modified by fankes on 2023/1/9. */ -package com.highcapable.yukihookapi.hook.xposed.helper +package com.highcapable.yukihookapi.hook.core.api.proxy -import android.app.AndroidAppHelper -import android.app.Application -import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.hook.core.api.priority.YukiHookPriority /** - * 这是一个宿主 Hook 功能接口 - * - * 对接 [AndroidAppHelper] + * Hook 替换方法回调接口抽象类 + * @param priority Hook 优先级- 默认 [YukiHookPriority.DEFAULT] */ -internal object YukiHookAppHelper { +internal abstract class YukiMemberReplacement(override val priority: YukiHookPriority = YukiHookPriority.DEFAULT) : YukiMemberHook(priority) { + + override fun beforeHookedMember(param: Param) { + param.result = replaceHookedMember(param) + } + + override fun afterHookedMember(param: Param) {} /** - * 获取当前宿主的 [Application] - * @return [Application] or null + * 拦截替换为指定结果 + * @param param Hook 结果回调接口 + * @return [Any] or null */ - internal fun currentApplication() = runCatching { AndroidAppHelper.currentApplication() }.getOrNull() - - /** - * 获取当前宿主的 [ApplicationInfo] - * @return [ApplicationInfo] or null - */ - internal fun currentApplicationInfo() = runCatching { AndroidAppHelper.currentApplicationInfo() }.getOrNull() - - /** - * 获取当前宿主的包名 - * @return [String] or null - */ - internal fun currentPackageName() = runCatching { AndroidAppHelper.currentPackageName() }.getOrNull() - - /** - * 获取当前宿主的进程名 - * @return [String] or null - */ - internal fun currentProcessName() = runCatching { AndroidAppHelper.currentProcessName() }.getOrNull() + abstract fun replaceHookedMember(param: Param): Any? } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/result/YukiHookResult.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/result/YukiHookResult.kt new file mode 100644 index 00000000..81d0f1bc --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/result/YukiHookResult.kt @@ -0,0 +1,37 @@ +/* + * 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 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.result + +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook + +/** + * Hook 结果实现类 + * @param isAlreadyHooked 是否已经被 Hook - 默认否 + * @param hookedMember 当前 Hook 的实例对象 - 默认空 + */ +internal data class YukiHookResult(val isAlreadyHooked: Boolean = false, val hookedMember: YukiMemberHook.HookedMember? = null) \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/store/YukiHookCacheStore.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/store/YukiHookCacheStore.kt new file mode 100644 index 00000000..ee911b99 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/api/store/YukiHookCacheStore.kt @@ -0,0 +1,41 @@ +/* + * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. + * Copyright (C) 2019-2022 HighCapable + * https://github.com/fankes/YukiHookAPI + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is Created by fankes on 2022/7/28. + * This file is Modified by fankes on 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.core.api.store + +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook +import java.lang.reflect.Member + +/** + * Hook 过程的功能缓存实现类 + */ +internal object YukiHookCacheStore { + + /** 已经 Hook 的 [Member] 数组 */ + internal val hookedMembers = HashSet() +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/ClassBaseFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/ClassBaseFinder.kt index ad8c2541..4e57fbc6 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/ClassBaseFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/ClassBaseFinder.kt @@ -29,9 +29,9 @@ package com.highcapable.yukihookapi.hook.core.finder.base import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.YukiPrivateApi +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerI -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge /** * 这是 [Class] 查找类功能的基本类实现 @@ -60,11 +60,11 @@ abstract class ClassBaseFinder internal constructor(internal open val loaderSet: internal fun compatType(any: Any?, tag: String) = any?.compat(tag, loaderSet) /** - * 在开启 [YukiHookAPI.Configs.isDebug] 且在 [YukiHookBridge.hasXposedBridge] 情况下输出调试信息 + * 在开启 [YukiHookAPI.Configs.isDebug] 且在 [HookApiCategoryHelper.hasAvailableHookApi] 情况下输出调试信息 * @param msg 调试日志内容 */ internal fun onDebuggingMsg(msg: String) { - if (YukiHookAPI.Configs.isDebug && YukiHookBridge.hasXposedBridge) yLoggerI(msg = msg) + if (YukiHookAPI.Configs.isDebug && HookApiCategoryHelper.hasAvailableHookApi) yLoggerI(msg = msg) } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/MemberBaseFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/MemberBaseFinder.kt index 08e4b544..447864fc 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/MemberBaseFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/base/MemberBaseFinder.kt @@ -30,11 +30,11 @@ package com.highcapable.yukihookapi.hook.core.finder.base import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.YukiPrivateApi import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerI import com.highcapable.yukihookapi.hook.utils.await import com.highcapable.yukihookapi.hook.utils.unit -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Member @@ -145,11 +145,11 @@ abstract class MemberBaseFinder internal constructor( } /** - * 在开启 [YukiHookAPI.Configs.isDebug] 且在 [YukiHookBridge.hasXposedBridge] 且在 Hook 过程中情况下输出调试信息 + * 在开启 [YukiHookAPI.Configs.isDebug] 且在 [HookApiCategoryHelper.hasAvailableHookApi] 且在 Hook 过程中情况下输出调试信息 * @param msg 调试日志内容 */ internal fun onDebuggingMsg(msg: String) { - if (YukiHookAPI.Configs.isDebug && YukiHookBridge.hasXposedBridge && hookInstance != null) yLoggerI(msg = msg) + if (YukiHookAPI.Configs.isDebug && HookApiCategoryHelper.hasAvailableHookApi && hookInstance != null) yLoggerI(msg = msg) } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/classes/DexClassFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/classes/DexClassFinder.kt index fda6fbed..47ff5f35 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/classes/DexClassFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/classes/DexClassFinder.kt @@ -53,7 +53,7 @@ import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.utils.await import com.highcapable.yukihookapi.hook.utils.runBlocking -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import dalvik.system.BaseDexClassLoader import java.lang.reflect.Constructor import java.lang.reflect.Field @@ -81,6 +81,12 @@ class DexClassFinder @PublishedApi internal constructor( /** 缓存的存储文件名 */ private const val CACHE_FILE_NAME = "config_yukihook_cache_obfuscate_classes" + /** + * 获取当前运行环境的 [Context] + * @return [Context] or null + */ + private val currentContext get() = AppParasitics.hostApplication ?: AppParasitics.currentApplication + /** * 通过 [Context] 获取当前 [SharedPreferences] * @param versionName 版本名称 - 默认空 @@ -97,11 +103,11 @@ class DexClassFinder @PublishedApi internal constructor( * 清除当前 [DexClassFinder] 的 [Class] 缓存 * * 适用于全部通过 [ClassLoader.searchClass] or [PackageParam.searchClass] 获取的 [DexClassFinder] - * @param context 当前 [Context] - 不填默认获取 [YukiHookAppHelper.currentApplication] + * @param context 当前 [Context] - 不填默认获取 [currentContext] * @param versionName 版本名称 - 默认空 * @param versionCode 版本号 - 默认空 */ - fun clearCache(context: Context? = YukiHookAppHelper.currentApplication(), versionName: String? = null, versionCode: Long? = null) { + fun clearCache(context: Context? = currentContext, versionName: String? = null, versionCode: Long? = null) { context?.currentSp(versionName, versionCode)?.edit()?.clear()?.apply() ?: yLoggerW(msg = "Cannot clear cache for DexClassFinder because got null context instance") } @@ -440,7 +446,7 @@ class DexClassFinder @PublishedApi internal constructor( * @return [HashSet]<[Class]> */ private fun readFromCache(): HashSet> = - if (async && name.isNotBlank()) YukiHookAppHelper.currentApplication()?.let { + if (async && name.isNotBlank()) currentContext?.let { hashSetOf>().also { classes -> it.currentSp().getStringSet(name, emptySet())?.takeIf { it.isNotEmpty() } ?.forEach { className -> if (className.hasClass(loaderSet)) classes.add(className.toClass(loaderSet)) } @@ -454,7 +460,7 @@ class DexClassFinder @PublishedApi internal constructor( private fun HashSet>.saveToCache() { if (name.isNotBlank() && isNotEmpty()) hashSetOf().also { names -> takeIf { it.isNotEmpty() }?.forEach { names.add(it.name) } - YukiHookAppHelper.currentApplication()?.also { + currentContext?.also { if (it.packageName == "android") error("Cannot create classes cache for \"android\", please remove \"name\" param") it.currentSp().edit().apply { putStringSet(name, names) }.apply() } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/ConstructorFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/ConstructorFinder.kt index 0f013a63..91c89b7b 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/ConstructorFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/ConstructorFinder.kt @@ -289,7 +289,6 @@ class ConstructorFinder @PublishedApi internal constructor( @YukiPrivateApi override fun failure(throwable: Throwable?) = Result(isNoSuch = true, throwable) - @YukiPrivateApi override fun denied(throwable: Throwable?) = Process(isNoSuch = true, throwable) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/MethodFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/MethodFinder.kt index 2b2bfabc..0c706a91 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/MethodFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/members/MethodFinder.kt @@ -32,6 +32,7 @@ package com.highcapable.yukihookapi.hook.core.finder.members import com.highcapable.yukihookapi.annotation.YukiPrivateApi import com.highcapable.yukihookapi.hook.bean.VariousClass import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator +import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder import com.highcapable.yukihookapi.hook.core.finder.base.MemberBaseFinder import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData @@ -44,7 +45,6 @@ import com.highcapable.yukihookapi.hook.type.defined.UndefinedType import com.highcapable.yukihookapi.hook.type.defined.VagueType import com.highcapable.yukihookapi.hook.utils.runBlocking import com.highcapable.yukihookapi.hook.utils.unit -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper import java.lang.reflect.Member import java.lang.reflect.Method @@ -377,7 +377,6 @@ class MethodFinder @PublishedApi internal constructor( @YukiPrivateApi override fun failure(throwable: Throwable?) = Result(isNoSuch = true, throwable) - @YukiPrivateApi override fun denied(throwable: Throwable?) = Process(isNoSuch = true, throwable) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/tools/ReflectionTool.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/tools/ReflectionTool.kt index 718e15fe..228e6e6c 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/tools/ReflectionTool.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/tools/ReflectionTool.kt @@ -46,8 +46,6 @@ import com.highcapable.yukihookapi.hook.type.java.NoSuchFieldErrorClass import com.highcapable.yukihookapi.hook.type.java.NoSuchMethodErrorClass import com.highcapable.yukihookapi.hook.utils.* import com.highcapable.yukihookapi.hook.utils.value -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import dalvik.system.BaseDexClassLoader import java.lang.reflect.Constructor @@ -119,12 +117,7 @@ internal object ReflectionTool { */ fun loadWithDefaultClassLoader() = if (initialize.not()) loader?.loadClass(name) else classForName(name, initialize, loader) return ReflectsCacheStore.findClass(hashCode) ?: runCatching { - when { - YukiHookBridge.hasXposedBridge -> runCatching { if (initialize.not()) YukiHookHelper.findClass(name, loader) else null } - .getOrNull() ?: loadWithDefaultClassLoader() ?: classForName(name, initialize) - loader == null -> classForName(name, initialize) - else -> loadWithDefaultClassLoader() - }.also { ReflectsCacheStore.putClass(hashCode, it) } + (loadWithDefaultClassLoader() ?: classForName(name, initialize)).also { ReflectsCacheStore.putClass(hashCode, it) } }.getOrNull() ?: throw createException(loader ?: AppParasitics.baseClassLoader, name = "Class", "name:[$name]") } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt index 4220b024..fd7ffd4a 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt @@ -30,28 +30,11 @@ package com.highcapable.yukihookapi.hook.entity import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit /** * [YukiHookAPI] 的子类 Hooker 实现 * - * 也许你的 Module 中存在多个 Hooker - 继承此类可以方便帮你管理每个 Hooker - * - * 你可以继续继承此类进行自定义 Hooker 相关参数 - * - * 你可以在 [IYukiHookXposedInit] 的 [IYukiHookXposedInit.onHook] 中实现如下用法: - * - * 1.调用 [YukiHookAPI.encase] - * - * ```kotlin - * encase(MainHooker(), SecondHooker(), ThirdHooker() ...) - * ``` - * - * 2.调用 [PackageParam.loadHooker] - * - * ```kotlin - * loadHooker(hooker = CustomHooker()) - * ``` + * 也许你的模块中存在多个功能模块 (Hooker) - 继承并使用此类可以方便帮你管理每个功能模块 (Hooker) * * 更多请参考 [InjectYukiHookWithXposed] 中的注解内容 * @@ -66,7 +49,7 @@ abstract class YukiBaseHooker : PackageParam() { * @param packageParam 需要使用的 [PackageParam] */ internal fun assignInstance(packageParam: PackageParam) { - baseAssignInstance(packageParam) + assign(packageParam.wrapper) onHook() } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt index d901c9ea..f41d5e98 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt @@ -40,7 +40,7 @@ import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool import com.highcapable.yukihookapi.hook.core.finder.type.factory.* import com.highcapable.yukihookapi.hook.type.java.* -import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus +import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiXposedModuleStatus import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import dalvik.system.BaseDexClassLoader import java.lang.reflect.* @@ -421,7 +421,7 @@ inline fun Class<*>.allFields(isAccessible: Boolean = true, result: (index: Int, */ @PublishedApi internal fun Class<*>.checkingInternal() { - if (name == YukiHookModuleStatus.IMPL_CLASS_NAME) return + if (name == YukiXposedModuleStatus.IMPL_CLASS_NAME) return if (name == classOf().name || name.startsWith("com.highcapable.yukihookapi.hook")) throw RuntimeException( "!!!DO NOT ALLOWED!!! You cannot hook or reflection to call the internal class of the YukiHookAPI itself, " + "The called class is [$this]" diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt index 7528ab69..c6d2e36d 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt @@ -32,11 +32,11 @@ package com.highcapable.yukihookapi.hook.log import android.system.ErrnoException import android.util.Log import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.yukihookapi.hook.utils.toStackTrace -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics -import de.robv.android.xposed.XposedBridge import java.io.File import java.io.Serializable import java.text.SimpleDateFormat @@ -52,16 +52,26 @@ enum class LoggerType { LOGD, /** - * 仅使用 [XposedBridge.log] + * 仅在 (Xposed) 宿主环境使用 + * + * - ❗此方法已弃用 - 在之后的版本中将直接被删除 + * + * - ❗请现在转移到 [XPOSED_ENVIRONMENT] + */ + @Deprecated(message = "请使用新的命名方法", ReplaceWith("XPOSED_ENVIRONMENT")) + XPOSEDBRIDGE, + + /** + * 仅在 (Xposed) 宿主环境使用 * * - ❗只能在 (Xposed) 宿主环境中使用 - 模块环境将不生效 */ - XPOSEDBRIDGE, + XPOSED_ENVIRONMENT, /** * 分区使用 * - * (Xposed) 宿主环境仅使用 [XPOSEDBRIDGE] + * (Xposed) 宿主环境仅使用 [XPOSED_ENVIRONMENT] * * 模块环境仅使用 [LOGD] */ @@ -70,7 +80,7 @@ enum class LoggerType { /** * 同时使用 * - * (Xposed) 宿主环境使用 [LOGD] 与 [XPOSEDBRIDGE] + * (Xposed) 宿主环境使用 [LOGD] 与 [XPOSED_ENVIRONMENT] * * 模块环境仅使用 [LOGD] */ @@ -105,8 +115,8 @@ data class YukiLoggerData internal constructor( init { timestamp = System.currentTimeMillis() time = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ROOT).format(Date(timestamp)) - packageName = YukiHookBridge.hostProcessName.takeIf { it != "unknown" } ?: YukiHookBridge.modulePackageName - userId = AppParasitics.findUserId(packageName) + packageName = if (YukiXposedModule.isXposedEnvironment) YukiXposedModule.hostProcessName else AppParasitics.currentPackageName + userId = AppParasitics.findUserId(AppParasitics.currentPackageName) } /** @@ -285,7 +295,7 @@ object YukiHookLogger { /** * 自定义调试日志对外显示的元素 * - * 只对日志记录和 [XposedBridge.log] 生效 + * 只对日志记录和 (Xposed) 宿主环境的日志生效 * * 日志元素的排列将按照你在 [item] 中设置的顺序进行显示 * @@ -311,14 +321,14 @@ object YukiHookLogger { } /** - * 向控制台和 [XposedBridge] 打印日志 - 最终实现方法 + * 向控制台和 (Xposed) 宿主环境打印日志 - 最终实现方法 * @param type 日志打印的类型 * @param data 日志数据 * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID */ private fun baseLogger(type: LoggerType, data: YukiLoggerData, isImplicit: Boolean = false) { /** 打印到 [Log] */ - fun loggerInLogd() = when (data.priority) { + fun logByLogd() = when (data.priority) { "D" -> Log.d(data.tag, data.msg) "I" -> Log.i(data.tag, data.msg) "W" -> Log.w(data.tag, data.msg) @@ -326,25 +336,23 @@ private fun baseLogger(type: LoggerType, data: YukiLoggerData, isImplicit: Boole else -> Log.wtf(data.tag, data.msg, data.throwable) } - /** 打印到 [XposedBridge.log] */ - fun loggerInXposed() { - XposedBridge.log(data.also { it.isImplicit = isImplicit }.toString()) - data.throwable?.also { e -> XposedBridge.log(e) } - } + /** 打印到 (Xposed) 宿主环境 */ + fun logByHooker() = YukiHookHelper.logByHooker(data.also { it.isImplicit = isImplicit }.toString(), data.throwable) + @Suppress("DEPRECATION") when (type) { - LoggerType.LOGD -> loggerInLogd() - LoggerType.XPOSEDBRIDGE -> loggerInXposed() - LoggerType.SCOPE -> if (YukiHookBridge.hasXposedBridge) loggerInXposed() else loggerInLogd() + LoggerType.LOGD -> logByLogd() + LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> logByHooker() + LoggerType.SCOPE -> if (YukiXposedModule.isXposedEnvironment) logByHooker() else logByLogd() LoggerType.BOTH -> { - loggerInLogd() - if (YukiHookBridge.hasXposedBridge) loggerInXposed() + logByLogd() + if (YukiXposedModule.isXposedEnvironment) logByHooker() } } if (isImplicit.not() && YukiHookLogger.Configs.isRecord) YukiHookLogger.inMemoryData.add(data) } /** - * [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - D + * [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - D * @param msg 日志打印的内容 * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID * @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false @@ -355,7 +363,7 @@ internal fun yLoggerD(msg: String, isImplicit: Boolean = false, isDisableLog: Bo } /** - * [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - I + * [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - I * @param msg 日志打印的内容 * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID * @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false @@ -366,7 +374,7 @@ internal fun yLoggerI(msg: String, isImplicit: Boolean = false, isDisableLog: Bo } /** - * [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - W + * [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - W * @param msg 日志打印的内容 * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID * @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false @@ -377,7 +385,7 @@ internal fun yLoggerW(msg: String, isImplicit: Boolean = false, isDisableLog: Bo } /** - * [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - E + * [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - E * @param msg 日志打印的内容 * @param e 可填入异常堆栈信息 - 将自动完整打印到控制台 * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID @@ -389,9 +397,9 @@ internal fun yLoggerE(msg: String, e: Throwable? = null, isImplicit: Boolean = f } /** - * 向控制台和 [XposedBridge] 打印日志 - D + * 向控制台和 (Xposed) 宿主环境打印日志 - D * - * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] + * (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag] * @param msg 日志打印的内容 * @param type 日志打印的类型 - 默认为 [LoggerType.BOTH] @@ -400,9 +408,9 @@ fun loggerD(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerT baseLogger(type, YukiLoggerData(priority = "D", tag = tag, msg = msg)) /** - * 向控制台和 [XposedBridge] 打印日志 - I + * 向控制台和 (Xposed) 宿主环境打印日志 - I * - * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] + * (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag] * @param msg 日志打印的内容 * @param type 日志打印的类型 - 默认为 [LoggerType.BOTH] @@ -411,9 +419,9 @@ fun loggerI(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerT baseLogger(type, YukiLoggerData(priority = "I", tag = tag, msg = msg)) /** - * 向控制台和 [XposedBridge] 打印日志 - W + * 向控制台和 (Xposed) 宿主环境打印日志 - W * - * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] + * (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag] * @param msg 日志打印的内容 * @param type 日志打印的类型 - 默认为 [LoggerType.BOTH] @@ -422,9 +430,9 @@ fun loggerW(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerT baseLogger(type, YukiLoggerData(priority = "W", tag = tag, msg = msg)) /** - * 向控制台和 [XposedBridge] 打印日志 - E + * 向控制台和 (Xposed) 宿主环境打印日志 - E * - * [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg] + * (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg] * @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag] * @param msg 日志打印的内容 * @param e 可填入异常堆栈信息 - 将自动完整打印到控制台 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt index e4f9f88b..d41f0818 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/HookParam.kt @@ -31,10 +31,10 @@ package com.highcapable.yukihookapi.hook.param import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator.MemberHookCreator +import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback import com.highcapable.yukihookapi.hook.factory.classOf import com.highcapable.yukihookapi.hook.log.yLoggerE -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookCallback -import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper import java.lang.reflect.Constructor import java.lang.reflect.Member import java.lang.reflect.Method 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 5ba6070b..49c9759b 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 @@ -46,14 +46,13 @@ import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool import com.highcapable.yukihookapi.hook.core.finder.type.factory.ClassConditions import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker -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.bridge.YukiXposedModule +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiModuleResources +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType 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 @@ -63,6 +62,18 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs */ open class PackageParam internal constructor(@PublishedApi internal var wrapper: PackageParamWrapper? = null) { + @PublishedApi + internal companion object { + + /** 获取当前 Xposed 模块包名 */ + @PublishedApi + internal val modulePackageName get() = YukiXposedModule.modulePackageName + + /** Android 系统框架名称 */ + @PublishedApi + internal const val SYSTEM_FRAMEWORK_NAME = AppParasitics.SYSTEM_FRAMEWORK_NAME + } + /** 当前设置的 [ClassLoader] */ private var currentClassLoader: ClassLoader? = null @@ -77,7 +88,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: */ var appClassLoader get() = currentClassLoader ?: wrapper?.appClassLoader - ?: YukiHookAppHelper.currentApplication()?.classLoader + ?: AppParasitics.currentApplication?.classLoader ?: javaClass.classLoader ?: error("PackageParam got null ClassLoader") set(value) { currentClassLoader = value @@ -87,7 +98,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * 获取当前 Hook APP 的 [ApplicationInfo] * @return [ApplicationInfo] */ - val appInfo get() = wrapper?.appInfo ?: YukiHookAppHelper.currentApplicationInfo() ?: ApplicationInfo() + val appInfo get() = wrapper?.appInfo ?: AppParasitics.currentApplicationInfo ?: ApplicationInfo() /** * 获取当前 Hook APP 的用户 ID @@ -103,7 +114,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * - ❗首次装载可能是空的 - 请延迟一段时间再获取或通过设置 [onAppLifecycle] 监听来完成 * @return [Application] or null */ - val appContext get() = AppParasitics.hostApplication ?: YukiHookAppHelper.currentApplication() + val appContext get() = AppParasitics.hostApplication ?: AppParasitics.currentApplication /** * 获取当前 Hook APP 的 Resources @@ -126,13 +137,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * 默认的进程名称是 [packageName] * @return [String] */ - val processName get() = wrapper?.processName ?: YukiHookAppHelper.currentProcessName() ?: packageName + val processName get() = wrapper?.processName ?: AppParasitics.currentProcessName /** * 获取当前 Hook APP 的包名 * @return [String] */ - val packageName get() = wrapper?.packageName ?: YukiHookAppHelper.currentPackageName() ?: "" + val packageName get() = wrapper?.packageName ?: AppParasitics.currentPackageName /** * 获取当前 Hook APP 是否为第一个 [Application] @@ -154,7 +165,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * - ❗作为 Hook API 装载时无法使用 - 会获取到空字符串 * @return [String] */ - val moduleAppFilePath get() = AppParasitics.moduleAppFilePath + val moduleAppFilePath get() = YukiXposedModule.moduleAppFilePath /** * 获取当前 Xposed 模块自身 [Resources] @@ -164,8 +175,8 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: * @throws IllegalStateException 如果当前 Hook Framework 不支持此功能 */ val moduleAppResources - get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) AppParasitics.moduleAppResources - else AppParasitics.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources") + get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) YukiXposedModule.moduleAppResources + else YukiXposedModule.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources") /** * 获得当前使用的存取数据对象缓存实例 @@ -197,11 +208,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: else error("YukiHookDataChannel cannot used in zygote") /** - * 赋值并克隆另一个 [PackageParam] - * @param anotherParam 另一个 [PackageParam] + * 设置 [PackageParam] 使用的 [PackageParamWrapper] + * @param wrapper [PackageParam] 的参数包装类实例 + * @return [PackageParam] */ - internal fun baseAssignInstance(anotherParam: PackageParam) { - this.wrapper = anotherParam.wrapper + internal fun assign(wrapper: PackageParamWrapper?): PackageParam { + this.wrapper = wrapper + return this } /** @@ -213,7 +226,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: fun resources() = HookResources(wrapper?.appResources) /** 刷新当前 Xposed 模块自身 [Resources] */ - fun refreshModuleAppResources() = AppParasitics.refreshModuleAppResources() + fun refreshModuleAppResources() = YukiXposedModule.refreshModuleAppResources() /** * 监听当前 Hook APP 生命周期装载事件 @@ -292,7 +305,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: */ inline fun loadApp(isExcludeSelf: Boolean = false, initiate: PackageParam.() -> Unit) { if (wrapper?.type != HookEntryType.ZYGOTE && - (isExcludeSelf.not() || isExcludeSelf && packageName != YukiHookBridge.modulePackageName) + (isExcludeSelf.not() || isExcludeSelf && packageName != modulePackageName) ) initiate(this) } @@ -307,7 +320,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: */ fun loadApp(isExcludeSelf: Boolean = false, hooker: YukiBaseHooker) { if (wrapper?.type != HookEntryType.ZYGOTE && - (isExcludeSelf.not() || isExcludeSelf && packageName != YukiHookBridge.modulePackageName) + (isExcludeSelf.not() || isExcludeSelf && packageName != modulePackageName) ) loadHooker(hooker) } @@ -323,7 +336,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: fun loadApp(isExcludeSelf: Boolean = false, vararg hooker: YukiBaseHooker) { if (hooker.isEmpty()) error("loadApp method need a \"hooker\" param") if (wrapper?.type != HookEntryType.ZYGOTE && - (isExcludeSelf.not() || isExcludeSelf && packageName != YukiHookBridge.modulePackageName) + (isExcludeSelf.not() || isExcludeSelf && packageName != modulePackageName) ) hooker.forEach { loadHooker(it) } } @@ -331,13 +344,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(SYSTEM_FRAMEWORK_NAME, initiate) /** * 装载并 Hook 系统框架 * @param hooker Hook 子类 */ - fun loadSystem(hooker: YukiBaseHooker) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, hooker) + fun loadSystem(hooker: YukiBaseHooker) = loadApp(SYSTEM_FRAMEWORK_NAME, hooker) /** * 装载并 Hook 系统框架 @@ -345,7 +358,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper: */ fun loadSystem(vararg hooker: YukiBaseHooker) { if (hooker.isEmpty()) error("loadSystem method need a \"hooker\" param") - loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, *hooker) + loadApp(SYSTEM_FRAMEWORK_NAME, *hooker) } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/PackageParamWrapper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/PackageParamWrapper.kt index d2ac89e3..a69f31a6 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/PackageParamWrapper.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/wrapper/PackageParamWrapper.kt @@ -30,16 +30,13 @@ package com.highcapable.yukihookapi.hook.param.wrapper import android.content.pm.ApplicationInfo -import com.highcapable.yukihookapi.annotation.YukiPrivateApi import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.param.type.HookEntryType -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType import dalvik.system.PathClassLoader /** * 用于包装 [PackageParam] - * - * - ❗这是一个私有 API - 请不要在外部使用 * @param type 当前正在进行的 Hook 类型 * @param packageName 包名 * @param processName 当前进程名 @@ -47,8 +44,8 @@ import dalvik.system.PathClassLoader * @param appInfo APP [ApplicationInfo] * @param appResources APP [YukiResources] */ -@YukiPrivateApi -class PackageParamWrapper internal constructor( +@PublishedApi +internal class PackageParamWrapper internal constructor( var type: HookEntryType, var packageName: String, var processName: String, @@ -68,5 +65,5 @@ class PackageParamWrapper internal constructor( internal val isCorrectProcess get() = type == HookEntryType.ZYGOTE || (type != HookEntryType.ZYGOTE && appClassLoader is PathClassLoader) override fun toString() = - "PackageParamWrapper [type] $type [packageName] $packageName [processName] $processName [appInfo] $appInfo [appResources] $appResources" + "[type] $type [packageName] $packageName [processName] $processName [appClassLoader] $appClassLoader [appInfo] $appInfo [appResources] $appResources" } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt deleted file mode 100644 index 4e28df27..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiHookBridge.kt +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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/4/3. - */ -@file:Suppress("unused", "StaticFieldLeak") - -package com.highcapable.yukihookapi.hook.xposed.bridge - -import android.content.pm.ApplicationInfo -import com.highcapable.yukihookapi.YukiHookAPI -import com.highcapable.yukihookapi.annotation.YukiGenerateApi -import com.highcapable.yukihookapi.hook.factory.classOf -import com.highcapable.yukihookapi.hook.factory.field -import com.highcapable.yukihookapi.hook.factory.hasClass -import com.highcapable.yukihookapi.hook.log.yLoggerE -import com.highcapable.yukihookapi.hook.param.PackageParam -import com.highcapable.yukihookapi.hook.param.type.HookEntryType -import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources -import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus -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 -import de.robv.android.xposed.IXposedHookZygoteInit -import de.robv.android.xposed.XposedBridge -import de.robv.android.xposed.callbacks.XC_InitPackageResources -import de.robv.android.xposed.callbacks.XC_LoadPackage - -/** - * 这是一个对接 Xposed Hook 入口与 [XposedBridge] 的装载类实现桥 - * - * 实现与 [IXposedHookZygoteInit]、[IXposedHookLoadPackage]、[IXposedHookInitPackageResources] 接口通讯 - * - * - ❗装载代码将自动生成 - 请勿修改或移动以及重命名此类的任何方法与变量 - */ -@YukiGenerateApi -object YukiHookBridge { - - /** Android 系统框架名称 */ - @PublishedApi - internal const val SYSTEM_FRAMEWORK_NAME = "android" - - /** Xposed 是否装载完成 */ - private var isXposedInitialized = false - - /** 当前 Hook 进程是否正处于 [IXposedHookZygoteInit.initZygote] */ - private var isInitializingZygote = false - - /** 已在 [PackageParam] 中被装载的 APP 包名 */ - private val loadedPackageNames = HashSet() - - /** 当前 [PackageParamWrapper] 实例数组 */ - private val packageParamWrappers = HashMap() - - /** 当前 [PackageParam] 方法体回调 */ - internal var packageParamCallback: (PackageParam.() -> Unit)? = null - - /** 当前 Hook Framework 是否支持 Resources Hook */ - internal var isSupportResourcesHook = false - - /** - * 当前宿主正在进行的 Hook 进程标识名称 - * @return [String] - */ - internal val hostProcessName get() = if (isInitializingZygote) "android-zygote" else YukiHookAppHelper.currentPackageName() ?: "unknown" - - /** - * 获取项目编译完成的时间戳 (当前本地时间) - * @return [Long] - */ - internal val compiledTimestamp get() = runCatching { YukiHookBridge_Impl.compiledTimestamp }.getOrNull() ?: 0L - - /** - * 自动生成的 Xposed 模块构建版本号 - * - * 获取 [compiledTimestamp] 并转换为字符串 - * @return [String] - */ - internal val moduleGeneratedVersion get() = compiledTimestamp.toString() - - /** - * 预设的 Xposed 模块包名 - * - * - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常 - */ - @YukiGenerateApi - var modulePackageName = "" - - /** - * 模块是否装载了 Xposed 回调方法 - * - * - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常 - * @return [Boolean] - */ - @YukiGenerateApi - val isXposedCallbackSetUp - get() = isXposedInitialized.not() && packageParamCallback != null - - /** - * 获取当前 Hook 框架的名称 - * - * 从 [XposedBridge] 获取 TAG - * @return [String] 无法获取会返回 unknown - [hasXposedBridge] 不存在会返回 invalid - */ - internal val executorName - get() = runCatching { - if (YukiHookModuleStatus.EXPOSED_BRIDGE_CLASS_NAME.hasClass(YukiHookAppHelper.currentApplication()?.classLoader)) - YukiHookModuleStatus.TAICHI_XPOSED_NAME - else classOf().field { name = "TAG" }.ignored().get().string().takeIf { it.isNotBlank() } - ?.replace("Bridge", "")?.replace("-", "")?.trim() ?: "unknown" - }.getOrNull() ?: "invalid" - - /** - * 获取当前 Hook 框架的版本 - * - * 获取 [XposedBridge.getXposedVersion] - * @return [Int] 无法获取会返回 -1 - */ - internal val executorVersion get() = runCatching { XposedBridge.getXposedVersion() }.getOrNull() ?: -1 - - /** - * 是否存在 [XposedBridge] - * @return [Boolean] - */ - internal val hasXposedBridge get() = executorVersion >= 0 - - /** - * 自动忽略 MIUI 系统可能出现的日志收集注入实例 - * @param packageName 当前包名 - * @return [Boolean] 是否存在 - */ - private fun isMiuiCatcherPatch(packageName: String?) = - (packageName == "com.miui.contentcatcher" || packageName == "com.miui.catcherpatch") && "android.miui.R".hasClass() - - /** - * 当前包名是否已在指定的 [HookEntryType] 被装载 - * @param packageName 包名 - * @param type 当前 Hook 类型 - * @return [Boolean] 是否已被装载 - */ - private fun isPackageLoaded(packageName: String, type: HookEntryType): Boolean { - if (loadedPackageNames.contains("$packageName:$type")) return true - loadedPackageNames.add("$packageName:$type") - return false - } - - /** - * 创建、修改 [PackageParamWrapper] - * - * 忽略在 [type] 不为 [HookEntryType.ZYGOTE] 时 [appClassLoader] 为空导致首次使用 [XposedBridge.BOOTCLASSLOADER] 装载的问题 - * @param type 当前正在进行的 Hook 类型 - * @param packageName 包名 - * @param processName 当前进程名 - * @param appClassLoader APP [ClassLoader] - * @param appInfo APP [ApplicationInfo] - * @param appResources APP [YukiResources] - * @return [PackageParamWrapper] or null - */ - private fun assignWrapper( - type: HookEntryType, - packageName: String?, - processName: String? = "", - appClassLoader: ClassLoader? = null, - appInfo: ApplicationInfo? = null, - appResources: YukiResources? = null - ) = run { - isInitializingZygote = type == HookEntryType.ZYGOTE - if (packageParamWrappers[packageName] == null) - if (type == HookEntryType.ZYGOTE || appClassLoader != null) - PackageParamWrapper( - type = type, - packageName = packageName ?: SYSTEM_FRAMEWORK_NAME, - processName = processName ?: SYSTEM_FRAMEWORK_NAME, - appClassLoader = appClassLoader ?: XposedBridge.BOOTCLASSLOADER, - appInfo = appInfo, - appResources = appResources - ).also { packageParamWrappers[packageName ?: SYSTEM_FRAMEWORK_NAME] = it } - else null - else packageParamWrappers[packageName]?.also { wrapper -> - wrapper.type = type - packageName?.takeIf { it.isNotBlank() }?.also { wrapper.packageName = it } - processName?.takeIf { it.isNotBlank() }?.also { wrapper.processName = it } - appClassLoader?.takeIf { type == HookEntryType.ZYGOTE || it is PathClassLoader }?.also { wrapper.appClassLoader = it } - appInfo?.also { wrapper.appInfo = it } - appResources?.also { wrapper.appResources = it } - } - } - - /** - * 标识 Xposed API 装载完成 - * - * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 - */ - @YukiGenerateApi - fun callXposedInitialized() { - isXposedInitialized = true - } - - /** - * 装载 Xposed API Zygote 回调 - * - * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 - * @param sparam Xposed [IXposedHookZygoteInit.StartupParam] - */ - @YukiGenerateApi - fun callXposedZygoteLoaded(sparam: IXposedHookZygoteInit.StartupParam) { - AppParasitics.moduleAppFilePath = sparam.modulePath - AppParasitics.refreshModuleAppResources() - } - - /** - * 装载 Xposed API 回调 - * - * 这里的入口会装载三次 - * - * - 在 [IXposedHookZygoteInit.initZygote] 时装载 - * - * - 在 [IXposedHookLoadPackage.handleLoadPackage] 时装载 - * - * - 在 [IXposedHookInitPackageResources.handleInitPackageResources] 时装载 - * - * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed API 事件 - * @param isZygoteLoaded 是否为 Xposed [IXposedHookZygoteInit.initZygote] - * @param lpparam Xposed [XC_LoadPackage.LoadPackageParam] - * @param resparam Xposed [XC_InitPackageResources.InitPackageResourcesParam] - */ - @YukiGenerateApi - fun callXposedLoaded( - isZygoteLoaded: Boolean, - lpparam: XC_LoadPackage.LoadPackageParam? = null, - resparam: XC_InitPackageResources.InitPackageResourcesParam? = null - ) { - if (isMiuiCatcherPatch(packageName = lpparam?.packageName ?: resparam?.packageName).not()) when { - isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, SYSTEM_FRAMEWORK_NAME, SYSTEM_FRAMEWORK_NAME) - lpparam != null -> - if (isPackageLoaded(lpparam.packageName, HookEntryType.PACKAGE).not()) - assignWrapper(HookEntryType.PACKAGE, lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo) - else null - resparam != null -> - if (isPackageLoaded(resparam.packageName, HookEntryType.RESOURCES).not()) - assignWrapper(HookEntryType.RESOURCES, resparam.packageName, appResources = YukiResources.wrapper(resparam.res)) - else null - else -> null - }?.also { - runCatching { - if (it.isCorrectProcess) YukiHookAPI.onXposedLoaded(it) - if (it.type != HookEntryType.ZYGOTE && it.packageName == modulePackageName) - AppParasitics.hookModuleAppRelated(it.appClassLoader, it.type) - if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName) - if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true - }.onFailure { yLoggerE(msg = "An exception occurred in the Hooking Process of YukiHookAPI", e = it) } - } - } -} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiXposedModule.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiXposedModule.kt new file mode 100644 index 00000000..1b1a2887 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/YukiXposedModule.kt @@ -0,0 +1,217 @@ +/* + * 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/4/3. + * This file is Modified by fankes on 2023/1/9. + */ +package com.highcapable.yukihookapi.hook.xposed.bridge + +import android.content.pm.ApplicationInfo +import android.content.res.Resources +import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper +import com.highcapable.yukihookapi.hook.factory.hasClass +import com.highcapable.yukihookapi.hook.log.yLoggerE +import com.highcapable.yukihookapi.hook.param.PackageParam +import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper +import com.highcapable.yukihookapi.hook.xposed.bridge.proxy.IYukiXposedModuleLifecycle +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiModuleResources +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics +import dalvik.system.PathClassLoader + +/** + * Xposed 模块核心功能实现类 + */ +internal object YukiXposedModule : IYukiXposedModuleLifecycle { + + /** Xposed 模块是否已被装载 */ + private var isModuleLoaded = false + + /** Xposed 模块是否装载完成 */ + private var isModuleLoadFinished = false + + /** 当前 Hook 进程是否正处于 Zygote */ + private var isInitializingZygote = false + + /** 当前 [PackageParam] 实例 */ + private val packageParam = PackageParam() + + /** 已在 [PackageParam] 中被装载的 APP 包名 */ + private val loadedPackageNames = HashSet() + + /** 当前 [PackageParamWrapper] 实例数组 */ + private val packageParamWrappers = HashMap() + + /** 当前 [PackageParam] 方法体回调 */ + internal var packageParamCallback: (PackageParam.() -> Unit)? = null + + /** 当前 Hook Framework 是否支持 Resources Hook */ + internal var isSupportResourcesHook = false + + /** 预设的 Xposed 模块包名 */ + internal var modulePackageName = "" + + /** 当前 Xposed 模块自身 APK 路径 */ + internal var moduleAppFilePath = "" + + /** 当前 Xposed 模块自身 [Resources] */ + internal var moduleAppResources: YukiModuleResources? = null + + /** + * 获取当前 Xposed 模块自身动态 [Resources] + * @return [YukiModuleResources] or null + */ + internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull() + + /** + * 模块是否装载了 Xposed 回调方法 + * @return [Boolean] + */ + internal val isXposedCallbackSetUp get() = isModuleLoadFinished.not() && packageParamCallback != null + + /** + * 当前宿主正在进行的 Hook 进程标识名称 + * @return [String] + */ + internal val hostProcessName get() = if (isInitializingZygote) "android-zygote" else AppParasitics.currentPackageName + + /** + * 获取当前是否为 (Xposed) 宿主环境 + * @return [Boolean] + */ + internal val isXposedEnvironment get() = HookApiCategoryHelper.hasAvailableHookApi && isModuleLoaded + + /** + * 自动忽略 MIUI 系统可能出现的日志收集注入实例 + * @param packageName 当前包名 + * @return [Boolean] 是否存在 + */ + private fun isMiuiCatcherPatch(packageName: String?) = + (packageName == "com.miui.contentcatcher" || packageName == "com.miui.catcherpatch") && "android.miui.R".hasClass() + + /** + * 当前包名是否已在指定的 [HookEntryType] 被装载 + * @param packageName 包名 + * @param type 当前 Hook 类型 + * @return [Boolean] 是否已被装载 + */ + private fun isPackageLoaded(packageName: String?, type: HookEntryType): Boolean { + if (packageName == null) return false + if (loadedPackageNames.contains("$packageName:$type")) return true + loadedPackageNames.add("$packageName:$type") + return false + } + + /** + * 创建、修改 [PackageParamWrapper] + * + * 忽略在 [type] 不为 [HookEntryType.ZYGOTE] 时 [appClassLoader] 为空导致首次使用 [ClassLoader.getSystemClassLoader] 装载的问题 + * @param type 当前正在进行的 Hook 类型 + * @param packageName 包名 + * @param processName 当前进程名 + * @param appClassLoader APP [ClassLoader] + * @param appInfo APP [ApplicationInfo] + * @param appResources APP [YukiResources] + * @return [PackageParamWrapper] or null + */ + private fun assignWrapper( + type: HookEntryType, + packageName: String?, + processName: String? = "", + appClassLoader: ClassLoader? = null, + appInfo: ApplicationInfo? = null, + appResources: YukiResources? = null + ) = run { + isInitializingZygote = type == HookEntryType.ZYGOTE + if (packageParamWrappers[packageName] == null) + if (type == HookEntryType.ZYGOTE || appClassLoader != null) + PackageParamWrapper( + type = type, + packageName = packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME, + processName = processName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME, + appClassLoader = appClassLoader ?: ClassLoader.getSystemClassLoader(), + appInfo = appInfo, + appResources = appResources + ).also { packageParamWrappers[packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME] = it } + else null + else packageParamWrappers[packageName]?.also { wrapper -> + wrapper.type = type + packageName?.takeIf { it.isNotBlank() }?.also { wrapper.packageName = it } + processName?.takeIf { it.isNotBlank() }?.also { wrapper.processName = it } + appClassLoader?.takeIf { type == HookEntryType.ZYGOTE || it is PathClassLoader }?.also { wrapper.appClassLoader = it } + appInfo?.also { wrapper.appInfo = it } + appResources?.also { wrapper.appResources = it } + } + } + + /** 刷新当前 Xposed 模块自身 [Resources] */ + internal fun refreshModuleAppResources() { + dynamicModuleAppResources?.let { moduleAppResources = it } + } + + override fun onStartLoadModule(packageName: String, appFilePath: String) { + isModuleLoaded = true + modulePackageName = packageName + moduleAppFilePath = appFilePath + refreshModuleAppResources() + } + + override fun onFinishLoadModule() { + isModuleLoadFinished = true + } + + override fun onPackageLoaded( + type: HookEntryType, + packageName: String?, + processName: String?, + appClassLoader: ClassLoader?, + appInfo: ApplicationInfo?, + appResources: YukiResources? + ) { + if (isMiuiCatcherPatch(packageName).not()) when (type) { + HookEntryType.ZYGOTE -> + assignWrapper(HookEntryType.ZYGOTE, AppParasitics.SYSTEM_FRAMEWORK_NAME, AppParasitics.SYSTEM_FRAMEWORK_NAME, appClassLoader) + HookEntryType.PACKAGE -> + if (isPackageLoaded(packageName, HookEntryType.PACKAGE).not()) + assignWrapper(HookEntryType.PACKAGE, packageName, processName, appClassLoader, appInfo) + else null + HookEntryType.RESOURCES -> + /** 这里可能会出现 [packageName] 获取到非实际宿主的问题 - 如果包名与 [AppParasitics.currentPackageName] 不相同这里做忽略处理 */ + if (isPackageLoaded(packageName, HookEntryType.RESOURCES).not() && packageName == AppParasitics.currentPackageName) + assignWrapper(HookEntryType.RESOURCES, packageName, appResources = appResources) + else null + }?.also { + runCatching { + if (it.isCorrectProcess) packageParamCallback?.invoke(packageParam.assign(it).apply { YukiHookAPI.printSplashInfo() }) + if (it.type != HookEntryType.ZYGOTE && it.packageName == modulePackageName) + AppParasitics.hookModuleAppRelated(it.appClassLoader, it.type) + if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName) + if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true + }.onFailure { yLoggerE(msg = "An exception occurred in the Hooking Process of YukiHookAPI", e = it) } + } + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/caller/YukiXposedModuleCaller.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/caller/YukiXposedModuleCaller.kt new file mode 100644 index 00000000..ab0b2543 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/caller/YukiXposedModuleCaller.kt @@ -0,0 +1,104 @@ +/* + * 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 2023/1/9. + */ +@file:Suppress("unused") + +package com.highcapable.yukihookapi.hook.xposed.bridge.caller + +import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.annotation.YukiGenerateApi +import com.highcapable.yukihookapi.hook.log.yLoggerE +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType + +/** + * Xposed 模块核心功能调用类 + * + * - ❗装载代码将自动生成 - 请勿手动调用 + */ +@YukiGenerateApi +object YukiXposedModuleCaller { + + /** + * 模块是否装载了 Xposed 回调方法 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @return [Boolean] + */ + @YukiGenerateApi + val isXposedCallbackSetUp get() = YukiXposedModule.isXposedCallbackSetUp + + /** + * 标识 Xposed 模块开始装载 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @param packageName 当前 Xposed 模块包名 + * @param appFilePath 当前 Xposed 模块自身 APK 路径 + */ + @YukiGenerateApi + fun callOnStartLoadModule(packageName: String, appFilePath: String) = YukiXposedModule.onStartLoadModule(packageName, appFilePath) + + /** + * 标识 Xposed 模块装载完成 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + */ + @YukiGenerateApi + fun callOnFinishLoadModule() = YukiXposedModule.onFinishLoadModule() + + /** + * 标识可用的 Hook APP (宿主) 开始装载 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @param type 当前正在进行的 Hook 类型 + * @param packageName 宿主包名 + * @param processName 宿主进程名 + * @param appClassLoader 宿主 [ClassLoader] + * @param appInfo 宿主 [ApplicationInfo] + * @param appResources 宿主 [YukiResources] + */ + @YukiGenerateApi + fun callOnPackageLoaded( + type: HookEntryType, + packageName: String?, + processName: String? = "", + appClassLoader: ClassLoader? = null, + appInfo: ApplicationInfo? = null, + appResources: YukiResources? = null + ) = YukiXposedModule.onPackageLoaded(type, packageName, processName, appClassLoader, appInfo, appResources) + + /** + * 打印内部 E 级别的日志 + * + * - ❗装载代码将自动生成 - 请勿手动调用 + * @param msg 日志打印的内容 + * @param e 异常堆栈信息 - 默认空 + */ + @YukiGenerateApi + fun internalLoggerE(msg: String, e: Throwable? = null) = yLoggerE(msg = msg, e = e) +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/YukiXposedEvent.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/YukiXposedEvent.kt index 32f23faa..2eccdc99 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/YukiXposedEvent.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/YukiXposedEvent.kt @@ -24,12 +24,12 @@ * SOFTWARE. * * This file is Created by fankes on 2022/4/30. + * This file is Modified by fankes on 2022/1/10. */ @file:Suppress("unused") package com.highcapable.yukihookapi.hook.xposed.bridge.event -import com.highcapable.yukihookapi.annotation.YukiGenerateApi import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam @@ -40,13 +40,13 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam object YukiXposedEvent { /** 监听 initZygote 开始的回调方法 */ - private var initZygoteCallback: ((StartupParam) -> Unit)? = null + internal var initZygoteCallback: ((StartupParam) -> Unit)? = null /** 监听 handleLoadPackage 开始的回调方法 */ - private var handleLoadPackageCallback: ((LoadPackageParam) -> Unit)? = null + internal var handleLoadPackageCallback: ((LoadPackageParam) -> Unit)? = null /** 监听 handleInitPackageResources 开始的回调方法 */ - private var handleInitPackageResourcesCallback: ((InitPackageResourcesParam) -> Unit)? = null + internal var handleInitPackageResourcesCallback: ((InitPackageResourcesParam) -> Unit)? = null /** * 对 [YukiXposedEvent] 创建一个方法体 @@ -79,49 +79,4 @@ object YukiXposedEvent { fun onHandleInitPackageResources(result: (InitPackageResourcesParam) -> Unit) { handleInitPackageResourcesCallback = result } - - /** - * 回调监听事件处理类 - * - * - ❗装载代码将自动生成 - 请勿手动调用 - */ - @YukiGenerateApi - object EventHandler { - - /** - * 回调 initZygote 事件监听 - * - * - ❗装载代码将自动生成 - 请勿手动调用 - * @param sparam Xposed API 实例 - */ - @YukiGenerateApi - fun callInitZygote(sparam: StartupParam?) { - if (sparam == null) return - initZygoteCallback?.invoke(sparam) - } - - /** - * 回调 handleLoadPackage 事件监听 - * - * - ❗装载代码将自动生成 - 请勿手动调用 - * @param lpparam Xposed API 实例 - */ - @YukiGenerateApi - fun callHandleLoadPackage(lpparam: LoadPackageParam?) { - if (lpparam == null) return - handleLoadPackageCallback?.invoke(lpparam) - } - - /** - * 回调 handleInitPackageResources 事件监听 - * - * - ❗装载代码将自动生成 - 请勿手动调用 - * @param resparam Xposed API 实例 - */ - @YukiGenerateApi - fun callHandleInitPackageResources(resparam: InitPackageResourcesParam?) { - if (resparam == null) return - handleInitPackageResourcesCallback?.invoke(resparam) - } - } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/caller/YukiXposedEventCaller.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/caller/YukiXposedEventCaller.kt new file mode 100644 index 00000000..810b0d2d --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/event/caller/YukiXposedEventCaller.kt @@ -0,0 +1,81 @@ +/* + * 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/1/10. + */ +@file:Suppress("unused") + +package com.highcapable.yukihookapi.hook.xposed.bridge.event.caller + +import com.highcapable.yukihookapi.annotation.YukiGenerateApi +import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent +import de.robv.android.xposed.IXposedHookZygoteInit +import de.robv.android.xposed.callbacks.XC_InitPackageResources +import de.robv.android.xposed.callbacks.XC_LoadPackage + +/** + * 实现对原生 Xposed API 装载事件监听的回调监听事件处理类 + * + * - ❗装载代码将自动生成 - 请勿手动调用 + */ +@YukiGenerateApi +object YukiXposedEventCaller { + + /** + * 回调 initZygote 事件监听 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @param sparam Xposed API 实例 + */ + @YukiGenerateApi + fun callInitZygote(sparam: IXposedHookZygoteInit.StartupParam?) { + if (sparam == null) return + YukiXposedEvent.initZygoteCallback?.invoke(sparam) + } + + /** + * 回调 handleLoadPackage 事件监听 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @param lpparam Xposed API 实例 + */ + @YukiGenerateApi + fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { + if (lpparam == null) return + YukiXposedEvent.handleLoadPackageCallback?.invoke(lpparam) + } + + /** + * 回调 handleInitPackageResources 事件监听 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + * @param resparam Xposed API 实例 + */ + @YukiGenerateApi + fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) { + if (resparam == null) return + YukiXposedEvent.handleInitPackageResourcesCallback?.invoke(resparam) + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt deleted file mode 100644 index 18201fef..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/factory/YukiBridgeFactory.kt +++ /dev/null @@ -1,299 +0,0 @@ -/* - * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. - * Copyright (C) 2019-2022 HighCapable - * https://github.com/fankes/YukiHookAPI - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is Created by fankes on 2022/7/28. - */ -package com.highcapable.yukihookapi.hook.xposed.bridge.factory - -import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder -import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder -import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder -import com.highcapable.yukihookapi.hook.log.yLoggerE -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import de.robv.android.xposed.XC_MethodHook -import de.robv.android.xposed.XposedBridge -import de.robv.android.xposed.XposedHelpers -import java.lang.reflect.Member - -/** - * Hook 回调优先级配置类 - */ -internal object YukiHookPriority { - - /** 默认 Hook 回调优先级 */ - internal const val PRIORITY_DEFAULT = 50 - - /** 延迟回调 Hook 方法结果 */ - internal const val PRIORITY_LOWEST = -10000 - - /** 更快回调 Hook 方法结果 */ - internal const val PRIORITY_HIGHEST = 10000 -} - -/** - * 已经 Hook 的方法、构造方法缓存数组 - */ -internal object YukiHookedMembers { - - /** 已经 Hook 的 [Member] 数组 */ - internal val hookedMembers = HashSet() -} - -/** - * Hook 核心功能实现实例 - * - * 对接 [XposedBridge] 实现 Hook 功能 - */ -internal object YukiHookHelper { - - /** - * 使用 [XposedHelpers.findClass] 来查找 [Class] - * @param name [Class] 的完整包名+名称 - * @param loader [Class] 所在的 [ClassLoader] - 默认空 - 可不填 - * @return [Class] or null - * @throws ClassNotFoundException 如果找不到 [Class] - */ - internal fun findClass(name: String, loader: ClassLoader? = null) = - if (YukiHookBridge.hasXposedBridge) XposedHelpers.findClass(name, loader) else null - - /** - * Hook [BaseFinder.BaseResult] - * @param traction 直接调用 [BaseFinder.BaseResult] - * @param callback 回调 - * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) - */ - internal fun hook(traction: BaseFinder.BaseResult, callback: YukiHookCallback) = runCatching { - val member: Member? = when (traction) { - is MethodFinder.Result -> traction.ignored().give() - is ConstructorFinder.Result -> traction.ignored().give() - else -> error("Unexpected BaseFinder result interface type") - } - hookMember(member, callback) - }.onFailure { yLoggerE(msg = "An exception occurred when hooking internal function", e = it) }.getOrNull() ?: Pair(null, false) - - /** - * Hook [Member] - * - * 对接 [XposedBridge.hookMethod] - * @param member 需要 Hook 的方法、构造方法 - * @param callback 回调 - * @return [Pair] - ([YukiMemberHook.Unhook] or null,[Boolean] 是否已经 Hook) - */ - internal fun hookMember(member: Member?, callback: YukiHookCallback): Pair { - runCatching { - YukiHookedMembers.hookedMembers.takeIf { it.isNotEmpty() }?.forEach { - if (it.member.toString() == member?.toString()) return@runCatching it - } - } - return when { - member == null -> Pair(null, false) - YukiHookBridge.hasXposedBridge -> - XposedBridge.hookMethod(member, callback.compat()).compat().let { memberUnhook -> - YukiHookedMembers.hookedMembers.add(memberUnhook) - Pair(memberUnhook, false) - } - else -> Pair(null, false) - } - } - - /** - * 执行原始 [Member] - * - * 未进行 Hook 的 [Member] - * @param member 实例 - * @param args 参数实例 - * @return [Any] or null - */ - internal fun invokeOriginalMember(member: Member?, instance: Any?, vararg args: Any?) = - if (YukiHookBridge.hasXposedBridge && YukiHookedMembers.hookedMembers.any { it.member.toString() == member.toString() }) - member?.let { - runCatching { XposedBridge.invokeOriginalMethod(it, instance, args) } - .onFailure { yLoggerE(msg = "Invoke original Member [$member] failed", e = it) } - .getOrNull() - } - else null - - /** - * 兼容对接已 Hook 的 [Member] 接口 - * @return [YukiMemberHook.Unhook] - */ - private fun XC_MethodHook.Unhook.compat() = object : YukiMemberHook.Unhook() { - override val member get() = hookedMethod - override fun unhook() = this@compat.unhook() - } - - /** - * 兼容对接 Hook 回调接口 - * @return [XC_MethodHook] 原始接口 - */ - private fun YukiHookCallback.compat() = object : XC_MethodHook(priority) { - - override fun beforeHookedMethod(param: MethodHookParam?) { - if (param == null) return - if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type") - if (this@compat is YukiMemberReplacement) - param.result = replaceHookedMember(param.compat()) - else beforeHookedMember(param.compat()) - } - - override fun afterHookedMethod(param: MethodHookParam?) { - if (param == null) return - if (this@compat !is YukiMemberHook) error("Invalid YukiHookCallback type") - afterHookedMember(param.compat()) - } - } - - /** - * 兼容对接 Hook 结果回调接口 - * @return [YukiHookCallback.Param] - */ - private fun XC_MethodHook.MethodHookParam.compat() = object : YukiHookCallback.Param { - override val member get() = this@compat.method - override val instance get() = this@compat.thisObject - override val args get() = this@compat.args - override val hasThrowable get() = this@compat.hasThrowable() - override var result - get() = this@compat.result - set(value) { - this@compat.result = value - } - override var throwable - get() = this@compat.throwable - set(value) { - this@compat.throwable = value - } - } -} - -/** - * Hook 替换方法回调接口抽象类 - * @param priority Hook 优先级- 默认 [YukiHookPriority.PRIORITY_DEFAULT] - */ -internal abstract class YukiMemberReplacement(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiMemberHook(priority) { - - override fun beforeHookedMember(param: Param) { - param.result = replaceHookedMember(param) - } - - override fun afterHookedMember(param: Param) {} - - /** - * 拦截替换为指定结果 - * @param param Hook 结果回调接口 - * @return [Any] or null - */ - abstract fun replaceHookedMember(param: Param): Any? -} - -/** - * Hook 方法回调接口抽象类 - * @param priority Hook 优先级 - 默认 [YukiHookPriority.PRIORITY_DEFAULT] - */ -internal abstract class YukiMemberHook(override val priority: Int = YukiHookPriority.PRIORITY_DEFAULT) : YukiHookCallback(priority) { - - /** - * 在方法执行之前注入 - * @param param Hook 结果回调接口 - */ - open fun beforeHookedMember(param: Param) {} - - /** - * 在方法执行之后注入 - * @param param Hook 结果回调接口 - */ - open fun afterHookedMember(param: Param) {} - - /** - * 已经 Hook 且可被解除 Hook 的 [Member] 实现接口抽象类 - */ - internal abstract class Unhook internal constructor() { - - /** - * 当前被 Hook 的 [Member] - * @return [Member] or null - */ - internal abstract val member: Member? - - /** 解除 Hook */ - internal abstract fun unhook() - - /** 解除 Hook 并从 [YukiHookedMembers.hookedMembers] 缓存数组中移除 */ - internal fun remove() { - if (YukiHookBridge.hasXposedBridge.not()) return - unhook() - runCatching { YukiHookedMembers.hookedMembers.remove(this) } - } - } -} - -/** - * Hook 回调接口抽象类 - * @param priority Hook 优先级 - */ -internal abstract class YukiHookCallback(open val priority: Int) { - - /** - * Hook 结果回调接口 - */ - internal interface Param { - - /** - * [Member] 实例 - * @return [Member] or null - */ - val member: Member? - - /** - * 当前实例对象 - * @return [Any] or null - */ - val instance: Any? - - /** - * 方法、构造方法数组 - * @return [Array] or null - */ - val args: Array? - - /** - * 获取、设置方法结果 - * @return [Any] or null - */ - var result: Any? - - /** - * 判断是否存在设置过的方法调用抛出异常 - * @return [Boolean] - */ - val hasThrowable: Boolean - - /** - * 获取、设置方法调用抛出的异常 - * @return [Throwable] or null - * @throws Throwable - */ - var throwable: Throwable? - } -} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/proxy/IYukiXposedModuleLifecycle.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/proxy/IYukiXposedModuleLifecycle.kt new file mode 100644 index 00000000..7fb132ce --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/proxy/IYukiXposedModuleLifecycle.kt @@ -0,0 +1,66 @@ +/* + * 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 2023/1/11. + */ +package com.highcapable.yukihookapi.hook.xposed.bridge.proxy + +import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType + +/** + * Xposed 模块生命周期实现接口 + */ +internal interface IYukiXposedModuleLifecycle { + + /** + * 当 Xposed 模块开始装载 + * @param packageName 当前 Xposed 模块包名 + * @param appFilePath 当前 Xposed 模块自身 APK 路径 + */ + fun onStartLoadModule(packageName: String, appFilePath: String) + + /** 当 Xposed 模块装载完成 */ + fun onFinishLoadModule() + + /** + * 当可用的 Hook APP (宿主) 开始装载 + * @param type 当前正在进行的 Hook 类型 + * @param packageName 宿主包名 + * @param processName 宿主进程名 + * @param appClassLoader 宿主 [ClassLoader] + * @param appInfo 宿主 [ApplicationInfo] + * @param appResources 宿主 [YukiResources] + */ + fun onPackageLoaded( + type: HookEntryType, + packageName: String?, + processName: String? = "", + appClassLoader: ClassLoader? = null, + appInfo: ApplicationInfo? = null, + appResources: YukiResources? = null + ) +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiModuleResources.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiModuleResources.kt similarity index 97% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiModuleResources.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiModuleResources.kt index f11802e6..9876e9c2 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiModuleResources.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiModuleResources.kt @@ -27,7 +27,7 @@ */ @file:Suppress("DEPRECATION") -package com.highcapable.yukihookapi.hook.xposed.bridge.dummy +package com.highcapable.yukihookapi.hook.xposed.bridge.resources import android.content.res.Resources import android.content.res.XModuleResources diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResForwarder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResForwarder.kt similarity index 97% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResForwarder.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResForwarder.kt index 7acee0b7..e96e5901 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResForwarder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResForwarder.kt @@ -27,7 +27,7 @@ */ @file:Suppress("unused") -package com.highcapable.yukihookapi.hook.xposed.bridge.dummy +package com.highcapable.yukihookapi.hook.xposed.bridge.resources import android.content.res.Resources import android.content.res.XResForwarder diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResources.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResources.kt similarity index 98% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResources.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResources.kt index be365c78..fc3afb15 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/dummy/YukiResources.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/YukiResources.kt @@ -27,14 +27,14 @@ */ @file:Suppress("unused", "DEPRECATION", "DiscouragedApi") -package com.highcapable.yukihookapi.hook.xposed.bridge.dummy +package com.highcapable.yukihookapi.hook.xposed.bridge.resources import android.content.res.Resources import android.content.res.XResources import android.graphics.drawable.Drawable import android.view.View import com.highcapable.yukihookapi.hook.log.yLoggerE -import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources.LayoutInflatedParam +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources.LayoutInflatedParam import de.robv.android.xposed.callbacks.XC_LayoutInflated /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/caller/YukiXposedResourcesCaller.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/caller/YukiXposedResourcesCaller.kt new file mode 100644 index 00000000..aee81329 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/resources/caller/YukiXposedResourcesCaller.kt @@ -0,0 +1,53 @@ +/* + * 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 2023/1/10. + */ +@file:Suppress("unused") + +package com.highcapable.yukihookapi.hook.xposed.bridge.resources.caller + +import android.content.res.XResources +import com.highcapable.yukihookapi.annotation.YukiGenerateApi +import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources + +/** + * Xposed 模块资源钩子 (Resources Hook) 调用类 + * + * - ❗装载代码将自动生成 - 请勿手动调用 + */ +@YukiGenerateApi +object YukiXposedResourcesCaller { + + /** + * 从 [XResources] 创建 [YukiResources] + * + * - ❗装载代码将自动生成 - 请勿手动调用 + * @param xResources [XResources] 实例 + * @return [YukiResources] or null + */ + @YukiGenerateApi + fun createYukiResourcesFromXResources(xResources: XResources?) = xResources?.let { YukiResources.wrapper(it) } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt similarity index 62% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt index 3d9ae803..706e9cdd 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiHookModuleStatus.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt @@ -24,11 +24,11 @@ * SOFTWARE. * * This file is Created by fankes on 2022/2/3. + * This file is Modified by fankes on 2023/1/9. */ package com.highcapable.yukihookapi.hook.xposed.bridge.status import com.highcapable.yukihookapi.YukiHookAPI -import de.robv.android.xposed.XposedBridge /** * 这是一个 Xposed 模块 Hook 状态类 @@ -39,58 +39,23 @@ import de.robv.android.xposed.XposedBridge * * 调用 [YukiHookAPI.Status.isXposedModuleActive] * - * 你还可以使用以下方法获取当前 Hook 框架的详细信息 - * - * 调用 [YukiHookAPI.Status.executorName] 来获取当前 Hook 框架的名称 - * - * 调用 [YukiHookAPI.Status.executorVersion] 来获取当前 Hook 框架的版本 + * 你还可以通过调用 [YukiHookAPI.Status.Executor] 获取当前 Hook 框架的详细信息 * * 详情请参考 [Xposed 模块判断自身激活状态](https://fankes.github.io/YukiHookAPI/zh-cn/guide/example#xposed-%E6%A8%A1%E5%9D%97%E5%88%A4%E6%96%AD%E8%87%AA%E8%BA%AB%E6%BF%80%E6%B4%BB%E7%8A%B6%E6%80%81) * * For English version, see [Xposed Module own Active State](https://fankes.github.io/YukiHookAPI/en/guide/example#xposed-module-own-active-state) */ -internal object YukiHookModuleStatus { +internal object YukiXposedModuleStatus { - /** 定义 Jvm 方法名 */ internal const val IS_ACTIVE_METHOD_NAME = "__--" - - /** 定义 Jvm 方法名 */ internal const val IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME = "_--_" + internal const val GET_EXECUTOR_NAME_METHOD_NAME = "_-_-" + internal const val GET_EXECUTOR_API_LEVEL_METHOD_NAME = "-__-" + internal const val GET_EXECUTOR_VERSION_NAME_METHOD_NAME = "-_-_" + internal const val GET_EXECUTOR_VERSION_CODE_METHOD_NAME = "___-" - /** 定义 Jvm 方法名 */ - internal const val GET_XPOSED_VERSION_METHOD_NAME = "--__" - - /** 定义 Jvm 方法名 */ - internal const val GET_XPOSED_TAG_METHOD_NAME = "_-_-" - - /** TaiChi Xposed 框架名称 */ - internal const val TAICHI_XPOSED_NAME = "TaiChi" - - /** TaiChi ExposedBridge 完整类名 */ - internal const val EXPOSED_BRIDGE_CLASS_NAME = "me.weishu.exposed.ExposedBridge" - - /** [YukiHookModuleStatus_Impl] 完整类名 */ - internal const val IMPL_CLASS_NAME = "com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus_Impl" - - /** - * 获取当前 Hook 框架的名称 - * - * 从 [XposedBridge] 获取 TAG - * - * 请使用 [YukiHookAPI.Status.executorName] 获取 - * @return [String] 模块未激活会返回 unknown - */ - internal val executorName get() = runCatching { YukiHookModuleStatus_Impl.getXposedBridgeTag() }.getOrNull() ?: "unknown" - - /** - * 获取当前 Hook 框架的版本 - * - * 获取 [XposedBridge.getXposedVersion] - * - * 请使用 [YukiHookAPI.Status.executorVersion] 获取 - * @return [Int] 模块未激活会返回 -1 - */ - internal val executorVersion get() = runCatching { YukiHookModuleStatus_Impl.getXposedVersion() }.getOrNull() ?: -1 + /** [YukiXposedModuleStatus_Impl] 完整类名 */ + internal const val IMPL_CLASS_NAME = "com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiXposedModuleStatus_Impl" /** * 获取当前模块的激活状态 @@ -98,7 +63,7 @@ internal object YukiHookModuleStatus { * 请使用 [YukiHookAPI.Status.isModuleActive]、[YukiHookAPI.Status.isXposedModuleActive]、[YukiHookAPI.Status.isTaiChiModuleActive] 判断模块激活状态 * @return [Boolean] */ - internal val isActive get() = runCatching { YukiHookModuleStatus_Impl.isActive() }.getOrNull() ?: false + internal val isActive get() = runCatching { YukiXposedModuleStatus_Impl.isActive() }.getOrNull() ?: false /** * 获取当前 Hook Framework 是否支持资源钩子 (Resources Hook) @@ -106,5 +71,37 @@ internal object YukiHookModuleStatus { * 请使用 [YukiHookAPI.Status.isSupportResourcesHook] 判断支持状态 * @return [Boolean] */ - internal val isSupportResourcesHook get() = runCatching { YukiHookModuleStatus_Impl.isSupportResourcesHook() }.getOrNull() ?: false + internal val isSupportResourcesHook get() = runCatching { YukiXposedModuleStatus_Impl.isSupportResourcesHook() }.getOrNull() ?: false + + /** + * 获取当前 Hook 框架的名称 + * + * 请使用 [YukiHookAPI.Status.Executor.name] 获取 + * @return [String] 模块未激活会返回 unknown + */ + internal val executorName get() = runCatching { YukiXposedModuleStatus_Impl.getExecutorName() }.getOrNull() ?: "unknown" + + /** + * 获取当前 Hook 框架的 API 版本 + * + * 请使用 [YukiHookAPI.Status.Executor.apiLevel] 获取 + * @return [Int] 模块未激活会返回 -1 + */ + internal val executorApiLevel get() = runCatching { YukiXposedModuleStatus_Impl.getExecutorApiLevel() }.getOrNull() ?: -1 + + /** + * 获取当前 Hook 框架的版本名称 + * + * 请使用 [YukiHookAPI.Status.Executor.versionName] 获取 + * @return [Int] 模块未激活会返回 unknown + */ + internal val executorVersionName get() = runCatching { YukiXposedModuleStatus_Impl.getExecutorVersionName() }.getOrNull() ?: "unknown" + + /** + * 获取当前 Hook 框架的版本号 + * + * 请使用 [YukiHookAPI.Status.Executor.versionCode] 获取 + * @return [Int] 模块未激活会返回 -1 + */ + internal val executorVersionCode get() = runCatching { YukiXposedModuleStatus_Impl.getExecutorVersionCode() }.getOrNull() ?: -1 } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/type/HookEntryType.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/type/HookEntryType.kt similarity index 68% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/type/HookEntryType.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/type/HookEntryType.kt index 75dda8bc..442764f8 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/type/HookEntryType.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/bridge/type/HookEntryType.kt @@ -24,33 +24,26 @@ * SOFTWARE. * * This file is Created by fankes on 2022/4/26. + * This file is Modified by fankes on 2023/1/9. */ -package com.highcapable.yukihookapi.hook.param.type +package com.highcapable.yukihookapi.hook.xposed.bridge.type -import com.highcapable.yukihookapi.annotation.YukiPrivateApi -import com.highcapable.yukihookapi.hook.param.type.HookEntryType.* -import de.robv.android.xposed.IXposedHookInitPackageResources -import de.robv.android.xposed.IXposedHookLoadPackage -import de.robv.android.xposed.IXposedHookZygoteInit +import com.highcapable.yukihookapi.annotation.YukiGenerateApi /** * 当前正在进行的 Hook 类型 * - * [ZYGOTE] 为 [IXposedHookZygoteInit.initZygote] - * - * [PACKAGE] 为 [IXposedHookLoadPackage.handleLoadPackage] - * - * [RESOURCES] 为 [IXposedHookInitPackageResources.handleInitPackageResources] + * - ❗装载代码将自动生成 - 请勿手动调用 */ -@YukiPrivateApi +@YukiGenerateApi enum class HookEntryType { - /** initZygote */ + /** 装载 Zygote */ ZYGOTE, - /** handleLoadPackage */ + /** 装载 APP */ PACKAGE, - /** handleInitPackageResources */ + /** 装载 Resources Hook */ RESOURCES } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt index b50b3b7c..7a3204a4 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt @@ -25,7 +25,7 @@ * * This file is Created by fankes on 2022/5/16. */ -@file:Suppress("StaticFieldLeak", "UNCHECKED_CAST", "unused", "MemberVisibilityCanBePrivate", "DEPRECATION", "KotlinConstantConditions") +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST", "StaticFieldLeak", "KotlinConstantConditions") package com.highcapable.yukihookapi.hook.xposed.channel @@ -48,11 +48,11 @@ import com.highcapable.yukihookapi.hook.log.YukiLoggerData import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData import com.highcapable.yukihookapi.hook.xposed.channel.data.wrapper.ChannelDataWrapper import com.highcapable.yukihookapi.hook.xposed.channel.priority.ChannelPriority -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import java.io.Serializable import java.util.concurrent.ConcurrentHashMap import kotlin.math.round @@ -75,7 +75,10 @@ class YukiHookDataChannel private constructor() { internal companion object { /** 是否为 (Xposed) 宿主环境 */ - private val isXposedEnvironment = YukiHookBridge.hasXposedBridge + private val isXposedEnvironment = YukiXposedModule.isXposedEnvironment + + /** 自动生成的 Xposed 模块构建版本号 */ + private val moduleGeneratedVersion = YukiHookAPI.Status.compiledTimestamp.toString() /** * 系统广播允许发送的最大数据字节大小 @@ -154,7 +157,7 @@ class YukiHookDataChannel private constructor() { /** 检查 API 装载状态 */ private fun checkApi() { if (YukiHookAPI.isLoadedFromBaseContext) error("YukiHookDataChannel not allowed in Custom Hook API") - if (YukiHookBridge.hasXposedBridge && YukiHookBridge.modulePackageName.isBlank()) + if (isXposedEnvironment && YukiXposedModule.modulePackageName.isBlank()) error("Xposed modulePackageName load failed, please reset and rebuild it") isAllowSendTooLargeData = false } @@ -165,6 +168,7 @@ class YukiHookDataChannel private constructor() { * @return [Boolean] */ private fun isCurrentBroadcast(context: Context?) = runCatching { + @Suppress("DEPRECATION") context is Application || isXposedEnvironment || (((context ?: receiverContext) ?.getSystemService(ACTIVITY_SERVICE) as? ActivityManager?) ?.getRunningTasks(9999)?.filter { context?.javaClass?.name == it?.topActivity?.className }?.size ?: 0) > 0 @@ -183,7 +187,7 @@ class YukiHookDataChannel private constructor() { * @return [String] */ private fun moduleActionName(context: Context? = null) = "yukihookapi.intent.action.MODULE_DATA_CHANNEL_${ - YukiHookBridge.modulePackageName.ifBlank { context?.packageName ?: "" }.trim().hashCode() + YukiXposedModule.modulePackageName.ifBlank { context?.packageName ?: "" }.trim().hashCode() }" /** @@ -204,7 +208,7 @@ class YukiHookDataChannel private constructor() { nameSpace(context, packageName, isSecure = false).with { /** 注册监听模块与宿主的版本是否匹配 */ wait(GET_MODULE_GENERATED_VERSION) { fromPackageName -> - nameSpace(context, fromPackageName, isSecure = false).put(RESULT_MODULE_GENERATED_VERSION, YukiHookBridge.moduleGeneratedVersion) + nameSpace(context, fromPackageName, isSecure = false).put(RESULT_MODULE_GENERATED_VERSION, moduleGeneratedVersion) } /** 注册监听模块与宿主之间的调试日志数据 */ wait(GET_YUKI_LOGGER_INMEMORY_DATA) { fromPackageName -> @@ -368,7 +372,7 @@ class YukiHookDataChannel private constructor() { * @param result 回调是否匹配 */ fun checkingVersionEquals(priority: ChannelPriority? = null, result: (Boolean) -> Unit) { - wait(RESULT_MODULE_GENERATED_VERSION, priority) { result(it == YukiHookBridge.moduleGeneratedVersion) } + wait(RESULT_MODULE_GENERATED_VERSION, priority) { result(it == moduleGeneratedVersion) } put(GET_MODULE_GENERATED_VERSION, packageName) } @@ -651,11 +655,11 @@ class YukiHookDataChannel private constructor() { if (isSecure && context != null) if (isXposedEnvironment.not() && context !is Activity) error("YukiHookDataChannel only support used on an Activity, but this current context is \"${context.javaClass.name}\"") /** 发送广播 */ - (context ?: YukiHookAppHelper.currentApplication())?.sendBroadcast(Intent().apply { + (context ?: AppParasitics.currentApplication)?.sendBroadcast(Intent().apply { action = if (isXposedEnvironment) moduleActionName() else hostActionName(packageName) /** 由于系统框架的包名可能不唯一 - 为防止发生问题不再对系统框架的广播设置接收者包名 */ - if (packageName != YukiHookBridge.SYSTEM_FRAMEWORK_NAME) - setPackage(if (isXposedEnvironment) YukiHookBridge.modulePackageName else packageName) + if (packageName != AppParasitics.SYSTEM_FRAMEWORK_NAME) + setPackage(if (isXposedEnvironment) YukiXposedModule.modulePackageName else packageName) putExtra(wrapper.instance.key + keyNonRepeatName, wrapper) }) ?: yLoggerE(msg = "Failed to sendBroadcast like \"${wrapper.instance.key}\", because got null context in \"$packageName\"") } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt index 50268393..b6072f09 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt @@ -30,11 +30,9 @@ package com.highcapable.yukihookapi.hook.xposed.parasitic -import android.app.Activity -import android.app.ActivityManager -import android.app.Application -import android.app.Instrumentation +import android.app.* import android.content.* +import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources @@ -42,21 +40,20 @@ import android.os.Build import android.os.Handler import androidx.annotation.RequiresApi import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.core.api.compat.HookApiProperty +import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberHook +import com.highcapable.yukihookapi.hook.core.api.proxy.YukiMemberReplacement 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.param.type.HookEntryType import com.highcapable.yukihookapi.hook.type.android.* import com.highcapable.yukihookapi.hook.type.java.* -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.YukiHookCallback -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.status.YukiHookModuleStatus +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule +import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiXposedModuleStatus +import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.HandlerDelegate import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.IActivityManagerProxy @@ -69,6 +66,9 @@ import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.Instr */ internal object AppParasitics { + /** Android 系统框架名称 */ + internal const val SYSTEM_FRAMEWORK_NAME = "android" + /** [YukiHookDataChannel] 是否已经注册 */ private var isDataChannelRegistered = false @@ -88,12 +88,6 @@ internal object AppParasitics { */ internal var hostApplication: Application? = null - /** 当前 Xposed 模块自身 APK 路径 */ - internal var moduleAppFilePath = "" - - /** 当前 Xposed 模块自身 [Resources] */ - internal var moduleAppResources: YukiModuleResources? = null - /** * 当前环境中使用的 [ClassLoader] * @@ -103,12 +97,6 @@ internal object AppParasitics { */ internal val baseClassLoader get() = classOf().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 实例对象 @@ -119,6 +107,43 @@ internal object AppParasitics { ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke() } ?: error("Failed to got SystemContext") + /** + * 获取当前宿主的 [Application] + * @return [Application] or null + */ + internal val currentApplication + get() = runCatching { AndroidAppHelper.currentApplication() }.getOrNull() ?: runCatching { + ActivityThreadClass.method { name = "currentApplication" }.ignored().get().invoke() + }.getOrNull() + + /** + * 获取当前宿主的 [ApplicationInfo] + * @return [ApplicationInfo] or null + */ + internal val currentApplicationInfo + get() = runCatching { AndroidAppHelper.currentApplicationInfo() }.getOrNull() ?: runCatching { + ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call() + ?.current(ignored = true)?.field { name = "mBoundApplication" } + ?.current(ignored = true)?.field { name = "appInfo" } + ?.cast() + }.getOrNull() + + /** + * 获取当前宿主的包名 + * @return [String] + */ + internal val currentPackageName get() = currentApplicationInfo?.packageName ?: SYSTEM_FRAMEWORK_NAME + + /** + * 获取当前宿主的进程名 + * @return [String] + */ + internal val currentProcessName + get() = runCatching { AndroidAppHelper.currentProcessName() }.getOrNull() ?: runCatching { + ActivityThreadClass.method { name = "currentPackageName" }.ignored().get().string() + .takeIf { it.isNotBlank() } ?: SYSTEM_FRAMEWORK_NAME + }.getOrNull() ?: SYSTEM_FRAMEWORK_NAME + /** * 获取指定 [packageName] 的用户 ID * @@ -133,10 +158,30 @@ internal object AppParasitics { }.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid) }.getOrNull() ?: 0 + /** + * 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法 + * @param loader 当前 [ClassLoader] + * @param result 回调 - ([Class] 实例对象) + */ + internal fun hookClassLoader(loader: ClassLoader?, result: (Class<*>) -> Unit) { + if (loader == null) return + if (YukiXposedModule.isXposedEnvironment.not()) return yLoggerW(msg = "You can only use hook ClassLoader method in Xposed Environment") + classLoaderCallbacks[loader.hashCode()] = result + if (isClassLoaderHooked) return + runCatching { + YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringClass, BooleanType) }, object : YukiMemberHook() { + override fun afterHookedMember(param: Param) { + param.instance?.also { loader -> + (param.result as? Class<*>?)?.also { classLoaderCallbacks[loader.hashCode()]?.invoke(it) } + } + } + }) + isClassLoaderHooked = true + }.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") } + } + /** * Hook 模块 APP 相关功能 - 包括自身激活状态、Resources Hook 支持状态以及 [SharedPreferences] - * - * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 * @param loader 模块的 [ClassLoader] * @param type 当前正在进行的 Hook 类型 */ @@ -147,18 +192,33 @@ internal object AppParasitics { if ((param.args?.get(0) as? String?)?.endsWith("preferences.xml") == true) param.args?.set(1, 1) } }) - if (YukiHookAPI.Configs.isEnableHookModuleStatus) YukiHookModuleStatus.IMPL_CLASS_NAME.toClassOrNull(loader)?.apply { + if (YukiHookAPI.Configs.isEnableHookModuleStatus.not()) return + YukiXposedModuleStatus.IMPL_CLASS_NAME.toClassOrNull(loader)?.apply { if (type != HookEntryType.RESOURCES) { - YukiHookHelper.hook(method { name = YukiHookModuleStatus.IS_ACTIVE_METHOD_NAME }, object : YukiMemberReplacement() { - override fun replaceHookedMember(param: Param) = true - }) - YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_TAG_METHOD_NAME }, object : YukiMemberReplacement() { - override fun replaceHookedMember(param: Param) = YukiHookBridge.executorName - }) - YukiHookHelper.hook(method { name = YukiHookModuleStatus.GET_XPOSED_VERSION_METHOD_NAME }, object : YukiMemberReplacement() { - override fun replaceHookedMember(param: Param) = YukiHookBridge.executorVersion - }) - } else YukiHookHelper.hook(method { name = YukiHookModuleStatus.IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME }, + YukiHookHelper.hook(method { name = YukiXposedModuleStatus.IS_ACTIVE_METHOD_NAME }, + object : YukiMemberReplacement() { + override fun replaceHookedMember(param: Param) = true + }) + YukiHookHelper.hook(method { name = YukiXposedModuleStatus.GET_EXECUTOR_NAME_METHOD_NAME }, + object : YukiMemberReplacement() { + override fun replaceHookedMember(param: Param) = HookApiProperty.name + }) + YukiHookHelper.hook( + method { name = YukiXposedModuleStatus.GET_EXECUTOR_API_LEVEL_METHOD_NAME }, + object : YukiMemberReplacement() { + override fun replaceHookedMember(param: Param) = HookApiProperty.apiLevel + }) + YukiHookHelper.hook( + method { name = YukiXposedModuleStatus.GET_EXECUTOR_VERSION_NAME_METHOD_NAME }, + object : YukiMemberReplacement() { + override fun replaceHookedMember(param: Param) = HookApiProperty.versionName + }) + YukiHookHelper.hook( + method { name = YukiXposedModuleStatus.GET_EXECUTOR_VERSION_CODE_METHOD_NAME }, + object : YukiMemberReplacement() { + override fun replaceHookedMember(param: Param) = HookApiProperty.versionCode + }) + } else YukiHookHelper.hook(method { name = YukiXposedModuleStatus.IS_SUPPORT_RESOURCES_HOOK_METHOD_NAME }, object : YukiMemberReplacement() { override fun replaceHookedMember(param: Param) = true }) @@ -245,8 +305,7 @@ internal object AppParasitics { runCatching { /** 过滤系统框架与一系列服务组件包名不唯一的情况 */ if (isDataChannelRegistered || - (YukiHookAppHelper.currentPackageName() == YukiHookBridge.SYSTEM_FRAMEWORK_NAME && - packageName != YukiHookBridge.SYSTEM_FRAMEWORK_NAME) + (currentPackageName == SYSTEM_FRAMEWORK_NAME && packageName != SYSTEM_FRAMEWORK_NAME) ) return YukiHookDataChannel.instance().register(it, packageName) isDataChannelRegistered = true @@ -258,47 +317,23 @@ internal object AppParasitics { } } - /** - * 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法 - * @param loader 当前 [ClassLoader] - * @param result 回调 - ([Class] 实例对象) - */ - internal fun hookClassLoader(loader: ClassLoader?, result: (Class<*>) -> Unit) { - if (loader == null) return - if (YukiHookBridge.hasXposedBridge.not()) return yLoggerW(msg = "You can only use hook ClassLoader method in Xposed Environment") - classLoaderCallbacks[loader.hashCode()] = result - if (isClassLoaderHooked) return - runCatching { - YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringClass, BooleanType) }, object : YukiMemberHook() { - override fun afterHookedMember(param: Param) { - param.instance?.also { loader -> - (param.result as? Class<*>?)?.also { classLoaderCallbacks[loader.hashCode()]?.invoke(it) } - } - } - }) - isClassLoaderHooked = true - }.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") } - } - /** * 向 Hook APP (宿主) 注入当前 Xposed 模块的资源 * @param hostResources 需要注入的宿主 [Resources] */ internal fun injectModuleAppResources(hostResources: Resources) { - if (YukiHookBridge.hasXposedBridge) runCatching { - if (YukiHookAppHelper.currentPackageName() == YukiHookBridge.modulePackageName) + if (YukiXposedModule.isXposedEnvironment) runCatching { + if (currentPackageName == YukiXposedModule.modulePackageName) return yLoggerE(msg = "You cannot inject module resources into yourself") - hostResources.assets.current(ignored = true).method { name = "addAssetPath"; param(StringClass) }.call(moduleAppFilePath) + hostResources.assets.current(ignored = true).method { + name = "addAssetPath" + param(StringClass) + }.call(YukiXposedModule.moduleAppFilePath) }.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 (宿主) 注册当前 Xposed 模块的 [Activity] * @param context 当前 [Context] @@ -307,12 +342,12 @@ internal object AppParasitics { @RequiresApi(Build.VERSION_CODES.N) internal fun registerModuleAppActivities(context: Context, proxy: Any?) { if (isActivityProxyRegistered) return - if (YukiHookBridge.hasXposedBridge.not()) return yLoggerW(msg = "You can only register Activity Proxy in Xposed Environment") - if (context.packageName == YukiHookBridge.modulePackageName) return yLoggerE(msg = "You cannot register Activity Proxy into yourself") + if (YukiXposedModule.isXposedEnvironment.not()) return yLoggerW(msg = "You can only register Activity Proxy in Xposed Environment") + if (context.packageName == YukiXposedModule.modulePackageName) return yLoggerE(msg = "You cannot register Activity Proxy into yourself") if (Build.VERSION.SDK_INT < 24) return yLoggerE(msg = "Activity Proxy only support for Android 7.0 (API 24) or higher") runCatching { ActivityProxyConfig.apply { - proxyIntentName = "${YukiHookBridge.modulePackageName}.ACTIVITY_PROXY_INTENT" + proxyIntentName = "${YukiXposedModule.modulePackageName}.ACTIVITY_PROXY_INTENT" proxyClassName = proxy?.let { when (it) { is String, is CharSequence -> it.toString() diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppActivity.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppActivity.kt index 0e644ab6..8b4236cc 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppActivity.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppActivity.kt @@ -35,7 +35,7 @@ import android.os.Bundle import androidx.annotation.CallSuper import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader /** @@ -51,7 +51,7 @@ open class ModuleAppActivity : Activity() { @CallSuper override fun onConfigurationChanged(newConfig: Configuration) { - if (YukiHookBridge.hasXposedBridge) injectModuleAppResources() + if (YukiXposedModule.isXposedEnvironment) injectModuleAppResources() super.onConfigurationChanged(newConfig) } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppCompatActivity.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppCompatActivity.kt index 9cb7d396..5a918533 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppCompatActivity.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/base/ModuleAppCompatActivity.kt @@ -36,7 +36,7 @@ import androidx.annotation.CallSuper import androidx.appcompat.app.AppCompatActivity import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader /** @@ -60,7 +60,7 @@ open class ModuleAppCompatActivity : AppCompatActivity() { @CallSuper override fun onConfigurationChanged(newConfig: Configuration) { - if (YukiHookBridge.hasXposedBridge) injectModuleAppResources() + if (YukiXposedModule.isXposedEnvironment) injectModuleAppResources() super.onConfigurationChanged(newConfig) } @@ -72,7 +72,7 @@ open class ModuleAppCompatActivity : AppCompatActivity() { @CallSuper override fun onCreate(savedInstanceState: Bundle?) { - if (YukiHookBridge.hasXposedBridge && moduleTheme != -1) setTheme(moduleTheme) + if (YukiXposedModule.isXposedEnvironment && moduleTheme != -1) setTheme(moduleTheme) super.onCreate(savedInstanceState) } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/HandlerDelegate.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/HandlerDelegate.kt index ad12efdb..afb73e0b 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/HandlerDelegate.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/HandlerDelegate.kt @@ -42,7 +42,7 @@ import com.highcapable.yukihookapi.hook.type.android.ActivityThreadClass import com.highcapable.yukihookapi.hook.type.android.ClientTransactionClass import com.highcapable.yukihookapi.hook.type.android.IBinderClass import com.highcapable.yukihookapi.hook.type.android.IntentClass -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig /** @@ -73,7 +73,7 @@ internal class HandlerDelegate private constructor(private val baseInstance: Han msg.obj.current(ignored = true).field { name = "intent" }.apply { cast()?.also { intent -> IntentClass.field { name = "mExtras" }.ignored().get(intent).cast() - ?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader + ?.classLoader = AppParasitics.currentApplication?.classLoader if (intent.hasExtra(ActivityProxyConfig.proxyIntentName)) set(intent.getParcelableExtra(ActivityProxyConfig.proxyIntentName)) } @@ -86,7 +86,7 @@ internal class HandlerDelegate private constructor(private val baseInstance: Han ?.apply { cast()?.also { intent -> IntentClass.field { name = "mExtras" }.ignored().get(intent).cast() - ?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader + ?.classLoader = AppParasitics.currentApplication?.classLoader if (intent.hasExtra(ActivityProxyConfig.proxyIntentName)) intent.getParcelableExtra(ActivityProxyConfig.proxyIntentName).also { subIntent -> if (Build.VERSION.SDK_INT >= 31) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/IActivityManagerProxy.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/IActivityManagerProxy.kt index b46bc7c7..d4d98dee 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/IActivityManagerProxy.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/IActivityManagerProxy.kt @@ -30,8 +30,7 @@ package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate import android.app.ActivityManager import android.content.Intent -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig import java.lang.reflect.InvocationHandler @@ -61,8 +60,8 @@ internal class IActivityManagerProxy private constructor(private val baseInstanc val argsInstance = (args[index] as? Intent) ?: return@also val component = argsInstance.component if (component != null && - component.packageName == YukiHookAppHelper.currentPackageName() && - component.className.startsWith(YukiHookBridge.modulePackageName) + component.packageName == AppParasitics.currentPackageName && + component.className.startsWith(YukiXposedModule.modulePackageName) ) args[index] = Intent().apply { setClassName(component.packageName, ActivityProxyConfig.proxyClassName) putExtra(ActivityProxyConfig.proxyIntentName, argsInstance) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/InstrumentationDelegate.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/InstrumentationDelegate.kt index 43e8fe5e..3464fbda 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/InstrumentationDelegate.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/InstrumentationDelegate.kt @@ -41,7 +41,7 @@ import android.os.* import android.view.KeyEvent import android.view.MotionEvent import com.highcapable.yukihookapi.hook.factory.* -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics /** @@ -65,15 +65,15 @@ internal class InstrumentationDelegate private constructor(private val baseInsta * @param icicle [Bundle] */ private fun Activity.injectLifecycle(icicle: Bundle?) { - if (icicle != null && current().name.startsWith(YukiHookBridge.modulePackageName)) + if (icicle != null && current().name.startsWith(YukiXposedModule.modulePackageName)) icicle.classLoader = AppParasitics.baseClassLoader - if (current().name.startsWith(YukiHookBridge.modulePackageName)) injectModuleAppResources() + if (current().name.startsWith(YukiXposedModule.modulePackageName)) injectModuleAppResources() } override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity? = try { baseInstance.newActivity(cl, className, intent) } catch (e: Throwable) { - if (className?.startsWith(YukiHookBridge.modulePackageName) == true) + if (className?.startsWith(YukiXposedModule.modulePackageName) == true) className.toClass().buildOf() ?: throw e else throw e } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/context/wrapper/ModuleContextThemeWrapper.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/context/wrapper/ModuleContextThemeWrapper.kt index 0df8078d..a53b345c 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/context/wrapper/ModuleContextThemeWrapper.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/context/wrapper/ModuleContextThemeWrapper.kt @@ -35,7 +35,7 @@ import android.content.res.Configuration import android.content.res.Resources import android.view.ContextThemeWrapper import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader /** @@ -73,7 +73,7 @@ class ModuleContextThemeWrapper private constructor(baseContext: Context, theme: baseResources = baseContext.createConfigurationContext(it)?.resources baseResources?.updateConfiguration(it, baseContext.resources.displayMetrics) } - if (YukiHookBridge.hasXposedBridge) resources?.injectModuleAppResources() + if (YukiXposedModule.isXposedEnvironment) resources?.injectModuleAppResources() } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/reference/ModuleClassLoader.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/reference/ModuleClassLoader.kt index ab6eb250..9cc127e4 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/reference/ModuleClassLoader.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/parasitic/reference/ModuleClassLoader.kt @@ -30,8 +30,7 @@ package com.highcapable.yukihookapi.hook.xposed.parasitic.reference -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge -import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics /** @@ -89,8 +88,8 @@ class ModuleClassLoader private constructor() : ClassLoader(AppParasitics.baseCl private val baseLoader get() = AppParasitics.baseClassLoader override fun loadClass(name: String, resolve: Boolean): Class<*> { - if (YukiHookBridge.hasXposedBridge.not()) return baseLoader.loadClass(name) - return YukiHookAppHelper.currentApplication()?.classLoader?.let { hostLoader -> + if (YukiXposedModule.isXposedEnvironment.not()) return baseLoader.loadClass(name) + return AppParasitics.currentApplication?.classLoader?.let { hostLoader -> excludeHostClasses.takeIf { it.isNotEmpty() }?.forEach { runCatching { if (name == it) return@let hostLoader.loadClass(name) } } excludeModuleClasses.takeIf { it.isNotEmpty() }?.forEach { runCatching { if (name == it) return@let baseLoader.loadClass(name) } } runCatching { return@let baseLoader.loadClass(name) } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt index b37e7111..dd84b6ca 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt @@ -25,10 +25,7 @@ * * This file is Created by fankes on 2022/2/8. */ -@file:Suppress( - "SetWorldReadable", "CommitPrefEdits", "DEPRECATION", "WorldReadableFiles", - "unused", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "StaticFieldLeak" -) +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "StaticFieldLeak", "SetWorldReadable", "CommitPrefEdits", "UNCHECKED_CAST") package com.highcapable.yukihookapi.hook.xposed.prefs @@ -38,7 +35,7 @@ import androidx.preference.PreferenceFragmentCompat import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.log.yLoggerE import com.highcapable.yukihookapi.hook.log.yLoggerW -import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge +import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData import com.highcapable.yukihookapi.hook.xposed.prefs.ui.ModulePreferenceFragment import de.robv.android.xposed.XSharedPreferences @@ -70,8 +67,8 @@ class YukiHookModulePrefs private constructor(private var context: Context? = nu internal companion object { - /** 是否为 (Xposed) 宿主环境 */ - private val isXposedEnvironment = YukiHookBridge.hasXposedBridge + /** 当前是否为 (Xposed) 宿主环境 */ + private val isXposedEnvironment = YukiXposedModule.isXposedEnvironment /** 当前 [YukiHookModulePrefs] 单例 */ private var instance: YukiHookModulePrefs? = null @@ -102,7 +99,7 @@ class YukiHookModulePrefs private constructor(private var context: Context? = nu } /** 存储名称 - 默认包名 + _preferences */ - private var prefsName = "${YukiHookBridge.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences" + private var prefsName = "${YukiXposedModule.modulePackageName.ifBlank { context?.packageName ?: "" }}_preferences" /** 是否使用键值缓存 */ private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache @@ -147,7 +144,7 @@ class YukiHookModulePrefs private constructor(private var context: Context? = nu /** 检查 API 装载状态 */ private fun checkApi() { if (YukiHookAPI.isLoadedFromBaseContext) error("YukiHookModulePrefs not allowed in Custom Hook API") - if (isXposedEnvironment && YukiHookBridge.modulePackageName.isBlank()) + if (isXposedEnvironment && YukiXposedModule.modulePackageName.isBlank()) error("Xposed modulePackageName load failed, please reset and rebuild it") } @@ -158,7 +155,7 @@ class YukiHookModulePrefs private constructor(private var context: Context? = nu private val xPrefs get() = checkApi().let { runCatching { - XSharedPreferences(YukiHookBridge.modulePackageName, prefsName).apply { + XSharedPreferences(YukiXposedModule.modulePackageName, prefsName).apply { checkApi() makeWorldReadable() reload() @@ -174,6 +171,7 @@ class YukiHookModulePrefs private constructor(private var context: Context? = nu private val sPrefs get() = checkApi().let { runCatching { + @Suppress("DEPRECATION", "WorldReadableFiles") context?.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE).also { isUsingNewXSharedPreferences = true } ?: error("YukiHookModulePrefs missing Context instance") }.getOrElse { diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/ui/ModulePreferenceFragment.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/ui/ModulePreferenceFragment.kt index 28b76ce8..263b5c2d 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/ui/ModulePreferenceFragment.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/ui/ModulePreferenceFragment.kt @@ -25,8 +25,6 @@ * * This file is Created by fankes on 2022/4/17. */ -@file:Suppress("WorldReadableFiles", "DEPRECATION") - package com.highcapable.yukihookapi.hook.xposed.prefs.ui import android.app.Activity @@ -105,6 +103,7 @@ abstract class ModulePreferenceFragment : PreferenceFragmentCompat(), SharedPref /** 设置自动适配模块 Sp 存储全局可读可写 */ private fun makeNewXShareReadableIfPossible() = runCatching { + @Suppress("DEPRECATION", "WorldReadableFiles") currentActivity.getSharedPreferences(prefsName, Context.MODE_WORLD_READABLE) }.onFailure { YukiHookModulePrefs.makeWorldReadable(currentActivity, prefsFileName = "$prefsName.xml") }.unit() } \ No newline at end of file