为栈页设置PAGE_GUARD后无法触发自定义异常处理的问题
栈GUARD页异常无法捕获的原因与解决方案
你的推测完全正确:Windows会静默处理栈自动扩展相关的GUARD页违规异常,不会把这类异常抛给用户态的异常处理程序——不管是SetUnhandledExceptionFilter还是AddVectoredExceptionHandler都拿不到。
为什么你的代码没触发异常?
Windows的栈自动扩展机制本身就依赖GUARD页:线程栈创建时,内核会在栈的末端设置一个GUARD页。当程序访问这个GUARD页时,内核会自动完成以下操作,全程对用户态透明:
- 移除该页的GUARD属性,转为普通读写页
- 若栈还没达到设定的最大值,分配新的栈页并在新末端设置GUARD页
- 让程序继续执行,不会把异常传递给用户态的异常处理链
你的代码里访问的stackTop - 0x1500属于未被程序使用过的栈区域,内核把这次访问当成了栈扩展请求,直接处理掉了异常,所以你的异常处理函数根本不会被调用。
能不能捕获这类异常?
用户态的限制
用户态下抓不到栈扩展触发的GUARD页异常——内核在用户态异常处理逻辑之前就把异常处理完了,异常根本不会进入用户态的异常处理流程。
能捕获的场景
如果你给已经被程序使用过的栈页手动加上GUARD属性,访问这类页时就会触发用户态异常。比如修改你的代码,先使用某个栈地址,再给对应页加GUARD:
#include <stdio.h> #include <Windows.h> #include <assert.h> LONG WINAPI ExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo) { printf("捕获到异常代码: 0x%lx\n", ExceptionInfo->ExceptionRecord->ExceptionCode); // 移除GUARD属性,让程序可以继续执行 MEMORY_BASIC_INFORMATION mbi; VirtualQuery((LPCVOID)ExceptionInfo->ExceptionRecord->ExceptionAddress, &mbi, sizeof(mbi)); DWORD oldProtect; VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &oldProtect); return EXCEPTION_CONTINUE_EXECUTION; } int main(void) { AddVectoredExceptionHandler(1, ExceptionHandler); // 先在栈上创建变量,确保该页被标记为已使用 DWORD usedStackVar = 0; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(&usedStackVar, &mbi, sizeof(mbi)); // 给这个已使用的栈页添加GUARD属性 DWORD savedProtect = 0; if(!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE | PAGE_GUARD, &savedProtect)){ fprintf(stderr, "[Error]: 无法添加GUARD页: %ld\n", GetLastError()); return 1; } // 访问已使用的栈页,会触发异常处理程序 usedStackVar = 0xdeadbeef; printf("usedStackVar的值: 0x%x\n", usedStackVar); return 0; }
要捕获栈扩展异常怎么办?
如果一定要捕获栈扩展触发的GUARD页异常,唯一的办法是写内核模式驱动,用KeRegisterExceptionCallback注册内核异常回调,在内核处理异常之前捕获STATUS_GUARD_PAGE_VIOLATION(0x80000001)。用户态没有任何途径能拿到这类异常。
内容的提问来源于stack exchange,提问作者Louis Kronberg
相关产品推荐
相关产品推荐

