Page 14 - 《软件学报》2026年第6期
P. 14
李玉璋 等: BinDec: 面向 RISC-V 的 LLM 与符号执行协同反编译方法 2333
由于 LLM 本身存在固有的不确定性, 即使其输出的 IR 代码可通过编译, 仍无法保证其与原始二进制代码在
功能上完全一致. 为确保语义一致性, BinDec 采用基于符号执行的单元测试方法对生成的 IR 进行验证. 首先, 为
提升后的 IR 函数生成一个包含 main 函数的可执行程序 (称为产品程序, 如图 3 所示). 该 main 函数包括对目标函
数参数的符号化声明、函数调用以及运行结果输出等部分, 其生成基于对目标函数的签名分析和预设的产品程序
模板. 需要注意的是, 目标函数可能调用外部函数. 由于 BinDec 以函数粒度运行, 一种直接的处理方式是按照调用
顺序依次处理各函数, 以确保外部函数已被提前反编译. 然而, 这种方式可能导致外层函数涉及复杂的调用关系,
显著增加符号执行的复杂度. 为此, 本文采用简化外部函数建模的策略, 参考已有研究 [19] 将外部函数定义为直接
返回所有参数最低字节之和. 与实际函数定义相比, 该方法大幅提升了符号执行的效率; 同时, 由于所有参数均被
使用, 避免了因路径中断而影响符号执行的准确性. 随后, 对产品程序进行符号执行, 并根据搜索到的路径生成多
个测试用例. 接下来, 将产品程序编译为 RISC-V 目标二进制程序, 并通过二进制注入技术将其中的 main 函数及
所有模拟的外部函数定义注入原始二进制程序中, 从而实现对原始二进制代码中对应函数的独立执行. 最后, 分别
运行产品程序与注入后的原始二进制程序, 通过对比不同测试用例下的函数返回值, 判断提升后的 IR 代码与原始
二进制代码的语义是否一致. 若出现返回值不一致或执行崩溃, 则判定为语义不一致, 此时丢弃当前提升结果, 重
新请求 LLM 生成 IR 代码并重复上述验证流程, 直至获得在测试集上完全等价的结果或达到最大尝试次数.
图 3 用于单元测试的产品程序示例
2.3 中间代码反编译
在基于第 2.1 节方法获得可编译且语义与原始二进制代码一致的 IR 代码后, BinDec 进一步将其反编译为 C
代码. 本节将介绍从 IR 代码反编译为 C 代码的具体过程, 主要包括 LLM 查询、C 语言代码重编译以及语义验证
这 3 个步骤.
本阶段仍以 LLM 为核心, 利用其代码生成能力实现从中间代码到高级语言表示的转换. 与二进制代码提升阶
段不同, 本阶段的输入为 IR 代码. 由于 IR 代码具有平台无关性且具备一定的可读性, 因此相比二进制代码更易
于 LLM 理解. 为进一步降低 LLM 理解 IR 代码的难度, BinDec 首先对输入的 IR 代码进行规范化处理. 具体做法
包括将变量和标签全部匿名化 (即统一表示为类似“%1”的无命名形式), 并移除所有非必要的函数和变量属性信
息 (例如用于指导代码优化的属性). 在常规编译流程中, 这类命名和属性信息通常由编译器前端生成, 以维持可读
性或指导后续优化步骤. 然而, 对于通过 LLM 从二进制代码提升得到的 IR 代码, 此类信息往往不准确甚至无意
义. 移除这些信息有助于降低 LLM 的理解负担, 并减少错误信息带来的干扰. 随后, BinDec 基于规范化后的 IR 代
码片段构造提示词, 要求 LLM 生成具备可读性的 C 代码. 对于 LLM 输出的 C 代码, BinDec 采用与二进制代码提
升阶段类似的方法进行可编译性修复, 即通过 C 编译器对代码进行编译, 并根据编译器报错信息构造提示词引导
LLM 迭代修复代码, 直至生成语法正确、可编译的 C 代码或达到预设的最大尝试次数.
为保障反编译所得 C 代码与输入 IR 代码之间的语义一致性, BinDec 在本阶段继续采用符号执行技术进行等
价性检验. 但与前一阶段不同的是, 本阶段不再依赖由符号执行生成的具体测试用例进行具体执行, 而是完全在符
号执行框架下完成判别. 该方法可减少等价性检查的步骤, 从而提升验证效率. 这一差异源于符号执行技术的适用

