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 |
Windows XP下的向量異常處理
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.