Primarily used to inject custom code into specified applications in the cloud machine, allowing modifications to the Java layer or native layer.
Automatic injection of DEX files
Automatic injection of SO files
Built-in Java layer Hook framework: LspLant
Built-in native layer Hook framework: Dobby
This project currently uses:
Important: build this project with JDK 21. If you use a newer JDK such as JDK 23,
./gradlew assembleDebugmay fail with:
Unsupported class file major version 67
It is recommended to open and build this project with Android Studio, and install these components in advance:
Settings / Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle.Gradle JDK, select Download JDK....21, download it, and apply the setting.If you only build inside Android Studio, this is the simplest approach.
You can also install OpenJDK 21 / Temurin 21 yourself and configure JAVA_HOME.
Example for Linux / macOS:
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:
org.gradle.java.home=/path/to/jdk-21
This lets Gradle use JDK 21 even when the terminal default Java version is not 21.
Gradle JDK is set to JDK 21.Build -> Make Project, or directly build assembleDebug.First, make sure the current terminal is using Java 21:
java -version
./gradlew -version
Then run:
./gradlew assembleDebug
After a successful build, the APK is generated at:
app/build/outputs/apk/debug/app-debug.apk
Unsupported class file major version 67Cause: Gradle is running with the wrong JDK version, usually JDK 23 instead of JDK 21.
Fix:
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.
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
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.
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
]
}
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;
}
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");
}
});
}
}
This module tests the Settings application. When the Settings app is closed and reopened, the relevant logs are as shown in the figure below.
