一种缓冲区溢出的检测方法及系统
技术领域
本发明涉及软件保护领域,特别涉及一种可执行文件中缓冲区溢出的检查方法及系统。
背景技术
软件作为一种特殊的产品,由于其数字化的特征,从问世起就一直遭受形形色色的攻击,其中缓冲区溢出攻击是一种常见的威胁计算机安全的攻击手段。通过缓冲区溢出给软件植入恶意代码后可以控制计算机系统,窃取重要数据,甚至是出于破坏目的格式化硬盘。缓冲区溢出给软件的使用者造成了巨大的损失,也极大的阻碍了整个软件行业的发展。目前的静态分析检测缓冲区溢出的方法不能解决软件运行时攻击和对未知攻击的检测。
现有的静态分析主要是对源代码进行分析,通过词法分析、语法分析和静态语义分析,对缓冲区溢出的模式进行匹配,检测程序中潜在的安全漏洞。
早期的静态分析主要是对源代码进行词法扫描和分析,然后对提供的词典进行匹配,例如在Unix平台下使用的grep工具(grep,global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来,是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。Unix的grep家族包括grep、egrep和fgrep)搜索源代码中可能存在的对不安全库函数的调用。
LCLint是一种使用规范来检查代码安全性的工具,使用C源代码文件和一系列LCL语言(LCL是一种标准的ANSI C 语言的Larch接口语言,它使用类似于C的语法)编写的规范文件作为输入,然后自动检查源文件和规范文件及编程传统之间的不一致性,从而输出相应的警告。
基于静态分析还有基于语义约束分析等方法来检测缓冲区溢出。
基于静态分析的检测方法检测的误报率较高,且对不能解决软件运行时攻击和对未知攻击的检测。
发明内容
根据本发明的一个方面,提供一种检测可执行文件的缓冲区溢出的方法,包括:
分析原始文件的原始入口点;
分析原始文件中调用函数的指令,记录调用函数的指令的地址;
将所述原始文件的原始入口点及调用函数的指令的地址注入原始文件,得到受保护的可执行文件;
所述受保护的可执行文件运行时,根据记录的所述原始文件的原始入口点,在所述原始文件的原始入口点处设置访问断点;
当所述受保护的可执行文件运行到所述原始文件的原始入口点后,根据所述记录的调用函数的指令的地址,在调用函数的指令的地址处设置访问断点;
在上述两处断点设置完成后,控制所述受保护的可执行文件的运行,并记录函数被调用前预期的返回地址及函数调用完成后实际返回的地址;
比较所述记录的函数被调用前预期的返回地址和函数调用完成后实际返回的地址;
如果二者不同,则认为存在缓冲区溢出,软件停止运行;
如果二者相同,继续运行软件。
根据本发明的一个方面,所述原始入口点是软件运行时第一条执行的指令的地址。
根据本发明的一个方面,所述注入是指向原始文件添加额外的代码。
根据本发明的一个方面,所述添加额外的代码是给原始文件增加一个新的区段,将增加的模块和数据以二进制数据的形式注入到新的区段。
根据本发明的另一个方面,提供一种检测可执行文件的缓冲区溢出的系统,包括:
调试器模块、指令分析模块、溢出检查模块、文件链接模块;
所述调试器模块,用于控制可执行文件的执行过程,并记录函数被调用前预期的返回地址,及函数调用完成后实际返回的地址;
所述指令分析模块,用于分析所述可执行文件中的原始入口点,记录原始入口点,分析出调用函数的指令,记录调用函数指令的地址;
所述溢出检查模块,通过分析调试器模块记录的函数被调用前预期的返回地址和函数调用完成后实际返回的地址,如果二者不同,则认为存在缓冲区溢出,软件停止运行;如果二者相同,则继续运行软件;
所述文件链接模块,用于将所述原始文件的原始入口点及调用函数的指令的地址注入原始文件。
根据本发明提供的方法,所取得的有益效果在于:在函数被调用前先预先判断函数执行后应该返回的地址,在函数执行完毕后判断函数的执行完毕后实际返回地址,比较预先判断的返回地址和实际返回的地址来判断是否发生了缓冲区溢出,大大提高了软件的安全性。
附图说明
图1为按照本发明的一优选实施例的保护过程的流程示意图。
图2为按照本发明的一优选实施例中的文件运行时示意图。
图3是PE文件的结构示意图。
具体实施方式
为使本发明的目的、技术方案及优点更加清楚明白,以下参照附图并举实施例,对本发明进一步详细说明。
有鉴于此,本发明公开了一种检测可执行文件中缓冲区溢出的方法及系统。缓冲区溢出的手段是通过精心构造的数据(即,数据中包含需要执行的指令的二进制机器码,比如开一个命令行窗口,或者下载指定地址的恶意软件的代码)淹没函数的返回地址,从而控制程序的执行权限,因此本发明在函数被调用前先预先判断函数执行后应该返回的地址,在函数执行完毕后判断函数执行完毕后实际返回的地址,比较预先判断的返回地址和实际返回的地址,如果两个地址不同,则认为发生了缓冲区溢出,软件停止运行;如果两个地址相同,则软件继续运行,该方法提高了软件的安全性。
根据本发明的一个实施方式,如图1、2所示,提供一种可检测可执行文件的缓冲区溢出的方法,具体包括:
1. 通过指令分析模块分析原始文件的原始入口点,其中,原始入口点是指程序运行时第一条执行的指令的地址,并进行记录。其中,根据本发明的一个实施方式,所述原始入口点记录在全局变量中,以备后续使用。
2. 指令分析模块分析原始文件中调用函数的指令,记录调用函数的指令的地址。其中,根据本发明的一个实施方式,将所述地址记录在分配的内存中。
3. 文件链接模块将调试器模块、溢出检查模块及指令分析模块分析的原始文件的原始入口点,及调用函数的指令的地址注入原始文件。其中,根据本发明的一个实施方式,注入是指向原始文件添加额外的代码,例如,给原始文件增加一个新的区段,将增加的模块和数据以二进制数据的形式注入到新的区段。
4. 如图2所示,保护后的可执行文件运行时,调试器模块根据指令分析模块记录的原始入口点,在原始入口点处设置访问断点,运行到原始入口点后,根据指令分析模块记录的调用函数的指令的地址,在调用函数的指令的地址处设置访问断点,断点设置完成后,调试器模块控制可执行文件的运行,并记录函数被调用前预期的返回地址 (其中,函数预期的返回地址可以栈中得来,进入被调用的函数后,也就是进入call指令的操作数地址后,系统会将函数返回地址压入栈中)及函数调用完成后实际返回的地址。其中,根据本发明的一个实施方式,函数的返回地址可以由调试器模块单步运行调用的函数得来,被调用的函数执行完ret指令后的地址为函数的返回地址。
5. 通过溢出检查模块,分析记录的函数返回的预期地址和实际返回地址,如果二者不同,则认为存在缓冲区溢出,软件停止运行;如果二者相同,继续运行软件。
根据本发明的一个实施方式,提供一种可检测可执行文件的缓冲区溢出的系统,具体包括:
调试器模块、指令分析模块、溢出检查模块、文件链接模块。
所述指令分析模块,用于分析可执行文件中的原始入口点,记录原始入口点,分析出调用函数的指令,记录调用函数指令的地址。其中,原始入口点指程序运行时第一条执行的指令的地址。
所述调试器模块,用于控制可执行文件的执行过程,并记录函数被调用前预期的返回地址,及函数调用完成后实际返回的地址。
所述溢出检查模块,通过分析调试器模块记录的函数返回的预期地址和实际返回地址,如果二者不同,则认为存在缓冲区溢出,软件停止运行;如果二者相同,则继续运行软件。
所述文件链接模块,用于将调试器模块、溢出检查模块、原始文件的原始入口点及调用函数的指令的地址注入原始文件。
根据本发明的一个实施方式,下面给出一个实施例来说明本发明。
该实施例以微软Windows系统下32位的PE文件为例,来描述根据本申请一个具体实施例实现可执行文件保护的具体过程。
如图3所示,PE文件是微软Windows操作系统上的程序文件(可间接被执行,如DLL)。PE文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件。PE文件结构如图3所示,主要包括区段(Section)、区段表(Section Table)、PE头部(PE Header)和DOS头部(DOS Header)。PE文件各个部分的详细定义可参阅微软windows系统的联机帮助,在此不做过多介绍。
下面以给记事本notepad.exe,版本号为5.1,添加缓冲区溢出检查为例来进行说明。
指令分析模块,根据PE文件的DOS Header中的e_lfanew找到IMAGE_NT_HEADER数据结构,根据IMAGE_NT_HEADER中的IMAGE_FILE_HEADER中的NumberOfsections找到文件中的区块表的数目,记事本中区段表的数目为3个,根据IMAGE_NT_HEADER中的数据结构IMAGE_OPTIONAL_HEADER32中的ImageBase找到可执行文件的默认装入地址,记事本的装入地址为0x01000000,根据IMAGE_NT_HEADER中的数据结构IMAGE_OPTIONAL_HEADER32中的AddressOfEntryPoint找到可执行文件的原始入口点,记事本的原始入口点的相对虚拟地址为0x0000739D,地址为0x0100739D,将原始入口点记录在全局变量中,IMAGE_FILE_HEADER后面为区块表,根据IMAGE_SECTION_HEADER中的Characteristics中是否包含IMAGE_SCN_CNT_CODE来判断是否是代码段,记事本中的.text为代码段,根据代码段中的VirtualAddress和 VirtualSize来对指令进行分析,记事本中代码段的VirtualAddress值为0x00001000,VirtualSize值为0x00007748,如果指令的机器码为“call”,记录该指令所在的地址,记事本第一个call指令的地址为0x010073A4 ,指令为call 01007568。将代码段所有的call指令地址进行记录,记录在分配的内存中。
文件链接模块,在记事本中的区段表中新增一个新的区段,将调试器模块、溢出检查模块、原始文件的原始入口点及调用函数的指令的地址保存到新增的区段,修改新文件的原始入口点和区块表的数目,达到注入原始文件的目的,供运行时使用,构建成为新的文件。
保护后的文件运行时,具体步骤如下:
调试器模块调用SetBreakPoint函数,在保护后的记事本的原始入口点0x0100739D处设置断点。
调试器模块调试运行记事本到的原始入口点处0x0100739D。
调试器模块在访问函数的地址处下访问断点,如在第一条调用函数的地址010073A4处调用SetBreakPoint处下断点。
调试器模块运行程序,运行到访问函数地址处0x010073A4,该地址处的指令为call 01007568,记录函数被调用前预期的返回地址,调试器继续单步跟踪0x10007568处的代码,可以从栈(根据本发明的一个实施方式,此处的栈是存储局部变量和进行函数调用必不可少的连续内存区域,栈空间由编译器产生的代码自动分配和释放。堆上的空间需要由编程人员申请和释放)中得到函数的预期返回地址为0x010073A9,函数调用完成后,记录实际返回的地址。
溢出检查模块通过分析调试器模块记录的函数返回的预期地址和实际返回地址,如果不同则认为存在缓冲区溢出,软件停止运行,如果相同,继续运行软件。
以上所述仅为本发明的较佳实施例而已,并非用于限定本发明的保护范围。凡在本发明的精神和原则之内,所作的任何修改、等同替换以及改进等,均应包含在本发明的保护范围之内。