mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-08 19:44:16 +08:00
Added new documentations files
This commit is contained in:
677
docs-source/src/zh-cn/guide/example.md
Normal file
677
docs-source/src/zh-cn/guide/example.md
Normal file
@@ -0,0 +1,677 @@
|
||||
# 用法示例
|
||||
|
||||
> 这里介绍了 `YukiHookAPI` 的基本工作方式以及列举了简单的 Hook 例子和常用功能。
|
||||
|
||||
## 结构图解
|
||||
|
||||
> 下方的结构描述了 `YukiHookAPI` 的基本工作方式和原理。
|
||||
|
||||
```:no-line-numbers
|
||||
Host Environment
|
||||
└── YukiMemberHookCreator
|
||||
└── Class
|
||||
└── MemberHookCreator
|
||||
└── Member
|
||||
├── Before
|
||||
└── After
|
||||
MemberHookCreator
|
||||
└── Member
|
||||
├── Before
|
||||
└── After
|
||||
...
|
||||
YukiResourcesHookCreator
|
||||
└── Resources
|
||||
└── ResourcesHookCreator
|
||||
└── Drawable
|
||||
└── Replace
|
||||
ResourcesHookCreator
|
||||
└── Layout
|
||||
└── Inject
|
||||
...
|
||||
```
|
||||
|
||||
> 上方的结构换做代码将可写为如下形式。
|
||||
|
||||
```kotlin
|
||||
TargetClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
// Your code here.
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
// Your code here.
|
||||
}
|
||||
replaceTo(...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
> 你可以在下方找到 API 提供的 Demo 来学习 `YukiHookAPI` 的使用方法。
|
||||
|
||||
- 宿主 APP Demo [点击这里查看](https://github.com/fankes/YukiHookAPI/tree/master/demo-app)
|
||||
|
||||
- 模块 APP Demo [点击这里查看](https://github.com/fankes/YukiHookAPI/tree/master/demo-module)
|
||||
|
||||
同时安装宿主和模块 Demo,通过激活模块来测试宿主中被 Hook 的功能。
|
||||
|
||||
## 一个简单的 Hook 例子
|
||||
|
||||
> 这里给出了 Hook APP、Hook 系统框架与 Hook Resources 等例子,可供参考。
|
||||
|
||||
### Hook APP
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中的 `onCreate` 方法并弹出一个对话框。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
AlertDialog.Builder(instance())
|
||||
.setTitle("Hooked")
|
||||
.setMessage("I am hook!")
|
||||
.setPositiveButton("OK", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至此,`onCreate` 方法将被成功 Hook 并在 `com.android.browser` 中的每个 `Activity` 启动时弹出此对话框。
|
||||
|
||||
那么,我想继续 Hook `onStart` 方法要怎么做呢?
|
||||
|
||||
在刚刚的代码中,继续插入一个 `injectMember` 方法体即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
AlertDialog.Builder(instance())
|
||||
.setTitle("Hooked")
|
||||
.setMessage("I am hook!")
|
||||
.setPositiveButton("OK", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "onStart"
|
||||
emptyParam()
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
对于当前项目下没有的 `Class`,你可以使用 `stub` 方式或 `findClass` 方法来得到需要 Hook 的类。
|
||||
|
||||
比如,我要得到 `com.example.demo.TestClass`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "com.example.demo.TestClass").hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若 `com.example.demo` 是你要 Hook 的 APP,那么写法可以更简单。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "$packageName.TestClass").hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
到这里有些同学可能就开始说了,在某些场景下 `findClass` 显得有些繁琐。
|
||||
|
||||
因为可能有些同学有如下需求。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
const val TestClass = "com.example.demo.TestClass"
|
||||
|
||||
TestClass.hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
没关系,你还可以使用字符串类名直接创建一个 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
"$packageName.TestClass".hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [MemberHookCreator](../api/public/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator#memberhookcreator-class)。
|
||||
|
||||
:::
|
||||
|
||||
### Hook Zygote
|
||||
|
||||
在 APP 启动时,新的进程被 fork 后的第一个事件 `initZygote`。
|
||||
|
||||
假设我们要全局 Hook 一个 APP `Activity` 的 `onCreate` 事件
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
在 **loadZygote** 中进行的功能十分有限,几乎很少的情况下需要用到 **loadZygote** 方法。
|
||||
|
||||
:::
|
||||
|
||||
### Hook 系统框架
|
||||
|
||||
在 `YukiHookAPI` 中,Hook 系统框架的实现非常简单。
|
||||
|
||||
假设,你要得到 `ApplicationInfo` 与 `PackageInfo` 并对它们进行一些操作。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadSystem {
|
||||
ApplicationInfoClass.hook {
|
||||
// Your code here.
|
||||
}
|
||||
PackageInfoClass.hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
**loadZygote** 与 **loadSystem** 有直接性区别,**loadZygote** 会在 **initZygote** 中装载,系统框架被视为 **loadApp(name = "android")** 而存在,若要 Hook 系统框架,可直接使用 **loadSystem**。
|
||||
|
||||
:::
|
||||
|
||||
### Hook Resources
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中 `string` 类型的 `app_name` 内容替换为 `123`。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "app_name"
|
||||
string()
|
||||
}
|
||||
replaceTo("123")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若当前 APP 使用 `app_name` 设置了标题栏文本,则它就会变成我们的 `123`。
|
||||
|
||||
你还可以使用当前 Xposed 模块的 Resources 替换 Hook APP 的 Resources。
|
||||
|
||||
假设,我们要继续 Hook `com.android.browser` 中 `mipmap` 类型的 `ic_launcher`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至此目标 APP 的图标将会被替换为我们设置的图标。
|
||||
|
||||
若你想替换系统框架的资源,同样也可以这样实现,只需要把 `loadApp` 换成 `loadZygote` 即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [ResourcesHookCreator](../api/public/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator#resourceshookcreator-class)。
|
||||
|
||||
:::
|
||||
|
||||
### 解除 Hook
|
||||
|
||||
原生的 Xposed 为我们提供了一个 `XC_MethodHook.Unhook` 功能,可以从 Hook 队列中将当前 Hook 移除,`YukiHookAPI` 同样可以实现此功能。
|
||||
|
||||
第一种方法,保存当前注入对象的 `Result` 实例,在适当的时候和地方调用 `remove` 即可解除该注入对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 设置一个变量保存当前实例
|
||||
val hookResult = injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// 在适当的时候调用如下方法即可
|
||||
hookResult.remove()
|
||||
```
|
||||
|
||||
第二种方法,在 Hook 回调方法中调用 `removeSelf` 移除自身。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// 直接调用如下方法即可
|
||||
removeSelf()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [MemberHookCreator](../api/public/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator#memberhookcreator-class)。
|
||||
|
||||
:::
|
||||
|
||||
## 异常处理
|
||||
|
||||
> `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
|
||||
|
||||
### 监听异常
|
||||
|
||||
你可以处理 Hook 方法过程发生的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}.result {
|
||||
// 处理 Hook 开始时的异常
|
||||
onHookingFailure {}
|
||||
// 处理 Hook 过程中的异常
|
||||
onConductFailure { param, throwable -> }
|
||||
// 处理全部异常
|
||||
onAllFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
在 Resources Hook 时此方法同样适用。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
// Your code here.
|
||||
}.result {
|
||||
// 处理 Hook 时的任意异常
|
||||
onHookingFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
你还可以处理 Hook 的 `Class` 不存在时发生的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
TargetClass.hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}.onHookClassNotFoundFailure {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以处理查找方法时的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
method {
|
||||
// Your code here.
|
||||
}.onNoSuchMethod {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [MemberHookCreator.Result](../api/public/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator#result-class)、[ResourcesHookCreator.Result](../api/public/com/highcapable/yukihookapi/hook/core/YukiResourcesHookCreator#result-class)。
|
||||
|
||||
:::
|
||||
|
||||
这里介绍了可能发生的常见异常,若要了解更多请参考 [API 异常处理](../config/api-exception)。
|
||||
|
||||
### 抛出异常
|
||||
|
||||
在某些情况下,你可以**手动抛出异常**来达到提醒某些功能存在问题的目的。
|
||||
|
||||
上面已经介绍过,在 `hook` 方法体内抛出的异常会被 `YukiHookAPI` 接管,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
|
||||
|
||||
以下是 `YukiHookAPI` 接管时这些异常的运作方式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// <情景1>
|
||||
injectMember {
|
||||
method {
|
||||
throw RuntimeException("Exception Test")
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}.result {
|
||||
// 能够捕获到 RuntimeException
|
||||
onHookingFailure {}
|
||||
}
|
||||
// <情景2>
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
throw RuntimeException("Exception Test")
|
||||
}
|
||||
}.result {
|
||||
// 能够捕获到 RuntimeException
|
||||
onConductFailure { param, throwable -> }
|
||||
}
|
||||
```
|
||||
|
||||
以上情景只会在 (Xposed) 宿主环境被处理,不会对宿主自身造成任何影响。
|
||||
|
||||
若我们想将这些异常直接抛给宿主,原生的 Xposed 为我们提供了 `param.throwable` 方法,`YukiHookAPI` 同样可以实现此功能。
|
||||
|
||||
若想在 Hook 回调方法体中将一个异常直接抛给宿主,可以有如下实现方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
RuntimeException("Exception Test").throwToApp()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你也可以直接在 Hook 回调方法体中抛出异常,然后标识将异常抛给宿主。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
throw RuntimeException("Exception Test")
|
||||
}.onFailureThrowToApp()
|
||||
}
|
||||
```
|
||||
|
||||
以上两种方法均可在宿主接收到异常从而使宿主进程崩溃。
|
||||
|
||||
::: warning
|
||||
|
||||
为了保证 Hook 调用域与宿主内调用域相互隔离,异常只有在 **beforeHook** 与 **afterHook** 回调方法体中才能抛给宿主。
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [Throwable.throwToApp](../api/public/com/highcapable/yukihookapi/hook/param/HookParam#throwable-throwtoapp-i-ext-method)、[YukiMemberHookCreator.MemberMookCreator.HookCallback](../api/public/com/highcapable/yukihookapi/hook/core/YukiMemberHookCreator#hookcallback-class)。
|
||||
|
||||
:::
|
||||
|
||||
## 状态监听
|
||||
|
||||
在使用 `XposedHelpers` 的同学往往会在 Hook 后打印 `Unhook` 的方法确定是否 Hook 成功。
|
||||
|
||||
在 `YukiHookAPI` 中,你可以用以下方法方便地重新实现这个功能。
|
||||
|
||||
首先我们可以监听 Hook 已经准备开始。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
YourClass.hook {
|
||||
// Your code here.
|
||||
}.onPrepareHook {
|
||||
loggerD(msg = "$instanceClass hook start")
|
||||
}
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
**instanceClass** 建议只在 **onPrepareHook** 中使用,否则被 Hook 的 **Class** 不存在会抛出无法拦截的异常导致 Hook 进程“死掉”。
|
||||
|
||||
:::
|
||||
|
||||
然后,我们还可以对 Hook 的方法结果进行监听是否成功。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}.onHooked { member ->
|
||||
loggerD(msg = "$member has hooked")
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展用法
|
||||
|
||||
> 你可以在 Hook 过程中使用下面的方法方便地实现各种判断和功能。
|
||||
|
||||
### 多个宿主
|
||||
|
||||
如果你的模块需要同时处理多个 APP 的 Hook 事件,你可以使用 `loadApp` 方法体来区分你要 Hook 的 APP。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
// Your code here.
|
||||
}
|
||||
loadApp(name = "com.android.phone") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [PackageParam.loadApp](../api/public/com/highcapable/yukihookapi/hook/param/PackageParam#loadapp-method)。
|
||||
|
||||
:::
|
||||
|
||||
### 多个进程
|
||||
|
||||
如果你 Hook 的宿主 APP 有多个进程,你可以使用 `withProcess` 方法体来对它们分别进行 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
withProcess(mainProcessName) {
|
||||
// Your code here.
|
||||
}
|
||||
withProcess(name = "$packageName:tool") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [PackageParam.withProcess](../api/public/com/highcapable/yukihookapi/hook/param/PackageParam#withprocess-method)。
|
||||
|
||||
:::
|
||||
|
||||
## 写法优化
|
||||
|
||||
为了使代码更加简洁,你可以删去 `YukiHookAPI` 的名称,将你的 `onHook` 入口写作 `lambda` 形式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun onHook() = encase {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
## Xposed 模块判断自身激活状态
|
||||
|
||||
通常情况下,我们会选择写一个方法,使其返回 `false`,然后 Hook 掉这个方法使其返回 `true` 来证明 Hook 已经生效。
|
||||
|
||||
在 `YukiHookAPI` 中你完全不需要再这么做了,`YukiHookAPI` 已经帮你封装好了这个操作,你可以直接进行使用。
|
||||
|
||||
现在,你可以直接使用 `YukiHookAPI.Status.isXposedModuleActive` 在模块中判断自身是否被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isXposedModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
由于一些特殊原因,在太极、无极中的模块无法使用标准方法检测激活状态。
|
||||
|
||||
此时你可以使用 `YukiHookAPI.Status.isTaiChiModuleActive` 判断自身是否被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isTaiChiModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
若你想使用两者得兼的判断方案,`YukiHookAPI` 同样为你封装了便捷的方式。
|
||||
|
||||
此时你可以使用 `YukiHookAPI.Status.isModuleActive` 判断自身是否在 Xposed 或太极、无极中被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
更多功能请参考 [YukiHookAPI.Status](../api/public/com/highcapable/yukihookapi/YukiHookAPI#status-object)。
|
||||
|
||||
:::
|
||||
|
||||
::: warning
|
||||
|
||||
若模块激活判断中包含太极、无极中的激活状态,就必须将模块的 **Application** 继承于 **ModuleApplication** 或直接使用 **ModuleApplication**;
|
||||
|
||||
**1.0.91** 版本后的 API 修改了激活逻辑判断方式,现在你可以在模块与 Hook APP (宿主) 中同时使用此 API;
|
||||
|
||||
需要确保 **YukiHookAPI.Configs.isEnableHookModuleStatus** 是启用状态;
|
||||
|
||||
除了提供标准 API 的 Hook 框架之外,其它情况下模块可能都将无法判断自己是否被激活。
|
||||
|
||||
:::
|
186
docs-source/src/zh-cn/guide/home.md
Normal file
186
docs-source/src/zh-cn/guide/home.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# 介绍
|
||||
|
||||
> 这是一个 Hook API 框架,本身不提供任何 Hook 功能,需要 Xposed 基础 API 的支持。
|
||||
|
||||
## 背景
|
||||
|
||||
这是一个使用 `Kotlin` 重新构建的高效 Xposed Hook API。
|
||||
|
||||
名称取自 [《ももくり》女主 栗原 雪(Yuki)](https://www.bilibili.com/bangumi/play/ss5016)。
|
||||
|
||||
前身为 [开发学习项目](https://github.com/fankes/TMore) 中使用的 Innocent Xposed API,现在重新命名并开源。
|
||||
|
||||
## 用途
|
||||
|
||||
`YukiHookAPI` 完全采用 `Kotlin` `lambda` 语法构建。
|
||||
|
||||
抛弃原始不太友好的 `XposedHelpers`,你可以使用它来轻松创建 Xposed 模块以及轻松实现自定义 Hook API。
|
||||
|
||||
## 语言要求
|
||||
|
||||
请使用 `Kotlin`,框架部分代码构成同样兼容 `Java` 但基础 Hook 场景的实现**可能完全无法使用**。
|
||||
|
||||
文档全部的 Demo 示例代码都将使用 `Kotlin` 进行描述,如果你完全不会使用 `Kotlin` 那你将有可能无法使用 `YukiHookAPI`。
|
||||
|
||||
部分 Java Demo 代码可在 [这里](https://github.com/fankes/YukiHookAPI/tree/master/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/java) 找到,但不推荐使用。
|
||||
|
||||
## 灵感来源
|
||||
|
||||
以前,我们在构建 Xposed 模块的时候,首先需要在 `assets` 下创建 `xposed_init` 文件。
|
||||
|
||||
然后,将自己的入口类名手动填入文件中,使用 `XposedHelpers` 去实现我们的 Hook 逻辑。
|
||||
|
||||
自 `Kotlin` 作为 Android 主要开发语言以来,这套 API 用起来确实已经不是很优雅了。
|
||||
|
||||
有没有什么 **好用、轻量、优雅** 的解决办法呢?
|
||||
|
||||
本着这样的想法,`YukiHookAPI` 诞生了。
|
||||
|
||||
现在,我们只需要编写少量的代码,一切时间开销和花费交给自动化处理。
|
||||
|
||||
借助 `Kotlin` 优雅的 `lambda` 写法以及 `YukiHookAPI`,可以让你的 Hook 逻辑更加美观清晰。
|
||||
|
||||
> 示例如下
|
||||
|
||||
:::: code-group
|
||||
::: code-group-item Yuki Hook API
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "sym_def_app_icon"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::: code-group-item Xposed API
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
|
||||
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
XResources.setSystemWideReplacement(
|
||||
"android", "mipmap", "sym_def_app_icon",
|
||||
moduleResources.fwd(R.mipmap.ic_launcher)
|
||||
)
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
null, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
if (lpparam.packageName == "com.android.browser")
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
lpparam.classLoader, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
if (resparam.packageName == "com.android.browser")
|
||||
resparam.res.setReplacement(
|
||||
"com.android.browser", "mipmap", "ic_launcher",
|
||||
moduleResources.fwd(R.mipmap.ic_launcher)
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::::
|
||||
|
||||
是的,你没有看错,仅仅就需要这些代码,就能完全取代 Xposed API 实现同样的功能。
|
||||
|
||||
现在,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
|
||||
|
||||
## 支持的 Hook 框架
|
||||
|
||||
以下是 `YukiHookAPI` 支持的 `Hook Framework` 以及 Xposed 框架。
|
||||
|
||||
| Hook Framework | ST | Description |
|
||||
| --------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------- |
|
||||
| [LSPosed](https://github.com/LSPosed/LSPosed) | ✅ | 多场景下稳定使用 |
|
||||
| [LSPatch](https://github.com/LSPosed/LSPatch) | ⭕ | 将在此项目完善后逐渐加入 API 支持 |
|
||||
| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ❎ | 已停止维护,不再推荐使用 |
|
||||
| [Pine](https://github.com/canyie/pine) | ⭕ | 可以使用 |
|
||||
| [SandHook](https://github.com/asLody/SandHook) | ⭕ | 可以使用 |
|
||||
| [Whale](https://github.com/asLody/whale) | ⭕ | 需要 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale) |
|
||||
| [YAHFA](https://github.com/PAGalaxyLab/YAHFA) | ❗ | 需要自行实现 Xposed API |
|
||||
| [FastHook](https://github.com/turing-technician/FastHook) | ❗ | 需要自行实现 Xposed API |
|
||||
| [Epic](https://github.com/tiann/epic) | ❗ | 需要自行对接 [Dexposed](https://github.com/alibaba/dexposed) |
|
||||
| [TaiChi](https://github.com/taichi-framework/TaiChi) | ⭕ | 可以作为模块使用 |
|
||||
| [Xposed](https://github.com/rovo89/Xposed) | ❎ | 未测试,不再推荐使用 |
|
85
docs-source/src/zh-cn/guide/knowledge.md
Normal file
85
docs-source/src/zh-cn/guide/knowledge.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 基础知识
|
||||
|
||||
> 这里收集了 Xposed 相关的介绍以及开启前需要掌握的知识要点,已经了解的同学可以略过。
|
||||
|
||||
基础知识内容<u>**并不一定完全准确**</u>,请根据自己的见解酌情阅读,若发现内容**有错误欢迎指正并帮助我们完善和改进**。
|
||||
|
||||
## 相关介绍
|
||||
|
||||
> 这里介绍了 Xposed 以及 Hook 的工作原理。
|
||||
|
||||
### Xposed 是什么
|
||||
|
||||
> Xposed 框架(Xposed Framework)是一套开源的、在 Android 高权限模式下运行的框架服务,可以在不修改 APK 文件的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。
|
||||
|
||||
上述内容复制自百度百科。
|
||||
|
||||
### Xposed 能做什么
|
||||
|
||||
> 下方的结构描述了 Xposed 的基本工作方式和原理。
|
||||
|
||||
```:no-line-numbers
|
||||
Xposed Framework
|
||||
└── App's Environment
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
App's Environment
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
我们可以在宿主 (APP) 运行时通过注入宿主 (APP) 来达到控制其行为的最终目的。
|
||||
|
||||
Xposed 的这种运行方式被称为**寄生**,Xposed 模块跟随宿主的生命周期,在宿主的生命周期内完成自己的生命历程。
|
||||
|
||||
我们可以通过反射的方式调用宿主的方法、变量、构造方法,以及使用 `XposedBridge` 所提供的 Hook 操作动态地在宿主 (APP) 要执行的方法前后插入自己的代码,或完全替换目标,甚至是拦截。
|
||||
|
||||
### 发展过程
|
||||
|
||||
如今的 Xposed 管理器已完全被其衍生作品替代,而 **SuperSU** 的时代也已经落幕了,现在,借助 **Magisk** 使后面的一切又成为了可能。
|
||||
|
||||
> 其发展史大致可分为 **Xposed(Dalvik)** → **Xposed(ART)** → **Xposed(Magisk)** → **EdXposed(Riru)**/**LSPosed(Riru/Zygisk)**
|
||||
|
||||
### 衍生产品
|
||||
|
||||
> 下方的结构描述了类似 Xposed 的 Hook Framework 的工作方式和原理。
|
||||
|
||||
```:no-line-numbers
|
||||
App's Environment
|
||||
└── Hook Framework
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
```
|
||||
|
||||
通过 Xposed 的运行原理,从而衍生了很多同类型框架,随着当今时代的移动设备获取 Root 权限甚至刷机越来越困难且不是刚需的时候,一些免 Root 框架也随之产生,例如**太极**。
|
||||
|
||||
这些在 ART 层面上的 Hook 框架同样也可不借助 Xposed API 完成其和 Xposed 原理一样的 Hook 流程,免 Root 的运行原理为修改 APK 并将 Hook 进程注入宿主,通过外部模块对其进行控制。
|
||||
|
||||
另外一种产品就是利用 Android 运行环境现有的功能虚拟出一个完全与当前设备系统一样的环境,并在其中运行 APP,这个就是虚拟 APP 技术 **VirtualApp**,后来衍生为 **VirtualXposed**。
|
||||
|
||||
上述提到的免 Root 框架分别为**太极/无极**、**VirtualXposed/SandVXposed**。
|
||||
|
||||
### YukiHookAPI 做了什么
|
||||
|
||||
自从 Xposed 出现到现在为止,除了开发者人人皆知的 `XposedHelpers`,依然没有一套针对 `Kotlin` 打造的语法糖以及用法封装十分完善的 API。
|
||||
|
||||
本 API 框架的诞生就是希望在 Xposed 的如今时代,能让更多有动手能力的 Xposed 模块开发者少走弯路,更容易、更简单地完成整个开发流程。
|
||||
|
||||
未来,`YukiHookAPI` 将在使用 Xposed API 的目标基础上适配更多第三方 Hook 框架,使得整个生态得到完善,并帮助更多开发者让 Xposed 模块开发变得更加简单和易懂。
|
||||
|
||||
## 让我们开始吧
|
||||
|
||||
在开始之前,你需要拥有以下基础才能更好地使用 `YukiHookAPI`。
|
||||
|
||||
- 掌握并了解 Android 开发及简单的系统运行原理
|
||||
|
||||
- 掌握并了解 Android APK 内部结构以及简单的反编译知识要领,可参考 [Jadx](https://github.com/skylot/jadx) 与 [ApkTool](https://github.com/iBotPeaches/Apktool)
|
||||
|
||||
- 掌握并熟练使用 Java 反射,了解简单的 Smali 语法,了解 DEX 文件结构,会使用逆向分析定位方法位置
|
||||
|
||||
- 掌握基础的原生 [Xposed API](https://api.xposed.info) 用法,了解 Xposed 的运行原理,可参考本文以及 [这里](https://blog.ketal.icu/2022/01/13/Xposed%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8%E4%BF%9D%E5%A7%86%E7%BA%A7%E6%95%99%E7%A8%8B) **(友情链接)**
|
||||
|
||||
- 掌握 Kotlin 语言,学会灵活运用 [Kotlin lambda](https://blog.ketal.icu/2022/01/01/kotlin-lambda%E5%85%A5%E9%97%A8) **(友情链接)**
|
||||
|
||||
- 掌握并了解 Kotlin 与 Java 混编、互相调用以及 Kotlin 生成的 Java 字节码
|
259
docs-source/src/zh-cn/guide/move-to-new-api.md
Normal file
259
docs-source/src/zh-cn/guide/move-to-new-api.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# 从 Xposed API 迁移
|
||||
|
||||
> 若你熟悉 Xposed API,你可以参考下方的相同点将自己的 API 快速迁移到 `YukiHookAPI`。
|
||||
|
||||
## 迁移 Hook 入口点
|
||||
|
||||
> 从 `XC_LoadPackage.LoadPackageParam` 迁移至 `PackageParam`。
|
||||
|
||||
`YukiHookAPI` 对 `PackageParam` 实现了 `lambda` 方法体 `this` 用法,在 `encase` 方法体内即可全局得到 `PackageParam` 对象。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
:::: code-group
|
||||
::: code-group-item Yuki Hook API
|
||||
|
||||
```kotlin
|
||||
override fun onHook() = encase {
|
||||
// 得到当前 Hook 的包名
|
||||
packageName
|
||||
// 得到当前 Hook 的 ApplicationInfo
|
||||
appInfo
|
||||
// 得到系统上下文对象
|
||||
systemContext
|
||||
// 得到宿主 Application 生命周期
|
||||
appContext
|
||||
// Hook 指定的 APP
|
||||
loadApp(name = "com.demo.test") {
|
||||
// Class Hook
|
||||
findClass("com.demo.test.TestClass").hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(BooleanType)
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
// Resources Hook (固定用法)
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::: code-group-item Xposed API
|
||||
|
||||
```kotlin
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
lpparam.packageName
|
||||
// 得到当前 Hook 的 ApplicationInfo
|
||||
lpparam.applicationInfo
|
||||
// 得到系统上下文对象
|
||||
// 在原生 Xposed API 中没有现成的调用方法,你需要自行反射 ActivityThread 来实现
|
||||
// 得到宿主 Application 生命周期
|
||||
AndroidAppHelper.currentApplication()
|
||||
// Class Hook
|
||||
if(lpparam.packageName == "com.demo.test")
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.demo.test.TestClass", lpparam.classLoader,
|
||||
"test", Boolean::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
resparam.packageName
|
||||
// Resources Hook
|
||||
resparam.res.setReplacement(
|
||||
"com.demo.test", "mipmap","ic_launcher",
|
||||
moduleResources.fwd(R.mipmap.ic_launcher)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::::
|
||||
|
||||
## 迁移 Hook 方法体
|
||||
|
||||
> 从 `XC_MethodHook.MethodHookParam` 迁移至 `HookParam`。
|
||||
|
||||
### Before/After Hook
|
||||
|
||||
`YukiHookAPI` 同样对 `HookParam` 实现了 `lambda` 方法体 `this` 用法,在 `beforeHook`、`afterHook` 等方法体内即可全局得到 `HookParam` 对象。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
:::: code-group
|
||||
::: code-group-item Yuki Hook API
|
||||
|
||||
```kotlin
|
||||
afterHook {
|
||||
// 得到当前 Hook 的实例
|
||||
instance
|
||||
// 得到当前 Hook 的 Class 实例
|
||||
instanceClass
|
||||
// 得到并 cast 当前 Hook 的实例为指定类型 T
|
||||
instance<T>()
|
||||
// 得到方法参数数组
|
||||
args
|
||||
// 得到方法参数的第一位 T
|
||||
args().first().cast<T>()
|
||||
// 得到方法参数的最后一位 T
|
||||
args().last().cast<T>()
|
||||
// 得到方法参数的任意下标 T,这里用 2 举例
|
||||
args(index = 2).cast<T>()
|
||||
// 设置方法参数的任意下标,这里用 2 举例
|
||||
args(index = 2).set(...)
|
||||
// 得到返回值
|
||||
result
|
||||
// 得到返回值并 cast 为 T
|
||||
result<T>()
|
||||
// 修改返回值内容
|
||||
result = ...
|
||||
// 删除返回值内容
|
||||
resultNull()
|
||||
// 向 Hook APP 抛出异常
|
||||
Throwable("Fatal").throwToApp()
|
||||
// 执行未经 Hook 的原始方法并使用原始方法参数调用,泛型可略
|
||||
callOriginal<Any?>()
|
||||
// 执行未经 Hook 的原始方法并自定义方法参数调用,泛型可略
|
||||
invokeOriginal<Any?>(...)
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::: code-group-item Xposed API
|
||||
|
||||
```kotlin
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// 得到当前 Hook 的实例
|
||||
param.thisObject
|
||||
// 得到当前 Hook 的 Class 实例
|
||||
param.thisObject.javaClass
|
||||
// 得到并 cast 当前 Hook 的实例为指定类型 T
|
||||
param.thisObject as T
|
||||
// 得到方法参数数组
|
||||
param.args
|
||||
// 得到方法参数的第一位 T
|
||||
param.args[0] as T
|
||||
// 得到方法参数的最后一位 T
|
||||
param.args[param.args.lastIndex] as T
|
||||
// 得到方法参数的任意下标 T,这里用 2 举例
|
||||
param.args[2] as T
|
||||
// 设置方法参数的任意下标,这里用 2 举例
|
||||
param.args[2] = ...
|
||||
// 得到返回值
|
||||
param.result
|
||||
// 得到返回值并 cast 为 T
|
||||
param.result as T
|
||||
// 修改返回值内容
|
||||
param.result = ...
|
||||
// 删除返回值内容
|
||||
param.result = null
|
||||
// 向 Hook APP 抛出异常
|
||||
param.throwable = Throwable("Fatal")
|
||||
// 执行未经 Hook 的原始方法
|
||||
XposedBridge.invokeOriginalMethod(param.method, param.thisObject, ...)
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
::::
|
||||
|
||||
### Replace Hook
|
||||
|
||||
`replaceHook` 方法比较特殊,`YukiHookAPI` 为它做出了多种形式以供选择。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
:::: code-group
|
||||
::: code-group-item Yuki Hook API
|
||||
|
||||
```kotlin
|
||||
/// 无返回值的方法 void
|
||||
|
||||
replaceUnit {
|
||||
// 直接在这里实现被替换的逻辑
|
||||
}
|
||||
|
||||
/// 有返回值的方法
|
||||
|
||||
replaceAny {
|
||||
// 在这里实现被替换的逻辑
|
||||
// ...
|
||||
// 需要返回方法对应的返回值,无需写 return,只需将参数放到最后一位
|
||||
// 假设这个方法的返回值是 Int,我们只需要保证最后一位是我们需要的返回值即可
|
||||
0
|
||||
}
|
||||
|
||||
/// 有些方法我们只需替换其返回值,则有如下实现
|
||||
/// 需要注意的是:直接替换返回值的方法传入的参数是固定不变的,若想实现动态替换返回值请使用上面的 replaceAny 方法体
|
||||
|
||||
// 替换为你需要的返回值
|
||||
replaceTo(...)
|
||||
// 替换为 Boolean 类型的返回值
|
||||
replaceToTrue()
|
||||
// 拦截返回值
|
||||
intercept()
|
||||
```
|
||||
|
||||
:::
|
||||
::: code-group-item Xposed API
|
||||
|
||||
```kotlin
|
||||
/// 无返回值的方法 void
|
||||
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Any? {
|
||||
// 直接在这里实现被替换的逻辑
|
||||
return null
|
||||
}
|
||||
|
||||
/// 有返回值的方法
|
||||
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Int {
|
||||
// 在这里实现被替换的逻辑
|
||||
// ...
|
||||
// 假设这个方法的返回值是 Int
|
||||
return 0
|
||||
}
|
||||
|
||||
/// 有些方法我们只需替换其返回值,则有如下实现
|
||||
|
||||
// 替换为你需要的返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = ...
|
||||
// 替换为 Boolean 类型的返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = true
|
||||
// 拦截返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = null
|
||||
```
|
||||
|
||||
:::
|
||||
::::
|
||||
|
||||
## 迁移其它功能
|
||||
|
||||
`YukiHookAPI` 对 Xposed API 进行了完全重写,你可以参考 [API 文档](../api/home) 以及 [特色功能](../api/special-features/reflection) 来决定一些功能性的迁移和使用。
|
208
docs-source/src/zh-cn/guide/quick-start.md
Normal file
208
docs-source/src/zh-cn/guide/quick-start.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# 快速开始
|
||||
|
||||
> 集成 `YukiHookAPI` 到你的项目中。
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Windows 7 及以上/macOS 10.14 及以上/Linux 发行版(Arch/Debian)
|
||||
|
||||
- Android Studio 2021.1 及以上
|
||||
|
||||
- IntelliJ IDEA 2021.1 及以上
|
||||
|
||||
- Kotlin 1.7.0 及以上
|
||||
|
||||
- Android Gradle Plugin 7.0 及以上
|
||||
|
||||
- Gradle 7.0 及以上
|
||||
|
||||
- Jvm 11 及以上 (Since API `1.0.80`)
|
||||
|
||||
## 自动构建项目
|
||||
|
||||
`YukiHookAPI` 提供了一个自动化构建工具,它可以帮助你快速构建一个拥有 Xposed 模块依赖的 Android 标准项目模板,使用构建好的模板即可直接开始下一步工作。
|
||||
|
||||
你可以 [点击这里](../tools/yukihookapi-projectbuilder) 进行查看。
|
||||
|
||||
## 手动配置项目
|
||||
|
||||
若你不想使用自动化构建工具,你依然可以按照以下方式手动配置项目依赖。
|
||||
|
||||
### 创建项目
|
||||
|
||||
使用 `Android Studio` 或 `IntelliJ IDEA` 创建新的 Android 项目,并在 `Language` 一栏选择 `Kotlin` 以自动添加基础依赖。
|
||||
|
||||
### 集成依赖
|
||||
|
||||
在你的项目 `build.gradle` 中添加依赖。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// ❗若你的 Plugin 版本过低,作为 Xposed 模块使用务必添加,其它情况可选
|
||||
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
maven { url "https://api.xposed.info/" }
|
||||
// MavenCentral 有 2 小时缓存,若无法集成最新版本请添加此地址
|
||||
maven { url "https://s01.oss.sonatype.org/content/repositories/releases" }
|
||||
}
|
||||
```
|
||||
|
||||
在你的 app `build.gradle` 中添加 `plugin`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
id 'com.google.devtools.ksp' version '<ksp-version>'
|
||||
}
|
||||
```
|
||||
|
||||
在你的 app `build.gradle` 中添加依赖。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
// 基础依赖
|
||||
implementation 'com.highcapable.yukihookapi:api:<yuki-version>'
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:<yuki-version>'
|
||||
}
|
||||
```
|
||||
|
||||
请将 **<ksp-version>** 修改为 [这里](https://github.com/google/ksp/releases) 的最新版本 **(请注意选择你当前对应的 Kotlin 版本)**。
|
||||
|
||||
请将 **<yuki-version>** 修改为 [这里](../about/changelog) 的最新版本。
|
||||
|
||||
::: danger
|
||||
|
||||
**YukiHookAPI** 的 **api** 与 **ksp-xposed** 依赖的版本必须一一对应,否则将会造成版本不匹配错误。
|
||||
|
||||
:::
|
||||
|
||||
在你的 app `build.gradle` 中修改 `Kotlin` 的 Jvm 版本为 11 及以上。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```groovy
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
自 API **1.0.80** 版本后 Jvm 版本默认为 11,不再支持 1.8 及以下版本。
|
||||
|
||||
:::
|
||||
|
||||
### 作为 Xposed 模块使用
|
||||
|
||||
在你的 `AndroidManifest.xml` 中添加基础代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<!-- 设置为 Xposed 模块 -->
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
android:value="true" />
|
||||
|
||||
<!-- 设置你的模块描述 -->
|
||||
<meta-data
|
||||
android:name="xposeddescription"
|
||||
android:value="填写你的 Xposed 模块描述" />
|
||||
|
||||
<!-- 最低 Xposed 版本号,若你正在使用 EdXposed/LSPosed,建议最低为 93 -->
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="93" />
|
||||
|
||||
<!-- 可选:配置支持 New XSharePrefs 可无需调整 xposedminversion 为 93 -->
|
||||
<meta-data
|
||||
android:name="xposedsharedprefs"
|
||||
android:value="true"/>
|
||||
```
|
||||
|
||||
在你的项目中创建一个 Hook 入口类,继承于 `IYukiHookXposedInit` 并加入注解 `@InjectYukiHookWithXposed`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = YukiHookAPI.encase {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: tip 建议
|
||||
|
||||
你可以将你的模块 APP 的 **Application** 继承于 **ModuleApplication** 以实现完整使用体验。
|
||||
|
||||
更多功能请参考 [ModuleApplication](../api/public/com/highcapable/yukihookapi/hook/xposed/application/ModuleApplication)。
|
||||
|
||||
:::
|
||||
|
||||
然后,你就可以开始编写 Hook 代码了。
|
||||
|
||||
有关作为 Xposed 模块使用的相关配置详细内容,你可以 [点击这里](../config/xposed-using) 继续阅读。
|
||||
|
||||
若你目前正在使用 Xposed API,你可以参考 [从 Xposed API 迁移](../guide/move-to-new-api)。
|
||||
|
||||
### 作为 Hook API 使用
|
||||
|
||||
#### 集成方式
|
||||
|
||||
创建你的自定义 `Application`。
|
||||
|
||||
::: danger
|
||||
|
||||
无论使用任何 **Hook Framework**,你都需要加入其对接的 Xposed 依赖支持。
|
||||
|
||||
若目标 **Hook Framework** 没有集成 Xposed API 你需要自行实现并对接 **XposedBridge**。
|
||||
|
||||
:::
|
||||
|
||||
在 `attachBaseContext` 中添加 `YukiHookAPI.encase` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 Hook Framework
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
```
|
||||
|
||||
然后,你就可以开始编写 Hook 代码了,方式与作为 Xposed 模块使用基本一致。
|
||||
|
||||
有关作为 Hook API 使用的相关配置详细内容,你可以 [点击这里](../config/api-using) 继续阅读。
|
||||
|
||||
::: warning
|
||||
|
||||
使用自定义的 Hook 框架而并非完整的 Xposed 模块时,**YukiHookModuleStatus**、**YukiHookModulePrefs**、**YukiHookDataChannel** 以及 Resources Hook 功能将失效。
|
||||
|
||||
:::
|
Reference in New Issue
Block a user