使用unidbg执行安卓so层中的方法
字数 841 2025-08-22 12:23:30

Unidbg安卓SO层方法执行教学文档

一、Unidbg简介

Unidbg是一个基于Java的开源项目,主要用于模拟安卓或iOS设备环境,能够直接执行SO文件中的算法而无需逆向分析内部实现。主要功能包括:

  • 模拟执行ARM/MIPS/x86架构代码
  • 内存访问与操作
  • 指令级跟踪
  • 函数调用跟踪
  • JNI环境模拟

支持多种后端:

  • Unicorn(功能全面但速度较慢)
  • Dynarmic(速度快但不支持所有特性)
  • QEMU(稳定性高)

二、环境配置

1. 开发环境要求

  • JDK 8+
  • IntelliJ IDEA
  • Maven构建工具

2. 项目导入步骤

  1. 克隆或下载unidbg项目
  2. 使用IDEA打开项目
  3. 等待Maven依赖自动下载完成
  4. 运行com包下的测试用例验证环境

三、核心API详解

1. 模拟器创建与初始化

// 创建32位模拟器实例
emulator = AndroidEmulatorBuilder
    .for32Bit()  // 指定32位CPU
    .addBackendFactory(new DynarmicFactory(true))  // 使用Dynarmic后端
    .setProcessName("com.example.target")  // 设置进程名
    .build();

// 内存操作接口
Memory memory = emulator.getMemory();

// 设置Android SDK版本(23对应Android 6.0)
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);

// 创建Dalvik虚拟机
vm = emulator.createDalvikVM(new File("target.apk"));

// 加载SO文件
DalvikModule dm = vm.loadLibrary(new File("libtarget.so"), false);

// 获取模块句柄
module = dm.getModule();

// 调用JNI_OnLoad
dm.callJNI_OnLoad(emulator);

2. 常用模拟器操作

// 获取内存接口
Memory memory = emulator.getMemory();

// 获取进程ID
int pid = emulator.getPid();

// 寄存器操作
emulator.showRegs();  // 显示寄存器状态
RegisterContext context = emulator.getContext();

// 跟踪调试
emulator.traceRead(1, 0);  // 跟踪内存读取
emulator.traceWrite(1, 0); // 跟踪内存写入
emulator.traceCode(1, 0);  // 跟踪代码执行

// 获取后端CPU
Backend backend = emulator.getBackend();

四、SO函数调用方法

1. 符号调用(推荐简单场景)

public void callBySymbol() {
    // 创建代理对象(包名必须与目标一致)
    DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
    
    // 调用JNI方法
    // 格式: methodName(参数类型签名)返回值类型签名
    DvmObject<?> result = obj.callJniMethodObject(
        emulator, 
        "md5(Ljava/lang/String;)Ljava/lang/String;", 
        "inputData");
    
    // 获取结果
    String value = (String) result.getValue();
    System.out.println("Result: " + value);
}

2. 地址调用(复杂但灵活)

private void callByAddress() {
    // 获取JNI环境指针
    Pointer jniEnv = vm.getJNIEnv();
    
    // 创建参数列表
    List<Object> args = new ArrayList<>();
    args.add(jniEnv);  // 第一个参数总是JNIEnv*
    args.add(vm.addLocalObject(ProxyDvmObject.createObject(vm, this))); // this
    
    // 添加字符串参数
    StringObject data = new StringObject(vm, "inputData");
    args.add(vm.addLocalObject(data));
    
    // 调用函数(地址偏移需要+1)
    Number result = module.callFunction(emulator, 0x849 + 1, args.toArray());
    
    // 处理返回值
    DvmObject<?> object = vm.getObject(result.intValue());
    String value = (String) object.getValue();
    System.out.println("Result: " + value);
}

五、调试技巧

1. 控制台调试

// 附加调试器
Debugger debugger = emulator.attach(DebuggerType.CONSOLE);

// 设置断点
debugger.addBreakPoint(module.base + 0xC65);

// 常用调试命令:
// c - 继续执行
// s - 单步步入
// n - 单步步过
// reg - 查看寄存器
// mem - 查看内存
// bt - 查看调用栈

2. 日志输出配置

// 启用详细JNI调用日志
vm.setVerbose(true);

// 重定向输出到文件
PrintStream out = new PrintStream(new FileOutputStream("unidbg.log"));
emulator.getBackend().setStdout(out);

六、实战案例:PlzDebugMe分析

1. Java层分析

目标APK关键逻辑:

public void onClick(View view) {
    String input = text.getText().toString();
    String key = g4tk4y();  // 来自SO层
    String encrypted = new FishEnc(key).doEnc(input);  // Blowfish加密
    boolean result = check(encrypted);  // SO层验证
    // 显示结果...
}

2. SO层函数调用

g4tk4y函数实现

JNIEXPORT jstring JNICALL Java_work_pangbai_debugme_MainActivity_g4tk4y
    (JNIEnv *env, jobject obj) {
    // 初始化检查
    if (check()) exit(0);
    
    // 魔改Base64编码
    char table[] = "4KBbSzwWClkZ2gsr1qA+Qu0FtxOm6/iVcJHPY9GNp7EaRoDf8UvIjnL5MydTX3eh";
    char input[] = "7h4K4y";
    char output[8];
    
    // 特殊编码过程
    output[0] = table[input[0] & 0x3F];
    output[1] = table[((input[0] >> 6) & 0x3) | (4 * (input[1] & 0xF))];
    // ...其他位处理
    
    return (*env)->NewStringUTF(env, output);
}

check函数实现

JNIEXPORT jboolean JNICALL Java_work_pangbai_debugme_MainActivity_check
    (JNIEnv *env, jobject obj, jstring input) {
    
    // 获取输入字符串
    const char *str = (*env)->GetStringUTFChars(env, input, 0);
    
    // 加密算法核心
    for (int i = 0; i < len; i++) {
        int tmp = input[i+1] ^ input[i];
        if (v6) {
            tmp = (tmp << (31-i)) | (tmp >> (i+1));
        } else {
            tmp = (tmp >> (31-i)) | (tmp << (i+1));
        }
        input[i+1] = tmp ^ input[i];
    }
    
    // 与硬编码值比较
    return memcmp(result, expected, len) == 0;
}

3. Unidbg解决方案

获取g4tk4y返回值

public void getKey() {
    DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
    DvmObject<?> result = obj.callJniMethodObject(
        emulator, 
        "g4tk4y()Ljava/lang/String;");
    String key = (String) result.getValue();
    System.out.println("Key: " + key);
}

逆向加密算法

def decrypt(encrypted):
    # 逆向check函数中的加密算法
    for i in range(len(enc)-1, -1, -1):
        tmp = enc[(i+1)%12] ^ enc[i]
        if v6:  # 根据实际情况确定
            tmp = ((tmp << (i+1)) | (tmp >> (32-(i+1)))) & 0xFFFFFFFF
        else:
            tmp = ((tmp >> (i+1)) | (tmp << (32-(i+1)))) & 0xFFFFFFFF
        enc[(i+1)%12] = tmp ^ enc[i]
    
    # 转换为字符串
    return ''.join(pack('<I', x).decode() for x in enc)

七、最佳实践建议

  1. 环境配置

    • 优先使用Dynarmic后端提升速度
    • 设置正确的SDK版本匹配目标环境
    • 保持包名与目标一致以避免JNI问题
  2. 函数调用

    • 简单调用使用符号调用方式
    • 复杂场景使用地址调用
    • 注意ARM模式下地址需要+1
  3. 调试技巧

    • 关键函数设置断点
    • 结合IDA等工具交叉分析
    • 记录完整执行日志
  4. 性能优化

    • 避免不必要的内存操作
    • 复用模拟器实例
    • 适当减少日志输出
  5. 常见问题解决

    • JNIEnv错误检查JNI调用约定
    • 内存访问错误验证指针有效性
    • 无输出检查SO加载是否正确

通过本教学文档,您应该能够掌握使用Unidbg执行安卓SO层方法的核心技术,包括环境搭建、API使用、函数调用和逆向分析等关键技能。

Unidbg安卓SO层方法执行教学文档 一、Unidbg简介 Unidbg是一个基于Java的开源项目,主要用于模拟安卓或iOS设备环境,能够直接执行SO文件中的算法而无需逆向分析内部实现。主要功能包括: 模拟执行ARM/MIPS/x86架构代码 内存访问与操作 指令级跟踪 函数调用跟踪 JNI环境模拟 支持多种后端: Unicorn(功能全面但速度较慢) Dynarmic(速度快但不支持所有特性) QEMU(稳定性高) 二、环境配置 1. 开发环境要求 JDK 8+ IntelliJ IDEA Maven构建工具 2. 项目导入步骤 克隆或下载unidbg项目 使用IDEA打开项目 等待Maven依赖自动下载完成 运行 com 包下的测试用例验证环境 三、核心API详解 1. 模拟器创建与初始化 2. 常用模拟器操作 四、SO函数调用方法 1. 符号调用(推荐简单场景) 2. 地址调用(复杂但灵活) 五、调试技巧 1. 控制台调试 2. 日志输出配置 六、实战案例:PlzDebugMe分析 1. Java层分析 目标APK关键逻辑: 2. SO层函数调用 g4tk4y函数实现 check函数实现 3. Unidbg解决方案 获取g4tk4y返回值 逆向加密算法 七、最佳实践建议 环境配置 优先使用Dynarmic后端提升速度 设置正确的SDK版本匹配目标环境 保持包名与目标一致以避免JNI问题 函数调用 简单调用使用符号调用方式 复杂场景使用地址调用 注意ARM模式下地址需要+1 调试技巧 关键函数设置断点 结合IDA等工具交叉分析 记录完整执行日志 性能优化 避免不必要的内存操作 复用模拟器实例 适当减少日志输出 常见问题解决 JNIEnv错误检查JNI调用约定 内存访问错误验证指针有效性 无输出检查SO加载是否正确 通过本教学文档,您应该能够掌握使用Unidbg执行安卓SO层方法的核心技术,包括环境搭建、API使用、函数调用和逆向分析等关键技能。