前一篇文章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
版权声明:本文为博主原创文章,未经博主允许不得转载。