Library / SDK
LSPosed/LSPlant avatar
LSPosed/LSPlant

LSPlant: Android ART method hooking with inline deoptimization

A hook framework for Android Runtime (ART)

1,407 stars341 forksC++LGPL-3.0

At a glance

What is it?
LSPlant is an LGPL-3.0 C++ library for hooking and unhooking Java methods on Android ART, with support for API levels 21 through 37 and a mechanism for deoptimizing callers affected by inlining.
Who is it for?
LSPlant fits native Android framework authors who already manage a JNI environment, `libart.so` symbol resolution, and an inline hooking backend. It is unsuitable for a conventional app-only stack or for callers that require predictable concurrent hooks on the same `Method`.
Can I use it commercially?
Yes, with conditions. LGPL-3.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why LSPlant targets Android runtime internals

LSPlant solves a native Android integration problem: changing the behavior of a Java method after the application and ART runtime are already loaded. It is aimed at developers building framework-level components that work with `Method` objects through JNI, rather than developers who only need ordinary Java source changes or a conventional Android application dependency.

The library supports Android 5.0 through Android 17, corresponding to API levels 21 through 37. Its documented binary targets are `armeabi-v7a`, `arm64-v8a`, `x86`, `x86-64`, and `riscv64`. That range makes LSPlant relevant to code that must span phone, emulator, and newer Android ABI environments, although the documentation does not describe device-specific setup beyond supplying the library to the Android build.

LSPlant is part of the LSPosed framework and is licensed under GNU Lesser General Public License 3.0. It has no GitHub releases listed. The repository is not archived, and its last push was on 2026-10-06, so this review does not treat it as an abandoned project. The README still leaves several operational questions to the linked API documentation and the consuming framework.

The native bridge behind LSPlant hooks

LSPlant operates between JNI and ART. A consumer first calls `Init` from `JNI_OnLoad`, supplying a `JNIEnv` and an `InitInfo` object. According to the README, initialization prefetches required symbols and hooks some functions. The `InitInfo` provides an inline hooker, an unhooker, and a symbol resolver for `libart.so`, which LSPlant uses to reach native ART functions.

The returned value from `Init` reports whether initialization succeeded. The documented boundary is strict: calling another LSPlant interface before initialization, or after initialization fails, produces undefined behavior. This is more consequential than an ordinary setup error because the library cannot provide a safe fallback path once its runtime assumptions are missing.

A hook requires a target `Method`, a `hooker_object`, and a callback `Method`. When the target is invoked, LSPlant invokes the callback instead of the original method. The callback must belong to `hooker_object`, return `Object`, and accept `Object[]`. The hooker's fields can retain context, such as a backup method or callback state shared by multiple framework modules. `Hook` returns a backup method that can be invoked reflectively, or `null` when the hook fails.

Installing the Maven package in an Android build

The README provides two Gradle dependency choices. The normal artifact is `org.lsposed.lsplant:lsplant:+`, and the documented Android build enables Prefab with `buildFeatures { prefab true }`. The `+` version selector is copied from the README, so a production project would still need to review how its dependency policy handles dynamic versions.

The following block is the README's basic dependency setup. It adds the Maven Central repository and declares LSPlant as an implementation dependency:

gradle
repositories {
    mavenCentral()
}

android {
    buildFeatures {
        prefab true
n}

dependencies {
    implementation "org.lsposed.lsplant:lsplant+"
}

If the application should not include `libc++_shared.so` in its APK, the README directs consumers to `lsplant-standalone`:

gradle
dependencies {
    implementation "org.lsposed.lsplant:lsplant-standalone:+"
}

That choice is a packaging decision with native consequences. The README does not provide a complete sample Android application, a `JNI_OnLoad` implementation, or instructions for selecting an ABI at packaging time. Those parts must be supplied by the integrating project.

A first JNI hook and its callback contract

A first use begins after the Android library has been loaded and the native library has entered JNI. The consumer must initialize LSPlant before calling any other interface. The API shown below is the documented initializer, not a complete `JNI_OnLoad` implementation:

c++
bool Init(JNIEnv *env,
          const InitInfo &info);

After successful initialization, obtain the target Java method and callback objects in JNI, then call `Hook`. The callback is not an arbitrary function signature. It must be a method of the supplied `hooker_object` with this shape:

java
public Object callback_method(Object []args)

The native call itself is:

c++
jobject Hook(JNIEnv *env,
             jobject target_method,
             jobject hooker_object,
             jobject callback_method);

The returned `jobject` is the backup method. The README says it can be invoked reflectively to reach the original implementation. A null return indicates failure. The README does not specify how a caller obtains `Method` objects, constructs `InitInfo`, or stores the backup in Java fields, so those details belong to the integrating framework. A hook also causes LSPlant to generate a stub class. For debugging, the generated class, field, source, and method names can be controlled through the `generated_*` entries in `InitInfo`.

Unhooking, inspection, and ART inlining limits

LSPlant exposes a direct status check and a removal operation:

c++
bool IsHooked(JNIEnv *env,
              jobject method);

Use `IsHooked` to determine whether LSPlant has hooked a Java method. Removal takes the target `Method` and returns whether the operation succeeded:

c++
bool UnHook(JNIEnv *env,
            jobject target_method);

The backup method must not be called after unhooking. The README calls that undefined behavior. There is also a concurrency boundary. `Hook` is documented as thread-safe when called simultaneously from multiple threads, but operations targeting the same `target_method` are not atomic. `UnHook` and `IsHooked` therefore do not provide a reliable result for that same method before the concurrent operation returns. Simultaneous hooks on one target with different `hooker_object` instances have undefined behavior.

ART inlining creates a separate failure case. If method A has inlined method B, a later hook on B may not run through A. LSPlant addresses this with `Deoptimize`, which backs callees out of inlining for a selected method:

c++
bool Deoptimize(JNIEnv *env,
                jobject method);

The README says callers must identify the relevant callers themselves. Applying it to a hooked method is safe because LSPlant deoptimizes the backup method instead. It is still a targeted runtime intervention, not a blanket solution to every missed hook.

Where LSPlant is the wrong integration choice

LSPlant is not a general-purpose Java mocking library. Its API requires native JNI integration, ART symbol resolution, and an `InitInfo` containing an inline hook framework. An application team that only needs dependency injection, a Java method wrapper, or a test double does not need this runtime-level mechanism, and the README does not describe those use cases.

The inline deoptimization feature also has a practical limitation. A developer who hooks a small callee may need to locate every caller whose compiled code contains that callee. The README explicitly says that finding all callers can be difficult. Deoptimizing a caller that is not actually affected adds unnecessary runtime disruption, while choosing the wrong hook point may leave the behavior unchanged. For a hook that must affect an inlined call, the library asks the consumer to decide which callers matter rather than discovering the complete call graph automatically.

The same-target concurrency rule is another reason to reject LSPlant for designs that expect independent modules to attach hooks to one `Method` without coordination. Multiple different hooker objects are explicitly undefined in that situation. The library supports context through `hooker_object`, but support for multiple modules is not permission for uncoordinated simultaneous hooks.

Packaging, licensing, and upgrade expectations

The normal Maven artifact and `lsplant-standalone` differ in native packaging. The README specifically identifies `libc++_shared.so` as the reason to consider the standalone artifact, so an integrator should inspect the resulting APK rather than assume the two dependencies are interchangeable. The README does not document binary size, minimum NDK, linker flags, symbol visibility, or release packaging details.

The project uses LGPL-3.0. The README identifies LSPlant as part of the LSPosed framework under that license, but it does not provide instructions for relinking, source distribution, or other obligations. This article therefore does not interpret the license beyond naming it. A team responsible for distribution should review the repository's `LICENSE` file and its own legal obligations separately.

There are no GitHub releases to compare or upgrade from using the repository listing. The repository has recent repository activity, with the last push on 2026-10-06, but the README does not publish a compatibility matrix for individual commits or versions. Since the examples use `+`, an application may resolve a changing artifact unless its own Gradle configuration constrains versions. That makes the exact resolved artifact and its native contents an important first verification point.

Editorial conclusion

LSPlant fits native Android framework authors who already manage a JNI environment, `libart.so` symbol resolution, and an inline hooking backend. It is unsuitable for a conventional app-only stack or for callers that require predictable concurrent hooks on the same `Method`. Before adoption, verify the `lsplant-standalone` packaging choice, the `InitInfo` inline hooker and unhooker, and the callback signature `public Object callback_method(Object []args)` against the target Android versions.

Frequently asked questions

What is LSPlant used for?

LSPlant is an Android ART hook library for native integrations. It hooks and unhooks Java methods through JNI and can deoptimize callers affected by ART inlining.

How do I initialize LSPlant?

Call `Init(JNIEnv *env, const InitInfo &info)` from `JNI_OnLoad`, with an `InitInfo` containing the inline hooker, unhooker, and `libart.so` symbol resolver. Other LSPlant interfaces must not be called before initialization succeeds.

Which callback signature does LSPlant require?

The callback must be a method of `hooker_object` with the signature `public Object callback_method(Object []args)`. A different signature produces undefined behavior according to the README.

Official sources

  1. Issues
  2. License: LGPL-3.0
  3. LSPosed/LSPlant on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/lsposed-lsplant.svg)](https://hysenlabs.com/projects/lsposed-lsplant)

Was this useful?