Page 86 - 《软件学报》2025年第7期
P. 86
贾昂 等: 面向函数内联场景的二进制到源代码函数相似性检测方法 3007
一个函数. 图 2(a) 展示了二进制函数 dtls1_get_record 的控制流图. 图 2(b) 展示其所对应的源函数代码. 虚线矩形
和双头箭头表示从二进制函数到源代码函数的映射关系. 根据编译信息, 二进制函数 dtls1_get_record 是由源代码
函数 dtls1_get_record 编译而来, 并且内联了多个其他源代码函数的内容, 包括 dtls1_process_buffered_records、
dtls1_get_bitmap 和 dtls1_record_replay_check. 由于源代码函数 dtls1_get_record 包含了漏洞 CVE-2014-3571, 二进
制函数 dtls1_get_record 也继承了这一漏洞.
(a) 二进制函数 dtls1_get_record 的 CFG (b) 源函数 dtls1_get_record
图 2 函数内联场景下的“一对多”匹配问题示例
然而, 当使用现有的二进制到源代码相似性检测技术来比较二进制函数 dtls1_get_record 和源代码函数
dtls1_get_record 时, 两个函数之间的相似度低于 50%, 导致无法检测出该漏洞. 通过编译信息可以看出, 尽管二进
制函数 dtls1_get_record 是由源代码函数 dtls1_get_record 编译生成, 但其也包含了来自源代码函数 dtls1_process_
buffered_records、dtls1_get_bitmap 和 dtls1_record_replay_check 的内容. 实际上, 漏洞函数的代码仅是二进制函数
dtls1_get_record 的一部分. 此时二进制函数 dtls1_get_record 和源代码函数之间已经成为了“一对多”的映射关系.
因此, 通过现有的“一对一”匹配机制难以识别出二进制函数所复用的源代码函数.
总体而言, 在函数内联场景下进行二进制到源代码函数相似性检测面临以下 3 个挑战.
挑战 1: 超出语料库问题. 由内联生成的二进制函数通常由一个以上的源代码函数生成. 因此, 在源代码函数
中并不存在这样一个源代码函数, 其语义完全等同于查询的二进制函数. 因此, 含有内联的二进制函数在查询时面
临着超出语料库问题, 即在开源软件语料库中不存在与查询二进制函数语义完全等价的源代码函数.
挑战 2: 函数内联的不透明性. 大多数开源软件项目无法实现全自动编译 [7] . 因此即使拥有完整的源代码项目,
也很难推断函数内联将在何处发生. 这是因为内联操作并非直接在源代码上执行, 而是在编译过程中的中间语言
阶段进行, 此时源代码已经经过了预处理、转换和优化 [13] . 而内联策略决定是否进行内联的指标, 在源代码和中
间语言上已经发生了改变.
挑战 3: 二进制文件中函数内联的多样性. 二进制文件可以通过各种编译设置以及不同的内联策略生成, 这导
致二进制文件中存在着多种多样的内联结果. 在具体实现中, 几乎不可能手动总结所有这些内联模式, 这使得大规
模处理“一对多”匹配面临着巨大挑战.
2.3 O2NMatcher 方法设计
为了应对挑战 1, O2NMatcher 通过构建源代码函数集合来作为内联二进制函数的匹配对象. 源代码函数集合
不再是源代码仓库中的单一函数, 而是由初始源代码函数和参与内联的其他源代码函数组成. 例如在图 2 的例子
中, O2NMatcher 会生成一个由源函数 dtls1_get_record, dtls1_process_buffered_records, dtls1_get_bitmap, 以及
dtls1_record_replay_check 组成的源代码函数集合, 由于该源代码函数集合和二进制函数 dtls1_get_record 具有相

