Windows Exception Handling
VEH#
-
优先级最高
-
进程级别,跨线程有效
// 在主线程注册VEH AddVectoredExceptionHandler(0, GlobalHandler); // 在工作线程中发生的异常也会被处理 Create(NULL, 0, WorkerThread, NULL, 0, NULL); -
可以注册多个,形成处理链
PVOID handler1 = AddVectoredExceptionHandler(0, Handler1); PVOID handler2 = AddVectoredExceptionHandler(0, Handler2); // 执行顺序: Handler1 -> Handler2 (如果 Handler1 返回 CONTINUE_SEARCH) -
储存在堆内存中
- 不依赖栈帧,即使栈损坏也能工作
- 适合处理栈溢出等严重异常
SEH#
-
基于栈帧,线程布局
__try { *((int*)0) = 42; } __except(EXCEPTION_EXECUTE_HANDLER) { printf("Access violation caught\n"); } -
嵌套结构,就近原则
void OuterFunction() { __try { InnerFunction(); } __except (EXCEPTION_EXECUTE_HANDLER) { printf("Outer handler\n"); // executes second } } void InnerFunction() { __try { *((int*)0) = 42; } __except (EXCEPTION_EXECUTE_HANDLER) { printf("Inner handler\n"); // executes first } } -
编译器集成,语法简洁
UEF (Unhandled Exception Filter)#
-
全局唯一,最后防线
LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = SetUnhandledExceptionFilter(MyUEF); // 新的 UEF 会覆盖旧的UEF -
返回值直接决定程序生死
LONG NTAPI UEFHandler (struct _EXCEPTION_POINTERS* ExceptionInfo) { if (CanRecover (ExceptionInfo)) { FixTheException (ExceptionInfo); return EXCEPTION_CONTINUE_EXECUTION; // 程序继续运行 } else { GenerateCrashDump (ExceptionInfo); return EXCEPTION_EXECUTE_HANDLER; // 程序终止 } } -
不受调试器影响
- 即使在调试器下运行,UEF 仍然会被调用
- 适合实现产品级的崩溃报告系统
VCH (Vectored Continue Handler)#
它的名字比较有迷惑性。VCH 的执行在所有其它异常处理程序之后,不管前面的异常处理结果如何都不影响它的执行,即使程序崩溃,它也会发出最后的波纹。
-
无条件调用
// 无论异常是否被处理,VCH 都会被调用 LONG NTAPI AlwaysCalledHandler (struct _EXCEPTION_POINTERS* ExceptionInfo) { printf("This ALWAYS executes\n"); return EXCEPTION_CONTINUE_SEARCH; } -
主要用于监控和清理
LONG NTAPI MonitoringContinueHandler (struct _EXCEPTION_POINTERS* ExceptionInfo) { // 记录所有异常,包括已被处理的 LogExceptionToFile (ExceptionInfo); // 更新性能计数器 IncrementExceptionCounter(); // 不影响程序执行 return EXCEPTION_CONTINUE_SEARCH; } -
可以注册多个,形成处理链
VCH 不是为了“挽救”失败的异常处理而设计的,而是为了:
- 监控和日志记录
- 记录所有异常,包括致命的、已被处理的
- 统计异常发生频率和类型
- 资源清理和数据保存
- 在程序崩溃前保存重要数据
- 清理可能导致资源泄露的对象
- 调试和诊断
- 在异常处理完成后执行额外的调试逻辑
- 收集诊断信息
SEH Function Definition:
EXCEPTION_DISPOSITION __cdecl _except_handler(
_In_ struct _EXCEPTION_RECORD *_ExceptionRecord, // 异常记录结构 指针
_In_ void * _EstablisherFrame, // 指向 SEH 链(EXCEPTION_REGISTRATION)
_Inout_ struct _CONTEXT *_ContextRecord, // Context 结构指针,即线程上下文(寄存器)
_Inout_ void * _DispatherContext // 调度器上下文,无意义
)
UEF, VEH, VCH 函数的参数只有一个,即指向结构体 _EXCEPTION_POINTERS 的指针。
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord; // pointer of EXCEPTION_RECORD
PCONTEXT ContextRecord; // pointer of CONTEXT
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; // 异常号
DWORD ExceptionFlags; // 异常标志
struct _EXCEPTION_RECORD *ExceptionReccord; // 指向 **下一个** 节点,即下一个异常记录
PVOID ExceptionAddress; // 发生异常的地址
DWORD NumberParameters; // 异常信息的个数(数组ExceptionInformation的长度)
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; // 异常信息数组
} EXCEPTION_RECORD, *PECEPTION_RECORD;
线程上下文,没有细看
/* 浮点寄存器 */
typedef struct _FLOATING_SAVE_AREA {
DWORD ControlWord;
DWORD StatusWord;
DWORD TagWord;
DWORD ErrorOffset;
DWORD ErrorSelector;
DWORD DataOffset;
DWORD DataSelector;
BYTE RegisterArea[SIZE_OF_80387_REGISTERS];
DWORD Spare0;
} FLOATING_SAVE_AREA;
typedef FLOATING_SAVE_AREA *PFLOATING_SAVE_AREA;
typedef struct _CONTEXT {
//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
typedef CONTEXT *PCONTEXT;
一些使用方法/API#
SEH#
__asm
{
push HandlerAddr;
mov eax, fs:[0];
push eax;
mov fs:[0], esp;
}
或者 __try/__except
Others#
SetUnhandledExceptionFilter
AddVectoredEcceptionHandler(ULONG first, // 1 表示添加到链表头部,0 表示添加到链表i尾部
PVECTORED_EXCEPTION_HANDLER handler // 异常处理器的地址
);
RemoveVectoredExceptionHandler
AddVectoredContinueHandler
RemoveVectoredContinueHandler
LONG PvectoredExceptionHandler(
_EXCEPTION_POINTERS *ExceptionInfo
)
{
...
}
异常处理程序的参数#
typedef struct _EXCEPTION_POINTERS
{
PEXCEPTION_RECORD ExceptionRecord; // 记录异常的信息
PCONTEXT ContextRecord; // 表示寄存器的内容
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
返回值#
EXCEPTION_CONTINUE_EXECUTION 表示继续执行;
EXCEPTION_CONTINUE_SEARCH 表示继续查找
SEH 的返回值 (VEH, VCH 只有前两种)#
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
__except & SEH#
__except 是对 _except_handler 函数的封装
EH 调用顺序#
调试器 > VEH > SEH > UEF > VCH > 调试器 > 调用异常端口通知 csrss.exe (弹出一个对话框, 但在Win11没有见到, 可能和系统版本有关)
#include<Windows.h>
#include<iostream>
EXCEPTION_DISPOSITION myExceptHandler(
struct _EXCEPTION_RECORD* ExceptionRecord,
PVOID EstablisherFrame,
PCONTEXT pcontext,
PVOID DispatcherContext
)
{
MessageBoxA(0, "SEH处理了异常", "异常报错", MB_OK);
pcontext->Eip = pcontext->Eip+ 4;
return ExceptionContinueExecution;
}
LONG WINAPI PvectoredExceptionHandler(
_EXCEPTION_POINTERS* ExceptionInfo
)
{
MessageBoxA(0, "VEH处理了异常", "异常处理", MB_OK);
ExceptionInfo->ContextRecord->Eip += 4;
return EXCEPTION_CONTINUE_EXECUTION;
}
int main()
{
AddVectoredExceptionHandler(1, PvectoredExceptionHandler);
DWORD execptionFuncAddr = (DWORD)myExceptHandler;//异常处理的函数地址
__asm
{
push execptionFuncAddr;
mov eax, fs: [0] ;
push eax;
mov fs : [0] , esp;
//添加异常,并且把异常放到异常链里面,并且把异常的头指针指向新的头结点
}
char* str = NULL;
str[0] = 'a';
printf("Sna1lGo\n");
system("pause");
return 0;
}
Read other posts