
1.教程 Demo
2.IDEA
3.IDA
文件访问 (File Access) 补环境是 Unidbg 应用中仅次于 JNI 补环境的重要环节。当 Unidbg 模拟执行的原生库(. So 文件)尝试通过文件 I/O 操作访问文件系统时——例如读取设备信息 (/proc/cpuinfo)、检测运行环境 (/proc/self/maps、/xbin/su) 或校验自身完整性 (APK 文件)——Unidbg 必须能够对这些访问请求作出响应。在一个纯净的 Unidbg 环境中,这些被访问的目标文件通常是不存在的。如果一个关键的文件访问失败或返回了不符合预期的内容,原生库可能会改变其执行逻辑、触发风控机制或直接中断执行。因此,文件访问补环境的目的就是通过 Unidbg 的 IOResolver 责任链机制拦截这些文件系统调用,并提供一个内容、权限都符合原生库逻辑预期的虚拟文件或目录。
Unidbg 并未将所有文件处理逻辑写死,而是采用了一种灵活的责任链模式。当代码尝试访问一个文件时,请求会依次通过一个处理器链,直到被成功处理或最终失败。
这个处理链的顺序通常是:
在 resolve 方法中,你可以返回一个 FileResult 对象,它有三种状态:
4. FileResult.success(FileIO): 表示文件访问成功,返回一个 FileIO 对象。这是最常用的方式。
5. FileResult.failed(errno): 表示文件访问失败,并返回一个指定的错误码,如 UnixEmulator.EACCES (权限不足)。
6. FileResult.fallback(): 表示回退,只有当其他所有处理器都无法处理时,才会由它来处理。
处理文件访问需要结合三方面的知识:Linux/Android 文件系统知识、对业务(风控/检测)的经验以及 Unidbg 的实现方法。
当需要返回一个真实存在的文件时,SimpleFileIO 是最佳选择。你需要将文件从真机上导出,并放置在你的项目路径下。
案例:补 boot_id 和 APK 文件
boot_id 常用于生成设备指纹,而 APK 文件访问常用于签名校验或资源读取,两者都必须处理。
boot_id 文件在开机时生成,在设备关机前不会改变内容。我们将这个文件从真机上 push 出来
当文件内容需要动态生成或随机化时(例如躲避基于设备指纹的风控),ByteArrayFileIO 非常有用。它直接接收一个字节数组作为文件内容。
案例:随机化 boot_id 和补 CPU 频率
注意:ByteArrayFileIO 不支持写入操作,如果样本需要写入文件,使用它会抛出 UnsupportedOperationException。
多数情况下,对于环境检测类的文件(如 su 文件),我们什么都不做,让它自然失败即可。但在某些特殊场景,需要手动返回失败。
案例:模拟无 Root 权限
有些 Root 检测会尝试访问 /data 目录的权限。由于 Unidbg 的虚拟文件系统会自动创建 /data 目录导致访问成功,我们需要手动拦截并返回“权限不足”。
/proc 目录下的文件访问频率极高,且容易出错,需要特殊对待。
maps 文件记录了进程的内存映射,是所有文件访问中处理起来最复杂也最重要的一项。原生库通过读取它来检查环境中是否存在 Frida、Xposed 等 Hook 框架的特征模块,或者获取自身 APK 文件的路径以进行签名校验。
处理 maps 的三种核心策略:
面对 maps 的复杂性,理论上的最佳方案是完全逆向分析其所有检测点,然后构造一份完美的 maps 文件。但在实践中,我们可以采用以下三种更高效的策略,并根据情况灵活选择。
}
```
首先,一个应用程序(例如一个 App 的 .so 文件)通常不会自己从零开始实现所有功能。它会依赖大量由操作系统或第三方提供的共享库(Shared Libraries),在 Android/Linux 中,这些库通常是 .so 文件(Shared Object)。
库函数就是这些共享库中提供的、可供应用程序调用的、预先编译好的函数。
当你的 so 文件调用 fopen 时,它实际上是在请求操作系统加载 libc.so,并执行其中的 fopen 函数代码。
案例 1:补 getProperty
__system_property_get 函数详解
在 Android 系统中,App 获取设备信息(如型号、品牌、系统版本等)最常用、最底层的手段之一就是调用 libc.so 库中的 __system_property_get 函数。理解并控制这个函数,是 Unidbg 环境模拟的重中之重。
1. 它是做什么的?
__system_property_get 是一个 C 函数,原型如下:
小结:
深入讲解了 Unidbg 实现此功能的核心机制——基于责任链模式的 IOResolver,并介绍了如何通过 SimpleFileIO(处理真实文件)、ByteArrayFileIO(动态生成内容)等 FileIO 对象来制定精细化的文件处理策略。课程重点剖析了 /proc/self/maps、/proc/self/status 等高频检测文件的处理技巧与策略,强调了按需构造、精准伪造的核心思想。
解释了原生库对 libc.so 等系统共享库函数的依赖,并指出直接 Hook 这些底层函数是伪造设备信息、绕过风控检测的关键。课程以 Android 系统中最常见的属性获取函数 __system_property_get 为例,详细演示了如何使用 Unidbg 自带的 SystemPropertyHook 或更底层的 Dobby 框架来拦截函数调用,从而实现对运行环境的深度伪造。
完整代码:
百度云
阿里云
哔哩哔哩
教程开源地址
PS: 解压密码都是 52 pj,阿里云由于不能分享压缩包,所以下载 exe 文件,双击自解压
public class BiliIOResolver implements IOResolver<AndroidFileIO> {
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
System.out.println("File open request: " + pathname);
return null;
}
}
public class Bili extends AbstractJni {
public Bili() {
emulator.getSyscallHandler().addIOResolver(new BiliIOResolver());
}
}
public class BiliIOResolver implements IOResolver<AndroidFileIO> {
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
System.out.println("File open request: " + pathname);
return null;
}
}
public class Bili extends AbstractJni {
public Bili() {
emulator.getSyscallHandler().addIOResolver(new BiliIOResolver());
}
}
C:\Users\zhengji>adb shell
* daemon not running; starting now at tcp:5037
* daemon started successfully
vermeer:/ $ cat /proc/sys/kernel/random/boot_id
c7de54b2-f238-481d-b8e1-41c05413b2cd
vermeer:/ $ cp /proc/sys/kernel/random/boot_id /sdcard
vermeer:/ $ exit
C:\Users\zhengji>adb pull /sdcard/boot_id D:\unidbg-master\unidbg-android\src\test\resources
/sdcard/boot_id: 1 file pulled, 0 skipped. 0.0 MB/s (37 bytes in 0.012s)
C:\Users\zhengji>adb shell
* daemon not running; starting now at tcp:5037
* daemon started successfully
vermeer:/ $ cat /proc/sys/kernel/random/boot_id
c7de54b2-f238-481d-b8e1-41c05413b2cd
vermeer:/ $ cp /proc/sys/kernel/random/boot_id /sdcard
vermeer:/ $ exit
C:\Users\zhengji>adb pull /sdcard/boot_id D:\unidbg-master\unidbg-android\src\test\resources
/sdcard/boot_id: 1 file pulled, 0 skipped. 0.0 MB/s (37 bytes in 0.012s)
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
switch (pathname) {
case "/proc/sys/kernel/random/boot_id":{
return FileResult.<AndroidFileIO>success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/cpu/boot_id"), pathname));
}
}
return null;
}
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
switch (pathname) {
case "/proc/sys/kernel/random/boot_id":{
return FileResult.<AndroidFileIO>success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/cpu/boot_id"), pathname));
}
}
return null;
}
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
switch (pathname) {
case "/proc/sys/kernel/random/boot_id": {
String randomBootId = UUID.randomUUID().toString();
return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, randomBootId.getBytes(StandardCharsets.UTF_8)));
}
case "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq": {
return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, "1766400".getBytes()));
}
}
return null;
}
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
switch (pathname) {
case "/proc/sys/kernel/random/boot_id": {
String randomBootId = UUID.randomUUID().toString();
return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, randomBootId.getBytes(StandardCharsets.UTF_8)));
}
case "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq": {
return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, "1766400".getBytes()));
}
}
return null;
}
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
if ("/data".equals(pathname)) {
return FileResult.failed(UnixEmulator.EACCES);
}
return null;
}
@Override
public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
if ("/data".equals(pathname)) {
return FileResult.failed(UnixEmulator.EACCES);
}
return null;
}
| 文件路径 (Path) |
主要用途 (Purpose) |
推荐处理策略 (Recommended Strategy) |
注意事项 (Key Points) |
/proc/self/cmdline |
获取当前进程名 |
必须补。使用 ByteArrayFileIO 动态生成。 |
路径中的 pid 需用 emulator.getPid() 动态获取;内容末尾必须包含 \0。 |
/proc/self/status |
获取进程状态,检测 TracerPid |
必须补。使用 ByteArrayFileIO 动态生成。 |
TracerPid 必须为 0;Pid 和 Tgid 字段应动态替换为 emulator.getPid()。 |
/proc/self/maps |
获取内存映射,检测 frida 等 |
策略性处理: 1. 优先:用真实 maps 文件 (SimpleFileIO)。 2. 备选:若发生内存错误,则不处理,退回使用 Unidbg 默认的 fakeMaps。 |
真实 maps 可能导致内存地址访问异常;fakeMaps 缺少 APK等映射信息。 |
/data/app/.../base.apk |
签名校验、资源读取 |
必须补。使用 SimpleFileIO 提供真实的 APK 文件。 |
路径在不同设备上可能不同,注意日志中的实际访问路径。 |
/proc/sys/kernel/random/boot_id |
生成设备指纹 |
建议补。用 SimpleFileIO (固定) 或 ByteArrayFileIO (随机化)。 |
有些风控会记录 boot_id,随机化可避免关联。 |
/sys/.../cpuinfo_max_freq |
读取设备硬件信息 |
建议补。使用 ByteArrayFileIO 提供一个常见值。 |
直接返回一个字符串即可,如 "2867200"。 |
/xbin/su, /system/bin/su |
Root 环境检测 |
无需处理或显式失败。让其自然失败 (返回 null) 或通过 stat 返回 ENOENT。 |
不要提供一个假文件,让文件访问失败是最真实的无 Root模拟。 |
/proc/net/tcp, /proc/net/udp |
检测代理、抓包工具 |
强烈建议不补。 |
高版本 Android 已禁止访问。不补能模拟高版本系统行为,且避免暴露主机网络信息。 |
case "/proc/self/cmdline":
return FileResult.success(new ByteArrayFileIO(oflags, pathname, PACKAGE_NAME.getBytes()));
case "/proc/self/cmdline":
return FileResult.success(new ByteArrayFileIO(oflags, pathname, PACKAGE_NAME.getBytes()));
case "/proc/self/status":
String statusContent = "Name:\t" + PACKAGE_NAME + "\n" +
"Umask:\t0077\n" +
"State:\tS (sleeping)\n" +
"Tgid:\t12345\n" +
"Pid:\t12345\n" +
"PPid:\t1\n" +
"TracerPid:\t0\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, statusContent.getBytes()));
case "/proc/self/status":
String statusContent = "Name:\t" + PACKAGE_NAME + "\n" +
"Umask:\t0077\n" +
"State:\tS (sleeping)\n" +
"Tgid:\t12345\n" +
"Pid:\t12345\n" +
"PPid:\t1\n" +
"TracerPid:\t0\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, statusContent.getBytes()));
case "/proc/self/maps": {
final String APK_PATH = "/data/app/com.zj.wuaipojie-1/base.apk";
String maps = "7fbe852000-7fbe853000 r-xp 00000000 00:00 0 " + APK_PATH + "\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, maps.getBytes(StandardCharsets.UTF_8)));
case "/proc/self/maps": {
final String APK_PATH = "/data/app/com.zj.wuaipojie-1/base.apk";
String maps = "7fbe852000-7fbe853000 r-xp 00000000 00:00 0 " + APK_PATH + "\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, maps.getBytes(StandardCharsets.UTF_8)));
int __system_property_get(const char* name, char* value);
int __system_property_get(const char* name, char* value);
SystemPropertyHook systemPropertyHook = new SystemPropertyHook(emulator);
systemPropertyHook.setPropertyProvider(new SystemPropertyProvider() {
@Override
public String getProperty(String key) {
switch (key){
case "ro.board.platform": {
return "kalama";
}
case "ro.product.model": {
return "23113RKC6C";
}
}
return null;
}
});
memory.addHookListener(systemPropertyHook);
private void hookSystemPropertyGet() {
Module libc = emulator.getMemory().findModule("libc.so");
Symbol propertyGetSymbol = libc.findSymbolByName("__system_property_get");
Dobby dobby = Dobby.getInstance(emulator);
dobby.replace(propertyGetSymbol, new ReplaceCallback() {
@Override
public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
UnidbgPointer keyPtr = context.getPointerArg(0);
UnidbgPointer valuePtr = context.getPointerArg(1);
String key = keyPtr.getString(0);
String value = null;
if ("ro.board.platform".equals(key)) {
value = "kalama";
} else if ("ro.product.model".equals(key)) {
value = "23113RKC6C";
}
if (value != null) {
System.out.println("[HOOK] __system_property_get: Intercepted key=" + key + ", Faking value=" + value);
byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
valuePtr.write(0, valueBytes, 0, valueBytes.length);
valuePtr.setByte(valueBytes.length, (byte) 0);
return HookStatus.LR(emulator, valueBytes.length);
}
return HookStatus.RET(emulator, originFunction);
}
});
}
SystemPropertyHook systemPropertyHook = new SystemPropertyHook(emulator);
systemPropertyHook.setPropertyProvider(new SystemPropertyProvider() {
@Override
public String getProperty(String key) {
switch (key){
case "ro.board.platform": {
return "kalama";
}
case "ro.product.model": {
return "23113RKC6C";
}
}
return null;
}
});
memory.addHookListener(systemPropertyHook);
private void hookSystemPropertyGet() {
Module libc = emulator.getMemory().findModule("libc.so");
Symbol propertyGetSymbol = libc.findSymbolByName("__system_property_get");
Dobby dobby = Dobby.getInstance(emulator);
dobby.replace(propertyGetSymbol, new ReplaceCallback() {
@Override
public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
UnidbgPointer keyPtr = context.getPointerArg(0);
UnidbgPointer valuePtr = context.getPointerArg(1);
String key = keyPtr.getString(0);
String value = null;
if ("ro.board.platform".equals(key)) {
value = "kalama";
} else if ("ro.product.model".equals(key)) {
value = "23113RKC6C";
}
if (value != null) {
System.out.println("[HOOK] __system_property_get: Intercepted key=" + key + ", Faking value=" + value);
byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
valuePtr.write(0, valueBytes, 0, valueBytes.length);
valuePtr.setByte(valueBytes.length, (byte) 0);
return HookStatus.LR(emulator, valueBytes.length);
}
return HookStatus.RET(emulator, originFunction);
}
});
}
package com.zj.wuaipojie.util;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;
import com.github.unidbg.hook.hookzz.Dobby;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.SystemPropertyHook;
import com.github.unidbg.linux.android.SystemPropertyProvider;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
public class ChallengeTenTwo implements IOResolver<AndroidFileIO> {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private static final String PACKAGE_NAME = "com.zj.wuaipojie\0";
private static final String EXPECTED_BOOT_ID = "8442043f-98e2-48bb-8707-525e98a66898";
private static final String EXPECTED_BOARD_PLATFORM = "kalama";
private static final String EXPECTED_PHONE_MODEL = "23113RKC6C";
private static final String EXPECTED_SIG = "3082034f30820237a00302010202046c07fed4300d06092a864886f70d01010b05003058310f300d06035504061306303030303030310b300906035504081302626a310b300906035504071302626a310c300a060355040a13036f727a310c300a060355040b13036f727a310f300d06035504030c06e6ada3e5b7b1301e170d3232303832313132323934305a170d3437303831353132323934305a3058310f300d06035504061306303030303030310b300906035504081302626a310b300906035504071302626a310c300a060355040a13036f727a310c300a060355040b13036f727a310f300d06035504030c06e6ada3e5b7b130820122300d06092a864886f70d01010105000382010f003082010a0282010100e6f5d0394247b0a7bb02daedeb78f4d373d394cde087c8edbd9f5fa87781f88ed733f822c10e6573a4f9fa52c46fa7cbc48136f35f0a554fe6ab6c605cab00c1edaa18ab2d9d052968413053fc72c945e9be05dea00d3ebb9a2404d2fd2e5520d784d739de28b9a3611c7ccc51df75e057d054c984931627381d572ff545047ae089d36a99ac5866aa236e971fa322e500335474ae5932142b14464e000605c53ce902a53aaba2bcd576a255db6913c849e144d4cb83ca9043078160025219205ef859f2611c89a168f153dff6020a992a77fe2ab8d37ec3d65e97a4c74577f2f5c6ae690afe26a2f97de7697046ae1ef18c986d437c77a83e22c272485487bd0203010001a321301f301d0603551d0e04160414cd08d93fc2483c6afa2e182a5357acbc6cc21e17300d06092a864886f70d01010b05000382010100a46400714e97485805062c6fe392f61a52f52083b616a170affb043cc3272332132adfe55db2b13abc7bbbcbf2ae83741e767d196deb3b924cd7037ed6afa5360ea10505ed8ba37c33ce7ac5a24aa6192b49c02ad711b99c1eec185aed272671c3dfdc7df5c3c0511b41b581c94ddf1076e397b27a75a0b73e5e207f4ec636067540b85e3e750ce59599ddb1d954e4537275d27227f2db9eb2fa3a2a29ea1c8a5415d6058e798517ebec4194cfb8715e7c17c043c0309c7e9dd7c17c502b3dd27153929ba5462182a0aa58c15972bc75a7ede1849ef5c9341d0ca747c9fb41ccabfa2c04c1147ea884cf951481bca37ffe929552962b35cbffff21675e4d5581";
public ChallengeTenTwo() {
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName(PACKAGE_NAME)
.build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
emulator.getSyscallHandler().addIOResolver(this);
vm.setJni(new MyJni(vm));
DalvikModule dm = vm.loadLibrary(soFile, true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
hookSystemPropertyGet();
}
/**
* Hook __system_property_get 函数,用于伪造系统属性
*/
private void hookSystemPropertyGet() {
Module libc = emulator.getMemory().findModule("libc.so");
if (libc == null) {
throw new IllegalStateException("Failed to find libc.so");
}
long address = libc.findSymbolByName("__system_property_get").getAddress();
Dobby dobby = Dobby.getInstance(emulator);
dobby.replace(address, new ReplaceCallback() {
@Override
public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
UnidbgPointer keyPtr = context.getPointerArg(0);
UnidbgPointer valuePtr = context.getPointerArg(1);
String key = keyPtr.getString(0);
String value = null;
if ("ro.board.platform".equals(key)) {
value = EXPECTED_BOARD_PLATFORM;
System.out.println("[HOOK] __system_property_get: Intercepted key=" + key + ", Faking value=" + value);
} else if ("ro.product.model".equals(key)) {
value = EXPECTED_PHONE_MODEL;
System.out.println("[HOOK] __system_property_get: Intercepted key=" + key + ", Faking value=" + value);
}
if (value != null) {
byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
valuePtr.write(0, valueBytes, 0, valueBytes.length);
valuePtr.setByte(valueBytes.length, (byte) 0);
return HookStatus.LR(emulator, valueBytes.length);
}
return HookStatus.RET(emulator, originFunction);
}
});
}
public void callUnidbgLevel2() {
System.out.println("====== 开始执行 unidbg_level2 函数 ======");
DvmClass securityUtilClass = vm.resolveClass("com/zj/wuaipojie/util/SecurityUtil");
DvmClass ctxClz = vm.resolveClass("android/content/Context");
DvmObject<?> mockCtx = ctxClz.newObject(null);
StringObject result = securityUtilClass.callStaticJniMethodObject(
emulator, "unidbg_level2(Landroid/content/Context;)Ljava/lang/String;",
mockCtx);
System.out.println("JNI 函数返回结果: " + (result != null ? result.getValue() : null));
}
public static void main(String[] args) throws FileNotFoundException {
ChallengeTenTwo challenge = new ChallengeTenTwo();
challenge.callUnidbgLevel2();
}
@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) {
return FileResult.success(new ByteArrayFileIO(oflags, pathname, EXPECTED_BOOT_ID.getBytes()));
return FileResult.success(new ByteArrayFileIO(oflags, pathname, PACKAGE_NAME.getBytes()));
case "/proc/self/status":
String statusContent = "Name:\t" + PACKAGE_NAME + "\n" +
"Umask:\t0077\n" +
"State:\tS (sleeping)\n" +
"Tgid:\t12345\n" +
"Pid:\t12345\n" +
"PPid:\t1\n" +
"TracerPid:\t0\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, statusContent.getBytes()));
case "/proc/self/maps": {
final String APK_PATH = "/data/app/com.zj.wuaipojie-1/base.apk";
String maps = "7fbe852000-7fbe853000 r-xp 00000000 00:00 0 " + APK_PATH + "\n";
return FileResult.success(new ByteArrayFileIO(oflags, pathname, maps.getBytes(StandardCharsets.UTF_8)));
}
}
return null;
}
/** 自定义 JNI,伪造 PackageManager / PackageInfo / Signature */
static class MyJni extends AbstractJni {
private final VM vm;
MyJni(VM vm) { this.vm = vm; }
@Override
public DvmObject<?> callObjectMethodV(BaseVM baseVm, DvmObject<?> obj, String signature, VaList vaList) {
if (signature.equals("android/content/Context->getPackageManager()Landroid/content/pm/PackageManager;")) {
DvmClass pmClz = vm.resolveClass("android/content/pm/PackageManager");
return pmClz.newObject("mockPM");
}
if (signature.equals("android/content/pm/PackageManager->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;")) {
StringObject apkPathObj = vaList.getObjectArg(0);
int flags = vaList.getIntArg(1);
java.util.HashMap<String, Object> fields = new java.util.HashMap<>();
fields.put("packageName", new StringObject(vm, PACKAGE_NAME));
DvmClass sigClz = vm.resolveClass("android/content/pm/Signature");
DvmObject<?> sigObj = sigClz.newObject("mockSignature");
ArrayObject sigArr = new ArrayObject(sigObj);
fields.put("signatures", sigArr);
return pkgInfoClz.newObject(fields);
}
if (signature.equals("android/content/pm/Signature->toCharsString()Ljava/lang/String;")) {
return new StringObject(vm, EXPECTED_SIG);
}
return super.callObjectMethodV(baseVm, obj, signature, vaList);
}
@Override
public DvmObject<?> getObjectField(BaseVM baseVm, DvmObject<?> obj, String signature) {
if (signature.equals("android/content/pm/PackageInfo->packageName:Ljava/lang/String;")) {
Object v = ((java.util.Map<?, ?>) obj.getValue()).get("packageName");
return (DvmObject<?>) v;
}
if (signature.equals("android/content/pm/PackageInfo->signatures:[Landroid/content/pm/Signature;")) {
Object v = ((java.util.Map<?, ?>) obj.getValue()).get("signatures");
return (DvmObject<?>) v;
}
return super.getObjectField(baseVm, obj, signature);
}
}
}
package com.zj.wuaipojie.util;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;
import com.github.unidbg.hook.hookzz.Dobby;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.SystemPropertyHook;
import com.github.unidbg.linux.android.SystemPropertyProvider;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
public class ChallengeTenTwo implements IOResolver<AndroidFileIO> {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private static final String PACKAGE_NAME = "com.zj.wuaipojie\0";
private static final String EXPECTED_BOOT_ID = "8442043f-98e2-48bb-8707-525e98a66898";
private static final String EXPECTED_BOARD_PLATFORM = "kalama";
private static final String EXPECTED_PHONE_MODEL = "23113RKC6C";
private static final String EXPECTED_SIG = "3082034f30820237a00302010202046c07fed4300d06092a864886f70d01010b05003058310f300d06035504061306303030303030310b300906035504081302626a310b300906035504071302626a310c300a060355040a13036f727a310c300a060355040b13036f727a310f300d06035504030c06e6ada3e5b7b1301e170d3232303832313132323934305a170d3437303831353132323934305a3058310f300d06035504061306303030303030310b300906035504081302626a310b300906035504071302626a310c300a060355040a13036f727a310c300a060355040b13036f727a310f300d06035504030c06e6ada3e5b7b130820122300d06092a864886f70d01010105000382010f003082010a0282010100e6f5d0394247b0a7bb02daedeb78f4d373d394cde087c8edbd9f5fa87781f88ed733f822c10e6573a4f9fa52c46fa7cbc48136f35f0a554fe6ab6c605cab00c1edaa18ab2d9d052968413053fc72c945e9be05dea00d3ebb9a2404d2fd2e5520d784d739de28b9a3611c7ccc51df75e057d054c984931627381d572ff545047ae089d36a99ac5866aa236e971fa322e500335474ae5932142b14464e000605c53ce902a53aaba2bcd576a255db6913c849e144d4cb83ca9043078160025219205ef859f2611c89a168f153dff6020a992a77fe2ab8d37ec3d65e97a4c74577f2f5c6ae690afe26a2f97de7697046ae1ef18c986d437c77a83e22c272485487bd0203010001a321301f301d0603551d0e04160414cd08d93fc2483c6afa2e182a5357acbc6cc21e17300d06092a864886f70d01010b05000382010100a46400714e97485805062c6fe392f61a52f52083b616a170affb043cc3272332132adfe55db2b13abc7bbbcbf2ae83741e767d196deb3b924cd7037ed6afa5360ea10505ed8ba37c33ce7ac5a24aa6192b49c02ad711b99c1eec185aed272671c3dfdc7df5c3c0511b41b581c94ddf1076e397b27a75a0b73e5e207f4ec636067540b85e3e750ce59599ddb1d954e4537275d27227f2db9eb2fa3a2a29ea1c8a5415d6058e798517ebec4194cfb8715e7c17c043c0309c7e9dd7c17c502b3dd27153929ba5462182a0aa58c15972bc75a7ede1849ef5c9341d0ca747c9fb41ccabfa2c04c1147ea884cf951481bca37ffe929552962b35cbffff21675e4d5581";
public ChallengeTenTwo() {
emulator = AndroidEmulatorBuilder.for64Bit()
.setProcessName(PACKAGE_NAME)
.build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
emulator.getSyscallHandler().addIOResolver(this);
vm.setJni(new MyJni(vm));
DalvikModule dm = vm.loadLibrary(soFile, true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
hookSystemPropertyGet();
}
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!