动态函数监控技术在缓冲区溢出检测中的应用

动态函数监控技术在缓冲区溢出检测中的应用
摘要:本文通过对Window环境下函数劫持方法的分析,并结合调试器技术,提出了动态监测进程函数调用的方法。同时,对缓冲区溢出shellcode进行了分析,用有限状态自动机的模型描述了缓冲区溢出的入侵及检测模型。最后,将缓冲区溢出的检测模型与函数调用监测结合,提出了一种对缓冲区溢出行为进行动态检测的手段。
关键字:入侵检测 函数劫持 调试器 自动机
Abstract:This paper discusses both the method of API function Inject and the Debuger technology,at last brings forward a dynamic method of detecting the Process API calls. And then, analyse the buffer overflow shellcode,and using Finite Automata to describe the Module of the buffer overflow Attack. Finally, brings forward a dynamic method to detect buffer overflow Attack using API Injec4 and Mode Match.
KeyWord: IDS API Injection Debugger Finite Automata
目录
第一章   绪论   1
1.1课题背景   1
1.2发展现状   1
第二章   技术原理   3
2.1函数劫持   3
2.2调试器技术   4
HYPERLINK /L "_TOC75167249" 2.3函数劫持与调试器相结合实现函数调用分析   7
2.4基于有限状态自动机的缓冲区溢出检测模型   8
第三章   用函数劫持与调试器技术实现SOFTIDS   13
3.1 SOFTIDS简介   13
3.2技术实现   14
3.3效率问题   14
后 记   16
参考文献   17
 

第一章   绪论
1.1课题背景
缓冲区溢出漏洞是用C/C++语言开发的程序中常见的漏洞。缓冲区溢出攻击已经成为一种非常重要的攻击手段。对缓冲区溢出攻击的研究以及对缓冲区溢出攻击的防护,已经成为网络安全领域重要的研究方向。IDS(入侵检测)成为一种重要的检测缓冲区溢出的方法。
1.2发展现状
IDS(入侵检测)是一种能够比较有效的检测网络攻击行为的手段。
1980年,James P.Andeson为美国空军做了一份题为《Computer Security Threat Monitering And Surverlance》的技术报告,第一次详细阐述了入侵检测的概念,被称为入侵检测的开山之作。
1984 年—1986年,乔治敦大学的Doroty Denning和SRI/CSL的Petter Neumann 研究出了一个实时的入侵检测系统,该模型由六个部分组成:主体、对象、审计记录、应用环墁、系统弱点以及入侵类型,为构建通用的入侵检测系统提供了通用的框架。
1990年是入侵检测系统发展的一个分水岭,这一年,加州大学戴维斯分校的L.T.Heberlein等开发了NSM(NetWork Security Monitor),该系统第一次将网络流作为数据来源,从这以后,入侵检测的两大阵营正式形成:基于网络的IDS和基于主机的IDS。
现代IDS一般都具有如下功能:
(1)   监测并分析用户和系统的活动
(2)   检查系统配置和漏洞
(3)   评估系统关键资源和数据文件的完整性
(4)   识别已知的攻击行为
(5)   统计分析异常行为
(6)操作系统日志管理,并识别违反安全策略的用户行为。
IDS 分为HIDS和NIDS两种。HIDS即基于主机的IDS,提供对单个主机的监控及保护。NIDS是基于网络的IDS,为某一特定网络提供保护。HIDS 能够对攻击行为提供比较准确的报警及监控,但是这种IDS往往会占用大量的资源,影响目标主机的效率。NIDS有一台专门的服务器对网络中的异常行为进行监控,所NIDS对系统效率的影响比较小,但NIDS在入侵检测的准确性上要逊于HIDS。
现在的IDS一般都是通过Shellcode关键字匹配来实现对缓冲区溢出攻击的检测的,这种方法的好处是检测的速度快,效率高,对系统资源的消耗较小,缺点是容易漏报攻击行为。
本文提出的基于进程行为分析的缓冲区溢出检测模式可以比较精确的检测出缓冲区溢出攻击。
 
第二章   技术原理
2.1函数劫持
一、函数劫持的作用
Windows操作系统提供的API函数调用接口为用户空间的程序设计提供了非常强大的函数库。API函数调用实际上构成了Windows应用程序的骨架。
通过对API函数的劫持,可以在程序调用API函数之前加入自己的处理程序,这对软件行为分析、函数调用关系图分析以及入侵检测等具有非常重要的意义。
在Windows环境下开发的商业软件几乎都没有公开源代码,很多情况下需要对这些软件进行评估与测试,而评估和测试的一个非常重要的方面就是对这个软件调用API函数的情况进行分析,这时候就需要利用函数劫持来实现函数调用的分析。
二、函数劫持的实现
一个比较简单有效的劫持API函数调用的方法是通过更改目标进程空间中函数入口指令来实现。
Windows系统中采用了虚拟内存管理机制,每个进程都有2G的私有内存空间,同时共享2G的内核空间。普通进程是不能访问内核空间的。
在Windows2000以后的操作系统中,进程不但能够访问本进程的私有空间,同时还能通过专门的内存管理函数访问其他进程的内存空间。这就为劫持其他进程中的API函数调用提供了基础。
在Windows系统中,每一段内存区都被赋予某种属性:
(1)可写
(2)可读
(3)可执行
程序的代码段默认所具有的属性为可执行可读不可写,但是我们可以通过VirtualProtectEx函数将内存区的属性改为可写可执行。不仅对其他进程空间的.text段可写,对进程本身的.text段,我们也可以通过这个函数调用来更改相应的内存单元属性。这样,我们就可以通过更改函数入口指令来实现对 API函数的劫持。
三、函数劫持的两种方式
通过更改函数入口指令实现函数劫持有三种不同的方式。
第一种方式是将函数第一条指令改为一条jmp指令,跳转到用户提供的地址进行执行,在执行完用户功能后恢复原来的指令,然后再跳转回函数入口地址,实现函数正常的功能。这种方法的缺点就是不能对函数调用进行重复的劫持,每发生一次函数调用就需要进行一次劫持。
第二种方式是将函数入口的五个字节指令保存到某个空间,同时将函数入口指令更改到相应的用户功能代码处,在执行完用户需要添加的功能代码后,跳转到所保存的函数入口指令处,执行完函数入口指令后再回到正常的API函数内完成正常的函数功能。这种方法的好处就是不需要重复劫持,速度快。但是这种方法要求函数本身的入口指令不能使用相对寻址方式。MicroSoft提供的detour函数库就是基于这个思想。
第三种方式就是将函数劫持与调试器配合起来使用,将函数的第一条指令改成int 3,每次产生一个用户中断供调试器处理,由调试器负责实现用户要求的功能,保存和恢复现场,保证函数正常功能的实现。用int 3劫持和调试器相结合的方式实现函数劫持,最大的好处就是功能强大,结构清晰,但是在效率上会有一些折扣。
2.2调试器技术
一、调试器概述
在程序设计和调试的过程中,调试器是一个非常重要的工具。一些常见的功能强大的调试器包括OllyDbg、SoftIce、WinDbg,以及VC编程环境中自带的调试器。这些调试器都是一种通用型的调试器,为我们的程序调试提供了方便。
调试器可以对被调试进程进行一系列的操作,包括读写内存,读取和设置线程环境变量,设置目标进程的寄存器等。同时被调试进程中每产生的一个调试事件都会先将自己挂起,通知调试器进行处理后才继续程序执行。这些调试事件包括进程创建,进程结束,线程创建,线程结束,装载DLL,卸载DLL,以及一切用户中断和异常。
调试器的功能并不只是为我们的程序设计和编译提供有力的支持,调试器技术还可以用于入侵检测,漏洞分析等方面。这就需要专门的调试器,将调试功能应用到这些方面。
微软的函数库里面提供了一组调试函数供用户使用实现自己的专用调试器。这组函数包括:
ContinueDebugEvent //继续被调试进程
DebugActiveProcess //调试一个正在运行的进程
DebugBreak         //产生一次调试中断
FatalExit         //退出调试
FlushInstructionCache //清空指令缓存
GetThreadContext      //取得目标线程环境块   
GetThreadSelectorEntry //取得线程选择子入口
IsDebuggerPresent    //进程是否被调试
OutputDebugString      //输出调试信息
ReadProcessMemory      //读取目标进程内存空间
ReadProcessMemoryVlm //读取特定进程空间内存
SetDebugErrorLevel   //设定被调试进程错误通知等级
SetThreadContext      //设置目标线程环境
WaitForDebugEvent     //等待目标进程产生调试时间
WriteProcessMemory  //写目标进程内存空间
WriteProcessMemoryVlm //写特定进程空间内存单元
通过这些函数,就可以定制自己的调试器。
二、调试进程
要实现对进程的调试,可以通过两种方法来实现。
一是创建新进程用于调试。在父进程调用CreateProcess创建被调试进程的时候指定为调试。这样,父进程和子进程之间就是一种调试和被调试的关系。在进程被完全装入内存空间后,系统会将子进程挂起,并调用DBGBreakPoint产生一次int 3中断,当调试器处理完这个int 3中断后,程序才会继续执行。
二是调用DebugActiveProcess绑定正在运行的进程。要调试正在运行的进程,调试器必须首先获得对目标进程的调试权限。
对于Administrator运行的调试器来说,默认情况下可以获得所有非SYSTEM用户组用户所创建的进程的调试权。对于SYSTEM用户组的进程,比如Lsass,Winlogon,svchost等,就必须先提升调试器进程的权能,授予自己调试权能,实现对这些系统进程的调试。
在完成绑定工作后,被调试进程同样会调用DBGBreakPoint产生一次int 3中断,调试器必须处理这个中断才能继续改进程的运行。
三、调试器主循环
在完成调试前的准备工作后,调试器循环调用WaitForDebugEvent等待调试事件的产生,然后进行相应的处理。WaitForDebugEvent是一个可阻塞的函数,当被调试进程没有调试事件产生时,调试器不会因为大量的循环工作而消耗CPU时间。
四、同时调试多个进程
调试器可以同时调试多个进程。每个被调试的进程都必须有一个相应的调试线程与之对应。在这个调试线程里面必须实现全部的包括绑定目标进程、调试循环在内的所有的调试器功能,这就意味着在系统中是以线程为单位通知相应的调试事件的。这就使得我们可以通过一个进程监控多个进程。

2.3函数劫持与调试器相结合实现函数调用分析
使用函数劫持和调试器相结合的技术可以很方便的对进程的函数调用情况进行分析,包括函数调用关系,函数调用的参数等。
调试器可以获得被调试进程的完全权限。这样,调试器就可以通过WriteProcessmemory和ReadProcessmemory来对目标进程的内存空间进行操作,包括读,写内存,并设置进程的线程环境,控制目标进程的执行。
(1)调试目标进程
(2)保存函数入口指令
为了在劫持函数调用后恢复函数本身功能,需要保存目标函数入口指令。
定义一个类来描述API函数:
class apiInfo{
   char name[128];      //API函数名
   ULONG entry;      //API函数入口地址
   UCHAR inst[4];      //API函数入口指令   
}
利用STL库的List模板将所有的被劫持函数组织成一个列表。
(3)通过int 3劫持函数
用int 3的机器指令更改函数的入口指令。
(4)调试器对目标进程进行分析和处理
在完成以上三步工作之后,调试器等待int 3用户中断的产生。当调试器检测到int 3中断的时候,分析函数调用,包括函数调用关系和函数调用的参数。同时,遍历函数信息列表,恢复相应的函数入口指令。将被调试线程EIP减一,设置单步中断,恢复进程执行。
调试器响应单步中断,重新劫持相应的函数,实现函数劫持的可持续性。
通过以上步骤,调试器在不影响目标进程功能的情况下实现了对目标进程的劫持,对目标进程函数调用的分析。
2.4基于有限状态自动机的缓冲区溢出检测模型
一、缓冲区溢出攻击
缓冲区溢出是一种非常重要和有效的攻击手段。在溢出之后攻击者往往可以直接拿到目标主机的最高权限。
Windows系列的操作系统中存在着很多缓冲区溢出漏洞,包括已经公布的和没有公布的,这些漏洞往往会给用户造成非常重大的安全隐患。著名的冲击波病毒和最近爆发的震荡波病毒就是利用Window系统进程的缓冲区溢出漏洞来进行传染的。
对于已经公布的漏洞,我们可以通过安装补丁程序或进行相应的配置来防范攻击者对这些漏洞的溢出攻击。而对于那些由于某种目的暂时没有公布的漏洞,就可以通过专门的缓冲区溢出入侵检测系统来防护。
二、对溢出攻击的检测
缓冲区溢出攻击针对系统中运行的一个特定进程(线程),所以对缓冲区溢出攻击的检测可以通过基于进程的监控与检测来实现。
对缓冲区溢出攻击的检测方法很多,包括堆栈执行状态分析,以及特定函数调用模型匹配等。
本文讨论采用特殊函数调用监控及入侵模式匹配的方法来实现对缓冲区溢出的检测。
入侵检测系统基于两项关键技术,一是对异常的检测,第二就是对检测到的异常行为进行匹配。
函数劫持与调试器相结合的技术已经解决了对异常的监测。我们通过对入侵行为的抽象来构建检测模型,然后用检测到的敏感函数调用情况与这个模型进行匹配,实现对溢出的检测。
(1)溢出攻击函数调用序列
在缓冲区溢出之后会执行一段shellcode,攻击者通过这段shellcode来实现相应的黑客行为。
现在比较普遍的shellcode有三种:
①直连型shellcode
这种shellcode调用API函数的情况是:
connect + CreateProcess,CreateProcess + connect。
②反连型shellcode
这种shellcode调用API函数的模型是:
Accept + CreateProcess,CreateProcess + Accept。
③下载文件并执行型shellcode
这种shellcode的函数调用模型为:
URLDownloadToFile + CreateProcess。
(2)构建溢出攻击检测模型
我们可以对这些shellcode所使用的API函数调用进行归纳和总结,得出一个shellcode特定API函数调用的有限状态自动机。通过对这些敏感函数的劫持配合调试器来监视在一个进程中这些函数的调用情况,同时匹配有限状态自动机,检测进程是否正在执行shellcode,实现入侵检测。
用有限状态自动机来模拟被保护线程的状态。P0为初始状态,INVAD状态表示线程已经被入侵。P1...Pn为一系列的中间状态。函数调用及其参数是各个状态之间转换的条件。这样,实际上就构成了一个线程状态转移图,顶点是目标线程的状态,边是函数调用及其参数表示的状态转移条件。
用函数调用以及其参数的情况作为状态之间转换的条件,每发生一次函数调用则产生一个状态转移码,线程根据自动机模型变换一次状态,当受保护进程的某一线程状态最终转移到INVAD时,则可以确定主机受到了缓冲区溢出攻击。

我们定义了一个类ThdST描述线程的状态并描述线程状态之间的变换模型。
//定义线程的状态
#define INVAD      -1         //线程处于被入侵状态
#define P0      0         //初始状态
#define P1      1         //线程建立了网络连接
#define P2      2         //线程创建了一个CMD进程
#define P3      3         //线程从网络上下载了一个文件
//定义状态之间转换代码
#define ACCEPT         0      //接收网络连接
#define CONNECT         1      //主动连接网络
#define CREATEPROCESS      2      //创建CMD进程或下载的文件运行
#define DOWNFILE      3      //下载文件
//线程状态描述
class ThdST
{
public:
   ThdST(DWORD Tid):ThreadID(Tid)   
   {
      Situation = P0;         //将线程状态置为初始状态
   }
   bool IsInvade()            //线程是否被入侵
   {
      if(Situation==-1)
         return true;
      else
         return false;
   }
   bool ChangeSituation(DWORD chCode);   //线程状态变换
   
public:
   DWORD ThreadID;            //线程ID
   DWORD Situation;         //线程状态
};
三、有效性问题
基于函数调用自动机模型的缓冲区溢出检测的有效性决定于线程状态转换自动机模型的完整性。
要使缓冲区溢出检测具有更高的有效性,就必需构造一个完备的函数调用与线程状态转换间的自动机模型。但是这是基于IDS开发者或者使用者经验的模型,并不能证明该模型的有效性和完备性。
当然,只要有相应的函数检测自动机模型,通过这种方法还可以检测到寄生于目标进程中的恶意后门程序。

第三章   用函数劫持与调试器技术实现SoftIDS
3.1 SoftIDS简介
SoftIDS是我在毕业实习中开发的一个专门检测缓冲区溢出攻击的IDS。使用函数劫持及调试器技术配合线程状态转换自动机模型实现对缓冲区溢出攻击的检测。
SoftIDS是一个基于进程的缓冲区溢出攻击检测程序。它能够精确定位缓冲区溢出攻击到线程一级,在攻击造成更大的危害前及时终止被入侵的线程。它能够同时对多个进程进行保护。

3.2技术实现
(1)为每一个需要保护的进程创建一个保护线程。
(2)在保护线程中调用DebugActiveProcess绑定需要保护的进程,使其被本线程调试。
(3)保护线程进入调试循环,WaitForDebugEvent等待被调试进程中产生调试事件。
(4)系统强制被调试进程产生一个int 3中断,同时被调试进程挂起,等待调试线程处理int 3。
(5)保护线程响应第一个int 3中断,用int 3配合调试器的方法劫持需要监控的敏感API函数调用。这些敏感函数调用包括:CreateProcess,Accept,connect, URLDownLoadToFile等。
(6)继续被保护进程的执行,等待int 3中断。
(7)处理int 3中断,分析被保护进程的函数调用情况,包括其参数使用情况,得出线程状态转移码。
(8)根据状态转移码转移线程状态,检查其是否被入侵。如果被入侵,则结束被入侵线程,否则跳转到(6)执行。
3.3效率问题
使用函数劫持配合调试器的方法实现缓冲区溢出入侵检测不会给系统带来太大的效率上的负担。同时,也不会影响被保护的进程的执行效率。
(1)调试事件有限
Windows系统支持的调试事件有限。在一个稳定执行的进程中几乎不会产生任何调试事件。
SoftIDS 本身不会占用太多的CPU资源。因为对于IDS来说,关注和处理的调试事件只有int 3中断和单步中断。SoftIDS大量的时间都用来等待调试事件的产生,而WaitForDebugEvent函数调用是一个基于消息通知机制的函数,这就意味着SoftIDS大部分时间都处于阻塞状态,不会消耗任何CPU资源。
(2)Windows调试机制基于消息通知
Windows调试机制基于消息通知机制。被调试进程只有在产生调试事件之后才会通知调试器对调试事件进行处理。在没有调试事件产生的时候不会对被调试进程产生任何影响。
参考文献
[1] Microsoft Corporation. Detours: Binary Interception of Win32 Functionsdetour. 2001
[2] Jeffrey Richter. Programming Applications for Microsoft Windows . 4th Ed. MicroSoft Press. 2001
[3] David Solomon, Mark Russinovich. Inside win2000. 3rd ed. MicroSoft Press . 2001
[4] HBGary. BugScan Technical White Paper.
http://www.hbgary.com/text/Predator _White_Paper.pdf. 2002
[5] Artemis. System-Wide API Hooks On Win9x.
http://www.codeproject.com/useritems/syswidehook9x.asp.1999
[6] James P.Andersion. Computer Security Threat Monitoring and Surveillance. http://csrc.nist.gov/publications/history/ande80.pdf. 1980
[7] Intel. IA-32 Intel Architecture SoftWare Developers'Manual. Intel Corporation. 2003
[8] 陈有祺.形式语言与自动机.天津:南开大学出版社.1998.8
[9]宋献涛 叶惠敏. IDS 二十年风雨历程.
http://www.chinaitlab.com/www/news/article_show.asp?id=6058
[10] 多友安全网. 入侵检测系统简介. http://www.chinaitlab.com/www/news/article_show.asp?id=2508
[11] Ivo Ivanov. API hooking revealed.
http://www.codeproject.com/system/hooksys.asp.2001


Trackback: http://tb.donews.net/TrackBack.aspx?PostId=543916

你可能感兴趣的:(动态函数监控技术在缓冲区溢出检测中的应用)