安全搜索进程内存空间
作者:lisl03
本文发表在黑防2008的2期上,按照他们要求的间隔时间已经过了,所以贴上来。本文设计了用于演示的程序,一下子没有找到怎么发附件,所以需要的读者就向邮箱lisl03@gmail.com发 application mail吧。
ShellCode的编写好像是一个永远说不完的话题。在11期《黑客防线》上我写了《再谈绕过卡巴斯基主动防御的ShellCode编写》的文章。小弟最近在编写一个漏洞的利用程序时,遇到了在进程空间中安全搜索ShellCode的 问题。小弟在网上搜索了一番也没有什么有用的东西。在为这个问题困扰了许久之后,小弟请教了一位大虾后才知道原来国外的一些大牛们早就作过这方面的深入研 究了。在参阅了一些相关的资料后,小弟终于解决了如何安全搜索内存空间的问题。好东西不敢独享,把他写出来献给如我一样正在艰难学习中的菜鸟,大虾看了就飘过吧!
很多情况下在编写ShellCode的时候我们都会面临着如何安全地搜索进程空间,找到我们的可爱的没有被破坏得ShellCode的问题。例如,有些溢出程序对编码有着特殊得要求,比如会将0x2F(‘/’)转化为0x5C(‘’)等等的情况,甚至还有不能出现0xFF的情况。这个时候可能不管你怎么变形都无法符合要求;有些文件格式型的溢出由于文件结构自身的特点,在溢出点附近不可能放入大得ShellCode。溢出产生的时候,我们都必须用一个简短的Search Code查找到原始的ShellCode后,再执行这个功能ShellCode来实现我们的溢出利用。这种通过设置一定得标志字节,由小ShellCode找在内存中查找大ShellCode的方法有一个有趣得叫法:Egg-Hunt.。在栈溢出利用时,EIP开始在堆栈里执行后,由于堆栈在内存中的地址往往比较低:如0x0012XXXX,而原始ShellCode却在0x07BAXXXX远的高地址。直接搜索内存的时候会由于:违规访问未分配内存、用户断点、 浮点异常等等问题而造成系统的崩溃。于是在我们好不容易获得CPU控制权之际,却得到了如下的报错提示框,刚刚得到的CPU控制权就这样丢失了。
图一、内存搜索错误
图二、异常详细信息因此必须实现对进程空间的安全搜索,最终找到实现功能的ShellCode,并在那里安全着陆。对于这样Egg-Hunt的ShellCode的编写有如下三个基本要求:
搜索程序的鲁棒性:这样的要求主要体现在搜索程序必须要有处理“非法地址”访问的能力,才能够安全地搜索进程地址空间,否则对无效地址的访问将导致被溢出进程的崩溃。搜索程序的简洁性:汇编代码的体积是编写搜索程序时需要考虑的一个非常重要的方面,因为绕过溢出环境对Search Code大小的限制是我们使用这种功两段式ShellCode的原因。
搜索程序的高效性:溢出的时候我们当然希望能够尽快跳转到ShellCode去执行我们希望的操作。否则将留下一个不能得到及时更新的屏幕将会是溢出特征的一个最好提示。
在以上三个对Egg-hunt的要求中,程序的鲁棒性是最重要的,因为在执行对进程地址空间搜索的时候,非法地访问毕竟是我们最常遇到的问题。我们当然不希望由于搜索内存失败而造成程序的崩溃,使我们丧失了执行ShellCode的机会。
那么下面我们将主要探讨一些在Windows平台下,常用的安全搜索内存空间的方法。一、利用SHE机制安全搜索内存空间:
Windows系统中的SEH机制是大家在进行溢出攻击时常常利用的一种机制。在这里我们在执行进程空间搜索之前首先注册一个异常处理函数,用它来捕获并完成对“非法地址访问”及各种“运行时异常”的处理,以提高搜索程序的鲁棒性,使我们能够顺利地找到进程空间中的ShellCode。为了对这样的方法的原理有更好的理解,我们再将Windows 系统中异常处理方面的内容再复习一下。
1、Windows的结构化异常处理(SEH)
Windows在创建线程时,操作系统会为每个线程分配TEB结构,并且将FS段选择器指向当前线程的TEB数据结构(结构的定义可以参见参考资料)。Typedef struct _NT_TIB {
Struct _EXCEPTION_REGISTERATION_RECORD * ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
Union {
PVOID FiberData;
ULONG Version;
};
PVOID ArbitaryUserPointer;
Struct _NT_TIB * Self;
} NT_TIB;偏移为0的_EXCEPTION_REGISTERATION_RECORD主要用于处理SHE,因此我们使用FS:[0]也就能够访问SEH。其结构定义如下:
EXCEPTION_REGISTERATION Struct
Prev dd ? 前一个EXCEPTION_REGISTERATIONj结构
Hander dd ? 异常处理例程入口
_EXCEPTION_REGISTERATION end };其中prev指向前一个_EXCEPTION_REGISTERATION的指针,因此线程的异常处理例程形成了一个链状结构。当系统处理异常时就查找异常处理链表,调用相应的异常处理函数执行对程序异常的处理。
2、异常信息
当一个异常发生时,操作系统向异常处理的线程堆栈中压入3个结构,这三个结构是:EXCEPTION_RECORD,CONTEXT, EXCEPTION_POINTERS。
1、 EXCEPTION_ RECORD结构
EXCEPTION_ RECORD结构包含了有关最近发生异常的详细信息,这些信息独立于CPU。其结构如下:EXCEPTION_ RECORD STRUCT {
+0 DWORD ExceptionCode ;异常代码
+4 DWORD ExceptionFlags ;异常标志
+8 struct EXCEPTION_RECORD ;指向另一个EXCEPTION_ RECORD的指针
+C PVOID ExceptionAddress ;异常发生的地址
+10 DWORD NumberParatemeters ;与异常联系的参数数量(0-15)
+14 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]
} EXCEPTION_ RECORD其中,ExceptionCode字段定义了产生异常的原因。图二中表示程序引发了一个EXCEPTION_ACCESS_VIOLATION异常,异常代码为0xC0000005,查阅该异常代码后我们知道程序读写了一个没有可读写属性的地址。
3、CONTEXT结构
CONTEXT结构包含了特定处理器的寄存器数据,系统使用CONTEXT结构来执行各种内部操作。该结构可参考WinNt.h的定义,在X86平台下该结构定义为CONTEXT86,后面将直接用CONTEXT代指CONTEXT86。因为由于该结构比较长,出于篇幅考虑只列出来结构的部分成员:Typedef struct _ CONTEXT86{
……
//通用寄存器
+9C DWORD Edi;
+A0 DWORD Esi;
+A4 DWORD Ebx;
+A8 DWORD Edx;
+AC DWORD Ecx;
+B0 DWORD Eax;
//控制寄存器
+B4 DWORD Ebp;
+B8 DWORD Eip;
+BC DWORD SegCs;
+C0 DWORD EFlags;
+C4 DWORD Esp;
+C8 DWORD SegSs;
……
} CONTEXT86;CONTEXT结构非常重要,通过修改CONTEXT结构中的成员,可以使程序能够在异常时执行相应的处理工作,使程序能够继续执行。在Windows Xp系统中,系统调用NTDLL.DLL模块中的KiUserExceptionDIspatcher函数来执行异常处理。该函数最终将程序中注册的异常处理函数的地址装入ECX,然后调用执行。此时堆栈的分布情况如下:
Esp+0x0 -> *EXCEPTION_RECORD
Esp+0x4 -> *_EXCEPTION_REGISTRATION( 简称ERR)
Esp+0x8 -> *CONTEXT record
Esp+0xC -> *Param如果注册的异常处理函数将CONTEXT结构中保存的EIP值修改到一个“安全的地方”,那么执行完异常处理函数后,程序将返回到“安全代码”中去执行。
下面的例子程序将在执行搜索内存的操作之前向异常处理链表中注册一个异常处理函数,用于在发生地址访问错误的时候修正程序的执行路径,修改EIP的值为发生访问违例(执行内存比较)的下一条指令地址。这样程序将能够安全、顺利地执行完搜索内存的操作。
为了弄清楚异常发生时系统的状态我们进行如下跟踪分析:
① 在Ollydbg中打开程序Seh.exe,由于搜索的起始地址为0x200000将会导致一个访问异常。我们OllyDbg的命令窗口中输入bp 0x7C9237BD,在Windows Xp系统将要调用异常处理函数时断下来观察堆栈的状态(Windows 2000 中是 0x77F8E43E)。此时程序的状态如图三所示:
图三、KiUserExceptionDIspatcher函数调用异常处理时的堆栈状态此时Esp的值为0x12FBF0,该地址指向EXCEPTION_RECORD结构,结构偏移为0x0处保存了异常代码0xC0000005,偏移为0xC处保存了异常发生的地址0x401017,该指令是我们执行模式匹配的指令scasd。Esp+4(0x12FFBC)保存了指向下一个异常处理结构的指针。Esp+8(0x12FCF0)保存了指向CONTEXT结构的指针,该结构偏移为0xB8处保存了异常发生时EIP寄存器的值0x401017,偏移为0x9C处保存了Edi寄存器的值0x200000。CONTEXT保存的各寄存器的状态可参见图四。
图四、异常发生时CONTEXT结构的状态②按下F7后执行预定义的异常处理函数。该函数将修正CONTEXT中保存的EIP的值为指令add ebx, 01000h在内存中的地址(0x0040101C)。当程序恢复执行时,将从该指令继续执行内存空间的搜索操作。修改后的CONTEXT状态可参见图五。
图五、修正后CONTEXT结构的状态
补充:综合编程 , 安全编程 ,