SetUnhandledExceptionFilter (发生异常时系统的处理顺序)

来源:互联网 发布:淘宝评价后怎么看五星 编辑:程序博客网 时间:2024/05/01 00:09
现在有好多壳用 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
本文出自 51CTO.COM技术博客
原创粉丝点击