一文搞懂 SO 脱壳全流程:识别加壳、Frida Dump、原理深入解析
字数 1327 2025-09-04 23:22:12

SO 脱壳全流程详解:识别加壳、Frida Dump 与原理解析

一、SO 加壳识别方法

1.1 IDA 识别特征

  • ELF 结构异常:IDA 打开 so 文件时提示无法正确识别 ELF 文件结构
  • Section 定义无效:section 定义不符合预期格式
  • 红色代码块:存在大量红色汇编代码块,表示错误或未能正常解析的地址/数据
  • 其他特征:代码被"混淆"、"裁剪"或"加壳"的迹象

二、Frida Dump 脱壳实战

2.1 准备工作

  1. 克隆 frida_dump 工具到本地
  2. 修改远程连接配置(如需):
    # 原配置
    device: frida.core.Device = frida.get_usb_device()
    
    # 远程连接配置
    device = frida.get_device_manager().add_remote_device("127.0.0.1:1234")
    

2.2 脱壳执行流程

  1. 执行脱壳命令:
    python dump_so.py libGameVMP.so
    
  2. 输出示例:
    {'name': 'libGameVMP.so', 'base': '0x7bd7b81000', 'size': 462848, 'path': '/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libGameVMP.so'}
    libGameVMP.so.dump.so
    

2.3 SoFixer 修复流程

  1. 推送 SoFixer 到设备:
    adb push android/SoFixer64 /data/local/tmp/SoFixer
    
  2. 执行修复:
    adb shell /data/local/tmp/SoFixer -m 0x7bd7b81000 -s /data/local/tmp/libGameVMP.so.dump.so -o /data/local/tmp/libGameVMP.so.dump.so.fix.so
    
  3. 修复过程关键日志:
    [main_loop:87]start to rebuild elf file
    [Load:69]dynamic segment have been found in loadable segment
    [RebuildPhdr:37]RebuildPhdr End
    [ReadSoInfo:696]soname
    [ReadSoInfo:621] constructors (DT_INIT) found at 1bd68
    [ReadSoInfo:629] constructors (DT_INIT_ARRAY) found at 6e9e8
    [ReadSoInfo:703]ReadSoInfo End
    [RebuildShdr:536]RebuildShdr End
    [RebuildRelocs:809]RebuildRelocs End
    [RebuildFin:733]End
    [main:123]Done!!!
    

三、SO 查找与信息获取

3.1 查找单个 SO

rpc.exports.findmodule("libGameVMP.so")
// 返回示例
{
  "base": "0x7b6ae0e000",
  "name": "libGameVMP.so",
  "path": "/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libGameVMP.so",
  "size": 462848
}

3.2 获取所有 SO 信息

rpc.exports.allmodule()
// 返回示例
[
  {
    "base": "0x6545887000",
    "name": "app_process64",
    "path": "/system/bin/app_process64",
    "size": 40960
  },
  {
    "base": "0x7c69419000",
    "name": "linker64",
    "path": "/system/bin/linker64",
    "size": 225280
  }
]

四、Frida Dump 脱壳原理详解

4.1 整体流程

  1. 使用 Frida 连接目标 Android 进程
  2. 加载 dump_so.js 脚本
  3. 获取目标 .so 文件的基地址和大小
  4. 从内存中转储目标 .so 文件
  5. 使用 SoFixer 修复转储的内存数据
  6. 下载修复后的 .so 文件到本地

4.2 关键代码解析

4.2.1 Python 端代码

def read_frida_js_source():
    with open("dump_so.js", "r") as f:
        return f.read()

def on_message(message, data):
    pass

if __name__ == "__main__":
    device = frida.get_device_manager().add_remote_device("127.0.0.1:1234")
    pid = device.get_frontmost_application().pid
    session: frida.core.Session = device.attach(pid)
    script = session.create_script(read_frida_js_source())
    script.on('message', on_message)
    script.load()

4.2.2 JavaScript 端代码

rpc.exports = {
    findmodule: function(so_name) {
        var libso = Process.findModuleByName(so_name);
        return libso;
    },
    dumpmodule: function(so_name) {
        var libso = Process.findModuleByName(so_name);
        if (libso == null) {
            return -1;
        }
        Memory.protect(ptr(libso.base), libso.size, 'rwx');
        var libso_buffer = ptr(libso.base).readByteArray(libso.size);
        libso.buffer = libso_buffer;
        return libso_buffer;
    },
    allmodule: function() {
        return Process.enumerateModules()
    },
    arch: function() {
        return Process.arch;
    }
}

4.2.3 修复函数

def fix_so(arch, origin_so_name, so_name, base, size):
    if arch == "arm":
        os.system("adb push android/SoFixer32 /data/local/tmp/SoFixer")
    elif arch == "arm64":
        os.system("adb push android/SoFixer64 /data/local/tmp/SoFixer")
    os.system("adb shell chmod +x /data/local/tmp/SoFixer")
    os.system("adb push " + so_name + " /data/local/tmp/" + so_name)
    os.system("adb shell /data/local/tmp/SoFixer -m " + base + " -s /data/local/tmp/" + so_name + " -o /data/local/tmp/" + so_name + ".fix.so")
    os.system("adb pull /data/local/tmp/" + so_name + ".fix.so " + origin_so_name + "_" + base + "_" + str(size) + "_fix.so")
    os.system("adb shell rm /data/local/tmp/" + so_name)
    os.system("adb shell rm /data/local/tmp/" + so_name + ".fix.so")
    os.system("adb shell rm /data/local/tmp/SoFixer")
    return origin_so_name + "_" + base + "_" + str(size) + "_fix.so"

五、SoFixer 修复原理

5.1 为什么需要修复

  • dump 下来的 .so 是执行视图(段为主)
  • IDA 需要的是链接视图(节为主)
  • SoFixer 作为桥梁,用于还原链接视图结构

5.2 修复过程

  1. 重建 ELF 文件头
  2. 修复动态段(dynamic segment)
  3. 重建程序头表(Phdr)
  4. 重建节头表(Shdr)
  5. 修复重定位表(Relocs)

六、SO 定位原理

6.1 Frida 枚举模块机制

  1. 遍历 linker 内部维护的 soinfo 链表
  2. dlopen 成功后,linker 会将 .so 加入 solist
  3. frida-gum 是 Frida 实现这些功能的核心组件

6.2 调用链

gum_android_enumerate_modules
  → gum_enumerate_soinfo
    → gum_linker_api_get
      → gum_linker_api_try_init
        → gum_android_get_linker_module
          → gum_try_init_linker_module
            → 遍历 /proc/self/maps 查找 linker
  → for (si = api->solist_get_head (); carry_on && si != NULL; si = next)
    → gum_emit_module_from_soinfo

6.3 soinfo 结构

struct soinfo {
    const char* name;   // 共享库的文件名
    Elf_Addr base;      // 共享库加载到内存的基地址
    size_t size;        // 共享库在内存中的大小
    soinfo* next;       // 指向链表中下一个已加载共享库的指针
};

6.4 solist 定位

  • solist 是 linker 中的静态变量
  • 真实符号:__dl__ZL6solist
  • 位于 linker64 的 .bss 段
  • 可通过 adb pull /apex/com.android.runtime/bin/linker64 获取

七、脱壳关键点

  1. 定位解密后的 .so:通过 solist 和 soinfo 结构找到内存中的 so 信息
  2. 内存转储:获取正确的基地址和大小后 dump 内存数据
  3. 结构修复:使用 SoFixer 将执行视图转换为链接视图
  4. 验证:使用 IDA 打开修复后的 so,确认能正常解析代码

八、总结

SO 脱壳的核心流程可以概括为:

  1. 识别加壳特征
  2. 使用 Frida 定位内存中的 so 信息
  3. 转储内存中的 so 数据
  4. 修复 ELF 结构
  5. 验证脱壳结果

理解 linker 的 solist 和 soinfo 机制是成功脱壳的关键,而 SoFixer 则是将内存转储数据转换为可分析格式的重要工具。

SO 脱壳全流程详解:识别加壳、Frida Dump 与原理解析 一、SO 加壳识别方法 1.1 IDA 识别特征 ELF 结构异常 :IDA 打开 so 文件时提示无法正确识别 ELF 文件结构 Section 定义无效 :section 定义不符合预期格式 红色代码块 :存在大量红色汇编代码块,表示错误或未能正常解析的地址/数据 其他特征 :代码被"混淆"、"裁剪"或"加壳"的迹象 二、Frida Dump 脱壳实战 2.1 准备工作 克隆 frida_ dump 工具到本地 修改远程连接配置(如需): 2.2 脱壳执行流程 执行脱壳命令: 输出示例: 2.3 SoFixer 修复流程 推送 SoFixer 到设备: 执行修复: 修复过程关键日志: 三、SO 查找与信息获取 3.1 查找单个 SO 3.2 获取所有 SO 信息 四、Frida Dump 脱壳原理详解 4.1 整体流程 使用 Frida 连接目标 Android 进程 加载 dump_ so.js 脚本 获取目标 .so 文件的基地址和大小 从内存中转储目标 .so 文件 使用 SoFixer 修复转储的内存数据 下载修复后的 .so 文件到本地 4.2 关键代码解析 4.2.1 Python 端代码 4.2.2 JavaScript 端代码 4.2.3 修复函数 五、SoFixer 修复原理 5.1 为什么需要修复 dump 下来的 .so 是执行视图(段为主) IDA 需要的是链接视图(节为主) SoFixer 作为桥梁,用于还原链接视图结构 5.2 修复过程 重建 ELF 文件头 修复动态段(dynamic segment) 重建程序头表(Phdr) 重建节头表(Shdr) 修复重定位表(Relocs) 六、SO 定位原理 6.1 Frida 枚举模块机制 遍历 linker 内部维护的 soinfo 链表 dlopen 成功后,linker 会将 .so 加入 solist frida-gum 是 Frida 实现这些功能的核心组件 6.2 调用链 6.3 soinfo 结构 6.4 solist 定位 solist 是 linker 中的静态变量 真实符号: __dl__ZL6solist 位于 linker64 的 .bss 段 可通过 adb pull /apex/com.android.runtime/bin/linker64 获取 七、脱壳关键点 定位解密后的 .so :通过 solist 和 soinfo 结构找到内存中的 so 信息 内存转储 :获取正确的基地址和大小后 dump 内存数据 结构修复 :使用 SoFixer 将执行视图转换为链接视图 验证 :使用 IDA 打开修复后的 so,确认能正常解析代码 八、总结 SO 脱壳的核心流程可以概括为: 识别加壳特征 使用 Frida 定位内存中的 so 信息 转储内存中的 so 数据 修复 ELF 结构 验证脱壳结果 理解 linker 的 solist 和 soinfo 机制是成功脱壳的关键,而 SoFixer 则是将内存转储数据转换为可分析格式的重要工具。