认识Unidbg

了解Unicorn

Unicorn是一个基于QEMU的轻量级、多平台、多架构的CPU模拟器框架,它允许用户在不同平台上模拟不同架构的CPU操作。

Unidbg

unidbg 是一款基于 unicorn 和 dynarmic(ARM的动态重编译器) 的逆向工具,可以模拟执行so文件。

安装项目

在github上下载项目unidbg,可以直接clone到本地
下载好之后用IDEA打开,等待加载依赖(科学上网)

项目结构

在这里插入图片描述
  在unidbg-android目录包含了特定于 Android 平台的模拟和分析工具,src下有main和test两个目录,main包含主程序的代码,test包含用于测试 Unidbg 功能的测试代码。
  在unidbg-api目录里面包含了unidbg提供的API接口。

分析实例

unidbg\unidbg-android\src\test\java\com\bytedance\frameworks\core\encrypt

在这里插入图片描述

构造函数里的模拟器介绍

    TTEncrypt(boolean logging) {
        this.logging = logging;

        emulator = AndroidEmulatorBuilder
        	.for32Bit()  //指定模拟器目标平台
        	.setProcessName("com.qidian.dldl.official") //设置进程名
            .addBackendFactory(new Unicorn2Factory(true)) //模拟器的底层执行引擎
            .build(); // 创建模拟器实例
            
        // 获取模拟器的内存操作接口
        final Memory memory = emulator.getMemory(); 
        // 设置系统库解析器
        memory.setLibraryResolver(new AndroidResolver(23)); 
        
        // 创建一个 Dalvik 虚拟机实例。
        vm = emulator.createDalvikVM(); 
        // 设置虚拟机的日志输出模式。
        vm.setVerbose(logging); 
        
        // 调用虚拟机的 loadLibrary 方法来加载指定的本地库
        // 第二个参数 false 表示不自动调用库的 JNI_OnLoad 函数。
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/libttEncrypt.so"), false); 
        // 手动执行JNI_OnLoad函数
        dm.callJNI_OnLoad(emulator); 
        // 获取加载的库对应的模块对象
        module = dm.getModule(); 
		// 解析类TTEncryptUtils
        TTEncryptUtils = vm.resolveClass("com/bytedance/frameworks/core/encrypt/TTEncryptUtils");
    }

destroy()

    void destroy() {
    	// 调用 IOUtils.close 方法来关闭 emulator 实例。
        IOUtils.close(emulator);
        if (logging) {
        	// 在控制台上打印字符串 "destroy"。
            System.out.println("destroy");
        }
    }

ttEncrypt()

这个函数代码比较多,我把关键的部分留下来

    byte[] ttEncrypt() {
        if (logging) {
            ...
        }
        if (logging) {
            emulator.attach(DebuggerType.ANDROID_SERVER_V7); // 附加IDA android_server,可输入c命令取消附加继续运行
        }
        byte[] data = new byte[16];
        ByteArray array = TTEncryptUtils.callStaticJniMethodObject(emulator, "ttEncrypt([BI)[B", new ByteArray(vm, data), data.length); // 执行Jni方法
        return array.getValue();
    }

main()

    public static void main(String[] args) throws Exception {
    	// 创建TTEncrypt 类实例
        TTEncrypt test = new TTEncrypt(true);
		// 调用 TTEncrypt 实例的 ttEncrypt 方法
        byte[] data = test.ttEncrypt();
        // 用于打印或以其他方式检查 data 数组的内容。"ttEncrypt" 是一个标签,用于标识这些数据是来自 ttEncrypt 方法的结果。
        Inspector.inspect(data, "ttEncrypt");
		// 调用 TTEncrypt 实例的 destroy 方法,清理和释放与 emulator 相关的资源。
        test.destroy();
    }

ttEncrypt函数里的callStaticJniMethodObject

在 Unidbg 框架中,callStaticJniMethodObject 是一个用于调用 Android 应用中静态 JNI 方法的方法。

ByteArray array = TTEncryptUtils.callStaticJniMethodObject(emulator, "ttEncrypt([BI)[B", new ByteArray(vm, data), data.length);

这里面TTEncryptUtils是获取的Java类的DvmClass对象。
callStaticJniMethodObject方法用于调用该类的静态JNI方法。
emulator是AndroidEmulator 实例,它提供了执行环境。
ttEncrypt([BI)[B是 JNI 方法的签名。指定了方法的名称(ttEncrypt),参数类型(一个字节数组 byte[],表示为 [B),I表示整型,和返回类型(一个字节数组 byte[])。
new ByteArray(vm, data) 创建了一个新的 ByteArray 实例,它包装了要加密的数据 data。vm 是虚拟机实例,用于创建和管理 ByteArray 对象。
data.length 是传递给 JNI 方法的第二个参数,它可能是加密方法需要的额外信息,例如数据的长度。

callStaticJniMethodObject

通过符号寻找函数的地址,在动态注册函数中以符号为键寻找函数地址,如果找不到,则按静态注册规则拼接符号,然后寻找函数地址函数名和函数签名

### 使用 Unidbg 进行 SDK 逆向工程检测 #### 准备工作 为了使用 Unidbg 对 APK 文件及其关联的 `.so` 库进行逆向分析,需先准备好环境并加载必要的依赖项。通常情况下,APK 可以被当作 ZIP 文件处理来提取其中的内容。 ```bash $ unzip app.apk -d output_directory/ ``` 这一步骤允许访问 APK 中嵌入的所有资源文件以及本地库(`.so`),这些对于后续的动态分析至关重要[^1]。 #### 加载与配置 Unidbg 实例 创建一个 Java 或 Python 的开发环境用于集成 Unidbg 框架,并编写脚本来初始化模拟器实例: ```java // 创建Unidbg实例 Darm arm = new DarmImpl(); Emulator emulator = Emulator.createARM(); // 设置系统路径和架构类型 emulator.setSyscall(new LinuxARM SyscallHandler(emulator)); RegisterContext context = emulator.getContext(); context.setArmMode(true); // 注册内存管理模块 Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); // API Level设置为23 ``` 通过上述代码片段可以构建起基础运行平台,在此基础上可进一步挂接目标应用所需的共享对象(`.so`)。 #### 动态链接 .so 文件 将之前从 APK 解包出来的 `libsignature.so` 添加到仿真环境中以便于调用其导出函数: ```java // 将 so 文件映射至虚拟地址空间内 File file = new File("/path/to/libsignature.so"); byte[] bytes = Files.readAllBytes(file.toPath()); long baseAddr = MemoryBlock.allocate(memory, "libsignature", bytes.length); memory.write(baseAddr, bytes, true); // 执行 dlopen 来完成实际装载过程 Pointer handle = Native.loadLibrary("libsignature", null).getHandle(); if (handle == Pointer.NULL){ throw new RuntimeException("Failed to load library."); } ``` 此时已经成功地让 Unidbg 虚拟机认识到了外部引入的目标二进制组件,接下来就可以着手准备具体的功能测试了。 #### Hook 关键方法实现监控 针对特定场景下的需求,比如想要拦截某些敏感操作或是观察内部状态变化,则可以通过 hook 技术达到目的。这里给出一段基于 frida-gadget 的 JavaScript 示例说明如何挂钩 open() 系统调用来捕捉所有尝试打开的文件名: ```javascript var pth = Module.findExportByName(null,"open"); Interceptor.attach(ptr(pth),{ onEnter:function(args){ let filename = args[0].readUtf8String(); console.log("[OPEN]",filename); if(filename.endsWith('.so')){ // 自定义逻辑... } }, onLeave:function(retval){} }); ``` 这段脚本能够帮助识别哪些动态库正在被加载,从而为进一步深入研究提供线索[^2]。 #### 结合调试工具辅助分析 当遇到复杂情况难以仅靠自动化手段解决时,还可以借助专业的反汇编软件如 OllyDbg 辅助查看机器指令级细节,理解程序执行流程,定位潜在漏洞位置[^3]。 综上所述,利用 Unidbg 平台配合其他技术手段可以使开发者更高效地开展移动安全领域内的探索活动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值