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
| Wrapper | Usage |
|---|---|
StringObject | new StringObject(vm, "text") |
DvmInteger | DvmInteger.valueOf(vm, 42) |
DvmLong | DvmLong.valueOf(vm, 42L) |
DvmBoolean | DvmBoolean.valueOf(vm, true) |
ByteArray | new ByteArray(vm, bytes) |
IntArray | new IntArray(vm, ints) |
Signature format
Field: com/example/ClassName->fieldName:FieldType
Method: com/example/ClassName->methodName(ParamTypes)ReturnType
Key classes
| Class | Path |
|---|---|
AbstractJni | unidbg-android/.../dvm/AbstractJni.java |
Jni (interface) | unidbg-android/.../dvm/Jni.java |
VarArg | unidbg-android/.../dvm/VarArg.java |
DvmObject | unidbg-android/.../dvm/DvmObject.java |
StringObject | unidbg-android/.../dvm/StringObject.java |
| Array wrappers | unidbg-android/.../dvm/array/ |
| Primitive wrappers | unidbg-android/.../dvm/wrapper/ |