Modify decoupling Xposed API

- Remove YukiBridgeFactory, merge it to hook/core/api/*
- Remove YukiHookBridge, merge it to YukiXposedModule
- Remove the direct call function of Xposed API
- Change hook entry class way of injecting
- Fix some bugs in LoggerFactory, YukiXposedModule (formerly YukiHookBridge), AppParasitics
This commit is contained in:
2023-01-12 00:42:03 +08:00
parent 1efbd2e836
commit 5e478fe285
61 changed files with 2229 additions and 1223 deletions

View File

@@ -60,7 +60,7 @@ const val API_VERSION_CODE: Int
`v1.0.91` `removed`
请转移到 `Status.executorName`
请转移到 `Status.Executor.name`
<h2 class="deprecated">executorVersion - field</h2>
@@ -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 <span class="symbol">- object</span>
@@ -114,49 +114,25 @@ val isXposedEnvironment: Boolean
> 获取当前是否为 (Xposed) 宿主环境。
### executorName <span class="symbol">- field</span>
```kotlin:no-line-numbers
val executorName: String
```
<h3 class="deprecated">executorName - field</h3>
**Change Records**
`v1.0.91` `added`
**Function Illustrate**
`v1.1.5` `deprecated`
> 获取当前 Hook 框架的名称。
请转移到 `Executor.name`
无法获取会返回 `unknown``XposedBridge` 不存在会返回 `invalid`。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
### executorVersion <span class="symbol">- field</span>
```kotlin:no-line-numbers
val executorVersion: Int
```
<h3 class="deprecated">executorVersion - field</h3>
**Change Records**
`v1.0.91` `added`
**Function Illustrate**
`v1.1.5` `deprecated`
> 获取当前 Hook 框架的版本。
无法获取会返回 `-1`。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
请转移到 `Executor.apiLevel`、`Executor.versionName`、`Executor.versionCode`
### isModuleActive <span class="symbol">- field</span>
@@ -250,6 +226,100 @@ val isSupportResourcesHook: Boolean
:::
### Executor <span class="symbol">- object</span>
```kotlin:no-line-numbers
object Executor
```
**Change Records**
`v1.1.5` `added`
**Function Illustrate**
> 当前 `YukiHookAPI` 使用的 Hook 框架相关信息。
#### name <span class="symbol">- field</span>
```kotlin:no-line-numbers
val name: String
```
**Change Records**
`v1.1.5` `added`
**Function Illustrate**
> 获取当前 Hook 框架的名称。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### apiLevel <span class="symbol">- field</span>
```kotlin:no-line-numbers
val apiLevel: Int
```
**Change Records**
`v1.1.5` `added`
**Function Illustrate**
> 获取当前 Hook 框架的 API 版本。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### versionName <span class="symbol">- field</span>
```kotlin:no-line-numbers
val versionName: String
```
**Change Records**
`v1.1.5` `added`
**Function Illustrate**
> 获取当前 Hook 框架的版本名称。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### versionCode <span class="symbol">- field</span>
```kotlin:no-line-numbers
val versionCode: Int
```
**Change Records**
`v1.1.5` `added`
**Function Illustrate**
> 获取当前 Hook 框架的版本号。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
## Configs <span class="symbol">- object</span>
```kotlin:no-line-numbers

View File

@@ -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 <span class="symbol">- class</span>
@@ -50,19 +50,29 @@ LOGD
> 仅使用 `android.util.Log`。
### XPOSEDBRIDGE <span class="symbol">- enum</span>
```kotlin:no-line-numbers
XPOSEDBRIDGE
```
<h3 class="deprecated">XPOSEDBRIDGE - enum</h3>
**Change Records**
`v1.1.0` `added`
`v1.1.5` `deprecated`
请转移到 `XPOSED_ENVIRONMENT`
### XPOSED_ENVIRONMENT <span class="symbol">- enum</span>
```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`,你可以进行自定义。

View File

@@ -52,7 +52,7 @@ const val API_VERSION_CODE: Int
`v1.0.91` `移除`
请转移到 `Status.executorName`
请转移到 `Status.Executor.name`
<h2 class="deprecated">executorVersion - field</h2>
@@ -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 <span class="symbol">- object</span>
@@ -106,49 +106,25 @@ val isXposedEnvironment: Boolean
> 获取当前是否为 (Xposed) 宿主环境。
### executorName <span class="symbol">- field</span>
```kotlin:no-line-numbers
val executorName: String
```
<h3 class="deprecated">executorName - field</h3>
**变更记录**
`v1.0.91` `新增`
**功能描述**
`v1.1.5` `作废`
> 获取当前 Hook 框架的名称。
请转移到 `Executor.name`
无法获取会返回 `unknown``XposedBridge` 不存在会返回 `invalid`。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
### executorVersion <span class="symbol">- field</span>
```kotlin:no-line-numbers
val executorVersion: Int
```
<h3 class="deprecated">executorVersion - field</h3>
**变更记录**
`v1.0.91` `新增`
**功能描述**
`v1.1.5` `作废`
> 获取当前 Hook 框架的版本。
无法获取会返回 `-1`。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
请转移到 `Executor.apiLevel`、`Executor.versionName`、`Executor.versionCode`
### isModuleActive <span class="symbol">- field</span>
@@ -242,6 +218,100 @@ val isSupportResourcesHook: Boolean
:::
### Executor <span class="symbol">- object</span>
```kotlin:no-line-numbers
object Executor
```
**变更记录**
`v1.1.5` `新增`
**功能描述**
> 当前 `YukiHookAPI` 使用的 Hook 框架相关信息。
#### name <span class="symbol">- field</span>
```kotlin:no-line-numbers
val name: String
```
**变更记录**
`v1.1.5` `新增`
**功能描述**
> 获取当前 Hook 框架的名称。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### apiLevel <span class="symbol">- field</span>
```kotlin:no-line-numbers
val apiLevel: Int
```
**变更记录**
`v1.1.5` `新增`
**功能描述**
> 获取当前 Hook 框架的 API 版本。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### versionName <span class="symbol">- field</span>
```kotlin:no-line-numbers
val versionName: String
```
**变更记录**
`v1.1.5` `新增`
**功能描述**
> 获取当前 Hook 框架的版本名称。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
#### versionCode <span class="symbol">- field</span>
```kotlin:no-line-numbers
val versionCode: Int
```
**变更记录**
`v1.1.5` `新增`
**功能描述**
> 获取当前 Hook 框架的版本号。
::: warning
在模块环境中需要启用 **Configs.isEnableHookModuleStatus**。
:::
## Configs <span class="symbol">- object</span>
```kotlin:no-line-numbers

View File

@@ -10,7 +10,7 @@ pageClass: code-page
**功能描述**
> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 `XposedBridge.log` 打印日志的功能。
> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 (Xposed) 宿主环境打印日志的功能。
## LoggerType <span class="symbol">- class</span>
@@ -42,19 +42,29 @@ LOGD
> 仅使用 `android.util.Log`。
### XPOSEDBRIDGE <span class="symbol">- enum</span>
```kotlin:no-line-numbers
XPOSEDBRIDGE
```
<h3 class="deprecated">XPOSEDBRIDGE - enum</h3>
**变更记录**
`v1.1.0` `新增`
`v1.1.5` `作废`
请转移到 `XPOSED_ENVIRONMENT`
### XPOSED_ENVIRONMENT <span class="symbol">- enum</span>
```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`,你可以进行自定义。

View File

@@ -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(

View File

@@ -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" +

View File

@@ -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')
}

View File

@@ -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 {
/**
* 获取项目编译完成的时间戳 (当前本地时间)

View File

@@ -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!")
}

View File

@@ -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))
}

View File

@@ -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] 接管类

View File

@@ -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<YukiMemberHook.Unhook>()
private val hookedMembers = HashSet<YukiMemberHook.HookedMember>()
/** 当前需要 Hook 的 [Method]、[Constructor] */
internal val members = HashSet<Member>()
@@ -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<YukiMemberHook.Unhook?, Boolean> {
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]")

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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<XposedBridge>().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
}
}

View File

@@ -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")
}

View File

@@ -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<Any?>?,
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)
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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<Any?>?
/**
* 获取、设置方法返回值 (结果)
* @return [Any] or null
*/
var result: Any?
/**
* 是否存在设置过的方法调用抛出异常
* @return [Boolean]
*/
val hasThrowable: Boolean
/**
* 获取、设置方法调用抛出的异常
* @return [Throwable] or null
* @throws Throwable
*/
var throwable: Throwable?
}
}

View File

@@ -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()
}
}

View File

@@ -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?
}

View File

@@ -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)

View File

@@ -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<YukiMemberHook.HookedMember>()
}

View File

@@ -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)
}
/**

View File

@@ -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)
}
/**

View File

@@ -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<Class<*>> =
if (async && name.isNotBlank()) YukiHookAppHelper.currentApplication()?.let {
if (async && name.isNotBlank()) currentContext?.let {
hashSetOf<Class<*>>().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<Class<*>>.saveToCache() {
if (name.isNotBlank() && isNotEmpty()) hashSetOf<String>().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()
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]")
}

View File

@@ -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()
}

View File

@@ -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<YukiHookAPI>().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]"

View File

@@ -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 可填入异常堆栈信息 - 将自动完整打印到控制台

View File

@@ -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

View File

@@ -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)
}
/**

View File

@@ -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"
}

View File

@@ -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<String>()
/** 当前 [PackageParamWrapper] 实例数组 */
private val packageParamWrappers = HashMap<String, PackageParamWrapper>()
/** 当前 [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<XposedBridge>().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) }
}
}
}

View File

@@ -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<String>()
/** 当前 [PackageParamWrapper] 实例数组 */
private val packageParamWrappers = HashMap<String, PackageParamWrapper>()
/** 当前 [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) }
}
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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<YukiMemberHook.Unhook>()
}
/**
* 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<YukiMemberHook.Unhook?, Boolean> {
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<Any?>?
/**
* 获取、设置方法结果
* @return [Any] or null
*/
var result: Any?
/**
* 判断是否存在设置过的方法调用抛出异常
* @return [Boolean]
*/
val hasThrowable: Boolean
/**
* 获取、设置方法调用抛出的异常
* @return [Throwable] or null
* @throws Throwable
*/
var throwable: Throwable?
}
}

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
/**

View File

@@ -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) }
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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<String>(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<String>(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<String>(RESULT_MODULE_GENERATED_VERSION, priority) { result(it == YukiHookBridge.moduleGeneratedVersion) }
wait<String>(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\"")
}

View File

@@ -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<YukiHookAPI>().classLoader ?: error("Operating system not supported")
/**
* 获取当前 Xposed 模块自身动态 [Resources]
* @return [YukiModuleResources] or null
*/
internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull()
/**
* 获取当前系统框架的 [Context]
* @return [Context] ContextImpl 实例对象
@@ -119,6 +107,43 @@ internal object AppParasitics {
ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
} ?: 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<Application>()
}.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<ApplicationInfo>()
}.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()

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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<Intent?>()?.also { intent ->
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
?.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<Intent?>()?.also { intent ->
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader
?.classLoader = AppParasitics.currentApplication?.classLoader
if (intent.hasExtra(ActivityProxyConfig.proxyIntentName))
intent.getParcelableExtra<Intent>(ActivityProxyConfig.proxyIntentName).also { subIntent ->
if (Build.VERSION.SDK_INT >= 31)

View File

@@ -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)

View File

@@ -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<Activity>() ?: throw e
else throw e
}

View File

@@ -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()
}
/**

View File

@@ -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) }

View File

@@ -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 {

View File

@@ -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()
}