发新话题
打印

Windows XP下的向量异常处理

Windows XP下的向量异常处理

首先回顾一下(SEH)结构化异常处理,结构化异常处理用EXCEPTION_RETGISTRATION结构链起来的
异常处理系统,那么当异常发生时,系统会遍历这个链,首先是链最前面的,系统会问:这个异常你
处理吗?如果回答YES(通过返回EXCEPTION_CONTINUE_EXECUTION)的话,那系统就把控制权交给他,
然后由其处理这个异常,返回到异常处理程序想返回的任何地方.如果通过返回EXCEPTION_CONTI
NUE_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
--------------------------------------------------------------------------------

TOP

看不懂..

TOP

哈哈. 你也有看不懂的东西,..
哇哈哈哈 ....  

TOP


.

的确不懂..

Oye

TOP

发新话题