Menu

How to develop plug-in modules

Module Plugin Usage Scenarios

Primarily used to inject custom code into specified applications in the cloud machine, allowing modifications to the Java layer or native layer.

Supported Features

Automatic injection of DEX files

Automatic injection of SO files

Built-in Java layer Hook framework: LspLant

Built-in native layer Hook framework: Dobby

Module Example Download

dplus_demo code
dplus_demo

Build Environment

This project currently uses:

  • Android Gradle Plugin: 8.6.0
  • Gradle Wrapper: 8.7
  • compileSdk / targetSdk: 34
  • minSdk: 29
  • CMake: 3.22.1
  • JDK: 21

Important: build this project with JDK 21. If you use a newer JDK such as JDK 23, ./gradlew assembleDebug may fail with:

Unsupported class file major version 67

Environment Preparation

It is recommended to open and build this project with Android Studio, and install these components in advance:

  • Android SDK Platform 34
  • Android SDK Build-Tools
  • CMake 3.22.1
  • Android NDK
  • JDK 21

Download and Configure JDK 21

Option 1: Download via Android Studio

  1. Open Android Studio.
  2. Go to Settings / Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle.
  3. In Gradle JDK, select Download JDK....
  4. Choose version 21, download it, and apply the setting.

If you only build inside Android Studio, this is the simplest approach.

Option 2: Install JDK 21 manually

You can also install OpenJDK 21 / Temurin 21 yourself and configure JAVA_HOME.

Example for Linux / macOS:

bash Copy
export JAVA_HOME=/path/to/jdk-21
export PATH=$JAVA_HOME/bin:$PATH

java -version

After confirming the output contains 21, run the build command.

If you use zsh, you can append the two environment variable lines above to ~/.zshrc to make them persistent.

If you do not want to change the system default JDK, you can also point Gradle to JDK 21 explicitly. Add this to ~/.gradle/gradle.properties or the project's root gradle.properties:

properties Copy
org.gradle.java.home=/path/to/jdk-21

This lets Gradle use JDK 21 even when the terminal default Java version is not 21.

Build Instructions

Build with Android Studio

  1. Open the project in Android Studio.
  2. Make sure Gradle JDK is set to JDK 21.
  3. Wait for Gradle Sync to finish.
  4. Run Build -> Make Project, or directly build assembleDebug.

Build from the command line

First, make sure the current terminal is using Java 21:

bash Copy
java -version
./gradlew -version

Then run:

bash Copy
./gradlew assembleDebug

After a successful build, the APK is generated at:

bash Copy
app/build/outputs/apk/debug/app-debug.apk

Common Issue

1. Unsupported class file major version 67

Cause: Gradle is running with the wrong JDK version, usually JDK 23 instead of JDK 21.

Fix:

bash Copy
export JAVA_HOME=/path/to/jdk-21
export PATH=$JAVA_HOME/bin:$PATH
java -version
./gradlew assembleDebug

If you build in Android Studio, switch Gradle JDK to JDK 21.

If you keep multiple JDKs installed and do not want to switch frequently, you can also use the org.gradle.java.home setting above to pin Gradle to JDK 21.

Module Installation

Open the project in Android Studio. After compilation, obtain the module file app-debug.apk.

Upload this file to the cloud machine, for example, to the path /sdcard/Download/app-debug.apk.

Use the following commands for installation and viewing:

Copy
// First connect to the cloud machine
adb connect xx.xx.xx.xx

// Enter the cloud machine terminal
adb shell

// Then install the module
dplus install patch:/sdcard/Download/app-debug.apk

// After successful installation, view the module
dplus dump

// Uninstall and delete the module by module name
dplus uninstall dplus_demo

Module Development Introduction

The module source code structure is as shown in the figure below.

The config.json file is used to configure the module.

native-lib.cpp implements examples of Dobby hooks.

Entry.java serves as the entry point for Java layer hooks.

Config Configuration Explanation

This file is primarily used to describe the module and guide the injection process. Sample data is as follows:

Copy
{
    "name": "dplus_demo",   // Module name, used during uninstallation
    "package":"com.example.dplus_demo",  // Module package name
    "desc": "module",       // Module description
    "type": "user",         // Module type, mainly "system" or "user"
    "libs": "libdplus_demo.so",  // SO files to inject; can be omitted or configured with multiple entries separated by ';'
    "pattern": [
        "com.android.settings"   // Package names where the module takes effect
    ]
}

Dobby Example

The following demonstrates a simple hook on the openat function to output logs:

Copy
int (*source_openat)(int fd, const char *path, int oflag, int mode) = nullptr;

int MyOpenAt(int fd, const char *pathname, int flags, int mode) {
    LOGI("MyOpenAt  pathname %s",pathname);
    return source_openat(fd, pathname, flags, mode);
}

void HookOpenAt() {
    // Resolve function address
    void *__openat =
            DobbySymbolResolver("libc.so", "__openat");

    if (__openat == nullptr) {
        LOGI("__openat null ");
        return;
    }

    LOGI("Got __openat address ");
    
    // Replace original function with Dobby
    if (DobbyHook((void *) __openat,
                  (void *) MyOpenAt,
                  (void **) &source_openat) == 0) {
        LOGI("DobbyHook __openat success");
    }
}

jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {

    LOGI("Test JNI_OnLoad starting load");
    HookOpenAt();
    return JNI_VERSION_1_6;
}

Java Layer Example

The init function serves as an entry point after the application starts. Execute relevant logic here. The LspLant-related code has been slightly modified, so some classes and functions may not use their original names.

Copy
public class Entry {

    public String TAG="demo_Entry";

    public void init(Application app){
        DPLog.i(TAG,"enter init");
        LSPHelpers.findAndHookMethod("java.util.HashMap", app.getClassLoader(), "put",Object.class,Object.class, new LSP_MethodH() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                Log.i(TAG,"enter HashMap.put key:"+param.args[0]+",value:"+param.args[1]);
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Log.i(TAG,"leave HashMap.put");
            }
        });
    }

}

Effects After Module Injection

This module tests the Settings application. When the Settings app is closed and reopened, the relevant logs are as shown in the figure below.

Previous
Cloud Number SMS
Next
How to Pass Google Verification
Last modified: 2026-04-21Powered by