mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 01:35:17 +08:00
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:
@@ -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
|
||||
|
@@ -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`,你可以进行自定义。
|
@@ -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
|
||||
|
@@ -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`,你可以进行自定义。
|
@@ -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(
|
||||
|
@@ -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" +
|
||||
|
@@ -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')
|
||||
}
|
@@ -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 {
|
||||
|
||||
/**
|
||||
* 获取项目编译完成的时间戳 (当前本地时间)
|
@@ -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!")
|
||||
}
|
@@ -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))
|
||||
}
|
@@ -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] 接管类
|
||||
|
@@ -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]")
|
||||
|
@@ -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")
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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?
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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?
|
||||
}
|
@@ -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)
|
@@ -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>()
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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()
|
||||
}
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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]")
|
||||
}
|
||||
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
|
@@ -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]"
|
||||
|
@@ -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 可填入异常堆栈信息 - 将自动完整打印到控制台
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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"
|
||||
}
|
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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?
|
||||
}
|
||||
}
|
@@ -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
|
||||
)
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
||||
/**
|
@@ -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) }
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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\"")
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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) }
|
||||
|
@@ -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 {
|
||||
|
@@ -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()
|
||||
}
|
Reference in New Issue
Block a user