unidbg使用笔记


搭架子

public class Main implements IOResolver {
    private final AndroidEmulator emulator;

    private final VM vm;
    private final Module module;

    Main(){
        // 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
        emulator = AndroidEmulatorBuilder.for32Bit().setRootDir(new File("target/rootfs")).setProcessName("com.ashenone.demo").build();
//        emulator.getSyscallHandler().addIOResolver(this);
        // 获取模拟器的内存操作接口
        Memory memory = emulator.getMemory();
        // 设置系统类库解析
        memory.setLibraryResolver(new AndroidResolver(23));
        // 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
        vm = emulator.createDalvikVM(new File("apkpath"));
//        vm.setDvmClassFactory(new ProxyClassFactory());
        vm.setJni(new MyJni());
        //开启log
        vm.setVerbose(true);
        //最好调整一下log级别
        Logger.getLogger("com.github.unidbg.linux.ARM32SyscallHandler").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.unix.UnixSyscallHandler").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.AbstractEmulator").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm.DalvikVM").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm.BaseVM").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm").setLevel(Level.DEBUG);
        // 加载目标SO
        DalvikModule dm = vm.loadLibrary(new File("elfpath"), true);
        //获取本SO模块的句柄,后续需要用它
        module = dm.getModule();
        //调用JNI OnLoad
        dm.callJNI_OnLoad(emulator);
    }
}

补环境

Jni

public class MyJni extends AbstractJni {
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;":
                return new StringObject(vm,"/sdcard/");
        }
        return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
    }

    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature){
            case "java/lang/String->getAbsolutePath()Ljava/lang/String;":
                return new StringObject(vm,(String) dvmObject.getValue());
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }
}

主动调用

Java层

static函数

public String y2(boolean b1, String configData,boolean b2,String AinfoKey) {
    DvmClass clz = vm.resolveClass("com/ashenone/demo/MyClass");
    String methodSign = "y2(ZLjava/lang/String;ZLjava/lang/String;)Ljava/lang/String;";
    StringObject obj = clz.callStaticJniMethodObject(emulator, methodSign, b1, configData,b2,AinfoKey);
    return obj.getValue();
}

native层

通过地址调用JNI函数

public String _y2(boolean b1, String configData,boolean b2,String AinfoKey){
    // args list
    List<Object> list = new ArrayList<>(10);
    // arg1 env
    list.add(vm.getJNIEnv());
    // arg2 jobject/jclazz 一般用不到,直接填0
    list.add(0);

    list.add(vm.addLocalObject(DvmBoolean.valueOf(vm,b1)));
    list.add(vm.addLocalObject(new StringObject(vm,configData)));
    list.add(vm.addLocalObject(DvmBoolean.valueOf(vm,b2)));
    list.add(vm.addLocalObject(new StringObject(vm,AinfoKey)));

    // 参数准备完成
    // call function
    Number number = module.callFunction(emulator,
            0x3f7ad,
            list.toArray());
    String result = vm.getObject(number.intValue()).getValue().toString();
    return result;
}

通过地址调用C函数

public String decodeString(String input){
    MemoryBlock block=memory.malloc(input.length(),false);
    UnidbgPointer str_ptr=block.getPointer();
    str_ptr.write(input.getBytes());
    String content= str_ptr.getString(0);
    System.out.println("decodeString:"+str_ptr.getString(0));

    Number number = module.callFunction(emulator,
            0x79ec+1,
            str_ptr);
    UnidbgPointer result = memory.pointer(number.longValue());
    return result.getString(0);
}

参数构造

通过list来存放每个参数,最后调用toArray()传入callFunction中,但是各种类型的参数在传入list之前需要进行包装

Env

vm.getJNIEnv()

String

vm.addLocalObject(new StringObject(vm,"Hello"))

Boolean

vm.addLocalObject(DvmBoolean.valueOf(vm,true))

Context

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

byte[]

ByteArray inputByteArray = new ByteArray(vm,inputByte);
list.add(vm.addLocalObject(inputByteArray));

Patch

直接写bytecode

int patchCode = 0x4FF00100;
emulator.getMemory().pointer(module.base + offset1).setInt(offset2,patchCode);

或者用Keystone,参考SO逆向入门实战教程二:calculateS

Pointer pointer = UnidbgPointer.pointer(emulator, module.base + offset);
assert pointer != null;
byte[] code = pointer.getByteArray(0, 4);
if (!Arrays.equals(code, new byte[]{ (byte)0xFF, (byte) 0xF7, (byte) 0xEB, (byte) 0xFE })) { // BL sub_1C60
    throw new IllegalStateException(Inspector.inspectString(code, "patch32 code=" + Arrays.toString(code)));
}
try (Keystone keystone = new Keystone(KeystoneArchitecture.Arm, KeystoneMode.ArmThumb)) {
    KeystoneEncoded encoded = keystone.assemble("mov r0,1");
    byte[] patch = encoded.getMachineCode();
    if (patch.length != code.length) {
        throw new IllegalStateException(Inspector.inspectString(patch, "patch32 length=" + patch.length));
    }
    pointer.write(0, patch, 0, patch.length);
}

Hook

有多种Hook框架,只看HookZz

public void HookMDStringold(){
    // 加载HookZz
    IHookZz hookZz = HookZz.getInstance(emulator);

    hookZz.wrap(module.base + offset, new WrapCallback<HookZzArm32RegisterContext>() { // inline wrap导出函数
        @Override
        // 类似于 frida onEnter
        public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
            // 类似于Frida args[0]
            Pointer input = ctx.getPointerArg(0);
            System.out.println("input:" + input.getString(0));
        };

        @Override
        // 类似于 frida onLeave
        public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
            Pointer result = ctx.getPointerArg(0);
            System.out.println("input:" + result.getString(0));
        }
    });
}

trace

//trace指令执行
emulator.traceCode(long begin, long end);
//trace内存读写
emulator.traceRead(long begin, long end);
emulator.traceWrite(long begin, long end);

文章作者: 大A
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 大A !
评论
  目录