LSPosed/LSPlant
GitHub: LSPosed/LSPlant
LSPlant 是一个面向 Android ART 的轻量级钩子框架,解决在运行时动态插桩与方法操控的需求。
Stars: 1237 | Forks: 295
# LSPlant





LSPlant 是一个 Android ART 钩子库,提供 Java 方法钩子/取消钩子以及内联去优化功能。
该项目是 LSPosed 框架的一部分,遵循 GNU Lesser General Public License。
## 功能特性
+ 支持 Android 5.0 - 15 Beta2(API 级别 21 - 35)
+ 支持 armeabi-v7a、arm64-v8a、x86、x86-64、riscv64
+ 支持自定义内联钩子框架和 ART 符号解析器
## 文档
https://lsposed.org/LSPlant/namespacelsplant.html
## 快速开始
```
repositories {
mavenCentral()
}
android {
buildFeatures {
prefab true
}
}
dependencies {
implementation "org.lsposed.lsplant:lsplant:+"
}
```
如果你不想在 APK 中包含 `libc++_shared.so`,可以使用 `lsplant-standalone`:
```
dependencies {
implementation "org.lsposed.lsplant:lsplant-standalone:+"
}
```
### 1. 在 JNI_OnLoad 中初始化 LSPlant
为后续钩子操作初始化 LSPlant。主要用于预取所需符号并钩住一些函数。
+ `env` 是 Java 环境。
+ `info` 是初始化信息。
通常,info 提供了内联钩子和取消钩器,以及用于钩取和提取 ART 所需原生函数的 `libart.so` 符号解析器。
```
bool Init(JNIEnv *env,
const InitInfo &info);
```
返回初始化是否成功。在初始化完成前或初始化失败后调用其他 LSPlant 接口,行为未定义。
### 2. 钩子
通过提供 `target_method` 以及上下文对象 `hooker_object` 和其回调 `callback_method` 来钩住一个 Java 方法。
+ `env` 是 Java 环境。
+ `target_method` 是要钩住的目标方法的 `Method` 对象。
+ `hooker_object` 是用于存储钩子上下文的对象。
最可能的用法是将其存储备份方法,以便在 `callback_method` 被调用时可以调用原始方法。另一种场景是,例如在 Xposed 框架中,多个模块可以钩住同一个 Java 方法,此时 `hooker_object` 可用于存储所有回调,以允许多个模块同时工作而不冲突。
+ `callback_method` 是一个 `Method` 对象,用于替换 `target_method` 的回调方法。
每当 `target_method` 被调用时,将调用 `callback_method` 而不是原始 `target_method`。`callback_method` 的签名必须为:`public Object callback_method(Object []args)`。
即返回类型必须为 `Object`,参数类型必须为 `Object[]`。如果签名不匹配,行为未定义。可以通过定义 `hooker_object` 的成员变量来提供额外信息。此方法必须是 `hooker_object` 的一个方法。
```
jobject Hook(JNIEnv *env,
jobject target_method,
jobject hooker_object,
jobject callback_method);
```
返回备份方法。你可以通过反射调用它来调用原始方法。如果失败则返回 null。
此函数会自动为钩子生成一个存根类。若要帮助调试,可以通过在 `InitInfo` 中设置 `generated_*` 来指定生成的类名、字段名、源文件名和方法名。
此函数是线程安全的(你可以从多个线程同时调用它),但对同一个 `target_method` 不是原子的。这意味着在返回之前,`UnHook` 或 `IsUnhook` 不能保证在同一个 `target_method` 上正常工作。同时调用此函数针对同一个 target_method 也不能保证只有一个会成功。如果对同一个 `target_method` 同时使用不同的 `hooker_object`,行为未定义。
### 3. 检查
检查一个 Java 函数是否已被 LSPlant 钩住。
```
bool IsHooked(JNIEnv *env,
jobject method);
```
返回该方法是否已被钩住。
### 4. 取消钩子
取消一个之前已被钩住的 Java 函数。
+ `env` 是 Java 环境。
+ `target_method` 是要取消钩子的目标方法的 `Method` 对象。
```
bool UnHook(JNIEnv *env,
jobject target_method);
```
返回取消钩子是否成功。
在取消钩子后调用备份方法(`Hook()` 的返回值)是未定义行为。请阅读 `Hook()` 的注释以获取更多细节。
### 5. 去优化
去优化一个方法以避免被钩住的被调用者因内联而未被调用。
+ `env` 是 Java 环境。
+ `method` 是要去优化的方法的 `Method` 对象。
通过去优化该方法,该方法将不再内联其被调用者。例如,如果你钩住了方法 B,而方法 A 调用了 B,但你发现对 B 的回调未被触发,这可能意味着 A 在其方法体内内联了 B。要强制 A 调用你钩住的 B,可以对 A 进行去优化。这样钩子就能生效。通常需要找到被钩住方法的所有调用者,这很难实现。如果确定需要去优化的调用者都是你需要的,可以使用此函数;否则,最好更改钩点或手动对整个应用进行去优化(只需重新安装应用而不卸载)。
```
bool Deoptimize(JNIEnv *env,
jobject method);
```
返回去优化是否成功。
对已钩住的方法调用去优化是安全的,因为去优化会在备份方法上执行。
## 致谢
灵感来自以下框架:
- [YAHFA](https://github.com/PAGalaxyLab/YAHFA)
- [SandHook](https://github.com/asLody/SandHook)
- [Pine](https://github.com/canyie/pine)
- [Epic](https://github.com/tiann/epic)
标签:Android 15, Android 5.0, Android ART, Android Runtime, API 21, API 35, arm64-v8a, armeabi-v7a, Hakrawler, Java方法钩取, JNI, LGPL-3.0, LSPlant, LSPosed, riscv64, URL提取, x86, x86-64, 云资产清单, 内联钩子, 反编译, 开源框架, 持续集成, 方法钩子, 符号解析, 调试, 逆向工程