Windows XP下的向量異常處理

Windows XP下的向量異常處理  

作者:Hume/冷雨飄心·發佈日期:2002-8-9·


原作名稱: Vectored Exception Handling
原作: Matt Pietrek
翻譯改寫: hume/冷雨飄心

   首先回顧一下(SEH)結構化異常處理,結構化異常處理用EXCEPTION_RETGISTRATION結構鏈起來的異常處理系統,那麼當異常發生時,系統會遍歷這個鏈,首先是鏈最前面的,系統會問:這個異常你處理嗎?如果回答YES(通過返回EXCEPTION_CONTINUE_EXECUTION)的話,那系統就把控制權交給他,然後由其處理這個異常,返回到異常處理程序想返回的任何地方.如果通過返回EXCEPTION_CONTINUE_SEARCH回答:NO,let others do that! 系統就繼續查找這個鏈,不厭其煩地問同樣的問題和採取相同的處理原則.這個鏈最前面的EXCEPTION_RETGISTRATION是由fs:[0]處的一個dword指針指向的.具體細節還請參閱相關資料或我以前的<>.
   讓我們來看一下seh的缺點,就是最後安裝的seh處理例程總是優先得到控制權,這有時並不是最好的解決方案,但確實是seh的工作機制,當然Final型的或稱top型的(還記得嗎,也就是通過SetUnHandledExceptionFilter安裝的)例外,因爲他是不允許嵌套的.我們提到的是線程相關的也就是per_Thread類型的.爲什麼不是好的解決方案呢,讓我們設想一下,假如你用兩週寫了一個異常完美的seh處理例程,能夠完美處理所有異常,並希望異常全部由你來處理,但很不幸,比如你調用了一個外部模塊,而這個模塊自己安裝了一個ugly的seh處理例程,他的動作是只要有異常發生就簡單地終止程序...hmmm...!!!這意味着什麼?你的兩週工作全部付諸東流!又比如你想在你的加殼程序裏面加密目標程序代碼段,然後發生無效指令異常的時候用你自己安裝的處理句柄來解密代碼段繼續執行,聽起來這的確是一個好主意,但遺憾的是大多數C/C++代碼都用_try{}_except{}塊來保證其正確運行,而這些異常處理例程是在你殼註冊的例程之後安裝的,因而也就在鏈的前面,無效指令一執行,首先是C/C++編譯器本身提供的處理例程或者程序其他的異常處理例程來處理,可能簡單結束程序或者....天知道!
   在Xp下,Microsoft又提供了又一種異常處理,那就是VEH(Vectored Exception Handling),我譯作向量異常處理,這個東東用如下api註冊,類似於SEH,也是一個鏈狀結構,讓我們來看看他的不同之處,噫,好像差不多啊:

    WINBASEAPI PVOID WINAPI AddVectoredExceptionHandler(
    ULONG FirstHandler,
    PVECTORED_EXCEPTION_HANDLER VectoredHandler );

    FirstHandler:是一個標誌,可以指定是否將你的VEH處理例程放在VEH鏈的最前面!=0,放在最後,其他放在最前
    VectoredHandler:這個東東是異常處理例程入口
    返回註冊的VEH句柄,後面卸載的時候要用到.

    LONG NTAPI VectoredExceptionHandler(PEXCEPTION_POINTERS);
    PEXCEPTION_POINTERS是指向EXCEPTION_POINTERS的指針,和SEH中FINAL型的EXCEPTION_POINTERS
    的結構是一致的.

    正像你看到的,好像和Final型SEH處理差不多?不一樣!區別如下:
    1)首先是AddVectoredExceptionHandler添加的異常處理句柄可以嵌套,而不是隻能指定一個
    2)其次是AddVectoredExceptionHandler可以指定你的異常處理句柄是否在鏈的最前面,hoho,
    這可是我們期望的!當然如果在你後面有人調用AddVectoredExceptionHandler也作同樣指定,那對不起,你只得在他後面了.

    相同之處在於:
    1)他們都是進程而不是線程相關的.
    2)若所有均不處理異常,最後系統要進行展開,不過不會調用VEH例程

    XP仍然支持SEH,那麼問題來了SEH和VEH是什麼關係,答案很簡單,VEH優先權高於SEH,只有所有VEH全不處理某個異常的時候,異常處理權纔會到達SEH.只要目標程序中沒有利用VEH,你的VEH就是第一個得到控制者.嘿嘿,現在的採用SEH作爲異常處理的普通C/C++程序對你不會再有干擾了!你可以用VEH來hook了,god!
    另外一個問題,如果有debuger怎麼辦?控制權轉向又如何呢?不幸的消息來了,異常發生後首先通知的還是debugger,debugger不處理才返回控制權給VEH,[VEH不處理,返回給SEH,seh不處理,又給debugger一個機會,如果還不處理,才由系統處理

    RemoveVectoredExceptionHandler 用來移除VEH處理句柄.是否需要看你的了,不過有一點必須注意,系統不會自動移除註冊的VEH例程,如果指向的VEH例程所在exe或dll已經卸載,通常會導致嚴重錯誤.
    最後有一點要聲明,在VEH回調處理例程中必須保護好寄存器,否則會引起莫名其妙的異常,這可是我化了2個小時的代價阿.

下面是一個例子:
;============================================================
;asm ex,By Hume
;.............
.586
.model flat, stdcall
option casemap :none   ; case sensitive
include c:/hd/hd.h
include c:/hd/mac.h
;~~~~~~~~~~~~~~~~~~~protos
ASSUME  fs:nothing
;~~~~~~~~~~~~~~~~~~~~~~~~~
;;--------------

	.DATA
sztit   db "By Hume,2K2",0

	.DATA?
rd      hK32                    ;Kernel32模塊地址
rd      hVec                    ;AddVectoredExceptionHandler函數地址
rd      hRemov                  ;RemoveVectoredExceptionHandler函數地址
rd      hvectorhandler1
rd      hvectorhandler2
;;-----------------------------------------
	.CODE
__Start:
        __msg   begin Testing...

        mov     hK32,$invoke(LoadLibrary,CTEXT("KERNEL32.DLL"))
        JEAXZ   _err_1
        mov     hVec,$invoke(GetProcAddress,eax,CTEXT("AddVectoredExceptionHandler"))
        JEAXZ   _err_2
        mov     hRemov,$invoke(GetProcAddress,hK32,CTEXT("RemoveVectoredExceptionHandler"))
        JEAXZ   _err_2

                ;相當於invoke AddVectoredExceptionHandler,0,offset vEcp1
        sWin32	hVec,0,offset vEcp1     ;First VEH
        mov     hvectorhandler1,eax
        sWin32	hVec,0,offset vEcp2     ;Second....
        mov     hvectorhandler2,eax

        lea     eax,[esp-8]
        xchg    eax,fs:[0]
        push    offset sEh1
        push    eax
                                ;以上是安裝VEH回調例程和
                                ;SEh回調例程

        pushfd
        or      dword ptr [esp],100h
        popfd
        nop                     ;Here Exception!->veh1
        nop
     @3:
        mov     esi,CTEXT("Good,Was Solved by VEH No 2")
        JMP     @F

     @1:
        INVOKE	MessageBox,0,CTEXT("Good,Was Solved by VEH No 1"),addr sztit,0
        xor     eax,eax
        mov     eax,[eax]       ;Here!->seh1
        JMP     @1
     @2:
        INVOKE	MessageBox,0,CTEXT("Good,Was Solved by seh No 1"),addr sztit,0
        INT     3               ;VEH2
        JMP     @2


        ;--------------------------------------------
        ; 以下是簡單錯誤處理
        ;--------------------------------------------
_err_1:
        mov     esi,CTEXT("Can't load DLL")
        jmp     @F

_err_2:
        mov     esi,CTEXT("Can't Find function")
        @@:
_xit:
        INVOKE	MessageBox,0,esi,CTEXT("By Hume,2K2"),0

                        ;卸載所有的VEH和SEH處理例程
        pop     fs:[0]
        pop     eax
        sWin32	hRemov,hvectorhandler1
        sWin32	hRemov,hvectorhandler2
	invoke ExitProcess,0

        ;=======================
        Assume  esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT

vEcp1   proc    USES esi edi ebx pExcetionPointers:DWORD
        mov     eax,pExcetionPointers
        mov     esi,[eax]               ;pEXCEPTION_RECORD
        mov     edi,[eax+4]             ;pCONTEXT

        ;call    dP
        xor     eax,eax
        test    [esi].ExceptionFlags,4+2+1    ;Unwind or serious
        JNE     @NO_HANDLE
        cmp     [esi].ExceptionCode,80000004h ;Single step
        JNE     @NO_HANDLE
        m2m     [edi].regEip,offset @1
        dec     eax             ;mov     eax,EXCEPTION_CONTINUE_EXECUTION

@NO_HANDLE:                     ;mov     eax,EXCEPTION_CONTINUE_SEARCH
        ret
vEcp1   Endp

        ;-----------------------------------------
vEcp2   proc    USES esi edi ebx pExcetionPointers:DWORD
        mov     eax,pExcetionPointers
        mov     esi,[eax]               ;pEXCEPTION_RECORD
        mov     edi,[eax+4]

        ;call    dP
        xor     eax,eax
        test    [esi].ExceptionFlags,4+2+1    ;Unwind or serious
        JNE     @NO_HANDLE
        cmp     [esi].ExceptionCode,80000003h ;STATUS_BREAKPOINT
        JNE     @NO_HANDLE

        m2m     [edi].regEip,offset @3
        dec     eax              ;mov     eax,EXCEPTION_CONTINUE_EXECUTION

@NO_HANDLE:                      ;mov     eax,EXCEPTION_CONTINUE_SEARCH
        ret
vEcp2   Endp

        ;-----------------------------------------

sEh1    proc    USES esi edi ebx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
        mov     esi,pExcept
        mov     edi,pContext

        ;call    dP

        xor     eax,eax
        inc     eax
        test    [esi].ExceptionFlags,6+1         ;Unwind & Serious
        jne     @F
        cmp     [esi].ExceptionCode,0C0000005h  ;STATUS_ACCESS_VIOLATION
        jne     @F

        m2m     [edi].regEip,offset @2
        dec     eax
 @@:
        ret
sEh1    endp

       ;Release assume
       Assume  esi:Nothing,edi:Nothing
;-----------------------------------------

        ;FOR    debug purpose,Rubbish,....you can del it!
.data
fmt     db "The Cur EIP IS: %08X   Cur Excpt NUM is: %08X",0dh,0ah
        db "The Ecpt FLAGS VALUE in HEX: %X",0dh,0ah
        db "Common REG DUMP:",0dh,0ah
        db "EAX: %08X       EBX: %08X",0dh,0ah
        db "ECX: %08X       EDX: %08X",0dh,0ah
        db "ESI: %08X       EDI: %08X",0dh,0ah
.code
                ;DUMP   THREAD CONTEXTS Need esi:pt Excpt_Record
                ;edi: pt Context
dP      proc
local   buf[256]:byte
        pushad
        mov     eax,(EXCEPTION_RECORD ptr [esi]).ExceptionFlags
        INVOKE	wsprintf,addr buf,offset fmt,(CONTEXT PTR [edi]).regEip,(dword ptr [esi]),eax,/
                        (CONTEXT PTR [edi]).regEax,(CONTEXT PTR [edi]).regEbx,/
                        (CONTEXT PTR [edi]).regEcx,(CONTEXT PTR [edi]).regEdx,/
                        (CONTEXT PTR [edi]).regEsi,(CONTEXT PTR [edi]).regEdi
        INVOKE	MessageBox,0,addr buf,CTEXT("VEH Detector...debug purpose...Hume"),0
        popad
        ret
dP      endp

END	__Start


;==============================================================
    下面附例子用到的幾個宏:
    CTEXT,相信諸位見過多次了,不多說.
    sWin32:相當於push syntax call label
    rd:    數據定義DWORD
    m2m:   相當於push syn1 pop syn2
    JEAXZ  :eax=0,jmp des
    $incoke():inline coding

    詳細請下載在我主頁上的最新頭文件
    revargs     MACRO args:VARARG
        LOCAL   target
        target     TEXTEQU <>
	IFNB
		FOR     arg,
			IFNB
			target     CATSTR ,,target
			ENDIF
		ENDM
		target     SUBSTR target,1,@SizeStr(%target)-1
	ENDIF
	EXITM   target
        ENDM

        sWin32	Macro	label:REQ,args:VARARG		;Which allow no protos discalaiming
        %	FOR	pxx,		;But you need to guarantee the
                   IFNB 				;the syntax yourself
                     push	pxx
                   ENDIF
                ENDM
                call	label
        ENDM

        m2m MACRO M1, M2                          ;mov is too boring!
              push M2
              pop  M1
        ENDM

        $invoke Macro fun:REQ,args:VARARG
        IFNB
           invoke	fun,&args
        ELSE
	   invoke	fun
        ENDIF
        EXITM
        ENDM

        rd	Macro label:REQ,count
	        IFNB
	                label dd &count dup(?)
	        ELSE
	                label	dd ?
	        ENDIF
        EndM

        JEAXZ	MACRO	Destination			;Like JECXZ,for Convinient
                test	eax,eax
                je	Destination
        ENDM


                                                The way OF Hume,2002.7
                                                [email protected]
                                                humeasm.yeah.net
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章