pingjingde
驱动牛犊
驱动牛犊
  • 注册日期2003-03-05
  • 最后登录2004-08-26
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:1935回复:4

window系统服务的内存溢出为什么可以得到shell那???

楼主#
更多 发布于:2004-05-18 10:33
window系统服务的内存溢出为什么可以得到shell那???

 shell具有管理员权限那???

最新喜欢:

slimpigslimpi... quickerquicke...
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
沙发#
发布于:2004-05-18 10:39
window系统服务本身作为OS信任部分,拥有管理员权限,而内存溢出使溢出代码在该服务进程空间内,享有同样的特权......
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
RED_spring
驱动中牛
驱动中牛
  • 注册日期2002-07-28
  • 最后登录2016-11-06
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望19点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
  • 社区居民
板凳#
发布于:2004-05-21 18:51
请wowocock及其他各位高手谈一下window的异常处理机制,如何? :)
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
地板#
发布于:2004-05-21 19:28
                                        SEH  in ASM 研究

                                                        By Hume/冷雨飘心

更好排版WORD格式文档下载

为什么老调重弹:  

SEH出现已绝非一日,但很多人可能还不彻底了解Seh的运行机制;有关seh的知识资料不是很多,asm级的详细资料就更少!seh不仅可以简化程序错误处理,使你的程序更加健壮,还被广泛应用于反跟踪以及加解密中,因此,了解seh非常必要,但遗憾的是关于seh详细介绍的中文资料非常少,在实践的基础上,把自己学习的一点笔记奉献给大家,希望对喜欢ASM的朋友有所帮助.如有错误,请高手不吝指正.

    第一部分 基础篇

                          PART    I   简单接触

一、SEH背景知识

    SEH(\"Structured Exception Handling\"),即结构化异常处理.是(windows)操作系统提供给程序设计者的强有力的处理程序错误或异常的武器.在VISUAL C++中你或许已经熟悉了_try{} _finally{} 和_try{} _except {} 结构,这些并不是编译程序本身所固有的,本质上只不过是对windows内在提供的结构化异常处理的包装,不用这些高级语言编译器所提供的包装 ,照样可以利用系统提供的强大seh处理功能,在后面你将可以看到,用系统本身提供seh结构和规则以及ASM语言,我们将对SEH的机制以及实现进行一番(深入?)探究.

使用windows的人对microsoft设计的非法操作对话框一定不会陌生,尤其是在9X下.这表示发生了一个错误,如果是应用程序的错误,那么windows可能要关闭应用程序,如果是系统错误,你很可能不得不reset以重新启动计算机.从程序编写的角度来看,这种异常产生的原因很多,诸如堆栈溢出,非法指令,对windows保护内存的读写权限不够等等.幸运的是windows通过she机制给了应用程序一个机会来修补错误,事实上windows内部也广泛采用seh来除错.让我们先来看看如果一个应用程序发生错误后windows是怎么处理的.

 程序发生异常时系统的处理顺序(most by Jeremy Gordon):

       1.因为有很多种异常,系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正处于被调试状态,则系统挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.剩下的事情就由调试器全权负责.如果系统级调试器存在,对于int 1,int 3这样的异常在faults on时一般是会选择处理的,因而如果你的异常处理程序由他们来进入,则不会得到执行,呵呵,这不是正好可以用来探测调试器的存在吗?

       2.如果你的程序没有被调试或者调试器未能处理异常(/错误),系统就会继续查找你是否安装了线程相关的异常处理例程,如果你安装了线程相关的异常处理例程,系统就把异常发送给你程序的线程相关的seh处理例程,交由其处理.

       3.每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程,可交由链起来的其他例程处理.

       4.如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger.

       5.如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了final型异常处理例程的话,系统转向对它的调用.

       6.如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框,       你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统就调用ExitProcess终结程序.

       7.不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会.

以上大致描述了异常/错误发生时系统的逻辑处理顺序,如果你看了一头雾水的话,别着急,化点时间慢慢理解或者进入下一部分实例操作.作几个例子后你也许就会慢慢理解了.

    你要有一个最基本的观念就是She只不过是系统在终结你应用程序之前给你的一个最后处理错误的机会,从程序设计的角度来说就是给你自己设计的一个回调函数执行的机会.

二.初步实战演习:

你在自己程序里可以设计两种异常处理例程,一种是通过SetUnhandledExceptionFilter API设置的,姑且称之为final型的,他是进程相关的,也就是说在线程相关的异常处理部分不能处理的异常才会到达final处理例程.

另外一种是线程相关的,他一般用来监视处理进程中某个线程的异常情况.他比较灵活,可选择监视线程中的某一小段代码.姑且称之为thread型的.

下面看看如何设计一个最简单的异常处理程序.

挂接异常处理例程:

   I. final型的异常处理  

    对于final型的,在你的异常未能得到调试器以及线程相关处理例程处理操作系统在即将关闭程序之前会回调的例程,这个例程是进程相关的而不是线程相关的,因此无论是哪个线程发生异常未能被处理,都会调用这个例程.

    见下面的例子1:

;==========================================

; ex. 1,by Hume,2001 演示final型异常处理

;==========================================

.586p

.model flat, stdcall

option casemap :none   ; case sensitive

include c:\\hd\\hd.h      ;头部包含文件

include c:\\hd\\mac.h    ;常用宏,自己维护一个吧

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

        .data

szCap     db \"By Hume[AfO],2001...\",0

szMsgOK db \"OK,the exceptoin was handled by final handler!\",0

szMsgERR1 db \"It would never Get here!\",0

        .code

_start:

          push         offset Final_Handler

          call        SetUnhandledExceptionFilter      

 ;调用SetUnhandledExceptionFilter来安装final SEH

                           ;原型很简单SetUnhandledExceptionFilter proto

                           ;pTopLevelExceptionFilter:DWORD

        xor      ecx,ecx

        mov     eax,200  

        cdq

              div       ecx                                ;除0错误

                                               ;以下永远不会被执行

        invoke       MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h  

        invoke       ExitProcess,0                    ;30h=MB_ICONEXCLAMATION

                                                                                ;1000h=MB_SYSTEMMODAL

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

Final_Handler:

       invoke       MessageBox,0,addr szMsgOK,addr szCap,30h

       mov       eax,EXCEPTION_EXECUTE_HANDLER    

;==1 这时不出现非法操作的讨厌对话框

       ;mov       eax,EXCEPTION_CONTINUE_SEARCH    

                                                              ;==0 出现,这时是调用系统默认的异常

;处理过程,程序被终结了

        ;mov    eax,EXCEPTION_CONTINUE_EXECUTION

;==-1 不断出现对话框,你将陷入死循环,可

;别怪我

              ret 4

end _start

  简单来解释几句,windows根据你的异常处理程序的返回值来决定如何进一步处理

    EXCEPTION_EXECUTE_HANDLER  equ 1    表示我已经处理了异常,可以优雅地结束了

    EXCEPTION_CONTINUE_SEARCH  equ 0    表示我不处理,其他人来吧,于是windows调用默认的处理程序,显示一个除错对话框,并结束

EXCEPTION_CONTINUE_EXECUTION         equ -1   表示错误已经被修复,请从异常发生处继续执行

    你可以试着让程序返回0和-1然后编译程序,就会理解我所有苍白无力的语言...

II.线程相关的异常处理.

通常每个线程初始化准备好运行时fs指向一个TIB结构(THREAD INFORMATION BLOCK),这个结构的第一个元素fs:[0]指向一个_EXCEPTION_REGISTRATION结构,具体结构见下,后面_EXCEPTION_REGISTRATION为了简化,用ERR来代替这个结构...不要说没见过啊...

    fs:[0]->

     _EXCEPTION_REGISTRATION struc

     prev dd ?                    ;前一个_EXCEPTION_REGISTRATION结构

     handler dd ?                 ;异常处理例程入口....呵呵,现在明白该怎么作了吧

     _EXCEPTION_REGISTRATION ends

    我们可以建立一个ERR结构然后将fs:[0]换成指向他的指针,当然最常用的是堆栈,如果你非要用静态内存区也可以,

    把handler域换成你的程序入口,就可以在发生异常时调用你的代码了,好马上实践一下,见例子2

;===========================================

; ex. 2,by Hume,2001  线程相关的异常处理

;===========================================

.386

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h          

;;============================

.data

szCap     db \"By Hume[AfO],2001...\",0

szMsgOK db \"It\'s now in the Per_Thread handler!\",0

szMsgERR1 db \"It would never Get here!\",0

.code

_start:

   ASSUME FS:NOTHING        ;否则Masm编译报错

        push    offset perThread_Handler

              push    fs:[0]      

        mov     fs:[0],esp            ;建立SEH的基本ERR结构,如果

;不明白,就仔细研究一下前面所述

        xor     ecx,ecx                          

        mov     eax,200  

        cdq

              div       ecx

                                                  ;以下永远不会被执行

        invoke       MessageBox,0,addr szMsgERR1,addr szCap,10h+1000h

        pop     fs:[0]              ;清除seh链表

        add     esp,4

        invoke       ExitProcess,0        

;============================

perThread_Handler:

        invoke       MessageBox,NULL,addr szMsgOK,addr szCap, 40h+1000h

        mov     eax,1         ;ExceptionContinueSearch,不处理,由其他例程或系统处理

        ;mov    eax,0         ;ExceptionContinueExecution,表示已经修复CONTEXT,可

;从异常发生处继续执行

              ret                    ;这里如果返回0,你会陷入死循环,不断跳出对话框....

end _start

    嘿嘿,这个简单吧,我们由于没有足够的资料,暂时还不能修复ecx的值使之从异常发生处继续执行,只是简单显示一个MSG,然后让系统处理,自然跳出讨厌的对话框了....

    注意化5分钟研究和final返回值的含义不同...windows也是根据返回值来决定下一步的动作的.

好像到此为止,我们并没有从异常处理中得到任何好处,除了在异常发生后可以执行一点我们微不足道的代码,事实上SEH可以修复这些异常或者干我们想干的任何事情然后从希望的地方继续执行,嘿嘿,很爽吧,可惜我们没有足够的信息,那里找到我们所需要的信息?

                                                  PART II   继续深入

三、传递给异常处理例程的参数

    I、传递给final型的参数,只有一个即指向EXCEPTION_POINTERS结构的指针, EXCEPTION_POINTERS定义如下:

       EXCEPTION_POINTERS STRUCT

        pExceptionRecord  DWORD      ?              

       ContextRecord     DWORD      ?

      EXCEPTION_POINTERS ENDS

       执行时堆栈结构如下:

                 esp    -> ptEXCEPTION_POINTERS

                            然后执行call _Final_Handler

注意堆栈中的参数是指向EXCEPTION_POINTERS 的指针,而不是指向pExceptionRecord的指针

以下是EXCEPTION_POINTERS两个成员的详细结构

              EXCEPTION_RECORD STRUCT

          ExceptionCode         DWORD      ?       ;异常码

          ExceptionFlags        DWORD      ?       ;异常标志

          PExceptionRecord     DWORD      ?      ;指向另外一个EXCEPTION_RECORD的指针

          ExceptionAddress      DWORD      ?       ;异常发生的地址

          NumberParameters      DWORD      ?       ;下面ExceptionInformation所含有的dword数目

          ExceptionInformation  DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)

        EXCEPTION_RECORD ENDS                      ;EXCEPTION_MAXIMUM_PARAMETERS ==15

;================具体参数解释========================================

ExceptionCode 异常类型,SDK里面有很多类型,但你最可能遇到的几种类型如下:

              C0000005h----读写内存冲突

              C0000094h----非法除0

              C00000FDh----堆栈溢出或者说越界

              80000001h----由Virtual Alloc建立起来的属性页冲突

              C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常

              C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,例如调用RtlUnwind时没有Exception Record参数时产生的异常填入的就是这个代码

              80000003h----调试时因代码中int3中断

              80000004h----处于被单步调试状态

              注:也可以自己定义异常代码,遵循如下规则:

              _____________________________________________________________________+

      位:       31~30            29~28           27~16          15~0

              _____________________________________________________________________+

    含义:     严重程度          29位            功能代码        异常代码

              0==成功         0==Mcrosoft     MICROSOFT定义   用户定义

              1==通知         1==客户

              2==警告          28位

              3==错误         被保留必须为0

ExceptionFlags 异常标志

              0----可修复异常

              1----不可修复异常

              2----正在展开,不要试图修复什么,需要的话,释放必要的资源

pExceptionRecord 如果程序本身导致异常,指向那个异常结构

ExceptionAddress 发生异常的eip地址

ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时(ExceptionCode=C0000005h) 的含义如下,其他情况下一般没有意义

              第一个dword 0==读冲突 1==写冲突

第二个dword 读写冲突地址

;==========CONTEXT具体结构含义================================

              CONTEXT STRUCT                     ; _                  

          ContextFlags  DWORD      ?       ;  |---------------  +00

          iDr0          DWORD      ?       ;  |                 +04

          iDr1          DWORD      ?       ;  |                 +08

          iDr2          DWORD      ?       ;   >调试寄存器       +0C

          iDr3          DWORD      ?       ;  |                 +10

          iDr6          DWORD      ?       ;  |                 +14

          iDr7          DWORD      ?       ; _|                 +18

          FloatSave     FLOATING_SAVE_AREA <>  ;浮点寄存器区      +1C~~+88

          regGs         DWORD      ?       ;--|                 +8C

          regFs         DWORD      ?       ;  |\\段寄存器         +90        

          regEs         DWORD      ?       ;  |/                +94

          regDs         DWORD      ?       ;--|                 +98

          regEdi        DWORD      ?       ;____________        +9C

          regEsi        DWORD      ?       ;       |  通用      +A0  

          regEbx        DWORD      ?       ;       |   寄       +A4

          regEdx        DWORD      ?       ;       |   存       +A8

          regEcx        DWORD      ?       ;       |   器       +AC

          regEax        DWORD      ?       ;_______|___组_      +B0    

          regEbp        DWORD      ?       ;++++++++++++++++    +B4

          regEip        DWORD      ?       ;    |控制           +B8

          regCs         DWORD      ?       ;    |寄存           +BC

          regFlag       DWORD      ?       ;    |器组            +C0

          regEsp        DWORD      ?       ;    |               +C4  

          regSs         DWORD      ?       ;+++++++++++++++++   +C8

          ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)

        CONTEXT ENDS

以上是两个成员的详细结构,下面给出一个final型的例子,这也是本文所讨论的最后一个final型的例子,以后的例子集中在thread类型上.

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

; Ex3,演示final处理句柄的参数获取,加深前面

; 参数传递的介绍理解如果难于理解请先看partIII

; 再回来看这个例子

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

.586

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h

include mac.h

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

.data

sztit   db \"exceptION MeSs,by hume[AfO]\",0

fmt     db \"Context eip--> %8X   ebx--> %8X \",0dh,0ah

        db \"Flags   Ex.c-> %8x   flg--> %8X\",0

szbuf   db 200 dup(0)

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

    .CODE

_Start:

        assume  fs:nothing

        push    offset _final_xHandler0

        call    SetUnhandledExceptionFilter

        xor     ebx,ebx

        mov     eax,200

        cdq

        div     ebx

        invoke    MessageBox,0,ddd(\"Good,divide overflow was solved!\"),addr sztit,40h

        xor     eax,eax

        mov     [eax],ebx

        invoke    ExitProcess,0    

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

_final_xHandler0:

        push    ebp

        mov     ebp,esp

        mov     eax,[ebp+8]      ;the pointer to EXCEPTION_POINTERS

        mov     esi,[eax]        ;pointer to _EXCEPTION_RECORD

        mov     edi,[eax+4]      ;pointer to _CONTEXT

        test    dword ptr[esi+4],1  

        jnz     @_final_cnotdo

        test    dword ptr[esi+4],6  

        jnz     @_final_unwind

        ;call    dispMsg

        cmp     dword ptr[esi],0c0000094h

        jnz     @_final_cnotdo

        mov     dword ptr [edi+0a4h],10

        call    dispMsg

        mov     eax,EXCEPTION_CONTINUE_EXECUTION       ;GO ON

        jmp     @f

@_final_unwind:

        invoke    MessageBox,0,CTEXT(\"state:In final unwind...\"),addr sztit,0

                             ;好像不论处理不处理异常,都不会被调用,right?

@_final_cnotdo:                            

        mov     eax,EXCEPTION_CONTINUE_SEARCH

        jmp     @f        

@@:      

        mov     esp,ebp

        pop     ebp

        ret

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

dispMsg    proc                      ;My  lame proc to display some message

        pushad

        mov     eax,[esi]

        mov     ebx,[esi+4]

        mov     ecx,[edi+0b8h]

        mov     edx,[edi+0a4h]

        invoke    wsprintf,addr szbuf,addr fmt,ecx,edx,eax,ebx

        invoke    MessageBox,0,addr szbuf,CTEXT(\"related Mess of context\"),0

        popad

        ret

dispMsg    endp

END    _Start

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

II、 传递给per_thread型异常处理程序的参数,如下:

     在堆栈中形成如下结构

             esp    -> *EXCEPTION_RECORD

             esp+4  -> *ERR                     ;注意这也就是fs:[0]的指向

             esp    -> *CONTEXT record           ;point to registers

             esp    -> *Param                    ;呵呵,没有啥意义

           然后执行 call _Per_Thread_xHandler

操作系统调用handler的MASM原型是这样

        invoke xHANDLER,*EXCEPTION_RECORD,*_EXCEPTION_REGISTRATION,*CONTEXT,*Param

              即编译后代码如下:

        PUSH *Param                     ;通常不重要,没有什么意义

        push *CONTEXT record            ;上面的结构

        push *ERR                       ;the struc above

        push *EXCEPTION_RECORD          ;see above

        CALL HANDLER

        ADD ESP,10h

    下一部分给出thread类型的具体实例.

                                                     PART III 不是终结

我们的目标是分三步走,学会SEH,现在让我们接触最有趣的部分:SEH的应用.seh设计的最初目的就是为了使应用程序运行得更健壮,因此SEH用于除错,避免应用程序和系统的崩溃是最常见的用途.例如:

1.比如你的程序里出现了除0错,那你就可以在你的seh处理程序中将除数改为非零值,per_Thread seh返回0(ExceptionContinueExecution)、final返回-1 (EXCEPTION_CONTINUE_EXECUTION),系统就会根据你的意图用改变过的context加载程序在异常处继续执行,由于被除数已经改变为非零值,你的程序就可以正常仿佛什么也没有发生的继续执行了.

2.seh还可以处理内存读写异常,如果你分配的堆栈空间不够,产生溢出,这时你就可以处理这个异常,再多分配一些空间,然后结果是你的程序照常运行了,就好像什么也没有发生过,这在提高内存运用效率方面很值得借鉴,虽然会降低一些程序的执行效率.另外,在很多加壳或反跟踪软件中,利用vitualAlloc和VitualProtect制造异常来进入异常程序,或仅仅是用,mov [0],XXX来进入异常程序,要比用int3或者int1或

pushf

and [esp],100h

popf

进入要隐蔽得多,如果可以随机引起这些异常的话,效果会更好...当然应用很多了,感兴趣自己去找.话题似乎有点远了,让我们回到最基础的地方.  

前面的例子中你可能已经注意到,假如我们改变了Context的内容,(注意啊,context包含了系统运行时各个重要的寄存器),并且返回0(ExceptionContinueExecution-->perThread SEH),或者-1(EXCEPTION_CONTINUE_EXECUTION,final SEH),就表示要系统已现有的context继续执行程序,当然我们的改变被重载了,就像周星驰的月光宝盒改变了历史一样奇妙,程序就会以改变的context内容去执行程序,通过这种手段,我们可以修复程序,使其继续执行.

看下面的例子4.

读之前,先再罗嗦几句,由于前面介绍了seh例程被调用的时候,系统把相关信息已经压入堆栈,所以我们只要在程序里寻址调用就行了,怎么寻址呢???唉....回顾一下call指令执行的基本知识,一般对于近调用,通过[esp+4]即刻找到

*EXCEPTION_RECORD,其余的不用说了吧,如果执行了push ebp;mov ebp,esp的话,就是[ebp+8]指向*EXCEPTION_RECORD,这也是大多数程序用的和我们最常见到的,明白了吗?不明白?我--去--跳--楼.

;________________________________________________________________________

;|EX.4 By hume,2001,to show the basic simple  seh function

;|________________________________________________________________________

.386

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h           ;//相关的头文件,你自己维护一个吧

.data

szCap     db \"By Hume[AfO],2001...\",0

szMsgOK db \"It\'s now in the Per_Thread handler!\",0

szMsg1 db \"In normal,It would never Get here!\",0

fmt     db \"%s \",0dh,0ah,\"  除法的商是:%d\",0

buff    db 200 dup(0)

.code

_start:

  Assume FS:NOTHING

        push    offset perThread_Handler

              push    fs:[0]      

        mov     fs:[0],esp                       ;//建立SEH的基本ERR结构,如果不

;//明白,就仔细研究一下吧

        xor     ecx,ecx                          

        mov     eax,200  

        cdq

              div       ecx

 WouldBeOmit:                                   ;//正常情况以下永远不会被执行

        add     eax,100                         ;//这里不会执行,因为我们改变了eip的值    

 ExecuteHere:

        div     ecx                             ;//从这里开始执行,从结果可以看到

        invoke       wsprintf,addr buff,addr fmt,addr szMsg1,eax        

        invoke       MessageBox,NULL,addr buff,addr szCap,40h+1000h

        pop     fs:[0]                          ;//修复后显示20,因为我们让ecx=10

        add     esp,4

        invoke       ExitProcess,NULL        

perThread_Handler proc \\

uses ebx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD        

        mov eax,pContext

  Assume eax:ptr CONTEXT

        mov     [eax].regEcx,20  ;//Ecx改变

        lea     ebx, ExecuteHere

        mov     [eax].regEip,ebx ;//从我们想要的地方开始执行,嘿嘿,这就是很多

;//反跟踪软件把你引向的黑暗之域

        mov     eax,0         ;//ExceptionContinueExecution,表示已经修复

;//CONTEXT,可从异常发生处

                              ;//reload并继续执行

       ret                    

perThread_Handler endp

end _start

;//====================================================

哈哈,从这个例子里我门可以真正看到seh结构化处理的威力,他不仅恢复了ecx的内容而且使程序按照你想要的顺序执行了,哈哈,如果你对反跟踪很感兴趣的话,你还可以在例程中加入

        xor     ebx,ebx

        mov     [eax].iDr0,ebx

        mov     [eax].iDr2,ebx

        mov     [eax].iDr3,ebx

        mov     [eax].iDr4,ebx

清除断点,跟踪者....嘿嘿,不说你也体验过,当然也可以通过检验drx的值来判断是否被跟踪,更复杂地,你可以设置dr6,和dr7产生一些有趣的结果,我就不罗嗦了.

上面的例子理解了吧,因为我用的是MASM提供的优势来简化程序,老Tasm Fans可能会不以为然,你可以试一下下面的代码代替,是TASM,MASM compatibale的

perThread_Handler:

        push    ebp

        mov     ebp,esp

        mov     eax,[ebp+10h]     ;取context的指针

        mov     [eax+0ach],20     ;将ecx=0,可以对照前面的例程和context结构

        lea     ebx, ExecuteHere

        mov     [eax+0b8h],ebx    ;eip== offset ExecuteHere,呵呵

        xor     eax,eax

        mov     esp,ebp

        pop     ebp

        ret

这是raw asm的,不过masm既然给我们设计了这么多好东西,我们为什么不好好利用呢?

好,到现在为止,基本知识已经结束了,我们应该可理解seh的相关文章和写简单的seh处理程序了,但关于seh还只是刚刚开始,很多内容和应用还没有涉及到,请继续看提高篇.

                     SEH IN ASM 研究

第二部分提高篇

                                        By Hume[AfO]/冷雨飘心

PART IV  关于异常处理的嵌套和堆栈展开

在实际程序设计过程中,不可能只有一个异常处理例程,这就产生了异常处理程序嵌套的问题,可能很多处理例程分别监视若干子程序并处理其中某种异常,另外一个监视所有子程序可能产生的共性异常,这作起来实际很容易,也方便调试.你只要依次建立异常处理框架就可以了.关于VC++异常处理可以嵌套很多人可能比较熟悉,用起来更容易不过实现比这里也就复杂得多,在VC++中一个程序所有异常只指向一个相同的处理句例程,然后在这个处理例程里再实现对各个子异常处理例程的调用,他的大致方法是建立一个子异常处理例程入口的数组表,然后根据指针来调用子处理例程,过程比较烦琐,原来打算大致写一点,现在发现自己对C/C++了解实在太少,各位有兴趣还是自己参考MSDN Matt Pietrek 1996年写的一篇文章<<A Crash Course on the Depths of Win32? Structured Exception Handling>>,里面有非常详细的说明,对于系统的实现细节也有所讨论,不过相信很多人都没有兴趣.hmmm...:)实际上Kernel的异常处理过程和VC++的很相似.我们的程序中当然也可以采用这种方法,不过一般应用中不必牛刀砍蚂蚁.

 有异常嵌套就涉及到我们可能以前经常看到并且被一些污七八糟的不负责任的”翻译家”搞得头晕脑涨不知为何的”异常展开”的问题,也许你注意到了如果按照我前面的例子包括final都不处理异常的话,最后系统在终结程序之前会来一次展开,在试验之后发现,展开不会调用final只是对per_thread例程展开(right?).什么是堆栈展开?为什么要进行堆栈展开?如何进行堆栈展开?

 我曾经为堆栈展开迷惑过,原因是各种资料的描述很不一致,Matt Pietrek说展开后前面的ERR结构被释放,并且好像seh链上后面的处理例程如果决定处理异常必须对前面的例程来一次展开,很多C/C++讲述异常处理的书也如斯说这使人很迷惑,我们再来看看Jeremy Gordon的描述,堆栈展开是处理异常的例程自愿进行的.呵呵,究竟事实如何?

 在迷惑好久之后我终于找到了答案:Matt Pietrek讲的没有错,那是VC++以及系统kernel的处理方法,Jeremy Gordon说的也是正确的,那是我门asm Fans的自由!

 好了,现在来说堆栈展开,堆栈展开这个词似乎会使人有所误解,堆栈怎么展开呢?事实上,堆栈展开是异常处理例程在决定处理某个异常的时候给前面不处理这个异常的处理例程的一个清洗的机会,前面拒绝处理这个异常的例程可以释放必要的句柄对象或者释放堆栈或者干点别的工作...那完全是你的自由,叫stack unwind似乎有点牵强.堆栈展开有一个重要的标志就是EXCEPTION_RECORD.ExceptionFlag为2,表示正在展开,你可以进行相应的处理工作,但实际上经常用的是6这是因为还有一个UNWIND_EXIT equ 4什么的,具体含义未知,不过kernel确实就是检测这个值,因此我们也就检测这个值来判断展开.

注意在自己的异常处理例程中,unwind不是自动的,必须你自己自觉地引发,如果所有例程都不处理系统最后的展开是注定的. 当然如果没有必要你也可以选择不展开.

win32提供了一个api RtlUnwind来引发展开,如果你想展开一下,就调用这个api吧,少候讲述自己代码如何展开

RtlUnwind调用描述如下:

        PUSH Return value         ;返回值,一般不用

        PUSH pExceptionRecord     ;指向EXCEPTION_RECORD的指针

        PUSH OFFSET CodeLabel    ;展开后从哪里执行

        PUSH LastStackFrame       ;展开到哪个处理例程终止返回,通常是处理异常的Err结构

        CALL RtlUnwind

调用这个api之前要注意保护ebx,esi和edi,否则...嘿嘿

MASM格式如下:      

 Invoke

 RtlUnwind,pFrame,OFFSET return_code_Address,pExceptionRecord,Return_value

这样在展开的时候,就以pExceptionRecord.flag=2 依次调用前面的异常处理例程,到决定异常的处理例程停止,Jeremy Gordon手动展开代码和我下面的例子有所不同.他描述最后决定处理异常的ERR结构的prev成员为-1,好像

我的结果和他的有所差异,因此采用了另外的方法,具体看下面的例子.

最后一点要注意在嵌套异常处理程序的时候要注意保存寄存器,否则你经常会得到系统异常代码为C00000027h的异常调用,原因是异常处理中又产生异常,而这个异常又无法解决,只好由操作系统终结你的程序.

   一下给出一点垃圾代码演示可能有助于理解,注意link的时候要加入 /section:.text,RWE 否则例子里面的代码段不能写,SMC功能会产生异常以致整个程序不能进行.

注意:2K/XP下非法指令异常的代码不一致,另外用下面的例子在2K下不能工作,具体错误未知.因此只能在9X下演示,为了在2k/Xp下也能运行我加了点代码,有兴趣看看,另外帮我解决一下2K/Xp下SMC的问题?thx!

下面例子很烂,不过MASM格式写起来容易一点,也便于理解.看来比较长实际很简单,耐心看下去.

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

;Ex5,演示堆栈展开和异常嵌套处理 by Hume,2002

;humewen@21cn.com

;hume.longcity.net

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

.586

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h

include mac.h

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

per_xHandler1         proto C :DWORD,:DWORD,:DWORD,:DWORD

per_xHandler2         proto C :DWORD,:DWORD,:DWORD,:DWORD

per_xHandler3         proto C :DWORD,:DWORD,:DWORD,:DWORD

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

.data

sztit   db \"except Mess,by hume[AfO]\",0

count   dd 0,0

Expt1_frm   dd 0                     ;ERR结构指针,用于堆栈展开手动代码

Expt2_frm   dd 0

Expt3_frm   dd 0

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

       .CODE

_Start:

        assume  fs:nothing

        push    offset per_xHandler3

        push    fs:[0]

        mov     fs:[0],esp

        mov     Expt3_frm,esp

        push    offset per_xHandler2

        push    fs:[0]

        mov     fs:[0],esp

        mov     Expt2_frm,esp

        push    offset per_xHandler1

        push    fs:[0]

        mov     fs:[0],esp

        mov     Expt1_frm,esp

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

        ;install xhnadler

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

        xor     ebx,ebx

        mov     eax,200

        cdq

        div     ebx                  ;除法错误

        invoke       MessageBox,0,ddd(\"Good,divide overflow was solved!\"),addr sztit,40h

        sub     eax,eax

        mov     [eax],ebx            ;内存写错误

succ:

        invoke       MessageBox,0,ddd(\"Good,memory write violation solved!\"),addr sztit,40h

        db      0F0h,0Fh,0C7h,0C8h   ;什么cmpchg8b指令的非法形式?我从来没有成功过!!

                                     ;演示程序中使用seh实现SMC技术,加密??...

        invoke       MessageBox,0,ddd(\"illeagal instruction was solved!\"),addr sztit,20h

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

        ;uninstall xhnadler

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

        pop     fs:[0]            

        add     esp,4

        pop     fs:[0]            

        add     esp,4

       ;或者add esp,10h

        pop     fs:[0]            

        add     esp,4

       invoke       ExitProcess,0

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

;异常处理句柄1,处理除法异常错误

per_xHandler1 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD

        pushad

        MOV     ESI,pExcept

ASSUME  ESI:PTR EXCEPTION_RECORD

        TEST    [ESI].ExceptionFlags,1

        JNZ     @cantdo1

        TEST    [ESI].ExceptionFlags,6

        JNZ     @unwind1

        CMP     [ESI].ExceptionCode,0C0000094h

        JNZ     @cantdo1

        MOV     EDI,pContext

ASSUME  EDI:PTR CONTEXT

        m2m     [edi].regEbx,20             ;将ebx置20,修复除法错误,继续执行

        popad

       MOV  EAX, ExceptionContinueExecution

        RET

@unwind1:

        invoke       MessageBox,0,CTEXT(\"state: unwinding in xhandler1...\"),addr sztit,0

@cantdo1:

        popad

        MOV     EAX,ExceptionContinueSearch

       RET

per_xHandler1 ENDP

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

;异常处理句柄2,处理内存写错误,扩展可以有其他的例子如自动扩充堆栈

per_xHandler2 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD

        pushad

       MOV     ESI,pExcept

ASSUME  ESI:PTR EXCEPTION_RECORD

        MOV     EDI,pContext

ASSUME  EDI:PTR CONTEXT

        call    Dispcont                             ;显示一点lame的消息,自己调试用

        TEST    [ESI].ExceptionFlags,1

        JNZ     @cantdo2

        TEST    [ESI].ExceptionFlags,6

        JNZ     @unwind2

        CMP     [ESI].ExceptionCode,0C0000005h

        JNZ     @cantdo2

        .data                                         ;ASM的数据定义灵活性,如果需要这是可以的

         validAddress dd 0

        .code

        m2m     [EDI].regEax,<offset  validAddress>    ;置eax为有效地址      

        popad

              MOV  EAX, ExceptionContinueExecution

        RET

@unwind2:

        invoke       MessageBox,0,CTEXT(\"hmmm... unwinding in xhandler2...\"),addr sztit,40h

@cantdo2:

        popad

        MOV     EAX,ExceptionContinueSearch

       RET

per_xHandler2 ENDP

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

per_xHandler3 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD

       pushad

        MOV     ESI,pExcept

ASSUME  ESI:PTR EXCEPTION_RECORD

        MOV     EDI,pContext

ASSUME  EDI:PTR CONTEXT      

        TEST    [ESI].ExceptionFlags,1

        JNZ     @cantdo3

        TEST    [ESI].ExceptionFlags,6

        JNZ     @unwind3

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

        push    ecx

        mov     ecx,cs

        xor     cl,cl

        jecxz   win2k_Xp        

 win9X:

        pop     ecx

        CMP     [ESI].ExceptionCode,0C000001DH       ;非法指令异常,与2K/XP下的不一致

        JNZ     @cantdo3

        jmp     ok_here

win2k_Xp:

        pop     ecx                                  ;注意,只有在9X下才可以

        CMP     [ESI].ExceptionCode,0C000001EH       ;非法指令异常->2K/XP

        JNZ     @cantdo3                             ;sMc不成

        mov     [edi].regEip,offset safereturn

        popad

        mov     eax,0

        ret

        push    ebx

        push    esi

        push    edi      

comment $ 调用RtlUnwind展开堆栈        

        lea     ebx,unwindback

        invoke       RtlUnwind,Expt3_frm,ebx,esi,0

        $

        mov     dword ptr [esi+4],2               ;置展开标志,准备展开,这里是

                                               ;手动代码

        mov     ebx,fs:[0]

selfun:

        ;mov     eax,Expt2_frm                 ;这里显示了ASM手动展开的灵活性

        mov     eax,Expt3_frm                      

        cmp     ebx,eax                    ;按照Jeremy Gordon的好像不大对头

        ;cmp     dword ptr [ebx],-1         ;这样好像有问题,只好如上,请教答案          

        jz      unwindback

        push    ebx

        push    esi                    ; 压入Err和Exeption_registration结构

        call    dword ptr[ebx+4]

        add     esp,8

        mov     ebx,[ebx]

        jmp     selfun

unwindback:

        invoke       MessageBox,0,CTEXT(\"I am Back!\"),addr sztit,40h

        pop     edi

        pop     esi

        pop     ebx                              ;一定要保存这三个寄存器!

        MOV     EAX,[EDI].regEip

        MOV     DWORD PTR[EAX],90909090H  ;改为nop指令...SMC? 很简单吧

                                               ;SMC注意连接/section:RWE

        popad

              MOV  EAX, ExceptionContinueExecution

        RET

@unwind3:

        invoke       MessageBox,0,CTEXT(\"Note... unwinding in xhandler3...\"),addr sztit,40h

@cantdo3:

        popad

        MOV     EAX,ExceptionContinueSearch

       RET

per_xHandler3 ENDP

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

;lame routine for debug

Dispcont       proc

                inc     count

                call    dispMsg

                ret

Dispcont       endp

dispMsg       proc

        local szbuf[200]:byte

        pushad

        mov     eax,dword ptr[esi]

        mov     ebx,dword ptr[esi+4]

        mov     ecx,dword ptr[edi+0b8h]

        mov     edx,dword ptr[edi+0a4h]

        .data

        fmt     db \"Context eip--> %8X   ebx--> %8X \",0dh,0ah

                db \"Flags   Ex.c-> %8x   flg--> %8X\",0dh,0ah

                db \"it\'s the %d times xhandler was called!\",0

        .code

        invoke       wsprintf,addr szbuf,addr fmt,ecx,edx,eax,ebx,count

        invoke       MessageBox,0,addr szbuf,CTEXT(\"related Mess of context\"),0

        popad

       ret

dispMsg       endp

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

END       _Start

;------------------下面是我在本文里用到到的宏,我的mac.h比较长,就不贴了-----

    ddd       MACRO Text                 ;define data in .data section

        local name                        ;This and other can be used as: ddd(\"My god!\")

        .data                                 ;isn\'t cool?

            name    db Text,0

        .code

        EXITM <addr name>  

    ENDM

   CTEXT MACRO y:VARARG                     ;This is a good macro

              LOCAL sym

       CONST segment

              IFIDNI <y>,<>

                     sym db 0

              ELSE

                     sym db y,0

              ENDIF

       CONST ends

              EXITM <OFFSET sym>

     ENDM

    m2m MACRO M1, M2                          ;mov is too boring sometimes!

      push M2

      pop  M1

    ENDM    

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

BTW:够长了吧,基本内容介绍完毕,更多内容下一部分介绍一点利用Seh的tricks,哪位大侠有什么好的想法或者有什么错误,请不吝指正,毕竟我是菜鸟...

PART V 利用SEH进入ring0以及单步自跟踪的实现

--SEH的简单应用

实在太累了,这将是最后一部分.

 一、ring0!并不遥远...

   作为seh的一个有趣的应用是进入ring0,ring0意味着更多的权利,意味着你可以进行一些其他ring3级应用程序不能进行的操作,譬如改自己的代码段(在不修改段属性的前提下),改系统数据(病毒?)等等,在9X下进入ring0的方法很多,在NT下困难的多,SEH只是其中较简单的一种.打开调试器看看系统kernel的工作状态,在9X下cs一般是28h,ds,ss等通常是30h,因此只要我们的cs和ss等在异常处理程序中被赋予上述ring0选择子值,进入ring0就可以实现.可能我们需要执行较复杂的操作,在ring0下一般不能直接调用常用api,当然VxD,WDM等提供的系统服务是另外一种选择. 否则,这在用下述简单方法进入ring0后执行会产生错误,因此,我们在ring0下尽快完成需要完成的任务,然后迅速返回ring3.

在ring0下要完成如下任务:

1.取CR3的值,返回ring3显示.在ring3下不可以读取cr3的值.你可以打开kernel调试器看看例子程序取到的值是否正确.

2.修改代码段后面的jmp ****代码,这在通常情况下只会导致保护错误.而在ring0下是可以的,就像在前面例子中用she实现SMC的效果是一样的,最后显示几个MsgBox,证明我们曾经到达过ring0

这个例子是参考owl的那个nasm写的例子用masm改写,并增加ring0下SMC的代码部分以作演示.另外代码中iretd指令并不是简单实现跳转,而是实现从ring0切回ring3的功能,在变换代码特权级的同时,堆栈的也要变换到ring3.可能原例子ljtt前辈的中文注释容易引起初学者的误解.

别的不说,我发现进入ring0后修改代码段可以使trw的跟踪崩溃...hmmm,好消息?代码如下:

其中用的一些宏在Ex5中已经贴了,就不再重复.

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

;Ex6,演示利用seh进入ring0! by Hume,2002

;humewen@21cn.com

;hume.longcity.net

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

.586

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h

include mac.h

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

ring0_xHandler         proto C :DWORD,:DWORD,:DWORD,:DWORD

        .data

szbuf   db 100 dup (0)

count   dd 0

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

       .CODE

_Start:

        assume fs:nothing

              push    offset ring0_xHandler

        push    fs:[0]

        mov     fs:[0],esp

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

        mov     ecx,ds

        test    ecx,100b

        jz      NT_2K_XP         ;NT/2K/XP has no LDT

        pushfd

        mov     eax,esp

        int     3

        mov     ebx,cr3          ;现在,正式宣布,进入ring0!

                                 ;呵呵这样简单就进入ring0了,至于进入

        push    ebx              ;ring0有啥用,不要问我!

        lea     ebx,offset _modi  ;SMC

        mov     byte ptr[ebx],75h ;修改jmp addinfo为jnz addinfo指令

        pop     ebx

        push    edx                     ;ss

        push    eax                     ;esp

        push    dword ptr[eax]          ;eflags          

        push    ecx                     ;cs

        push    offset ring3back        ;eip

        iretd                           ;这里是通过iretd 指令返回特权级3

ring3back:

        popfd

        invoke       wsprintf,addr szbuf,ddd(\"It\'s in ring0,please see CR3==%08X\",0dh,oah,\"following display Modified info...\"),ebx

        invoke       MessageBox,0,addr szbuf,ddd(\"Ring0! by Hume[AfO]\"),40h  

        xor     eax,eax

        ;add     eax,2

        .data

        Nosmc db \"Not modified area!\",0

        besmc db \"haha,I am modified by self in ring0!\",0

        .code

        mov     ebx,offset Nosmc

        mov     eax,0

_modi:

        jmp     addinfo            ;SMC后将这里改为jnz addinfo

        mov     ebx,offset besmc

        mov     eax,30h

addinfo:

        invoke       MessageBox,0,ebx,ddd(\"Rin0 SMC test\"),eax

_exit:

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

        pop     fs:[0]

        add     esp,4

        invoke       ExitProcess,0

NT_2K_XP:

        invoke       MessageBox,0,ddd(\"The example not support NT/2K/Xp,only 9x!\"),ddd(\"By hume\"),20h

        jmp     _exit

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

ring0_xHandler PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD

        pushad        

assume  edi:ptr CONTEXT

assume  esi:ptr EXCEPTION_RECORD

        mov     esi,pExcept

        mov     edi,pContext          

        test    dword ptr[esi+4],1              ;Exception flags

        jnz     @f        

        test    dword ptr[esi+4],6

        jnz     @f

        cmp    dword ptr[esi],80000003h        ;break ponit flag

        jnz     @f        

        m2m     [edi].regEcx,[edi].regCs        ;保存3级代码段选择子

        mov     [edi].regCs,28h                 ;0级代码段选择子

        m2m     [edi].regEdx,[edi].regSs        ;保存3级堆栈段选择子

        mov     [edi].regSs,30h                 ;0级堆栈选择子

        mov     dword ptr[esp+7*4],0

        popad        

        ret

@@:

        mov     dword ptr[esp+7*4],1

        popad    

        ret

ring0_xHandler ENDP

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

END       _Start

 由于在NT/2K/XP下这种进入ring0的方法不能使用,所以首先区别系统版本,如果是NT/2K/XP则拒绝执行, 原理是在NT/2K/XP下没有LDT,因此测试选择子是否指向LDT,这是一种简单的方法,但不推荐使用, 最好使用GetVersionEx...至于

         mov     dword ptr[esp+7*4],0

         popad        

 是返回eax=1的实现.

二.seh实现单步自跟踪.

有时如果你对SICE,TRW或者其他调试器显示的信息有所怀疑的话,你可以用seh显示一些信息作为简单的调试手段,当然,单步跟踪的用途远不止于此.首先回忆一下我们以前了解的单步的概念,当EFLAGS的TF位为1的话执行完某条指令后CPU将产生单步异常,与执行软指令int1类似.注意产生单步陷阱后eip已经指向下一条指令.但进入单步异常处理程序后cpu自动清除TF,以便下条指令正常执行.

我们要作的就是在seh例程中继续置TF位为1,以便下一条指令执行完毕后继续产生单步陷阱实现跟踪功能,直到遇到popfd指令为止,当然你也可以随便检测其他指令或者用记数器来终止单步.

下面例子中如果没有单步跟踪eax的最后结果是3,由于有了单步自跟踪,在seh处理例程中我们每中断一次要加1,所以最后的结果是7,呵呵.请看下面的例子.

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

;Ex7,演示利用seh单步自跟踪 by Hume,2002

;humewen@21cn.com

;hume.longcity.net

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

.586

.model flat, stdcall

option casemap :none   ; case sensitive

include hd.h

include mac.h

singlestep_xHandler        proto C :DWORD,:DWORD,:DWORD,:DWORD

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

.data

count   dd      0

Msg0    db      \"Eax==\"

DispEAX dd      0,0

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

       .CODE

_Start:

   assume fs:nothing

       push    offset singlestep_xHandler

        push    fs:[0]

        mov     fs:[0],esp

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

        xor     eax,eax

        pushfd

        pushfd

        or      dword ptr[esp],100h      

        popfd                             ;置TF标志进入单步状态

        nop                               ; nop执行完后单步异常引发  

        inc     eax                       ; eip指向,nop后面的指令,就是这里

        inc     eax                       ; 单步执行

        inc     eax                       ; normal eax==3,but infact eax==7

        popfd

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

        add     eax,30h                   ;convert to ASCIIZ

        mov     DispEAX,eax

        invoke       MessageBox,0,addr Msg0,ddd(\"The Eax equal to...\"),0

        pop     fs:[0]

        add     esp,4

        invoke       ExitProcess,0

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

singlestep_xHandler PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD

        pushad        

assume  edi:ptr CONTEXT

assume  esi:ptr EXCEPTION_RECORD

        mov     esi,pExcept

        mov     edi,pContext      

        test    dword ptr[esi+4],1              ;Exception flags test common stuff

        jnz     @f        

        test    dword ptr[esi+4],6

        jnz     @f

        cmp    dword ptr[esi],80000004h         ;是否为单步异常标志

        jnz     @f

        inc     [edi].regEax

        mov     ebx,[edi].regEip

        cmp     byte ptr[ebx],9Dh               ;是否是popfd,因为目的是取消

        jz     @finish_singlestep               ;单步状态,所以这时就不应该重置TF

        or      [edi].regFlag,100h              ;否则,重置TF    

                                                ;每单步中断一次,eax加1

                                                ;所以eax最后不等于3,而是

                                                ;中断4次后,eax==7        

@finish_singlestep:        

        mov     dword ptr[esp+7*4],0            ;eax==0  hehe...

        popad                                 ;研究一下pushad指令就明白了

        ret

@@:

        mov     dword ptr[esp+7*4],1            ;eax==1

        popad    

        ret

singlestep_xHandler ENDP

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

END       _Start

原后记:    

最近很郁闷,生活上的工作上的,就这样懒懒懒散散地目无光彩地苟活于世,最近看了一点C++,有的地方头大,于是拿起老家伙asm写了点以前许诺过的东西,感觉还是ASM最有助于理解基本原理~~~不过C/C++给了我们另外的工具,虽然目标代码不够紧凑,毕竟不需要我们每个人写内核,C/C++可以提高生产能力. crack也一样,如果一些基本概念都不懂明白还谈什么crack?昨天看精华III一些高手的文章,真是惭愧啊!

我的专业不是计算机或者以后永远也不会搞计算机,这些当也许只是业余爱好,永远不会放弃的业余爱好.以后也许很长一段时间里面我会离开大家,毕竟,面临的还有生活.

由于有关seh的文档资料比较少,所以有了这篇学习心得,在看到这里的时候如果前面的已经完全理解相信seh对我们而言不会再是什么难题,由于seh是win32通用的错误处理方法Nt/2K/Xp仍然支持但某些细节或许有所改变,这需要你自己的研究.

写这点笔记的过程也是我自己学习的一个过程,由于时间紧张,乱七八糟还算写完了感谢你能耐心看到这里,如果有什么错误或者你有什么好的想法,请不要忘了告诉我.

作任何事情也许都要善始善终,由于前一段时间比较郁闷,曾一度想放弃.感谢hying,夜月,破解勇,fpc,FiNALSErAPH(这次拼对了?呵呵...),皮皮,dREAMtHEATER等还有许多其他朋友,没有你们的鼓励和督促,我不会坚持写到最后.

2002.2 重新整理

参考资料:

1.      Jeffy Ritcher  <<windows高级编程指南>>  tsinghua  press

2.      Matt Pietrek 1996 MSJ<<A Crash Course on the Depths of Win32? Structured Exception Handling>>

3.      Jeremy Gordon <<Win32 Exception handling for assembler programmers>>

4.      owl和EliCZ等asm高手的汇编源代码

5.      others I can’t remember

                                                     QQ: 8709369

                                                         humewen@21cn.com

                                                         humewen@263.net

                                                         humeasm.yeah.net

                                                         hume.longcity.net

                                                         hume/冷雨飘心

                                                               2002.1写毕

                                                               2002.2 整理

                                                         (转贴请保持完整)
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
RED_spring
驱动中牛
驱动中牛
  • 注册日期2002-07-28
  • 最后登录2016-11-06
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望19点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
  • 社区居民
地下室#
发布于:2004-05-22 13:13
太好了! 收藏之。
游客

返回顶部