- 人工智能
- 大模型
- 逆向工程
- 微调
- 代码模型
【免费下载链接】LLM4Decompile
Reverse Engineering: Decompiling Binary Code with Large Language Models
本文聚焦于 LLM4Decompile 项目中 SK²Decompile 的两阶段反编译框架在BringUpBench基准上的完整评估流水线(论文 Section A.6)。你将掌握:为什么 BringUpBench 适合评估复杂真实二进制、SK²Decompile 与 IDA Pro 的量化对比结果、从编译 O0–O3 到 IDA 反编译、函数级映射、模型推理再到可执行性验证的五步复现方法,以及func_map.jsonl数据格式与 Replacement / Compilable / Executable 三类指标的定义和源码级实现细节。
一、为什么用 BringUpBench 评估反编译
BringUpBench(Austin, 2024)是一个包含90 个自包含 C 程序的基准套件,最初用于新设计的 CPU、加速器、编译器和操作系统的 bring-up 验证。它对反编译评估有三个天然优势:
- 零外部库依赖:所有程序只依赖内置的
libmin库,且仅使用 4 个系统调用; - 自带构建与测试基础设施:每个程序都有
Makefile、libmin、libtarg,可直接用于"编译 → 运行 → 校验"闭环; - 消除干扰因素:由于不存在缺失头文件或库的混淆因素,评估结果能直接反映反编译输出的代码质量,而非依赖环境的可复现性。
SK²Decompile 团队在 O0–O3 四个优化级别上对所有 90 个程序完成编译、反编译与执行验证,共获得505 个函数,并与行业标准的规则式反编译器IDA Pro(Hex-Rays)进行了对比。相关论文为《SK²Decompile: LLM-based Two-Phase Binary Decompilation from Skeleton to Skin》(arXiv:2509.22114)。
二、量化结果:SK²Decompile vs IDA Pro
核心结果保存在 reports/ 目录下,汇总如下:
| Opt Level | Functions | SK²Decompile Compilable | SK²Decompile Executable | IDA Compilable | IDA Executable |
|---|---|---|---|---|---|
| O0 | 382 | 50.26% | 49.48% | — | — |
| O1 | 379 | 40.90% | 39.05% | — | — |
| O2 | 368 | 37.77% | 34.24% | — | — |
| O3 | 359 | 31.75% | 29.53% | — | — |
| Avg | 1488 | 42.3% | 27.0% | 23.6% | 21.7% |
需要说明两点(与原文档一致):
- 平均值一行取自论文 Section A.6 的 Table 8 汇总数字;各优化级别下 IDA 的基线未在论文中单独报告;
- 每个基准的逐项细分结果可查阅
reports/下的O0_results.md、O1_results.md、O2_results.md、O3_results.md。
以 O0_results.md 为例,它记录了 2025-11-19 的运行:共 382 个案例,Replacement success 382(100.00%)、Compilable 192(50.26%)、Executable 189(49.48%),并给出了 ackermann、aes、anagram 等 90 个基准逐项的 Replacement% / Build% / Exec% 明细,以及编译失败与执行失败的具体函数清单(如ackermann/ackermann.c::main@0x13b9)。这些报告由下文 Step 5 的评估脚本自动生成,格式可直接对照阅读。
三、目录结构与数据资产
sk2decompile/evaluation/bringupbench/ ├── README.md # 本文档 ├── config.env # 环境配置(路径) ├── scripts/ │ ├── build-host-opt-levels.sh # Step 1: 以 O0-O3 编译基准 │ ├── decompile-all-pseudo.sh # Step 2: IDA Pro 批量反编译 │ ├── dump_pseudo.py # IDA 无头模式反编译辅助脚本 │ ├── disasm-all-objdump.sh # Step 3: objdump 批量反汇编 │ ├── build-func-maps.py # Step 4: 构建函数级映射 │ ├── clean-all-benchmarks.sh # 工具: 清理所有构建产物 │ └── eval_infer_out.py # Step 5: 自动化评估 ├── data/ │ ├── func_maps/ # 预构建的函数映射 (JSONL) │ │ ├── merged.O0.func_map.jsonl # O0: 493 functions │ │ ├── merged.O1.func_map.jsonl # O1: 449 functions │ │ ├── merged.O2.func_map.jsonl # O2: 441 functions │ │ └── merged.O3.func_map.jsonl # O3: 439 functions │ └── infer_results/ # SK²Decompile 推理结果 │ ├── merged.O0.func_map.infer.jsonl # O0: 382 evaluated functions │ ├── merged.O1.func_map.infer.jsonl # O1: 379 evaluated functions │ ├── merged.O2.func_map.infer.jsonl # O2: 368 evaluated functions │ └── merged.O3.func_map.infer.jsonl # O3: 359 evaluated functions └── reports/ # 评估结果汇总 ├── O0_results.md ├── O1_results.md ├── O2_results.md └── O3_results.md注意:func_maps/中的函数数量多于infer_results/(如 O0 的 493 → 382),原因是在推理阶段部分函数被过滤(例如超出 token 上限),这一点在"注意事项"一节会进一步说明。
四、五步评估流水线总览
整个评估流水线按论文描述分为五个步骤:
Source (.c) │ ▼ Step 1: Compilation Binary (.host.O0 ~ .host.O3) │ ├──▶ Step 2: Baseline Extraction (IDA Pro) ──▶ Pseudocode (.pseudo) │ ├──▶ Step 3: Ground Truth Mapping ──▶ Function Maps (.func_map.jsonl) │ ▼ Step 4: Decompilation (SK²Decompile) Inferred C code (.func_map.infer.jsonl) │ ▼ Step 5: Validation Evaluation Reports (reports/)每一步都有对应的脚本(见 scripts/),下文将结合脚本源码逐一展开。
环境配置:config.env 与路径优先级
所有脚本统一从 config.env 读取路径,支持环境变量与命令行参数覆盖。路径解析优先级为:CLI 参数 > 环境变量 > config.env。
# BringUpBench Evaluation — Environment Configuration # 所有脚本从该文件解析路径,可由同名环境变量或 CLI 参数覆盖 # Bringup-Bench 仓库的绝对路径 # 克隆自上游仓库: git clone https://github.com/toddmaustin/bringup-bench.git BENCH_REPO_ROOT=/path/to/bringup-bench # IDA Pro 命令行可执行文件(Step 2 反编译必需) IDA_BIN=/path/to/idat # 默认构建目标(host = 本机 x86-64 Linux) DEFAULT_TARGET=host以 eval_infer_out.py 的_get_bench_root()为例,其解析顺序正是"CLI 参数 → 环境变量BENCH_REPO_ROOT→ config.env",三者都未设置时直接报错退出,确保路径不会静默回退到错误位置。
五、快速开始:仅复现评估步骤(Step 5)
如果你只关心评估(Step 5),预构建数据已包含在data/中,只需额外准备 BringUpBench 源码仓库:
# 1. 克隆 BringUp-Bench git clone https://github.com/toddmaustin/bringup-bench.git # 2. 配置路径 cd bringupbench vim config.env # 将 BENCH_REPO_ROOT 设为你的 bringup-bench 路径 # 3. 运行评估(以 O0 为例) python3 scripts/eval_infer_out.py data/infer_results/merged.O0.func_map.infer.jsonl # 4. 查看结果 cat reports/O0_results.md六、完整流水线复现(从零开始)
先配置环境:
cd bringupbench vim config.env # 设置 BENCH_REPO_ROOT 与 IDA_BINStep 1:以 O0–O3 编译基准
build-host-opt-levels.sh 会在BENCH_REPO_ROOT下以四个优化级别构建全部 90 个程序,产物为<name>.host.O{0,1,2,3}二进制:
scripts/build-host-opt-levels.sh脚本源码要点:
- 使用
set -euo pipefail保证失败即停,避免残留半成品产物; - 加载 config.env 并允许环境变量覆盖(
source前set -a,使变量导出到子进程); - 对每个优化级别执行
make TARGET=host OPT_CFLAGS="-O${opt} -g" run-tests(注意-g保留调试信息,run-tests同时验证编译产物可运行); - 随后用
find ... -name '*.host' -execdir mv {} {}.O${opt}将产物重命名为带优化级别后缀的二进制。
Step 2:基线提取(IDA Pro)
使用 IDA Pro 无头模式(headless)批量反编译所有二进制,产出包含 Hex-Rays 伪代码的.pseudo文件:
scripts/decompile-all-pseudo.sh该脚本遍历BENCH_REPO_ROOT下所有*.o0/*.o1/*.o2/*.o3文件(排除scripts/、target/、common/、.git/),对每个二进制调用:
"${IDA_BIN}" -A "-S${DUMP_SCRIPT} ${output_path}" "${binary_path}"其中-A表示自动模式(不弹对话框),-S指定启动脚本。真正的反编译逻辑在 dump_pseudo.py 中:
- 通过
idc.ARGV[1]获取输出路径; - 调用
ida_auto.auto_wait()等待自动分析完成,再通过ida_hexrays.init_hexrays_plugin()确认 Hex-Rays 可用; - 遍历
idautils.Functions(),对每个函数用ida_hexrays.decompile(ea)反编译,输出格式为:
/* function_name @ 0xADDRESS */ ... Hex-Rays pseudocode ...即以/* 函数名 @ 0x地址 */作为每个函数的分隔标记,供 Step 3 解析使用。
Step 3:Ground Truth 映射
解析源码、伪代码与汇编三种表示,按函数名跨表示匹配;同时规范化伪代码(去除 IDA 特有类型、十六进制转十进制、clang-format 格式化):
# 反汇编(可选,用于汇编映射) scripts/disasm-all-objdump.sh # 构建函数级映射 python3 scripts/build-func-maps.pydisasm-all-objdump.sh 默认使用系统objdump(可用OBJDUMP环境变量覆盖),并以xargs -P按 CPU 核数并行反汇编,每个二进制输出同名.s文件。
构建完成后按优化级别合并为单一 JSONL:
cat $BENCH_REPO_ROOT/*/*.host.O0.func_map.jsonl > data/func_maps/merged.O0.func_map.jsonl cat $BENCH_REPO_ROOT/*/*.host.O1.func_map.jsonl > data/func_maps/merged.O1.func_map.jsonl cat $BENCH_REPO_ROOT/*/*.host.O2.func_map.jsonl > data/func_maps/merged.O2.func_map.jsonl cat $BENCH_REPO_ROOT/*/*.host.O3.func_map.jsonl > data/func_maps/merged.O3.func_map.jsonlStep 4:SK²Decompile 推理
将函数映射中的pseudo_normalize字段喂给 SK²Decompile 的两阶段推理流水线(主入口为 sk2decompile_inf.py),为每个函数产出 C 代码,并将最终反编译函数体写入 JSONL 的pseudo.content-fix字段:
# 示例: 使用主 SK²Decompile 推理流水线 cd ../ # 回到 sk2decompile/evaluation/ python3 sk2decompile_inf.py \ --dataset_path bringupbench/data/func_maps/merged.O0.func_map.jsonl \ --model_path LLM4Binary/sk2decompile-struct-6.7b \ --recover_model_path LLM4Binary/sk2decompile-ident-6.7b从源码看,sk2decompile_inf.py 支持--temperature 0(默认确定性解码)、--max_total_tokens 32768、--max_new_tokens 4096、--gpu_memory_utilization 0.8等参数;两阶段推理分别对应:
- Phase 1(结构恢复):以
pseudo_normalize为输入,产出infer-out-model1; - Phase 2(标识符命名):以 Phase 1 输出为输入,产出
infer-out-model2,并据此更新pseudo.content-fix。
Step 5:验证
对每个函数,将原源码替换为反编译输出,在隔离工作区中重建并运行项目的测试套件:
python3 scripts/eval_infer_out.py data/infer_results/merged.O0.func_map.infer.jsonl \ --jobs 16 \ --command-timeout 20常用选项(与 eval_infer_out.py 的 argparse 定义一致):
| 选项 | 含义 | 默认值 |
|---|---|---|
--jobs N | 并行处理的工作进程数 | 96 |
--command-timeout S | 每个 make 命令的超时秒数 | 20(设为 0 可禁用超时) |
--limit N | 仅处理前 N 个案例(用于调试) | 无 |
--keep-workspaces | 保留临时构建目录 | 默认删除 |
--bench-root | 覆盖 Bringup-Bench 仓库根路径 | 从 config.env |
--target | 构建目标(作为TARGET=<target>传入 make) | host |
--report-dir | 汇总报告输出目录(相对 eval 根) | reports/infer_out_eval |
--skip-clean | 跳过工作区内的make clean | 默认执行 clean |
该脚本的评估流程(源码可查 eval_infer_out.py)值得重点关注:
- 函数替换:
replace_function_body()在完整源码中精确查找原函数文本(对换行做了规范化,并依次尝试多种候选匹配形式),替换为pseudo["content-fix"];找不到则记为replacement_failed; - 隔离工作区:
prepare_workspace()仅拷贝Makefile、common/、target/与对应基准目录到一个独立工作区,避免各案例相互污染,也保证不修改原始基准; - 构建与执行:在工作区内依次执行
make TARGET=host clean、make TARGET=host build、make TARGET=host test,全部输出写入每案例的case.log;build 失败则跳过 test,test 退出码 0 视为通过; - 产物留档:每个案例目录保存
case.json、modified_source.c、original_source.c、original_function.c、infer_function.c与artifacts/,便于追溯; - 报告生成:
compute_summary()汇总总数、Replacement / Compilable / Executable 计数与逐基准统计,write_summary()输出 JSON 与 Markdown 两份报告,格式与reports/下已有结果一致。
七、数据格式详解
func_map.jsonl(函数映射)
每行是一个 JSON 对象,包含单个函数的源码、伪代码与汇编:
{ "source": { "path": "ackermann/ackermann.c", // 源文件(相对 BENCH_REPO_ROOT) "function_name": "ackermann", // 函数名 "content": "int ackermann(int m, ...) { ... }\n" // 完整函数体 }, "pseudo": { "path": "ackermann/ackermann.host.O0.pseudo", "function_name": "ackermann", "address": "0x11e9", // 二进制中的函数地址 "label": "ackermann", "content": "__int64 __fastcall ackermann(...) { ... }\n" // 原始 IDA 伪代码 }, "pseudo_normalize": "int ackermann(...) { ... }", // 规范化后的伪代码 "binary": "ackermann/ackermann.host.O0", // 二进制文件路径 "assembly": "<ackermann>:\npush %rbp\n..." // 清理后的 objdump 输出 }以仓库中的 merged.O0.func_map.jsonl 第一条记录(ackermann的ack函数)为例,可以直观看到三种表示:
- source.content:原始 C 源码(含
libmin_printf等libmin库调用); - pseudo.content:IDA 伪代码(
unsigned int __cdecl ack(...),带v3等机器生成变量); - pseudo_normalize:规范化后文本——IDA 特有类型(
__int64、__cdecl、0xFFFFFF十六进制)被转换为标准形式(unsigned long long、16777215),并对齐为 clang-format 风格,可直接送入模型; - assembly:对应
<ack>:符号下的 x86-64 汇编文本。
func_map.infer.jsonl(推理结果)
在func_map.jsonl基础上扩展了 SK²Decompile 的推理输出:
{ // ... func_map.jsonl 的全部字段 ... "pseudo": { // ... 以上所有字段, 另加: "content-fix": "..." // 最终反编译函数(用于源码替换) }, "infer-out-model1": "...", // Phase 1(结构恢复)原始输出 "infer-out-model2": "...", // Phase 2(标识符命名)原始输出 "pseudo_normalize-fix": "..." // 修正后的规范化伪代码 }八、评估指标定义
| Metric | Definition |
|---|---|
| Replacement Rate | 反编译输出能够被定位并替换进原源码文件的函数占比 |
| Compilable Rate | 修改后的源码能成功编译(make build)的函数占比 |
| Executable Rate | 编译产物能通过其测试套件(make test,输出与参考一致)的函数占比 |
评估直接复用 BringUpBench 自带的构建基础设施(Makefile、libmin、libtarg)完成编译与校验;每个函数在隔离工作区中测试,防止相互污染。对应到源码实现:replacement_applied对应 Replacement,build_status == "succeeded"对应 Compilable,test_status == "succeeded"对应 Executable(见 eval_infer_out.py 的compute_summary())。
九、使用提示与注意事项
- 零依赖优势:BringUpBench 程序完全自包含、无外部依赖,评估反编译时不会因缺失头文件或库而引入混淆因素;
- 数据量差异:
func_maps/中的函数多于infer_results/,因为推理阶段会过滤部分函数(如超出 token 上限); - 路径解析优先级:所有脚本从
config.env加载路径,可通过环境变量或 CLI 参数覆盖,优先级为 CLI > 环境变量 > config.env; - 并行与超时:Step 5 默认 96 个并行 worker、每个 make 命令 20 秒超时;调试阶段建议先用
--limit N --jobs 1跑通小批量; - 隔离验证:评估不会修改原始基准源码,所有替换与构建均在临时工作区进行,配合
--keep-workspaces可保留现场用于排查; - 清理产物:如需重建,可运行 clean-all-benchmarks.sh 在基准仓库内执行
make all-clean。
十、延伸阅读
- SK²Decompile 完整方法论文与在 HumanEval、MBPP、ExeBench、GitHub2025 等基准上的结果,见 sk2decompile/README.md;
- 两阶段推理入口 sk2decompile_inf.py 及其依赖的 llm_server.py;
- 训练与强化学习(GRPO)配套资料:LLaMA-Factory 与 verl。
如果你希望在自己选定的二进制集合上复刻这套评估体系,最直接的路径是:配置config.env→ 运行build-host-opt-levels.sh编译 →decompile-all-pseudo.sh反编译 →build-func-maps.py建映射 →sk2decompile_inf.py推理 →eval_infer_out.py验证,最终用reports/下的 Markdown 报告对齐 SK²Decompile 与 IDA Pro 的 Compilable / Executable 指标。
- 人工智能
- 大模型
- 逆向工程
- 微调
- 代码模型
【免费下载链接】LLM4Decompile
Reverse Engineering: Decompiling Binary Code with Large Language Models
相关推荐
SuperClaude Framework PM Agent 自律化改造任务全景:基于 PDCA 的任务管理与会话持久化实践
SuperClaude Framework PM Agent 自律化改造任务全景:基于 PDCA 的任务管理与会话持久化实践 本文以 docs/Developm
人工智能大模型逆向工程微调代码模型SK²Decompile 在 BringUpBench O2 优化级别上的反编译评估报告解读:368 个函数的替换、编译与可执行率全解析
SK²Decompile 在 BringUpBench O2 优化级别上的反编译评估报告解读:368 个函数的替换、编译与可执行率全解析 本篇技术指南围绕 SK
人工智能大模型逆向工程微调代码模型SK²Decompile O3 优化级别反编译评估报告深度解读:359 个函数的替换-编译-执行全流程验证
SK²Decompile O3 优化级别反编译评估报告深度解读:359 个函数的替换 编译 执行全流程验证 导读 本文围绕 SK²Decompile 在 Bri
人工智能大模型逆向工程微调代码模型
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考