LSPosed/LSPlant

GitHub: LSPosed/LSPlant

LSPlant 是一个面向 Android ART 的轻量级钩子框架,解决在运行时动态插桩与方法操控的需求。

Stars: 1237 | Forks: 295

# LSPlant ![](https://img.shields.io/badge/license-LGPL--3.0-orange.svg) ![](https://img.shields.io/badge/Android-5.0%20--%2015%20Beta2-blue.svg) ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86--64%7C%20riscv64-brightgreen.svg) ![](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/df448aa4b0231441.svg) ![](https://img.shields.io/maven-central/v/org.lsposed.lsplant/lsplant.svg) 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, 云资产清单, 内联钩子, 反编译, 开源框架, 持续集成, 方法钩子, 符号解析, 调试, 逆向工程