结构异常处理之一 异常分发 SEH


   Exception Dispatching
   异常分发:
   当一个硬件或软件的异常发生时,处理机终止异常进程的执行并将控制权转交给操作系统。首先,操作系统将描述异常的信息和当前线程的机器状态保存起来。然后,系统尝试着去找一个异常处理程序来处理这个异常。
   产生异常的线程的机器状态字被保存在一个CONTEXT结构中。如果这个异常被成功处理,那么CONTEXT结构(称作环境记录)中的信息就能让操作系统在异常产生的地方继续执行。而描述一个异常的信息(也称异常记录)被保存在EXCEPTION RECORD的结构中。由于将与机器相关的环境记录和与机器无关的异常信息分开存储,使得异常处理机制就能够在不同的平台上移植。
   环境记录和异常记录的信息能够被GetExceptionInformation函数使用,也能被任何异常处理程序使用,作为异常处理程序执行后的结果;异常记录包含的信息如下所示:
   1)异常码,用于确定异常的类型;
   2) 异常标志位,标示异常事件是否是连续的。任何尝试都将在一个不可开拓的异常后引发一个新的异常。                                         说明: 如在异常标志中包含了EXCEPTION_NONCONTINUABLE标志位时,如果异常处理函数返回的值为EXCEPTION_CONTINUE_EXECUTION,这样,只会引发另一个异常; 
   3)指向另一个异常纪录的指针。如果异常嵌套着产生,那么将产生一异常链接表。(链表) 
   4)异常发生的地址
   5)一个用于提供异常附加信息的32位参数的数组。
   typedef struct _EXCEPTION_RECORD { // exr 
       DWORD ExceptionCode; 
       DWORD ExceptionFlags; 
       struct _EXCEPTION_RECORD *ExceptionRecord; 
       PVOID ExceptionAddress; 
       DWORD NumberParameters; 
       DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; 
   } EXCEPTION_RECORD; 
  
   当在用户模式代码中有异常产生时,系统使用下面的搜索顺序来查找一个异常处理函数:
   1.如果进程正在被调试,系统将通知调试器。更多的信息,请参考Debugger Exception Handling
   2.如果进程没有被调试,或者是和它关联的调试器并没有处理异常,操作系统通过搜索产生异常的线程堆栈结构(也称为栈帧)来试图定位一个基于框架的异常处理程序;操作系统首先查找当前堆栈结构,然后逆序把堆栈结构仔细的搜寻一遍;
   3.如果没有基于框架的处理程序被发现,或者没有基于框架的处理程序处理异常,但是进程正在被调试,那么操作系统将再一次的通知调试器;
   4.如果进程没有被调试,或者是和它关联的调试器没有处理异常,操作系统就根据异常类型提供默认处理。对于大多数而言,默认的处理是调用ExitProcess函数;
   当一个异常产生在内核模式代码中时,操作系统查找内核堆栈结构试图定位一个异常处理程序。如果异常处理程序没有被定位或是没有异常处理程序处理这个异常,好像ExitWindows函数将被调用,操作系统被关闭;
   Debugger Exception Handling
   调试器异常处理
   用户模式异常的处理方式为高级调试器提供了很好的支持。如果产生异常的进程正在被调试,操作系统就产生一调试事件。如果调试器是用WaitForDeBugEvent函数,调试事件将导致这个函数返回一指向DEBUG EVENT结构的指针。这个结构包含了进程,线程标识符和调试器能访问的环境记录(CONTEXT结构体)。这个结构还包含一个EXCEPTION DEBUG INFO结构,此结构包含一个异常记录(EXCEPTION_RECORD 结构体)的拷贝。
   当操作系统找到一个异常处理程序时,它尝试着通知进程的调试器两次。第一次通知会提供给调试器一个处理断点或单步调试异常的机会。这被认为是首次机会通知。在这时,用户能在任何异常处理函数完成之前,通过输入调试命令来巧妙的处理线程环境。第二次通知调试器当且仅当操作系统不能找到基于框架的异常处理函数来处理这个异常时才会发生。如果调试器在最后一次收到通知时都不进行异常处理,系统就终止正在被调试的进程;
   设置了每一个通知后,调试器使用ContinueDebugEvent函数来将控制权返回给系统,在控制权返回给系统之前,调试器能处理异常并修改合适的线程状态,它同样可以选择不处理异常。用ContinueDebugEvent 函数,调试器能指出它要处理的异常,在这种情况下,机器状态字被恢复,线程继续执行。调试器同样能指出它不处理的异常,在这种情况下,系统将继续为它查找异常处理程序。
  
   附录:CONTEXT结构,MSDN中提示在winnt。H文件中定义:
   typedef struct _CONTEXT {
  
       //
       // The flags values within this flag control the contents of
       // a CONTEXT record.
       //
       // If the context record is used as an input parameter, then
       // for each portion of the context record controlled by a flag
       // whose value is set, it is assumed that that portion of the
       // context record contains valid context. If the context record
       // is being used to modify a threads context, then only that
       // portion of the threads context will be modified.
       //
       // If the context record is used as an IN OUT parameter to capture
       // the context of a thread, then only those portions of the thread's
       // context corresponding to set flags will be returned.
       //
       // The context record is never used as an OUT only parameter.
       //
  
       DWORD ContextFlags;
  
       //
       // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
       // set in ContextFlags.   Note that CONTEXT_DEBUG_REGISTERS is NOT
       // included in CONTEXT_FULL.
       //
  
       DWORD    Dr0;
       DWORD    Dr1;
       DWORD    Dr2;
       DWORD    Dr3;
       DWORD    Dr6;
       DWORD    Dr7;
  
       //
       // This section is specified/returned if the
       // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
       //
  
       FLOATING_SAVE_AREA FloatSave;
  
       //
       // This section is specified/returned if the
       // ContextFlags word contians the flag CONTEXT_SEGMENTS.
       //
  
       DWORD    SegGs;
       DWORD    SegFs;
       DWORD    SegEs;
       DWORD    SegDs;
  
       //
       // This section is specified/returned if the
       // ContextFlags word contians the flag CONTEXT_INTEGER.
       //
  
       DWORD    Edi;
       DWORD    Esi;
       DWORD    Ebx;
       DWORD    Edx;
       DWORD    Ecx;
       DWORD    Eax;
  
       //
       // This section is specified/returned if the
       // ContextFlags word contians the flag CONTEXT_CONTROL.
       //
  
       DWORD    Ebp;
       DWORD    Eip;
       DWORD    SegCs;               // MUST BE SANITIZED
       DWORD    EFlags;              // MUST BE SANITIZED
       DWORD    Esp;
       DWORD    SegSs;
  
       //
       // This section is specified/returned if the ContextFlags word
       // contains the flag CONTEXT_EXTENDED_REGISTERS.
       // The format and contexts are processor specific
       //
  
       BYTE     ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
  
   } CONTEXT;
==============================================
 

这些参数是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意义如下:

  pExcept: --- EXCEPTION_RECORD结构的指针

  pErr:  --- 前面ERR结构的指针

  pContext: --- CONTEXT结构的指针

  pDispatch:---没有发现有啥意义

  ERR结构是前面介绍的_EXCEPTION_REGISTRATION结构,往前翻翻,Dispatch省略,下面介绍

  EXCEPTION_RECORD和CONTEXT结构的定义:

;//================================以下是两个成员的详细结构========================================
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里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:

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即内存异常时含义如下

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

  第二个dword 读写冲突地址

 

 

;//================================解释结束========================================
off.
CONTEXT STRUCT          ; _
ContextFlags DWORD   ?   ; |      +0
iDr0     DWORD   ?   ; |      +4
iDr1     DWORD   ?   ; |      +8
iDr2     DWORD   ?   ; >调试寄存器 +C
iDr3     DWORD   ?   ; |      +10
iDr6     DWORD   ?   ; |      +14
iDr7     DWORD   ?   ; _|      +18
FloatSave  FLOATING_SAVE_AREA <> ;浮点寄存器区 +1C~~~88h
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
;//================================以上是两个成员的详细结构========================================
 
      EXCEPTION_POINTERS STRUCT
pExceptionRecord DWORD   ?      
ContextRecord  DWORD   ?
EXCEPTION_POINTERS ENDS

  在call xHandler之前,堆栈结构如下:

  esp  -> *EXCEPTION_RECORD

  esp+4 -> *CONTEXT record ;//具体结构见下面

  然后执行call _Final_Handler,这样在程序里要调用什么不轻而易举了吗?

  II、 传递给per_thread句柄的参数,如下:

  在call xHandler之前,在堆栈中形成如下结构

      esp  -> *EXCEPTION_RECORD
esp+4 -> *ERR          ;//注意这也就是fs:[0]的指向
esp  -> *CONTEXT record     ;//point to registers
esp  -> *Param          ;//呵呵,没有啥意义

  然后执行 call _Per_Thread_Handler

  调用handler的原型是这样

  invoke HANDLER,*EXCEPTION_RECORD,*_EXCEPTION_REGISTRATION,*CONTEXT record,*Param

  即编译代码如下:

    PUSH *Param          ;//通常不重要,没有什么意义
push *CONTEXT record      ;//上面的结构
push *ERR           ;//the struc above
push *EXCEPTION_RECORD     ;//see above
CALL HANDLER
ADD ESP,10h

你可能感兴趣的:(职场,结构,休闲,seh,分发)