0%

工具使用-unidbg

[TOC]

unidbg

  • 版本:0.9.3
  • 下载地址:

环境

  • app: ddan.1.27.1.1028.1607website.434.apk
  • so: libInnoSecure.so

代码

unidbg java smail
ByteArray byte[] [B
StringObject String
  • 创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    1. 
    byte[] data = new byte[16];
    new ByteArray(vm, data)

    2.
    new StringObject(vm, "12345")

    3.
    DvmInteger dvmInt = DvmInteger.valueof(vm, 2)

    3.
    DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null)
    vm.addLocalObject(context)

    4.
    class UnidbgPointer extends Pointer

    模块
    AndroidModule extends VirtualModule<VM>
    DalvikModule
    Module
    LinuxModule extends Module
  • 打补丁

    1
    2
    3
    4
    5
    6
    int patchCode = 0x4FF00100; //0x4FF00100:mov r0,1的机器码
    emulator.getMemory().pointer(module.base + 0x1E86).setInt(0,patchCode);

    Pointer pointer = UnidbgPointer.pointer(emulator, module.base + 0x1E86);
    byte[] patch
    pointer.write(0, patch, 0, patch.length);

定义类

  • 位置

unidbg-android/src/test/java/com/包下新建一个包,名字自己定,比如:jason.qjp

在自己建的包下新建类,名字自己定,比如:TestDanDan

  • 有二种方式
1
2
3
4
5
6
7
方式一:extends方式
public class TestDanDan extends AbstractJni {
}

方式二:
public class TestDanDan {
}

推荐用方式一:public class TestDanDan extends AbstractJni

  • 2种方式的区别:

extends方式:方式二适用的场景,全部适用,还能解决方式二不适用的场景

变量值

需要确定的变量值:

  1. for32Bit() / for64Bit()
  2. classPath
  • 32位还是64位
1
2
//for32Bit(), for64Bit() 可选
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.jifen.dandan").build();

确定方法:
方法一:试错法
在确定apk内有libInnoSecure.so的情况下,先试for64Bit()

1
2
3
4
Exception in thread "main" java.lang.IllegalStateException: load library failed: InnoSecure
at com.github.unidbg.linux.android.dvm.BaseVM.loadLibrary(BaseVM.java:206)
at com.jason.qjp.TestDanDan.<init>(TestDanDan.java:61)
at com.jason.qjp.TestDanDan.main(TestDanDan.java:28)

发现出错了,改为for32Bit()

方法二:file命令查看libInnoSecure.so

1
2
➜ file libInnoSecure.so
libInnoSecure.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=aa4d75912e8c15080bdf1eafaeb45c3267b5e4b8, stripped

方法三: 查看apk的lib目录

1
2
3
4
5
lib
armeabi-v7a/
libInnoSecure.so
x86/
libInnoSecure.so

armeabi-v7a为32位

  • classPath
1
2
String classPath = "com/inno/innosecure/InnoSecureUtils";
vm.resolveClass(classPath);

jadx-gui反编译apk,在里面搜索System.loadLibrary("InnoSecure");,所在的类为:com.inno.innosecure.InnoSecureUtils, 把.替换为/即为classPath的值

  • libso
1
2
String libso = "InnoSecure";
DalvikModule dm = vm.loadLibrary( libso,false);

这里只给了so的名字InnoSecurevm.loadLibrary知道从哪里加载。
因为有写如下代码:

1
2
3
4
vm = emulator.createDalvikVM(new File(apkFilePath));
vm.setVerbose(true);
vm.setJni(this);
new AndroidModule(emulator, vm).register(memory);

这里的场景是:只给了一个apk,没有单独的so文件,把apk做为AndroidModule加载到内存register(memory),vm.loadLibrary从内存memory加载so

函数调用

  • DvmClass

    1
    DvmObject callStaticJniMethodObject(Emulator<?> emulator, String method, Object...args)
  • Module

    1
    2
    3
    Number[] callFunction(Emulator<?> emulator, long offset, Object... args)

    Number[] callFunction(Emulator<?> emulator, String symbolName, Object... args)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //获取JNIEnv *
    Pointer jniEnv = vm.getJNIEnv();

    //创建jobject对象
    DvmObject<?> thiz = vm.resolveClass("com.kanxue.test2").newObject(null);

    //准备入参
    List<Object> args = new ArrayList<>();
    args.add(jniEnv);
    args.add(vm.addLocalObject(thiz));
    args.add(vm.addLocalObject(new StringObject(vm,"XuE")));

    //根据地址调用
    Number[] numbers = module.callFunction(emulator, 0x9180 + 1, args.toArray());
    System.out.println(numbers[0].intValue());

AbstractJni方法重写

1
2
3
4
5
String apkFilePath = "/Users/zhoujie/Downloads/ddan.1.27.1.1028.1607website.434.apk";        
String classPath = "com/inno/innosecure/InnoSecureUtils";
String libso = "InnoSecure";
TestDanDan test = new TestDanDan(apkFilePath, libso, classPath);
test.destroy();

运行输出如下:

1
2
3
4
5
6
JNIEnv->FindClass(com/inno/innosecure/InnoSecureUtils) was called from RX@0x4000245d[libInnoSecure.so]0x245d
JNIEnv->RegisterNatives(com/inno/innosecure/InnoSecureUtils, RW@0x4000a004[libInnoSecure.so]0xa004, 4) was called from RX@0x40002473[libInnoSecure.so]0x2473
RegisterNative(com/inno/innosecure/InnoSecureUtils, secure(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)[B, RX@0x40005d75[libInnoSecure.so]0x5d75)
RegisterNative(com/inno/innosecure/InnoSecureUtils, decode([BLjava/lang/String;[B)[B, RX@0x40005edd[libInnoSecure.so]0x5edd)
RegisterNative(com/inno/innosecure/InnoSecureUtils, getn()Ljava/lang/String;, RX@0x40006069[libInnoSecure.so]0x6069)
RegisterNative(com/inno/innosecure/InnoSecureUtils, getv([B)Ljava/lang/String;, RX@0x400060ed[libInnoSecure.so]0x60ed)

是因为设置了如下代码:vm.setVerbose(true),设置是否打印Jni调用细节
从输出可以看出libInnoSecure.so动态注册了4个方法

  • 调用so中方法
1
2
3
4
5
6
7
8
9
10
11
12
// java中的声明:public native String getn();

public class TestDanDan extends AbstractJni {
// ......
private final DvmClass MainActivity;
MainActivity = vm.resolveClass(classPath);
String getn() {
DvmObject obj = MainActivity.newObject(null);
DvmObject result = obj.callJniMethodObject(emulator, "getn()Ljava/lang/String;");
return result.toString();
}
}
1
2
DvmObject<String> result = obj.callJniMethodObject(emulator, "getn()Ljava/lang/String;");
return result.getValue();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception {
//......
TestDanDan test = new TestDanDan(apkFilePath, libso, classPath);
String r = test.getn();
System.out.println(r);
}

// 出错:
JNIEnv->CallStaticObjectMethod(class android/app/ActivityThread, currentApplication()Landroid/app/Application;) was called from RX@0x40001d73[libInnoSecure.so]0x1d73
[10:19:55 045] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:436) - handleInterrupt intno=2, NR=-1073744048, svcNumber=0x12e, PC=unidbg@0xfffe0374[libandroid.so]0x374, syscall=null
java.lang.UnsupportedOperationException: android/app/ActivityThread->currentApplication()Landroid/app/Application;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:340)
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:335)
at com.github.unidbg.linux.android.dvm.DvmMethod.callStaticObjectMethod(DvmMethod.java:55)
at com.github.unidbg.linux.android.dvm.DalvikVM$47.handle(DalvikVM.java:1032)
at com.github.unidbg.linux.ARM32SyscallHandler.hook(ARM32SyscallHandler.java:93)
  • 添加callStaticObjectMethod
1
2
3
4
5
6
7
8
@Override
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if (signature.equals("android/app/ActivityThread->currentApplication()Landroid/app/Application;")) {
return vm.resolveClass("android/app/Application", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(signature);
}

return super.callStaticObjectMethod(vm,dvmClass,signature,varArg);
}
  • callStaticObjectMethod vs callStaticObjectMethodV
  • callObjectMethod vs callObjectMethodV
1
2
3
4
5
6
7
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
throw new UnsupportedOperationException(signature);
}

public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
//...... 一大堆代码
}
1
2
3
4
5
6
7
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
//...... 一大堆代码
}

public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
//...... 一大堆代码
}
  1. 优先重写callStaticObjectMethodcallObjectMethod
  2. 带V的一般没调用到
  • 被调用的系统API
1
2
3
4
5
6
android/app/ActivityThread->currentApplication()Landroid/app/Application;
android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;
android/app/Application->getPackageName()Ljava/lang/String;
android/content/pm/PackageManager->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
android/content/pm/Signature->toCharsString()Ljava/lang/String;
java/lang/String->getBytes(Ljava/lang/String;)[B

图片

  • AndroidEmulator
    AndroidEmulator
  • VM
    VM