unidbg 加载 so 并调用 so 中函数

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

unidbg 简介

unidbg 是一个基于 Java 的 Android 动态分析框架,专注于模拟 Android 应用中的 native 代码。以下是它的主要功能:

  • 支持对so的加载;

  • 支持对JNI接口函数的模拟调用;

  • 支持常见syscalls的模拟调用;

  • 支持ARM32和ARM64。

  • 支持Android以及iOS

  • 基于HookZz实现的inline hook

  • xHook实现的hook

  • iOS fishhook,substrate、whale hook等

  • 支持gdb、IDA远程调试等高级功能。

Unidgb 项目地址:https://github.com/zhkl0228/unidbg

unidbg 底层引擎

unidbg 的 CPU 指令模拟主要由 dynarmic 和 unicorn 驱动。

dynarmic 是专注于 ARM 架构的模拟器,虽然它和 unicorn 都提供 ARM 模拟功能,但它们的设计目标和性能特点不同。unicorn 更加通用,而 dynarmic 则更注重效率。

dynarmic 与 unicorn 效率对比

clone unidbg 源码到本地并导入 IDEA,在 unidbg-android/src/test/java/com/kanxue/test2

word/media/image1.png

MainActivity.java 中示例代码实现了爆破 so 中 test 函数 flag 参数(已知 so 中 test 函数参数为一个字符串,该字符串长度为3且仅包含大小写字母),如果 flag 正确函数会返回 true,通过不断调用该 jni 函数测试所有字母组合直到得到正确结果。

package com.kanxue.test2;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;

import java.io.File;

/**
 * <a href="https://bbs.pediy.com/thread-263345.htm">CrackMe</a>
 */
public class MainActivity {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        MainActivity mainActivity = new MainActivity();
        System.out.println("load offset=" + (System.currentTimeMillis() - start) + "ms");
        mainActivity.crack();
    }

    private final AndroidEmulator emulator;
    private final VM vm;

    private MainActivity() {
        emulator = AndroidEmulatorBuilder
                .for32Bit()
                .addBackendFactory(new DynarmicFactory(true))
                .build();
        Memory memory = emulator.getMemory();
        LibraryResolver resolver = new AndroidResolver(23);
        memory.setLibraryResolver(resolver);

        vm = emulator.createDalvikVM();
        vm.setVerbose(false);
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so"), false);
        dm.callJNI_OnLoad(emulator);
    }

    private static final char[] LETTERS = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    };

    private void crack() {
        DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
    
### 使用 Unidbg 加载调用 Android SO 动态库 #### 准备工作 为了使用 Unidbg 调试加载 Android 的 `.so` 文件,需先设置好开发环境。确保已安装 Java 开发工具包 (JDK),Unidbg 库加入项目依赖。 #### 创建 Unidbg 实例 创建一个新的 `Unidbg` 对象实例用于模拟 ARM 架构下的执行环境: ```java // 初始化 Unidbg 实例 Unidbg unidbg = new Unidbg(ArchEnum.ARM); ``` #### 设置文件系统路径映射 为了让被调试的应用程序能够找到所需的共享对象(`.so`),需要配置虚拟文件系统的根目录以及应用程序私有数据存储位置: ```java // 配置 so 文件所在的真实路径到 /data/app-lib/ 下的映射关系 unidbg.setAppPath("/path/to/apk"); unidbg.addFileSystem("/path/to/lib"); // 存放 .so 文件的实际路径 ``` #### 注册必要的模块和服务 某些情况下可能还需要注册特定的服务接口或实现类以便于更好地支持目标应用的功能需求: ```java // 如果需要的话可以在这里添加自定义服务或其他初始化操作 unidbg.getEmulator().addService(new MyCustomService()); ``` #### 加载指定的动态链接库 通过 `loadLibrary()` 方法传入要分析的目标 `.so` 名字来进行加载动作: ```java Dlfcn dlopen = unidbg.loadLibrary("libexample.so", Dlfcn.class); // libexample.so 是待测试的具体 so 文件名 ``` #### 定义回调函数处理 API 请求 当遇到未实现的标准 C/C++ 运行时函数或者其他外部符号引用时,可以通过重写相应的方法来自定义行为逻辑: ```java public class HookedFunction implements Function { @Override public ReturnAction hook(Emulator<?> emulator, long originFunction) { Memory memory = emulator.getMemory(); // 获取参数列表 Stack stack = ((ARM32RegisterContext)emulator.getContext()).getStack(); Pointer ptrArg0 = stack.popPointer(); Log.d("HookedFunction", "Called with arg0=" + ptrArg0.getStringUtf8()); // 返回期望的结果给原生层 return new ReturnAction(emulator).returnNow(0L /* 成功 */); } } ``` #### 执行具体功能 最后一步就是触发想要研究的那个导出函数入口点了。这通常涉及到构造合适的输入参数通过指针传递进去,之后读取返回值完成整个交互过程: ```java long addressOfTargetFunc = dlopen.dlsym(dlopen.dlopen("libexample.so"), "target_function_name"); if(addressOfTargetFunc != 0){ RegisterContext context = unidbg.getEmulator().getContext(); // 填充 R0 寄存器作为第一个参数... context.setR0(/* 参数1 */); // ...其他寄存器也可能用来携带更多参数... // 让 CPU 开始运行直到该地址处指令被执行完毕 unidbg.runUntilAddress(addressOfTargetFunc); } else { throw new RuntimeException("Failed to find symbol 'target_function_name'"); } // 取得结果 long resultValue = context.getR0(); // 或者根据实际情况调整获取方式 Log.i("Result:", Long.toString(resultValue)); ``` 以上即为利用 Unidbg 工具集加载调用 Android 平台上的本地二进制组件的一个基本流程概述[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值