Unidbg补环境基础

补环境需要详细的调试信息,建议开启详细日志:vm.setVerbose(true)

系统库函数依赖

扩展点:SyscallHandler.java
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//替换默认处理器
AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(true) {
@Override
public AndroidEmulator build() {
return new AndroidARM64Emulator(processName, rootDir, backendFactories) {
@Override
protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) {
return new MySyscallHandler(svcMemory);//MySyscallHandler为自定义类
}
};
}
};
builder.setProcessName("进程名");
emulator = builder.build();

....

public class MySyscallHandler extends ARM64SyscallHandler {
public MySyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
setVerbose(true); // 可按需开启详细日志
}
// 在这里重写或添加你的处理逻辑

......

}

补JNI,Java层的调用

继承:AbstractJni.java
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...//创建DalvikVM实例之后
vm.setjni(this)
...//后面继承与重写建议在同路径下新建JAVA文件再写
//xxxx可以是`Object`, `Boolean`, `Int`, `Void` 等,根据报错判断
public class MyJNI extends AbstractJni{
@Override//模拟静态方法
public DvmObject<?> callStaticxxxxxxMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "com/example/MyClass->getAppContext()Landroid/content/Context;":
// 返回一个模拟的 Context 对象
return vm.resolveClass("android/content/Context").newObject(null);
// 可以添加更多 case 来处理其他需要补全的 Java 方法
default:
// 对于未处理的方法,务必调用父类的默认实现
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}
}
@Override //模拟构造方法
public DvmObject<?> newObejectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "java/util/HashMap-><init>()V": {
return ProxyDvmObject.createObject(vm, new HashMap<>());
}

case "com/zj/wuaipojie/ui/ChallengeTen$UserInfo-><init>(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Lcom/zj/wuaipojie/ui/ChallengeTen$AccountStatus;Ljava/util/Map;)V": {
System.out.println("【补环境 Level 3】拦截到 UserInfo 构造方法");
Map<String, DvmObject<?>> userInfoData = new HashMap<>();
userInfoData.put("status", vaList.getObjectArg(4));
userInfoData.put("properties", vaList.getObjectArg(5));
return dvmClass.newObject(userInfoData);
}
}
return super.newObjectV(vm, dvmClass, signature, vaList);
}
@Override //模拟字段访问
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
// 匹配枚举类的 PREMIUM 静态字段
if ("com/zj/wuaipojie/ui/ChallengeTen$AccountStatus->PREMIUM:Lcom/zj/wuaipojie/ui/ChallengeTen$AccountStatus;".equals(signature)) {
System.out.println("【补环境】拦截到获取 AccountStatus.PREMIUM 静态字段");
// 创建一个枚举实例(用字符串"PREMIUM"作为其值,方便后续name()方法返回正确结果)
DvmObject<?> premium = dvmClass.newObject("PREMIUM");
return premium;
}
return super.getStaticObjectField(vm, dvmClass, signature);
}

@Override
public void setIntField(BaseVM vm, DvmObject<?> dvmObject, String signature, int value) {
// signature的格式是:com/example/User->age:I
if ("com/example/User->age:I".equals(signature)) {
System.out.println("SO 正在设置 User 对象的 age 字段,值为: " + value);
// 你可以在这里记录值,或者什么都不做
return; // 注意set方法是void返回
}
super.setIntField(vm, dvmObject, signature, value);
}
}

补文件访问

unidbg有责任机制:优先是用户定义的文件处理器->unidbg默认文件处理器
添加文件处理器(在模拟执行之前):emulator.getSyscallHandler().addIOResolver()
1、继承:IOResolver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
emulator.getSyscallHandler().addIOResolver(this);//注册IOResolver

......

public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
System.out.println("[IOResolver] Intercepted file access -> Path: '" + pathname + "', Flags: " + oflags);
switch(pathname){
case "xxxx":
return FileResult.success(new ByteArrayFileIO(oflags, pathname, 字符串.getBytes()))//动态文件,只能读
case "xxxxxx":
return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/cpu/boot_id"), pathname));//物理文件,可读写
case "xxxxx":
return FileResult.failed(UnixEmulator.EACCES);//访问失败
}
}

2、rootDir:

1
2
3
4
5
6
emulator = AndroidEmulatorBuilder
.for64Bit()
//虚拟目录
.setRootDir(new File("unidbg-android/src/test/resources/FileDemo/VFS"))
.build();//创建仿真器的时候创建根目录
//直接操作VFS文件夹,so产生的所有文件访问最后兜底的就是这个文件系统,

优先级:IOResolver > rootDir,均属于VFS系统。可以同时使用。

如何看报错

判断缺失数据类型:

signature的组成一般是:(类路径)->(字段名):(字段类型签名);
例如com/zj/wuaipojie/util/SecurityUtil$Config->deviceId:Ljava/lang/String;:
com/zj/wuaipojie/util/SecurityUtil$Config:表示在config这个子类中;
deviceId:表示缺的是config类中的deviceId成员变量;
Ljava/lang/String;:表示这个变量的类型是一个字符串。
由此可知我们在补环境时,补的是一个字符串变量。

一个较完整补环境模板

MainActivity.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.example;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.android.AndroidARM64Emulator;
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.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixEmulator;
import com.github.unidbg.unix.UnixSyscallHandler;
import com.example.MySyscallHandler;
import net.dongliu.apk.parser.Main;

import java.io.File;
import java.io.FileNotFoundException;

public class MainActivity extends AbstractJni implements IOResolver<AndroidFileIO> {

private final AndroidEmulator emulator ;
private final VM vm;
private final Module module;

public MainActivity(){
//替换默认处理器
AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(true) {
@Override
public AndroidEmulator build() {
return new AndroidARM64Emulator(processName, rootDir, backendFactories) {
@Override
protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) {
return new MySyscallHandler(svcMemory);
}
};
}
};
builder.setProcessName("进程名");
emulator = builder.setRootDir(new File("unidbg/unidbg-android/src/test/resources/example")).build();//创建模拟器,设置模拟文件系统(补文件访问环境)
emulator.getSyscallHandler().setVerbose(true);//开启详细日志
emulator.getSyscallHandler().addIOResolver(this);//注册IOResolver
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM();

MyJni myjni=new MyJni(vm);
vm.setJni(myjni);
vm.setVerbose(true);

File soFile = new File("unidbg-android/src/test/so路径");
DalvikModule dm = vm.loadLibrary(soFile, true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
}

//补文件访问
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
// 无论是否处理,都打印出来,这是发现未知文件访问的关键。
System.out.println("[IOResolver] Intercepted file access -> Path: '" + pathname + "', Flags: " + oflags);
switch(pathname){
case "xxxxxxx":{
String statusContent="xxxxxxxxxxxx";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, statusContent.getBytes()));//成功返回,动态,仅可读,不可写
}
case "xxxxxx": {
//.......
return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/cpu/boot_id"), pathname));//物理文件,可读写
}
case "xxxxx": {
//........
return FileResult.failed(UnixEmulator.EACCES);//访问失败,权限不足
}

}
return null;//return null表示交给下一个处理器
}

public void run(){
DvmClass securityUtilClass = vm.resolveClass("");
StringObject result = securityUtilClass.callStaticJniMethodObject(emulator, "");
}

public static void main(String[] args) throws FileNotFoundException {
MainActivity myinstance=new MainActivity();
myinstance.run();
}
}

MyJni.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example;

import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.VM;

public class MyJni extends AbstractJni {
private final VM vm;
public MyJni(VM vm) {
this.vm=vm;
super();
}
// 补JNI环境
// @Override
// public .......

}

MySyscallHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example;

import com.github.unidbg.Emulator;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.linux.ARM64SyscallHandler;
import com.github.unidbg.memory.SvcMemory;
import java.util.Random;


public class MySyscallHandler extends ARM64SyscallHandler{
private final Random rng = new Random();

public MySyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
setVerbose(true); // 可按需开启详细日志
}
@Override
protected boolean handleUnknownSyscall(Emulator<?> emulator, int NR) {
System.err.println(">>> MySyscallHandler is processing syscall NR = " + NR);
Backend backend = emulator.getBackend();
switch (NR) {
case 1: {
//To Do
}
case 2: {
//To do
}

}
return super.handleUnknownSyscall(emulator, NR);
}
}

学习资源:《安卓逆向这档事》