2011年10月7日星期五

[轉載]SEH分析筆記(X86篇)二


一、SEH 創建代碼
  這裡我沒有繼續使用上面的SimpleSeh 進行分析,而是新寫了一個簡單的SehTest 函數。

代碼:
        VOID SehTest()
        {
            ULONG ulVal = 0;

            __try // 第一個 __try 域
            {
                ulVal = 0x11111111; // 最後一位為1表示“在__try 代碼塊中”
            }
            __except(Filter_0())
            {
                ulVal = 0x11111110; // 最後一位為0表示“在__except/__finally 代碼塊中”
            }

            __try // 第二個 __try 域
            {
                ulVal = 0x22222222;

                __try // 第三個 __try 域
                {
                    ulVal = 0x33333333;

                    *((ULONG*)NULL) = ulVal; // 觸發異常
                }
                __finally
                {
                    ulVal = 0x33333330;
                }
            }
            __except(Filter_2())
            {
                ulVal = 0x22222220;
            }

            return;
        }

  反彙編代碼如下:
  (需要說明一下我的命名習慣是,"l_"前綴的變量是函數的局部變量,沒有任何前綴的變量是傳入的參數)

引用:
    kd> uf passthrough!SehTest
        ;PassThrough!SehTest [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 604]:
          f8720040 mov edi,edi
          f8720042 push ebp ; l_ExceptionRegistration->_ebp
          f8720043 mov ebp,esp
          f8720045 push 0FFFFFFFEh ; l_ExceptionRegistration->trylevel = TRYLEVEL_INVALID (-2)
          f8720047 push offset PassThrough!__safe_se_handler_table+0x8 (f8721468) ; l_ExceptionRegistration->scopetable
          f872004c push offset PassThrough!_except_handler4 (f8720390) ; l_ExceptionRegistration->handler
          f8720051 mov eax,dword ptr fs:[00000000h]
          f8720057 push eax ; _EXCEPTION_REGISTRATION::prev
          f8720058 add esp,0FFFFFFF4h ; 這里分配了0xc 字節的棧空間,其中緊貼著l_ExceptionRegistration->prev
                                            ; 的4個字節存放著l_ExceptionRegistration->xpointers
          f872005b push ebx
          f872005c push esi
          f872005d push edi
          f872005e mov eax,dword ptr [PassThrough!__security_cookie (f87220b0)]
          f8720063 xor dword ptr [ebp-8],eax ; 對scopetable 進行異或加密
          f8720066 xor eax,ebp ; 對__security_cookie 進行加密
          f8720068 push eax ; 把加密了的__security_cookie 也壓入棧中,後面用來對scopetable 進行解密
          f8720069 lea eax,[ebp-10h]
          f872006c mov dword ptr fs:[00000000h],eax ; 將l_ExceptionRegistration 掛入線程異常鍊錶中
          f8720072 mov dword ptr [ebp-18h],esp
          f8720075 mov dword ptr [ebp-1Ch],0
          f872007c mov dword ptr [ebp-4],0 ; 進入第一個__try 域,l_ExceptionRegistration->trylevel = 0
          f8720083 mov dword ptr [ebp-1Ch],11111111h
          f872008a mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第一個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
          f8720091 jmp PassThrough!SehTest+0x6a (f87200aa)
            ; ------------------------------------------------- --------------------------------
            ; 這裡有個空洞,用uf 命令是不會顯示的。範圍是 f8720093 到 f87200a9,彙編碼如下
            ;
            ; PassThrough!SehTest+0x53 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 611]:
            ; f8720093 call PassThrough!Filter_0 (f8720010)
            ; f8720098 ret
            ;
            ; f8720099 mov esp,dword ptr [ebp-18h] ; 第一個__except 處理域
            ; f872009c mov dword ptr [ebp-1Ch],11111110h
            ; f87200a3 mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第一個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
            ; ------------------------------------------------- --------------------------------
        ;PassThrough!SehTest+0x6a [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 616]:
          f87200aa mov dword ptr [ebp-4],1 ; 進入第二個__try 域,l_ExceptionRegistration->trylevel = 1
          f87200b1 mov dword ptr [ebp-1Ch],22222222h
          f87200b8 mov dword ptr [ebp-4],2 ; 進入第三個__try 域,l_ExceptionRegistration->trylevel = 2
          f87200bf mov dword ptr [ebp-1Ch],33333333h
          f87200c6 mov eax,dword ptr [ebp-1Ch]
          f87200c9 mov dword ptr ds:[00000000h],eax ; 觸發異常
          f87200ce mov dword ptr [ebp-4],1 ; 離開第三個__try 域,l_ExceptionRegistration->trylevel = 1
          f87200d5 call PassThrough!SehTest+0x9c (f87200dc)
          f87200da jmp PassThrough!SehTest+0xa4 (f87200e4)
            ; ------------------------------------------------- --------------------------------
            ; 空洞,範圍是f87200dc 到f87200e3,彙編碼如下
            ;
            ; PassThrough!SehTest+0x9c [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 628]:
            ; f87200dc c745e430333333 mov dword ptr [ebp-1Ch],33333330h ; __finally 域
            ; ------------------------------------------------- --------------------------------
        ;PassThrough!SehTest+0xa4 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 630]:
          f87200e4 mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第二個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
          f87200eb jmp PassThrough!SehTest+0xc4 (f8720104)
            ; ------------------------------------------------- --------------------------------
            ; 空洞,範圍是f87200ed 到f8720103,彙編碼如下
            ;
            ; PassThrough!SehTest+0xad [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 631]:
            ; f87200ed e83effffff call PassThrough!Filter_2 (f8720030)
            ; f87200f2 c3 ret
            ;
            ; f87200f3 8b65e8 mov esp,dword ptr [ebp-18h] ; 第二個__except 處理域
            ; f87200f6 c745e420222222 mov dword ptr [ebp-1Ch],22222220h
            ; f87200fd c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh ; 離開第三個__try 域,l_ExceptionRegistration->trylevel = TRYLEVEL_NONE (-2)
            ; ------------------------------------------------- --------------------------------
        ;PassThrough!SehTest+0xc4 [d:\workspace\code\mycode\r0\passthrough\passthrough.c @ 637]:
          f8720104 mov ecx,dword ptr [ebp-10h]
          f8720107 mov dword ptr fs:[0],ecx ; 恢復舊的EXCEPTION_REGISTRATION。即從線程異常鍊錶中摘除l_ExceptionRegistration
          f872010e pop ecx
          f872010f pop edi
          f8720110 pop esi
          f8720111 pop ebx
          f8720112 mov esp,ebp
          f8720114 pop ebp
          f8720115 ret

  來看看 scopetable 的內容:

  kd> dd f8721468
        f8721468 fffffffe 00000000 ffffffd4 00000000 < 16個字節的坑
        f8721478 [fffffffe f8720093 f8720099] [fffffffe
        f8721488 f87200ed f87200f3] [00000001 00000000
        f8721498 f87200dc] 00000000 00000000 00000000
        f87214a8 00000000 00000000 00000000 00000000
        f87214b8 00000000 00000000 00000000 00000000
        f87214c8 00000000 00000000 00000000 00000000
        f87214d8 00000000 00000000 00000000 00000000

  前16個字節是坑,坑的作用晚點再說。之後就是三個scopetable_entry,被我用大括號擴起來了。對照前面的彙編碼可以發現,scopetable_entry::lpfnFilter 和scopetable_entry::lpfnHandler 就是彙編碼中的空洞處的代碼。其中,第三個scopetable_entry::lpfnFilter 是NULL,對照代碼可以發現,是因為這是一個__try & __finally 塊,沒有lpfnFilter。

  彙編代碼很簡單,註釋裡也有詳細的分析過程了,我不再多囉嗦。只提兩點:
  1. EXCEPTION_REGISTRATION::scopetable 指針被用__security_cookie 進行了異或加密。
  2. EXCEPTION_REGISTRATION::scopetable 並不直接指向scopetable_entry 數組,在第一個scopetable_entry 之前有16 個字節的坑。後續分析中會看到,它的主要作用是幫助驗證scopetable 是否被破壞。第三個DWORD,即上文中的ffffffd4 是一個偏移量,後續的分析過程中會看得很清楚。

没有评论:

发表评论