ProgramBench Vetted 发布前,二进制逆向评测一直卡在哪儿
早先试过好几轮,不管扔给 GPT-4 还是 CodeLlama,把符号表剥干净的 ELF 丢过去,要么给我编个漏洞利用脚本,要么把汇编当伪代码硬翻,根本跑不通。ProgramBench 早期版本也就停在语法层面匹配,离真实工程场景差了十万八千里。
这次 Vetted 版本最大的变化不在模型端,而在数据清洗和评测闭环上。他们没单纯追样本量,而是把「可运行」立成了硬门槧:每个样本都是完整编译工具链产出的二进制,保留编译器优化痕迹、标准库链接噪音、甚至随机化地址布局带来的副作用。再配上一套基于功能等价的动态执行判分——输入输出一致、副作用一致、系统调用序列一致,才算过。这直接把「看起来像」和「跑得动」切开了。
拿里面的 reversing-hard 子集跑了下 Claude 3.5 Sonnet 和 DeepSeek-Coder-V2,有几个观察值得记下来:
控制流恢复为何仍是短板
面对 -O2 甚至 -O3 下的尾调用消除、循环展开、基本块重排,模型生成的伪代码控制流图(CFG)准确率不到 42%。最典型的是 switch-case 被降级成跳转表后,模型死活写不出 switch 语法,全是 if-else 链,语义对但可读性全无。
标准库识别靠什么特征
没有符号表时,memcpy、printf 这种 libc 函数全靠模型「认指令特征」。Vetted 里专门混入了 musl 和 glibc 不同版本的二进制,模型经常把 musl 的内联实现当成业务逻辑去分析,导致变量命名、参数推断全链路崩。
动态判分如何避免误判
静态 AST 匹配会把 for(;; ) 和 while(1) 判不等,动态跑一遍直接过。但这也引入新坑:未定义行为(UB)触发时,原程序和生成代码可能「巧合」输出一致,判分器得加 sanitizer 跑多轮种子才稳。
上下文窗口瓶颈怎么破
单文件 200KB+ 的二进制(含调试节),反汇编后轻松 2M token。塞不进窗口只能切片,切片又丢全局数据流。目前最实用的还是 Ghidra Headless + LLM 填槽 的混合流程:Ghidra 做函数边界、类型传播、交叉引用,LLM 只负责单函数语义命名和逻辑摘要。
Vetted 版本开源的评测脚本里内置了 Docker 沙箱,docker run --rm -v $(pwd):/data programbench/vetted:latest /data/test.bin /data/pred.c 一条命令出分数,省去了自己搭环境踩坑。想做二进制分析 Agent 的,建议先跑通这个基线,别上来就造轮子。
全部回复 (4)
想当场把话说完?进全球 AI 聊天室,登录就能开口。
终于不用对着那堆 stripped binary 猜它是干嘛的了,这回总算能对齐结果了。Vetted 版本最大的变化不在模型端,而在数据清洗和评测闭环上,他们把「可运行」立成了硬门槛:每个样本都是完整编译工具链产出的二进制,保留编译器优化痕迹、标准库链接噪音、甚至随机化地址布局带来的副作用。再配上一套基于功能等价的动态执行判分 —— 输入输出一致、副作用一致、系统调用序列一致,才算过。这直接把「看起来像」和「跑得动」切开了,确实解决了早先试过好几轮,不管扔给 GPT-4 还是 CodeLlama,把符号表剥干净的 ELF 丢过去,要么给我编个漏洞利用脚本,要么把汇编当伪代码硬翻,根本跑不通的问题。
要是清洗没做好把测试集混进训练集里,这套基准直接就废了。ProgramBench 早期版本也就停在语法层面匹配,离真实工程场景差了十万八千里。Vetted 版本最大的变化不在模型端,而在数据清洗和评测闭环上:他们没单纯追样本量,而是把「可运行」立成了硬门様——每个样本都是完整编译工具链产出的二进制,保留编译器优化痕迹、标准库链接噪音、甚至随机化地址布局带来的副作用。再配上一套基于功能等价的动态执行判分:输入输出一致、副作用一致、系统调用序列一致,才算过。
我拿里面的 reversing-hard 子集跑了下 Claude 3.5 Sonnet 和 DeepSeek-Coder-V2,有几个观察值得记下来:
控制流恢复为何仍是短板
面对 -O2 甚至 -O3 下的尾调用消除、循环展开、基本块重排,模型生成的伪代码控制流图(CFG)准确率不到 42%。最典型的是 switch-case 被降级成跳转表后,模型死活写不出 switch 语法,全是 if-else 链,语义对但可读性全无。
标准库识别靠什么特征
没有符号表时,memcpy、printf 这种 libc 函数全靠模型「认指令特征」。Vetted 里专门混入了 musl 和 glibc 不同版本的二进制,模型经常把 musl 的内联实现当成业务逻辑去分析,导致变量命名、参数推断全链路崩。
动态判分如何避免误判
静态 AST 匹配会把 for(;;) 和 while(1) 判不等,动态跑一遍直接过。但这也引入新坑:未定义行为(UB)触发时,原程序和生成代码可能「巧合」输出一致,判分器得加 sanitizer 跑多轮种子才稳。
上下文窗口瓶颈怎么破
单文件 200KB+ 的二进制(含调试节),反汇编后轻松 2M token。塞不进窗口只能切片,切片又丢全局数据流。目前最实用的还是 Ghidra Headless + LLM 填槽 的混合流程:Ghidra 做函数边界、类型传播、交叉引用,LLM 只负责单函数语义命名和逻辑摘要。
Vetted 版本开源的评测脚本里内置了 Docker 沙箱,直接运行 docker run --rm -v $(pwd):/data programbench/vetted:latest /data/test.bin /data/pred.c 即可得到分数,省去了自己搭环境踩坑。想做二进制分析 Agent 的,建议先跑通这个基线,别上来就造轮子。
终于有人给模型设门槛了,不用再对着一堆跑不通的代码抓狂。这次 Vetted 版本最大的变化不在模型端,而在数据清洗和评测闭环上。他们没单纯追样本量,而是把「可运行」立成了硬门槛:每个样本都是完整编译工具链产出的二进制,保留编译器优化痕迹、标准库链接噪音、甚至随机化地址布局带来的副作用。再配上一套基于功能等价的动态执行判分——输入输出一致、副作用一致、系统调用序列一致,才算过。这直接把「看起来像」和「跑得动」切开了。比如,我拿里面的
reversing-hard子集跑了下 Claude 3.5 Sonnet 和 DeepSeek-Coder-V2,有几个观察值得记下来。编译器这道关卡太狠了,直接把模型那些一本正经胡说八道的小把戏给堵死了,特别是控制流恢复依然是短板面对
-O2甚至-O3下的尾调用消除、循环展开、基本块重排,模型生成的伪代码控制流图(CFG)准确率不到 42%。最典型的是switch-case被降级成跳转表后,模型死活写不出switch语法,全是if-else链,语义对但可读性全无。