百趣云 百趣云的博客

unidbg 实战:脱离真机模拟执行阿里 SecurityGuard SO 签名

为什么需要 unidbg

上一篇讲了 Frida RPC 方案:在真机上注入优酷 App,借 App 自己的代码算 x-sign。这个方案能用,但有几个硬伤:

  • 依赖常驻设备,手机一挂服务就挂
  • 单进程串行,并发上不去
  • App 不能升级、不能清数据,维护成本高

unidbg 提供了另一条路:在 JVM 里模拟 ARM 指令集,直接加载并执行 App 的 SO 库。不需要手机,不需要 App 运行,一台服务器可以起几百个实例并发跑。

unidbg 原理简述

unidbg 是一个基于 unicorn(CPU 指令模拟器)的 Java 库,它做了三层模拟:

  1. 指令层:unicorn 模拟 ARM/ARM64 指令执行
  2. 系统层:模拟 Linux 系统调用(文件、内存、信号等)
  3. 运行时层:模拟 Android 的 JNI 环境、Dalvik VM、动态链接器

对逆向来说这意味着:把 App 的 .so 文件拖出来,unidbg 就能像 Android 系统一样加载它,并执行里面的 JNI 函数。

准备工作

先从手机里把 SecurityGuard 相关的库 pull 出来(以 6.6.241104 版本为例):

libsgmainso-6.6.241104.so          # 主库,签名入口
libsgmiddletierso-6.6.241104.so    # 中间层
libsgsecuritybodyso-6.6.241104.so  # 安全体
libsgmiscso-6.5.9.so               # 杂项
libsgnocaptchaso-6.5.8.so   # 验证码组件

除了 SO,还要把 App 数据目录下的 app_SGLib 整个目录 pull 下来——里面是 SecurityGuard 的运行时数据(设备指纹、密钥材料等)。没有它,签名要么算不出来,要么算出来也是无效的。

代码实现

核心代码骨架:

public class SgSign {

    private final AndroidEmulator emulator;
    private final VM vm;

    public SgSign() {
        // 1. 创建 32 位 ARM 模拟器
        emulator = AndroidEmulatorBuilder.for32Bit()
                .setProcessName("com.youku.phone")
                .build();

        // 2. 注册 Android 系统库解析器(API 23)
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));

        // 3. 创建 Dalvik VM
        vm = emulator.createDalvikVM(new File("youku.apk"));
        vm.setJni(this);     // 缺失的 JNI 函数由本类补环境
        vm.setVerbose(true);

        // 4. 加载 sgmain 主库并执行 JNI_OnLoad
        DalvikModule dm = vm.loadLibrary(
                new File("sglib/libsgmainso-6.6.241104.so"), true);
        dm.callJNI_OnLoad(emulator);
    }
}

加载成功后,就可以通过 vm.resolveClass() 拿到 Java 层的 SecurityGuard 入口类,调用它的签名方法,SO 内部会回调 Native 层完成计算:

DvmClass sgMain = vm.resolveClass(
        "com/alibaba/wireless/security/mainplugin/SecurityGuardMainPlugin");

补环境:unidbg 最大的工作量

SO 直接跑起来几乎一定会报各种缺失,因为 SecurityGuard 依赖大量 Android 环境:

  • JNI 函数缺失RegisterNatives 动态注册的函数,按日志逐个补
  • 系统属性ro.product.modelro.build.version.sdk 等,通过 Hook 系统属性接口喂给它
  • 文件读写:把 pull 下来的 app_SGLib 目录映射到模拟器文件系统的对应路径
  • 包名/签名校验:安全库会校验宿主包名和签名,补环境时要返回真实值

补环境的套路就是:跑 → 看日志报错 → 补 → 再跑,循环直到签名稳定输出。这个过程比较磨人,但只需要做一次。

效果对比

  • Frida RPC:依赖真机/模拟器常驻,并发低(单进程),维护成本高
  • unidbg:无设备依赖,多实例并发高,维护成本中

实际测试,unidbg 单次签名耗时在毫秒级,一台 4 核服务器轻松支撑每秒上百次签名,完全满足业务需求。

总结

unidbg 的本质是"用 CPU 模拟换部署自由":前期补环境很折磨,但一旦跑通,就彻底摆脱了对真机的依赖,签名服务可以像普通后端服务一样部署、扩容。

对于带 VMP 保护的阿里系 SO,这是目前最主流、最稳定的方案。

By 百趣云 阅读量:5 On