阅读:2458回复:4
Structured Exception Handling
Structured Exception Handling
1999 OSR Open Systems Resources, Inc. 董岩 译 greatdong_2001@163.com 直接操作用户模式地址或调用可能引起异常的 Windows NT 驱动程序必须处理所引起的异常。Windows NT 操作系统和 Visual C/C++ 提供了一种叫结构化异常处理(Structured Exception Handling,SEH)的机制来处理异常。 不对异常进行处理的驱动一般都会引起操作系统臭名昭著的 KMODE_EXCEPTION_NOT_HANDLED 蓝屏。一般这都是因为驱动访问了一个无效的用户地址。当然,如果驱动使用了 SEH,异常就能够得到处理,系统也就不会产生 bug check 了。 Visual C++ 使用关键字 __try 和 __except 来实现结构化异常处理。当为 Windows NT 构建驱动程序时,构建环境提供了关键字“try”和“except”的定义,将它们定义为了实际的 Visual C++ 关键字。这些简写的关键字可以用在 C 程序里,但是不能用在 C++ 程序中。在 OSR 我们通常编写 C 代码再保存为扩展名为“.cpp” 的文件来保证我们能得到 C++ 提供的更好的类型检查――但是我们就不能使用“try”和“except”关键字了,因为“try”实际上是 C++ 环境中的一个真正的语言关键字。遗憾的是,因为 C++ 异常处理机制与操作系统的异常处理机制并不相容,所以就不能在内核驱动中使用 C++ 的“try”关键字。 注意不能把我们这里讲到的 SEH model 和 termination handling model 相混淆。不幸的是,人们很容易将这两者混淆起来,因为 termination handling 用“__try”关键字来指示一个 termination 块的开始,用“__finally”关键字来指示是 termination 块。当试图确定资源都被正确释放时,Termination handling 就派上用场了。不管代码如何退出,termination 块中的代码都要被执行。但是,因为这与我们所讨论的异常处理无关,我们在以后的文章中再进行讨论。 在下面的 Figure 1 里我们给出了一个例子代码,这段代码在检查一个用户模式缓冲区以确定其有效性时使用了结构化异常处理: // // We\'ll be reading data from the user mode address. We should make certain it is // valid. // BOOLEAN OsrProbeForRead(Buffer, Length) { ULONG index; UCHAR dummyArg; PUCHAR effectiveAddress; // // Probe the input buffer by reading a byte from each page in the range - that\'s enough. // __try { for (index = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length); index; index--) { effectiveAddress = (PUCHAR) Buffer; effectiveAddress += ((index-1) * PAGE_SIZE); dummyArg = *effectiveAddress; } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint(\"Exception is 0x%x\\n\", GetExceptionCode()); return FALSE; } // // If we make it to here, the input buffer is valid. // return TRUE; } Figure 1 -- Structure Exception Hanlding 我们把这段代码看成一个受保护的块(__try 和 __except 之间的代码)和一个异常处理程序(__except(<expression>))之后的代码。这个例子中有几个地方需要注意: - __except 后的 expression 指示了当异常发生时应该采取什么行动。仅当 expression 的值为非 0 时才执行 __except () 后面的代码块。 - __except expression 范围中只能使用两个函数:GetExceptionInformation 和 GetExceptionCode。GetExceptionInformation 实际上调用了 __exception_info 函数,而 GetExceptionCode 则实际上调用了 __exception_code 函数。这两个函数提供了关于所发生异常的额外的信息。 - 并非所有的异常都能被捕捉到。这种情况下,操作系统仍然会出现 KMODE_EXCEPTION_NOT_HANDLED 的 bug check。 - 若被保护的块未发生异常,则不会调用异常逻辑。 正如我们在此例中演示的,驱动程序中的 SEH 的主要作用就是保护对用户模式缓冲区的保护。这是因为当访问无效地址时,操作系统中的页错误处理逻辑就会产生一个异常(STATUS_ACCESS_VIOLATION)。当然,其它的异常也会产生。确实,异常类型的整个“名字空间”有32位宽。 Windows NT 一般也将 status code 用作异常值。例如,驱动可以使用公开的 ExRaiseStatus() 函数来产生异常。注意这个函数的名字暗示甚至可以自己定义 status code! 异常处理是基于一个异常处理程序堆栈的。因此,编译器为 __try 异常生成的代码构建一个新的 exception registration 块,然后将它插入堆栈中。之后,如果有异常发生,操作系统先检查第一个 exception registration 块。这包括计算 __except 后 expression 的值再根据此值来采取行动。这个 expression 有三种有效的取值: - EXCEPTION_EXECUTE_HANDLER (1) |
|
沙发#
发布于:2005-03-12 09:48
不错
|
|
|
板凳#
发布于:2005-03-12 16:10
可惜没有针对内核异常的保护方法,不知道该如何来实现内核级别异常的保护呢?????
|
|
|
地板#
发布于:2005-04-08 22:47
可惜没有针对内核异常的保护方法,不知道该如何来实现内核级别异常的保护呢????? 大哥,Windows对内核异常的保护就是调用KeDbgCheck,蓝屏,嘿嘿,不过一般的Driver是没有catch内核异常的。 |
|
|
地下室#
发布于:2005-04-12 10:00
[quote]可惜没有针对内核异常的保护方法,不知道该如何来实现内核级别异常的保护呢????? 大哥,Windows对内核异常的保护就是调用KeDbgCheck,蓝屏,嘿嘿,不过一般的Driver是没有catch内核异常的。 [/quote] wowocock是指内核异常是如何转到调用KeBugCheck,或KeBugCheckEx这个函数弹出兰屏的,如发生IRQL不匹配,非法存取份页内存等 |
|
|