本发明涉及软件测试、威胁检测等技术领域,尤其涉及一种基于程序源代码文本特征,能够以较低的计算成本实现对大规模样本进行相似检测的方法。
发明背景
随着软件工程的发展,相似程序代码检测在软件测试、恶意软件检测、知识产权保护以及计算机教学等领域均能发挥重要的作用。目前,针对程序代码的相似度检测问题,国内外已有若干工具或系统实现,所采用技术包括串匹配方法、抽象语法树(abstractsyntaxtree,ast)方法、编译优化和反编译方法以及文档指纹(documentfingerprint)方法。
现有的代码相似度检测方法,所存在的问题包括:(1)检测效率低下,无法应对大规模样本检测需要;(2)要求被检测程序具备正确的逻辑或可被指定编译器编译,无法处理异常程序样本;(3)抗干扰能力差,易受到增加冗余代码、指令重排序等混淆手段影响。
技术实现要素:
本发明针对大规模样本的程序源代码相似度检测问题,提出了一种利用程序源代码中文本特征的局部敏感哈希对大规模样本进行相似度检测的方法,具有适应语言广、检测速度快、抗干扰能力强和可精确定位相似片段的优点。
本发明提供了一种适用于大规模样本的程序源代码相似度检测方法,包括如下步骤:
步骤1:对输入的程序源代码进行预处理,删除与程序逻辑无关的干扰信息,将程序源代码中的整数型常量统一为同一种进制的表示;
若进行模块(module)级相似度检测,执行步骤2,否则,若进行工程级相似度检测,将程序源代码的每个工程源代码作为一个工程级的待检测样本,直接进入步骤3执行;
步骤2:将经过步骤1处理后的程序源代码样本按照模块进行切分,得到模块级的待检测样本,进入步骤3执行;
将步骤1输出的源代码按照模块定义语法规则划分为若干代码区段,每个代码区段的内容为原始工程中对应模块内部的程序语句,不包含模块定义及端口定义语句。使用“原始样本编号”和“区块对应模块名”对所有划分后所得到的代码区段进行标识,作为步骤3的检测样本。
步骤3:对于每个模块级或工程级待检测样本,使用以下3种方法中的任意一种或多种组合提取其文本特征及并计算特征权重。
方法1:n元语法提取,具体步骤为:对于样本中的每个源代码行,假设其长度为m,自行首开始,按照固定的宽度n,以1为步长,将改行切分成(m-n+1)个长度为n的文本特征。m、n均为正整数,且n小于m。
方法2:哈希取模提取,具体步骤为:对于方法1所提取的文本特征,使用相同的哈希函数逐一计算哈希值,将所得的哈希值与固定的常数k进行取模运算,保留计算结果等于0的文本特征,舍弃计算结果不等于0的文本特征。k为正整数。
方法3:滑动窗口提取,具体步骤为:对于方法1所提取的文本特征序列,按照固定的窗口大小k,自文本特征序列头部选取k个特征,以相同的哈希函数计算各特征的哈希值,将窗口内k个特征中哈希值最小的文本特征保留,剩余特征丢弃;以1为步长,自头部向尾部移动窗口,使用上述方法在每个窗口内选出至多一个特征,若哈希值最小的特征已在此前的窗口中被选取,则不再重复选取该特征,即当前窗口内无特征被选取。
对于提取出的文本特征序列,将每个特征在序列中出现的频次作为其权重。
步骤4:对于每个待检测样本,使用步骤3提取的文本特征序列及其特征权重序列,根据给定的参数f计算出样本的局部敏感哈希值——f维向量hash。
步骤4中所使用的token_hash算法为一般的常规哈希算法,可使用计算开销较低的md5、sha1等算法实现。
步骤5:根据检测敏感度要求,建立t张有序表(t1,t2,…,tt),有序表ti中的元素由步骤4中所得到的全部样本特征向量应用排列规则πi变换产生。每个有序表ti对应一个特异的排列规则πi,排列规则πi是将样本的特征向量中指定的二进制位移至最高位,设移动的二进制位组成高位pi;i=1,2,…,t。
对于待检测样本的特征向量
在大规模应用场景下,上述步骤可并行化实现,由于ti是有序表,从中查找高位pi相同元素的最差时间复杂度为o(pi),理想情况下,即待检测样本的特征向量呈现完全随机分布,时间复杂度为o(logpi)。
本发明相较于现有的程序源代码相似度检测方法,其优点和积极效果在于:
(1)本发明所提出的方法借助局部敏感哈希函数,将待检测的程序样本以较低的计算开销转化为低维特征向量,并借助基于组合数学原理构造的查找表机制,大大降低了查找相似样本的时间复杂度,使其能够应用于大规模样本。
(2)本发明所提出的方法能够适用于各种类型的程序语言,不依赖编译器和语法分析器,能够对存在语法或逻辑错误的程序样本进行检测。
(3)本发明所提出的方法可根据实际应用场景,选择不同的文本特征提取方法,以实现对增加冗余代码等干扰手段的抑制和消除。
(4)本发明所提出的方法能够通过调整特征提取和局部敏感哈希计算中的参数,灵活适应不同的检测灵敏度需求。
附图说明
图1是本发明的大规模样本的程序源代码相似度检测方法流程图;
图2是veriloghdl源文件中同一整数形常量的不同表示方式示意图;
图3是veriloghdl的两种模块定义语法示意图;
图4是局部敏感哈希算法的流程示意图。
具体实施方式
下面结合附图和实施例来对本发明进行详细说明。
本发明的适用于大规模样本的程序源代码相似度检测方法,可针对整个待检测程序或待检测程序中的各个方法、模块,通过多种方式提取文本特征及其权重,利用局部敏感哈希(localitysensitivehashing)算法将所提取的文本特征转化至长度为32比特-128比特的特征向量,以特征向量间的汉明距离(hammingdistance)表示程序源代码间的相似度,并通过建立一系列查找表来实现对大规模样本检测的加速,具有适应语言广、检测速度快、抗干扰能力强和可精确定位相似片段的优点。
本发明实施例结合veriloghdl工程源文件,如图1所示,分下面五个步骤来说明本发明方法的具体实现过程。
步骤1:将veriloghdl工程源文件作为原始输入样本,对原始输入样本进行预处理,具体处理流程如下:
首先,排除样本中所有不包含veriloghdl源代码的文件;然后,对于每个源代码文件,删除所有注释信息,并将其包含的全部.h头文件内容展开。本发明实施例中的展开方式为将头文件全部内容复制至当前源文件。将不同形式表示的整数型常量统一为不标明位宽的十进制表示,如图2中的第一种表示形式;最后,将源代码中全部空格符号、制表符以及除换行符外的所有不可见字符移除。
步骤1预处理的目的是,不管原始输入样本采用何种程序语言,都最大限度消除与程序逻辑无关的干扰信息。在本步骤,还可根据待检测样本所使用程序语言的语法特性和语法规则,设计有针对性的正则化方法,来最大限度消除与程序逻辑无关的干扰信息。
步骤2:若需要进行模块(module)级相似度检测,则需要将经过步骤1处理后的veriloghdl工程源代码文件样本,根据如图3的(a)和(b)所示的veriloghdl的模块定义语法规则划分为若干代码区段,每个区段的内容为原始工程中对应模块内部的逻辑描述语句,不包含模块定义及端口定义语句。使用“原始样本编号”和“区块对应模块名”对所有划分后所得到的代码区段进行标识,作为步骤3的检测样本。
若不需要进行模块级相似度检测,即进行工程级相似度检测,则直接执行步骤3。是否进行模块级相似度检测,由用户根据应用场景或需求来设置。
图2中是veriloghdl中的两种模块定义语法规则,步骤2首先从veriloghdl工程源文件中提取出若干模块定义区段,根据模块定义语法规则,从各模块中提取出不包含模块定义及端口定义语句的逻辑描述语句,起到消除同义语法对相似度检测所带来的干扰。
步骤2在本发明中是可选的。步骤2将原始输入的待检测样本按照模块进行切分后作为后续检测步骤的输入,是为了实现模块级检测。对于c语言等非面向对象的程序语言,可将方法(method)作为检测的基本单元;对于c++、java、python等面向对象的程序语言,可将类(class)作为检测的基本单元。按照基本单元来对源代码进行模块级划分。
通过对样本进行适当的切分,采用模块级的相似度检测,可以提高相似度检测准确率,并帮助精确定位相似区段。
将每个模块的源代码或每个工程的源代码作为一个待检测样本输入步骤3和4中处理。
步骤3:一个待检测样本为模块级源代码或工程级源代码,对于每个模块级或工程级待检测样本,使用以下3种方法中的任意一种或多种组合提取其文本特征并计算特征权重:
方法1:n元语法(n-gram)提取,具体步骤为:对于样本中的每个源代码行,假设其长度为m,自行首开始,按照固定的宽度n,以1为步长,将该行切分成(m-n+1)个长度为n的文本特征。其中,n、m均为正整数。
方法2:哈希取模(mod)提取,具体步骤为:对于方法1所提取的文本特征,使用相同的哈希函数逐一计算哈希值,将所得的哈希值与固定的常数k进行取模运算,保留计算结果等于0的文本特征,舍弃计算结果不等于0的文本特征。其中,k为正整数。
方法3:滑动窗口提取,具体步骤为:通过方法1对每行源代码提取到一个文本特征序列,按照固定的窗口大小k,自文本特征序列头部选取k个文本特征,(1)以相同的哈希函数计算窗口内的各文本特征的哈希值,将窗口内哈希值最小的文本特征保留,剩余特征丢弃;(2)以1为步长,对文本特征序列自头部向尾部移动窗口,(3)使用(1)的方法在每个窗口内选出至多一个文本特征,若哈希值最小的文本特征已在此前的窗口中被选取,则不再重复选取该特征,即当前窗口内无文本特征被选取。其中,k为正整数。
通过上面1或2或3,对模块级或工程级的待检测样本的每行源代码都提取到一个文本特征序列,对每个文本特征,统计该文本特征在所有文本特征序列中出现的频次作为该文本特征的权重。
通过n元语法提取方法所提取的文本特征数量最多,能够完整反映原始样本,但抗干扰能力弱于其他两种方法。哈希取模或滑动窗口提取方法,通过对文本特征的筛选,可降低冗余代码等对相似度检测的干扰,但会提高非相似样本被检测为相似样本的概率,即假阳性率。
步骤4:对于每个待检测样本,使用步骤3提取的文本特征序列及其特征权重序列,按照图4所示的算法流程,根据给定的参数f计算出样本的局部敏感哈希值——f维向量hash。
具体计算步骤如下:
1.首先,初始化一个f维零向量hash;
2.然后,对于当前样本的特征集合中每一个特征token及其权重weight:
a.使用token_hash算法计算出token的f位哈希值token_hash;
b.对于token_hash中的每一个二进制位,若其值为1,则在hash的对应维加上值weight;若其值为0,则在hash的对应维减去值weight。
3.最后,对于hash中的每一维,若其值大于0,则将该维置1;反之,则置0。此时,所得到的f维二进制向量hash即为当前样本的局部敏感哈希值。
本步骤所使用的token_hash算法为一般的常规哈希算法,可使用计算开销较低的md5、sha1等算法实现。本步骤采用局部敏感哈希算法计算样本的局部敏感哈希值,通过简单的哈希运算叠加,将高维离散文本特征映射至f维向量空间,将样本间相似度计算问题转化为对汉明距离不超过一定阈值的f维向量对查找问题,降低了相似样本查找开销。
为避免内存不对齐造成的性能损失,f通常取值为32的正整数倍,例如:32、64、128。
根据f维向量的计算方法,计算得到每个模块级或工程级的待检测样本的局部敏感哈希值,即待检测样本的特征向量,即待检测样本的指纹。
步骤5:根据检测敏感度要求,如要求特征向量间的汉明距离不超过正整数h,建立t张有序表(t1,t2,…,tt),有序表ti中的元素由步骤4中所得到的全部样本特征向量应用排列规则πi变换产生。排列规则πi是将样本指纹中指定的二进制位移至指纹最高位。排列规则πi的一个具体实现方式是,将样本特征向量平均分成若干段,从中选取若干段按原先后顺序拼接,作为高位,剩余段按原先后顺序拼接作为低位。对每个有序表ti有一个排列规则πi,对应有高位pi。
对于待检测样本的特征向量
在大规模应用场景下,上述步骤可并行化实现,由于ti是有序表,从中查找高位pi相同元素的最差时间复杂度为o(pi),理想情况下,即待检测样本的特征向量呈现完全随机分布,时间复杂度为o(logpi)。
步骤5中的相似样本查找方法,通过利用组合数学中的抽屉原理,借助若干哈希表,进一步降低了相似样本对查找的时间复杂度,使得相似样本检测过程能够并行化实施。