逆向工程实战手册 第 90 章

第 90 章 跨版本符号迁移(Binary Diff / 符号迁移)

场景:旧版二进制有符号(函数名/结构),新版没有(缺 PDB/strip),你需要把旧版的知识"搬"到新版——这就是跨版本符号迁移。与第 19 章(补丁差分)的区别:补丁差分找"漏洞点"(攻击侧),符号迁移恢复"函数名"(分析侧)。这是商业软件逆向、内核分析、恶意软件版本对比的日常刚需。

📍 知识点地图 | 主题:符号迁移 | 前置:第35-37章 | 后续:第19章 | 核心概念:BinDiff/Diaphora、LLM批量迁移、置信度

90.1 核心场景(什么时候用)

□ 内核缺 PDB: 新内核驱动无符号,用旧版符号推导
□ 程序更新后: 老逆向结果迁移到新版本(函数名/偏移)
□ 恶意软件版本对比: 旧样本有符号,新变体无
□ 商业软件: 升级后之前的分析全部失效
□ 固件更新: 提取新固件,用旧固件符号还原

目标: 批量建立"旧符号 → 新地址"的映射表

90.2 方法与工具对比(先选路)

方法 原理 适用 成本
BinDiff 结构/图匹配 相似度高的版本 工具贵
Diaphora 相似度+图匹配(免费) 同上 免费
ghidriff Ghidra 原生 Ghidra 用户 免费
radiff2 指令级粗对比 快速侦查 免费
LLM 批量迁移 语义特征匹配 大规模/跨版本大 极低(约 1 元/200 函数)

90.3 二进制 Diff 工具实操(BinDiff / Diaphora)

# BinDiff 流程
1. IDA 打开两个版本  File  Produce file  生成 .BinExport
2. 打开新版  File  BinDiff  选旧版 .BinExport
3. 查看匹配结果: Matched(相同)/ Unmatched(新增/删除)
4. 按相似度排序  高相似度自动迁移符号

# Diaphora(免费替代)
1. IDA  File  Script file  diaphora.py(第一次:导出)
2. 打开第二个  再跑 diaphora.py  选择 diff
3. 输出: 匹配函数列表 + 相似度分数

# ghidriff(命令行,CI 友好)
ghidriff old_binary new_binary --output-dir diff/

阅读 diff 结果的要点

□ 匹配的函数(相似度 100%)→ 直接复制符号
□ 高相似(>90%)→ 大概率同函数,迁移并抽查
□ 中相似(70-90%)→ 可能改了逻辑,需人工确认
□ 不匹配 → 新增/删除/大改(重点分析对象!)
□ 关注: 变更函数的"调用者"是否也变了(逻辑影响面)

90.4 LLM 批量符号迁移(本仓库特色方法)

为什么 LLM 适合

传统 diff 局限:
  □ 编译器版本变化 → 结构完全不同 → 图匹配失效
  □ 大批量函数(数千个)→ 手工迁移不现实

LLM 优势:
  □ 语义理解(不看指令长相,看"函数干什么")
  □ 批量处理(程序化输入输出)
  □ 成本极低(约 1 元/200 函数)
  □ 跨"完全不同"的代码也能匹配(按行为特征)

工作流

Step 1: 提取旧版符号表(函数名 + 特征)
  IDA 导出 / 脚本: 函数名、参数、调用的 API、字符串引用、常量

Step 2: 提取新版函数特征(无符号)
  地址、大小、调用的 API、字符串引用、常量、结构特征

Step 3: LLM 批量匹配(按特征对拍)
  输入: 旧版函数清单(带特征) + 新版函数清单(带特征)
  输出: 映射表(新地址 → 旧符号名)

Step 4: 生成迁移结果(YAML/脚本)
  new_addr: old_name
  ...

Step 5: 抽查验证(随机 5-10% 人工确认)

Prompt 模板(第 29 章配合)

任务: 跨版本函数符号迁移
输入 A(旧版,有符号): 函数名+调用API+字符串+常量
输入 B(新版,无符号): 地址+调用API+字符串+常量
要求: 输出映射 "新地址 → 最可能的旧函数名" + 置信度
规则: 按行为特征匹配(API/字符串/常量),不按字节

90.5 迁移质量评估(重要)

□ 置信度分级(高/中/低)
□ 高置信度直接应用,中置信度人工复核
□ 字符串引用匹配是最强信号(函数用了同一批字符串 ≈ 同一函数)
□ API 调用序列次之
□ 常量特征最弱(很多函数用相同常量)
□ 抽查: 随机函数反编译验证迁移正确性

90.6 常见失败与处理

□ 大改版: 函数拆分/合并 → 1:1 匹配失效 → 按"行为簇"匹配
□ 混淆: 新版加混淆 → 特征隐藏 → 先脱混淆再迁移
□ 内联: 函数被内联进调用者 → 匹配不到 → 识别内联模式
□ 新增功能: 大量新函数 → 正常,标注"新增"即可
□ 编译器变化: 结构全变 → 用 LLM 语义匹配兜底

90.7 检查清单

□ 场景确认(符号迁移 vs 补丁差分)
□ 工具选型(BinDiff/Diaphora/ghidriff/LLM)
□ 旧版符号导出(函数名+特征)
□ 新版特征提取
□ diff/匹配执行
□ 置信度分级
□ 迁移结果生成(映射表)
□ 抽查验证(5-10%)
□ 产出: 映射表 + 未匹配清单(重点分析对象)

动手练习

  1. 编译一个程序的两个版本(加几个函数/改逻辑),用 radiff2 快速对比,再用 Diaphora 深度 diff。
  2. 练习 LLM 迁移:构造 10 个函数的"旧版清单+新版特征",让 LLM 生成映射,人工验证。
  3. 分析一次"程序升级"的 diff 结果:找出新增/修改/删除的函数,理解变更意图。
  4. 用 ghidriff 在命令行完成一次自动化 diff(CI 风格)。

深入阅读

  • 仓库:skills/binary-diff/SKILL.md(LLM 批量符号迁移方法论)
  • 仓库:skills/binary-diff/references/prompt-template.md(迁移 Prompt 模板)
  • 仓库:skills/patch-diff-exploit/(攻击侧补丁差分,第 19 章)
  • 仓库:skills/patch-diff-exploit/references/diff-tools-comparison.md(工具对比)
  • 仓库:skills/reverse-engineering/tools-advanced.md(BinDiff/Diaphora 章节)