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

本文详细探讨了VC++6.0扩展的SEH节点结构,并通过调试代码揭示了堆栈中PEXCEPTION_POINTERS变量的实际位置。文章深入分析了异常处理流程,解释了过滤函数如何在执行过程中使用异常函数中的栈变量,并通过save/loadebp技术实现。此外,文章还揭示了except_handler3函数内部的细节,包括ebp的恢复过程和过滤函数调用方式。
779

被折叠的 条评论
为什么被折叠?



