C++二进制文件中的类继承关系识别方法及电子装置

    专利查询2023-05-30  44

    c++二进制文件中的类继承关系识别方法及电子装置
    技术领域
    :1.本发明属于软件逆向分析
    技术领域
    :,具体涉及一种c++二进制文件中的类继承关系识别方法及电子装置。
    背景技术
    ::2.现实世界中的商业软件普遍缺乏安全审计,所以商业软件对安全审计的需求度大幅增加。然而,商业软件普遍规模大,复杂度高,所以想要对它们进行安全审计需要先进行程序的理解,即需要高层的抽象信息。由tiobe发布的2002年到现在的编程语言热度发展趋势统计图可知,c++语言一直稳定在前三的位置,由于其高性能、稳定和多态的特性,一直是大型商业软件的主流选择。尽管在c++逆向中,高层抽象信息即类继承关系的恢复极为困难,但是它对安全审计有着重要的帮助。它不仅可以帮助逆向人员更快的理解大型软件的整体框架和高层抽象信息,从而快速而有目的的进行接下来的逆向分析;而且它还可以帮助逆向人员理解程序关键位置的上下文,从而为漏洞挖掘或者漏洞利用提供支撑。3.以前的技术方案主要分为两类,一类技术方案是通过构造函数调用关系来推理类之间的继承关系(基于控制流的方法),该方法需要恢复控制流的信息,如函数调用图(fcg)等。然而在编译优化的情况下构造函数会有内联的情况,进而导致该方法失效。第二类技术方案是通过覆写分析和偏移量记录的方法来识别类关系(基于数据流的方法),该技术方案虽然不需要控制流信息,可以在一定程度上抵御函数内联带来的干扰,但是使用的启发式缺乏普适性,无法适用在多个平台或者较为复杂的编译优化环境。除此之外,以前的技术方案都无法识别虚继承,进而影响识别类和类关系的能力。另外,以前的技术方案分析效率较低,无法分析大型的二进制文件。技术实现要素:4.为解决上述问题,本发明提出一种c++二进制文件中的类继承关系识别方法及电子装置,依据c++二进制接口(abi)实现的特点,通过提取信息,执行过程间静态污点分析,并结合启发式推理的方式,并通过一套提速方法,从经过编译优化的商业软件中恢复类信息。5.为达到上述目的,本发明采用以下技术方案:6.一种c++二进制文件中的类继承关系识别方法,其步骤包括:7.1)从二进制文件中提取虚函数表、虚基类表与符号表;8.2)依据所述符号表获取每一虚函数表中的析构函数,将虚函数表与相应析构函数进行配对,并对每一虚函数表进行交叉引用查询,得到构造函数;9.3)依据构造函数、析构函数及直接或间接调用构造函数或析构函数的生成控制流图,对构造函数与析构函数执行虚函数表与虚基类的过程进行静态污点分析,构造对象内存布局;10.4)通过对象内存布局对虚函数表中的每一函数进行分析,恢复二进制文件中的类信息,得到类继承关系。11.进一步地,通过以下策略获取每一虚函数表中的析构函数:12.1)遍历每一虚函数表中的函数,检测该些函数是否具有虚函数表的内存的覆写操作;13.2)搜索该些函数的第一后面指令和调用该些函数的上层函数的第二后面指令,依据所述符号表检测是否具有delete操作;14.3)若一函数具有覆写操作且在第一后面指令或第二后面指令具有delete操作,则该函数为相应虚函数表的析构函数。15.进一步地,通过以下步骤获取所述构造函数:16.1)交叉引用查询每一虚函数表,排除掉析构函数,获取剩余函数;17.2)遍历剩余函数,检测是否具有虚函数表的内存的覆写操作;18.3)搜索剩余函数的第一前序指令和调用所述剩余函数的上层函数的第二前序指令,检测是否具有new操作;19.4)若一剩余函数具有覆写操作且在第一前序指令或第二前序指令具有new操作,则该剩余函数为相应虚函数表的构造函数。20.进一步地,通过以下步骤生成控制流图:21.1)遍历构造函数与析构函数的地址组成的集合,对每一构造函数与析构函数以跳转语句进行基本块的划分,并以每一构造函数与析构函数的起始地址所在的基本块为起始基本块;22.2)对于每一基本块,若是直接跳转语句或者条件跳转语句,则将跳转目标所在的基本块与该基本块相连接;23.3)查找包含非系统调用的call指令的基本块,将call指令所在的基本块与call指令指向的函数地址所在的基本块连接,并将call指令指向的函数地址集合与构造函数与析构函数的地址组成的集合进行并集;24.4)若跳转语句的跳转目标地址在已分析基本块中,且不是已分析基本块的起始地址或结束地址,则将该基本块对于跳转目标地址进行分割;25.5)分别对循环结构的走向循环路径分支与无返回的路径结构的走向路径分支进行标记。26.进一步地,通过以下步骤构造对象内存布局:27.1)依据所述控制流图,以每一构造函数与析构函数将this指针标记为初始污点,选择执行路径;28.2)对虚函数表与虚基类表进行污点的传播和消除,构造对象内存布局。29.进一步的,通过以下步骤对每一构造函数与析构函数将this指针标记为初始污点:30.1)若构造函数全部内联进普通函数中,this指针为rax寄存器,起始指令为new函数的下一条指令;31.2)若构造函数没有全部内联进普通函数中,this指针为rcx寄存器,起始指令为构造函数的第一条指令。32.进一步地,使用智能化路径选择策略选择执行路径;所述智能化路径选择策略包括:33.1)当出现两条分支都没有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记,走flase分支路径;34.2)避免选择标有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记的路径。35.进一步地,通过以下步骤避免选择标有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记:36.1)设置指令追踪;37.2)同一条分支指令执行超过一设定次数后开始采用随机游走的策略。38.进一步地,通过以下步骤对虚函数表与虚基类表进行污点的传播和消除:39.1)以基本块为单位,逐条扫描每条指令,若遇到分支指令时则进行路径选择;40.2)若污点寄存器中的值赋值给其他的寄存器,则将目标寄存器标记为污点;若常量赋值给污点寄存器,则将污点消除;若将污点寄存器中的值压入栈中,则记录对应栈偏移并标记污点;若有污点标记的栈变量弹出栈赋值给污点寄存器,则将对应的栈偏移消除污点,并将目标寄存器标记为污点;41.3)当执行到虚基类表写操作相关指令时,通过污点找到对应的内存偏移,将虚函数表记录到对应的对象内存布局中;42.4)当执行到虚函数表写操作相关指令时,通过污点找到对应的内存偏移,将虚函数使用尾插法记录到对应的对象内存布局中,并记录构造函数中的覆写顺序。43.5)当涉及虚基类表读操作相关指令时,通过污点从对象内存布局中寻找对应的虚基类表,并计算出真正的内存偏移,将虚基类表及其覆写顺序记录下来,并将该内存偏移标记为虚基类。44.进一步地,通过启发式推理从二进制文件中恢复类信息;所述启发式推理包括以下步骤:45.1)遍历每个对象的对象内存布局,将每个具有虚函数表属性的内存偏移中存储的第一个虚函数表提取出来,将该虚函数表从对象内存布局中删除,独立成为一个节点,并检查各虚函数表之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;46.2)循环提取每个具有虚函数表属性的内存偏移中的虚函数表,每次循环从每个内存偏移处提取一个虚函数表,将该虚函数表从对象内存布局中删除,独立成为一个节点,与上一个虚函数表所属的节点建立继承关系,并在每次循环中,检测各虚函数表之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;47.3)遍历所有节点,查看不同的节点的虚函数表是否具有相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;48.4)对于每个虚基类属性的内存偏移,从第二个虚函数表开始依次提取,提取的虚函数表从对象内存布局中删除,独立成一个节点,并与第一个虚函数表所属的节点建立虚继承关系,并遍历所有节点,寻找与该虚函数表具有相同析构函数的节点,并将其进行节点合并,合并时覆写顺序保留数值大的;若寻找到的节点其父节点或者以上节点具有虚继承关系,则删除该虚函数表所属节点与虚基类的虚继承关系;49.5)若存在孤立的节点或者节点树,记录其最下面的子节点的虚函数表,覆写顺序,遍历其他节点树,分析其他节点树中的节点的析构函数是否包含记录节点的虚函数表,若包含且其节点覆写顺序小于记录节点的覆写顺序,则将记录节点所属的节点或者节点树归到对应节点上,建立对象成员关系;50.6)将所有节点进行聚合和去重操作,相同的节点只保留一个。51.一种存储介质,所述存储介质中存储有计算机程序,其中,所述计算机程序被设置为运行时执行上述所述的方法。52.一种电子装置,包括存储器和处理器,所述存储器中存储有计算机程序,所述处理器被设置为运行所述计算机以执行上述所述的方法。53.与现有技术相比,本发明方法的优点是:54.1)提出了一种结合过程间静态污点分析与启发式推理的方法,可以恢复类和类关系,其中包括虚继承(之前的方法无法识别虚继承),提高了类识别的召回率。55.2)提出了一系列的技巧来提升分析的效率,其中包括启发式搜索构造函数,高效的cfg生成算法和过程间静态污点分析,这些技术使得分析大型二进制文件成为了可能。56.3)经过实验测试,使用本发明实现的系统在o2编译优化环境下恢复类的平均召回率有84.36%,相比于最前沿的工具ooanalyzer提高了50%左右,平均精确率有97.17%,并且分析效率相比于最前沿的工具ooanalyzer提高了三个数量级以上。附图说明57.图1本发明一实施例的可分析大型商业软件的c++二进制代码类继承关系识别方法整体框架图。58.图2本发明一实施例的实例源码与类关系示意图。59.图3本发明一实施例的d对象布局示意图。60.图4本发明一实施例的跳转析构函数示意图。61.图5本发明一实施例的智能化路径选择策略示意图。62.图6本发明一实施例的对象内存布局示意图。63.图7本发明一实施例的节点生成过程示意图。具体实施方式64.为了使本
    技术领域
    :的人员更好地理解本发明实施例中的技术方案,并使本发明的目的、特征和优点能够更加明显易懂,下面结合和事例对本发明中技术核心作进一步详细的说明。65.如图1所示,为本发明一实施例的可分析大型商业软件的c++二进制代码类继承关系识别技术整体框架图。如图2所示,是一个简单的c++程序的源码,一共有5个类a、b、c、d、e,其中d多重继承了b和c,b和c同时虚继承了a,并且e是d的对象成员。在实例化d的时候,其对象内存布局如图3所示,其中图3中的序号为初始化顺序。66.本发明的可分析大型商业软件的c++二进制代码类继承关系识别技术方案,包括以下步骤:67.1.从二进制文件中提取基本信息68.为了后续的分析能够正常进行,首先需要从二进制文件里提取基本信息,包括:虚函数表(vftable),虚基类表(vbtable),符号表。69.1)提取vftable依据的特征有:70.a)位置在只读段上,如.rdata;71.b)存储的地址在代码段上,如.text;72.c)第一项有交叉引用,将vftable赋值给虚函数表指针(vftptr);73.d)若有运行时类型信息(rtti),则运行时类型信息指针(rttiptr)在vftable前面。74.具体的,若a)-c)的特征都存在时,即可从二进制文件中提取vftable,并可通过d)提取到该vftable的一个附加信息。75.2)提取vbtable依据的特征有:76.a)位置在只读段上,如.rdata;77.b)固定两个字段,且字段长度固定为4字节,第一个字段为vftable偏移,固定为-4(32位程序)或-8(64位程序),第二个字段为虚基类偏移;78.c)第一项有交叉引用,将vbtable赋值给虚基类表指针(vbtptr)。79.同上,若a)-c)的特征都存在时,即可从二进制文件中提取vbtable80.3)符号表信息提取具体包括以下步骤:81.步骤a):通过上述vftable依据特征提取vftable,并记录下vftable地址和所包含的虚函数地址;82.步骤b):通过上述vbtable依据特征提取vbtable,并记录下vbtable地址和vbtable字段里的内容;83.步骤c):根据vftable地址和所包含的虚函数地址及vbtable地址和vbtable字段里的内容,提取符号表信息。84.2.析构函数与构造函数分析85.在本步骤中,搜索每个vftable中的析构函数,并将vftable与析构函数进行配对,然后对每个vftable进行交叉引用查询,启发式搜索构造函数。86.具体地,因为过程间静态污点分析不是对所有函数进行分析,而是从析构函数(dtor)或者构造函数(ctor)开始,所以需要先从二进制文件中识别出析构函数和构造函数,并将析构函数与vftable进行配对。87.以前基于控制流的方法将vftable与构造函数进行配对,在编译优化的情况下,基类的构造函数可能内联进派生类的构造函数中,或者构造函数全部内联进普通函数中,这样将导致该配对方法失效。而本发明采用将vftable与析构函数进行配对的方法,因为为了防止内存泄漏,析构函数一般被设置为虚函数,所以析构函数不存在函数内联问题。88.拥有多重继承关系的派生类具有多张vftable表,以前基于数据流的方法根据连续地址空间上的offsettotop字段来进行vftable的合并,并且一个vftable只与一个类绑定,然而在编译优化的情况下该方法并不适用。在编译优化的情况下,同一个类的不同vftable可能分布在不连续的地址空间;不同的类可能共享相同的vftable。使用以前的方法会导致类的缺失,所以本发明通过析构函数来合并vftable,而且不限于一对一的匹配,也可以一对多匹配。89.在真实世界的二进制文件中,同一个类的不同vftable中只有一个记录着析构函数,其他的vftable里不存在析构函数。但是通过仔细分析和观察可以发现,如图4所示,不存在析构函数的vftable中都存在一种跳转析构函数的wrapper函数,该函数主要的作用是修改this指针,然后再跳转到真正的析构函数。本发明通过探测语义来寻找语义上相同的析构函数作为启发式,合并同一个类的不同vftable。析构函数具有的语义主要是以下两个:90.1)覆写操作,例如movqword[rsi+0x8],vftptr;[0091]2)delete操作,例如calldelete()。[0092]析构函数分析具体包括以下步骤:[0093]步骤1:遍历每个vftable中的函数,检测其是否具有vftable的内存写入操作,即覆写操作,若存在则执行步骤2;[0094]步骤2:搜索该函数后面的指令和调用该函数的上层函数的后面的指令,检测其中是否具有delete操作,这里采用模式匹配的方法,通过信息提取中的符号表得到一个函数的符号,将常见的delete符号与之匹配,若匹配则具有delete操作,并执行步骤3,否则返回步骤1;[0095]步骤3:将vftable与识别的析构函数进行配对,若vftable未遍历完,则执行步骤1,否则结束;[0096]以前的方法通过遍历所有函数来搜索构造函数,这在面对大型二进制文件时会显著增加分析时间的开销,本发明采用启发式搜索构造函数的方法,对vftable进行交叉引用查询,排除掉先前识别的析构函数,剩下的再通过语义筛选,找到构造函数。在复杂的编译优化的情况下,构造函数可能全部被内联进普通函数内,所以本发明搜索得到的构造函数并不一定是真正的构造函数,而是包含构造函数行为的函数。构造函数具有的语义主要是以下两个:[0097]1)覆写操作,例如movqword[rsi+0x8],vftptr;[0098]2)new操作,例如callnew()。[0099]构造函数分析具体包括以下步骤:[0100]步骤1:交叉引用查询每一个vftable,排除掉析构函数,遍历剩下的函数,检测其是否具有覆写操作,若存在则执行步骤2;[0101]步骤2:搜索该函数前面的指令和调用该函数的上层函数的前面的指令,检测其中是否具有new操作,这里采用模式匹配的方法,通过信息提取中的符号表得到一个函数的符号,将常见的new符号与之匹配,若匹配则具有new操作,并执行步骤3,否则返回步骤1;[0102]步骤3:将识别出的构造函数加入构造函数列表中,若函数遍历完成则结束,否则执行步骤1。[0103]3.过程间静态污点分析[0104]在本步骤中,利用高效的控制流图(cfg)生成算法,依据构造函数、析构函数及直接或间接调用构造函数或析构函数的其它函数生成对应的cfg,然后对构造函数/析构函数执行过程间静态污点分析,构造出对象内存布局。[0105]以前的方法无法处理数据间接引用的问题,导致无法处理vbtable读写操作分别在不同函数的情况,进而无法识别虚继承。除此之外,在编译优化的情况下,构造函数内联情况会扰乱控制流的分析。基于以上原因,本发明采用过程间的方法来解决数据间接引用的问题,从而可以识别虚继承;以构造函数或析构函数为起点,从整体角度进行分析,构造对象内存布局,从而可以抵抗构造函数内联带来的影响。[0106]本发明采用静态污点分析的原因是为了避免符号执行的路径爆炸问题和动态分析的代码覆盖率低的问题。符号执行的路径爆炸问题在面对分析大型二进制文件时尤为突出,会导致分析时间大幅度增加。由于类继承关系的特征性,只需要专注与vftable和vbtable相关的操作即可,所以污点分析更加适合一些。以前的二进制污点分析技术大多是动态的,动态分析方法具有代码覆盖率低的缺点。解决此问题的方法是构建多个测试样本或从多个起点运行,但是这两种方法都会带来新的挑战。构造多个测试样本需要解决如何自动构造可以触发所有类方法的多个测试用例的问题;从多个起点运行需要解决如何确定每个起点的上下文的问题。这些问题在当前的研究进展中还没有得到很好的解决,因此动态分析方法在这里并不适用。基于以上的原因,所以本发明采用的方法是过程间静态污点分析。[0107]过程间静态污点分析包括三个部分:高效的cfg生成算法、智能化路径选择策略和污点初始化与污点传播规则。[0108]过程间静态污点分析具体包括以下步骤:[0109]步骤1:对构造函数和析构函数及其相关的函数生成cfg;[0110]步骤2:对每个构造函数/析构函数执行污点分析,采用智能化路径选择策略选择执行路径;[0111]步骤3:将this指针标记为初始污点,利用污点传播规则进行关于vftable和vbtable的污点的传播和消除,执行完后构造出对象内存布局。[0112]以前的方法都会对所有函数生成cfg,这在分析大型二进制文件时会极大的增加时间开销。在c++二进制代码类继承关系识别领域中,只需要分析与覆写分析相关的函数即可,这一般只占总函数的20%左右,所以本发明采用了部分cfg生成的策略,只对构造函数和析构函数及其相关的函数生成cfg,具体包括以下步骤:[0113]步骤1:将构造函数和析构函数的地址放在同一集合中,遍历该集合,并对每个函数以跳转语句进行基本块的划分,每个函数以函数起始地址所在的基本块为起始点,每个基本款以用于划分的跳转语句作为末尾;[0114]步骤2:对于每个基本块,若包含直接跳转语句或者条件跳转语句,则将跳转目标所在的基本块与该基本块相连接;[0115]步骤3:对于每个基本块,若存在call指令,通过符号表模式匹配是否为系统调用,若是则直接丢弃,否则将call指令指向的函数地址所在的基本块与该基本块相连接,若call指令指向的函数地址不存在集合中,则将其添加进集合中;[0116]步骤4:在处理跳转语句时,若跳转目标地址在已经分析过的基本块中,且不是该基本块的起始地址和结束地址,则将该基本块对于跳转目标地址进行分割;[0117]步骤5:若遇到循环结构时,将走向循环路径的分支标记为loop,为智能路径选择策略提供判断依据;[0118]步骤6:若遇到无返回的路径结构(noreturn)时,将走向noreturn路径的分支标记为noreturn,为智能路径选择策略提供判断依据。[0119]智能化路径选择策略的主要思想是模拟真实程序执行流程,避免路径爆炸,对路径进行筛选,提高分析效率,并防止执行路径进入无限循环的情况,使得污点分析可以正常执行和结束,如图5所示,具体包括以下策略:[0120]1)在分支语句时避免选择loop或者noreturn标记的分支路径;[0121]2)若两条分支没有loop或者noreturn标记,依据编译器原理和逆向经验,走flase分支路径可覆盖覆写操作相关指令;[0122]3)为避免陷入过程间循环等复杂的无限循环中,设置指令追踪,同一条分支指令执行超过10次后开始采用随机游走的策略。[0123]污点初始化与污点传播规则主要追踪this指针,寻找与vftable和vbtable相关的内存读和内存写操作,并以此构造对象内存布局。因为有些多态类在源码中被定义了,但没有被使用,经过编译后,这些多态类在二进制文件中不存在构造函数,如果只对构造函数进行静态污点分析可能会造成漏报的情况。为了防止内存泄露,析构函数一般是虚函数,所以存在于二进制文件中,但是不能只分析析构函数,因为析构函数中关于vftable和vbtable的操作指令可能会有缺失,而构造函数中所包含的信息会更加全面。基于以上原因,本发明的过程间静态污点分析过程中先分析构造函数,之后对于没有分析到的vftable,分析它们的析构函数,这样的组合分析策略即可降低漏报率,也可降低误报率。污点初始化与污点传播规则具体包括以下步骤:[0124]步骤1:以每个识别的构造函数/析构函数为起始分析点,初始污点为this指针。其中,若是构造函数完全内联的情况,即构造函数全部内联进普通函数中,this指针为rax寄存器,起始指令为new函数的下一条指令;其他情况this指针为rcx寄存器,起始指令为构造函数或构造函数被内联的第一条指令;[0125]步骤2:以基本块为单位,逐条扫描每条指令,若遇到分支指令时则按照智能化路径选择策略进行路径选择;[0126]步骤3:若污点寄存器(rax寄存器或rcx寄存器)中的值赋值给cpu中的其他寄存器,则将目标寄存器标记为污点,若常量赋值给污点寄存器,则将污点消除。若将污点寄存器中的值压入栈中,则记录对应栈偏移并标记污点,若有污点标记的栈变量弹出栈赋值给污点寄存器,则将对应的栈偏移消除污点,并将目标寄存器标记为污点;[0127]步骤4:当执行到vbtable写操作相关指令时(如movqword[rsi+0x8],vbtptr),通过污点找到对应的内存偏移,将vbtable记录到对应的对象内存布局中;[0128]步骤5:当执行到vftable写操作相关指令时(如movqword[rsi+0x8],vftptr),通过污点找到对应的内存偏移,将vftable使用尾插法记录到对应的对象内存布局中,同时将覆写顺序(构造函数中的覆写顺序)也记录下来。其中若涉及vbtable读操作相关指令时,通过污点从对象内存布局中寻找对应的vbtable,并计算出真正的内存偏移,将vftable及其覆写顺序记录下来(尾插法),并将该内存偏移标记为虚基类(vbase)。[0129]在执行完过程间静态污点分析之后,便构造出对象内存布局。对象内存布局是把每个对象的vftable与vbtable的空间位置与时间顺序都记录下来,例如,即将图3中的信息转换成图6里的形式。其中,纵向是空间维度,记录内存的偏移和内容的属性,内容的属性包括:vftable,vbtable,vbase,var;横向是时间维度,记录着vftable或者vbtable,以及vftable的覆写顺序。[0130]4.启发式推理[0131]在得到对象内存布局之后,可对其进行启发式推理来恢复类和类关系。对每一个对象的对象内存布局进行启发推理分析,循环进行节点添加和节点合并的过程,然后对孤立的节点或节点树进行对象成员的分析。因为不同的对象可能包含相同的类节点,最后再把所有节点进行聚合和去重操作,得到类信息:包括类、类关系、类方法等信息,其中类关系包括:单一继承、多重继承、虚继承和对象成员。[0132]启发式推理使用了四个与编译优化无关的启发式,具体解释和选取原因如下:[0133]1)本发明没有采用构造函数与vftable配对的方式来进行类继承关系的恢复,这种方法在编译优化的情况下是无效的。本发明通过静态污点分析技术记录了每个对象关于vftable和vbtable的内存布局,由编译器原理可知,相同内存偏移处的vftable所属的类之间具有继承关系。[0134]2)通过逆向分析可以发现同一个类的多张vftable具有语言上相同的析构函数,这在析构函数分析中已经进行处理,在这里可以使用该启发式将多个vftable合并为一个类。[0135]3)在识别虚基类的时候,会存在数据间接引用的操作,以前的方法都无法处理这个问题,使用过程间的静态污点分析技术可以准确处理该问题,并能计算出正确的内存偏移,将vftable和对应的覆写顺序记录下来,同时会在该内存偏移处标上vbase标签。该内存偏移处的所有vftable所属的类具有虚继承关系,并且通过逆向经验可知第一次写入该内存偏移处的vftable所属的类是虚基类。[0136]4)对象成员的识别是通过覆写顺序来判别的,覆写顺序指在构造函数中具有vftable覆写操作的时间顺序。在构造函数执行流程中,对象成员的初始化是在完成该类的vftptr初始化之后进行的,所以对象成员的覆写顺序是大于对象成员所属类的覆写顺序的,这与以前的方法很近似。除此之外,因为识别的时候是遍历以vftable为集合的节点,而vftable没有与构造函数配对,所以这里使用了析构函数,通过检测一个vftable是否在某个析构函数内,并结合覆写顺序共同判断,来恢复对象成员,这与以前的方法不同。[0137]基于以上原因,四个启发式可总结如下:[0138]1.同一内存偏移处的vftable所属的类存在继承关系,负责类继承关系的恢复;[0139]2.同一个类的不同的vftable具有相同的析构函数,负责节点合并;[0140]3.带有vbase的内存偏移中的第一个vftable所属的类为虚基类,后面的类都具有虚继承关系,负责虚继承关系和虚基类的恢复;[0141]4.对象成员的vftable的相关覆写指令存在与其所属类的析构函数中,且对象成员的覆写顺序大于所属类的覆写顺序,负责对象成员的恢复。[0142]依据这四条启发式,启发式推理具体包括以下步骤:[0143]步骤1:遍历每个对象的对象内存布局,进行节点生成操作,执行步骤2到步骤6,遍历结束后进行节点去重操作,执行步骤7;[0144]步骤2:将每个具有vftable属性的内存偏移中存储的第一个vftable提取出来,将该vftable从对象内存布局中删除,独立成为一个节点,代表一个类,并检查各vftable之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;[0145]步骤3:循环提取每个具有vftable属性的内存偏移中的vftable,每次循环从每个内存偏移处提取一个vftable,将该vftable从对象内存布局中删除,独立成为一个节点,并与上一个vftable所属的节点建立继承关系。在每次循环中,检测各vftable之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;[0146]步骤4:遍历所有节点,查看不同的节点的vftable是否具有相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;[0147]步骤5:对于每个vbase属性的内存偏移,从第二个vftable开始依次提取,提取的vftable从对象内存布局中删除,独立成为一个节点,并与第一个vftable所属的节点(虚基类)建立虚继承关系,其中提取第二个vftable时会将第一个vftable也提取。与此同时,遍历所有节点,寻找与该vftable具有相同析构函数的节点,并将其进行节点合并,合并时覆写顺序保留数值大的。若寻找到的节点其父节点或者其以上的节点具有虚继承关系,则删除该vftable所属节点与虚基类的虚继承关系;[0148]步骤6:若存在孤立的节点或者节点树,记录其最下面的子节点的vftable,覆写顺序,遍历其他节点树,分析其他节点树中的节点的析构函数是否包含记录节点的vftable,若包含且其节点覆写顺序小于记录节点的覆写顺序,则将记录节点所属的节点或者节点树(对象成员)归到对应节点上,建立对象成员关系;[0149]步骤7:将所有节点进行聚合和去重操作,相同的节点只保留一个。[0150]为了能够使得本领域的技术人员快速理解,举了一个实例,如图7所示,是将图6执行启发式推理的节点生成过程。[0151]尽管为说明目的公开了本发明的具体内容、实施算法以及附图,其目的在于帮助理解本发明的内容并据以实施,但是本领域的技术人员可以理解:在不脱离本发明及所附的权利要求的精神和范围内,各种替换、变化和修改都是可能的。本发明不应局限于本说明书最佳实施例和附图所公开的内容,本发明要求保护的范围以权利要求书界定的范围为准。当前第1页12当前第1页12
    技术特征:
    1.一种c++二进制文件中的类继承关系识别方法,其步骤包括:1)从二进制文件中提取虚函数表、虚基类表与符号表;2)依据所述符号表获取每一虚函数表中的析构函数,将虚函数表与相应析构函数进行配对,并对每一虚函数表进行交叉引用查询,得到构造函数;3)依据构造函数、析构函数及直接或间接调用构造函数或析构函数的生成控制流图,对构造函数与析构函数执行虚函数表与虚基类的过程进行静态污点分析,构造对象内存布局;4)通过对象内存布局对虚函数表中的每一函数进行分析,恢复二进制文件中的类信息,得到类继承关系。2.如权利要求1所述的方法,其特征在于,通过以下策略获取每一虚函数表中的析构函数:1)遍历每一虚函数表中的函数,检测该些函数是否具有虚函数表的内存的覆写操作;2)搜索该些函数的第一后面指令和调用该些函数的上层函数的第二后面指令,依据所述符号表检测是否具有delete操作;3)若一函数具有覆写操作且在第一后面指令或第二后面指令具有delete操作,则该函数为相应虚函数表的析构函数。3.如权利要求1所述的方法,其特征在于,通过以下步骤获取所述构造函数:1)交叉引用查询每一虚函数表,排除掉析构函数,获取剩余函数;2)遍历剩余函数,检测是否具有虚函数表的内存的覆写操作;3)搜索剩余函数的第一前序指令和调用所述剩余函数的上层函数的第二前序指令,检测是否具有new操作;4)若一剩余函数具有覆写操作且在第一前序指令或第二前序指令具有new操作,则该剩余函数为相应虚函数表的构造函数。4.如权利要求1所述的方法,其特征在于,通过以下步骤生成控制流图:1)遍历构造函数与析构函数的地址组成的集合,对每一构造函数与析构函数以跳转语句进行基本块的划分,并以每一构造函数与析构函数的起始地址所在的基本块为起始基本块;2)对于每一基本块,若是直接跳转语句或者条件跳转语句,则将跳转目标所在的基本块与该基本块相连接;3)查找包含非系统调用的call指令的基本块,将call指令所在的基本块与call指令指向的函数地址所在的基本块连接,并将call指令指向的函数地址集合与构造函数与析构函数的地址组成的集合进行并集;4)若跳转语句的跳转目标地址在已分析基本块中,且不是已分析基本块的起始地址或结束地址,则将该基本块对于跳转目标地址进行分割;5)分别对循环结构的走向循环路径分支与无返回的路径结构的走向路径分支进行标记。5.如权利要求1所述的方法,其特征在于,通过以下步骤构造对象内存布局:1)依据所述控制流图,以每一构造函数与析构函数将this指针标记为初始污点,选择执行路径;
    2)对虚函数表与虚基类表进行污点的传播和消除,构造对象内存布局。6.如权利要求5所述的方法,其特征在于,通过以下步骤对每一构造函数与析构函数将this指针标记为初始污点:1)若构造函数全部内联进普通函数中,this指针为rax寄存器,起始指令为new函数的下一条指令;2)若构造函数没有全部内联进普通函数中,this指针为rcx寄存器,起始指令为构造函数的第一条指令。7.如权利要求6所述的方法,其特征在于,使用智能化路径选择策略选择执行路径;所述智能化路径选择策略包括:1)当出现两条分支都没有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记,走flase分支路径;2)避免选择标有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记的路径。其中,通过以下步骤避免选择标有循环结构的走向循环路径分支标记与无返回的路径结构的走向路径分支标记:1)设置指令追踪;2)同一条分支指令执行超过一设定次数后开始采用随机游走的策略。8.如权利要求5所述的方法,其特征在于,通过以下步骤对虚函数表与虚基类表进行污点的传播和消除:1)以基本块为单位,逐条扫描每条指令,若遇到分支指令时则进行路径选择;2)若污点寄存器中的值赋值给其他的寄存器,则将目标寄存器标记为污点;若常量赋值给污点寄存器,则将污点消除;若将污点寄存器中的值压入栈中,则记录对应栈偏移并标记污点;若有污点标记的栈变量弹出栈赋值给污点寄存器,则将对应的栈偏移消除污点,并将目标寄存器标记为污点;3)当执行到虚基类表写操作相关指令时,通过污点找到对应的内存偏移,将虚函数表记录到对应的对象内存布局中;4)当执行到虚函数表写操作相关指令时,通过污点找到对应的内存偏移,将虚函数使用尾插法记录到对应的对象内存布局中,并记录构造函数中的覆写顺序。5)当涉及虚基类表读操作相关指令时,通过污点从对象内存布局中寻找对应的虚基类表,并计算出真正的内存偏移,将虚基类表及其覆写顺序记录下来,并将该内存偏移标记为虚基类。9.如权利要求1所述的方法,其特征在于,通过以下步骤对二进制文件中的每一函数进行分析:1)遍历每个对象的对象内存布局,将每个具有虚函数表属性的内存偏移中存储的第一个虚函数表提取出来,将该虚函数表从对象内存布局中删除,独立成为一个节点,并检查各虚函数表之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;2)循环提取每个具有虚函数表属性的内存偏移中的虚函数表,每次循环从每个内存偏移处提取一个虚函数表,将该虚函数表从对象内存布局中删除,独立成为一个节点,与上一
    个虚函数表所属的节点建立继承关系,并在每次循环中,检测各虚函数表之间是否存在相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;3)遍历所有节点,查看不同的节点的虚函数表是否具有相同的析构函数,若存在则进行节点的合并,合并时覆写顺序保留数值大的;4)对于每个虚基类属性的内存偏移,从第二个虚函数表开始依次提取,提取的虚函数表从对象内存布局中删除,独立成一个节点,并与第一个虚函数表所属的节点建立虚继承关系,并遍历所有节点,寻找与该虚函数表具有相同析构函数的节点,并将其进行节点合并,合并时覆写顺序保留数值大的;若寻找到的节点其父节点或者以上节点具有虚继承关系,则删除该虚函数表所属节点与虚基类的虚继承关系;5)若存在孤立的节点或者节点树,记录其最下面的子节点的虚函数表,覆写顺序,遍历其他节点树,分析其他节点树中的节点的析构函数是否包含记录节点的虚函数表,若包含且其节点覆写顺序小于记录节点的覆写顺序,则将记录节点所属的节点或者节点树归到对应节点上,建立对象成员关系;6)将所有节点进行聚合和去重操作,相同的节点只保留一个。10.一种电子装置,包括存储器和处理器,所述存储器中存储有计算机程序,所述处理器被设置为运行所述计算机程序以执行如权利要求1-9中任一所述方法。

    技术总结
    本发明提供一种C++二进制文件中的类继承关系识别方法及电子装置,包括从二进制文件中提取虚函数表、虚基类表与符号表;依据所述符号表获取每一虚函数表中的析构函数,将虚函数表与相应析构函数进行配对,并对每一虚函数表进行交叉引用查询,得到构造函数;依据构造函数、析构函数及直接或间接调用构造函数或析构函数的生成控制流图,对构造函数与析构函数执行虚函数表与虚基类的过程进行静态污点分析,构造对象内存布局;通过对象内存布局对虚函数表中的每一函数进行分析,恢复二进制文件中的类信息,得到类继承关系。本发明通过启发式搜索构造函数、高效CFG生成算法和过程间静态污点分析,可恢复包括虚继承在内的类继承关系,提高类识别召回率。提高类识别召回率。提高类识别召回率。


    技术研发人员:龚晓锐
    受保护的技术使用者:中国科学院信息工程研究所
    技术研发日:2020.11.23
    技术公布日:2022/5/25
    转载请注明原文地址:https://tc.8miu.com/read-16671.html

    最新回复(0)