一种残膜回收机防缠绕挑膜装置的制 一种秧草收获机用电力驱动行走机构

基于二进制程序模块化的开源组件检测方法

2022-11-30 10:07:18 来源:中国专利 TAG:


1.本发明属于开源组件检测技术领域,具体涉及一种基于二进制程序模块化的开源组件检测方法。


背景技术:

2.在现代软件开发过程中,随着开源软件(open-source software,oss)的日益普及,软件重用成为一种普遍现象,开发人员往往利用开源软件提供的丰富功能来缩短开发周期,将更多的时间花在个性化开发上。近年来,开源软件的数量正以指数级的速度增长。截至目前,github上有超过4400万个存储库,大量的开源软件给软件开发带来了极大的便利。然而,oss使用不当会导致潜在的安全风险。含有过时的oss的软件更有可能被利用。例如,一个名为heartbleed的安全漏洞在openssl(一个流行的加密软件库)的1.0.1g版本之前的1.0.1版本中被发现。对于使用该库的易受攻击的1.0.1版本的软件,攻击者可以窃取诸如用户名和密码之类的私人信息,它影响了许多著名的软件,例如4.2.0到4.2.2版本的libreoffice软件和vmware workstation 10。
3.商用软件(commercial off-the-shelf,cots)通常是以闭源的方式进行发布,无法直接获取源码进行软件安全分析,为了检测开源组件漏洞,在二进制程序中识别复用的开源软件尤为重要,它在各种软件安全工程相关任务中发挥着重要作用,如恶意软件检测、漏洞搜索、逆向工程等。目前的工作多数通过提取目标二进制程序和源码中的特征,选择合适的特征匹配算法进行开源组件的识别。但是,已有的工作在两个方面存在不足,分别是特征选择和开源组件在二进制程序中的识别粒度。
4.首先,特征选择方面,有些方法提取的特征太少或者易受编译优化影响。多数方法依赖字符串和导出函数名等语法特征,但是一些商业软件为了隐藏其软件组成而去除字符串。因此,为了克服特征的单一性,控制流图成为新的选择。但是由于编译优化的影响,控制流很容易发生改变,故控制流图不能作为合适的特征进行匹配。
5.其次,在二进制程序中开源组件的识别粒度方面,现有的方法是从文件粒度检测开源组件,即以整个二进制程序为粒度单元,通过直接映射、层次匹配等算法,对提取的特征进行匹配,然后计算特征相似度,判断整个程序中是否复用开源组件。针对二进制程序的开源组件识别,会有一些下游任务的开展,例如漏洞检测,是否包含高危漏洞;是否受供应链投毒影响;推动供应商更新重要依赖组件的版本等。基于目前的方法,若进行下游任务开源漏洞的搜索,其范围为整个二进制程序,对于时间和工作量都是一个较高的要求。


技术实现要素:

6.针对开源组件检测方法存在的缺陷和问题,本发明提供一种基于二进制程序模块化的开源组件检测方法。
7.本发明解决其技术问题所采用的方案是:一种基于二进制程序模块化的开源组件检测方法,包括二进制程序模块化和开源组件识别,其中二进制程序模块化包括以下内容:
8.(1)对二进制程序进行函数地址提取和函数调用图创建,通过函数地址和函数有向调用关系的提取构造模块化步骤的输入图;
9.(2)使用基于模块度改进的bcm算法对二进制程序模块进行划分;
10.开源组件识别包括以下内容:
11.(1)特征选择与提取:针对二进制程序的每个模块,提取每个模块中的字符类型及函数的复杂分支序列作为提取特征,其中字符类型包括字符串字面量、字符串数组和导出函数名;对于函数的复杂分支序列,将switch/case、if/else的分支序列作为选择的特征;
12.(2)模块粒度的开源组件识别:将二进制程序每个模块的提取特征与源码特征进行比对,检测出开源组件所在的二进制模块。
13.上述的基于二进制程序模块化的开源组件检测方法,对二进制程序模块划分包括以下步骤:
[0014]ⅰ、初始假设每个函数都是一个独立的模块,对任意相邻的节点i和节点j,计算将节点i加入其邻居节点j所在模块c时对应的模块度增量δq,
[0015][0016]
式中:s
i,in
表示节点i与模块c内部函数连边权重之和;m是网络中所有边权重之和;wc是模块c内部所有边的权重和;sc是所有与模块c内部的函数相关联的边的权重和;
[0017]
然后计算节点i与所有邻居节点的模块度增量,选出其中最大的一个;当该值为正时,把节点i加人相应的邻居节点所在的模块;否则,节点i留在原模块中;
[0018]
重复进行,直至不再出现合并现象,划分出第一层模块;
[0019]ⅱ、基于ⅰ形成的模块结果,重复ⅰ中的方法对新的模块结果进行模块划分,得到第二层模块,其中模块之间连边的权重是两个模块之间所有节点连边的权重和;
[0020]
重复直至网络模块度不再增加,完成二进制程序模块划分。
[0021]
上述的基于二进制程序模块化的开源组件检测方法,从二进制模块中提取字符串的过程为:通过模块中每个函数的地址获取该函数的所有片段,遍历每个函数片段中的所有指令,遍历该指令的数据引用地址,当该地址存放的是字符串时,将字符串的地址及值添加到字符串列表中。
[0022]
上述的基于二进制程序模块化的开源组件检测方法,从二进制程序中提取导出函数名的过程为:首先需要获取模块中的所有函数名,然后通过遍历整个二进制程序中的导出函数名的方式,保留模块中的导出函数名。
[0023]
上述的基于二进制程序模块化的开源组件检测方法,开源组件的对比流程为:遍历待匹配的特征类型,获取模块和开源组件的该类型特征;遍历获取的特征,将匹配的特征添加到匹配特征列表中,当匹配的特征得分超过阈值,将该类型特征添加到匹配的特征类型列表中,此列表非空,则该模块复用开源组件。
[0024]
上述的基于二进制程序模块化的开源组件检测方法,在开源组件识别时,
[0025]
a、对于字符类型的特征,当二进制程序模块与开源组件中的字符特征一致时,则判定特征为匹配;
[0026]
b、对于函数的复杂分支序列,通过最长公共子序列长度匹配if/else特征,当最长
公共子序列长度超过设定阈值则判定为匹配。
[0027]
本发明的有益效果:本发明基于二进制程序函数具备层次化的特点,提取函数地址及调用关系,进行二进制程序模块化;基于特征的模块粒度开源组件检测,依据特征选择标准,提取二进制模块及源码的特征,对测试数据集进行开源组件检测,精度比b2sfinde提高60.12%,极大的降低了误报率,提高特征提取效率。
[0028]
本发明在评估模块粒度检测效率及cots中的开源组件应用时,结果表明即使增加模块化过程,依旧保持检测效率;而且本发明能够在cots中检测到开源组件的复用,并且定位具体模块,提高软件安全分析下游任务的效率。
附图说明
[0029]
图1为本发明整体框架示意图。
[0030]
图2为开源组件定位模块数量的频率统计图。
[0031]
图3为开源组件复用频率统计图。
具体实施方式
[0032]
下面结合附图和实施例对本发明进一步说明。
[0033]
实施例1:本实施例提供的基于二进制程序模块化的开源组件检测方法,如图1所示,整体分为两个阶段,分别为二进制程序模块化与基于特征的模块粒度开源组件识别。在模块化阶段,提取二进制程序的函数直接调用信息,并以图的形式表示这些信息,并使用基于模块度进行改进的bcm方法,对二进制程序中的函数进行聚类。在模块粒度的开源组件识别阶段,提取字符串、导出函数名、字符串数组、函数中复杂的分支序列特征,将二进制程序的各个模块与开源组件进行匹配,识别二进制模块与开源组件的对应关系。
[0034]
第一阶段:二进制程序模块化,主要包括以下内容
[0035]
1、属性选择及图的创建
[0036]
在二进制程序分析的工作中,常见的图有调用图(call graph,cg)、控制流图(control-flow graph,cfg)和数据流图(data flow graph,dfg)。
[0037]
调用图、控制流图都是表示有向传递的图,但侧重点和节点不同。调用图表示的是整个程序中函数之间调用关系的图,是程序内函数调用的流向,流向节点是函数,边表示调用关系。而控制流图是表示一个函数内的程序执行流的图,表示函数中语句跳转流向,流向节点是程序语句,边表示执行流。数据流图表示一系列操作之间的数据依赖关系的图,图中的边表示从一个操作的结果到另一个操作输入的数据流,一旦一条指令的所有输入数据值都被消耗完,它就会执行。当一条指令执行时,它产生一个新的数据,这个数据值被传播到其他连接的指令。二进制程序分析的工作人员利用较多的是控制流图和数据流图,便于更好的理解函数。
[0038]
但是针对二进制程序的模块化是全局的分析,在这种情况下,控制流图和数据流图则不具备适用性,因此,选择函数调用图,对整个二进制程序具备全局的分析。函数调用图表示一个程序的不同函数如何相互调用,相互调用的函数一般会共同协作完成同一功能,因此更大的概率属于同一个模块。通过函数地址及函数有向调用关系的提取构造模块化步骤的输入图。
[0039]
2、二进制程序模块划分
[0040]
(1)模块质量度量
[0041]
模块度q是常用的一种衡量社团划分质量的标准,其基本思想是把划分社团后的网络与相应的零模型进行比较,以度量社团划分的质量。所谓与一个网络对应的零模型,就是指与该网络具有某些相同的性质,如相同的边数或者相同的度分布等,而在其他方面完全随机的随机图模型。二进制程序模块划分是针对函数的聚类,可以类比网络图中的社团检测。因此,本文以社团检测中的模块度q作为模块划分效果的度量指标,模块度定义为:
[0042][0043]
其中,i和j表示节点;m是网络中所有边权重之和;a
ij
表示节点i和j之间的边权重;ki表示与节点i相连的边权重和;ci表示节点i所属社团;cj表示节点j所属社团;δ(ci,cj)表示节点i和节点j是否属于一个社团,属于值为1,否则为0。
[0044]
(2)二进制程序模块化方法
[0045]
网络的社团结构具有每个社团内部节点之间连接较为紧密,各个社团之间的连接相对来说比较稀疏的特点。程序“高内聚,低耦合”的模块与复杂网络的“社团”非常相似,并且社团检测技术的发展较成熟。因此本实施例使用社团检测的方法进行二进制程序的模块化。但是一些社团检测算法需要预先知道社团个数信息,例如谱分析、基于机器学习的社团检测算法,而二进制程序的模块数量是未知的。基于模块度概念的算法,不需要提前知道社团个数。
[0046]
本实施例通过提取的二进制函数地址和函数有向调用信息,对适用于无向图的基于模块度概念的凝聚算法进行改进,得到有向图检测的bcm方法,对二进制程序进行模块化。软件中实现的各个功能模块是具有层次性的,在源码文件目录中表现为各个文件夹中的文件,选择层次化社团结构分析的算法契合了软件功能实现的层次特性。
[0047]
具体的,使用bcm算法划分二进制程序模块分为两个阶段:
[0048]ⅰ、初始时假设每个函数都是一个独立的模块;对任意相邻的节点i和节点j,计算将节点i加入其邻居节点j所在模块(记为模块c)时对应的模块度增量δq,
[0049][0050]
其中,s
i,in
表示节点i与模块c内部函数连边权重之和;m是网络中所有边权重之和;wc是模块c内部所有边的权重和;sc是所有与模块c内部的函数相关联的边的权重和。
[0051]
计算节点i与所有邻居节点的模块度增量,然后选出其中最大的一个。当该值为正时,把节点i加人相应的邻居节点所在的模块;否则,节点i留在原模块中。这种模块合并过程重复进行,直到不再出现合并现象,这样就划分出了第一层模块。
[0052]ⅱ、基于过程ⅰ形成一个新的模块结果,模块之间连边的权重是两个模块之间所有节点连边的权重和。然后重复ⅰ中的方法对新的模块结果进行模块划分,得到第二层模块。以此类推,直到网络模块度q不再增加为止。
[0053]
二进制程序模块化示例算法如表1所示。
[0054]
初始化各个模块的模块度为-1(第2行),然后迭代进行节点移动,获得移动后的模
块度(第5-6行),若模块度增加,则合并模块(第7-9行),直到模块度不再增加(第10-11行),停止迭代过程。
[0055]
表1二进制程序模块化示例算法
[0056][0057]
利用社团检测技术进行二进制程序的模块划分,很好的解决了目前二进制程序模块聚类的问题,同时为新的模块粒度上的开源组件检测提供了基础。
[0058]
第二阶段:开源组件识别
[0059]
目前针对二进制程序的开源组件识别粒度为文件级别,解决的问题是检测整个二进制程序是否复用开源组件,未准确定位开源组件的位置。本实施例将二进制程序模块一一与开源组件匹配,定位开源组件所在模块,将文件粒度的识别细化到模块粒度,为软件安全工作中的下游任务缩小分析范围。主要内容如下。
[0060]
1、特征选择与提取
[0061]
现有的方法常用特征包括字符串、导出函数名、整型常量、全局数组、调用图(call graph,cg)和控制流图(control-flow graph,cfg)等。绝大多数的开源组件提取特征之后,几乎不存在枚举数组。在开源组件检测的过程中,通过整型数组检测到开源组件的比率接近于零。整型常量明显受到编译的影响,在编译和优化过程中,大量与源代码无关的立即数,如内存偏移量和堆栈偏移量,被作为噪声引入二进制代码中。调用图和控制流图也会因为编译优化而发生改变。
[0062]
本实施例排除以上对于检测没有实际价值、徒增特征提取工作量的全局数组和易受编译优化影响的整型常量、调用图与控制流图特征,选择字符串、字符串数组、导出函数名及函数的复杂分支序列。提取的特征如表2所示。
[0063]
表2特征选择
[0064][0065]
对于开源组件检测,现有的一些方法是将源码编译为二进制程序,然后进行二进制比对。但是会产生减少字符串等特征,从而导致较低的匹配率的编译设置和时间消耗问题。
[0066]
本实施例直接针对源码分析,在提取特征之前结合编译信息,只从与编译相关的文件中提取特征。一方面,减少了特征提取工作量,另一方面,剔除了不会被二进制程序复用的无关代码,例如test等文件。
[0067]
(1)字符串及数组、导出函数名
[0068]
针对二进制程序的每个模块,通过模块中每个函数的地址,获取该函数的所有指令地址,遍历指令的数据引用,提取所引用的字符串。
[0069]
以字符串提取算法为例,来描述从二进制模块中提取特征的过程。如表3所示。
[0070]
表3模块字符串提取算法流程
[0071][0072]
通过模块中每个函数的地址获取该函数的所有片段(第3-4行),遍历每个函数片段中的所有指令,遍历该指令的数据引用地址(第5-6行),当该地址存放的是字符串时,将字符串的地址及值添加到字符串列表中(第7-13行)。
[0073]
从整个二进制程序中可以获取所有导出函数名,提取模块中的导出函数名与此不
同,首先需要获取模块中的所有函数名,然后通过遍历整个二进制程序中的导出函数名的方式,保留模块中的导出函数名。针对给定的源代码文件,通过clang将源码解析成抽象语法树(abstract syntax tree,ast),把对源码的特征提取转为对ast的操作,从而提取源码中字符串、字符串数组及函数名。但是字符串特征很容易删除,特别是作为日志消息和错误消息。当软件中不包含可以匹配的有用字符串特征时,需要考虑其它特征的选择。
[0074]
(2)函数的复杂分支序列
[0075]
在二进制相似性比对工作中常用的调用图和控制流图很容易受到编译优化影响,为达到补充抗编译优化的代码特征的目的,这两种类型的特征排除在选择范围外。通过分析发现,函数中的switch/case、if/else结构在编译过程中是稳定的,因此,可以将switch/case、if/else的分支序列作为选择的特征,它们包含函数中比较、跳转和分支的相对完整信息。
[0076]
对于二进制模块,if/else特征是通过分析它的比较和跳转指令来提取,利用模块中函数跳转表提取switch/case特征。在clang解析源码之后,分析中间代码的分支序列,提取特征。
[0077]
2、模块粒度的开源组件识别
[0078]
如图1所示,开源组件的检测阶段是将二进制程序的每个模块与开源组件源码进行匹配。具体过程是从二进制程序各个模块和源码中提取字符串、导出函数名和函数的复杂分支序列特征,将每个模块特征都与源码特征比对,检测出开源组件所在的二进制模块。
[0079]
对于字符类型的特征,当二进制程序模块与开源组件中的字符特征一致时,则认为特征是匹配的。
[0080]
函数的复杂分支序列是通过语义对其进行特征匹配。通过最长公共子序列长度匹配if/else特征,当最长公共子序列长度超过设定阈值则判定为匹配。将源码添加默认分支的switch/case无序列表与模块中的switch/case进行序列比对。每种特征的匹配判定涉及的阈值根据经验确定,当特征的匹配分数超过阈值,则判定该模块复用开源组件。
[0081]
各个模块与开源组件具体的匹配流程示例如表4所示。
[0082]
表4模块粒度开源组件监测算法示例
[0083][0084]
遍历待匹配的特征类型(第4行),获取模块和开源组件的该类型特征(第6-7行),遍历获取的特征,将匹配的特征添加到匹配特征列表中(第8-11行),当匹配的特征得分超过阈值,将该类型特征添加到匹配的特征类型列表中,此列表非空,则该模块复用开源组件(第12-15行)。
[0085]
实施例2:本实施例通过实验评估模块粒度的开源组件识别,全局数组特征对于检测结果的影响,检测效率,并且对实际的商用软件(cots)开展检测分析。使用idapro反汇编二进制程序,利用idapython提取函数调用信息和二进制程序各个模块特征,通过clang和llvm工具,提取源代码特征。
[0086]
1、数据集
[0087]
模块粒度的开源组件识别评估实验中的二进制程序利用已知复用关系的二进制程序作为测试数据集,包括bcfinder、isrd和modx中的测试程序。源码从github等托管平台获取。二进制程序测试数据集以及与其具备复用关系的开源组件源码描述如表5所示
[0088]
表5用于准确率评估的数据集
[0089][0090]
模块粒度的cots检测应用中所涉及的部分软件数据集如表6所示。
[0091]
表6部分cots
[0092][0093]
2、对比方法
[0094]
实验从目前存在的开源组件检测工作中选择已开放源码的b2sfinder和腾讯安全科恩实验室正式发布的在线软件成分分析平台binaryai作为对比方法。b2sfinder在目前
文件粒度的二进制程序匹配开源组件源码工作中,选取特征较为全面,结果优于其它方法。binaryai是二进制文件软件成分分析(software component analysis,sca)的智能分析平台。将b2sfinder和binaryai与本文模块粒度的开源组件识别结果进行对比。
[0095]
3、模块粒度的检测评估
[0096]
(1)评估指标
[0097]
实验选择precision、recall和f1-score作为评估指标,定义如公式所示。
[0098][0099][0100][0101]
其中,tp指被正确匹配为复用开源组件的数量,tn被正确匹配为非复用开源组件的数量,fn指被错误匹配为复用开源组件的数量,tp被正确匹配为非复用开源组件的数量。
[0102]
(2)模块粒度的开源组件检测效果评估
[0103]
针对实验测试数据集,进行模块粒度的检测评估,将本文模块粒度的检测bsca-m与3种方法进行比对,分别是未进行模块化的检测bsca、目前二进制程序匹配开源组件源码工作中较优的b2sfinder、binaryai方法,结果如表7所示。
[0104]
表7检测效果评估
[0105]
方法precisionrecallf1-scorebsca-m82.35%63.64%71.8%b2sfinder60%81.82%69.23%binaryai84.62%50%62.86%
[0106]
从表中可知,本发明模块粒度检测开源组件的精度为82.35%,接近于binaryai,与b2sfinder达到的仅为51.43%的精度相比,本文精度明显提高了约60.12%。原因在于本发明模块粒度的检测,极大地减少假阳例,降低了误报率。召回率处于b2sfinder与binaryai之间,很好地平衡了两种方法的召回率差值过大的现象。
[0107]
整体评估结果来看,本发明方法的f1-score达到71.8%,分别比binaryai和b2sfinder提高14.22%、3.71%,并且将二进制程序中是否包含开源组件的检测细化为定位组件所在的具体二进制模块。
[0108]
(3)全局数组特征的影响评估
[0109]
本发明模块化的开源组件检测方法未选择整型数组、枚举数组特征,b2sfinder的特征选择包括全局数组。为评估全局数组特征对于检测结果及效率的影响,需要排除模块化的过程,因此,将bsca(未进行模块化)与b2sfinder进行对比分析,结果如表8所示。
[0110]
表8全局数组特征影响评估
[0111]
方法precisionrecallf1-score时耗bsca64%72.73%69.33%920.38b2sfinder60%81.82%69.23%1151.01
[0112]
从表中可见,未进行模块化的bsca与b2sfinder相比,召回率微低,但是精度稍高,主要原因则是b2sfinder中一部分假阳例是通过此类全局数组的匹配到的,降低了检测精度。f1-score趋于相等,表明去除掉b2sfinder中的整型数组、枚举数组,对于f1-score几乎没有影响。
[0113]
针对测试中涉及到的所有开源组件进行特征提取,将未提取与提取此类全局数组的效率进行对比。从表中可知,bsca相对于b2sfinder,由于未选择整型和枚举数组特征,对于开源组件的特征提取时耗明显降低20%。
[0114]
因此,本实施例去除全局整型数组、枚举数组的选择,减少了针对全局数组的特征提取工作量,而且在特征匹配的阶段,避免了对于无效特征的比对。
[0115]
(4)效率评估
[0116]
对本文模块粒度的检测bsca-m的两个阶段,即模块化过程和开源组件检测的时间与b2sfinder的开源组件检测进行对比分析,结果如表9所示,单位为秒(s)。
[0117]
表9效率评估
[0118]
方法模块化开源组件检测总和bsca-m63.4021356.5271419.929b2sfinder—1255.6821255.682
[0119]
从表中数据可知,本发明方法bsca-m与b2sfinder相比,整个流程耗时较为接近。虽然本发明方法包含针对二进制程序的模块化阶段,此过程增加了提取二进制程序信息和利用社团检测技术划分二进制模块的时间,并且在检测阶段,对每个模块提取特征并进行匹配,但是依旧保持了效率。
[0120]
(5)开源组件定位模块数量及频率分析
[0121]
对二进制程序模块粒度的开源组件检测结果进行分析发现,复用的开源组件存在定位不止一个模块的情况。最理想的情况是复用开源组件的函数被划分到同一模块,存在只有一个模块与开源组件匹配。针对所有的测试二进制程序进行开源组件定位模块数量及相应的频率统计,如图2所示。
[0122]
从图中可知,开源组件定位模块数量为1的频率为57.14%,超过3的仅占21.42%。因此,二进制程序的模块化结果整体较好,将协作完成同一功能的函数划分到同一个模块,复用同一组件的函数不会被错误划分到其他模块,在开源组件检测阶段,则可以将复用的开源组件准确定位到同一个模块中,达到较好的模块粒度检测结果。
[0123]
4、模块粒度的cots开源组件检测应用
[0124]
商用软件的发布通常是以闭源的方式,当软件中复用了包含漏洞的开源代码,软件面临着一定的安全风险,故对商用软件进行开源组件检测,定位组件所在的具体模块,实验选取一些常用的商用软件进行检测,部分cots复用的开源组件列表如表10所示。
[0125]
表10cots复用的开源组件
[0126][0127]
根据对cots模块粒度的开源组件检测,分析发现一些开源组件在cots中被频繁复用,结合模块粒度的检测评估中复用的开源组件,对此类开源组件被复用的频率进行统计,结果如图3所示。
[0128]
图中显示了被频繁复用的前5种开源组件,其中xz在这些组件中被复用频率最高,占比达到50%,其次是zlib和libpng,为16.67%。因此,当组件中包含了漏洞,本发明模块粒度的开源组件检测可以缩小漏洞在复用这类组件的大量程序中的搜索范围,只需要在对应模块中进行漏洞检测。
再多了解一些

本文用于创业者技术爱好者查询,仅供学习研究,如用于商业用途,请联系技术所有人。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

相关文献