SetUnhandledExceptionFilter 的討論

看雪資料發表於2015-11-15

現在有好多殼用 SetUnhandledExceptionFilter 安裝了最後異常處理例程來愚弄 Ollydbg, 
一開始確實難倒了我等菜鳥, 幸好後來有位俄羅斯高人寫了個外掛, 解決了這個問題, 但一直想知道原因. 
最近抽空把 Hume 大俠的 SEH 文章反反覆覆看了好幾遍, 又看了外掛的 README, 總算有點明白了. 
把他寫出來, 請各位大俠看看, 多多指點.

例項下載:點選此處下載


(一) 發生異常時系統的處理順序(by Jeremy Gordon, Hume): 

    1.系統首先判斷異常是否應傳送給目標程式的異常處理例程,如果決定應該傳送,並且目標程式正在被除錯,則系統 
    掛起程式並向偵錯程式傳送EXCEPTION_DEBUG_EVENT訊息.

    2.如果你的程式沒有被除錯或者偵錯程式未能處理異常,系統就會繼續查詢你是否安裝了執行緒相關的異常處理例程,如果 
    你安裝了執行緒相關的異常處理例程,系統就把異常傳送給你的程式seh處理例程,交由其處理. 

    3.每個執行緒相關的異常處理例程可以處理或者不處理這個異常,如果他不處理並且安裝了多個執行緒相關的異常處理例程, 
        可交由鏈起來的其他例程處理. 

    4.如果這些例程均選擇不處理異常,如果程式處於被除錯狀態,作業系統仍會再次掛起程式通知debugger. 

    5.如果程式未處於被除錯狀態或者debugger沒有能夠處理,並且你呼叫SetUnhandledExceptionFilter安裝了最後異 
    常處理例程的話,系統轉向對它的呼叫. 

    6.如果你沒有安裝最後異常處理例程或者他沒有處理這個異常,系統會呼叫預設的系統處理程式,通常顯示一個對話方塊, 
    你可以選擇關閉或者最後將其附加到偵錯程式上的除錯按鈕.如果沒有偵錯程式能被附加於其上或者偵錯程式也處理不了,系統 
    就呼叫ExitProcess終結程式. 

    7.不過在終結之前,系統仍然對發生異常的執行緒異常處理控制程式碼來一次展開,這是執行緒異常處理例程最後清理的機會. 


外掛的 README:

THEORY
When  exception happens, Win2K/XP gives control to NTDLL!KiUserExceptionFilter,
which  in turn calls KERNEL32!UnhandledExceptionFilter. This function checks if
faulty  software  is  being  debugged.  If  not, UnhandledExceptionFilter calls
softwares' exception handler.

SOLUTION:
Patch  KERNEL32!UnhandledExceptionFilter  so that softwares' exception handler
is  always  called.  Used  signatures  work  for both Win2K SP4/WinXP SP1, and
hopefully for all other versions.




(二) 參考 Hume 的例子寫的第一個 MASM 程式, 沒有最終異常處理例程


;//============================  SEH1.EXE  ============================ 

.386 
.model flat, stdcall 
option casemap :none  ; case sensitive 

include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\user32.inc

includelib c:\masm32\lib\kernel32.lib 
includelib c:\masm32\lib\user32.lib 

;//============================ 

.data 

szCap1     db "Thread SEH 1",0 
szMsg1     db "We are in thread SEH handler 1.  ",0 

szCap2     db "Thread SEH 2",0 
szMsg2     db "We are in thread SEH handler 2.  ",0 

szCap     db "SEH example",0
szMsg     db "It would never get here.",0 

.code 
start: 

;//========prog begin==================== 

    ASSUME FS:NOTHING 
    push    offset pThread_Handler1 
    push    fs:[0]      
    mov     fs:[0],esp                            ;//建立SEH的基本ERR結構
    
    push    offset pThread_Handler2 
    push    fs:[0]      
    mov     fs:[0],esp                            ;//建立SEH的基本ERR結構                                         
                                                   
    xor    ecx,ecx 
    mov    eax,200     
    cdq 
    div    ecx 
                                                  ;//以下永遠不會被執行 
    invoke    MessageBox,NULL,addr szMsg,addr szCap,MB_OK+MB_ICONEXCLAMATION 
    invoke    ExitProcess,NULL 
        

;//============================ 
pThread_Handler1: 
    invoke    MessageBox,NULL,addr szMsg1,addr szCap1,MB_OK+MB_ICONINFORMATION 
    
    mov    eax,1                                 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理 
    ;mov    eax,0                                ;//ExceptionContinueExecution,表示已經修復,可從異常發生處繼續執行 
    ret                                          ;//這裡如果返回0,你會陷入死迴圈,不斷跳出對話方塊.... 

pThread_Handler2: 
    invoke    MessageBox,NULL,addr szMsg2,addr szCap2,MB_OK+MB_ICONINFORMATION 
    
    mov    eax,1                                 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理 
    ;mov    eax,0                                ;//ExceptionContinueExecution,表示已經修復,可從異常發生處繼續執行 
    ret                                          ;//這裡如果返回0,你會陷入死迴圈,不斷跳出對話方塊.... 
    
;//=============================Prog Ends============== 
end start 
 




SEH1.EXE 有兩個執行緒異常處理例程, 但都不處理除零異常.

執行 SEH1.EXE , 你將會看到 5 次對話方塊, 其中第三次是系統預設對話方塊, 最後兩次是執行緒異常展開.






(三) 參考 Hume 的例子寫的第二個 MASM 程式, 有最終異常處理例程


;//===============================   SEH2.EXE  ========================= 

.386 
.model flat, stdcall 
option casemap :none  ; case sensitive 

include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\user32.inc

includelib c:\masm32\lib\kernel32.lib 
includelib c:\masm32\lib\user32.lib 

;//============================ 

.data 

szCap1     db "Thread SEH 1",0 
szMsg1     db "We are in thread SEH handler 1.  ",0 

szCap2     db "Thread SEH 2",0 
szMsg2     db "We are in thread SEH handler 2.  ",0 

szCap0     db "Final SEH",0 
szMsg0     db "We are in final SEH handler.  ",0 

szCap     db "SEH example",0
szMsg     db "It would never get here.",0 

.code 
start: 

;//========prog begin==================== 

    ASSUME FS:NOTHING 
    push    offset pThread_Handler1 
    push    fs:[0]      
    mov     fs:[0],esp                            ;//建立SEH的基本ERR結構
    
    ;lea    eax,Final_Handler 
    ;invoke SetUnhandledExceptionFilter,eax        ;//呼叫SetUnhandledExceptionFilter來安裝final SEH 
                                                  ;//原型很簡單SetUnhandledExceptionFilter proto 
                                                  ;//pTopLevelExceptionFilter:DWORD
    push    offset pThread_Handler2 
    push    fs:[0]      
    mov     fs:[0],esp                            ;//建立SEH的基本ERR結構, 只是說明兩種異常先後載入的順序對系統沒影響                                          
                                                   
    xor    ecx,ecx 
    mov    eax,200     
    cdq 
    div    ecx 
                                                  ;//以下永遠不會被執行 
    invoke    MessageBox,NULL,addr szMsg,addr szCap,MB_OK+MB_ICONEXCLAMATION 
    invoke    ExitProcess,NULL 
        

;//============================ 
pThread_Handler1: 
    invoke    MessageBox,NULL,addr szMsg1,addr szCap1,MB_OK+MB_ICONINFORMATION 
    
    mov    eax,1                                 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理 
    ;mov    eax,0                                ;//ExceptionContinueExecution,表示已經修復,可從異常發生處繼續執行 
    ret                                          ;//這裡如果返回0,你會陷入死迴圈,不斷跳出對話方塊.... 

pThread_Handler2: 
    invoke    MessageBox,NULL,addr szMsg2,addr szCap2,MB_OK+MB_ICONINFORMATION 
    
    mov    eax,1                                 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理 
    ;mov    eax,0                                ;//ExceptionContinueExecution,表示已經修復,可從異常發生處繼續執行 
    ret                                          ;//這裡如果返回0,你會陷入死迴圈,不斷跳出對話方塊.... 
    
Final_Handler: 
    invoke    MessageBox,NULL,addr szMsg0,addr szCap0,MB_OK+MB_ICONEXCLAMATION
     
    mov    eax,EXCEPTION_EXECUTE_HANDLER        ;//==1 這時不出現非法操作的討厭對話方塊, 已經處理了, 可以結束了. 
    ;mov    eax,EXCEPTION_CONTINUE_SEARCH       ;//==0 出現,這時是呼叫系統預設的異常處理過程,程式被終結了 
    ;mov    eax,EXCEPTION_CONTINUE_EXECUTION    ;//==-1 表示已經修復,可從異常發生處繼續執行, 因為我們並沒有修復ecx
    ret                                         ;//     所以不斷產生異常,不斷呼叫這個例程, 出現對話方塊,陷入死迴圈,

;//=============================Prog Ends============== 
end start 


SEH2.EXE 有兩個執行緒異常處理例程, 一個最終異常處理例程, 都不處理除零異常.

執行 SEH2.EXE , 還是會看到 5 次對話方塊, 但是其中第三次換成我們自己的了, 最後兩次還是執行緒異常展開.
還要注意最終異常處理例程不會展開. 





(四) 下面我們開始用 OD 除錯 SEH2.EXE, 注意先不要安裝外掛, 不要忽略異常.

載入後, OD 停在 004010C0.

004010C0 >/$  68 16114000   PUSH SEH2.00401116                       ;  SE handler installation
004010C5  |.  64:FF35 00000>PUSH DWORD PTR FS:[0]
004010CC  |.  64:8925 00000>MOV DWORD PTR FS:[0],ESP

004010D3  |.  8D05 48114000 LEA EAX,DWORD PTR DS:[401148]
004010D9  |.  50            PUSH EAX                                 ; /pTopLevelFilter => SEH2.00401148
004010DA  |.  E8 89000000   CALL <JMP.&kernel32.SetUnhandledExceptio>; \SetUnhandledExceptionFilter

004010DF  |.  68 2F114000   PUSH SEH2.0040112F                       ;  SE handler installation
004010E4  |.  64:FF35 00000>PUSH DWORD PTR FS:[0]
004010EB  |.  64:8925 00000>MOV DWORD PTR FS:[0],ESP

004010F2  |.  33C9          XOR ECX,ECX
004010F4  |.  B8 C8000000   MOV EAX,0C8
004010F9  |.  99            CDQ
004010FA  |.  F7F1          DIV ECX                                  ; // 除零異常 

004010FC  |.  6A 30         PUSH 30                                  ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004010FE  |.  68 9B104000   PUSH SEH2.0040109B                       ; |Title = "SEH example"
00401103  |.  68 A7104000   PUSH SEH2.004010A7                       ; |Text = "It would never get here."
00401108  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
0040110A  |.  E8 5F000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA

0040110F  |.  6A 00         PUSH 0                                   ; /ExitCode = 0
00401111  \.  E8 4C000000   CALL <JMP.&kernel32.ExitProcess>         ; \ExitProcess

00401116  /$  6A 40         PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL; Structured exception handler
00401118  |.  68 14104000   PUSH SEH2.00401014                       ; |Title = "Thread SEH 1"
0040111D  |.  68 21104000   PUSH SEH2.00401021                       ; |Text = "We are in thread SEH handler 1.  "
00401122  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
00401124  |.  E8 45000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
00401129  |.  B8 01000000   MOV EAX,1
0040112E  \.  C3            RETN

0040112F  /$  6A 40         PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL; Structured exception handler
00401131  |.  68 43104000   PUSH SEH2.00401043                       ; |Title = "Thread SEH 2"
00401136  |.  68 50104000   PUSH SEH2.00401050                       ; |Text = "We are in thread SEH handler 2.  "
0040113B  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
0040113D  |.  E8 2C000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
00401142  |.  B8 01000000   MOV EAX,1
00401147  \.  C3            RETN

00401148   .  6A 30         PUSH 30                                  ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
0040114A   .  68 72104000   PUSH SEH2.00401072                       ; |Title = "Final SEH"
0040114F   .  68 7C104000   PUSH SEH2.0040107C                       ; |Text = "We are in final SEH handler.  "
00401154   .  6A 00         PUSH 0                                   ; |hOwner = NULL
00401156   .  E8 13000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
0040115B   .  B8 01000000   MOV EAX,1
00401160   .  C3            RETN


不要執行, 先看看 OD 的 SEH chain 視窗,  如下, 系統已經為我們設好了一個異常處理例程.

SEH chain of main thread
Address    SE handler
0012FFE0   kernel32.7C5C1F44                                       ; 記住這裡, 稱其為系統執行緒異常處理例程
                                                                   ; 關鍵的地方.



F9 執行,  OD 停在 004010FA, 除零異常. 再看一下 SEH chain 視窗.

SEH chain of main thread
Address    SE handler
0012FFB4   SEH2.0040112F
0012FFBC   SEH2.00401116
0012FFE0   kernel32.7C5C1F44


我們按照 OD 的提示 SHIFT+F7 走, 注意不是 SHIFT+F9

77F9FF60 >  8B4C24 04       MOV ECX,DWORD PTR SS:[ESP+4]           ; ntdll.KiUserExceptionDispatcher
77F9FF64    8B1C24          MOV EBX,DWORD PTR SS:[ESP]
77F9FF67    51              PUSH ECX
77F9FF68    53              PUSH EBX
77F9FF69    E8 6F6CFFFF     CALL ntdll.77F96BDD                    ; F7


我們發現系統把控制權交給了 ntdll.KiUserExceptionDispatcher, 
而不是 README 裡說的 KiUserExceptionFilter, 這也許是說法上的差異吧. 


我們一路 F7 走


77F96BDD    55              PUSH EBP
77F96BDE    8BEC            MOV EBP,ESP
77F96BE0    83EC 60         SUB ESP,60
77F96BE3    53              PUSH EBX
77F96BE4    56              PUSH ESI
77F96BE5    8D45 F4         LEA EAX,DWORD PTR SS:[EBP-C]
77F96BE8    57              PUSH EDI
77F96BE9    50              PUSH EAX
77F96BEA    8D45 F8         LEA EAX,DWORD PTR SS:[EBP-8]
77F96BED    50              PUSH EAX
77F96BEE    E8 C8FFFFFF     CALL ntdll.77F96BBB                    ; F8
77F96BF3    E8 DEFFFFFF     CALL ntdll.77F96BD6                    ; F8
77F96BF8    8365 FC 00      AND DWORD PTR SS:[EBP-4],0
77F96BFC    8BD8            MOV EBX,EAX
77F96BFE    83FB FF         CMP EBX,-1
77F96C01    74 71           JE SHORT ntdll.77F96C74
77F96C03    8B75 08         MOV ESI,DWORD PTR SS:[EBP+8]

77F96C06    3B5D F8         CMP EBX,DWORD PTR SS:[EBP-8]           ; // 迴圈處理執行緒異常鏈
77F96C09    8D43 08         LEA EAX,DWORD PTR DS:[EBX+8]
77F96C0C    0F82 40AF0100   JB ntdll.77FB1B52
77F96C12    3B45 F4         CMP EAX,DWORD PTR SS:[EBP-C]
77F96C15    0F87 37AF0100   JA ntdll.77FB1B52
77F96C1B    F6C3 03         TEST BL,3
77F96C1E    0F85 2EAF0100   JNZ ntdll.77FB1B52
77F96C24    F605 5604FD77 8>TEST BYTE PTR DS:[77FD0456],80
77F96C2B    0F85 AAAE0100   JNZ ntdll.77FB1ADB
77F96C31    FF73 04         PUSH DWORD PTR DS:[EBX+4]
77F96C34    8D45 F0         LEA EAX,DWORD PTR SS:[EBP-10]
77F96C37    50              PUSH EAX
77F96C38    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
77F96C3B    53              PUSH EBX
77F96C3C    56              PUSH ESI
77F96C3D    E8 3AFFFFFF     CALL ntdll.77F96B7C                   ; F7, 見下面
77F96C42    F605 5604FD77 8>TEST BYTE PTR DS:[77FD0456],80
77F96C49    8BF8            MOV EDI,EAX
77F96C4B    0F85 A0AE0100   JNZ ntdll.77FB1AF1
77F96C51    395D FC         CMP DWORD PTR SS:[EBP-4],EBX
77F96C54    0F84 A5AE0100   JE ntdll.77FB1AFF
77F96C5A    8BC7            MOV EAX,EDI
77F96C5C    33C9            XOR ECX,ECX
77F96C5E    2BC1            SUB EAX,ECX
77F96C60    0F84 8A030000   JE ntdll.77F96FF0
77F96C66    48              DEC EAX
77F96C67    0F85 9FAE0100   JNZ ntdll.77FB1B0C
77F96C6D    8B1B            MOV EBX,DWORD PTR DS:[EBX]
77F96C6F    83FB FF         CMP EBX,-1                          ; // 0FFFFFFFFh
77F96C72  ^ 75 92           JNZ SHORT ntdll.77F96C06            ; // 是不是最後一個執行緒異常處理例程





77F96B7C    BA AE6DF977     MOV EDX,ntdll.77F96DAE
77F96B81    55              PUSH EBP
77F96B82    8BEC            MOV EBP,ESP
77F96B84    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
77F96B87    52              PUSH EDX
77F96B88    64:FF35 0000000>PUSH DWORD PTR FS:[0]
77F96B8F    64:8925 0000000>MOV DWORD PTR FS:[0],ESP
77F96B96    FF75 14         PUSH DWORD PTR SS:[EBP+14]            ; pDispatch
77F96B99    FF75 10         PUSH DWORD PTR SS:[EBP+10]            ; pContext
77F96B9C    FF75 0C         PUSH DWORD PTR SS:[EBP+C]             ; pException_Registration_Record
77F96B9F    FF75 08         PUSH DWORD PTR SS:[EBP+8]             ; pException_Record
77F96BA2    8B4D 18         MOV ECX,DWORD PTR SS:[EBP+18]         
77F96BA5    FFD1            CALL ECX                              ; 執行緒異常處理例程, F7 跟進, 
77F96BA7    64:8B25 0000000>MOV ESP,DWORD PTR FS:[0]
77F96BAE    64:8F05 0000000>POP DWORD PTR FS:[0]
77F96BB5    8BE5            MOV ESP,EBP
77F96BB7    5D              POP EBP
77F96BB8    C2 1400         RETN 14                               ; ret to 77F96C42



我們將在 77F96BA5 進入執行緒處理例程, 一共三次.

前兩次是我們自己的程式碼, 都不處理除零異常, 返回 EAX=1.
最後一次, 就是前面記下的 系統預設的執行緒處理例程 7C5C1F44, F7 進去



7C5C1F44    55              PUSH EBP
7C5C1F45    8BEC            MOV EBP,ESP
7C5C1F47    83EC 08         SUB ESP,8
7C5C1F4A    53              PUSH EBX
7C5C1F4B    56              PUSH ESI
7C5C1F4C    57              PUSH EDI
7C5C1F4D    55              PUSH EBP
7C5C1F4E    FC              CLD

7C5C1F4F    8B5D 0C         MOV EBX,DWORD PTR SS:[EBP+C]        ; pException_Registration_Record
7C5C1F52    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]        ; pException_Record
7C5C1F55    F740 04 0600000>TEST DWORD PTR DS:[EAX+4],6         ; 異常標誌, =6 表示正展開中.
7C5C1F5C    75 77           JNZ SHORT kernel32.7C5C1FD5

7C5C1F5E    8945 F8         MOV DWORD PTR SS:[EBP-8],EAX
7C5C1F61    8B45 10         MOV EAX,DWORD PTR SS:[EBP+10]       ; pContext
7C5C1F64    8945 FC         MOV DWORD PTR SS:[EBP-4],EAX

7C5C1F67    8D45 F8         LEA EAX,DWORD PTR SS:[EBP-8]
7C5C1F6A    8943 FC         MOV DWORD PTR DS:[EBX-4],EAX
7C5C1F6D    8B73 0C         MOV ESI,DWORD PTR DS:[EBX+C]
7C5C1F70    8B7B 08         MOV EDI,DWORD PTR DS:[EBX+8]
7C5C1F73    83FE FF         CMP ESI,-1
7C5C1F76    74 56           JE SHORT kernel32.7C5C1FCE
7C5C1F78    8D0C76          LEA ECX,DWORD PTR DS:[ESI+ESI*2]
7C5C1F7B    837C8F 04 00    CMP DWORD PTR DS:[EDI+ECX*4+4],0
7C5C1F80    74 3A           JE SHORT kernel32.7C5C1FBC
7C5C1F82    56              PUSH ESI
7C5C1F83    55              PUSH EBP
7C5C1F84    8D6B 10         LEA EBP,DWORD PTR DS:[EBX+10]
7C5C1F87    FF548F 04       CALL DWORD PTR DS:[EDI+ECX*4+4]    ; F7 進入 7C598940




7C598940    8B45 EC         MOV EAX,DWORD PTR SS:[EBP-14]
7C598943    8B08            MOV ECX,DWORD PTR DS:[EAX]
7C598945    8B09            MOV ECX,DWORD PTR DS:[ECX]
7C598947    894D E4         MOV DWORD PTR SS:[EBP-1C],ECX
7C59894A    50              PUSH EAX
7C59894B    E8 8C330000     CALL kernel32.UnhandledExceptionFilter     ; 我們來到了 README 所說的第二個函式, F7 進去看看.
7C598950    C3              RETN




7C59BCDC >  55              PUSH EBP                                   ; kernel32.UnhandledExceptionFilter
7C59BCDD    8BEC            MOV EBP,ESP
7C59BCDF    6A FF           PUSH -1
7C59BCE1    68 C02E577C     PUSH kernel32.7C572EC0
7C59BCE6    68 441F5C7C     PUSH kernel32.7C5C1F44
7C59BCEB    64:A1 00000000  MOV EAX,DWORD PTR FS:[0]
7C59BCF1    50              PUSH EAX
7C59BCF2    64:8925 0000000>MOV DWORD PTR FS:[0],ESP
7C59BCF9    51              PUSH ECX
7C59BCFA    51              PUSH ECX
7C59BCFB    81EC D8020000   SUB ESP,2D8
7C59BD01    53              PUSH EBX
7C59BD02    56              PUSH ESI
7C59BD03    57              PUSH EDI
7C59BD04    8965 E8         MOV DWORD PTR SS:[EBP-18],ESP
7C59BD07    8B75 08         MOV ESI,DWORD PTR SS:[EBP+8]
7C59BD0A    8B06            MOV EAX,DWORD PTR DS:[ESI]
7C59BD0C    8138 050000C0   CMP DWORD PTR DS:[EAX],C0000005
7C59BD12    75 1B           JNZ SHORT kernel32.7C59BD2F
7C59BD14    33DB            XOR EBX,EBX
7C59BD16    3958 14         CMP DWORD PTR DS:[EAX+14],EBX
7C59BD19    74 16           JE SHORT kernel32.7C59BD31
7C59BD1B    FF70 18         PUSH DWORD PTR DS:[EAX+18]
7C59BD1E    E8 E8FEFFFF     CALL kernel32.7C59BC0B
7C59BD23    83F8 FF         CMP EAX,-1
7C59BD26    75 09           JNZ SHORT kernel32.7C59BD31
7C59BD28    0BC0            OR EAX,EAX
7C59BD2A    E9 8E020000     JMP kernel32.7C59BFBD
7C59BD2F    33DB            XOR EBX,EBX                                        ; EBX = 0 ;
7C59BD31    895D C8         MOV DWORD PTR SS:[EBP-38],EBX                      ; ProecssInfo = 0; 
7C59BD34    53              PUSH EBX                                           ; NULL
7C59BD35    6A 04           PUSH 4                                             ; sizeof(ProcessInfo)
7C59BD37    8D45 C8         LEA EAX,DWORD PTR SS:[EBP-38]
7C59BD3A    50              PUSH EAX                                           ; &ProcessInfo
7C59BD3B    6A 07           PUSH 7                                             ; ProcessDebugPort
7C59BD3D    E8 7BBBFFFF     CALL kernel32.GetCurrentProcess                    ; 返回 hProcess                   
7C59BD42    50              PUSH EAX                                           
7C59BD43    FF15 B810577C   CALL DWORD PTR DS:[<&ntdll.NtQueryInformationProce>; ntdll.ZwQueryInformationProcess
7C59BD49    3BC3            CMP EAX,EBX                                        ; EAX = 0 表示成功, EAX = -1 表示不成功
7C59BD4B    7C 09           JL SHORT kernel32.7C59BD56
7C59BD4D    395D C8         CMP DWORD PTR SS:[EBP-38],EBX                      ; [EBP-38]= -1 有偵錯程式, 0 沒有
7C59BD50    0F85 49020000   JNZ kernel32.7C59BF9F                              ; 所以我們只要把 [EBP-38] 清零
                                                                               ; 就可以瞞過系統, 使其以為目前未在除錯狀態
                                                                               ; 這就是外掛的作用吧.


上面 ZwQueryInformationProcess 某個程式是否正被ring3偵錯程式所除錯。
Blowfish 班主寫過一篇文章, 可以參考.
 
enum PROCESS_INFO_CLASS      { ProcessDebugPort = 7  }; 

typedef struct _PROCESS_DEBUG_PORT_INFO 

    HANDLE DebugPort; 
}    PROCESS_DEBUG_PORT_INFO;


PROCESS_DEBUG_PORT_INFO ProcessInfo; 

ZwQueryInformationProcess(GetCurrentProcess( ), ProcessDebugPort, &ProcessInfo, sizeof(ProcessInfo), NULL) 



我們把 [EBP-38]清零, 到 401148(Final_Handler) 下斷, F9 , 看看發生了什麼, 我們在 OD 裡來到了 Final_Handler.

沒有外掛, 我們也可以對付最終異常處理例程了.


 
(五) 我的結論

一. 程式沒有被除錯

    0.執行緒建立時, 系統安裝一個 系統執行緒異常處理例程

    1.異常發生時, Win2K/XP 控制權轉移給 NTDLL.KiUserExceptionDispatcher 

    2.如果你安裝了執行緒相關的異常處理例程, 系統就把異常傳送給你的處理例程,交由其處理. 

    3.每個執行緒相關的異常處理例程可以處理或者不處理這個異常,如果他不處理並且安裝了多個執行緒相關的異常處理例程, 
      可交由鏈起來的其他例程處理. 

    4.如果這些例程均選擇不處理異常,系統執行緒異常處理例程將起作用, 呼叫 ZwQueryInformationProcess 判斷是否被除錯, 
      沒有除錯並且你呼叫SetUnhandledExceptionFilter安裝了最後異常處理例程的話,系統轉向對它的呼叫. 

    5.如果你沒有安裝最後異常處理例程或者他沒有處理這個異常,系統會呼叫預設的系統處理程式,通常顯示一個對話方塊, 
      你可以選擇關閉或者最後將其附加到偵錯程式上的除錯按鈕. 
      如果沒有偵錯程式能被附加於其上或者偵錯程式也處理不了,系統就呼叫ExitProcess終結程式. 

    6.不過在終結之前,系統仍然對發生異常的執行緒異常處理控制程式碼來一次展開,這是執行緒異常處理例程最後清理的機會.
      一般只用來釋放資源, 不要試圖修復什麼. 注意只對執行緒異常有展開.



二. 程式被除錯

    0.執行緒建立時, 系統安裝一個系統執行緒異常處理例程

    1.異常發生時, 系統掛起程式並向偵錯程式傳送 EXCEPTION_DEBUG_EVENT 訊息.
       
    2.偵錯程式未能處理異常(比如我們在 OD 裡按Shift+F7), 控制權轉移給 NTDLL.KiUserExceptionDispatcher 
      如果你安裝了執行緒相關的異常處理例程,系統就把異常傳送給你的程式seh處理例程,交由其處理. 
     
    3.每個執行緒相關的異常處理例程可以處理或者不處理這個異常,如果他不處理並且安裝了多個執行緒相關的異常處理例程, 
      可交由鏈起來的其他例程處理. 

    4.如果這些例程均選擇不處理異常,系統執行緒異常處理例程將起作用, 呼叫 ZwQueryInformationProcess 判斷是否被除錯, 
     
    5.由於被除錯, 作業系統仍會再次掛起程式通知debugger. 
  
   
其實這裡, 我也有很多似懂非懂的地方, 各位大俠請多指點.    

相關文章