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

63 篇文章 4 订阅

    前一篇文章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


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值