Windows XP下的向量异常处理

来源:互联网 发布:左截断数据 编辑:程序博客网 时间:2024/05/07 11:21
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, stdcalloption casemap :none   ; case sensitiveinclude c:/hd/hd.hinclude c:/hd/mac.h;~~~~~~~~~~~~~~~~~~~protosASSUME  fs:nothing;~~~~~~~~~~~~~~~~~~~~~~~~~;;--------------.DATAsztit   db "By Hume,2K2",0.DATA?rd      hK32                    ;Kernel32模块地址rd      hVec                    ;AddVectoredExceptionHandler函数地址rd      hRemov                  ;RemoveVectoredExceptionHandler函数地址rd      hvectorhandler1rd      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        sWin32hVec,0,offset vEcp1     ;First VEH        mov     hvectorhandler1,eax        sWin32hVec,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:        INVOKEMessageBox,0,CTEXT("Good,Was Solved by VEH No 1"),addr sztit,0        xor     eax,eax        mov     eax,[eax]       ;Here!->seh1        JMP     @1     @2:        INVOKEMessageBox,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:        INVOKEMessageBox,0,esi,CTEXT("By Hume,2K2"),0                        ;卸载所有的VEH和SEH处理例程        pop     fs:[0]        pop     eax        sWin32hRemov,hvectorhandler1        sWin32hRemov,hvectorhandler2invoke ExitProcess,0        ;=======================        Assume  esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXTvEcp1   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        retvEcp1   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        retvEcp2   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 @@:        retsEh1    endp       ;Release assume       Assume  esi:Nothing,edi:Nothing;-----------------------------------------        ;FOR    debug purpose,Rubbish,....you can del it!.datafmt     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 ContextdP      proclocal   buf[256]:byte        pushad        mov     eax,(EXCEPTION_RECORD ptr [esi]).ExceptionFlags        INVOKEwsprintf,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        INVOKEMessageBox,0,addr buf,CTEXT("VEH Detector...debug purpose...Hume"),0        popad        retdP      endpEND__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 <>IFNBFOR     arg,IFNBtarget     CATSTR ,,targetENDIFENDMtarget     SUBSTR target,1,@SizeStr(%target)-1ENDIFEXITM   target        ENDM        sWin32Macrolabel:REQ,args:VARARG;Which allow no protos discalaiming        %FORpxx,;But you need to guarantee the                   IFNB ;the syntax yourself                     pushpxx                   ENDIF                ENDM                calllabel        ENDM        m2m MACRO M1, M2                          ;mov is too boring!              push M2              pop  M1        ENDM        $invoke Macro fun:REQ,args:VARARG        IFNB           invokefun,&args        ELSE   invokefun        ENDIF        EXITM        ENDM        rdMacro label:REQ,count        IFNB                label dd &count dup(?)        ELSE                labeldd ?        ENDIF        EndM        JEAXZMACRODestination;Like JECXZ,for Convinient                testeax,eax                jeDestination        ENDM                                                The way OF Hume,2002.7                                                humewen@21cn.com                                                humeasm.yeah.net
原创粉丝点击