- 人工智能
- 大模型
- 逆向工程
- 微调
- 代码模型
【免费下载链接】LLM4Decompile
Reverse Engineering: Decompiling Binary Code with Large Language Models
导读
本文以 SK²Decompile 项目在 BringUpBench 基准上的O1 优化级别评估报告(sk2decompile/evaluation/bringupbench/reports/O1_results.md)为核心,完整解读该次评估的实验设置、379 个函数的统计结果、逐基准细分数据以及失败案例分析,并结合仓库中的评估脚本(eval_infer_out.py)与数据文件,说明这些数字是如何一步步计算出来的。读完本文,你将理解 SK²Decompile 的两阶段反编译输出在 "替换→编译→执行" 三级验证下的真实表现,掌握 O1 报告的数据结构与评估脚本的复现方法。
实验背景:O1 报告在 SK²Decompile 评估体系中的位置
SK²Decompile 将反编译拆分为两阶段:第一阶段(Phase 1,Structure Recovery)从汇编恢复代码骨架,输出infer-out-model1;第二阶段(Phase 2,Identifier Naming)为骨架中的占位符命名,输出最终函数体infer-out-model2。BringUpBench 评估正是对第二阶段的最终产物做端到端验证。O1 报告标题 "Infer-Out Model 2 Evaluation" 指的就是对infer-out-model2字段的评估。
本仓库在 sk2decompile/evaluation/bringupbench/README.md 中说明:BringUpBench(Austin, 2024)是包含90 个自包含 C 程序的基准套件,零外部库依赖,仅依赖内置libmin库和 4 个系统调用,非常适合在无缺失头文件/库干扰的真实二进制上做反编译评估。项目对全部程序在 O0–O3 四个优化级别下进行了编译、反编译与执行验证,共得到 1488 个函数,并与 IDA Pro(Hex-Rays)对比。O1 报告正是这一整体评估中优化级别为 O1 的那一份。
从同一目录的 O0_results.md、O2_results.md、O3_results.md 可以看出,每个优化级别对应一份独立报告。O1 级别共评估 379 个函数,这是四个级别中评估数量最多的(O0 为 382,O2 为 368,O3 为 359),也是理解 SK²Decompile 中等优化强度下表现的关键样本。
O1 评估报告逐项解读
报告头部元数据
O1 报告头部给出了这次评估的完整上下文信息:
| 字段 | 值 | 含义 |
|---|---|---|
| Timestamp | 20251119-171212 | 报告生成时间戳,格式为YYYYMMDD-HHMMSS |
| Source JSONL | merged.O1.func_map.infer.jsonl | 评估输入数据文件,位于 data/infer_results/ |
| Target | host | 编译目标平台,即原生 x86-64 Linux |
| Total cases | 379 | 参与评估的函数总数 |
| Replacement success | 379 (100.00%) | 反编译函数体成功替换进原源码的比例 |
| Compilable | 155 (40.90%) | 替换后能通过make build编译的函数比例 |
| Executable | 148 (39.05%) | 编译通过且通过make test测试的函数比例 |
其中三个核心指标的严格定义在 README.md 的 "Evaluation Metrics" 一节中给出:
- Replacement Rate(替换率):反编译输出能被定位并替换进原始源文件的比例;
- Compilable Rate(可编译率):替换后的源码能成功编译(
make build)的比例; - Executable Rate(可执行率):编译后的程序通过其测试套件(
make test,输出与参考一致)的比例。
O1 级别在三个指标上分别达到100% 替换率、40.90% 可编译率、39.05% 可执行率。值得注意的细节是:所有 379 个函数都成功完成了源码替换,说明 SK²Decompile 输出的函数体在文本层面都能被准确定位并替换回原文件;而从编译到执行之间存在少量损耗(155 → 148),这 7 个函数编译通过但测试失败,被记录在 "Execution Failures" 列表中。
逐基准细分表(Benchmark Breakdown)
O1 报告的核心主体是覆盖全部基准程序的逐基准统计表,每个基准行给出 4 列数据:Cases(该基准参与评估的函数数)、Replacement%(替换率)、Build%(可编译率)、Exec%(可执行率)。这张表包含 90 余个基准程序,覆盖了从经典算法(bubble-sort、heapsort、n-queens、knapsack)到密码学(aes、blake2b、rsa-cipher、cipher)、信号处理(fft-int、idct-alg)、图算法(graph-tests、topo-sort、shortest-path)、数值计算(k-means、lu-decomp、grad-descent)、压缩(lz-compress、huff-encode、rle-compress)等广泛领域。
从这张表中可以观察到几个有代表性的数据点:
- 全绿基准:bubble-sort(3 例)、lz-compress(2 例)、nr-solver(1 例)三个基准的 Build% 与 Exec% 均为 100.00%,说明这些函数体被反编译后可以直接编译并正确运行;
- 高表现基准:huff-encode(13 例,92.31%)、checkers(16 例,81.25%)、bit-kernels(5 例,80.00%)、priority-queue(5 例,80.00%)、qsort-test(5 例,80.00%)、rho-factor(4 例,75.00%)、convex-hull(4 例,75.00%)等;
- 零分基准:audio-codec、banner、boyer-moore-search、ccmac、congrad、distinctness、gcd-list、grad-descent、heat-calc、mandelbrot、matmult、max-subseq、mersenne、monte-carlo、murmur-hash、natlog、nbody-sim、parrondo、pi-calc、quaternions、rabinkarp-search、rand-test、ransac、rle-compress、rsa-cipher、sieve、simple-grep、spelt2num、topo-sort、transcend、uniquify、verlet、weekday 等基准的 Build% 为 0.00%,这些基准中的函数替换后均未能通过编译;
- 编译与执行不一致:cipher(Build 33.33% / Exec 0.00%)、idct-alg(Build 66.67% / Exec 33.33%)、life(Build 21.43% / Exec 14.29%)、minspan(Build 37.50% / Exec 25.00%)、regex-parser(Build 25.00% / Exec 12.50%)、tetris-sim(Build 75.00% / Exec 66.67%)、vectors-3d(Build 12.50% / Exec 0.00%)等基准存在编译通过但执行失败的情况,这些正是报告末尾 "Execution Failures" 列表中的函数来源。
按函数数量看,评估规模较大的基准包括 graph-tests(19 例)、checkers(16 例)、avl-tree(17 例)、life(14 例)、anagram(13 例)、connect4-minimax(13 例)、huff-encode(13 例)、tetris-sim(12 例)、c-interp(10 例)、frac-calc(10 例),这些复杂程序为评估提供了足够的函数样本量。
失败清单解读:Compilation Failures 与 Execution Failures
报告的第三、四部分分别是失败函数的完整清单,每一条以路径/文件名.c::函数名@地址格式标识,例如aes/aes.c::aes_decrypt@0x161b。
Compilation Failures(编译失败)共 224 条,涵盖 379 个函数中的大部分(379 − 155 = 224)。这些函数的反编译输出替换回源码后无法通过make build。从清单中可以观察到几类典型的反编译失败模式:
- 多文件基准中的辅助函数:如 avl-tree 的 avlcore.c、element.c,checkers 的 functions.c,这些函数与主函数位于不同源文件,反编译时容易丢失类型信息;
- 涉及系统相关操作或特殊调用约定的函数:如
__readfsqword、__stack_chk_fail等栈保护相关调用,在 aes 的多个函数中出现; - 长函数与复杂控制流:c-interp 的 eval(0x35d3)等解释器核心函数体量庞大,反编译易出错;
- 小工具函数:大量基准的辅助函数地址为 0x11e9(如 banner/main@0x11e9、ccmac/main@0x11e9),这是 IDA 为多个程序分配的第一个函数地址,这些简单函数在 O1 优化后往往被内联或变换,反编译结果难以直接编译。
Execution Failures(执行失败)共 7 条,即编译通过但测试未通过:
cipher/cipher.c::decipher@0x1251 idct-alg/idct-alg.c::idct_2d@0x1216 life/life.c::init@0x11e9 minspan/minspan.c::displayTree@0x16b7 regex-parser/regex-parser.c::matchpattern@0x2491 tetris-sim/tetris-sim.c::clear_lines@0x12b6 vectors-3d/vectors-3d.c::get_angle@0x1429这 7 个函数编译成功但运行时行为与参考不一致,可能的原因包括逻辑改写错误(如循环条件、边界判断、位运算顺序被反编译器误改)、符号语义偏差等。从数据上看,执行失败仅占编译成功函数的约 4.5%(7/155),说明 SK²Decompile 第二阶段输出的函数体一旦能通过编译,其语义正确率较高。
报告数据从何而来:评估脚本的完整调用链
O1 报告不是手工整理的表格,而是由 eval_infer_out.py 自动生成的。理解该脚本的调用链,就能完全理解报告上每个数字的来源。
数据输入:merged.O1.func_map.infer.jsonl
报告的输入是 data/infer_results/merged.O1.func_map.infer.jsonl,共 379 行。每一行是一个 JSON 对象,包含函数的源码(source)、IDA 伪代码(pseudo)、规范化伪代码(pseudo_normalize)、二进制路径(binary)、汇编(assembly)以及 SK²Decompile 两阶段输出(infer-out-model1、infer-out-model2)和修正后的最终函数体(pseudo.content-fix)。以报告数据文件中的 ackermann 的ack函数为例:
source.content是原始 C 源码(带注释的递归 Ackermann 实现);pseudo.content是 IDA Hex-Rays 输出的伪代码,包含__fastcall调用约定、// eax寄存器注释等 IDA 特有信息;pseudo_normalize是规范化后的伪代码(去除了 IDA 类型,十六进制转十进制,clang-format 格式化);infer-out-model1是第一阶段输出(占位符命名 var1、var2、var3...);infer-out-model2是第二阶段输出(语义化命名 x、y、depth、table、maxdepth...);pseudo.content-fix是用于源码替换的最终反编译函数体。
这份文件的前 3 行示例恰好覆盖了 ackermann 和 aes 两个基准,其中 ackermann/ackermann.c 的 main 函数在 O1 报告的 Compilation Failures 清单中(ackermann/ackermann.c::main@0x131c),而 ack、aes 的多个函数也出现在失败清单中,与报告中相应基准的低 Build% 相互印证。
三级验证的核心逻辑:替换 → 编译 → 执行
从 eval_infer_out.py 的源码可以梳理出报告数据的完整产生流程,其核心在process_case函数中:
第一级:函数体替换(决定 Replacement 指标)
replace_function_body函数(eval_infer_out.py)将原始源文件中与case["source"]["content"]精确匹配的文本片段替换为case["pseudo"]["content-fix"]。为了应对换行符差异,它会尝试三种候选匹配方式(原文、去尾部换行加\n、strip 后原文),任一命中即视为替换成功。若在源文件中定位不到原始函数片段,则replacement_applied=False,该 case 被记入失败。O1 报告的 100% 替换率说明全部 379 个函数的content-fix都能被定位并替换。
第二级:隔离工作区编译(决定 Build 指标)
每个 case 都在独立的工作区中执行,prepare_workspace函数(eval_infer_out.py)将 BringUpBench 仓库的 Makefile、common 目录、target 目录以及该基准目录复制到临时工作区,然后在工作区内执行:
make TARGET=host clean make TARGET=host buildrun_command函数(eval_infer_out.py)负责执行命令并记录日志,支持超时控制。若make build返回 0 则build_status="succeeded",否则为 "failed",并跳过测试阶段。报告中的 Build% 即为build_status=="succeeded"的 case 数除以总 case 数。
第三级:测试执行(决定 Exec 指标)
编译成功后继续执行:
make TARGET=host test若make test返回 0 则test_status="succeeded",否则为 "failed"。报告中的 Exec% 即为test_status=="succeeded"的 case 数除以总 case 数。O1 的 7 条 Execution Failures 就是那些 build 成功但 test 失败的 case。
并行与日志细节
脚本通过ThreadPoolExecutor(eval_infer_out.py)以--jobs参数(默认 96)并行处理各 case。每个 case 生成独立的输出目录与case.log日志,保留modified_source.c、original_source.c、infer_function.c等工件(write_case_artifacts,eval_infer_out.py)。完成后由compute_summary与write_summary汇总生成 Markdown 与 JSON 双份报告,O1 报告正是write_summary以merged.O1.func_map.infer-host为文件名、host为目标生成的产物。
参数解析与配置优先级
脚本支持通过命令行参数精细控制评估过程,常用参数包括:
| 参数 | 默认值 | 作用 |
|---|---|---|
--bench-root | 来自 config.env | BringUpBench 仓库根路径 |
--limit N | 无 | 只处理前 N 个 case,用于调试 |
--target | host | 编译目标平台,作为TARGET=传给 make |
--report-dir | reports/infer_out_eval | 汇总报告输出目录 |
--workspace-root | reports/infer_out_eval/workspaces | 临时工作区目录 |
--skip-clean | 关闭 | 跳过make clean |
--keep-workspaces | 关闭 | 保留临时工作区 |
--command-timeout S | 20 | 每条 make 命令超时秒数,0 表示禁用 |
--jobs N | 96 | 并行处理的 case 数 |
路径解析遵循 CLI 参数 > 环境变量 > config.env 的优先级(_get_bench_root函数,eval_infer_out.py;config.env 中声明BENCH_REPO_ROOT、IDA_BIN、DEFAULT_TARGET三个键,注释明确说明这一优先级)。
复现 O1 评估结果的操作指南
O1 报告的评估数据(data/infer_results/merged.O1.func_map.infer.jsonl)已随仓库提供,若只想复现评估环节(Step 5),只需 BringUpBench 源码仓库,无需重新编译与反编译:
# 1. 克隆 BringUpBench 基准 git clone https://github.com/toddmaustin/bringup-bench.git # 2. 进入评估目录并配置路径 cd sk2decompile/evaluation/bringupbench # 编辑 config.env,将 BENCH_REPO_ROOT 指向 bringup-bench 路径 # 3. 运行 O1 评估(并指定 16 个并行任务、20 秒命令超时) python3 scripts/eval_infer_out.py data/infer_results/merged.O1.func_map.infer.jsonl \ --jobs 16 \ --command-timeout 20 # 4. 查看生成的报告 cat reports/O1_results.md若要复现报告中的 100% 替换率数字,可注意脚本在替换时尝试三种候选片段匹配(见replace_function_body),这是实现高替换率的关键实现细节。若机器资源有限,可先用--limit 5 --keep-workspaces调试少量 case 验证流程,再放开到全量 379 个 case。
如需从零开始复现完整流水线(编译 → IDA 反编译 → 映射构建 → SK²Decompile 推理 → 评估),可参考 bringupbench/README.md 中 Step 1–5 的完整说明:build-host-opt-levels.sh编译 O0–O3 二进制;decompile-all-pseudo.sh 调用 IDA 批量反编译(每个函数以/* function_name @ 0xADDRESS */分隔,由 dump_pseudo.py 实现);disasm-all-objdump.sh与build-func-maps.py构建函数级映射;推理阶段由 sk2decompile_inf.py 完成两阶段反编译,结果写入pseudo.content-fix字段。
O1 结果在整体评估中的定位与对比
将 O1 报告与同目录下其余三份报告对照,可以看到优化级别对反编译难度的影响:
| 优化级别 | 函数数 | 可编译率 | 可执行率 |
|---|---|---|---|
| O0 | 382 | 50.26% | 49.48% |
| O1 | 379 | 40.90% | 39.05% |
| O2 | 368 | 37.77% | 34.24% |
| O3 | 359 | 31.75% | 29.53% |
(O0/O2/O3 数据分别来自 O0_results.md、O2_results.md、O3_results.md。)
可以清晰地看到一条下降曲线:优化级别越高,函数内联、寄存器重分配、控制流改写越激进,反编译恢复的难度越大,可编译率与可执行率随之降低。O1 处于 O0 与 O2 之间,作为中等优化强度的代表,其 39.05% 的可执行率更贴近真实世界中带优化的发布二进制场景。
总结
O1 报告完整呈现了 SK²Decompile 在 BringUpBench O1 优化级别上的端到端评估结果:379 个函数全部成功完成源码替换(100%),155 个(40.90%)能重新编译,148 个(39.05%)能通过测试执行。报告中的每个数字都可以在 eval_infer_out.py 的替换 → 编译 → 执行三级验证流程中找到对应实现,失败清单则精确记录了哪些函数、哪个优化级别下的反编译尚未达到可编译/可执行标准,为后续改进提供了清晰的函数级证据。结合四份优化级别报告的横向对比,还可以量化优化强度对 LLM 反编译成功率的系统性影响。
- 人工智能
- 大模型
- 逆向工程
- 微调
- 代码模型
【免费下载链接】LLM4Decompile
Reverse Engineering: Decompiling Binary Code with Large Language Models
相关推荐
SK²Decompile 在 BringUpBench O2 优化级别上的反编译评估报告解读:368 个函数的替换、编译与可执行率全解析
SK²Decompile 在 BringUpBench O2 优化级别上的反编译评估报告解读:368 个函数的替换、编译与可执行率全解析 本篇技术指南围绕 SK
人工智能大模型逆向工程微调代码模型SK²Decompile 在 BringUpBench 上的反编译评估:从编译到函数级验证的完整复现指南
SK²Decompile 在 BringUpBench 上的反编译评估:从编译到函数级验证的完整复现指南 本文聚焦于 LLM4Decompile 项目中 SK²
人工智能大模型逆向工程微调代码模型SK²Decompile 在 BringUpBench O0 基准上的评估报告解读:382 个函数的替换—编译—执行全链路验证
SK²Decompile 在 BringUpBench O0 基准上的评估报告解读:382 个函数的替换—编译—执行全链路验证 本篇技术指南以仓库内生成的 O0
人工智能大模型逆向工程微调代码模型
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考