Complete Example

Complete Example

Minimal SO call (no JNI)

 1public class MinimalTest {
 2    public static void main(String[] args) throws Exception {
 3        AndroidEmulator emulator = AndroidEmulatorBuilder.for64Bit()
 4            .setProcessName("com.example.test")
 5            .build();
 6
 7        Memory memory = emulator.getMemory();
 8        memory.setLibraryResolver(new AndroidResolver(23));
 9
10        Module module = emulator.loadLibrary(new File("libnative.so"));
11        Number result = module.callFunction(emulator, "native_func");
12        System.out.println("Result: " + result.intValue());
13
14        emulator.close();
15    }
16}

Android JNI full workflow

 1public class MyApp extends AbstractJni {
 2
 3    private final AndroidEmulator emulator;
 4    private final VM vm;
 5    private final DvmClass myClass;
 6
 7    public MyApp() throws Exception {
 8        emulator = AndroidEmulatorBuilder.for32Bit()
 9            .setProcessName("com.example.app")
10            .setRootDir(new File("target/rootfs/default"))
11            .addBackendFactory(new DynarmicFactory(true))
12            .build();
13
14        Memory memory = emulator.getMemory();
15        memory.setLibraryResolver(new AndroidResolver(23));
16
17        vm = emulator.createDalvikVM(new File("app.apk"));
18        vm.setVerbose(true);   // log unhandled JNI calls
19        vm.setJni(this);
20
21        DalvikModule dm = vm.loadLibrary(new File("libnative.so"), true);
22        dm.callJNI_OnLoad(emulator);
23
24        myClass = vm.resolveClass("com/example/app/NativeHelper");
25    }
26
27    // JNI callback: native code calls Java
28    @Override
29    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
30        if ("com/example/app/Utils->getDeviceId()Ljava/lang/String;".equals(signature)) {
31            return new StringObject(vm, "fake-device-id-12345");
32        }
33        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
34    }
35
36    @Override
37    public int callStaticIntMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
38        if ("android/os/Build$VERSION->SDK_INT:I".equals(signature)) {
39            return 23;
40        }
41        return super.callStaticIntMethod(vm, dvmClass, signature, varArg);
42    }
43
44    // Invoke the target function
45    public byte[] sign(String input) throws Exception {
46        DvmObject<?> result = myClass.callStaticJniMethodObject(emulator,
47            "sign(Ljava/lang/String;)[B",
48            new StringObject(vm, input));
49
50        ByteArray byteArray = (ByteArray) result;
51        return byteArray.getValue();
52    }
53
54    public static void main(String[] args) throws Exception {
55        MyApp app = new MyApp();
56        byte[] signed = app.sign("data-to-sign");
57        System.out.println(Arrays.toString(signed));
58        app.emulator.close();
59    }
60}

Hook + call pattern

 1// Hook before calling to intercept internals
 2IxHook xHook = XHookImpl.getInstance(emulator);
 3xHook.register("libnative.so", "RAND_bytes", new ReplaceCallback() {
 4    @Override
 5    public HookStatus onCall(Emulator<?> emulator, long originFunction) {
 6        // Fix randomness for reproducible output
 7        RegisterContext ctx = emulator.getContext();
 8        UnidbgPointer buf = ctx.getPointerArg(0);
 9        int len = ctx.getIntArg(1);
10        buf.write(0, new byte[len], 0, len);  // zero fill
11        return HookStatus.LR(emulator, 1);    // return success
12    }
13});
14xHook.refresh();
15
16Number result = module.callFunction(emulator, "encrypt", dataPtr, dataLen);

Trace-and-call pattern

1// Enable tracing for a specific function range
2long funcAddr = module.base + 0x2000;
3long funcEnd  = module.base + 0x2200;
4emulator.traceCode(funcAddr, funcEnd);
5
6// Call will print disassembly as it executes
7module.callFunction(emulator, 0x2000);

Test class location

Reference implementations in the project:

FileLocation
AndroidTest.javaunidbg-android/src/test/java/com/github/unidbg/android/
Android64Test.javaunidbg-android/src/test/java/com/github/unidbg/android/
QDReaderJni.javaunidbg-android/src/test/java/com/github/unidbg/android/
AndroidNativeEmuTest.javaunidbg-android/src/test/java/org/aeonlucid/