一、前言
unidbg是一个基于unicorn的逆向工具,可黑盒调用移动app中的so文件,运行时不需要模拟器或真机。有时用真机调so文件难以过反调试,而unidbg可以弥补这块缺陷。当然了,unidbg也不是万能的,怎么给unidbg补环境也是个问题。
二、步骤
1. 首先要将java、maven、IDEA配置好。
https://blog.csdn.net/pan_junbiao/article/details/104264644
2. 下载unidbg项目
https://github.com/zhkl0228/unidbg
3. 导入到IDEA中
unidbg项目是java编写的,上述下载的是一个标准maven项目。
4. 测试unidbg
项目中的测试用例:src/test/java/com/bytedance/frameworks/core/encrypt/TTEncrypt.java,直接执行其中的main方法,会出现下述的提示
三、实例
看雪2021 KCTF秋季赛第8题 群狼环伺为一道安卓逆向题
https://ctf.pediy.com/game-season_fight-187.htm
我们可以采用unidbg模拟执行,按照正常步骤运行时,会出现如下错误
[14:10:36 669] WARN[com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:530) -handleInterrupt intno=2, NR=190, svcNumber=0x0, PC=RX@0x40284b5c[libc.so]0x41b5c, LR=RX@0x402725cb[libc.so]0x2f5cb, syscall=null
[14:10:36 670] WARN[com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:530) -handleInterrupt intno=2, NR=358, svcNumber=0x0, PC=RX@0x40284db0[libc.so]0x41db0, LR=RX@0x40272635[libc.so]0x2f635, syscall=null
顺着该错误信息往前倒,用ida在IntelliJIDEA v23版本arm版libc.so中定位到地址0x2f5cb和0x2f635,发现是在调用popen()是出现了错误。
从下面的帖子,说这个报错较大可能性是样本在通过popen或者system执行命令
https://github.com/zhkl0228/unidbg/issues/342
事实上,Unidbg目前还不支持直接跑popen,需要手动处理一下,继承和使用自己的syscallHandler
https://blog.csdn.net/qq_38851536/article/details/118073818
下面贴出完整的运行该so的完整unidbg代码
crackeme.java
package com.vprotect;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.context.Arm32RegisterContext;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.hook.hookzz.*;
import com.github.unidbg.linux.android.AndroidARMEmulator;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixSyscallHandler;
import java.io.File;
import java.io.FileNotFoundException;
public class crackme extends AbstractJni {
private final AndroidEmulator emulator;
private final Module module;
private final VM vm;
private final DvmClass NativeClass;
public crackme() throws FileNotFoundException {
AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(false) {
@Override
public AndroidEmulator build() {
return new AndroidARMEmulator(processName, rootDir, backendFactories) {
@Override
protected UnixSyscallHandler
createSyscallHandler(SvcMemory svcMemory) { return new MyARMSyscallHandler(svcMemory);
}
};
};
};
emulator = builder.setProcessName("com.vprotect").build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/vprotect/KCTF2021-android-crackme.apk"));
DalvikModule dm = vm.loadLibrary("crackme", true);
module = dm.getModule();
vm.setVerbose(true);
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
NativeClass = vm.resolveClass("www/vprotect/cn/crackme");
}
public static void main(String[] args) {
crackme mainActivity = null;
try {
mainActivity = new crackme();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
String ret = mainActivity.getGoodLuck();
System.out.println("getGoodLuck函数返回:"+ret);
}
public String getGoodLuck(){
String str1 = "4DD6F06301B04D13";
String str2 = "6461323135643835663832623133666234313635636637383266613665363961";
String str3 = "1234567";
StringObject stringObject = NativeClass.newObject(null).callJniMethodObject(emulator, "GoodLuck(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", str1, str2, str3);
return stringObject.getValue();
}
}
MyARMSyscallHandler.java
package com.vprotect;
import com.github.unidbg.Emulator;
import com.github.unidbg.arm.context.EditableArm32RegisterContext;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.DumpFileIO;
import com.github.unidbg.memory.SvcMemory;
import com.sun.jna.Pointer;
import java.util.concurrent.ThreadLocalRandom;
public class MyARMSyscallHandlerextends com.github.unidbg.linux.ARM32SyscallHandler{
public MyARMSyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
}
@Override
protected boolean handleUnknownSyscall(Emulator emulator, int NR) {
switch (NR) {
case 190:
vfork(emulator);
return true;
case 359:
pipe2(emulator);
return true;
}
return super.handleUnknownSyscall(emulator, NR);
}
private void vfork(Emulator emulator) {
EditableArm32RegisterContext context = (EditableArm32RegisterContext) emulator.getContext();
int childPid = emulator.getPid() + ThreadLocalRandom.current().nextInt(256);
int r0 =0;
r0 = childPid;
System.out.println("vfork pid=" + r0);
context.setR0(r0);
}
protected int pipe2(Emulator emulator) {
EditableArm32RegisterContext context = (EditableArm32RegisterContext) emulator.getContext();
Pointer pipefd = context.getPointerArg(0);
int flags = context.getIntArg(1);
int write = getMinFd();
this.fdMap.put(write, new DumpFileIO(write));
int read = getMinFd();
String stdout ="myid\n"; // getprop ro.build.id
this.fdMap.put(read, new ByteArrayFileIO(0, "pipe2_read_side", stdout.getBytes()));
pipefd.setInt(0, read);
pipefd.setInt(4, write);
System.out.println("pipe2 pipefd=" + pipefd +", flags=0x" + flags +", read=" + read +", write=" + write +", stdout=" + stdout);
context.setR0(0);
return 1;
}
}
最终返回“输入错误”