在本系列的上一篇文章Windows异常世界历险记(四)——VC6中结构化异常处理机制的反汇编分析(中)中,给出了针对VC6的异常处理机制进行逆向后得到的伪码。在本文中,我们仍然以之前写的小程序为例,通过调试的方式结合我们得到的VC6结构化异常处理的伪码,对其运行机制进行分析。

破局

在RaiseExcept中,即将触发异常时的情形如下:

0:000> p
eax=cccccccc ebx=003ea000 ecx=00000000 edx=00694687 esi=00401540 edi=0019fe5c
eip=00401143 esp=0019fe0c ebp=0019fe74 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
SEHTest1!RaiseExcept+0x43:
00401143 c7050000000000000000 mov dword ptr ds:[0],0  ds:002b:00000000=????????

先看看SEH链:

0:000> dd fs:[0]
0053:00000000  0019fe64 001a0000 0019c000 00000000
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0019fe64
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0x0019fed0 _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x00401460     _EXCEPTION_DISPOSITION  SEHTest1!_except_handler3+0
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0x0019fed0
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0x0019ff60 _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x00401460     _EXCEPTION_DISPOSITION  SEHTest1!_except_handler3+0
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0x0019ff60
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0x0019ffcc _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x00401460     _EXCEPTION_DISPOSITION  SEHTest1!_except_handler3+0
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0x0019ffcc
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0x0019ffe4 _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x775286d0     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0x0019ffe4
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x7753518e     _EXCEPTION_DISPOSITION  ntdll!FinalExceptionHandlerPad14+0

由上述结果可得出结论:目前一共安装有5个SEH异常处理函数,其中前3个是VC6自己的,后两个是系统的。对于前3个VC6安装的SEH节点,是在标准的SEH链结构EXCEPTION_REGISTRATION_RECORD的基础上“加了料”形成的,在上篇文章中已经给出了其结构,我们将其暂且命名为VC6_EXCEPTION_REGISTRATION_RECORD吧:

typedef struct VC6_EXCEPTION_REGISTRATION_RECORD
{DWORD  dwCurrentESP;                       // 保存的ESP值PVC6_EXCEPTION_INFORMATION pExceptInfo;       // 异常信息结构指针EXCEPTION_REGISTRATION_RECORD pSEHNode;      // 经典的异常链节点PVC6_SCOPETABLE  pScopeTable;            // VC的惯用伎俩——表结构DWORD    dwTryLevel;                 // 当前try的层级DWORD    dwEBP;          // 这个其实不是本结构的成员,只是本结构在栈上挨着函数入口处压入的EBP而已
}VC6_EXCEPTION_REGISTRATION_RECORD, *PVC6_EXCEPTION_REGISTRATION_RECORD;

需要指出的是,VC6_EXCEPTION_REGISTRATION_RECORD结构中最后一个成员dwEBP是我们根据栈结构添加进去的,因为在except_handler3中会用到。下面就按VC6_EXCEPTION_REGISTRATION_RECORD结构对前3个属于VC6的异常处理节点进行解析:

结构 第一节点 第二节点 第三节点
dwCurrentESP 0019fe0c 0019fe7c 0019ffcc
pExceptInfo(此时还没填) 00000800 0019fee0 00401460
pSEHNode.Next 0019fed0 0019ff60 00422248
pSEHNode.Handler 00401460 00401460 00000000
pScopeTable 004220c8 00422138 0019ff80
dwTryLevel 00000001 00000001 74d6fe09
dwEBP 0019fee0 0019ff70 003ea000

OK了,下面主要来看下各节点的ScopeTable,ScopeTable的结构定义在上一篇中被我们命名为了

typedef struct VC6_SCOPETABLE
{DWORD dwOuterLevel;PVOID pfnFilter;PVOID pfnHandler;
}VC6_SCOPETABLE, *PVC6_SCOPETABLE;

实际上ScopeTable是一个结构数组而已。

except_handler3之初见

在except_handler3处下断后,继续运行程序,由于触发异常该段点命中,命中时的情形如下:

SEHTest1!_except_handler3:
00401460 55              push    ebp
0:000> kb# ChildEBP RetAddr  Args to Child
00 0019f854 77535032 0019f95c 0019fe64 0019f9ac SEHTest1!_except_handler3
01 0019f878 77535004 0019f95c 0019fe64 0019f9ac ntdll!ExecuteHandler2+0x26
02 0019f944 77522936 0019f95c 0019f9ac 0019f95c ntdll!ExecuteHandler+0x24
03 0019f944 00401143 0019f95c 0019f9ac 0019f95c ntdll!KiUserExceptionDispatcher+0x26
04 0019fe74 00401268 00401540 00401540 003ea000 SEHTest1!RaiseExcept+0x43 [Main.c @ 30]
05 0019fee0 00401653 00400000 00000000 00694687 SEHTest1!WinMain+0x48 [Main.c @ 49]

我们重点关注其中访问ScopeTable的部分,我们从上篇文章中节选出这部分等价的C伪码

while (tryLevel != -1)
{// 若为try-except型if (pScopeTable[tryLevel].pfnFilter){// 执行过滤函数,并根据其返回值进行判定int nRet = pScopeTable[tryLevel].pfnFilter();if (nRet != 0){if (nRet < 0){return ExceptionContinueExecution;}// 执行全局展开global_unwind2(pSEHNode);DWORD dwOrgEBP = pVC6SEHNode->dwEBP;// 栈基址指针切换_asm{mov ebp, dwOrgEBP}// 执行局部展开local_unwind2(pSEHNode, tryLevel);NLG_Notify(1);pVC6SEHNode->dwTryLevel = pScopeTable[tryLevel].dwOuterLevel;// 调用异常处理函数pScopeTable[tryLevel].pfnHandler();}}tryLevel = pScopeTable[tryLevel].dwOuterLevel;
}

第一次访问ScopeTable时,从栈上读到的tryLevel为1,因此pScopeTable[tryLevel].pfnFilter是什么呢?

0:000> p
eax=0019f84c ebx=0019fe64 ecx=00000003 edx=77535050 esi=00000001 edi=004220c8
eip=0040149b esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
SEHTest1!_except_handler3+0x3b:
0040149b 837c8f0400      cmp     dword ptr [edi+ecx*4+4],0 ds:002b:004220d8=000000000:000> dd 004220c8
004220c8  ffffffff 00401182 00401199 00000000
004220d8  00000000 0040115b

是004220d8处的0,由于没有过滤函数,因此这应该是一个try-finally型异常处理函数,我们根据pfnHandler=0040115b来看看这是谁家的finally函数:

0:000> u 0040115b
SEHTest1!RaiseExcept+0x5b [C:\SRC\Test\SEHTest1\Main.c @ 34]:
0040115b 8bf4            mov     esi,esp
0040115d 6a00            push    0
0040115f 68b0204200      push    offset SEHTest1!`string' (004220b0)
00401164 688c204200      push    offset SEHTest1!`string' (0042208c)
00401169 6a00            push    0
0040116b ff15aca24200    call    dword ptr [SEHTest1!_imp__MessageBoxA (0042a2ac)]
00401171 3bf4            cmp     esi,esp
00401173 e8b8010000      call    SEHTest1!_chkesp (00401330)
0:000> da 004220b0
004220b0  "In Inner::finally"

很明显,可以实锤这是RaiseExcept中的内层try-finally。按照我们分析出来的伪码,由于不存在pfnFilter,这里将不执行pfnHandler。实证一下,确实跳到后面了:

0:000> t
eax=0019f84c ebx=0019fe64 ecx=00000003 edx=77535050 esi=00000001 edi=004220c8
eip=004014a0 esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x40:
004014a0 7445            je      SEHTest1!_except_handler3+0x87 (004014e7) [br=1]
0:000> t
eax=0019f84c ebx=0019fe64 ecx=00000003 edx=77535050 esi=00000001 edi=004220c8
eip=004014e7 esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x87:
004014e7 8b7b08          mov     edi,dword ptr [ebx+8] ds:002b:0019fe6c=004220c8

接下面是执行

tryLevel = pScopeTable[tryLevel].dwOuterLevel;

应该就是刚刚pfnFilter前面的那个dwOuterTryLevel,实证下:

0:000> p
eax=0019f84c ebx=0019fe64 ecx=00000003 edx=77535050 esi=00000001 edi=004220c8
eip=004014ed esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x8d:
004014ed 8b348f          mov     esi,dword ptr [edi+ecx*4] ds:002b:004220d4=00000000

然后开始下一轮循环,这次由于tryLevel值为0,因此pfnFilter值为00401182,而pfnHandler的值00401199,这是谁家的try-except结构呢?

0:000> u 00401199
SEHTest1!RaiseExcept+0x99 [C:\SRC\Test\SEHTest1\Main.c @ 37]:
00401199 8b65e8          mov     esp,dword ptr [ebp-18h]
0040119c 8bf4            mov     esi,esp
0040119e 6a00            push    0
004011a0 6884204200      push    offset SEHTest1!`string' (00422084)
004011a5 6874204200      push    offset SEHTest1!`string' (00422074)
004011aa 6a00            push    0
004011ac ff15aca24200    call    dword ptr [SEHTest1!_imp__MessageBoxA (0042a2ac)]
004011b2 3bf4            cmp     esi,esp
0:000> da 00422084
00422084  "haha"
0:000> da 00422074
00422074  "Never execute"

实锤这是RaiseExcept中外层的try-except结构。步过后果然看到了MessageBox弹框,执行完成后,该函数返回0,即所谓的EXCEPTION_CONTINUE_SEARCH,拒绝处理该异常。

eax=00000000 ebx=0019fe64 ecx=00690000 edx=00690000 esi=00000000 edi=004220c8
eip=004014ab esp=0019f834 ebp=0019fe74 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
SEHTest1!_except_handler3+0x4b:
004014ab 5d              pop     ebp

接下来又要取dwOuterTryLevel的值了:

0:000> p
eax=00000000 ebx=0019fe64 ecx=00000000 edx=00690000 esi=00000000 edi=004220c8
eip=004014ed esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x8d:
004014ed 8b348f          mov     esi,dword ptr [edi+ecx*4] ds:002b:004220c8=ffffffff

这次是-1了,except_handler3函数也到了该返回的时候,返回值是1,即ExceptionContinueSearch。

本次except_handler3函数执行,主要的工作是执行了RaiseExcept函数中外层try-except的pfnFilter,然而“落花有意,流水无情”,pfnFilter返回了0,拒绝对该异常进行处理。

except_handler3之重逢

上面已经分析了在RaiseExcept函数中注册的、位于SEH链首的except_handler3的行为,其中包含一个内层的try-finally块和外层的try-except块。其中外层的try-except块的过滤函数拒绝处理该异常,因此该函数返回。系统应用层的SEH机制将继续定位到下一个SEH节点,运行其异常处理函数。由于VC6为所有的SEH异常设置了统一的异常处理函数,即except_handler3,故执行流程又回到except_handler3中,这一次,究竟会是归人还是过客呢?

0:000> g
Breakpoint 6 hit
eax=00000000 ebx=00000000 ecx=00401460 edx=77535050 esi=00000000 edi=00000000
eip=00401460 esp=0019f858 ebp=0019f878 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3:
00401460 55              push    ebp
0:000> kb# ChildEBP RetAddr  Args to Child
00 0019f854 77535032 0019f95c 0019fed0 0019f9ac SEHTest1!_except_handler3
01 0019f878 77535004 0019f95c 0019fed0 0019f9ac ntdll!ExecuteHandler2+0x26
02 0019f944 77522936 0019f95c 0019f9ac 0019f95c ntdll!ExecuteHandler+0x24
03 0019f944 00401143 0019f95c 0019f9ac 0019f95c ntdll!KiUserExceptionDispatcher+0x26
04 0019fe74 00401268 00401540 00401540 003ea000 SEHTest1!RaiseExcept+0x43 [C:\SRC\Test\SEHTest1\Main.c @ 30]
05 0019fee0 00401653 00400000 00000000 00694687 SEHTest1!WinMain+0x48 [C:\SRC\Test\SEHTest1\Main.c @ 49]
06 0019ff70 74d6fe09 003ea000 74d6fdf0 0019ffdc SEHTest1!WinMainCRTStartup+0x113 [crt0.c @ 198]
07 0019ff80 7751662d 003ea000 5a14741e 00000000 KERNEL32!BaseThreadInitThunk+0x19
08 0019ffdc 775165fd ffffffff 7753518e 00000000 ntdll!__RtlUserThreadStart+0x2f
09 0019ffec 00000000 00401540 003ea000 00000000 ntdll!_RtlUserThreadStart+0x1b

有了上次的经验,我们就可以不必再全程跟踪一遍了。直接找到当前SEH节点的tryLevel和ScopeTable。其中,tryLevel为1,毕竟WinMain中我们也设置了内外两个try。ScopeTable内容如下:

0:000> dd 00422138
00422138  ffffffff 0040129d 004012a3 00000000
00422148  00000000 00401276

可以看出,序号为1的ScopeTable中是一个try-finally型的,其pfnFilter为0;而序号为0的ScopeTable中是一个try-except型的,它有自己的pfnFilter和pfnHandler。通过分析其函数入口可确定,它们分别对应WinMain中的内外两个try。

首先由于tryLevel为1,取到的pfnFilter为NULL:

0:000> p
eax=0019f84c ebx=0019fed0 ecx=00000003 edx=77535050 esi=00000001 edi=00422138
eip=0040149b esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
SEHTest1!_except_handler3+0x3b:
0040149b 837c8f0400      cmp     dword ptr [edi+ecx*4+4],0 ds:002b:00422148=000000000:000> p
eax=0019f84c ebx=0019fed0 ecx=00000003 edx=77535050 esi=00000001 edi=00422138
eip=004014ed esp=0019f83c ebp=0019f854 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x8d:
004014ed 8b348f          mov     esi,dword ptr [edi+ecx*4] ds:002b:00422144=00000000
; esi为取到的tryLevel

因此取其ScopeTable中的dwOuterTryLevel即0作为当前的tryLevel,此时定位到Scopetable[0].pfnFilter

eax=00000001 ebx=0019fed0 ecx=00690000 edx=00690000 esi=00000000 edi=00422138
eip=004014ab esp=0019f834 ebp=0019fee0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3+0x4b:
004014ab 5d              pop     ebp

由于该函数返回1即EXCEPTION_EXECUTE_HANDLER,表示自己愿意处理该异常,因此此时要执行global_unwind2函数执行。该函数只是对RtlUnwind的封装而已,关于这个API,我们已经在之前的文章里详细分析过其行为。

0:000> p
eax=00000001 ebx=0019fed0 ecx=00690000 edx=00690000 esi=00000000 edi=00422138
eip=0040137b esp=0019f810 ebp=0019f830 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
SEHTest1!_global_unwind2+0x13:
0040137b e842c50000      call    SEHTest1!RtlUnwind (0040d8c2)
0:000> dd esp
0019f810  0019fed0 00401380 00000000 00000000

由于传入的TargetFrame为0019fed0,TargetIP为00401380,其余两个参数为NULL,因此RtlUnwind执行的是局部展开(别和上面global_unwind2的名字搞混了,这里指的是对于操作系统用户级SEH机制是局部展开),因此会再次调用位于SEH链首的except_handler3,并在传入的EXCEPTION_RECORD.ExceptionFlags中加上EXCEPTION_UNWINDING标志(该宏为2)。下面验证一下:

0:000> p
Breakpoint 6 hit
eax=00000000 ebx=00000000 ecx=00401460 edx=77535080 esi=00000000 edi=00000000
eip=00401460 esp=0019f410 ebp=0019f430 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
SEHTest1!_except_handler3:
00401460 55              push    ebp
0:000> kb# ChildEBP RetAddr  Args to Child
00 0019f40c 77535032 0019f4e0 0019fe64 0019f530 SEHTest1!_except_handler3
01 0019f430 77535004 0019f4e0 0019fe64 0019f530 ntdll!ExecuteHandler2+0x26
02 0019f808 00401380 0019fed0 00401380 00000000 ntdll!ExecuteHandler+0x24
03 0019f830 004014bf 0019fed0 0019f854 00000000 SEHTest1!_global_unwind2+0x18
04 0019f854 77535032 0019f95c 0019fed0 0019f9ac SEHTest1!_except_handler3+0x5f
05 0019f878 77535004 0019f95c 0019fed0 0019f9ac ntdll!ExecuteHandler2+0x26
06 0019f944 77522936 0019f95c 0019f9ac 0019f95c ntdll!ExecuteHandler+0x24
07 0019f944 00401143 0019f95c 0019f9ac 0019f95c ntdll!KiUserExceptionDispatcher+0x26
08 0019fe74 00401268 00401540 00401540 003ea000 SEHTest1!RaiseExcept+0x43 [Main.c @ 30]
09 0019fee0 00401653 00400000 00000000 00694687 SEHTest1!WinMain+0x48 [Main.c @ 49]
0a 0019ff70 74d6fe09 003ea000 74d6fdf0 0019ffdc SEHTest1!WinMainCRTStartup+0x113 [crt0.c @ 198]
0b 0019ff80 7751662d 003ea000 5a14741e 00000000 KERNEL32!BaseThreadInitThunk+0x19
0c 0019ffdc 775165fd ffffffff 7753518e 00000000 ntdll!__RtlUserThreadStart+0x2f
0d 0019ffec 00000000 00401540 003ea000 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> dt _EXCEPTION_RECORD 019f4e0
ntdll!_EXCEPTION_RECORD+0x000 ExceptionCode    : 0n-1073741785+0x004 ExceptionFlags   : 2+0x008 ExceptionRecord  : (null) +0x00c ExceptionAddress : 0x00401380 Void+0x010 NumberParameters : 0+0x014 ExceptionInformation : [15] 0x6c8b60

需要注意这里调用except_handler3时,传入的pSEHNode值为0x0019fe64,即位于链首的SEH节点。这里对应的C伪码如下:

// 若为退出展开,则调用local_unwind2执行局部展开
if (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
{local_unwind2(pSEHNode, -1);result = ExceptionContinueSearch;
}

因此执行流程转到local_unwind2,下面再看其伪码,再次提醒,传入的pSEHNode为0x0019fe64,是RaiseExcept中注册的SEH节点:

// 局部展开(注意与原生SEH的局部展开概念加以区分),以函数为单位,当前的tryLevel为起点,
// 传入的nFinishTryLevel为终点(或达到本函数内最外层的try之外),由内向外执行所有的try-finally中的finally函数
PEXCEPTION_REGISTRATION_RECORD __cdecl local_unwind2(PEXCEPTION_REGISTRATION_RECORD pSEHNode, int nFinishTryLevel)
{PVC6_EXCEPTION_REGISTRATION_RECORD pVC6SEHNode = CONTAINING_RECORD(pSEHNode, VC6_EXCEPTION_REGISTRATION_RECORD, pSEHNode);PVC6_SCOPETABLE pScopeTable;int tryLevel;while (TRUE){pScopeTable = pVC6SEHNode->pScopeTable;tryLevel = pVC6SEHNode->dwTryLevel;if (tryLevel == -1 || tryLevel == nFinishTryLevel){break;}pVC6SEHNode->dwTryLevel = pScopeTable[tryLevel].dwOuterLevel;if (!pScopeTable[tryLevel].pfnFilter)    // finally型pfnFilter为NULL{NLG_Notify(257);pScopeTable[tryLevel].pfnHandler();   // 执行finally函数}}return pSEHNode;
}

从VC6_EXCEPTION_REGISTRATION_RECORD中取得的tryLevel为1,因此ScopeTable里的finally型函数都会被执行。在本例中,RaiseExcept中的finally块将被执行。可以概括出,local_unwind2的作用即是执行当前ScopeTable中所有在当前tryLevel之外(即包含当前异常代码)的finally函数。

在执行完global_unwind2后,RaiseExcept中的finally已经执行完了。下面接着执行local_unwind2,但传入的是本节点的SEHNode,因此会把当前tryLevel之上的finally块全部都执行了。

// 执行全局展开
global_unwind2(pSEHNode);
DWORD dwOrgEBP = pVC6SEHNode->dwEBP;
// 栈基址指针切换
_asm
{
mov ebp, dwOrgEBP
}
// 执行局部展开
local_unwind2(pSEHNode, tryLevel);
NLG_Notify(1);
pVC6SEHNode->dwTryLevel = pScopeTable[tryLevel].dwOuterLevel;
// 调用异常处理函数
pScopeTable[tryLevel].pfnHandler();

小结

若某个函数中使用了VC的结构化异常处理机制,则VC编译器会在函数入口和出口分别在栈上安装和卸载一个异常处理节点,该节点在EXCEPTION_REGISTRATION_RECORD的基础上“加了料”,成了VC6_EXCEPTION_REGISTRATION_RECORD(名字是咱自己取的),其中的Handler指向共用的except_handler3函数。

当被try保护的代码出现异常时,本函数内注册的except_handler3首先被执行,通过栈上的tryLevel和pScopeTable,定位到当前的异常处理函数,找到能够处理该异常的try-except。在找到try-except后,通过global_unwind2执行局部展开(相对系统用户层SEH来说只能算局部展开),将SEH链首到当前节点之间注册的SEH的except_handler3逐一进行调用,在except_handler3中检测到传入参数的ExceptRecord中的flags字段EXCEPTION_UNWINDING(2)标志置位后,则以当前tryLevel为起点,在本ScopeTable中依次向上调用各finally块。接着调用local_unwind2对当前层完成的各finally函数进行执行。完成这一切后再执行pfnHandler。

Windows异常世界历险记(五)——VC6中结构化异常处理机制的反汇编分析(下)相关推荐

  1. 什么是VB.NET的结构化异常处理

    深谈VB.NET结构化异常处理执行标准 对于VB.NET语言来说,在实际编程中有一个非常重要的操作值得我们去注意,那就关于异常方面的处理.今天我们就可以通过VB.NET结构化异常处理的相关介绍,来仔细 ...

  2. 异常处理第三讲,SEH(结构化异常处理),异常展开问题

    异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 不知道昨天有 ...

  3. Windows系统程序设计之结构化异常处理

    标 题: [原创]Windows系统程序设计之结构化异常处理 作 者: 北极星2003 时 间: 2006-09-20,20:21:28 链 接: http://bbs.pediy.com/showt ...

  4. windows核心编程学习笔记(八)结构化异常处理(Structured Exception Handling)

    首先要要知道,结构化异常处理(SEH)和C++提供的异常处理不相同. 一.Termination HandlersTermination Handlers使用很简单.在想使用SEH处理的地方使用 __ ...

  5. Windows内存管理(3)--检查内存可用性,结构化异常处理 和 ASSERT

    1.      检查内存可用性 在驱动程序开发中,对内存的操作要格外小心.如果某段内存是只读的,而驱动程序试图去写操作,会导致系统的崩溃. DDK提供了两个函数,帮助程序员在不知道某段内存是否可读写的 ...

  6. 深入探索Win32结构化异常处理

    原文:http://blog.csdn.net/diamont/article/details/4259590 Matt Pietrek 著 董岩 译 在Win32操作系统提供的所有功能中,使用最广泛 ...

  7. 深入解析结构化异常处理(SEH) - by Matt Pietrek

    目录 1.浅析SEH 2.移向更深处 3.编译器层面的SEH 4.扩展的异常处理帧 5.ShowSEHFrames程序 6.展开 7.未处理异常 8.进入地狱 9.结论 ​尽管以前写过一篇SEH相关的 ...

  8. Win32 结构化异常处理(SEH)探秘【下篇】

    图十三 UnHandledExceptionFilter 函数的伪代码 UnhandledExceptionFilter( STRUCT _EXCEPTION_POINTERS *pException ...

  9. 结构 win32_COM编程攻略(十五 持久化与结构化存储)

    前情提要: Froser:COM编程攻略(十四 连接点与其ATL实现)​zhuanlan.zhihu.com 这一篇主要来说一下持久化和结构化存储. 本篇文章和上一篇没有关系. 一.什么是持久化(Pe ...

最新文章

  1. 干货|对比理解不同概率估计和模型损失函数
  2. AI最优论文+代码查找神器:966个ML任务、8500+论文任你选
  3. 服务器2012r2系统安装数据库,数据库图文详解Windows Server2012 R2中安装SQL Server2008...
  4. 2015-2016 ACM-ICPC Northeastern European Regional Contest (NEERC 15)
  5. 挑战Redis单实例内存最大极限,“遭遇”NUMA陷阱!
  6. java stream中Collectors的用法
  7. 如何将目录下几百个lib加入到vs项目属性下的链接器
  8. spring boot中servlet启动原理
  9. android sd卡 格式化 rom,Android开发之获取SD卡及手机ROM容量的方法
  10. 9行Python代码搭建神经网络来掌握一些基本概念
  11. Uva 11395 Sigma Function (因子和)
  12. 前端大牛们都学过哪些东西
  13. MySQL error(2014) Commands out of sync; you can't run this command now(情形1)
  14. SharePoint 2013 添加Ribbon菜单
  15. 10款神奇的字符图案 amp; 词汇云生成工具
  16. 计算机科学梦想演讲稿,关于科技演讲稿讲话稿范文三篇
  17. dp算法求解矩阵连乘的问题
  18. 会员数据化运营RFM
  19. Excel数据去重(删除重复项)
  20. 四大CPU架构的区别

热门文章

  1. Hibernate作业(一)
  2. 今天写点什么练习题呢
  3. 笑死人不偿命的知乎沙雕问题排行榜
  4. 基于vue利用openlayers加载天地图的影像图,地形图
  5. 旋转数组的最小元素——《剑指offer》
  6. 中国石油大学《化工原理一》第一阶段在线作业
  7. transform: scale() 图片文字模糊的原因
  8. iOS 语言基础初探 Xcode 工具
  9. 【工具编写】python实现非对称加密RSA算法
  10. 【HEOI2012】采花