JNI Implementation

JNI Implementation

When native code calls back into Java (e.g., CallStaticIntMethod, GetIntField), unidbg routes these through the Jni interface. Extend AbstractJni and override only what the native library actually calls.

Setup

1public class MyJni extends AbstractJni {
2    // override methods as needed
3}
4
5VM vm = emulator.createDalvikVM();
6vm.setJni(new MyJni());
7vm.setVerbose(true);  // logs unhandled JNI calls — useful for discovery

Override patterns

Static method returning primitive

1@Override
2public long callStaticLongMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
3    if ("com/example/MyClass->myMethod(I)J".equals(signature)) {
4        int arg = varArg.getIntArg(0);
5        return arg * 2L;
6    }
7    return super.callStaticLongMethod(vm, dvmClass, signature, varArg);
8}

Static method returning object

1@Override
2public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
3    if ("com/example/MyClass->getKey()Ljava/lang/String;".equals(signature)) {
4        return new StringObject(vm, "secret-key");
5    }
6    return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
7}

Instance method

1@Override
2public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
3    if ("com/example/MyClass->getValue()I".equals(signature)) {
4        return 42;
5    }
6    return super.callIntMethod(vm, dvmObject, signature, varArg);
7}

Field access

 1@Override
 2public int getIntField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
 3    if ("com/example/MyClass->count:I".equals(signature)) {
 4        return 100;
 5    }
 6    return super.getIntField(vm, dvmObject, signature);
 7}
 8
 9@Override
10public void setIntField(BaseVM vm, DvmObject<?> dvmObject, String signature, int value) {
11    if ("com/example/MyClass->count:I".equals(signature)) {
12        // capture or ignore
13        return;
14    }
15    super.setIntField(vm, dvmObject, signature, value);
16}

Object construction

1@Override
2public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
3    if ("com/example/MyClass-><init>()V".equals(signature)) {
4        return dvmClass.newObject(null);
5    }
6    return super.newObject(vm, dvmClass, signature, varArg);
7}

VarArg — extracting arguments

1int i = varArg.getIntArg(0);
2long l = varArg.getLongArg(0);
3DvmObject<?> obj = varArg.getObjectArg(0);

Built-in DvmObject wrappers

WrapperUsage
StringObjectnew StringObject(vm, "text")
DvmIntegerDvmInteger.valueOf(vm, 42)
DvmLongDvmLong.valueOf(vm, 42L)
DvmBooleanDvmBoolean.valueOf(vm, true)
ByteArraynew ByteArray(vm, bytes)
IntArraynew IntArray(vm, ints)

Signature format

Field: com/example/ClassName->fieldName:FieldType Method: com/example/ClassName->methodName(ParamTypes)ReturnType

Key classes

ClassPath
AbstractJniunidbg-android/.../dvm/AbstractJni.java
Jni (interface)unidbg-android/.../dvm/Jni.java
VarArgunidbg-android/.../dvm/VarArg.java
DvmObjectunidbg-android/.../dvm/DvmObject.java
StringObjectunidbg-android/.../dvm/StringObject.java
Array wrappersunidbg-android/.../dvm/array/
Primitive wrappersunidbg-android/.../dvm/wrapper/