您的位置:首页 > 编程语言 > C语言/C++

vc++6对windows SEH扩展分析 一文拾遗

2015-08-12 20:44 281 查看
前一篇文章vc++6对windows SEH扩展分析 尚有遗漏,本篇加以补齐。

其实本文参考csdn上一篇名为<Win32结构化异常处理(SEH)--异常处理程序(__try/except)>,同时提出了一些质疑。

作者罗列了vc++6.0扩展的SEH节点的结构如下:

struct _EXCEPTION_REGISTRATION
   {
     struct _EXCEPTION_REGISTRATION *prev;
     void (*handler)(PEXCEPTION_RECORD,
           PEXCEPTION_REGISTRATION,
           PCONTEXT,
           PEXCEPTION_RECORD);
     struct scopetable_entry *scopetable;
     int trylevel;
     int _ebp;
     PEXCEPTION_POINTERS xpointers;
   };

如前一篇<vc++6对windows SEH扩展分析 >所述,这个结构是在进入函数时按push ebp/push 0xff一系列操作在堆栈上形成一个_EXCEPTION_REGISTRATION节点。一般而言push ebp是进入函数的第一条语句,那么,在栈中地址高于int _ebp的指针变量xpointers是谁压入的?一般而言,堆栈中[ebp+4]是函数返回地址,因此可以认为作者可能笔误写错了PEXCEPTION_POINTERS xpointers;在整个结构中的位置。那么,这个域在哪?本文通过调试代码,回答这个问题。

产生异常的代码如下:

#include <windows.h>

int filter1(EXCEPTION_POINTERS* excpInfo)
{
	DWORD accAddr = excpInfo->ExceptionRecord->ExceptionInformation[1];
	excpInfo->ContextRecord;
	return EXCEPTION_EXECUTE_HANDLER;
}

int main()
{
	int* a = NULL;
	__try
	{
		(*a)=0xcc;
	}
	__except(filter1(GetExceptionInformation()))
	{
		MessageBox(NULL,"","",MB_OK);
	}
	return 0;

}
程序触发异常后传入4个参数然后进入except_handler3,原型如下:

int __except_handler3( 
 struct _EXCEPTION_RECORD * pExceptionRecord,
 struct EXCEPTION_REGISTRATION * pRegistrationFrame,
 struct _CONTEXT *pContextRecord,
 void * pDispatcherContext );


except_handler3源码没有,忍忍看看反汇编的结果:

__except_handler3:
00401234   push        ebp
00401235   mov         ebp,esp
00401237   sub         esp,8
0040123A   push        ebx
0040123B   push        esi
0040123C   push        edi
0040123D   push        ebp
0040123E   cld
0040123F   mov         ebx,dword ptr [ebp+0Ch] ;ebx指向第二个参数
00401242   mov         eax,dword ptr [ebp+8] ;[ebp+8]指向第一个参数
00401245   test        dword ptr [eax+4],6
0040124C   jne         __except_handler3+0A0h (004012d4)
00401252   mov         dword ptr [ebp-8],eax
00401255   mov         eax,dword ptr [ebp+10h] ;[ebp+10h]指向第三个参数
00401258   mov         dword ptr [ebp-4],eax
0040125B   lea         eax,[ebp-8]
0040125E   mov         dword ptr [ebx-4],eax
注释说了,ebx指向第二个参数,这个其实是发生异常前main函数中堆栈中压入的struct _EXCEPTION_REGISTRATION结构。这段代码中有这么几句:
00401242   mov         eax,dword ptr [ebp+8] ;[ebp+8]指向第一个参数
<pre name="code" class="cpp">00401252   mov         dword ptr [ebp-8],eax
...
00401255   mov         eax,dword ptr [ebp+10h] ;[ebp+10h]指向第三个参数
00401258   mov         dword ptr [ebp-4],eax
...
<pre name="code" class="cpp">0040125E   mov         dword ptr [ebx-4],eax ;把结构的起始地址放入地址[pRegistrationFrame-4]





这段代码,把参数1 3放入某个结构中,然后把这个结构的地址传给[pRegistrationFrame-4],按照<Win32结构化异常处理(SEH)--异常处理程序(__try/except)>一文的作者描述,这个结构就是PEXCEPTION_POINTERS
xpointer。前面多次说过pRegistrationFrame是ide在堆栈上形成的结构,pRegistrationFrame-4相当于往堆栈上又压入一个4字节变量,因此,可以确定vc++6.0扩展的结构的原型应该是:

<pre name="code" class="cpp"><pre name="code" class="cpp">PEXCEPTION_POINTERS xpointers;



struct _EXCEPTION_REGISTRATION   {

    struct _EXCEPTION_REGISTRATION *prev;     void (*handler)(PEXCEPTION_RECORD,           PEXCEPTION_REGISTRATION,           PCONTEXT,           PEXCEPTION_RECORD);     struct scopetable_entry *scopetable;     int trylevel;     int _ebp;   };

两个变量紧挨在一起


当然也可以查看内存值:

触发异常之前,ebp=0x12ff48,trylevel=0x00,handler=0x403118,prev=0x12ff78;而0x12ff34,即xpointer处为0x83;



触发异常执行到
<pre name="code" class="cpp"><pre name="code" class="cpp">mov         dword ptr [ebx-4],eax ;把结构的起始地址放入地址[pRegistrationFrame-4]



语句之后



0x12FF34处值为0x12FAEC。而地址0012FAEC处的内存为:

0012FAE4  00 00 00 00 00 00 00 00  ........
0012FAEC  E0 FB 12 00 FC FB 12 00  帑....
0012FAF4  18 FB 12 00 79 71 85 77  ....yq厀
明眼人一看0x12FBE0和0x12FBFC就知道是堆栈值。可以在按ebp的值查看函数参数和返回地址:

0012FAEC  25 E1 82 77 DD AE 6C 00  %醾w莓l.
0012FAF4  18 FB 12 00 79 71 85 77  ....yq厀
0012FAFC  E0 FB 12 00 38 FF 12 00  帑..8...
0012FB04  FC FB 12 00 B4 FB 12 00
这两个值果然是except_handler3的两个参数。

由此至少可以说明

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">PEXCEPTION_POINTERS xpointers;



struct _EXCEPTION_REGISTRATION   {

    struct _EXCEPTION_REGISTRATION *prev;

void (*handler)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,PCONTEXT,PEXCEPTION_RECORD);

struct scopetable_entry *scopetable;

int trylevel;

int _ebp;

};


这个结构才是vc6扩展的SEH节点。到此本文可以完结了,但是调试代码时发现一个有趣的地方,就是从except_handler3跳去过滤函数执行时,except_handler3为了使得过滤函数中能使用异常函数中定义的栈变量,用了save/load ebp大法:

00401273   push        ebp
00401274   lea         ebp,[ebx+10h]
00401277   call        dword ptr [edi+ecx*4+4]
call指令将调用scopetable数组中定义的过滤函数,然而在此之前except_handler3从[ebx+10h]处恢复ebp的值,那么[ebx+10h]是啥?首先可以确定ebx还是进入except_handler3时设置的ebx,其指向_EXCEPTION_REGISTRATION。_EXCEPTION_REGISTRATION+10H不就是_EXCEPTION_REGISTRATION!ebp吗?这个ebp是进入main函数时保存的函数帧,因此可以通过相对于ebp的偏移取得main函数中的变量。

至于GetExceptionInformation()函数,就是取except_handler3设置的xpointer地址:

18:       __except(filter1(GetExceptionInformation()))
004010B5   mov         ecx,dword ptr [ebp-14h]
004010B8   push        ecx
004010B9   call        @ILT+5(filter1) (0040100a)
004010BE   add         esp,4
在正式进入过滤函数后,由于有一次push ebp修改了函数栈帧,因此不能在过滤函数用调用GetExceptionInformation


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: