在本系列的上一篇文章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的结构定义在上一篇中被我们命名为了