补系统调用(一)之得物
apk:得物
版本:4.94.0
目标方法:com.shizhuang.stone.main.SzSdk.lf
目标方法实现:libszstone.so
- 方法实现如下:
public static native byte[] lf(String str, int i2, int i3);1. 基本框架
- 基本的jni环境如下,这里不讨论;
package com.kanxue2w.sana;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
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.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class dewu extends AbstractJni {
public static AndroidEmulator emulator;
public static Memory memory;
public static VM vm;
public static Module module;
public dewu() {
emulator = AndroidEmulatorBuilder
.for32Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.shizhuang.duapp")
.build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("apks/kanxue2w/du/du.apk"));
vm.setJni(this);
// vm.setVerbose(true);
new AndroidModule(emulator, vm).register(memory);
DalvikModule dm = vm.loadLibrary("szstone", true);
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
}
public void call_lf() {
List<Object> list = new ArrayList<>(10);
// jnienv
list.add(vm.getJNIEnv());
list.add(0);
list.add(vm.addLocalObject(new StringObject(vm, "awt0bapt/data/user/0/com.shizhuang.duapp/filesbav4.94.0bavn486bcndewubc")));
list.add(1);
list.add(0);
Number number = module.callFunction(emulator, 0x302fd, list.toArray());
String res = (String) vm.getObject(number.intValue()).getValue();
System.out.println("call_lf-->> " + res);
}
@Override
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
switch (signature) {
case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;": {
// System.out.println("<<<--- dvmClass --->>>"+dvmClass);
return dvmClass.newObject(null);
}
case "android/provider/Settings$Secure->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;": {
System.out.println("getString方法 dvmClass -->>" + dvmClass);
System.out.println("getString方法 参数2 -->>" + varArg.getObjectArg(1).getValue());
return new StringObject(vm, "8drd14lf");
}
}
return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
}
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;": {
return vm.resolveClass("android/app/Application").newObject(null);
}
case "android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;": {
return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
}
case "android/app/Application->getContentResolver()Landroid/content/ContentResolver;": {
return vm.resolveClass("android/content/ContentResolver").newObject(null);
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}
public static void main(String[] args) {
dewu du = new dewu();
du.call_lf();
}
}2. clock_gettime
- 依据上述的代码骨架,尝试继续运行,会出现以下报错;
[17:10:10 070] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=263, svcNumber=0x0, PC=RX@0x40117b88[libc.so]0x40b88, LR=RX@0x400f0ef3[libc.so]0x19ef3, syscall=null
java.lang.UnsupportedOperationException: clk_id=2
at com.github.unidbg.linux.ARM32SyscallHandler.clock_gettime(ARM32SyscallHandler.java:1760)
at com.github.unidbg.linux.ARM32SyscallHandler.hook(ARM32SyscallHandler.java:411)
at com.github.unidbg.arm.backend.Unicorn2Backend$11.hook(Unicorn2Backend.java:343)
at com.github.unidbg.arm.backend.unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:109)- 首先分析这个错误是什么意思?一般来说,svcNumber等于0即代表系统调用,NR则是调用号,并且结合报错路径结合判断,它是一个系统调用的异常,去看看什么调用;

- 很显然,它也做了提示,这里也可以去异常位置看看;

- 这里证明,不只是一个结果,其实 Unidbg 对 clock_gettime 做了模拟实现,只是并没有做到完美的形态,它做到了一些分支,可以去看看;

- 那么需要知道这个系统调用具体是在做什么吧,问问ai;
// clock_gettime 是 Linux 系统中的一个高精度时间获取系统调用,用于从指定类型的时钟源中获取当前时间戳,支持纳秒级精度;
// 函数原型与参数:
#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);
//参数说明:
// clk_id:时钟类型标识符,决定时间来源(见下文详解)。
// tp:指向 struct timespec 的指针,用于存储获取的时间值。
// 该结构体包含:
// tv_sec:自 Unix Epoch(1970年1月1日00:00:00 UTC)的秒数。
// tv_nsec:纳秒级精度的时间(范围:0~999,999,999)。
// 返回值:成功返回 0,失败返回 -1 并设置 errno。
// timespec 结构体
struct timespec {
long tv_sec; // 秒时间戳
long tv_nsec; // 余下的纳秒时间戳
};- 如果想获取毫秒级时间戳,可以像下面这样写:
long long gettime3(){
struct timespec t = {0, 0};
clock_gettime(CLOCK_REALTIME, &t);
return (t.tv_sec*1000) + (t.tv_nsec/1000000);
}- 参数1是时钟类型标识符,在此是2,大致有以下的选择;
#define CLOCK_REALTIME 0 //当前的实际时间,是自 1970年1月1日(UNIX 时间纪元)以来经过的秒数,受到系统时钟调整的影响
#define CLOCK_MONOTONIC 1 //自系统启动以来经过的时间
#define CLOCK_PROCESS_CPUTIME_ID 2 //当前进程消耗的 CPU 时间
#define CLOCK_THREAD_CPUTIME_ID 3 //当前线程消耗的 CPU 时间
#define CLOCK_MONOTONIC_RAW 4 //类似于CLOCK_MONOTONIC,但是它不会受到任何系统时间调整或时钟源变化的影响
#define CLOCK_REALTIME_COARSE 5 //类似于 CLOCK_REALTIME,但是提供较低精度的时间
#define CLOCK_MONOTONIC_COARSE 6 //类似于 CLOCK_MONOTONIC,但提供较低精度的时间
#define CLOCK_BOOTTIME 7 //系统启动以来经过的时间,包含了系统休眠时间
#define CLOCK_REALTIME_ALARM 8 //当前时间,但以时间报警机制的精度为准
#define CLOCK_BOOTTIME_ALARM 9 //系统启动以来的时间,类似于 CLOCK_BOOTTIME,但它同样考虑了定时器或闹钟机制的精度。
#define CLOCK_SGI_CYCLE 10 //SGI(Silicon Graphics)的专有时钟
#define CLOCK_TAI 11 //国际原子时- 简而言之,时钟可以分为 真实时间、CPU 时间、开机时间、从不确定的某个时间点开始计时 这四大类;
- Unidbg、Qiling、ExAndroidNativeEmu 等模拟器,其实都是对它有所实现,但又没有实现每一个时钟;
CLOCK_REALTIME这个时钟,也就是真实时间,来看看各个工具如何实现:
# ExAndroidNativeEmu 通过 Python 内置 API 获得了秒级时间戳,然后赋给 tv_sec,tv_nsec 则是置空
if clk_id == CLOCK_REALTIME:
# Its time represents seconds and nanoseconds since the Epoch.
clock_real = calendar.timegm(time.gmtime())
mu.mem_write(tp_ptr + 0, int(clock_real).to_bytes(self.__ptr_sz, byteorder='little'))
mu.mem_write(tp_ptr + self.__ptr_sz, int(0).to_bytes(self.__ptr_sz, byteorder='little'))
return 0
# 这么做不太好,意味着获取返回的真实时间其精度只有秒,和语义有一定偏差
# qiling 则是完全没有管其他类型的时钟,只当成真实时间去处理
def ql_syscall_clock_gettime(ql, clock_gettime_clock_id, clock_gettime_timespec, *args, **kw):
ql.log.info(f"clock_gettime(clock_id={clock_gettime_clock_id}, tp={hex(clock_gettime_timespec)})")
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
tp = timespec(tv_sec= tv_sec, tv_nsec=tv_nsec)
ql.mem.write(clock_gettime_timespec, bytes(tp))
ql.log.debug(f"timespec(tv_sec={tv_sec}, tv_nsec={tv_nsec})")
return 0
# 相比 ExAndroidNativeEmu,它更完善一些,对 tv_nsec 也做了赋值
# Unidbg 在CLOCK_REALTIME时钟下,通过 Java 标准库获取了毫秒级时间戳,把秒的部分赋给 tv_sec,再毫秒部分乘 1000000 赋给 tv_nsec。和 Qiling 的逻辑完全一致
long offset = clk_id == CLOCK_REALTIME ? System.currentTimeMillis() * 1000000L : System.nanoTime() - nanoTime;- 简而言之,这几款工具都做了一定程度上的模拟,但都不齐全不完善,当然不止是上面的这种情况,那么这里的问题是clk_id=2的情况,2 对应于 CLOCK_PROCESS_CPUTIME_ID 即进程的 CPU 时间,这里我们也破罐子破摔,并入 CLOCK_THREAD_CPUTIME_ID;这是龙哥的原话,那么我们也这么来,因为3,即:
#define CLOCK_PROCESS_CPUTIME_ID 2 //当前进程消耗的 CPU 时间
#define CLOCK_THREAD_CPUTIME_ID 3 //当前线程消耗的 CPU 时间- 它俩是可以类似的,我们就按这个方案来处理,所以这里应该在源码里修改吗?当然不选择这样做,这是一种不优雅的做法,和补jni环境的时候类似,我们自实现系统调用处理器,新建一个类来做处理;
package com.kanxue2w.sana.dewu;
import com.github.unidbg.Emulator;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.linux.ARM32SyscallHandler;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;
import unicorn.ArmConst;
public class deWuSyscallHandler extends ARM32SyscallHandler {
public deWuSyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
}
protected int clock_gettime(Backend backend, Emulator<?> emulator) {
int clk_id = backend.reg_read(ArmConst.UC_ARM_REG_R0).intValue();
Pointer tp = UnidbgPointer.register(emulator, ArmConst.UC_ARM_REG_R1);
if (clk_id == 2) {
tp.setInt(0, 0);
tp.setInt(4, 1);
return 0;
}
return super.clock_gettime(backend, emulator);
}
}- 这里依据上面的逻辑,完全模仿着unidbg的处理就行了,在我们之前的代码需要使用这个系统调用,换成如下写法:
package com.kanxue2w.sana.dewu;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
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.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.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixSyscallHandler;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class dewu extends AbstractJni implements IOResolver {
@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
System.out.println("sana凑崎纱夏 file open:" + pathname);
return null;
}
public static AndroidEmulator emulator;
public static Memory memory;
public static VM vm;
public static Module module;
public dewu() {
// 重点start
// 创建模拟器实例 AndroidEmulatorBuilder 的参数表示是否是 64 位的
AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(false) {
@Override
public AndroidEmulator build() {
return new AndroidARMEmulator(processName, rootDir, backendFactories) {
@Override
protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) {
return new deWuSyscallHandler(svcMemory);
}
};
}
};
// 重点end
emulator = builder
// .for32Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.shizhuang.duapp")
.build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("apks/kanxue2w/du/du.apk"));
vm.setJni(this);
vm.setVerbose(true);
new AndroidModule(emulator, vm).register(memory);
DalvikModule dm = vm.loadLibrary("szstone", true);
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
}
public void call_lf() {
List<Object> list = new ArrayList<>(10);
// jnienv
list.add(vm.getJNIEnv());
list.add(0);
list.add(vm.addLocalObject(new StringObject(vm, "awt0bapt/data/user/0/com.shizhuang.duapp/filesbav4.94.0bavn486bcndewubc")));
list.add(1);
list.add(0);
Number number = module.callFunction(emulator, 0x302fd, list.toArray());
String res = (String) vm.getObject(number.intValue()).getValue();
System.out.println("call_lf-->> " + res);
}
@Override
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
switch (signature) {
case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;": {
return dvmClass.newObject(null);
}
case "android/provider/Settings$Secure->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;": {
System.out.println("getString方法 dvmClass -->>" + dvmClass);
System.out.println("getString方法 参数2 -->>" + varArg.getObjectArg(1).getValue());
return new StringObject(vm, "8drd14lf");
}
}
return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
}
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;": {
return vm.resolveClass("android/app/Application").newObject(null);
}
case "android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;": {
return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
}
case "android/app/Application->getContentResolver()Landroid/content/ContentResolver;": {
return vm.resolveClass("android/content/ContentResolver").newObject(null);
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}
public static void main(String[] args) {
dewu du = new dewu();
du.call_lf();
}
}- 运行看看会有什么情况;
[16:59:52 968] INFO [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:1911) - openat dirfd=-100, pathname=/proc/sys/kernel/random/boot_id, oflags=0x20000, mode=0
[16:59:52 973] INFO [com.github.unidbg.linux.AndroidSyscallHandler] (AndroidSyscallHandler:456) - Return default pipe pair.
[16:59:52 973] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=190, svcNumber=0x0, PC=RX@0x40118b5c[libc.so]0x41b5c, LR=RX@0x401065cb[libc.so]0x2f5cb, syscall=null
[16:59:52 974] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=358, svcNumber=0x0, PC=RX@0x40118db0[libc.so]0x41db0, LR=RX@0x40106635[libc.so]0x2f635, syscall=null
java.lang.AbstractMethodError: com.github.unidbg.linux.file.PipedWriteFileIO
at com.github.unidbg.file.AbstractFileIO.dup2(AbstractFileIO.java:160)
at com.github.unidbg.linux.ARM32SyscallHandler.dup3(ARM32SyscallHandler.java:2124)
at com.github.unidbg.linux.ARM32SyscallHandler.hook(ARM32SyscallHandler.java:501)
at com.github.unidbg.arm.backend.Unicorn2Backend$11.hook(Unicorn2Backend.java:343)
at com.github.unidbg.arm.backend.unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:109)3. popen
- 新的报错非常多,首先关键信息主要有以下内容:
[16:59:52 973] INFO [com.github.unidbg.linux.AndroidSyscallHandler] (AndroidSyscallHandler:456) - Return default pipe pair.
[16:59:52 973] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=190, svcNumber=0x0, PC=RX@0x40118b5c[libc.so]0x41b5c, LR=RX@0x401065cb[libc.so]0x2f5cb, syscall=null
[16:59:52 974] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=358, svcNumber=0x0, PC=RX@0x40118db0[libc.so]0x41db0, LR=RX@0x40106635[libc.so]0x2f635, syscall=null
java.lang.AbstractMethodError: com.github.unidbg.linux.file.PipedWriteFileIO- 先看系统调用,这里有两处;
NR=190 vfork
NR=358 dup3
popen内部会调用vfork和dup3 这种错误:com.github.unidbg.linux.file.PipedWriteFileIO 直接到popen去处理- 龙哥原话:为什么这里有多个系统调用会
WARN?事实上这是库函数popen惹的祸,只要读者看到com.github.unidbg.linux.file.PipedWriteFileIO报错,那么就应该直接转入处理popen的逻辑; 这就是典型的popen问题,我们这里要了解三个小知识:
- 基本语义和用法?
- 它是什么意思?
- Unidbg 为什么会在这个库函数上出错?
- Unidbg 里如何处理这个问题?
- 首先,popen 在 Android Native 上是一个高频函数,远非
getrusage这类函数可比,设备信息收集、环境检测都可以用到它;函数原型如下:
FILE *popen(const char *command, const char *type);- 其次它是什么意思?
popen 是 C 标准库中的一个函数(属于 文件操作接口,而非直接的系统调用),用于创建一个管道(pipe)并启动子进程执行指定的命令。其核心功能是 在程序中调用外部命令并与其输入/输出交互,类似于在终端中执行命令并捕获结果;- popen 以创建管道的方式启动一个进程, 并调用 shell,像下面这样使用,比如这里在作用上等价于 adb shell 里输入
getprop ro.build.id;
std::string cmd = "getprop ro.build.id";
char value[PROP_VALUE_MAX] = {0};
FILE* file = popen(cmd.c_str(), "r");
fread(value, PROP_VALUE_MAX, 1, file);
pclose(file);- 其次为什么 Unidbg 会在这个库函数上出错,popen 在底层原理上是创建了一个新进程,然后通过管道去做通信,返回数据;但 Unidbg 在多进程相关的系统调用处理上十分不完善,无法支撑
popen正常的执行它的底层逻辑; - 最后是 Unidbg 里如何处理好 popen,作者提供过一种好思路,就是越过创建多线程的相关逻辑,只处理好管道的返回值,来让它的功能基本满足;
- 下面做演示,这可以当成固定处理模式,遇到了就这么做就行;
- 第一步是用 Dobby 或任意 Hook 工具,在库函数的层面 Hook 获取到 popen 的 command,放到 emulator 的全局变量里,方便后续形成对应。我这里使用的是 xhook;
public void hookPopen(){
IxHook xHook = XHookImpl.getInstance(emulator);
xHook.register("libszstone.so", "popen", new ReplaceCallback() {
@Override
public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
RegisterContext registerContext = emulator.getContext();
String command = registerContext.getPointerArg(0).getString(0);
emulator.set("command", command);
System.out.println("sana command-->>"+command);
return HookStatus.RET(emulator, originFunction);
}
}, true);
// 使其生效
xHook.refresh();
}- 看看运行结果是什么,然后再根据提示去补;

- 我们在第一个自实现类里实现pipe2方法,重写 pipe2,根据当前的 command 返回从 adb shell 获取到的值,以及出现 popen 内部会使用的 vfork 和 wait4 系统调用,并依次在代码里进行解释;
package com.kanxue2w.sana.dewu;
import com.github.unidbg.Emulator;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.context.EditableArm32RegisterContext;
import com.github.unidbg.linux.ARM32SyscallHandler;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.DumpFileIO;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;
import unicorn.ArmConst;
import java.util.concurrent.ThreadLocalRandom;
public class deWuSyscallHandler extends ARM32SyscallHandler {
public deWuSyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
}
@Override
protected boolean handleUnknownSyscall(Emulator<?> emulator, int NR) {
switch (NR) {
// 仿照源码写
case 190:
vfork(emulator);
return true;
case 114:
wait4(emulator);
return true;
}
return super.handleUnknownSyscall(emulator, NR);
}
protected int clock_gettime(Backend backend, Emulator<?> emulator) {
int clk_id = backend.reg_read(ArmConst.UC_ARM_REG_R0).intValue();
Pointer tp = UnidbgPointer.register(emulator, ArmConst.UC_ARM_REG_R1);
if (clk_id == 2) {
tp.setInt(0, 0);
tp.setInt(4, 1);
return 0;
}
return super.clock_gettime(backend, emulator);
}
@Override
protected int pipe2(Emulator<?> emulator) {
EditableArm32RegisterContext context = (EditableArm32RegisterContext)
emulator.getContext();
Pointer pipefd = context.getPointerArg(0);
int write = getMinFd();
this.fdMap.put(write, new DumpFileIO(write));
int read = getMinFd();
String command = emulator.get("command");
System.out.println("sana pipe2 command-->>" + command);
// stdout中写入popen command 应该返回的结果
String stdout = "\n";
switch (command) {
// 这里根据command来判断 给它返回shell里的结果
case "stat /data": {
stdout = " File: /data\n" +
" Size: 4096 Blocks: 16 IO Blocks: 512 directory\n" +
"Device: 10305h/66309d Inode: 2 Links: 53\n" +
"Access: (0771/drwxrwx--x) Uid: ( 1000/ system) Gid: ( 1000/ system)\n" +
"Access: 1970-01-20 05:08:00.000000000 +0800\n" +
"Modify: 2024-11-19 21:13:13.707334006 +0800\n" +
"Change: 1970-05-22 00:15:12.503333433 +0800";
}
}
this.fdMap.put(read, new ByteArrayFileIO(0, "pipe2_read_side", stdout.getBytes()));
pipefd.setInt(0, read);
pipefd.setInt(4, write);
context.setR0(0);
return 0;
}
// 补足相应的系统调用报错 根据调用号查看
private void vfork(Emulator<?> emulator) {
EditableArm32RegisterContext context = emulator.getContext();
int childPid = emulator.getPid() +
ThreadLocalRandom.current().nextInt(256);
int r0 = childPid;
System.out.println("vfork pid-->>" + r0);
context.setR0(r0);
}
// 补足相应的系统调用报错 根据调用号查看
private void wait4(Emulator<?> emulator) {
return;
}
}- 尝试运行,看看会有什么反应;

- 大概截图一点,可以看到,还有多个case的情况,需要补齐,这里不管了,因为下一个异常已经出来了;
- 另外,包括文件访问的处理也并没有做;
又是系统调用,依旧去查看,// 这是文件访问
[16:24:07 447] INFO [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:1122) - stat64 pathname=/data/data/com.microvirt.launcher, LR=RX@0x400024c9[libszstone.so]0x24c9
[16:24:07 456] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=54, svcNumber=0x0, PC=RX@0x40118aa8[libc.so]0x41aa8, LR=RX@0x4011e7f1[libc.so]0x477f1, syscall=null
java.lang.AbstractMethodError: com.github.unidbg.linux.file.TcpSocket: request=0x8927, argp=0xbffff470
at com.github.unidbg.file.AbstractFileIO.ioctl(AbstractFileIO.java:57)
at com.github.unidbg.linux.ARM32SyscallHandler.ioctl(ARM32SyscallHandler.java:2045)
at com.github.unidbg.linux.ARM32SyscallHandler.hook(ARM32SyscallHandler.java:210)
at com.github.unidbg.arm.backend.Unicorn2Backend$11.hook(Unicorn2Backend.java:343)
at com.github.unidbg.arm.backend.unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:109)4. ioctl
[16:24:07 456] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:534) - handleInterrupt intno=2, NR=54, svcNumber=0x0, PC=RX@0x40118aa8[libc.so]0x41aa8, LR=RX@0x4011e7f1[libc.so]0x477f1, syscall=null
java.lang.AbstractMethodError: com.github.unidbg.linux.file.TcpSocket: request=0x8927, argp=0xbffff470
at com.github.unidbg.file.AbstractFileIO.ioctl(AbstractFileIO.java:57)
at com.github.unidbg.linux.ARM32SyscallHandler.ioctl(ARM32SyscallHandler.java:2045)
at com.github.unidbg.linux.ARM32SyscallHandler.hook(ARM32SyscallHandler.java:210)NR=54对应ioctl,在 Android Native 里,它常用来获取 MAC 地址。它的参数request用于指令操作类型,对于各种各样的文件和需求,有不同的request,Unidbg 首先预处理了其中的一部分,但这里报错的 0x8927 尚未处理;- 一个最简单的处理办法就是来到报错点
com.github.unidbg.file.AbstractFileIO.ioctl(AbstractFileIO.java:64),添加判断request=0x8927的情况,让其返回0;
@Override
public int ioctl(Emulator<?> emulator, long request, long argp) {
// add ioctl start
// if (log.isTraceEnabled()) {
// emulator.attach().debug();
// }
if (request == 0x8927){
return 0;
}
// add ioctl end
throw new AbstractMethodError(getClass().getName() + ": request=0x" + Long.toHexString(request) + ", argp=0x" + Long.toHexString(argp));
}- 根据龙哥的方式也可以:一个最简单的处理办法就是来到报错点
com.github.unidbg.file.AbstractFileIO.ioctl(AbstractFileIO.java:64),将抛出异常改为直接返回 -1,即函数调用失败之意;摆烂可耻但有用;
@Override
public int ioctl(Emulator<?> emulator, long request, long argp) {
if (log.isTraceEnabled()) {
emulator.attach().debug();
}
return -1;
}