一种重构CoSy中间表示的缓冲区溢出检测方法
技术领域
本发明涉及一种重构CoSy编译后的中间表示并进行缓冲区溢出检测的方法,属于信息安全技术领域。
背景技术
随着计算机技术和通讯技术的迅速发展,整个社会对计算机技术的依赖程度越来越高,各方面对系统的安全性要求越来越高。而现实情况是,计算机系统的安全事件有愈演愈烈的趋势,在近几年的安全事件中与缓冲区相关的安全事件超过了50%。采用C语言开发的程序缓冲区溢出安全事件具有高发生率。C语言在计算机软件开发中具有不可替代的作用,被广泛应用于嵌入式开发、系统软件、应用软件等各个方面,而C语言自身缺乏安全机制。正是由于C本身的缺陷,同时又广泛应用,造成了目前缓冲区溢出攻击横行的状况。
缓冲区溢出是病毒编写者和特洛伊木马编写者偏爱使用的一种攻击方法。攻击者或者病毒善于在系统当中发现容易产生缓冲区溢出之处,运行特别的程序,获取优先级别,指示计算机破坏文件,改变数据,纰漏敏感信息,产生后门访问点,感染或者攻击其他计算机。
一方面现在缓冲区溢出已经发展成了基于栈的缓冲区溢出、基于堆/BSS的缓冲区溢出、格式化缓冲区等多种方式,而且有很多的模板可以直接套用,实现方便。另一方面由于软件设计理论中,程序的正确性始终是未解决的问题,即是否能设计出绝对正确的软件是无法得到证明的,也从根本上决定了漏洞(bug)的存在,同时由于软件开发存在大量人为的因素,使得漏洞无法避免。这些因素共同作用,加剧了缓冲区溢出引发的安全问题。
目前用来检测缓冲区溢出漏洞的方法主要有两种:动态检测和静态检测。动态检测通过输入测试数据,动态执行程序来发现软件中存在的错误。动态检测虽然可以不修改程序的源代码,但是它对测试数据的输入有很大的依赖性,这就对发现软件中的错误有一定的局限性。同时随着程序规模的扩大,状态空间急剧增大,动态检测难以覆盖日益增大的程序状态空间,很多安全漏洞问题使用动态测试很难被发现。另外,软件运行过程中进行检测,发现错误后进行修改代价也比较大,是早期发现错误进行修改所需成本的好多倍。因此在动态检测前对软件进行静态检测可以尽早发现软件中的漏洞,加快开发速度。
静态检测主要是通过对源代码进行分析,来判断是否存在缓冲区溢出漏洞。静态检测相对于动态检测的特点是:a)它明确分析对象为源代码而不是更抽象的系统描述;b)其目标不是证明源代码或者系统设计符合特殊的安全策略,而是发现可能的不安全因素。静态检测包括:(1)词汇分析(lexical analysis)法,收集不安全函数并为之建立数据库,通过源代码与数据库中数据的比较来验证是否存在不安全因素;(2)限制条件(constraints)分析法,通过设计限制条件来判断程序对该函数的使用是否违反操作;(3)程序流(flow-based analysis)法,通过对控制流图、函数调用关系图等语法、语义信息的分析来判断。这些静态检测方法各有优缺点,限制条件分析法,以其检测全面,判断准确,成为一种比较好的缓冲区溢出检测机制。其中有一种简单易实施的方法,具体为:
(1)针对待分析源程序,直接利用GCC编译器生成抽象语法树;
(2)消除抽象语法树中的冗余信息;消除冗余信息的方法是根据语法树中“srcp”字段来判定该节点是否是冗余信息。
(3)遍历消除冗余信息后的抽象语法树,为每个缓冲区增加一个区间对,表示对应缓冲区的初始分配区间alloc和实际长度区间len;初始分配区间alloc(var)=[a, b];实际长度区间表示为len=[c,d],a ,c和b ,d分别表示初始分配区间和实际长度区间的上限和下限;
(4)将与缓冲区相关的待分析源程序语句和函数调用抽象为对应缓冲区区间对的操作,即对初始分配区间alloc和实际长度区间len进行更新;
(5)判断每个缓冲区的状态:若b≤c,则该缓冲区为安全状态,不会发生缓冲区溢出;若c≤b≤d,则缓冲区为不安全状态,可能会发生缓冲区溢出;若a≥d,则判定为缓冲区为危险状态,肯定会发生缓冲区溢出。
这种方法的缺点是:(1)冗余算法只针对GCC编译器产生的抽象语法树,不具有通用性,对于类似的其它编译系统产生的抽象语法树,算法实现起来需要较大改动。(2)为每个缓冲区增加一个区间对,表示方法不够准确,会导致检测不全面。例如忽略了对联合体,结构体变量的判断。(3)对缓冲区状态的判断方法也不够准确,会导致判定结果不准确。
发明内容
本发明的目的在于克服上述技术中的不足,提供一种重构CoSy (ACE公司的编译器开发工具)中间表示的缓冲区溢出检测方法,是一种简便快捷的缓冲区溢出检测机制。
按照本发明提供的技术方案,所述重构CoSy中间表示的缓冲区溢出检测方法,包括以下步骤:
a、CoSy编译器分析扫描源程序,生成中间表示文档CCMIR (Common CoSy Medium-level Intermediate Representation);
b、重构CCMIR文本,并为每个局部变量增加安全属性,用于对缓冲区溢出进行判定;
c、对重构后的CCMIR进行缓冲区溢出的判定。
具体的,步骤a中编译器对源程序进行词法分析和语法分析,生成文本格式的中间表示文档CCMIR。
步骤b包括:
b1. 定义DTD规则,将CCMIR文本转化为XML文本存储;
b2. 利用解析工具重新建立CCMIR文档,并在重构的过程中对每个非静态局部变量增加安全属性,用来对缓冲区溢出进行判断;
b3. 在重构的过程中去掉与分析程序流和数据流无关的信息。
所述步骤b1将CCMIR文本分为四类,用于定义DTD规则:
第一类、CCMIR的标识位:每遇到一个标识位bbx,建立一个结点元素node与之对应,将结点的标识属性值记录为x;
第二类、变量声明:为每一个变量建立结点元素node的子元素variable,variable有四个属性,分别是:变量名称、变量类型、变量的大小、变量存储类别。所述变量的存储类别分为静态存储区和动态存储区;若该变量是结构体或联合体变量,则为其每个成员变量建立属性;
第三类、记录中的表达式:为每个表达式建立结点元素node的子元素expression,expression有两个属性,分别是:表达式类型和具体的表达式内容,所示表达式类型用来区分一般表达式和函数调用;
第四类、记录中的跳转语句:跳转语句包含goto语句和if语句;为跳转语句建立边元素edge,在边元素中建立四个子元素,分别是:from元素记录起始节点、to_l元素记录左子树节点、to_r元素记录右子树节点、if_condition元素记录跳转条件,如果不存在if语句,则if_condition值为1。
步骤b2利用XML文本解析器解析XML文本,重新建立CCMIR,步骤为:
b21. 依次读入XML文本的元素;
b22. 判断读入的是结点元素还是边元素;
b23. 若读入的是结点元素,则根据其变量类型属性建立相应的树节点,读子元素及其属性,判断是否需要给该变量增加安全属性;
b24. 若读入的是边元素,就在from、to_l和to_r标识的节点之间建立复杂关系。
步骤b23所述的安全属性的增加方法是:若子元素为variable,读该子元素的变量存储类别属性,若所述变量存储类别属性为动态存储区,则为该变量增加整数区间对,即安全属性;若该变量是结构体或联合体变量,则为该变量的每个成员添加安全属性。
步骤b23所述的安全属性包括:一个整数区间对,该区间对表示对应变量的初始分配区间alloc和实际长度区间len,初始分配区间alloc表示为:alloc(v)=[v_alloc_lower, v_alloc_upper];实际长度区间len表示为:len(v)=[v_len_lower, v_len_upper]; v_alloc_upper,v_len_upper和v_alloc_lower, v_len_lower分别表示初始分配区间上限、实际长度区间上限,和初始分配区间下限、实际长度区间下限。
步骤c将与缓冲区相关的待分析源程序语句与函数调用抽象为对局部变量属性信息的操作,即对初始分配区间alloc和实际长度区间len进行更新;
判断每个缓冲区的状态:
c1) 若v_len_upper≤v_alloc_lower,则判断该缓冲区不会溢出;
c2) 若v_alloc_upper<v_len_lower,则肯定会发生缓冲区溢出;
c3) 若以上两者都不满足,则可能会发生缓冲区溢出;
对c2和c3的情况做出报警提示。
本发明的有益效果是:
(1)本发明采用的重构CoSy中间表示的方法,实现去除冗余信息的方法有很好的通用性,适用于所有树形结构的中间表示。
(2)利用XML成熟的解析工具建立CCMIR,可以降低建立CCMIR的复杂度。
(3)将已有技术中的为每个缓冲区添加安全属性,改为为所有非静态局部变量添加安全属性,这样表述更准确,实施起来更简单明确,使得判断结果更为全面。
附图说明
图1是本发明的流程示意图。
具体实施方式
下面结合附图和具体实施例对本发明进行详细描述。
如图1所示,本发明所述的重构CoSy中间表示的缓冲区溢出检测方法,其中的缓冲区溢出是指堆栈上的缓冲区溢出,该方法的具体流程如下:
步骤a、CoSy(ACE公司的编译器开发工具)编译器分析扫描源程序,生成中间表示CCMIR;
步骤b、重构CoSy中间表示CCMIR文本,并为所有非静态局部变量增加属性,用于对缓冲区溢出进行判定;
步骤b中,重构CCMIR的方法是:
b1) 定义DTD规则,将CCMIR文本转化为XML文本存储;
之所以要转换为XML文档,是因为CCMIR层次结构非常适合用XML文档表示,并且XML文档具有很强的表达能力,可以表示复杂的数据结构;而且也有成熟的解析XML文档的应用程序接口(API)可以使用。
CCMIR文档转换为XML文本的步骤:
首先创建类型文档定义(Document Type Definition,简称DTD),在DTD中定义两种重要元素结点(node)元素和边(edge)元素,结点(node)元素对应于CCMIR中的段;边(edge)元素对应于CCMIR中的段和段之间的联系。根据上述定义,将CCMIR文本中内容分为四类,制定不同的转换规则如图1。
第一类:CCMIR的标识位。每遇到一个标识位bbx,建立一个node元素与之对应,将node的标识(id)属性值记录为x;
第二类:变量声明。为每一个变量建立node元素的子元素variable。variable有四个属性,name记录变量名称,type记录变量类型,size记录变量的大小,alloc_type记录变量的存储类别(静态存储区static,动态存储区dynamic)。
第三类:记录中的表达式。为每个表达式建立node元素的子元素expression。expression有两个属性,type记录表达式类型,用来区分一般表达式和函数调用,content记录具体的表达式内容。
第四类:记录中的跳转语句。跳转语句包含goto语句和if语句。为跳转语句建立edge元素,在edge元素中建立四个子元素,from元素记录起始节点,to_l元素记录左子树节点,to_r元素记录右子树节点,if_condition元素记录跳转条件,如果不存在if语句,则if_condition条件值为1。
根据DTD规则将CCMIR文本转化为XML文本。
b2) 利用解析工具重新建立CCMIR文本,并在重构的过程中对所有非静态局部变量增加安全属性,用来对缓冲区溢出进行判断。
利用XML文本解析器,如SAX,DOM,根据需要解析XML文本,重新建立CCMIR。
如果读入的是node元素,就根据type属性建立一个相应类型的树节点,读入node元素的子元素,若子元素为variable(即变量元素),读该子元素的alloc_type属性,若该属性为“dynamic”,则为该变量增加整数区间对,即只对非静态局部变量添加安全属性,若该变量是结构体或联合体变量,则为该变量的每个成员添加整数区间对。该区间对包括表示对应变量的初始分配区间alloc和实际长度区间len;初始分配区间alloc表示为:alloc(v)=[v_alloc_lower, v_alloc_upper];实际长度区间len表示为:len(v)=[v_len_lower, v_len_upper]。将对缓冲区溢出问题的判定转换为对整数区间对的判定问题。如果读入的是edge元素,就在from和to属性标识的节点之间建立复杂关系,这样一颗完整的CCMIR树就可以建立起来。
CoSy编译系统是以源代码的函数为单位生成中间表示CCMIR的,CCMIR也是一个语法树,它与函数一一对应。CCMIR中每个函数调用被当作一个赋值操作,对同一个函数的调用对于被调函数本身来讲是一样的,因此每个函数只处理了一次。
因此按照上述方式可以为每个函数的非静态局部变量增加安全属性,并且函数调用被当作赋值来处理,在增加安全属性的过程中并不存在重复性的工作。
b3)在重构的过程中可以去掉与分析数据流、控制流无关的信息。
在重构的过程中可以根据需要决定有些元素是否需要恢复,例如,程序不关心类型定义中类型的位宽,类型定义结点的size属性就不需要恢复。通过这种方式可以去掉与分析数据流、控制流无关的信息,用于高效地进行代码分析。
这样重构后的CCMIR既可以保持和CoSy的一致性,利于以后对程序的分析和测试,又可以实现CCMIR与CoSy前端的分离,从而达到利用前端的结果,构造适应不同应用的后端的目的。
步骤c、对最终的CCMIR进行缓冲区溢出的判定。
安全判定问题的复杂性随着程序状态空间的增大而趋于复杂,而程序状态空间与所需分析的变量相关:变量越多,状态空间越大。对于单个变量,假设其在程序中只出现一次,则对其的安全判定很简单;直接使用与其相应的安全属性来判定。但是,当一个变量现在上下文中多次出现时,情况变得十分复杂:它的值不但会影响其本身的安全性,还可能会影响所有与其有关联的表达式的值,也就是说这个变量变得不再独立。
完整的分析应该沿着程序执行的每条路径来进行。将程序的一个执行当成是一条穿过状态空间的路径,这样,安全判定就是首先找到包括了所有可能的路径最小的一个域,然后逐一按照安全属性进行分析的过程。
具体的判定方法:
将与缓冲区相关的待分析源程序语句与函数调用抽象为对局部变量安全属性信息的操作,即对初始分配区间alloc和实际长度区间len进行更新;
判断每个缓冲区的状态:
a) 若v_len_upper≤v_alloc_lower,则判断该缓冲区不会溢出;
b) 若v_alloc_upper<v_len_lower,则肯定会发生缓冲区溢出;
c) 若两者都不满足,则可能会发生缓冲区溢出。
对b)和c)缓冲区的两种状态都给出报警提示。
通过这样的方法能够快速的重构代码,也可以自动检测程序中潜在的会造成缓冲区溢出的漏洞。
通过上面所述的实施例子可见:采用将CCMIR文本中的静态信息存储在XML文本中的方式,快速有效地对编译器的中间表示进行了重构,保证了重构后的中间表示与CoSy编译器生成的CCMIR的一致性,有效地去除了与分析数据流和控制流无关的信息。
与传统的去除冗余信息的算法比较:去冗余的算法虽然可以去掉与分析数据流和控制流无关的冗余信息,并保证信息的完整性,但是算法有一定的复杂性,操作起来并不简单易行,并且在算法的开始需要先把抽象语法树文本进行格式化,使得描述同一主节点的从节点在同一行上,如果代码量较大,这样的操作就繁琐,实现起来并不十分自动化。更重要的是,这种算法不具有通用性,对于类似的其它结构的中间表示,算法实现起来需要的改动较大。
上面的实施例子中的XML文本转化的方式,也能实现去除冗余信息的目的,实现起来更自动化,信息存储更规范化,信息显示更人性化,执行起来也更快速高效,且该方式有很好的通用性,适用于所有结构的中间表示。
实施例子在重构过程中为所有非静态局部变量添加安全属性,相比直接在CCMIR中添加安全属性,XML文本的表示方式更利于计算机来处理,有利于对信息的处理与交互。然后以树形化的形式显示源程序结构关系,帮助用户可以快速有效地理解程序的结构,也为对源程序进行更深层次的静态分析打下基础。
实施例子利用XML成熟的解析工具建立CCMIR,可以降低建立CCMIR的复杂度;更为重要的是实现CCMIR与CoSy的分离,从而达到利用CoSy的前端结果,构造适应不同应用的后端的目的。同时将已有技术中的为每个缓冲区添加安全属性,改为为所有非静态局部变量添加安全属性,这样表述更准确,实施起来更简单明确,使得判断结果更为全面。