### 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](https://resource-wangsu.helplook.net/docker_production/wkpufm/article/wXQ8ezGk/attachments/dplus_demo.zip)
[dplus_demo](https://resource-wangsu.helplook.net/docker_production/wkpufm/article/8FACyMwh/attachments/app-debug.apk)

### 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
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
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
java -version
./gradlew -version
```

Then run:

```bash
./gradlew assembleDebug
```

After a successful build, the APK is generated at:

```bash
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
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:

```
// 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.

![](https://resource-wangsu.helplook.net/docker_production/wkpufm/article/Jnu4zw3n/68ec705330f5a.png)

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:

```
{
    "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:

```
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.

```
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.

![](https://resource-wangsu.helplook.net/docker_production/wkpufm/article/Jnu4zw3n/68ec7375b7f5f.png)
