论文名称 | 发表期刊 | 发表时间 | 发表单位 |
A Survey of Binary Code Similarity | ACM Computing Surveys | 2022年 | IMDEA |
引入二进制代码基本概念、描述编译流程的扩展性以及现实中的多样性变换,明确二进制代码相似性检测(BCSD)定义。
1. 基础概念引入
二进制代码是指通过编译过程生成、可由CPU直接执行的机器码。标准的编译流程以程序的源代码文件为输入,结合所选的编译器、优化级别及特定平台(由架构、字长和操作系统定义),生成目标文件。这些目标文件随后会被链接为一个二进制程序,即独立的可执行文件或库文件。一般编译流程
2. 扩展编译流程
二进制代码相似性检测方法通常涉及一个扩展的编译流程,如上图所示,在标准编译流程的基础上增加了两个可选步骤:源代码变换和二进制代码变换。这两类变换通常是语义保持的,即不会改变程序的功能,最常用于代码混淆,以阻碍对分发的二进制程序的逆向工程。扩展编译流程
3. BCSD定义
同一份源代码在不同编译配置下可能生成不同的二进制表示。如上图所示,开发人员可以修改任意灰色模块,从而在不改变程序语义的前提下,生成多种不同的二进制程序。(1)这些差异可能源于标准编译过程中的变动,例如为了提高程序性能,开发人员可能调整编译器的优化级别,或更换不同的编译器,尽管源代码未变,但生成的二进制代码将随之变化。此外,开发人员也可能更换目标平台以适配不同的硬件架构,而若新平台使用不同的指令集,则生成的二进制代码可能差异巨大。(2)开发人员还可以主动应用混淆技术,使相同源代码生成多种语义等价但结构多样的变体。理想情况下,二进制代码相似性检测方法应能识别这些因变换而产生的不同二进制代码之间的相似性,其鲁棒性正体现为其应对各类编译和混淆变换的能力,即在这些变换存在的情况下依然能够准确检测代码相似性。
4. BCSD应用场景
应用领域 | 应用子领域 | 应用概述 | 相关研究 |
同源漏洞 | 补丁分析 | 补丁分析通过对比程序的不同版本,识别修补内容,从而实现对漏洞的有效分析。 | EXEDIFF ... |
漏洞检测 | 漏洞检测利用代码重用特性查找已知漏洞,从而发现可能重复存在相同或相似漏洞的代码片段。 | TRACY, TEDEM, MULTI-MH, DISCOVRE, ESH, GENIUS, BINGO, BINSEQUENCE, XMATCH, GITZ, GEMINI, FIRMUP, BINARM, αDIFF, VULSEEKER ... | |
恶意软件 | 恶意软件检测 | 恶意软件检测通过将给定的可执行文件与已知恶意软件样本进行二进制代码相似性比较,以识别其是否为已知恶意软件家族的变种。 | KKMRV2005, BMM2006, CXZ2014 ... |
恶意软件聚类 | 恶意软件聚类通过将相似的已知恶意可执行文件分组为家族,识别同一恶意程序的不同版本及其多态变种,重点采用基于二进制代码比较的方法。 | SMIT, MUTANTX-S, KLKI2016 ... | |
恶意软件谱系 | 恶意软件谱系通过构建程序版本图,利用版本间的演化关系,将属于同一恶意程序的可执行文件组织起来,特别适用于缺乏版本信息的恶意软件。 | BEAGLE, ILINE, MXW2015 ... | |
许可证合规性 | 合规性分析通过利用二进制代码相似性检测程序中未经授权的代码重用,如源代码被盗用、二进制代码被重复使用或未授权实现专利算法。 | COP ... |
同源漏洞:补丁生成与分析是最早期且最常见的应用之一,通过对比同一程序的两个连续或相近版本,识别新版本中的补丁修改,尤其在厂商未公开补丁细节的专有软件中具有重要意义。生成的二进制补丁体积较小,可用于高效的程序更新,并且能够自动识别修复漏洞的安全补丁、进行深入分析,甚至辅助生成未修复漏洞的利用代码。漏洞检测则通过在大型二进制代码库中查找已知漏洞对应的相似代码来进行。由于代码重用,缺陷代码片段可能出现在多个程序或同一程序的不同位置,因此,发现漏洞后,识别重用该缺陷代码的相似片段对漏洞扩散分析和补丁生成至关重要。漏洞搜索方法通常以有缺陷的二进制代码片段为查询输入,在目标代码库中寻找功能相似的片段,且跨平台漏洞搜索要求对不同平台(如 x86、ARM、MIPS)编译的代码具有更强的跨平台鲁棒性。
恶意软件:恶意软件检测,通过将待检测的可执行文件与已知恶意软件样本进行比对,若相似度较高,则该样本很可能是某一已知恶意家族的变体。相比于依赖系统或API调用行为的传统方法,BCSD更关注从代码层面识别语义相似性;恶意软件聚类,进一步将多个已知恶意样本按照二进制代码相似性划分为不同家族,每个家族通常包含同一恶意程序的不同版本及其多态变体(如加壳版本);恶意软件演化追踪,在已知属于同一恶意程序的样本集上构建版本演化图,图中每个节点代表一个程序版本,边表示其演化关系。由于恶意软件通常缺乏明确版本标识,演化追踪对于揭示其变种结构和传播路径具有重要意义,且通常依赖聚类结果作为前置步骤以确保分析的准确性。
许可证合规性分析:二进制代码相似性可用于识别对原告程序的未授权代码重用,例如源代码被窃取、二进制代码被直接复用、专利算法在未授权情况下被重新实现,或违反开源许可证(如将GPL代码用于商业软件)等情形。早期用于检测此类侵权行为的方法主要依赖软件指纹,即提取程序固有功能特征的签名。然而,如第三节所述,本文不讨论基于签名的方法,而聚焦于基于二进制代码相似性的检测技术。·
5. BCSD发展历程
BCSD起源:BCSD 技术起源于20世纪90年代,最初用于生成和分析补丁,以捕捉同一程序不同版本之间的差异。当时,文本差异工具(如UNIX diff)已广泛应用于源代码版本控制,而二进制差异工具则通过传输小型补丁提升效率,特别适用于低带宽环境。1991年,Reichenberger提出了一种字节级差异生成方法,无需理解文件结构即可识别二进制版本间的差异。1999年,Baker等人推出EXEDIFF工具,通过分析编译过程中的微小变化生成补丁以减少冗余;同年,Wang等人的BMAT工具首次聚焦于函数和基本块的比较,并采用哈希技术应对指针变化。此阶段的BCSD主要依赖语法特征,即代码的静态结构和表面相似性。
语法特征迈向语义特征:从2000年至2009年,BCSD技术从单纯的语法特征比较逐步扩展到语义特征的应用,标志着一次重要演进。2004年,Thomas Dullien提出基于图的二进制代码差异分析方法,首次解决了编译优化导致的指令重排序问题,并为BINDIFF插件奠定了基础。2005年,Kruegel等人引入图着色技术,通过按功能分类指令检测恶意软件的多态变种,开创了语义相似性与二进制代码聚类的结合。2008年,Gao等人的BINHUNT工具利用符号执行和约束求解器检查代码等价性,成为首个分析程序版本间语义差异的方法。2009年,Xin等人的SMIT工具将二进制代码搜索应用于恶意软件检测,通过图编辑距离搜索相似代码。这一阶段的研究表明,BCSD开始关注代码的行为和功能,而非仅限于静态结构。
语法特征关注代码的静态结构和组成元素,如指令序列和寄存器操作,用于识别代码的表面相似性。语义特征则侧重于代码的行为和功能,通过分析代码的执行路径和实际运行效果,判断其在特定输入下是否产生相同的输出。
传统检测迈向人工智能检测:2010至2019年间,BCSD技术取得显著进展,共发展出52种方法,研究热点集中于二进制代码搜索,尤其是跨架构版本的相似性检测。2013年,Wei等人的RENDEZVOUS工具将搜索粒度从整个程序缩小到函数级别,推动了克隆检测和漏洞搜索的应用。2014年,David等人的TRACY工具利用控制流图中的执行路径(tracelets)识别与漏洞函数相似的代码,成为首个专注漏洞搜索的方法。2015年,Pewny等人的MULTI-MH工具解决了跨架构代码搜索的难题。随着深度学习的兴起,2016年Lageman等人开始使用神经网络判断函数是否来自相同源代码,随后αDIFF、INNEREYE和ASM2VEC等人工智能方法逐渐成为主流。这一阶段标志着BCSD从传统检测向人工智能检测的转型,前者依赖规则和静态特征,后者则通过数据驱动的方式自动学习代码的语法与语义特征。
传统检测方法主要依赖于基于规则和特征的技术,关注于程序的静态结构和语法特征,如指令序列、控制流图、函数调用等。人工智能检测则采用深度学习和机器学习算法,能够基于数据驱动的方式自动学习程序的行为和特征,捕捉语法和语义层面的相似性。
论文名称 | 发表期刊 | 发表时间 | 发表单位 |
How Machine Learning Is Solving the Binary Function Similarity Problem | USENIX | 2022年 | Cisco |
6. BCSD技术路线
Direct vs. indirect comparision:两种方法的输入均为函数对,并从函数中提取特征用于相似性计算。前者直接根据提取的函数特征计算函数之间的相似性,而后者则将函数特征转换为低维向量,并使用距离度量方法计算相似性。从相似性检测精度来看,两者并没有显著区别,因为后者的低维向量表示可以结合相似性度量方法,等效于前者的函数对相似性计算。然而,由于BCSD广泛应用于基于相似性的代码搜索等任务,间接比较方法具有多个优势:
- 该方法允可以将函数的低维向量表示存储到数据库中,从而避免重复计算。
- 该方法可以结合近似最近邻等技术,实现对数据库中相似函数的高效筛选。
- 该方法通过矩阵运算,还能够实现一对多的函数相似性计算。
综上所述,间接方法相较于直接方法在扩展性上更具优势,且目前已成为研究领域的主流方法。接下来,本文将重点对间接方法进行详细介绍。
Hash vs. embedding base representation:基于哈希的表示方法通过生成低维度的哈希值来表示函数,其中模糊哈希是一种常见的技术,它的设计目的是将相似的输入映射为相似的哈希值,适用于比较函数之间的相似性。与此不同,基于嵌入的表示方法则利用机器学习技术将输入映射到一个低维度空间,其中语义相似的输入被映射为相互接近的点,能够捕捉到更深层次的函数特征。相较而言,基于哈希的方法主要依赖于函数的语法特征进行比较,而基于嵌入的方法则侧重于语义特征,具有更好的鲁棒性,特别是在面对复杂或具有变动的输入时。接下来,本文将重点对基于嵌入的方法进行详细介绍。
Graph vs. code embedding:图嵌入方法利用图神经网络(GNN)等机器学习技术,为函数的控制流图生成低维度表示,这些表示能够捕捉跨架构的特征,尤其适用于结构化数据。与此不同,代码嵌入方法通过将汇编代码视为文本,使用NLP技术(如word2vec、seq2seq或BERT)生成低维度嵌入表示,主要聚焦于捕捉代码的语法和语义特征。两者的主要差异在于,图嵌入侧重于捕捉代码的结构信息,尤其是控制流图中的节点和边的关系,而代码嵌入则更关注于代码的语法和语义层面,通常依赖于模型处理的指令流和语法结构。下面总结了两类嵌入的函数表示方法。
嵌入类型 | 函数表示 | 具体描述 |
代码嵌入 | 原始字节 | 将函数的原始字节作为输入,直接使用二进制信息进行相似性度量,或将其与其他信息(如控制流图或调用图)结合使用。 |
汇编代码 | 使用反汇编器获取的汇编指令作为输入,适用于指令大小或操作数的不同编码方式,如x86/64架构中的mov指令可能具有多种不同的操作码字节。 | |
规范化汇编代码 | 对汇编代码进行规范化,减少可能的变种,统一表示相同操作的不同变体,以简化词汇表大小并提高表示的一致性。 | |
中间表示 | 使用更高层次的抽象,即提升为中间表示(IR),能够统一表示语法不同但语义相同的指令,适应不同架构的抽象,并可应用程序分析技术简化代码结构。 | |
图嵌入 | 控制流图 | 提取函数的控制流图(CFG),作为函数的输入,以捕捉函数的执行流结构。许多方法进一步增强CFG,如通过基本块信息构建属性控制流图(ACFG)。 |
数据流图 | 提取函数的数据流图,进一步分析变量和指令之间的数据依赖关系,作为输入用于函数行为分析。 |
论文名称 | 发表期刊 | 发表时间 | 发表单位 |
Understanding the AI-powered Binary Code Similarity Detection | arxiv | 2024年 | 浙江大学 |
7. BCSD工作流程
二进制代码相似性检测(BCSD)方法的工作流程通常可划分为三个核心阶段:预处理阶段,通过标准化输入以提取稳定的低层次二进制特征;代码表示阶段,将预处理后的代码结构化为适用于神经网络建模的形式;代码嵌入阶段,利用神经网络将结构化表示转换为低维向量表示,以支持后续的相似性计算。主流的BCSD技术可以分为基于图嵌入和代码嵌入的方法,下文分别阐述这两类方法在各阶段的具体流程。
基于图嵌入的BCSD方法通常同样包含三个关键阶段。首先,在预处理阶段,该类方法常采用选择性函数内联等策略,以增强控制流图(CFG)等图结构的稳定性,从而提升图神经网络在跨编译器、跨平台场景下的泛化能力。接着,在代码表示阶段,将二进制代码构建为控制流图(CFG)、数据流图(DFG),有时还结合抽象语法树(AST)或指令序列构成复合图,以提供更丰富的结构与语义信息供图神经网络(GNN)建模。最后,在代码嵌入阶段,应用GCN、GAT、GGNN等图神经网络对图中节点进行嵌入学习,并通过全局池化方式生成函数级别的向量表示,用于计算函数对之间的相似度(如余弦相似度、欧几里得距离),或通过Siamese网络等学习方式进行相似性度量。代表方法包括GraphSim、BinGraph 与 SAFE-GNN 等。该类方法的优势在于其能够有效捕捉代码中的结构语义与数据依赖关系,然而其性能高度依赖于图结构构建的质量,在面对编译器优化和平台差异等扰动时,其鲁棒性相对较弱。
基于序列嵌入的BCSD方法亦遵循相同的三阶段流程。在预处理阶段,该类方法主要采用指令规范化操作以缓解词汇表外(OOV)问题,并统一指令格式;部分方法还引入手动特征(如指令类别统计)或指令级嵌入,增强语义表达的细粒度。在代码表示阶段,常将代码表示为指令序列或基本块序列,有时融合语法树结构或寄存器信息,以保留程序的执行语义顺序,形成更适合序列建模的输入形式。代码嵌入阶段则多采用RNN/LSTM或Transformer等顺序模型对上述序列进行建模,亦可引入CNN提取n-gram风格的局部上下文语义,最终生成函数级嵌入向量,用于相似性判定。代表方法包括INNEREYE、SAFE、ASM2Vec 与 TranSNN 等。该类方法在应对编译器优化、多样化编译选项等场景下展现出更强的鲁棒性,并能有效建模语义序列;但因其未显式利用代码结构,可能在捕捉全局结构语义方面存在一定局限。
论文名称 | 发表期刊 | 发表时间 | 发表单位 |
How Far Have We Gone in Binary Code Understanding Using Large Language Models | 2024 IEEE International Conference on Software Maintenance and Evolution | 2024年 | 中国科学院大学 |