2011年10月8日星期六

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


二、MSC 提供的EXCEPTION_REGISTRATION::handler 函數
  之前咱們有說過EXCEPTION_REGISTRATION::handler 指向由MSC 編譯器提供的一個函數,這個函數內部負責調用scopetable[?]->lpfnFilter/lpfnHandler。這個函數通常為module!_except_handler?,其中module為模塊名,? 表示某數字。在分析過程中發現,有的模塊完整實現了該函數,有的模塊直接使用了系統提供的nt!_except_handler?。不知道是因為版本的問題,還是編譯選項的問題。我沒有深究。
  我繼續以passhThrough 模塊為例,來說明這個函數。
  首先需要再次說明一下我的註釋習慣:後續列出來的反彙編代碼中,最左邊的:<>符號表示跳轉,是我在分析的過程中為了方便理解流程手寫的。其中< 表示跳轉源,> 是跳轉目標,: 用於組成連接線。
  為了方便對照,我把經過整理的PassThrough!_except_handler4 的原​​型列在這裡:

  EXCEPTION_DISPOSITION _except_handler4 (
      PEXCEPTION_RECORD pExceptionRecord,
      PEXCEPTION_REGISTRATION pExceptionRegistration,
      PCONTEXT,
      PDISPATCHER_CONTEXT
  );

  反彙編代碼:

代碼:
    kd> uf PassThrough!_except_handler4
                  PassThrough!_except_handler4 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 255]:
                    f8720390 mov edi,edi
                    f8720392 push ebp
                    f8720393 mov ebp,esp
                    f8720395 sub esp,14h
                    f8720398 push ebx
                    f8720399 mov ebx,dword ptr [ebp+0Ch] ; ebx = pExceptionRegistration
                    f872039c push esi
                    f872039d mov esi,dword ptr [ebx+8] ; esi = pExceptionRegistration->scopetable
                    f87203a0 xor esi,dword ptr [PassThrough!__security_cookie (f87220b0)] ; 用__security_cookie 對scopetable 解密
                    f87203a6 push edi
                    f87203a7 mov eax,dword ptr [esi]
                    f87203a9 mov byte ptr [ebp-1],0 ; ebp-1 存放的是一個BOOLEAN 值,用來表示是否執行過任何scopetable_entry::lpfnFilter
                    f87203ad mov dword ptr [ebp-8],1 ; ebp-8 被用來存放本函數的返回值,這裡初始化為ExceptionContinueSearch (1)
                    f87203b4 lea edi,[ebx+10h] ; edi = pExceptionRegistration->_ebp
                    f87203b7 cmp eax,0FFFFFFFEh ; 檢查scopetable 中坑的第一個DWORD 值,後續來做相關的安全處理
< f87203ba je PassThrough!_except_handler4+0x39 (f87203c9)
:
: PassThrough!_except_handler4+0x2c [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 315]:
: ; 校驗scopetable 完整性(1)
: f87203bc mov ecx,dword ptr [esi+4]
: f87203bf add ecx,edi
: f87203c1 xor ecx,dword ptr [eax+edi]
: f87203c4 call PassThrough!__security_check_cookie (f8720638)
:
: PassThrough!_except_handler4+0x39 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 315]:
: ; 校驗scopetable 完整性(2)
> f87203c9 mov ecx,dword ptr [esi+0Ch]
                   f87203cc mov eax,dword ptr [esi+8]
                   f87203cf add ecx,edi
                   f87203d1 xor ecx,dword ptr [eax+edi]
                   f87203d4 call PassThrough!__security_check_cookie (f8720638)
                    ; 安全工作處理完畢,開始進入異常處理流程
                   f87203d9 mov eax,dword ptr [ebp+8] ; eax = pExceptionRecord
                   f87203dc test byte ptr [eax+4],66h ; pExceptionRecord->ExceptionFlags & EXCEPTION_UNWIND,判斷是異常處理過程還是展開過程
< f87203e0 jne PassThrough!_except_handler4+0x138 (f87204c8)
:
: PassThrough!_except_handler4+0x56 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 331]:
: ; 異常處理過程
: f87203e6 mov ecx,dword ptr [ebp+10h] ; ecx = pContext
: f87203e9 lea edx,[ebp-14h]
: f87203ec mov dword ptr [ebx-4],edx ; ebx-4 是創建EXCEPTION_REGISTRATION 時候預留的xpointers 的空間,這裡給它賦值
: f87203ef mov ebx,dword ptr [ebx+0Ch] ; ebx = pExceptionRegistration->trylevel
: f87203f2 mov dword ptr [ebp-14h],eax ; [ebp-14] = pExceptionRecord,即xpointers->ExceptionRecord = pExceptionRecord
: f87203f5 mov dword ptr [ebp-10h],ecx ; [ebp-10] = pContext,即xpointers->ContextRecord = pContext
: f87203f8 cmp ebx,0FFFFFFFEh ; cmp pExceptionRegistration->trylevel, TRYLEVEL_INVALID
: < f87203fb je PassThrough!_except_handler4+0xcc (f872045c)
: :
: : PassThrough!_except_handler4+0x6d [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 341]:
: : f87203fd lea ecx,[ecx]
: :
: : PassThrough!_except_handler4+0x70 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 343]:
: : > f8720400 lea eax,[ebx+ebx*2] ; 這裡*3,下面緊接著*4,即*12,實際上是為了跳過x 個scopetable_entry (大小為12個字節)
: : : f8720403 mov ecx,dword ptr [esi+eax*4+14h] ; ecx = scopetable[i].lpfnFilter, 這裡14h是為了跳過10h大小的坑
: : : f8720407 lea eax,[esi+eax*4+10h] ; eax = &scopetable[i]
: : : f872040b mov dword ptr [ebp-0Ch],eax ; ebp-0Ch 存放的是pCurrentScopeTableEntry
: : : f872040e mov eax,dword ptr [eax] ; eax = scopetable[i].previousTryLevel
: : : f8720410 mov dword ptr [ebp+8],eax ; 這裡是將ebp+8 當作局部變量使用, [ebp+8] = scopetable[i].previousTryLevel
: : : f8720413 test ecx,ecx ; 判斷scopetable[i].lpfnFilter 是否為NULL
: : < : f8720415 je PassThrough!_except_handler4+0x9b (f872042b)
: : : :
: : : : PassThrough!_except_handler4+0x87 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 355]:
: : : : ; scopetable[i].lpfnFilter 不為NULL,調用它
: : : : f8720417 mov edx,edi ; edx = pExceptionRegistration->_ebp
: : : : f8720419 call PassThrough!_EH4_CallFilterFunc (f87205d1)
: : : : f872041e mov byte ptr [ebp-1],1 ; [ebp-1] 表示是否執行過lpfnFilter,它是個BOOLEAN 值
: : : : f8720422 test eax,eax
: : : < : f8720424 jl PassThrough!_except_handler4+0xd6 (f8720466) ; 如果是EXCEPTION_CONTINUE_EXECUTION (-1) 就跳
: : : : :
: : : : : PassThrough!_except_handler4+0x96 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 370]:
: : : : < : f8720426 jg PassThrough!_except_handler4+0xdf (f872046f) ; 如果是EXCEPTION_EXECUTE_HANDLER (1) 就跳
: : : : : :
: : : : : : PassThrough!_except_handler4+0x98 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 370]:
: : : : : : ; lpfnFilter 返回EXCEPTION_CONTINUE_SEARCH
: : : : : : f8720428 mov eax,dword ptr [ebp+8] ; eax = scopetable[i].previousTryLevel
: : : : : :
: : : : : : PassThrough!_except_handler4+0x9b [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 341]:
: : > : : : f872042b mov ebx,eax
: : : : : f872042d cmp eax,0FFFFFFFEh ; cmp scopetable[i].previousTryLevel, TRYLEVEL_INVALID
: : : : < f8720430 jne PassThrough!_except_handler4+0x70 (f8720400)
: : : :
: : : : PassThrough!_except_handler4+0xa2 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 478]:
: : : : f8720432 cmp byte ptr [ebp-1],0 ; 沒有執行過lpfnFilter,無需進行安全檢查
: : < : : f8720436 je PassThrough!_except_handler4+0xcc (f872045c)
: : : : :
: : : : : PassThrough!_except_handler4+0xa8 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 486]:
: : : : : ; 執行過lpfnFilter,需要校驗完整性
: : : : : > > f8720438 mov eax,dword ptr [esi]
: : : : : : : f872043a cmp eax,0FFFFFFFEh ; 根據scopetable 坑的第一個DWORD 值判斷是否需要做進一步的安全檢查
: : : : : < : : f872043d je PassThrough!_except_handler4+0xbc (f872044c)
: : : : : : : :
: : : : : : : : PassThrough!_except_handler4+0xaf [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 486]:
: : : : : : : : ; 校驗scopetable 完整性(1)
: : : : : : : : f872043f mov ecx,dword ptr [esi+4]
: : : : : : : : f8720442 add ecx,edi
: : : : : : : : f8720444 xor ecx,dword ptr [eax+edi]
: : : : : : : : f8720447 call PassThrough!__security_check_cookie (f8720638)
: : : : : : : :
: : : : : : : : PassThrough!_except_handler4+0xbc [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 486]:
: : : : : : : : ; 校驗scopetable 完整性(2)
: : : : : > : : f872044c mov ecx,dword ptr [esi+0Ch]
: : : : : : : f872044f mov edx,dword ptr [esi+8]
: : : : : : : f8720452 add ecx,edi
: : : : : : : f8720454 xor ecx,dword ptr [edx+edi] ; 正常情況下[edx+edi] 保存的是__security_cookie 的值
: : : : : : : f8720457 call PassThrough!__security_check_cookie (f8720638)
: : : : : : :
: : : : : : : PassThrough!_except_handler4+0xcc [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 495]:
: > > : : : > : f872045c mov eax,dword ptr [ebp-8]
: : : : : : f872045f pop edi
: : : : : : f8720460 pop esi
: : : : : : f8720461 pop ebx
: : : : : : f8720462 mov esp,ebp
: : : : : : f8720464 pop ebp
: : : : : : f8720465 ret
: : : : : :
: : : : : : PassThrough!_except_handler4+0xd6 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 367]:
: : : : : : ; lpfnFilter 返回EXCEPTION_CONTINUE_EXECUTION
: > : : : : f8720466 mov dword ptr [ebp-8],0 ; [ebp-8] = ExceptionContinueExecution (0)
: : < : : f872046d jmp PassThrough!_except_handler4+0xa8 (f8720438)
: : : :
: : : : PassThrough!_except_handler4+0xdf [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 396]:
: : : : ; lpfnFilter 返回EXCEPTION_EXECUTE_HANDLER
: > : : f872046f mov ecx,dword ptr [ebp+0Ch] ; ecx = pExceptionRegistration
: : : f8720472 call PassThrough!_EH4_GlobalUnwind (f87205fa)
: : : f8720477 mov eax,dword ptr [ebp+0Ch] ; eax = pExceptionRegistration
: : : f872047a cmp dword ptr [eax+0Ch],ebx ; cmp pExceptionRegistration->trylevel
: < : : f872047d je PassThrough!_except_handler4+0x101 (f8720491)
: : : :
: : : : PassThrough!_except_handler4+0xef [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 408]:
: : : : f872047f push offset PassThrough!__security_cookie (f87220b0)
: : : : f8720484 push edi
: : : : f8720485 mov edx,ebx
: : : : f8720487 mov ecx,eax ; ecx = pExceptionRegistration
: : : : f8720489 call PassThrough!_EH4_LocalUnwind (f8720614)
: : : : f872048e mov eax,dword ptr [ebp+0Ch]
: : : :
: : : : PassThrough!_except_handler4+0x101 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 417]:
: > : : f8720491 mov ecx,dword ptr [ebp+8] ; ecx = scopetable[i].previousTryLevel
: : : f8720494 mov dword ptr [eax+0Ch],ecx ; pExceptionRegistration->trylevel = scopetable[i].previousTryLevel
: : : f8720497 mov eax,dword ptr [esi]
: : : f8720499 cmp eax,0FFFFFFFEh
: < : : f872049c je PassThrough!_except_handler4+0x11b (f87204ab)
: : : :
: : : : PassThrough!_except_handler4+0x10e [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 431]:
: : : : ; 校驗scopetable 完整性(1)
: : : : f872049e mov ecx,dword ptr [esi+4]
: : : : f87204a1 add ecx,edi
: : : : f87204a3 xor ecx,dword ptr [eax+edi]
: : : : f87204a6 call PassThrough!__security_check_cookie (f8720638)
: : : :
: : : : PassThrough!_except_handler4+0x11b [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 431]:
: : : : ; 校驗scopetable 完整性(2)
: > : : f87204ab mov ecx,dword ptr [esi+0Ch]
: : : f87204ae mov edx,dword ptr [esi+8]
: : : f87204b1 add ecx,edi
: : : f87204b3 xor ecx,dword ptr [edx+edi]
: : : f87204b6 call PassThrough!__security_check_cookie (f8720638)
: : : ; 調用 lpfnHandler
: : : f87204bb mov eax,dword ptr [ebp-0Ch] ; eax = l_pCurrentScopeTableEntry
: : : f87204be mov ecx,dword ptr [eax+8] ; ecx = l_pCurrentScopeTableEntry->lpfnHandler
: : : f87204c1 mov edx,edi ; edx = pExceptionRegistration->_ebp
: : : f87204c3 call PassThrough!_EH4_TransferToHandler (f87205e8) ; 這裡不會返回! !
: : :
: : : PassThrough!_except_handler4+0x138 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 456]:
> : : f87204c8 mov edx,0FFFFFFFEh
              : : f87204cd cmp dword ptr [ebx+0Ch],edx ; cmp pExceptionRegistration->trylevel, TRYLEVEL_INVALID
              < : f87204d0 je PassThrough!_except_handler4+0xcc (f872045c)
                :
                : PassThrough!_except_handler4+0x142 [d:\5359\minkernel\crts\crtw32\misc\i386\chandler4.c @ 467]:
                : ; pExceptionRegistration->trylevel 不等於TRYLEVEL_INVALID,開始局部展開
                : f87204d2 push offset PassThrough!__security_cookie (f87220b0)
                : f87204d7 push edi ; pExceptionRegistration->_ebp
                : f87204d8 mov ecx,ebx ; ecx = pExceptionRegistration
                : f87204da call PassThrough!_EH4_LocalUnwind (f8720614)
                < f87204df jmp PassThrough!_except_handler4+0xa8 (f8720438)


  這個函數代碼不長。主要分為兩個大分支,一個分支處理異常,一個分支處理展開(參考地址f87203dc 處的test 指令)。
  處理異常的代碼負責遍歷scopetable,依次調用scopetable_entry::lpfnFilter(參考f8720419 處代碼),並針對不同的返回值做出不同的處理:
  1. 返回EXCEPTION_CONTINUE_EXECUTION,則說明異常已經被剛剛調用的lpfnFilter 修復。返回 ExceptionContinueExecution。
  2. 返回EXCEPTION_CONTINUE_SEARCH,則繼續遍歷下一個scopetable_entry。
  3. 返回EXCEPTION_EXECUTE_HANDLER,則說明當前scopetable_entry::lpfnHandler 負責處理該異常。於是調用它。
  對於展開的代碼,則直接開始局部展開,即對scopetable 進行展開。
  更具體的信息,請參考上面反彙編代碼中我附的註釋。

  有幾個小點需要說一下:
  1. 該函數多處使用scopetable 中坑內的數據進行安全檢查。這些操作對理解該函數流程沒有幫助,可以忽略。如果覺得上面代碼不純淨,可以參考我後續的附錄1《Ntfs!_except_handler3 的反彙編代碼》,這個函數流程更清晰簡單。
  2. 一旦有某scopetable_entry::lpfnFilter 返回EXCEPTION_EXECUTE_HANDLER,就會進行全局展開和局部展開。展開結束後會調用該scopetable_entry::lpfnHandler,該函數即為_except 處理域,該函數形式是一個函數,實際上只是一段不返回的代碼。即這段代碼中沒有 ret 指令。執行完整個_except 處理域後,會接著執行其後的指令,並不會返回_except_handler4。
  3. 該函數既啟動展開,又負責展開,於是會出現類似於“重入”的現象。理解的過程中容易擾亂思路。
 
  PassThrough!_except_handler4 在執行過程中可能會調用這幾個函數:
  PassThrough!_EH4_CallFilterFunc、
  PassThrough!_EH4_TransferToHandler、
  PassThrough!_EH4_GlobalUnwind、
  PassThrough!_EH4_LocalUnwind
  這幾個函數名很明白的說明了它們的功能:
  PassThrough!_EH4_CallFilterFunc 負責調用scopetable_entry::lpfnFilter;
  PassThrough!_EH4_TransferToHandler 負責調用scopetable_entry::lpfnFilter;
  PassThrough!_EH4_GlobalUnwind 負責全局展開;
  PassThrough!_EH4_LocalUnwind 負責局部展開。

  來看看_EH4_CallFilterFunc 和_EH4_TransferToHandler 的反彙編代碼,都很短。剩餘兩個展開相關的函數稍後咱們再來分析。


代碼:
    kd> uf PassThrough!_EH4_CallFilterFunc
        PassThrough!_EH4_CallFilterFunc [d:\5359\minkernel\crts\crtw32\misc\i386\exsup4.asm @ 408]:
          f87205d1 push ebp
          f87205d2 push esi
          f87205d3 push edi
          f87205d4 push ebx
          f87205d5 mov ebp,edx
          f87205d7 xor eax,eax
          f87205d9 xor ebx,ebx
          f87205db xor edx,edx
          f87205dd xor esi,esi
          f87205df xor edi,edi
          f87205e1 call ecx ; lpfnFilter();
          f87205e3 pop ebx
          f87205e4 pop edi
          f87205e5 pop esi
          f87205e6 pop ebp
          f87205e7 ret

    kd> uf PassThrough!_EH4_TransferToHandler
        PassThrough!_EH4_TransferToHandler [d:\5359\minkernel\crts\crtw32\misc\i386\exsup4.asm @ 450]:
          f87205e8 mov ebp,edx
          f87205ea mov esi,ecx ; esi = lpfnHandler
          f87205ec mov eax,ecx
          f87205ee xor eax,eax
          f87205f0 xor ebx,ebx
          f87205f2 xor ecx,ecx
          f87205f4 xor edx,edx
          f87205f6 xor edi,edi
          f87205f8 jmp esi ; jmp lpfnHandler



  到這裡,咱們就以PassThrough!_except_handler4 為例分析完了MSC 提供的EXCEPTION_REGISTRATION::handler 函數。這個過程中多次接觸到一個名為“展開”的概念,這就是咱們要講的第三個部分。

没有评论:

发表评论