基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现_第1页
基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现_第2页
基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现_第3页
基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现_第4页
基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现_第5页
已阅读5页,还剩34页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于函数控制流图比对算法的二进制可执行程序相似性分析系统的设计与实现一、引言1.1研究背景与意义1.1.1研究背景在当今数字化时代,二进制可执行程序广泛应用于各个领域,从操作系统、应用软件到嵌入式系统,无处不在。随着软件产业的快速发展,软件的规模和复杂度不断增加,二进制可执行程序的数量也呈爆炸式增长。这使得对二进制可执行程序的分析和理解变得愈发重要,而二进制可执行程序相似性分析作为其中的关键技术,在多个领域都发挥着不可或缺的作用。在恶意代码分析领域,随着网络攻击手段的日益复杂和多样化,恶意软件的数量和变种不断增加。据统计,每天都有大量新的恶意软件样本被发现。通过二进制可执行程序相似性分析,可以快速识别出恶意软件的家族关系和变种,追溯其来源和传播路径,为安全防护提供有力支持。例如,当发现一个新的恶意软件样本时,通过与已知恶意软件样本进行相似性比对,能够判断它是否属于某个已知的恶意软件家族,从而了解其可能的行为模式和危害程度,进而采取相应的防范措施。在软件漏洞发现方面,软件漏洞是导致系统安全风险的重要因素。许多软件漏洞具有相似的代码模式和特征,通过对二进制可执行程序进行相似性分析,可以发现潜在的漏洞。例如,已知某个软件存在特定类型的漏洞,通过相似性分析在其他软件中查找具有相似代码结构和行为的部分,就有可能发现类似的漏洞,从而提前进行修复,降低安全风险。此外,在软件知识产权保护、软件质量评估、软件复用等方面,二进制可执行程序相似性分析也具有重要的应用价值。在软件知识产权保护中,通过相似性分析可以检测软件是否存在抄袭行为;在软件质量评估中,可以通过对比相似软件来评估目标软件的质量;在软件复用中,相似性分析有助于找到可复用的代码模块,提高软件开发效率。当前软件市场中,对程序相似性分析的需求也日益迫切。一方面,软件开发者需要对自己开发的软件进行相似性分析,以确保软件的原创性和质量,避免潜在的知识产权纠纷;另一方面,软件安全检测机构和企业需要通过相似性分析来保障软件系统的安全,及时发现恶意软件和软件漏洞。然而,现有的二进制可执行程序相似性分析方法还存在诸多不足,如准确性不高、效率低下、对复杂情况的适应性差等,难以满足实际应用的需求。因此,研究更加高效、准确的二进制可执行程序相似性分析方法具有重要的现实意义。1.1.2研究意义本研究基于函数控制流图比对算法的二进制可执行程序相似性分析系统,具有多方面的应用价值。在代码质量评估方面,通过对二进制可执行程序的相似性分析,可以对比不同版本软件或不同开发者编写的相似功能模块的代码结构和逻辑。如果发现某些部分的相似度异常高,可能意味着存在代码抄袭或不合理的代码复用,这有助于评估代码的创新性和质量。例如,在一个大型软件项目中,通过相似性分析发现多个模块的代码相似度极高,进一步调查可能发现是由于开发人员为了节省时间而直接复制粘贴代码,这可能导致代码维护困难和潜在的漏洞风险,从而为代码质量改进提供方向。在恶意代码检测领域,该系统能够快速准确地识别恶意软件及其变种。当新的二进制程序进入检测范围时,通过与已知恶意软件样本的函数控制流图进行比对,判断其相似程度。如果相似度超过一定阈值,就可以初步判定该程序可能为恶意软件,为后续的深入分析和处理提供依据。这大大提高了恶意代码检测的效率和准确性,有助于及时防范网络攻击,保护用户的信息安全和系统稳定。软件版权保护是软件产业发展的重要保障。本研究的相似性分析系统可以用于检测软件中是否存在未经授权的代码复用或抄袭行为。通过将待检测软件与大量已有的软件进行相似性比对,能够发现潜在的侵权行为。例如,某软件公司怀疑其竞争对手的软件抄袭了自己的部分代码,利用该系统进行分析,若发现两者的函数控制流图存在高度相似的部分,且排除了合理的代码复用情况,就可以为软件版权纠纷提供有力的证据,维护软件开发者的合法权益。1.2国内外研究现状在二进制可执行程序相似性分析领域,国内外学者基于函数控制流图比对算法展开了广泛而深入的研究,取得了一系列有价值的成果。早期的研究主要侧重于基于传统图匹配算法的应用。例如,一些学者采用经典的K子图匹配算法来计算函数控制流图之间的相似性。这种方法通过寻找两个图中最大的相似子图来衡量整体的相似程度,能够在一定程度上识别出具有相似结构的二进制程序。然而,K子图匹配算法的计算复杂度较高,随着图规模的增大,计算效率会显著降低,难以满足大规模二进制程序分析的需求。随着机器学习技术的兴起,基于机器学习的函数控制流图比对方法逐渐成为研究热点。腾讯安全科恩实验室提出的Gemini算法,创新性地将图神经网络应用于控制流图分析。它将二进制函数的控制流图作为输入,利用人工设计的特征提取方法将每个基本块表示成低维向量,再通过Structure2vec算法计算图嵌入,最后使用孪生网络计算相似度得分。与传统方法相比,Gemini在速度和准确率上都有了大幅提升。然而,该算法在特征提取过程中采用人工设计的方式,会损失部分语义信息,并且对节点顺序信息的提取能力有限。为了弥补Gemini算法的不足,后续研究提出了改进的模型。如“OrderMatters:Semantic-AwareNeuralNetworksforBinaryCodeSimilarityDetection”论文中提出的方法,设计了包含semantic-aware模块、structural-aware模块以及order-aware模块的总体框架。在semantic-aware模块,使用BERT对控制流图进行预训练,以提取更丰富的语义信息;structural-aware模块利用MPNN算法得到图的语义和结构嵌入;order-aware模块通过将控制流图的邻接矩阵作为输入,使用CNN计算图的顺序嵌入。这种方法在语义信息和节点顺序信息的提取方面取得了较好的效果,相较于Gemini算法,准确率有了进一步提升。国内在该领域也有不少研究成果。一些研究团队针对不同的应用场景和需求,对函数控制流图比对算法进行了优化和改进。例如,有的研究结合了静态分析和动态分析技术,在提取函数控制流图时,不仅考虑程序的静态结构,还通过动态执行获取程序的运行时信息,从而更全面地描述程序的行为特征,提高相似性分析的准确性。尽管国内外在基于函数控制流图比对算法的二进制可执行程序相似性分析方面取得了一定进展,但现有方法仍存在一些不足之处。部分方法对特定的编译器、优化级别或指令架构具有较强的依赖性,在跨平台、跨编译器的情况下,相似性分析的准确性会受到较大影响;一些基于机器学习的方法需要大量的标注数据进行训练,而获取高质量的标注数据往往成本较高且耗时费力;此外,对于复杂的二进制程序,如经过混淆处理的程序,现有的方法在相似性分析时还面临着较大的挑战。1.3研究目标与内容本研究旨在设计并实现一个基于函数控制流图比对算法的二进制可执行程序相似性分析系统,具体目标如下:实现高效准确的二进制反汇编功能,能够将二进制可执行程序转换为汇编代码,为后续的函数控制流图提取提供基础。设计并实现一种先进的函数控制流图比对算法,该算法能够充分考虑函数的结构、语义以及指令序列等多方面信息,准确计算函数控制流图之间的相似性,提高二进制可执行程序相似性分析的准确率。开发一个用户友好的相似性分析系统,具备直观的界面和便捷的操作流程,方便用户输入待分析的二进制程序,并快速获取相似性分析结果,结果应包括相似程度的量化值以及相似部分的可视化展示。对所实现的系统进行全面的性能评估和测试,验证其在不同场景下的有效性和可靠性,确保系统能够满足实际应用的需求。围绕上述研究目标,本研究的主要内容包括:深入研究二进制反汇编技术,分析不同架构二进制程序的反汇编原理和方法,选择并优化适合本系统的反汇编工具,实现对多种类型二进制可执行程序的准确反汇编。详细研究函数控制流图的构建和表示方法,探索如何从反汇编后的汇编代码中提取函数控制流图,并对其进行有效的编码和表示,以便于后续的比对分析。重点研究函数控制流图比对算法,对比现有算法的优缺点,结合实际需求,提出一种改进的比对算法。该算法应综合考虑图的结构相似性、节点属性相似性以及边的权重等因素,通过合理的数学模型和计算方法,准确度量函数控制流图之间的相似程度。基于上述研究成果,使用合适的编程语言和开发框架,设计并实现二进制可执行程序相似性分析系统。系统应包括二进制文件预处理模块、函数控制流图提取模块、比对算法实现模块以及结果展示模块等,各模块之间应具有良好的交互性和协同性。收集大量的二进制程序样本,包括正常程序、恶意程序以及不同版本、不同编译器生成的程序等,组成测试数据集。使用该数据集对系统进行全面的测试,评估系统的性能指标,如准确率、召回率、运行时间等,并根据测试结果对系统进行优化和改进。1.4研究方法与技术路线本研究采用多种研究方法和技术路线来实现基于函数控制流图比对算法的二进制可执行程序相似性分析系统。在二进制反汇编技术方面,选用成熟的反汇编工具,如IDAPro、Ghidra等。这些工具具有强大的反汇编功能,能够支持多种指令架构和操作系统平台。同时,对工具进行二次开发和优化,以满足本系统对反汇编结果的特定需求。例如,通过编写插件来增强反汇编工具对函数识别和提取的准确性,确保能够完整地获取二进制程序中的函数信息,为后续的函数控制流图构建提供可靠的数据基础。函数控制流图比对算法是本研究的核心内容。首先,对现有的图比对算法进行深入研究和分析,包括基于子图同构的算法、基于图嵌入的算法以及基于机器学习的算法等。然后,结合二进制可执行程序的特点和相似性分析的需求,提出一种改进的算法。该算法将综合运用图论、机器学习和自然语言处理等多领域的技术。具体来说,利用图论中的最短路径算法和拓扑排序算法来分析函数控制流图的结构特征;借助机器学习中的神经网络算法,如卷积神经网络(CNN)和循环神经网络(RNN),学习函数控制流图的语义特征;引入自然语言处理中的词向量模型,如Word2Vec和GloVe,对指令序列进行向量化表示,从而更全面地捕捉函数之间的相似性。为了直观地展示相似性分析结果,本研究采用可视化展示技术。使用图形化库,如Graphviz、D3.js等,将函数控制流图以及相似性比对结果以图形的形式呈现给用户。通过不同的颜色、线条粗细和节点形状等视觉元素,清晰地展示相似部分和差异部分,使用户能够快速理解分析结果。例如,将相似的函数控制流图节点用相同颜色标记,相似的边用较粗的线条表示,便于用户直观地观察和比较。整个研究过程遵循以下技术路线:首先进行需求分析和相关技术调研,明确系统的功能需求和性能指标,了解二进制可执行程序相似性分析领域的最新研究成果和技术发展趋势;然后进行系统设计,包括整体架构设计、模块划分和算法设计等;接着进行系统实现,使用选定的编程语言和开发工具,按照设计方案实现各个功能模块;在系统实现完成后,进行全面的测试和验证,使用测试数据集对系统进行功能测试、性能测试和稳定性测试等,根据测试结果对系统进行优化和改进;最后对研究成果进行总结和评估,撰写研究报告和学术论文,展示研究成果和创新点。二、相关理论与技术基础2.1二进制可执行程序概述2.1.1二进制可执行程序结构二进制可执行程序是计算机能够直接运行的程序形式,其结构复杂且严谨,包含多个重要组成部分,各部分在程序的运行过程中发挥着独特而关键的作用。代码段,也被称为文本段,是二进制可执行程序中存放程序执行代码的区域。它包含了一系列的机器指令,这些指令是程序逻辑的具体体现,控制着程序的执行流程和操作。代码段通常具有只读属性,这一特性有效地防止了程序在运行过程中代码被意外修改,从而保证了程序执行的稳定性和正确性。例如,在一个简单的C语言程序中,函数的定义和实现部分就被编译成机器指令存放在代码段中。当程序运行时,计算机的处理器会从代码段中读取这些指令,并按照指令的顺序依次执行,实现程序的各种功能。数据段用于存储程序在运行过程中需要使用的全局变量和静态变量。这些变量在程序的整个生命周期内都存在,并且其值可以在程序运行过程中被修改。数据段又可进一步细分为已初始化数据段和未初始化数据段。已初始化数据段存放的是那些在程序源代码中已经被赋予初始值的全局变量和静态变量,它们在程序加载到内存时就已经具有确定的值;而未初始化数据段则存放的是未被显式初始化的全局变量和静态变量,它们在程序运行时会被自动初始化为默认值,通常为0或空值。以一个包含全局变量的C语言程序为例,若定义了一个全局变量intglobalVar=10;,那么globalVar的值10就会被存储在已初始化数据段中;若定义了一个静态变量staticintstaticVar;,在未对其初始化时,它会被存储在未初始化数据段中,运行时被初始化为0。栈段是程序运行时用于存储函数调用过程中的局部变量、函数参数、返回地址等信息的区域。栈是一种后进先出(LIFO)的数据结构,当一个函数被调用时,会在栈顶为该函数分配一块栈帧空间,用于存放该函数的局部变量和参数。函数执行完毕后,其栈帧空间会被释放,栈顶指针会恢复到调用该函数之前的位置。例如,在一个函数调用链中,当函数A调用函数B时,函数B的局部变量和参数会被压入栈顶,函数B执行完成返回后,这些局部变量和参数会从栈顶弹出,栈顶指针下移,恢复到函数A调用函数B之前的状态。栈段的这种动态分配和释放机制,使得程序能够高效地管理函数调用过程中的数据。堆段是程序在运行时用于动态内存分配的区域。与栈段不同,堆段的内存分配和释放是由程序员通过特定的函数(如C语言中的malloc和free函数)来控制的。堆段的内存分配相对灵活,可以根据程序的实际需求在运行时动态地申请和释放内存空间。例如,当程序需要创建一个动态数组时,就可以通过malloc函数在堆段中申请一块连续的内存空间来存储数组元素。在使用完该数组后,再通过free函数释放这块内存,以便其他部分的程序可以重新使用。堆段的存在使得程序能够更好地处理那些在编译时无法确定大小的数据结构。2.1.2二进制程序执行原理二进制程序在计算机中的执行是一个复杂而有序的过程,涉及多个关键环节,包括加载、指令执行等,这些环节紧密协作,确保程序能够正确运行。当用户启动一个二进制程序时,操作系统首先会将程序从存储设备(如硬盘)加载到内存中。在加载过程中,操作系统会根据二进制程序的文件格式(如Windows系统下的PE格式或Linux系统下的ELF格式)解析程序的头部信息,获取程序的入口地址、所需的内存空间、依赖的动态链接库等关键信息。然后,操作系统会为程序分配一块足够大的内存区域,并将程序的代码段、数据段、栈段和堆段等部分按照其在文件中的布局映射到内存中相应的位置。同时,操作系统还会初始化程序运行所需的环境,如设置栈指针、初始化寄存器等。例如,在Linux系统中,当执行一个ELF格式的二进制程序时,execve系统调用会负责将程序加载到内存中,并完成一系列的初始化工作,使得程序可以在内存中准备执行。程序加载到内存后,计算机的处理器会从程序的入口地址开始读取指令并执行。处理器按照指令的顺序依次从内存中取出指令,对指令进行译码,解析出指令的操作码和操作数,然后根据操作码执行相应的操作。操作数可以是寄存器中的值、内存中的数据或者立即数。在执行过程中,处理器会使用各种寄存器来暂存数据和中间结果,如通用寄存器(如x86架构中的EAX、EBX等)用于存储整数数据,浮点寄存器(如x87架构中的ST0-ST7)用于存储浮点数数据。例如,对于一条简单的加法指令ADDEAX,EBX,处理器会首先从寄存器EAX和EBX中取出操作数,将它们相加,然后将结果存储回EAX寄存器中。在程序执行过程中,还会涉及到函数调用和跳转等操作。当遇到函数调用指令时,处理器会将当前的指令地址(即返回地址)压入栈中,然后跳转到被调用函数的入口地址执行。被调用函数执行完毕后,会从栈中弹出返回地址,将控制权交还给调用函数,继续执行调用函数中后续的指令。跳转指令则用于改变程序的执行流程,根据条件判断的结果跳转到不同的地址执行。例如,在C语言中,if-else语句和switch语句在编译后会生成相应的跳转指令,根据条件判断的结果决定程序的执行路径。2.2函数控制流图理论2.2.1控制流图定义与表示控制流图(ControlFlowGraph,CFG)是一种用于表示程序执行流程的有向图,它在程序分析和理解中起着至关重要的作用。控制流图的定义基于程序的基本块,基本块是指程序中一段顺序执行的代码,其中没有跳转指令(除了基本块末尾的跳转指令),也没有跳转目标。控制流图中的节点表示基本块,边表示基本块之间的控制流转移关系。具体来说,控制流图可以定义为一个四元组G=(N,E,n_{entry},n_{exit}),其中:N是节点集合,每个节点n_i代表一个基本块;E是边集合,边(n_i,n_j)\inE表示从基本块n_i执行完毕后可能转移到基本块n_j;n_{entry}是入口节点,代表程序的入口基本块,程序执行从该节点开始;n_{exit}是出口节点,代表程序的出口基本块,程序执行结束于该节点。在控制流图中,边的表示方式有多种,常见的是使用有向线段连接两个相关的节点。边的方向表示控制流的转移方向,从源节点指向目标节点。例如,若基本块A执行完毕后可能直接跳转到基本块B,则在控制流图中存在一条从代表基本块A的节点到代表基本块B的节点的有向边。节点通常用图形元素(如圆形、矩形等)表示,每个节点内部可以标注该基本块的一些信息,如基本块的起始地址、结束地址、包含的指令数量等。控制流图能够清晰地反映程序的执行流程,通过分析控制流图,可以直观地了解程序中各个基本块之间的关系,以及程序可能的执行路径。例如,对于一个包含条件判断和循环结构的程序,其控制流图会呈现出复杂的分支和循环结构,通过观察控制流图,可以快速定位到程序中的关键逻辑部分,如条件判断节点和循环节点,从而更好地理解程序的功能和行为。2.2.2函数控制流图构建方法构建函数控制流图是进行二进制可执行程序相似性分析的关键步骤,目前有多种工具和方法可用于实现这一目标,其中IDAPro和Ghidra是两款广泛使用的二进制分析工具。IDAPro是一款功能强大的交互式反汇编工具,它支持多种操作系统和指令集架构,能够对二进制程序进行深度分析。使用IDAPro构建函数控制流图的一般步骤如下:加载二进制程序:将待分析的二进制程序加载到IDAPro中,IDAPro会自动识别程序的文件格式,并对程序进行初步解析。反汇编:IDAPro会对二进制程序进行反汇编,将机器指令转换为汇编代码,同时分析程序中的函数、变量等信息。在反汇编过程中,IDAPro会利用其内置的分析算法和数据库,尽可能准确地识别出程序中的各种指令和数据结构。识别基本块:IDAPro通过分析汇编代码中的跳转指令,将程序划分为多个基本块。基本块是程序中一段顺序执行的代码,其起始地址通常是跳转指令的目标地址,结束地址是下一个跳转指令的起始地址或者程序的结束地址。构建控制流图:根据识别出的基本块和它们之间的跳转关系,IDAPro会自动构建函数控制流图。在构建过程中,IDAPro会为每个基本块创建一个节点,并根据跳转关系在节点之间添加有向边,从而形成完整的控制流图。用户可以在IDAPro的图形界面中直观地查看和分析控制流图,通过点击节点和边,可以查看基本块的详细信息和控制流转移条件。Ghidra是美国国家安全局(NSA)开发并开源的一款逆向工程工具,它也具备强大的二进制分析功能。使用Ghidra构建函数控制流图的步骤与IDAPro类似,但在具体操作和功能上略有差异:导入二进制文件:将二进制程序导入到Ghidra中,Ghidra会对文件进行解析,识别其格式和架构信息。分析程序:Ghidra会对二进制程序进行全面的分析,包括反汇编、符号识别、函数分析等。在分析过程中,Ghidra会利用其丰富的插件和算法,尽可能准确地还原程序的结构和逻辑。生成控制流图:Ghidra根据分析结果,自动生成函数控制流图。用户可以在Ghidra的界面中查看和操作控制流图,通过不同的视图和工具,深入了解程序的执行流程和结构。Ghidra还提供了一些高级功能,如对控制流图进行优化、合并重复节点等,以提高分析效率和准确性。2.3相似性度量算法基础2.3.1常见相似性度量方法在二进制可执行程序相似性分析中,相似性度量方法起着关键作用,它用于量化两个程序或程序片段之间的相似程度。常见的相似性度量方法有多种,每种方法都基于不同的原理,适用于不同的场景。欧氏距离是一种常用的基于空间距离的相似性度量方法。对于两个向量X=(x_1,x_2,\cdots,x_n)和Y=(y_1,y_2,\cdots,y_n),它们之间的欧氏距离定义为:d(X,Y)=\sqrt{\sum_{i=1}^{n}(x_i-y_i)^2}。欧氏距离的原理是计算两个向量在多维空间中的几何距离,距离越小,表示两个向量越相似。在二进制可执行程序相似性分析中,若将程序的某些特征(如指令序列、函数调用次数等)表示为向量,就可以使用欧氏距离来衡量两个程序之间的相似程度。例如,对于两个程序的指令序列向量,通过计算它们的欧氏距离,可以判断这两个程序在指令层面的相似性。欧氏距离适用于数据分布较为均匀、特征之间相互独立的场景,在处理简单的数值型数据时表现较好。余弦相似度是一种基于向量夹角余弦值的相似性度量方法。对于两个非零向量X和Y,它们之间的余弦相似度定义为:\cos(X,Y)=\frac{X\cdotY}{\|X\|\|Y\|},其中X\cdotY表示向量X和Y的点积,\|X\|和\|Y\|分别表示向量X和Y的模。余弦相似度的取值范围在[-1,1]之间,值越接近1,表示两个向量的方向越相似,即两个向量所代表的数据越相似。在二进制可执行程序分析中,当关注程序的特征向量之间的方向一致性时,余弦相似度非常适用。例如,在比较两个程序的函数调用关系向量时,余弦相似度可以有效地衡量它们在函数调用结构上的相似程度,而不依赖于向量的长度(即特征的数量)。编辑距离,也称为莱文斯坦距离(LevenshteinDistance),是一种用于衡量两个字符串之间差异程度的方法。它的原理是计算将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数(包括插入、删除和替换)。编辑距离越小,两个字符串越相似。在二进制可执行程序相似性分析中,若将程序的指令序列或其他文本形式的特征看作字符串,就可以使用编辑距离来度量它们的相似性。例如,对于两个程序的汇编代码字符串,通过计算编辑距离,可以判断它们在代码层面的相似程度,这种方法对于检测代码的微小变化和抄袭行为非常有效。编辑距离适用于处理字符串类型的数据,在文本分析、生物信息学等领域也有广泛应用。2.3.2基于图的相似性度量在函数控制流图比对中,基于图的相似性度量方法具有重要的应用价值,它能够充分考虑程序的结构信息,更准确地衡量程序之间的相似程度。常见的基于图的相似性度量方法包括子图同构和图编辑距离等。子图同构是指在两个图中,寻找一个图的子图与另一个图完全同构的情况。对于函数控制流图而言,如果两个函数控制流图存在同构的子图,那么可以认为这两个函数在结构和逻辑上具有一定的相似性。判断子图同构是一个NP完全问题,其计算复杂度较高,随着图规模的增大,计算时间会呈指数级增长。为了降低计算复杂度,实际应用中通常采用一些近似算法或启发式算法。例如,可以通过限制子图的大小、采用贪心策略等方法来寻找近似的同构子图。在寻找子图同构时,不仅要考虑图的节点和边的拓扑结构,还可以结合节点和边的属性信息(如基本块的指令特征、控制流转移条件等),以提高相似性判断的准确性。图编辑距离是另一种重要的基于图的相似性度量方法,它通过计算将一个图转换为另一个图所需的最少编辑操作次数(包括节点删除、节点插入、边删除、边插入和节点标签替换、边标签替换等)来衡量两个图的相似程度。图编辑距离的计算过程相对复杂,需要考虑各种可能的编辑操作组合。在函数控制流图比对中,图编辑距离能够更全面地反映两个图之间的差异,因为它不仅考虑了图的拓扑结构,还考虑了节点和边的属性变化。例如,当两个函数控制流图的拓扑结构相似,但部分节点的指令序列或边的控制流转移条件有所不同时,图编辑距离可以通过适当的编辑操作来量化这种差异。为了提高图编辑距离的计算效率,研究人员提出了多种优化算法,如基于匈牙利算法的近似算法、基于启发式搜索的算法等,这些算法在实际应用中取得了较好的效果。三、系统设计3.1系统总体架构设计3.1.1系统功能模块划分本系统主要由二进制程序预处理、控制流图构建、函数控制流图提取、相似性匹配、可视化报告生成等模块组成,各模块相互协作,共同完成二进制可执行程序相似性分析任务。二进制程序预处理模块负责将二进制程序转换为便于后续分析的形式。它使用IDAPro等反汇编工具将二进制程序反汇编为汇编代码,并对汇编代码进行一系列处理和转换,提取出程序中的关键信息,如指令序列、函数调用关系等。例如,对于一个Windows系统下的可执行文件,该模块能够将其复杂的二进制机器码转换为可读性相对较高的汇编指令,为后续模块提供基础数据。控制流图构建模块根据预处理后的信息,利用自研的IR构建工具和CG构建工具分别生成中间表示控制流图(IRCFG)和调用图控制流图(CGCFG)。IRCFG从程序的中间表示形式出发,反映了程序内部的控制流逻辑;CGCFG则侧重于展示函数之间的调用关系和控制流转移。以一个包含多个函数调用和复杂条件判断的程序为例,IRCFG能够详细呈现每个函数内部的基本块之间的控制流走向,而CGCFG则清晰展示了各个函数之间的调用层级和顺序。函数控制流图提取模块从IRCFG和CGCFG中准确识别和提取出函数控制流图。它通过分析图中的节点和边的特征,确定每个函数的边界和内部结构,将函数控制流图从整体的控制流图中分离出来。在提取过程中,会综合考虑函数的入口和出口节点、基本块之间的跳转关系等因素,确保提取的函数控制流图准确反映函数的执行流程。相似性匹配模块采用基于子图匹配的相似度度量算法,对提取出的函数控制流图进行相似度计算。该算法充分考虑函数控制流图的结构、节点属性和边的权重等多方面因素,通过寻找最大相似子图等方法,准确度量两个函数控制流图之间的相似程度。例如,对于两个具有相似功能的函数控制流图,该模块能够通过细致的算法分析,给出它们之间的相似度量化值,为相似性判断提供依据。可视化报告生成模块使用matplotlib等可视化工具,将相似性分析结果以直观的图形、图表等形式展示出来,并生成详细的分析报告。通过不同的颜色、线条粗细和节点形状等视觉元素,清晰地展示相似部分和差异部分,使用户能够快速理解分析结果。报告中还会包含相似度结果、分析过程等详细内容,为用户提供全面的分析信息。例如,将相似的函数控制流图节点用相同颜色标记,相似的边用较粗的线条表示,便于用户直观地观察和比较。这些模块之间存在紧密的依赖关系和数据传递。二进制程序预处理模块的输出是控制流图构建模块的输入,控制流图构建模块生成的IRCFG和CGCFG又为函数控制流图提取模块提供数据,函数控制流图提取模块的结果则作为相似性匹配模块的输入,最后相似性匹配模块的结果由可视化报告生成模块进行展示和报告生成。3.1.2系统流程设计系统的整体流程从输入二进制程序开始,到输出相似性分析结果结束,涵盖了多个关键步骤,具体流程如下:用户将待分析的二进制程序输入到系统中。二进制程序可以是各种类型的可执行文件,如Windows系统下的.exe文件、Linux系统下的ELF文件等。二进制程序预处理模块启动,使用IDAPro等反汇编工具对二进制程序进行反汇编,将其转换为汇编代码。反汇编过程中,工具会根据二进制程序的文件格式和指令集架构,识别出程序中的机器指令,并将其转换为相应的汇编指令。例如,对于x86架构的二进制程序,IDAPro会将机器码转换为x86汇编指令。然后,对反汇编得到的汇编代码进行处理和转换,提取出指令序列、函数调用关系等有用信息。控制流图构建模块利用自研的IR构建工具,将二进制程序转换为中间表示形式,并生成IRCFG。IR构建工具会对预处理后的信息进行分析和处理,将程序的逻辑结构转换为中间表示,进而生成反映程序内部控制流的IRCFG。同时,利用CG构建工具,基于函数调用关系生成CGCFG。CG构建工具通过分析函数调用指令和相关信息,构建出展示函数之间调用关系的CGCFG。函数控制流图提取模块从IRCFG和CGCFG中识别和提取出函数控制流图。该模块会遍历IRCFG和CGCFG,根据函数的定义和特征,确定函数的边界和内部结构,提取出每个函数的控制流图。在提取过程中,会对函数控制流图进行初步的优化和简化,去除一些冗余信息,提高后续处理效率。相似性匹配模块采用基于子图匹配的相似度度量算法,对提取出的函数控制流图进行相似度计算。该算法会将两个函数控制流图进行比对,通过寻找最大相似子图、计算节点和边的相似度等步骤,得出两个函数控制流图之间的相似度值。可视化报告生成模块使用matplotlib等可视化工具,将相似性分析结果以直观的图形、图表等形式展示出来。例如,生成函数控制流图的对比图,用不同颜色区分相似和不同部分;生成相似度矩阵图表,直观展示多个函数控制流图之间的相似度关系。同时,生成详细的相似性分析报告,报告中包含相似度结果、分析过程、相似部分和差异部分的详细描述等内容,并将报告输出给用户。用户可以通过查看报告和可视化结果,快速了解二进制可执行程序之间的相似性情况。3.2二进制程序预处理模块设计3.2.1二进制程序反汇编二进制程序反汇编是将二进制机器码转换为汇编代码的过程,这是二进制可执行程序分析的基础步骤,在本系统中具有至关重要的作用。本系统选用IDAPro作为反汇编工具,IDAPro是一款功能强大的交互式反汇编工具,支持多种操作系统和指令集架构,能够对二进制程序进行深度分析。IDAPro反汇编二进制程序的过程基于其内置的复杂算法和丰富的指令数据库。首先,IDAPro加载二进制程序,识别程序的文件格式,如Windows系统下的PE(PortableExecutable)格式或Linux系统下的ELF(ExecutableandLinkableFormat)格式。根据文件格式,IDAPro解析程序的头部信息,获取程序的入口地址、代码段和数据段的位置及大小等关键信息。然后,IDAPro从程序的入口地址开始,按照指令集架构的规则,逐字节地解析二进制数据。对于每个字节或字节序列,IDAPro在其指令数据库中查找对应的操作码(opcode)和操作数模式。操作码决定了指令的功能,如加法、减法、跳转等;操作数模式则决定了操作数的获取方式和类型,操作数可以是寄存器、内存地址或立即数。例如,在x86架构中,操作码“0x8B”通常表示数据传送指令(MOV),后面的字节会根据操作数模式来确定具体的操作数。在解析过程中,IDAPro还会处理指令之间的依赖关系和跳转指令。对于跳转指令,IDAPro会记录跳转的目标地址,并在后续分析中从目标地址继续解析。通过这种方式,IDAPro逐步构建出完整的汇编代码清单,将二进制程序的执行逻辑以汇编语言的形式呈现出来。反汇编原理基于计算机体系结构和指令集的知识。不同的指令集架构,如x86、ARM、MIPS等,具有不同的指令格式和编码规则。以x86架构为例,其指令长度可变,从1字节到15字节不等,操作码和操作数的组合方式复杂多样。反汇编工具需要准确理解这些规则,才能正确地将二进制机器码转换为汇编代码。同时,反汇编过程还需要考虑指令的寻址方式,如直接寻址、间接寻址、寄存器间接寻址等,以正确解析操作数的实际值。3.2.2汇编代码处理与转换在使用IDAPro完成二进制程序的反汇编,得到汇编代码后,需要对汇编代码进行进一步的处理和转换,以提取出对后续分析有用的信息。首先,进行指令序列提取。汇编代码由一系列的指令组成,通过对汇编代码的逐行解析,提取出每条指令的操作码和操作数,将其存储为指令序列。例如,对于汇编代码“MOVEAX,[EBX+0x10]”,提取出操作码“MOV”和操作数“EAX”以及“[EBX+0x10]”,并将其按照顺序存储,形成指令序列。这一指令序列能够反映程序的基本执行步骤,为后续分析程序的逻辑提供基础数据。其次,函数调用关系分析是关键步骤。汇编代码中通过特定的指令,如“CALL”指令来表示函数调用。通过扫描汇编代码,识别出所有的“CALL”指令,并记录下调用的目标函数地址。同时,分析函数调用前后的寄存器状态和栈操作,确定函数的参数传递方式和返回值处理方式。例如,在x86架构中,函数参数通常通过寄存器或栈来传递,通过分析“CALL”指令前后的寄存器赋值和栈操作指令,可以准确确定参数的传递方式。这样,就能够构建出函数调用关系图,清晰展示程序中各个函数之间的调用层级和顺序,这对于理解程序的整体结构和功能具有重要意义。此外,还需要进行符号信息提取。汇编代码中可能包含一些符号,如函数名、变量名等,这些符号能够帮助理解程序的含义。IDAPro在反汇编过程中会尝试识别和解析这些符号,并将其与相应的地址关联起来。通过提取符号信息,可以在后续分析中使用有意义的符号来代替地址,提高分析的可读性。例如,将函数地址替换为函数名,使分析人员能够更直观地了解函数的功能和作用。为了提高处理效率和准确性,可以采用一些优化策略。例如,使用正则表达式来快速匹配和提取特定格式的指令和符号;利用数据结构,如哈希表来存储和查找函数地址和符号信息,减少查找时间。通过对汇编代码的处理和转换,提取出的指令序列、函数调用关系和符号信息等有用信息,为后续的控制流图构建和相似性分析提供了丰富的数据基础,有助于更深入地理解二进制可执行程序的内部结构和逻辑。3.3控制流图构建模块设计3.3.1IRCFG生成IRCFG(IntermediateRepresentationControlFlowGraph)的生成是控制流图构建模块的重要环节,它基于自研的IR构建工具,将二进制程序转换为中间表示形式,并在此基础上生成反映程序控制流的图结构。IR构建工具首先对二进制程序预处理模块提供的汇编代码进行语义分析。通过对汇编指令的理解和分析,将其转换为一种中间表示形式,这种中间表示形式通常具有更简洁、统一的结构,便于后续的处理和分析。例如,将不同指令集架构的汇编指令统一转换为一种基于三地址码的中间表示。三地址码的格式为每个语句右侧只有一个操作符,如语句“a=b+c+2”会转换为“t1=b+c;a=t1+2”,其中“t1”是临时变量。这种转换使得程序的逻辑结构更加清晰,便于提取控制流信息。在生成中间表示后,IR构建工具开始构建IRCFG。它将中间表示中的基本块作为IRCFG的节点。基本块是程序中一段顺序执行的代码,没有跳转指令(除了基本块末尾的跳转指令),也没有跳转目标。通过分析中间表示中的跳转指令,确定基本块之间的控制流转移关系,将这些关系作为IRCFG的边。例如,若中间表示中存在一条跳转指令“if(condition)gotolabel”,则表示从当前基本块到目标基本块(以“label”标识)存在一条控制流边。在构建过程中,还会为每个节点和边添加属性信息。节点属性可以包括基本块的起始地址、结束地址、包含的指令数量、指令序列的哈希值等,这些属性有助于在后续分析中快速识别和比较基本块。边属性可以包括跳转条件、跳转类型(如条件跳转、无条件跳转)等,这些属性能够更准确地反映控制流转移的条件和方式。生成IRCFG的算法核心在于对中间表示的准确分析和对控制流转移的正确识别。通过对跳转指令的深度解析,结合基本块的定义,构建出完整的控制流图。例如,采用深度优先搜索(DFS)或广度优先搜索(BFS)算法,从程序的入口基本块开始,遍历所有的基本块和控制流边,确保图的完整性和准确性。这种基于中间表示生成的IRCFG,能够更清晰地反映程序的内部控制流逻辑,为后续的函数控制流图提取和相似性分析提供了重要的基础。3.3.2CGCFG生成CGCFG(CallGraphControlFlowGraph)主要用于展示函数之间的调用关系和控制流转移,其生成基于函数调用关系,通过CG构建工具来实现。CG构建工具首先从二进制程序预处理模块提取的函数调用关系信息入手。在汇编代码中,函数调用通过特定的指令(如“CALL”指令)来实现,CG构建工具会扫描汇编代码,识别出所有的函数调用指令,并记录下调用者函数和被调用者函数的地址。然后,以函数为节点,以函数调用关系为边,构建CGCFG的初步结构。对于每一个函数调用,在图中添加一条从调用者函数节点到被调用者函数节点的有向边。例如,若函数A调用函数B,则在CGCFG中添加一条从代表函数A的节点到代表函数B的节点的有向边。在构建过程中,还会考虑函数的递归调用情况。对于递归函数,会在CGCFG中形成一个闭环结构。同时,为了更准确地反映函数调用的上下文信息,会为边添加属性,如函数调用的参数传递方式、返回值处理方式等。例如,若函数调用通过寄存器传递参数,则在边的属性中记录相关寄存器信息;若函数有返回值,记录返回值的存储方式和处理逻辑。此外,为了提高图的可读性和分析效率,会对CGCFG进行一些优化。例如,合并一些重复的函数节点和边,去除一些不必要的中间节点,使图的结构更加简洁明了。通过这种方式生成的CGCFG,能够清晰地展示程序中各个函数之间的调用层级和顺序,以及控制流在函数之间的转移情况。分析人员可以通过查看CGCFG,快速了解程序的整体结构和函数之间的依赖关系,为进一步的程序分析和相似性比较提供重要的参考依据。3.4函数控制流图提取模块设计3.4.1从IRCFG和CGCFG中提取函数控制流图从IRCFG和CGCFG中提取函数控制流图是函数控制流图提取模块的核心任务,其关键在于准确识别函数的边界和内部结构,确定函数节点和边。在IRCFG中,函数通常由一组连续的基本块组成,这些基本块之间通过控制流边相连。为了识别函数,首先需要确定函数的入口和出口基本块。函数入口基本块通常是程序中调用该函数的“CALL”指令的目标地址对应的基本块。通过查找IRCFG中所有“CALL”指令的目标地址,并结合符号信息(如果有的话),可以确定函数的入口基本块。函数出口基本块则通常是包含返回指令(如“RET”指令)的基本块,或者是无条件跳转到函数外部的基本块。确定函数的入口和出口基本块后,以入口基本块为起点,通过深度优先搜索(DFS)或广度优先搜索(BFS)算法遍历IRCFG,沿着控制流边收集所有可达的基本块,这些基本块及其之间的控制流边就构成了函数的控制流图。在遍历过程中,记录每个基本块的属性信息,如基本块的起始地址、结束地址、包含的指令序列等,这些属性信息将作为函数控制流图节点的属性。对于CGCFG,它主要展示了函数之间的调用关系。通过分析CGCFG中节点之间的连接关系,可以确定每个函数在调用层次结构中的位置和作用。从CGCFG中提取函数控制流图时,首先根据函数的唯一标识(如函数地址或函数名)在CGCFG中找到对应的节点。然后,从该节点出发,获取与该函数直接相关的调用边和被调用边,这些边所连接的函数节点以及它们之间的调用关系,反映了该函数在整个程序中的调用上下文。将这些信息与从IRCFG中提取的函数内部控制流图相结合,可以得到更完整的函数控制流图,不仅包含函数内部的执行流程,还包含函数在程序中的调用关系。在确定函数节点和边时,需要综合考虑多种因素。对于节点,除了基本块的属性信息外,还可以根据基本块的功能和作用进行分类和标记,如将包含条件判断的基本块标记为条件节点,将包含循环结构的基本块标记为循环节点,以便在后续的相似性分析中更好地识别和比较函数的结构和逻辑。对于边,根据控制流转移的条件和类型,赋予不同的权重和属性。例如,对于条件跳转边,根据跳转条件的复杂程度和可能性赋予不同的权重;对于无条件跳转边,标记其跳转的目标和作用。通过这些方法,可以准确地从IRCFG和CGCFG中提取出函数控制流图,并为后续的分析提供高质量的数据基础。3.4.2函数控制流图优化与简化对提取出的函数控制流图进行优化和简化是提高后续相似性比对效率的重要策略,通过去除冗余信息和合并相似节点等操作,使函数控制流图更加简洁、高效。一种常见的优化策略是去除无用节点和边。在函数控制流图中,可能存在一些孤立的节点或边,它们对函数的执行逻辑没有实际影响,或者是由于四、系统实现4.1开发环境与工具选择本系统的开发基于Windows10操作系统,选用C++作为主要编程语言。C++语言具有高效的执行效率和强大的底层控制能力,非常适合处理二进制程序分析这类对性能要求较高的任务。在处理大量二进制数据和复杂的算法逻辑时,C++的性能优势能够显著提升系统的运行速度和响应能力。开发工具方面,选用了VisualStudio2019。VisualStudio2019是一款功能强大的集成开发环境(IDE),它提供了丰富的代码编辑、调试和项目管理功能。在代码编辑方面,具有智能代码提示、语法高亮、代码自动补全等功能,能够大大提高开发效率;调试功能支持断点调试、单步执行、变量监视等,方便开发人员快速定位和解决代码中的问题;项目管理功能则能够方便地组织和管理项目中的各种文件和资源,支持多种项目类型和编译配置。在反汇编工具的选择上,本系统采用了IDAPro7.6。IDAPro是一款全球知名的交互式反汇编工具,支持多种操作系统和指令集架构,如Windows、Linux、ARM、x86等。它能够对二进制程序进行深度分析,准确地将二进制机器码转换为汇编代码,并提供丰富的分析功能,如函数识别、变量分析、交叉引用等。通过IDAPro的插件机制,还可以方便地进行二次开发,扩展其功能以满足本系统的特定需求。在数据存储方面,由于系统在运行过程中会产生大量的中间数据和分析结果,如反汇编后的汇编代码、控制流图信息、相似性匹配结果等,因此需要一个高效可靠的数据库来存储这些数据。本系统选用了SQLite数据库。SQLite是一款轻量级的嵌入式数据库,具有零配置、文件存储、占用资源少等特点。它能够快速地进行数据的读写操作,非常适合本系统对数据存储和管理的需求。在系统中,使用SQLite来存储二进制程序的基本信息、函数控制流图的节点和边信息、相似性匹配结果等数据,通过SQL语句进行数据的查询、插入、更新和删除操作,确保数据的高效管理和使用。在可视化工具方面,为了直观地展示相似性分析结果,本系统选用了matplotlib库。matplotlib是Python中一个广泛使用的绘图库,它提供了丰富的绘图函数和工具,能够生成各种类型的图表,如折线图、柱状图、散点图、饼图等。在本系统中,使用matplotlib来绘制函数控制流图的对比图、相似度矩阵图表等,通过不同的颜色、线条粗细和节点形状等视觉元素,清晰地展示相似部分和差异部分,使用户能够快速理解分析结果。同时,结合Python的其他库,如numpy和pandas,对数据进行预处理和分析,为matplotlib提供准确的数据支持,实现可视化报告的生成。4.2关键模块代码实现4.2.1二进制程序预处理代码实现二进制程序预处理模块的主要功能是将二进制程序反汇编为汇编代码,并对汇编代码进行处理和转换,提取出有用的信息。下面展示使用IDAPro接口实现二进制程序反汇编和汇编代码处理的关键代码:#include<ida.hpp>#include<idp.hpp>#include<loader.hpp>#include<kernwin.hpp>#include<bytes.hpp>#include<funcs.hpp>#include<xref.hpp>//获取二进制程序的入口地址ea_tget_entry_point(){returnget_imagebase()+get_entry_ordinal();}//反汇编指定地址的指令voiddisassemble_instruction(ea_taddr){insn_tinsn;if(decode_insn(&insn,addr)){charbuf[MAXSTR];print_insn_mnem(&insn,buf,sizeof(buf));msg("%s",buf);for(inti=0;i<insn.Op1.type;++i){charopbuf[MAXSTR];print_operand(&insn,i,opbuf,sizeof(opbuf));msg("%s",opbuf);}msg("\n");}}//处理汇编代码,提取指令序列和函数调用关系voidprocess_assembly_code(){ea_tentry=get_entry_point();func_t*func=get_func(entry);if(func){for(ea_taddr=func->startEA;addr<func->endEA;){insn_tinsn;if(decode_insn(&insn,addr)){//提取指令序列//这里可以将指令信息存储到相应的数据结构中//例如,将操作码和操作数存储到一个vector中std::vector<std::string>instruction;charbuf[MAXSTR];print_insn_mnem(&insn,buf,sizeof(buf));instruction.push_back(buf);for(inti=0;i<insn.Op1.type;++i){charopbuf[MAXSTR];print_operand(&insn,i,opbuf,sizeof(opbuf));instruction.push_back(opbuf);}//存储指令序列到全局数据结构或文件中//...//分析函数调用关系if(insn.itype==NN_call){ea_ttarget=get_operand_value(&insn,0);//这里可以建立函数调用关系,例如使用一个map存储调用者和被调用者std::map<ea_t,ea_t>call_relation;call_relation[addr]=target;//存储函数调用关系到全局数据结构或文件中//...}addr+=insn.size;}else{break;}}}}intidaapiinit(void){//初始化IDAPro插件returnPLUGIN_OK;}voididaapiterm(void){//清理IDAPro插件资源}voididaapirun(intarg){//运行插件,执行二进制程序预处理process_assembly_code();}plugin_tPLUGIN={IDP_INTERFACE_VERSION,0,//插件标志init,//初始化函数term,//终止函数run,//运行函数"BinaryPreprocessingPlugin",//插件名称"AuthorName",//作者名称"BinaryProgramPreprocessing"//插件描述};上述代码首先定义了获取二进制程序入口地址的函数get_entry_point,以及反汇编指定地址指令的函数disassemble_instruction。process_assembly_code函数是核心部分,它从程序入口地址开始,遍历函数中的每一条指令,提取指令序列并分析函数调用关系。在提取指令序列时,将操作码和操作数存储到std::vector<std::string>中;在分析函数调用关系时,使用std::map<ea_t,ea_t>来存储调用者和被调用者的地址。最后,通过IDAPro的插件机制,实现插件的初始化、终止和运行。4.2.2控制流图构建代码实现控制流图构建模块包括IRCFG和CGCFG的生成,下面分别呈现它们的代码逻辑和关键函数实现://IRCFG生成相关数据结构和函数classBasicBlock{public:ea_tstart_addr;ea_tend_addr;std::vector<ea_t>instructions;std::vector<BasicBlock*>successors;std::vector<BasicBlock*>predecessors;BasicBlock(ea_tstart,ea_tend):start_addr(start),end_addr(end){}};classIRCFG{public:std::vector<BasicBlock*>blocks;BasicBlock*entry_block;BasicBlock*exit_block;IRCFG():entry_block(nullptr),exit_block(nullptr){}//生成IRCFG的核心函数voidgenerate_IRCFG(func_t*func){//划分基本块std::vector<BasicBlock*>temp_blocks;ea_tcurrent_addr=func->startEA;while(current_addr<func->endEA){ea_tblock_end=current_addr;insn_tinsn;if(decode_insn(&insn,current_addr)){block_end+=insn.size;if(insn.itype==NN_jmp||insn.itype==NN_call||insn.itype==NN_ret){//遇到跳转、调用或返回指令,结束当前基本块BasicBlock*new_block=newBasicBlock(current_addr,block_end);new_block->instructions.push_back(current_addr);temp_blocks.push_back(new_block);current_addr=block_end;continue;}}current_addr=block_end;}//确定基本块之间的控制流关系for(size_ti=0;i<temp_blocks.size();++i){BasicBlock*current_block=temp_blocks[i];insn_tlast_insn;if(decode_insn(&last_insn,current_block->end_addr-1)){if(last_insn.itype==NN_jmp||last_insn.itype==NN_call){ea_ttarget=get_operand_value(&last_insn,0);for(size_tj=0;j<temp_blocks.size();++j){if(temp_blocks[j]->start_addr==target){current_block->successors.push_back(temp_blocks[j]);temp_blocks[j]->predecessors.push_back(current_block);break;}}}elseif(last_insn.itype!=NN_ret){//顺序执行,下一个基本块为后继if(i+1<temp_blocks.size()){current_block->successors.push_back(temp_blocks[i+1]);temp_blocks[i+1]->predecessors.push_back(current_block);}}}}//确定入口和出口块entry_block=temp_blocks[0];for(size_ti=0;i<temp_blocks.size();++i){if(temp_blocks[i]->successors.empty()){exit_block=temp_blocks[i];break;}}blocks=temp_blocks;}};//CGCFG生成相关数据结构和函数classFunctionNode{public:ea_tfunction_addr;std::stringfunction_name;std::vector<FunctionNode*>callees;std::vector<FunctionNode*>callers;FunctionNode(ea_taddr,conststd::string&name):function_addr(addr),function_name(name){}};classCGCFG{public:std::vector<FunctionNode*>nodes;FunctionNode*entry_node;CGCFG():entry_node(nullptr){}//生成CGCFG的核心函数voidgenerate_CGCFG(){//获取所有函数func_t*func=get_first_func();while(func){ea_tfunc_addr=func->startEA;std::stringfunc_name=get_func_name(func_addr);FunctionNode*new_node=newFunctionNode(func_addr,func_name);nodes.push_back(new_node);func=get_next_func(func->endEA);}//确定函数调用关系for(size_ti=0;i<nodes.size();++i){FunctionNode*current_node=nodes[i];func_t*func=get_func(current_node->function_addr);for(ea_taddr=func->startEA;addr<func->endEA;){insn_tinsn;if(decode_insn(&insn,addr)){if(insn.itype==NN_call){ea_ttarget=get_operand_value(&insn,0);for(size_tj=0;j<nodes.size();++j){if(nodes[j]->function_addr==target){current_node->callees.push_back(nodes[j]);nodes[j]->callers.push_back(current_node);break;}}}addr+=insn.size;}else{break;}}}//确定入口节点entry_node=nodes[0];//假设第一个函数为入口函数,实际应用中需更准确判断}};在上述代码中,IRCFG类用于生成中间表示控制流图。generate_IRCFG函数首先根据指令的跳转、调用和返回情况划分基本块,然后确定基本块之间的控制流关系,最后确定入口和出口块。CGCFG类用于生成调用图控制流图。generate_CGCFG函数首先获取所有函数并创建对应的节点,然后通过分析函数中的调用指令确定函数之间的调用关系,最后确定入口节点。这些代码实现了从二进制程序的汇编代码到控制流图的转换,为后续的函数控制流图提取和相似性分析提供了基础。4.2.3函数控制流图提取代码实现函数控制流图提取模块从IRCFG和CGCFG中提取函数控制流图,下面给出从IRCFG和CGCFG中提取函数控制流图的代码示例和实现细节://从IRCFG中提取函数控制流图std::vector<BasicBlock*>extract_function_cfg_from_IRCFG(IRCFG&ircfg,ea_tfunction_addr){std::vector<BasicBlock*>function_cfg;for(size_ti=0;i<ircfg.blocks.size();++i){BasicBlock*block=ircfg.blocks[i];if(block->start_addr>=function_addr&&block->end_addr<=get_func(function_addr)->endEA){function_cfg.push_back(block);}}returnfunction_cfg;}//从CGCFG中提取函数控制流图相关信息(这里假设函数控制流图在CGCFG中通过节点和边关系体现)FunctionNode*find_function_node_in_CGCFG(CGCFG&cgcfg,ea_tfunction_addr){for(size_ti=0;i<cgcfg.nodes.size();++i){if(cgcfg.nodes[i]->function_addr==function_addr){returncgcfg.nodes[i];}}returnnullptr;}//打印从CGCFG中提取的函数控制流图相关信息(简单示例,实际应用中可根据需求扩展)voidprint_function_cfg_from_CGCFG(CGCFG&cgcfg,ea_tfunction

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论