补环境需要详细的调试信息,建议开启详细日志:vm.setVerbose(true)
系统库函数依赖
扩展点:SyscallHandler.java
eg:
//替换默认处理器
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:
...//创建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
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:
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:
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:
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:
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);
}
}
学习资源:《安卓逆向这档事》