文章总结: 文档介绍Windows向量化异常处理(VEH)机制,其具有进程全局性和介入时机早的特点,优先于传统SEH执行。文章详细讲解了注册与移除VEH的API函数、异常处理函数原形及返回值含义,并通过C++代码示例演示了如何捕获除零与内存访问异常。此外,还探讨了VEH在无痕HOOK等高级场景中的应用潜力。 综合评分: 88 文章分类: 二进制安全,逆向分析,免杀
Windows VEH 介绍
原创
MyStackTrace MyStackTrace
MyStackTrace
2026年1月22日 23:29 北京
Windows 上的 VEH 全称是 Vectored Exception Handling,即向量化异常处理,它是 Windows 系统提供的一种全局的异常处理机制,允许程序在发生特定异常时执行自定义的处理逻辑。
当程序在运行中发生异常(如除零、访问违规内存)时,CPU 会捕获该异常并交由 Windows 内核处理,内核然后将异常信息传递给用户态的异常分发函数,异常分发函数会按照特定顺序寻找能处理该异常的代码 :
-
首先询问调试器(如果进程正在被调试)。
-
然后按顺序调用所有已注册的 VEH 处理函数。
-
如果 VEH 均未处理,最后才会轮到线程相关的结构化异常处理(Structured Exception Handling,SEH)。
所以,VEH 和传统的 SEH 相比有两个显著的特点:
- 全局性:与线程相关的结构化异常处理(SEH)不同,VEH 是进程级别的,这意味着通过 VEH 注册的异常处理函数可以对整个进程内发生的特定异常做出响应,而不依赖于单个线程的调用栈。
- 介入时机早:当程序发生异常时,系统的异常分发器会按照一个特定的顺序来寻找异常处理程序。通常,VEH 会先于传统的 SEH 被调用(除非进程正在被调试,调试器会最先获得机会)。在所有已注册的 VEH 处理函数中,可以通过注册参数指定其被调用的先后顺序。
VEH 的异常处理函数有固定的原型,它接收一个 EXCEPTION_POINTERS 结构体指针作为参数,该结构体包含了详细的异常记录和发生异常时的线程上下文(Context)信息。
我们可以通过函数 AddVectoredExceptionHandler 注册 VEH 异常处理函数,通过函数 RemoveVectoredExceptionHandler 取消已注册的 VEH 异常处理函数 。
- 参数 First:指定处理函数的调用顺序。如果为非零值,则将该处理函数作为第一个处理函数;如果为零,则作为最后一个处理函数。
- Handler:指向异常处理函数的指针(PVECTORED_EXCEPTION_HANDLER)。
- 返回值:返回一个句柄,用来取消已经注册成功的 Hander。
- 参数 Handle:使用 AddVectoredExceptionHandler 注册 VEH 异常处理函数成功时返回的句柄。
下面是个简单的例子程序,我在程序中故意执行了一个除零操作和一个访问空指针的操作,除零操作会触发 Divide By Zero Exception,访问空指针会触发 Access Violation Exception,通常情况我们的程序触发了这些异常就会立刻崩溃,但是下面的程序使用了 try-except 语句块把会触发异常的代码给圈了起来,并且又注册了 VEH 异常处理函数,因此该程序在经历两次异常处理之后仍然不会崩溃。我们可以根据 VEH 异常处理函数的 EXCEPTION_POINTERS 参数中的信息判断出来这是个什么类型的异常,并且还能够从中获取到该异常相关的信息,比如异常发生的地址,上下文寄存器等。
#include <Windows.h>#include <stdio.h>
LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo){ switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: printf("Access violation exception caught at address: 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress); printf("Attempt to %s at address: 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionInformation[0] ? "write" : "read", (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1]); return EXCEPTION_CONTINUE_SEARCH;
case EXCEPTION_INT_DIVIDE_BY_ZERO: printf("Integer divide by zero exception caught at address: 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress); ExceptionInfo->ContextRecord->Rax = 0; return EXCEPTION_CONTINUE_SEARCH;
case EXCEPTION_STACK_OVERFLOW: printf("Stack overflow exception caught\n"); return EXCEPTION_CONTINUE_SEARCH;
default: printf("Unhandled exception code: 0x%08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode); return EXCEPTION_CONTINUE_SEARCH; }}
int CauseDividedByZeroException(int a){ return a / 0; // This will cause a divide by zero exception}
void CauseAccessViolationException(){ int *p = NULL; *p = 1; // This will cause an access violation exception}
int main(int argc, char *argv[]){ PVOID handlerHandle = AddVectoredExceptionHandler(1, VectoredExceptionHandler); if (handlerHandle == NULL) { printf("Failed to install vectored exception handler. Error: %lu\n", GetLastError()); return 1; }
printf("Vectored exception handler installed successfully\n");
__try { printf("%d\n", CauseDividedByZeroException(10)); } __except(EXCEPTION_EXECUTE_HANDLER) { printf("Divide by zero exception handled via __except block\n"); }
__try { CauseAccessViolationException(); } __except(EXCEPTION_EXECUTE_HANDLER) { printf("Access violation handled via __except block\n"); }
printf("This program exit successfully\n");
return 0;}
下面是这个程序的运行效果,可以看到当程序执行除零的代码和访问空指针的代码时,程序都会跳转到我们注册的 VEH 处理函数(VectoredExceptionHandler)中进行对应的处理,最终程序还是正常退出了。
在上面的 VEH 异常处理函数中,我们的返回值都是 EXCEPTION_CONTINUE_SEARCH,表示当前 VEH 处理程序不处理此异常,系统应继续调用 VEH 链中的下一个处理程序(如果还有的话),如果所有 VEH 处理程序都返回此值,则系统会继续按正常流程处理异常(即进入 SEH 链)。此外还有两个异常处理函数的返回值 EXCEPTION_EXECUTE_HANDLER 和 EXCEPTION_CONTINUE_EXECUTION。EXCEPTION_EXECUTE_HANDLER 表示当前 VEH 处理程序已经处理了异常,并且系统应该停止搜索其他处理程序(包括 VEH 和 SEH),然后,系统会认为异常已处理,并继续执行原程序流程,但不会回到异常发生点。EXCEPTION_CONTINUE_EXECUTION 表示异常已被当前 VEH 处理程序处理,并且系统应该从异常发生的位置继续执行,处理程序必须确保导致异常的条件已经得到修正,否则会再次触发异常,比如上面这个例子,就不能返回 EXCEPTION_CONTINUE_EXECUTION,因为我们的 VEH 异常处理函数并没有修复异常,仅仅是打印了一些信息而已,如果返回 EXCEPTION_CONTINUE_EXECUTION 会导致这个程序不停的触发异常,陷入死循环中。
VEH 的强大之处在于其全局性和介入时机早,因此被广泛应用于一些特定领域,比如实现无痕 HOOK,这是 VEH 一个非常经典和强大的应用,通过在目标函数入口插入一个断点指令(如 int 3),当函数被调用触发断点异常时,VEH 处理函数会捕获这个异常。在处理函数中,可以执行自定义代码(如记录日志、修改参数等),然后修改线程上下文(如 EIP/RIP 寄存器)使其跳转到钩子函数或直接返回,从而实现拦截和监控,而无需像传统 inline hook 那样修改大量原始代码。这些有意思的应用我们后面可以来尝试一下😁。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:MyStackTrace MyStackTrace MyStackTrace《Windows VEH 介绍》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论