SEH stack 结构探索(5)--- __exception_handler4() 探秘3之 TryLevel

SEH stack 结构探索(5)--- __exception_handler4() 探秘3之 TryLevel


现在我们探索一下 SEH stack 里的 TryLevel

到底 TryLevel 是什么东西,来看看下面的代码:

        __try {                               /* 0-level */

                __try {                       /* 1-level */

                        __try {               /* 2-level */
                        } __except(1) {
                        }

                } __except(1) {
                }

        } __except(1) {
        }

上面的代码嵌套了三层 __try{} 结构,那么最外层被称作 0-level,依次往里最里一层是 2-level,这些层并不是我凭空编出来的,看看 VC++ 的编译结果:

 __try {
00FB3428 C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0 

  __try {
00FB342F C7 45 FC 01 00 00 00 mov         dword ptr [ebp-4],1 

   __try {
00FB3436 C7 45 FC 02 00 00 00 mov         dword ptr [ebp-4],2  
   } __except(1) {
00FB343D C7 45 FC 01 00 00 00 mov         dword ptr [ebp-4],1  
00FB3444 EB 10                jmp         $LN16+0Ah (0FB3456h)  
$LN15:
00FB3446 B8 01 00 00 00       mov         eax,1  
$LN17:
00FB344B C3                   ret  
$LN16:
00FB344C 8B 65 E8             mov         esp,dword ptr [ebp-18h]  
   }
00FB344F C7 45 FC 01 00 00 00 mov         dword ptr [ebp-4],1 

  } __except(1) {
00FB3456 C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0  
00FB345D EB 10                jmp         $LN12+0Ah (0FB346Fh)  
$LN11:
00FB345F B8 01 00 00 00       mov         eax,1  
$LN13:
00FB3464 C3                   ret  
$LN12:
00FB3465 8B 65 E8             mov         esp,dword ptr [ebp-18h]  
  }
00FB3468 C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0 

 } __except(1) {
00FB346F C7 45 FC FE FF FF FF mov         dword ptr [ebp-4],0FFFFFFFEh  
00FB3476 EB 10                jmp         $LN8+0Ah (0FB3488h)  
$LN7:
00FB3478 B8 01 00 00 00       mov         eax,1  
$LN9:
00FB347D C3                   ret  
$LN8:
00FB347E 8B 65 E8             mov         esp,dword ptr [ebp-18h]  
 }
00FB3481 C7 45 FC FE FF FF FF mov         dword ptr [ebp-4],0FFFFFFFEh 

在 __try {} 块头编译器生成设置 [ebp-4] 值的代码,每个 __try{} 就是一层,红色粗体部分代码显示正如前面所说的:最外层被设为 0 层,最内层被设为 2 层

蓝色粗体部分代码显示每退出一层,就设回上一层,最后粉红色粗体代码部分显示:退出所有的层后,被设回初始值 0xFFFFFFFF 

好啦,这个 [ebp-4] 位置就是我想要说的 TryLevel 值所在。

那么,我们对 SEH stack 的布局了解又增进了一分:

看完上图,我们会有许多疑问:这个 TryLevel 是怎样作用的? ScopeTable 后面的结构是怎样的?下面我会作出解答。

下面我们还是先看一段代码:

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{

    __try 
    { 
        *pResult = dividend / divisor; 
    } 
    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? 
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    { 
        return FALSE;
    }


    return TRUE;
}

 __try {} 里对这个除法进行保护,当除 0 时就会产生除 0 异常,异常码(ExceptionCode)是 EXCEPTION_INT_DIVIDE_BY_ZERO,那么系统应该去处理除 0 的Exception Handler(异常处理程序)。

现在我们重点放在 __except() 块里,括号里的表达式被称为 Exception Filter(异常过滤)表达式。这个表达式结果是什么在这里不是重点,重点是看看 VC++ 编译出来的代码:

    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? 
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
00D63444 8B 45 EC             mov         eax,dword ptr [ebp-14h]  
00D63447 8B 08                mov         ecx,dword ptr [eax]  
00D63449 8B 11                mov         edx,dword ptr [ecx]  
00D6344B 89 95 20 FF FF FF    mov         dword ptr [ebp-0E0h],edx  
00D63451 8B 85 20 FF FF FF    mov         eax,dword ptr [ebp-0E0h]  
00D63457 33 C9                xor         ecx,ecx  
00D63459 3D 94 00 00 C0       cmp         eax,0C0000094h  
00D6345E 0F 94 C1             sete        cl  
00D63461 8B C1                mov         eax,ecx  
$LN7:
00D63463 C3                   ret  
$LN6:
00D63464 8B 65 E8             mov         esp,dword ptr [ebp-18h]  
    { 
        return FALSE;
00D63467 C7 85 14 FF FF FF 00 00 00 00 mov         dword ptr [ebp-0ECh],0  
00D63471 C7 45 FC FE FF FF FF mov         dword ptr [ebp-4],0FFFFFFFEh  
00D63478 8B 85 14 FF FF FF    mov         eax,dword ptr [ebp-0ECh]  
00D6347E EB 0C                jmp         $LN6+28h (0D6348Ch)  
    }

上面这段就是 VC++ 编译出来的代码。在 __except() 的括号里未进入 { } 块里进行了许多处理,我关心的就是括号里的这段代码。

为什么我会这么关注这段代码?当然很关键,它关系到 TryLevel 是如何运作的。

这段代码被称为 FilterFunc(过滤器函数),TryLevel 就决定是怎样调用这段 FilterFunc 函数:

00D63444 8B 45 EC             mov         eax,dword ptr [ebp-14h]  
00D63447 8B 08                mov         ecx,dword ptr [eax]  
00D63449 8B 11                mov         edx,dword ptr [ecx]  
00D6344B 89 95 20 FF FF FF    mov         dword ptr [ebp-0E0h],edx  
00D63451 8B 85 20 FF FF FF    mov         eax,dword ptr [ebp-0E0h]  
00D63457 33 C9                xor         ecx,ecx  
00D63459 3D 94 00 00 C0       cmp         eax,0C0000094h  
00D6345E 0F 94 C1             sete        cl  
00D63461 8B C1                mov         eax,ecx  
$LN7:
00D63463 C3                   ret 

这段 FilterFunc 会返回给调用它的 caller,这一切答案就在 __except_handler4_common() 里,我们继续接着上一篇来看 __except_handler4_common() 的代码:

01152487 8b5510          mov     edx,dword ptr [ebp+10h]          ; ExceptionRecord
0115248a 8b4204          mov     eax,dword ptr [edx+4]            ; ExceptionRecord->ExceptionFlags
0115248d 83e066          and     eax,66h
01152490 0f8523010000    jne     MSVCR100D!_except_handler4_common+0x179 (011525b9)

MSVCR100D!_except_handler4_common+0x56:
01152496 8b4d10          mov     ecx,dword ptr [ebp+10h]          ; ExceptionRecord
01152499 894df8          mov     dword ptr [ebp-8],ecx            ; [ExceptionRecord]
0115249c 8b5518          mov     edx,dword ptr [ebp+18h]          ; ContextRecord
0115249f 8955fc          mov     dword ptr [ebp-4],edx            ; [ContextRecord]
011524a2 8b45e0          mov     eax,dword ptr [ebp-20h]          ; [RegistrationNode]
011524a5 8d4df8          lea     ecx,[ebp-8]                      ; &ExceptonRecord
011524a8 894804          mov     dword ptr [eax+4],ecx            ; ExceptionPointers = &ExceptionRecord
011524ab 8b55e0          mov     edx,dword ptr [ebp-20h]          ; [RegistrationNode]
011524ae 8b4214          mov     eax,dword ptr [edx+14h]          ; get TryLevel

011524b1 8945d8          mov     dword ptr [ebp-28h],eax
011524b4 eb06            jmp     MSVCR100D!_except_handler4_common+0x7c (011524bc)

MSVCR100D!_except_handler4_common+0x76:
011524b6 8b4dec          mov     ecx,dword ptr [ebp-14h]
011524b9 894dd8          mov     dword ptr [ebp-28h],ecx

MSVCR100D!_except_handler4_common+0x7c:
011524bc 837dd8fe       cmp     dword ptr [ebp-28h],0FFFFFFFEh
011524c0 0f84f1000000    je      MSVCR100D!_except_handler4_common+0x177 (011525b7)

MSVCR100D!_except_handler4_common+0x86:
011524c6 8b55d8          mov     edx,dword ptr [ebp-28h]
011524c9 6bd20c         imul    edx,edx,0Ch               ; TryLevel * 12
011524cc 8b45d4          mov     eax,dword ptr [ebp-2Ch]           ; ScopeTable
011524cf 8d4c1010        lea     ecx,[eax+edx+10h]                 ; ScopeRecord
011524d3 894de4          mov     dword ptr [ebp-1Ch],ecx
011524d6 8b55e4          mov     edx,dword ptr [ebp-1Ch]
011524d9 8b4204         mov     eax,dword ptr [edx+4]             ; get FilterFunc
011524dc 8945d0          mov     dword ptr [ebp-30h],eax
011524df 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
011524e2 8b11            mov     edx,dword ptr [ecx]               ; EnclosingLevel
011524e4 8955ec          mov     dword ptr [ebp-14h],edx
011524e7 837dd000        cmp     dword ptr [ebp-30h],0             ; FilterFunc == NULL ?
011524eb 0f84c1000000    je      MSVCR100D!_except_handler4_common+0x172 (011525b2)

MSVCR100D!_except_handler4_common+0xb1:
011524f1 8b55e8          mov     edx,dword ptr [ebp-18h]
011524f4 8b4dd0         mov     ecx,dword ptr [ebp-30h]               ; 传 FilterFunc 给 EH4_CallFilterFunc() 执行
011524f7 e806b4ffff      call    MSVCR100D!_EH4_CallFilterFunc (0114d902)


1. 获取 TryLevel

011524ab 8b55e0          mov     edx,dword ptr [ebp-20h]          ; [RegistrationNode]
011524ae 8b4214          mov     eax,dword ptr [edx+14h]          ; get TryLevel

请对照上面的 EH4 stack 结构图来看,[ebp-20h] 是上一篇分析得来的 RegistrationNode(局部变量),这个变量装的就是 EH4 stack 里保存 esp 的位置,那么[edx+14h] 也就是 esp 的位置加上 14h 得出 TryLevel 的位置。

再一次提醒:参照上面的 EH4 stack 进行理解


2. 检查 TryLevel 值

011524bc 837dd8fe        cmp     dword ptr [ebp-28h],0FFFFFFFEh
011524c0 0f84f1000000    je      MSVCR100D!_except_handler4_common+0x177 (011525b7)

[ebp-28h] 是获得 TryLevel 保存在局部 stack 的位置,代码中检查 TryLevel 是否为 0xFFFFFFFE 值,如果是 0xFFFFFFFE 值的话说明它是初始的 TryLevel 值,也就是没有可以处理的 FilterFunc,上面我们已经看到,当所有的 __try{} 退出后,会重新将 TryLevel 设为 0xFFFFFFFE 值


3. 获得 FilterFunc 函数

011524c6 8b55d8          mov     edx,dword ptr [ebp-28h]
011524c9 6bd20c          imul    edx,edx,0Ch               ; TryLevel * 12
011524cc 8b45d4          mov     eax,dword ptr [ebp-2Ch]           ; ScopeTable
011524cf 8d4c1010        lea     ecx,[eax+edx+10h]                 ; ScopeRecord
011524d3 894de4          mov     dword ptr [ebp-1Ch],ecx
011524d6 8b55e4          mov     edx,dword ptr [ebp-1Ch]
011524d9 8b4204          mov     eax,dword ptr [edx+4]             ; get FilterFunc
011524dc 8945d0          mov     dword ptr [ebp-30h],eax
011524df 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
011524e2 8b11            mov     edx,dword ptr [ecx]               ; EnclosingLevel
011524e4 8955ec          mov     dword ptr [ebp-14h],edx
011524e7 837dd000        cmp     dword ptr [ebp-30h],0             ; FilterFunc == NULL ?
011524eb 0f84c1000000    je      MSVCR100D!_except_handler4_common+0x172 (011525b2)

首先计算得出 EH4_SCOPETABLE_RECORD 结构,这个 EH4_SCOPETABLE_RECORD 是 3 个成共 12 bytes

0:000:x86> dt _EH4_SCOPETABLE_RECORD
MSVCR100D!_EH4_SCOPETABLE_RECORD
   +0x000 EnclosingLevel   : Uint4B
   +0x004 FilterFunc       : Ptr32     long 
   +0x008 u                : <unnamed-tag>

TryLevel * 12 得出来的结果就是获得哪一层的 ScopeRecord,假如 TryLevel 为 0,代表它正处于 0-level,此时 TryLevel * 12 = 0;

接下来 [eax+edx+10h] 也就是:ScopeTable 的基址加上 10h 偏移量,再加上 (TryLevel * 12) 的值,那就得到哪一层的 EH4_SCOPETABLE_RECORD

[edx+4] 结果就是 FilterFunc 函数的地址,最后测试 FilterFunc == NULL

因此:得到 FilterFunc 的方法是:

计算方法是:

FilterFunc = ScapeTable + TryLevel * sizeof(struct _EH4_SCOPETABLE_RECORD) + 0x10;

 

4. 将 FilterFunc 交由 EH4_CallFilterFun() 执行

011524f1 8b55e8          mov     edx,dword ptr [ebp-18h]
011524f4 8b4dd0         mov     ecx,dword ptr [ebp-30h]               ; 传 FilterFunc 给 EH4_CallFilterFunc() 执行
011524f7 e806b4ffff      call    MSVCR100D!_EH4_CallFilterFunc (0114d902)

[ebp-18h] 的值是 FramePointer 也就是 ebp,[ebp-30h] 的值是 FilterFunc 函数地址。


5. EH4_CallFilterFun() 函数

MSVCR100D!_EH4_CallFilterFunc:
6b78d902 55              push    ebp
6b78d903 56              push    esi
6b78d904 57              push    edi
6b78d905 53              push    ebx
6b78d906 8bea            mov     ebp,edx
6b78d908 33c0            xor     eax,eax
6b78d90a 33db            xor     ebx,ebx
6b78d90c 33d2            xor     edx,edx
6b78d90e 33f6            xor     esi,esi
6b78d910 33ff            xor     edi,edi
6b78d912 ffd1            call    ecx
6b78d914 5b              pop     ebx
6b78d915 5f              pop     edi
6b78d916 5e              pop     esi
6b78d917 5d              pop     ebp
6b78d918 c3              ret

其中这句 call ecx 跳转到 FilterFunc 去执行,而 FileterFunc 就是我们上面所看到的 __excpt() 块括号内的代码。

 

现在我们明白了 TryLevel 是怎样运作了吧。也明白了嵌套 __try{} 的结构了吧。还明白了怎样调用 FilterFunc 了吧。

今天的收获还不错,:)

 

上一页  目录  下一页


版权 mik 所有,转载请注明出处!

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值