VEH(Vectored Exception Handling)
摘 要:针对于windows的异常处理,已存在多种方式,VEH是后来出现的,相对晚一些。但是,其作用不可忽视,弥补了其它异常处理所存在的不足。
关键词:异常处理;VEH;SEH
概述
目前 Windows 平台下实现和使用的异常处理机制主要有 4 种:
筛选器异常处理,结构化异常处理(Structure Exception Handler, SEH),向量化异常处理(Vectored Exception Handler, VEH),C++异常处理(C++ Exception Handler, C++EH)。
其中,筛选器异常处理是通过异常回调函数来指定程序处理异常。这种方式的回调函数必须是唯一的,设置新的回调函数后以前的将失效。适用于进程范围。
前3 种是由 Windows 操作系统实现的异常处理机制, C++EH 是由 C++编译器实现的异常处理机制。
的不足
SEH(Structure Exception Handler,结构化异常处理)的具体实现原理这里就不做介绍了,课本上介绍的十分详细。这种异常处理方式的缺点十分明显,这里做下简单介绍。
最后安装的SEH处理例程总是优先得到控制权,这有时并不是最好的解决方案,但确实是SEH的工作机制。当然Final型的或称top型的(即:通过 SetUnHandledExceptionFilter安装的)例外,因为其不允许嵌套。我们现在所提到的是线程相关的,也就是per_Thread类型的。之所以不是最好的解决方案,原因如下:设想一下,假如你用两周写了一个异常完美的SEH处理例程,能够完美处理所有异常,并希望异常全部由你来处理,但很不幸的是,比如你调用了一个外部模块,而这个模块自己安装了一个ugly的SEH处理例程,他的动作是只要有异常发生就简单地终止程序。可想而知,这将意味着怎样的结果。你的两周工作全部付诸东流,完全没有起到一丁点儿的作用。
函数介绍
在XP下,Microsoft又提供了一种异常处理,即 VEH(Vectored Exception Handling,向量异常处理)。这种方式的异常处理用如下API注册,类似于SEH,也是一个链状结构,但是二者存在不同,却又有些类似。
VEH通过使用 Win32 API 函数 AddVectoredExceptionHandler可注册新的异常处理函数,函数的参数就是指向 EXCEPTION_POINTERS 结构的指针。同时,增加了函数地址的注册处理程序链表。由于系统中使用一个链表来存储矢量异常处理程序,程序可以安装尽可能多的向量处理器,只要有必要。
在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历 VEH 记录链表的函数, 如果没有找到可以处理异常的注册函数,再开始遍历 SEH 注册链表。
函数具体介绍如下:
* WINBASEAPI PVOID WINAPI AddVectoredExceptionHandler (
ULONG FirstHandler,
PVECTORED_EXCEPTION_HANDLER VectoredHandler);
该函数在WINBASE.H进行声明。
FirstHandler:是一个标志,可以指定是否将你的VEH处理例程放在VEH链的最前面。如果FirstHandler参数为非零值,新的处理程序节点插入到列表的头部,否则新的节点出现在列表的尾部。该处理程序列表不依赖于任何线程,是全局进程。因此,尽管可以要求在该处理程序列表的开头处被声明,但却不能保证是第一个被调用的。如果存在AddVectoredExceptionHandler代码的其他作品在其之后被定义在列表清单的开头处,并要求第一个处理程序,那么,你所要求的向量异常处理将不会被第一个使用。
VectoredHandler:这是异常处理例程入口,是异常处理函数的地址。
返回注册的VEH句柄,后面卸载的时候要用到。
* LONG NTAPI VectoredExceptionHandler(PEXCEPTION_POINTERS);
PEXCEPTION_POINTERS是指向EXCEPTION_POINTERS的指针,与SEH中FINAL型的 EXCEPTION_POINTERS结构是一致的。
PEXCEPTION_POINTERS参数是一个指针,此参数可给出相关异常的一些信息,包括异常的类型、地址和寄存器的值。
的执行
在Windows XP的Beta 2的向量异常处理实现非常简单。虽然表面上 AddVectoredExceptionHandler在中出现,它实际上只是间接传递在ntdll文件中 RtlAddVectoredExceptionHandler的功能。
异常处理程序的向量列表存储形式是一个循环链表。每个注册的异常处理程序的表现形式是由进程堆分配一个12 字节的节点。必须关注一个关键部分代码,即:在实际插入的头部或尾部处理程序的清单。因为没有代码检查是否以前安装的处理程序地址已经被注册了,所以,有可能为同一地址登记处理程序超过一次。
和VEH联系
虽然二者看上去差不多,但却不一样。
区别如下所述:
1) AddVectoredExceptionHandler添加的异常处理句柄可以嵌套,而不是只能指定一个。
2) AddVectoredExceptionHandler可以指定你的异常处理句柄是否在链的最前面。
当然如果在你后面有人调用AddVectoredExceptionHandler也作同样指定,那么,你所设置的异常处理只能在其后面了。
相同之处如下所述:
1)二者是进程而不是线程相关的。
2)若所有均不处理异常,最后系统要进行展开,不过不会调用VEH例程。
二者之间具体联系:VEH优先权高于SHE,只有所有VEH全不处理某个异常的时候,异常处理权才会到达SHE。只要目标程序中没有利用VEH,那么,你所设计的VEH将是第一个得到控制者。现在采用SEH作为异常处理的普通C/C++程序对你将不会再有干扰,可以通过使用VEH来进行hook处理了。
5.相关问题注意
(1)VEH是在XP系统出现后开发的,所以,向量异常处理机制只适用于windows的较高版本。在设计这一处理机制的时候,要考虑观察实际的运行环境,视情况而定。
(2)向量异常处理与结构化异常处理共存的共存问题。当在Windows XP中出现异常时,异常处理程序列表中,VEH出现在SEH的名单之前,要尽量做好这项工程与现有代码的兼容性。如果异常在矢量异常处理名单范围之外,且在SEH的名单,那么 SEH的处理程序可能处理异常,矢量异常处理是不会有机会看到此次异常的。
(3)关于调试,如果存在调试器,那么控制权转向将会发生新的变化。当异常发生后,首先通知的将会是调试器,调试器不处理才会再返回控制权给VEH;如果VEH不处理,再返回给SHE;若SEH不处理,再给调试器一个机会,如果还不处理,则交由系统处理。
(4)在VEH回调处理例程中必须保护好寄存器,否则会引起莫名其妙的异常。
参考文献:
《计算机病毒分析与对抗》 付建明 武汉大学出版社