WEB开发网
开发学院软件开发C++ Win32结构化异常处理(SEH)探秘(上) 阅读

Win32结构化异常处理(SEH)探秘(上)

 2010-10-15 09:07:37 来源:Web开发网   
核心提示:此外,这个 CONTEXT 结构与 GetThreadContext 和 SetThreadContext API 函数使用的结构是相同的,Win32结构化异常处理(SEH)探秘(上)(2),_except_handler 回调函数的第四个参数是 DispatcherContext,现在也可以忽略它,这些信息足以让操作

此外,这个 CONTEXT 结构与 GetThreadContext 和 SetThreadContext API 函数使用的结构是相同的。

_except_handler 回调函数的第四个参数是 DispatcherContext。现在也可以忽略它。

为了简化起见,当异常发生时,你有一个回调函数被调用。此回调函数带四个参数,其中三个是结构指针。在这些结构中,某些域是很重要的,其余的不是那么重要。关键是 _except_handler 回调函数接收

很多信息,比如发生了什么类型的异常,在哪里发生的。利用这些信息,异常回调机制需要确定要做什么。

虽然我迫不急但地想抛出例子程序示范 _except_handler 回调的运行,但还有一些事情不能漏掉,需要说明。特别是当错误发生时,操作系统如何知道到哪里调用?答案仍然涉及另外一个结构 EXCEPTION_REGISTRATION。你将自始自终在本文中看到这个结构,所以不要掠过这部分内容。我能找到正式定义 EXCEPTION_REGISTRATION 结构的唯一地方是 EXSUP.INC 文件,该文件来自 Visual C++ 运行库的源:

_EXCEPTION_REGISTRATION struc
prev dd ?
handler dd ?
_EXCEPTION_REGISTRATION ends

你还将看到该结构在 WINNT.H 文件中定义的 NT_TIB 结构中被引用为 _EXCEPTION_REGISTRATION_RECORD。唉,除此之外,没有什么地方能找到 _EXCEPTION_REGISTRATION_RECORD 的定义,所以我只能使用 EXSUP.INC 文件中定义的汇编语言结构。这也是我为什么在本文前述内容中说过的 SEH 缺乏文档的一个例证。

不管怎样,让我们回到手头的问题,当某个异常发生时,OS 如何知道到哪里调用回调函数?EXCEPTION_REGISTRATION 由两个域构成,第一个你现在可以忽略。第二个域是句柄,它包含 _except_handler 回调函数的指针。这让你更接近一点了,但目前问题来了,OS 在哪里查找并发现 EXCEPTION_REGISTRATION 结构?

为了回答这个问题,回想一下结构化异常处理是以线程为基础,并作用在每个线程上,明白这一点是有助于理解的。也就是说,每个线程具备其自己的异常处理回调函数。在我1996年5月的专栏文章中,我描述了一个关键的 Win32 数据结构——线程信息块(即 TEB 和 TIB)。该数据结构的某些域在 Windows NT、Windows 95、Win32s 和 OS/2 平台上是一样的。TIB 中的第一个 DWORD 是指向线程 EXCEPTION_REGISTRATION 结构的指针。在 Intel Win32 平台上,FS 寄存器总是指向当前的 TIB。因此,在 FS:[0]位置,你能找到 EXCEPTION_REGISTRATION 结构的指针。

现在我们知道了,当异常发生时,系统检查出错线程的 TIB 并获取 EXCEPTION_REGISTRATION 结构的指针。这个结构中就有一个 _except_handler 回调函数的指针。这些信息足以让操作系统知道在哪里以及如何调用 _except_handler 函数,如图二所示:

图二 _except_handler 函数

通过前面的描述,我写了一个小程序来对操作系统层的结构化异常进行示范。程序代码如下:

 //==================================================
  // MYSEH - Matt Pietrek 1997
  // Microsoft Systems Journal, January 1997
  // FILE: MYSEH.CPp
  // To compile: CL MYSEH.CPp
  //==================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

DWORD scratch;

EXCEPTION_DISPOSITION
__cdecl
_except_handler(
   struct _EXCEPTION_RECORD *ExceptionRecord,
   void * EstablisherFrame,
   struct _CONTEXT *ContextRecord,
   void * DispatcherContext )
{
   unsigned i;

// Indicate that we made it to our exception handler
   printf( "Hello from an exception handlern" );

// Change EAX in the context record so that it points to someplace
   // where we can successfully write
   ContextRecord->Eax = (DWORD)&scratch;

// Tell the OS to restart the faulting instruction
   return ExceptionContinueExecution;
}

int main()
{
   DWORD handler = (DWORD)_except_handler;
   __asm
   {
     // 创建 EXCEPTION_REGISTRATION 结构:
     push handler
// handler函数的地址
     push FS:[0] 
// 前一个handler函数的地址
     mov FS:[0],ESp
// 装入新的EXECEPTION_REGISTRATION结构
   }
   __asm
   {
     mov eax,0
// EAX清零
     mov [eax], 1
// 写EAX指向的内存从而故意引发一个错误
   }
   printf( "After writing!n" );
   __asm
   {
     // 移去我们的 EXECEPTION_REGISTRATION 结构记录
     mov eax,[ESP]  
// 获取前一个结构
     mov FS:[0], EAX 
// 装入前一个结构
     add esp, 8
// 将 EXECEPTION_REGISTRATION 弹出堆栈
   }
   return 0;
}

上一页  1 2 3 4 5 6 7  下一页

Tags:Win 结构化 异常

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接