Added new documentations files

This commit is contained in:
2022-09-17 14:46:07 +08:00
parent dbd5a74a5c
commit 237d5cbed0
99 changed files with 20686 additions and 0 deletions

View File

@@ -0,0 +1,395 @@
# API 基本配置
> 这里介绍了 `YukiHookAPI` 的基本配置方法。
## 功能配置
> 无论是 [作为 Xposed 模块使用](../config/xposed-using) 还是 [作为 Hook API 使用](../config/api-using),你都可以在 API 装载之前或装载过程中对 `YukiHookAPI` 进行配置。
### configs 方法
```kotlin:no-line-numbers
fun configs(initiate: Configs.() -> Unit)
```
`configs` 方法对 `Configs` 类实现了一个 `lambda` 方法体,你可以轻松地调用它进行配置。
::: tip
更多功能请参考 [YukiHookAPI.configs](../api/public/com/highcapable/yukihookapi/YukiHookAPI#configs-method) 方法。
:::
## Hooker 配置
> 一个 Xposed 模块或 Hook API 最重要的地方就是 Hooker 的创建与使用,`YukiHookAPI` 提供了两种使用方法。
### 通过 lambda 创建
> 这种方案是最简单的,如果你的模块功能不多,代码数量不大,不需要进行分类处理,推荐使用这种方式进行创建。
#### encase 方法
```kotlin:no-line-numbers
fun encase(initiate: PackageParam.() -> Unit)
```
`encase` 方法是 Hook 一切生命的开始,在一个模块或一个 Hook 过程中,`encase` 方法只能作用一次,用于创建 Hooker。
`PackageParam` 为宿主(目标 APP)的重要实例对象,通过 `PackageParam` 来实现对当前 Hook 作用对象的全部 Hook 操作。
::: tip
更多功能请参考 [PackageParam](../api/public/com/highcapable/yukihookapi/hook/param/PackageParam)。
:::
`encase` 方法可以在 `onHook` 方法中使用两种方案创建。
> 示例代码 1
```kotlin
YukiHookAPI.encase {
loadApp(name = "com.example.demo") {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
}
}
```
> 示例代码 2
```kotlin
encase {
loadApp(name = "com.example.demo") {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
}
}
```
在 `encase` 方法中进行你的 Hook 操作。
### 通过自定义 Hooker 创建
> 这种方案更加适用于大型项目,例如需要对 Hooker 进行分类或对 Hook 的作用对象进行分类。
#### encase 方法
```kotlin:no-line-numbers
fun encase(vararg hooker: YukiBaseHooker)
```
同样为 `encase` 方法,这里的方法可变数组参数 `hooker` 为创建入口提供了一个对象,你可以将所有继承于 `YukiBaseHooker` 的 Hooker 一次性进行装载。
#### YukiBaseHooker 用法
`YukiBaseHooker` 继承于 `PackageParam`,你需要将你的子 Hooker 继承于 `YukiBaseHooker`。
::: tip
更多功能请参考 [YukiBaseHooker](../api/public/com/highcapable/yukihookapi/entity/YukiBaseHooker)。
:::
> 示例如下
```kotlin
object CustomHooker : YukiBaseHooker() {
override fun onHook() {
// Your code here.
}
}
```
子 Hooker **建议使用**单例 `object` 创建,你也可以使用 `class` 但不推荐。
::: warning
你无需再在继承于 **YukiBaseHooker** 的 **onHook** 方法中重新调用 **encase**,这是错误的,且**不会生效**,你应该直接开始编写你的 Hook 代码。
:::
> 示例如下
```kotlin
object CustomHooker : YukiBaseHooker() {
override fun onHook() {
loadApp(name = "com.example.demo1") {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
}
loadApp(name = "com.example.demo2") {
findClass(name = "$packageName.CustomClass").hook {
// Your code here.
}
}
}
}
```
作为子 Hooker 使用,你还可以在外部调用 `loadApp` 方法,然后在内部直接开始 Hook。
> 示例如下
```kotlin
class HookEntry : IYukiHookXposedInit {
override fun onHook() = encase {
loadApp(name = "com.example.demo", ChildCustomHooker)
}
}
object ChildCustomHooker : YukiBaseHooker() {
override fun onHook() {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
}
}
```
你可以使用 `loadHooker` 方法在子 Hooker 中多层装载另一个 Hooker请按照你的喜好进行即可。
> 示例如下
```kotlin
object FirstHooker : YukiBaseHooker() {
override fun onHook() {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
loadHooker(SecondHooker)
loadHooker(ThirdHooker)
}
}
```
搭建完全部 Hooker 后,你就可以在你的 `HookEntryClass` 入口类中的 `onHook` 方法中装载你的 Hooker 了。
> 示例如下
```kotlin
class HookEntry : IYukiHookXposedInit {
override fun onHook() =
YukiHookAPI.encase(FirstHooker, SecondHooker, ThirdHooker ...)
}
```
当然,我们同样可以对其进行简写。
> 示例如下
```kotlin
class HookEntry : IYukiHookXposedInit {
override fun onHook() = encase(FirstHooker, SecondHooker, ThirdHooker ...)
}
```
### 扩展特性
如果你当前使用的 Hook Framework 支持并启用了资源钩子(Resources Hook)功能,你现在可以直接在 `encase` 中创建 Resources Hook。
你完全不需要与之前在使用 Xposed API 那样区分 `initZygote`、`handleLoadPackage`、`handleInitPackageResources` 方法来执行不同的功能。
在 `YukiHookAPI` 中,这些功能**是无缝的**。
> 示例如下
```kotlin
encase {
loadApp(name = "com.example.demo") {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
// 创建一个 Resources Hook (固定用法)
resources().hook {
// Your code here.
}
}
}
```
你还可以同时使用 `loadZygote` 方法来装载新的进程被 fork 后的第一个事件 `initZygote`。
> 示例如下
```kotlin
encase {
loadZygote {
ActivityClass.hook {
// Your code here.
}
// 在 Zygote 中创建 Resources Hook
resources().hook {
// Your code here.
}
}
loadApp(name = "com.example.demo") {
findClass(name = "$packageName.DemoClass").hook {
// Your code here.
}
// 在 APP 中创建 Resources Hook
resources().hook {
// Your code here.
}
}
}
```
### 注意事项
直接装载 Hooker 或直接开始 Hook 是错误的,`encase` 事件在被 Hook Framework 装载后,会经历三次回调。
- 装载 `initZygote` → `encase`
- 装载 `handleLoadPackage` → `encase`
- 装载 `handleInitPackageResources` → `encase`
在这个过程中,你需要使用 `loadApp`、`loadSystem`、`loadZygote` 来区分每一次装载代码的调用域,否则你的代码就会被<u>**多次执行造成错误**</u>。
::: warning
无论使用 **encase** 创建 **lambda** 方法体还是直接使用 Hooker 形式,你都不应该直接在首个 **onHook** 事件中直接装载 Hooker 或直接开始 Hook。
:::
下面是两个**错误**示例。
> 示例代码 1
```kotlin
encase {
// ❗错误的使用方法,不能直接开始 Hook
findClass(name = "com.example.demo.DemoClass").hook {
// ...
}
// ❗错误的使用方法,不能直接开始 Hook
resources().hook {
// ...
}
}
```
> 示例代码 2
```kotlin
class HookEntry : IYukiHookXposedInit {
override fun onHook() {
// <情景1>
encase {
loadHooker(CustomHooker)
}
// <情景2>
encase(CustomHooker)
}
}
object CustomHooker : YukiBaseHooker() {
override fun onHook() {
// ❗错误的使用方法,由于外层没有任何判断对象,不能直接开始 Hook
findClass(name = "com.example.demo.DemoClass").hook {
// ...
}
}
}
```
下面是上述错误示例的**正确**示例。
> 示例代码 1
```kotlin
encase {
// ✅ 正确的使用方法,在 Zygote 中装载
loadZygote(CustomHooker)
// ✅ 正确的使用方法,在 Zygote 中装载
loadZygote {
// ✅ 正确的使用方法,在 Zygote 内 Hook
resources().hook {
// ...
}
}
// ✅ 正确的使用方法,使用 APP 作用域装载
loadApp(/** name 参数可选 */, hooker = CustomHooker)
// ✅ 正确的使用方法,判断 APP 作用域后再装载 Hooker
loadApp(/** name 参数可选 */) {
loadHooker(CustomHooker)
// ✅ 正确的使用方法,在 APP 作用域内 Hook
findClass(name = "com.example.demo.DemoClass").hook {
// ...
}
// ✅ 正确的使用方法,在 APP 作用域内 Hook
resources().hook {
// ...
}
}
}
```
> 示例代码 2
```kotlin
class HookEntry : IYukiHookXposedInit {
override fun onHook() {
encase(CustomHooker)
}
}
object CustomHooker : YukiBaseHooker() {
override fun onHook() {
// ✅ 正确的使用方法,由于外层没有任何判断对象,需要判断 APP 作用域后再进行 Hook
loadApp(/** name 参数可选 */) {
findClass(name = "com.example.demo.DemoClass").hook {
// ...
}
}
}
}
```
## 作为 Hook API 使用需要注意的地方
若你作为 Hook API 使用,那么你只需要在入口处对 `encase` 方法进行区分。
::: warning
**encase** 方法对作为 Hook API 使用提供了两个完全一样的方法,但是比前两者仅多出一个参数 **baseContext**。
:::
> 方法 1
```kotlin:no-line-numbers
fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit)
```
> 方法 2
```kotlin:no-line-numbers
fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker)
```
此处的 `baseContext` 只需填入你在 `attachBaseContext` 处得到的 `Context` 即可,其它用法与上述内容完全一致。
::: danger
切勿以 Xposed 方式使用 **encase** 方法而漏掉 **baseContext** 参数,否则你的 Hook 将完全不工作。
Resources Hook 功能不支持作为 Hook API 使用。
:::

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
# 作为 Hook API 使用的相关配置
> 作为 Hook API 通常为做自身 APP 热更新或功能需要以及产品测试的 Hook 操作。
## 依赖配置
你只需要集成 `com.highcapable.yukihookapi:api` 依赖即可。
然后请集成你目标使用的 `Hook Framework` 依赖。
## 入口配置
创建你的自定义 `Application`
`attachBaseContext` 中添加 `YukiHookAPI.encase` 方法。
> 示例如下
```kotlin
class MyApplication : Application() {
override fun attachBaseContext(base: Context?) {
// 装载 Hook Framework
//
// Your code here.
//
// 配置 YukiHookAPI
YukiHookApi.configs {
// Your code here.
}
// 装载 YukiHookAPI
YukiHookAPI.encase(base) {
// Your code here.
}
super.attachBaseContext(base)
}
}
```
这样,你就完成了 API 的相关配置。
你可以 [点击这里](../config/api-example#作为-hook-api-使用需要注意的地方) 查看异同点和注意事项。
::: warning
你不能再使用 **loadApp** 进行包装,可直接开始编写你的 Hook 代码。
:::
## Hook Framework
> 这里给出了一些较高使用率的 `Hook Framework` 如何对接 `YukiHookAPI` 的相关方式。
### [Pine](https://github.com/canyie/pine)
> **所需 Xposed API 依赖** `top.canyie.pine:xposed`
> 示例如下
```kotlin
override fun attachBaseContext(base: Context?) {
// 装载 Pine
PineConfig.debug = true
PineConfig.debuggable = BuildConfig.DEBUG
// 装载 YukiHookAPI
YukiHookAPI.encase(base) {
// Your code here.
}
super.attachBaseContext(base)
}
```
### [SandHook](https://github.com/asLody/SandHook)
> **所需 Xposed API 依赖** `com.swift.sandhook:xposedcompat` 或 `com.swift.sandhook:xposedcompat_new`
> 示例如下
```kotlin
override fun attachBaseContext(base: Context?) {
// 装载 SandHook
SandHookConfig.DEBUG = BuildConfig.DEBUG
XposedCompat.cacheDir = base?.cacheDir
XposedCompat.context = base
XposedCompat.classLoader = javaClass.classLoader
XposedCompat.isFirstApplication = base?.processName == base?.packageName
// 装载 YukiHookAPI
YukiHookAPI.encase(base) {
// Your code here.
}
super.attachBaseContext(base)
}
```
### [Whale](https://github.com/asLody/whale)
> **所需 Xposed API 依赖** `com.wind.xposed:xposed-on-whale`
请参考 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale)。
> 示例如下
```kotlin
override fun attachBaseContext(base: Context?) {
// 装载 Whale 不需要任何配置
// 装载 YukiHookAPI
YukiHookAPI.encase(base) {
// Your code here.
}
super.attachBaseContext(base)
}
```

View File

@@ -0,0 +1,23 @@
# R8 与 Proguard 混淆
> 大部分场景下 Xposed 模块可通过原生混淆压缩体积,这里介绍了混淆的配置方法。
## R8
> 如果你使用的是 `R8`,那么你无需对 `YukiHookAPI` 进行任何特殊配置。
## Proguard
> ~~如果你仍然在使用 `Proguard`,你需要做一些规则配置。~~
::: danger
Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 **R8**,不再需要考虑混淆的问题。
:::
若要在任何版本下启用 `R8`,请在 `gradle.properties` 文件中加入如下规则Android Gradle Plugin 7.0 及以上版本无需任何配置。
```groovy
android.enableR8=true
```

View File

@@ -0,0 +1,280 @@
# 作为 Xposed 模块使用的相关配置
> 这里介绍了 `YukiHookAPI` 作为 Xposed 模块使用的相关配置方法。
## 依赖配置
> 作为 Xposed 模块,`YukiHookAPI` 提供了一个自动处理程序。
你需要在你的 `build.gradle` 中集成 `com.highcapable.yukihookapi:ksp-xposed` 依赖的最新版本。
## 自定义处理程序
> 你可以对 `YukiHookAPI` 将如何生成 `xposed_init` 入口进行相关配置。
### InjectYukiHookWithXposed 注解
```kotlin:no-line-numbers
annotation class InjectYukiHookWithXposed(
val sourcePath: String,
val modulePackageName: String,
val entryClassName: String,
val isUsingResourcesHook: Boolean
)
```
`@InjectYukiHookWithXposed` 注解是一个标记模块 Hook 入口的重要注解。
::: danger
**@InjectYukiHookWithXposed** 注解的 **Class** 必须实现 **IYukiHookXposedInit** 接口。
在你当前项目中的所有 **Class** 标记中**只能存在一次**,若存在多个声明自动处理程序<u>**会在编译时抛出异常**</u>,你可以自定义其相关参数。
:::
#### sourcePath 参数
`sourcePath` 参数决定了自动处理程序自动查找并匹配你当前项目路径的重要标识,此参数的内容为相对路径匹配,默认参数为 `src/main`。
::: danger
如果你的项目不在 **../src/main..** 或你手动使用 **sourceSets** 设置了项目路径,你就需要手动设置 **sourcePath** 参数,否则自动处理程序将无法识别你的项目路径并<u>**会在编译时抛出异常**</u>。
:::
> 示例如下
```kotlin
@InjectYukiHookWithXposed(sourcePath = "src/custom")
```
`sourcePath` 使用的文件路径分隔符写法根据 `Windows` 和 `Unix` 将自动进行识别,使用 `/` 或 `\` 均可。
#### modulePackageName 参数
`modulePackageName` 是你当前项目的 `applicationId`,也就是你的模块包名 (最终生成的应用包名),留空或不填时自动处理程序将对当前项目文件进行分析并生成。
::: warning
若你想使用模块包名自动生成,你需要确保你的项目命名空间在 **AndroidManifest.xml**、**build.gradle** 或 **build.gradle.kts** 中存在如下任意定义方式。
:::
示例命名空间 `com.example.demo`,以下定义方式任选其一。
以下定义方式仅供参考,通常情况下**只要你的项目能够正常生成 `BuildConfig.java` 文件,就不需要做额外操作**。
> `AndroidManifest.xml` 示例
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.demo">
```
> `build.gradle` 示例
```groovy
android {
namespace 'com.example.demo'
}
```
> `build.gradle.kts` 示例
```kotlin
android {
namespace = "com.example.demo"
}
```
若你的模块包名是非常规手段进行自动生成的,或你认为有必要手动定义模块包名,那么你可以直接设置 `modulePackageName` 的参数。
> 示例如下
```kotlin
@InjectYukiHookWithXposed(modulePackageName = "com.example.demo")
```
只要你自定义了 `modulePackageName` 的参数,你就会在编译时收到警告。
> 示例如下
```:no-line-numbers
You set the customize module package name to "com.example.demo", please check for yourself if it is correct
```
::: warning
手动定义的模块包名除了格式之外,自动处理程序将不会再检查模块包名是否正确,需要你自行确认其有效性。
:::
#### entryClassName 参数
`entryClassName` 决定了自动处理程序如何生成 `xposed_init` 中的入口类名,默认会使用你的入口类包名插入 `_YukiHookXposedInit` 后缀进行生成。
假设这是你的入口类。
> 示例如下
```kotlin
@InjectYukiHookWithXposed
class HookEntry: IYukiHookXposedInit
```
Xposed 入口类处理如下。
> 示例如下
```kotlin:no-line-numbers
class HookEntry_YukiHookXposedInit: IXposedHookZygoteInit, IXposedHookLoadPackage, ...
```
编译后的类名结构如下。
> 示例如下
```:no-line-numbers
...hook.HookEntry ← 你的入口类
...hook.HookEntry_Impl ← 自动生成的 Impl 类
...hook.HookEntry_YukiHookXposedInit ← 自动生成的 Xposed 入口类
```
我们现在定义入口类名称为 `HookXposedEntry`。
> 示例如下
```kotlin
@InjectYukiHookWithXposed(entryClassName = "HookXposedEntry")
class HookEntry: IYukiHookXposedInit
```
Xposed 入口类处理如下。
> 示例如下
```kotlin:no-line-numbers
class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, ...
```
编译后的类名结构如下。
> 示例如下
```:no-line-numbers
...hook.HookEntry ← 你的入口类
...hook.HookEntry_Impl ← 自动生成的 Impl 类
...hook.HookXposedEntry ← 自动生成的 Xposed 入口类
```
::: danger
你定义的 **entryClassName** 不可与 **xposed_init** 中的类名相同,否则自动处理程序<u>会在编译时抛出异常</u>。
:::
#### isUsingResourcesHook 参数
`isUsingResourcesHook` 决定了自动处理程序是否生成针对 Resources Hook 的相关代码,此功能默认是启用的。
启用后生成的入口类将为如下所示。
> 示例如下
```kotlin:no-line-numbers
class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {
// ...
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
// ...
}
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {
// ...
}
}
```
若你当前的项目并不需要用到 Reources Hook可以设置 `isUsingResourcesHook = false` 来关闭自动生成。
> 示例如下
```kotlin
@InjectYukiHookWithXposed(isUsingResourcesHook = false)
```
关闭后生成的入口类将为如下所示。
> 示例如下
```kotlin:no-line-numbers
class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage {
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {
// ...
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
// ...
}
}
```
### IYukiHookXposedInit 接口
`IYukiHookXposedInit` 接口为你的 `HookEntryClass` 必须实现的接口,这是你的模块开始 Hook 的起点。
::: tip
更多功能请参考 [IYukiHookXposedInit](../api/public/com/highcapable/yukihookapi/hook/xposed/proxy/IYukiHookXposedInit)。
:::
当你的模块被 Xposed 装载后,`onHook` 方法将会被回调,你需要在此方法中开始使用 `YukiHookAPI`。
> 基本的调用流程为 `_YukiHookXposedInit` → `IYukiHookXposedInit.onXposedEvent` → `IYukiHookXposedInit.onInit` → `IYukiHookXposedInit.onHook`
详情请参考 [API 基本配置](../config/api-example)。
## 原生 Xposed API 事件
若你当前的 Xposed 模块使用了第三方的资源,但是短时间内可能无法转移它们,此时,你可以使用 `onXposedEvent` 实现监听原生 Xposed API 的全部装载事件。
> 示例如下
```kotlin
@InjectYukiHookWithXposed
class HookEntry: IYukiHookXposedInit {
override fun onHook() {
// Your code here.
}
override fun onXposedEvent() {
// 监听原生 Xposed API 的装载事件
YukiXposedEvent.events {
onInitZygote {
// it 对象即 [StartupParam]
}
onHandleLoadPackage {
// it 对象即 [LoadPackageParam]
}
onHandleInitPackageResources {
// it 对象即 [InitPackageResourcesParam]
}
}
}
}
```
`onXposedEvent` 与 `onHook` 方法完全独立存在,互不影响,你可以继续在 `onHook` 方法中使用 `YukiHookAPI`。
::: tip
更多功能请参考 [IYukiHookXposedInit.onXposedEvent](../api/public/com/highcapable/yukihookapi/hook/xposed/proxy/IYukiHookXposedInit#onxposedevent-method) 方法。
:::