WindowsAPI查缺补漏-异常处理

结构化异常处理SEH

基本结构

1
2
3
4
5
6
7
__try {
//受保护语句
}
__except (异常过滤表达式) {
//异常处理语句
};
//其他语句

其中异常锅炉表达式可以是:

枚举值 含义
EXCEPTION_EXECUTE_HANDLER 执行except块内容并继续执行except块后其他语句,try块中剩下语句不执行
EXCEPTION_CONTINUE_SEARCH 不执行except块,继续向上搜索下一个具有最高优先级异常处理程序
EXCEPTION_CONTINUE_EXECUTION 不执行except块,重新执行发生异常的语句,慎用

GetExceptionCode

获取刚发生异常的异常代码,只能在异常过滤表达式或异常处理程序块中被调用。

1
2
3
DWORD GetExceptionCode(VOID);
#define GetExceptionCode _exception_code
unsigned long _cdecl _exception_code(void);

常见异常代码有:

异常 异常值 解释
EXCEPTION_ACCESS_VIOLATION 0xC0000005 程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008C 数组访问越界时引发的异常。
EXCEPTION_BREAKPOINT 0x80000003 触发断点时引发的异常。
EXCEPTION_DATATYPE_MISALIGNMENT 0x80000002 程序读取一个未经对齐的数据时引发的异常。
EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008D 如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。
EXCEPTION_FLT_DIVIDE_BY_ZERO 0xC000008E 浮点数除法的除数是0时引发该异常。
EXCEPTION_FLT_INEXACT_RESULT 0xC000008F 浮点数操作的结果不能精确表示成小数时引发该异常。
EXCEPTION_FLT_INVALID_OPERATION 0xC0000090 该异常表示不包括在这个表内的其它浮点数异常。
EXCEPTION_FLT_OVERFLOW 0xC0000091 浮点数的指数超过所能表示的最大值时引发该异常。
EXCEPTION_FLT_STACK_CHECK 0xC0000092 进行浮点数运算时栈发生溢出或下溢时引发该异常。
EXCEPTION_FLT_UNDERFLOW 0xC0000093 浮点数的指数小于所能表示的最小值时引发该异常。
EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001D 程序企图执行一个无效的指令时引发该异常。
EXCEPTION_IN_PAGE_ERROR 0xC0000006 程序要访问的内存页不在物理内存中时引发的异常。
EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094 整数除法的除数是0时引发该异常。
EXCEPTION_INT_OVERFLOW 0xC0000095 整数操作的结果溢出时引发该异常,实践证明该异常没有被支持。
EXCEPTION_INVALID_DISPOSITION 0xC0000026 异常处理器返回一个无效的处理的时引发该异常。
EXCEPTION_NONCONTINUABLE_EXCEPTION 0xC0000025 发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。
EXCEPTION_PRIV_INSTRUCTION 0xC0000096 程序企图执行一条当前CPU模式不允许的指令时引发该异常。
EXCEPTION_SINGLE_STEP 0x80000004 标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。
EXCEPTION_STACK_OVERFLOW 0xC00000FD 栈溢出时引发该异常。

异常代码命名有一定规则:

含义
31~30 严重性 0为Success 1为information 2为Warning 3为error
29 0为微软定义的 1为用户自定义的
28 0
27~16 设备代码 前256个值微软保留
15~0 异常代码

GetExceptionInformation

获取刚发生异常的相关信息,只能在异常过滤表达式中被调用。

1
2
3
LPEXCEPTION_POINTERS GetExceptionInformation();
#define GetExceptionInformation (struct _EXCEPTION_POINTERS*)_exception_info
void* __cdecl _exception_info();

其中EXCEPTION_POINTERS结构为:

1
2
3
4
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

EXCEPTION_RECODE和CONTEXT结构在“WindowsAPI查缺补漏-进程”的“进程调试”中有讲。

RaiseException

在调用线程中抛出一个软件异常:

1
2
3
4
5
6
VOID RaiseException(
_In_ DWORD dwExceptionCode, //异常代码
_In_ DWORD dwExceptionFlags, //异常标志
_In_ DWORD nNumberOfArguments, //lpArguments数组参数个数
_In_ CONST PULONG_PTR lpArguments //附加参数数组
);

通常调用函数失败通过返回错误代码,但也可以让函数抛出异常。CPU捕获某事件并抛出的异常为硬件异常,操作系统和应用程序抛出的异常为软件异常。

对于dwExceptionCode,用户可根据异常代码定义规则自定义。dwExceptionFlags为0表示程序可继续执行的异常,为EXCEPTION_NONCONTINUABLE表示不可继续执行的异常。nNumberOfArguments和lpArguments一般不用。

利用SEH反调试

所有异常处理都从内核底层异常处理程序开始,底层异常处理程序调用用户层异常处理程序,程序中每个函数都可以具有自己的异常处理程序。随着层层函数调用,所有异常处理程序形成一个SEH链表,try-except为在栈中构造一个SEH节点,最后加入的SEH节点位于SEH链表头部。每个线程都可以由自己的SEH链表,每个SEH节点为一个EXCEPTION_REGISTRATION_RECORD结构:

1
2
3
4
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next; //指向前一个SEH节点的本结构
PEXCEPTION_ROUTINE Handler; //异常处理程序0
} EXCEPTION_REGISTRATION_RECORD;

try-except在栈中构造上述结构到SEH链表头部用汇编描述:

1
2
3
4
5
6
push ExceptionHandler
push fs:[0] ;前一个SEH节点的EXCEPTION_REGISTRATION_RECORD
mov fs:[0], esp
//...
pop fs:[0]
pop eax

当一个异常发生时,如果产生异常的进程正在被调试,则向调试器发送EXCEPTION_DEBUG_EVENT事件;如果进程没有被调试或调试器不处理该异常,则调用用户层异常处理程序。例如以下反调试,但可利用反反调试插件如StrongOD的Skip Some Exceptions来跳过反调试。

1
2
3
4
5
6
7
8
9
BOOL CheckDebugging(VOID) {
__try {
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
};
return TRUE;
};

全局向量化异常处理VEH

简介

向量化异常处理VEH是对结构化异常处理SEH的扩展,是基于进程全局的。程序可注册一个函数来监视或处理该程序的所有异常,当进程中任何一个线程发生异常时都去调用该异常处理函数。

AddVectoredExceptionHandler

添加或注册一个向量化异常处理程序,所有向量化异常处理程序形成一个VEH链表。

1
2
3
4
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler, //调用异常处理程序的顺序
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler //异常处理回调函数指针
); //成功返回异常处理程序句柄 失败NULL

当FirstHandler非0则VectoredHandler指定的异常处理程序放在VEH链表头部,是第一个要调用的处理程序;为0则放在尾部,最后一个调用。

异常处理程序的指针类型为PVECTORED_EXCEPTION_HANDLER结构:

1
typedef LONG(NTAPI* PVECTORED_EXCEPTION_HANDLER)(struct _EXCEPTION_POINTERS* ExceptionInfo);

函数定义格式如下:

1
2
3
LONG CALLBACK VectoredHandler(
PEXCEPTION_POINTERS ExceptionInfo
);

发生异常时,系统在执行结构化异常过滤程序前,按照VEH链表顺序诸葛调用向量化异常处程序。如果某个异常处理程序可修复发生的问题则返回EXCEPTION_CONTINUE_EXECUTION使抛出异常的指令再次执行,VEH链表中其他异常处理程序和结构化异常过滤程序不会被执行;如果某个异常处理程序不能修复发生的问题则返回EXCEPTION_CONTINUE_SEARCH,使VEH链表中其他异常处理程序去处理,所有向量化异常处理程序都返回该值时结构化异常过滤程序执行;向量化异常处理程序不能返回EXCEPTION_EXECUTE_HANDLER。

RemoveVectoredExceptionHandler

删除向量化异常处理程序:

1
2
3
ULONG WINAPI RemoveVectoredExceptionHandler(
_In_ PVOID pHandler //AddVectoredExceptionHandler返回值
);

实例:利用VEH实现基于断点的API Hook

写一个DLL并注入到目标进程中,获取kernel32!LoadLibrary的字符串参数lpLibFileName即可,不需要导出函数,本节内容为x86。

LoadLibrary起始地址处设置一个int 3断点,当异常代码为EXCEPTION_BREAKPOINT,代表执行到int 3断点处,此时栈指针ESP指向地址为LoadLibrary函数返回地址,ESP+4指向地址是LoadLibrary函数模块名称字符串参数。先临时删除int 3断点,设置一个单步中断后返回EXCEPTION_CONTINUE_EXCEPTION来重新执行int 3异常的指令,执行后即可触发EXCEPTION_SINGLE_STEP单步异常暂停,处理时恢复起始地址处int 3断点,返回EXCEPTION_CONTINUE_EXECUTION继续执行程序,此时系统自动把标志寄存器TF位置0,等待下次触发int 3异常。

kernel32!LoadLibraryA/kernel32!LoadLibraryW调用关系如下,所以直接对kernelbase!LoadLibraryExW设置断点。

1
2
Kernel32.LoadLibraryW->KernelBase.LoadLibraryW->KernelBase.LoadLibraryExW
Kernel32.LoadLibraryA->KernelBase.LoadLibraryA->KernelBase.LoadLibraryExA->KernelBase.LoadLibraryExW

被注入的DLL实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <Windows.h>
// 全局变量
LPVOID g_pfnLoadLibraryExWAddress; // LoadLibraryExW函数地址
BYTE g_bOriginalCodeByte; // 保存LoadLibraryExW函数的第一个指令码
HWND g_hwndDlg; // CreateProcessInjectDll程序窗口句柄
// 函数声明
// 设置int 3断点(返回原指令码)
BYTE SetBreakPoint(LPVOID lpCodeAddr);
// 移除int 3断点
VOID RemoveBreakPoint(LPVOID lpCodeAddr, BYTE bOriginalCodeByte);
// 为LoadLibraryExW函数的int 3断点注册一个向量化异常处理程序
LONG CALLBACK LoadLibraryExWBPHandler(PEXCEPTION_POINTERS ExceptionInfo);
// LoadLibraryExW函数int 3中断以后执行用户所需的自定义操作
VOID LoadLibraryExWCustomActions(LPVOID lpCodeAddr, LPVOID lpStackAddr);
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
g_hwndDlg = FindWindow(TEXT("#32770"), TEXT("CreateProcessInjectDll"));
// 获取KernelBase.LoadLibraryExW函数的地址
g_pfnLoadLibraryExWAddress = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("KernelBase.dll")), "LoadLibraryExW");
// 为LoadLibraryExW函数的int 3断点注册一个向量化异常处理程序
AddVectoredExceptionHandler(1, LoadLibraryExWBPHandler);
// 在LoadLibraryExW函数上设置一个int 3断点
g_bOriginalCodeByte = SetBreakPoint(g_pfnLoadLibraryExWAddress);
break;
};
case DLL_PROCESS_DETACH: {};
case DLL_THREAD_ATTACH: {};
case DLL_THREAD_DETACH: {
break;
};
};
return TRUE;
};
// 内部函数
LONG CALLBACK LoadLibraryExWBPHandler(PEXCEPTION_POINTERS ExceptionInfo) {
DWORD dwExceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
if (dwExceptionCode == EXCEPTION_BREAKPOINT) {
// 检查是否是我们设置的int 3断点,如果不是,将它传递给其它异常处理程序
if (ExceptionInfo->ExceptionRecord->ExceptionAddress != g_pfnLoadLibraryExWAddress)
return EXCEPTION_CONTINUE_SEARCH;
// 对LoadLibraryExW函数执行用户所需的自定义操作
LoadLibraryExWCustomActions(ExceptionInfo->ExceptionRecord->ExceptionAddress, (LPVOID)(ExceptionInfo->ContextRecord->Esp));
// 临时移除int 3断点
RemoveBreakPoint(g_pfnLoadLibraryExWAddress, g_bOriginalCodeByte);
// 设置单步中断
ExceptionInfo->ContextRecord->EFlags |= 0x100;
// 重新执行发生int 3异常的指令,因为设置了单步中断,接下来会单步执行完第一条指令
return EXCEPTION_CONTINUE_EXECUTION;
}
else if (dwExceptionCode == EXCEPTION_SINGLE_STEP) {
if (ExceptionInfo->ExceptionRecord->ExceptionAddress != (LPBYTE)g_pfnLoadLibraryExWAddress + 2)
return EXCEPTION_CONTINUE_SEARCH;
// 已经执行完用户的自定义操作,也已经单步执行完LoadLibraryExW函数的第一条语句,重新设置int 3断点,以等待下一次LoadLibraryExW函数调用
SetBreakPoint(g_pfnLoadLibraryExWAddress);
// 继续运行
return EXCEPTION_CONTINUE_EXECUTION;
};
// 非int 3断点和单步中断都不处理
return EXCEPTION_CONTINUE_SEARCH;
};
BYTE SetBreakPoint(LPVOID lpCodeAddr) {
BYTE bOriginalCodeByte;
BYTE bInt3 = 0xCC;
DWORD dwOldProtect;
// 读取LoadLibraryExW函数的第一个指令码
ReadProcessMemory(GetCurrentProcess(), lpCodeAddr, &bOriginalCodeByte, sizeof(bOriginalCodeByte), NULL);
// 设置int 3断点
VirtualProtect(lpCodeAddr, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
WriteProcessMemory(GetCurrentProcess(), lpCodeAddr, &bInt3, sizeof(bInt3), NULL);
VirtualProtect(lpCodeAddr, 1, dwOldProtect, &dwOldProtect);
return bOriginalCodeByte;
};
VOID RemoveBreakPoint(LPVOID lpCodeAddr, BYTE bOriginalCodeByte) {
DWORD dwOldProtect;
VirtualProtect(lpCodeAddr, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
WriteProcessMemory(GetCurrentProcess(), lpCodeAddr, &bOriginalCodeByte, sizeof(bOriginalCodeByte), NULL);
VirtualProtect(lpCodeAddr, 1, dwOldProtect, &dwOldProtect);
return;
};
VOID LoadLibraryExWCustomActions(LPVOID lpCodeAddr, LPVOID lpStackAddr) {
TCHAR szDllName[MAX_PATH] = { 0 };
ReadProcessMemory(GetCurrentProcess(), (LPVOID)(*(LPDWORD)((LPBYTE)lpStackAddr + 4)), szDllName, sizeof(szDllName), NULL);
// dll名称显示到CreateProcessInjectDll程序的编辑控件中
SendDlgItemMessage(g_hwndDlg, 1002, EM_SETSEL, -1, -1);
SendDlgItemMessage(g_hwndDlg, 1002, EM_REPLACESEL, TRUE, (LPARAM)szDllName);
SendDlgItemMessage(g_hwndDlg, 1002, EM_REPLACESEL, TRUE, (LPARAM)TEXT("\r\n"));
return;
};

异常处理优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <windows.h>
#include "resource.h"
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo);
DWORD WINAPI ThreadProc(LPVOID lpParameter);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static LPVOID lpHandler;
HANDLE hThread = NULL;
switch (uMsg) {
case WM_INITDIALOG: {
lpHandler = AddVectoredExceptionHandler(1, VectoredHandler);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_OK: {
__try {
PCHAR pChar;
pChar = NULL;
*pChar = TEXT('A');
}
__except (EXCEPTION_EXECUTE_HANDLER) {
MessageBox(NULL, TEXT("SEH异常处理程序"), TEXT("SEH提示"), MB_OK);
};
break;
};
case IDC_BTN_OK2: {
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
if (hThread)
CloseHandle(hThread);
break;
};
case IDCANCEL: {
RemoveVectoredExceptionHandler(lpHandler);
EndDialog(hwndDlg, 0);
break;
};
};
return TRUE;
};
};
return FALSE;
};
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(NULL, TEXT("VEH异常处理程序"), TEXT("VEH提示"), MB_OK);
return EXCEPTION_CONTINUE_SEARCH;
};
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
__try {
PCHAR pChar;
pChar = NULL;
*pChar = TEXT('A');
}
__except (EXCEPTION_EXECUTE_HANDLER) {
MessageBox(NULL, TEXT("来自辅助线程:SEH异常处理程序"), TEXT("SEH提示"), MB_OK);
};
return 0;
};

全局顶层未处理异常过滤UEF

简介

当一个异常发生时,如果进程中所有VEH和当前线程中所有SEH都不处理这个异常,则产生一个未处理异常,这里不处理指的是没有相关异常处理/过滤程序或程序返回EXCEPTION_CONTINUE_SEARCH。系统默认的UEF即终止进程,程序可设置一个顶层未处理异常过滤器,当发生未处理异常时调用指定顶层未处理异常过滤器函数。

SetTopLevelExceptionFilter

设置一个顶层未处理异常过滤器:

1
2
3
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter //顶层未处理异常过滤器函数指针
);

LPTOP_LEVEL_EXCEPTION_FILTER结构为:

1
2
typedef LONG(WINAPI* PTOP_LEVEL_EXCEPTION_FILTER)(_In_ struct _EXCEPTION_POINTERS* ExceptionInfo);
typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;

顶层未处理异常过滤器函数定义格式为:

1
2
3
LONG WINAPI TopLevelUnhandledExceptionFilter(
PEXCEPTION_POINTERS ExceptionInfo
);

顶层未处理异常过滤器函数可用返回值有:

返回值 含义
EXCEPTION_EXECUTE_HANDLER 当前异常过滤器函数成功处理该异常,静默终止进程
EXCEPTION_CONTINUE_SEARCH 当前异常过滤器函数无法处理该异常,执行系统默认未处理异常程序,即终止进程并通知
EXCEPTION_CONTINUE_EXECUTION 重新执行发生异常的指令

该函数返回值是先前顶层未处理异常过滤器函数地址,若先前没有设置则返回NULL。进程中只可能有一个未处理过滤器函数,再次调用该函数时将替换先前函数。当参数为NULL表示恢复为系统默认异常处理程序UnhandledExceptionFilter

用户自定义的过滤器函数实际上都由该默认处理函数UnhandledExceptionFilter调用,该函数会用ntdll!ZwQueryInformationProcess判断当前进程是否在被调试,如果正在被调试则不会调用用户设置的顶层未处理异常过滤器函数,利用这个特性可以反调试。

全局向量化继续处理VCH

当VEH、SEH或UEF之一可以处理异常时执行向量化继续处理程序。用法与向量化异常处理相关函数完全相同。

AddVectoredContinueHandler

添加或注册一个向量化继续处理程序,多次调用可注册多个,继续处理程序都被添加到链表中。

1
2
3
4
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler, //继续调用处理程序的顺序
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler //继续处理回调函数指针
);

RemoveVectoredContinueHandler

删除不需要的之前注册的向量化继续处理程序:

1
2
3
ULONG WINAPI RemoveVectoredContinueHandler(
_In_ PVOID pHandler
);

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <windows.h>
#include "resource.h"
// 全局变量
HWND g_hwndDlg;
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
LONG CALLBACK VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo); // VEH异常处理程序
DWORD StructuredExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); // SEH异常过滤程序
LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo);// UEF异常过滤程序
LONG CALLBACK VectoredContinueHandler(PEXCEPTION_POINTERS ExceptionInfo); // VCH继续处理程序
DWORD WINAPI ThreadProc(LPVOID lpParameter);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static LPVOID lpHandler, lpHandlerContinue;
HANDLE hThread = NULL;
int n = 10, m = 0, x;
TCHAR szBuf[32] = { 0 };
switch (uMsg) {
case WM_INITDIALOG: {
g_hwndDlg = hwndDlg;
// VEH向量化异常处理
lpHandler = AddVectoredExceptionHandler(1, VectoredExceptionHandler);
// VCH向量化继续处理
lpHandlerContinue = AddVectoredContinueHandler(1, VectoredContinueHandler);
// UEF顶层未处理异常过滤器
SetUnhandledExceptionFilter(TopLevelUnhandledExceptionFilter);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_OK: {
__try {
// 会发生EXCEPTION_INT_DIVIDE_BY_ZERO除零异常
x = n / m;
wsprintf(szBuf, TEXT("%d / %d = %d"), n, m, x);
MessageBox(hwndDlg, szBuf, TEXT("已从异常中恢复"), MB_OK);
}
__except (StructuredExceptionFilter(GetExceptionInformation())) {
// 除非SEH异常过滤程序返回EXCEPTION_EXECUTE_HANDLER,否则这里不会执行
MessageBox(hwndDlg, TEXT("SEH异常处理程序"), TEXT("SEH提示"), MB_OK);
};
break;
};
case IDC_BTN_OK2: {
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
if (hThread)
CloseHandle(hThread);
break;
};
case IDCANCEL: {
RemoveVectoredExceptionHandler(lpHandler);
RemoveVectoredContinueHandler(lpHandlerContinue);
EndDialog(hwndDlg, 0);
break;
};
};
return TRUE;
};
};
return FALSE;
};
LONG CALLBACK VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(g_hwndDlg, TEXT("VEH异常处理程序"), TEXT("VEH提示"), MB_OK);
//ExceptionInfo->ContextRecord->Ecx = 2; // 把除数设置为2
return EXCEPTION_CONTINUE_SEARCH;
};
DWORD StructuredExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(g_hwndDlg, TEXT("SEH异常过滤程序"), TEXT("SEH提示"), MB_OK);
//ExceptionInfo->ContextRecord->Ecx = 2; // 把除数设置为2
return EXCEPTION_CONTINUE_SEARCH;
};
LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(g_hwndDlg, TEXT("UEF顶层未处理异常过滤器程序"), TEXT("UEF提示"), MB_OK);
//ExceptionInfo->ContextRecord->Ecx = 2; // 把除数设置为2
return EXCEPTION_CONTINUE_SEARCH;
};
LONG CALLBACK VectoredContinueHandler(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(g_hwndDlg, TEXT("VCH继续处理程序"), TEXT("VCH提示"), MB_OK);
return EXCEPTION_CONTINUE_SEARCH;
};
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
int n = 10, m = 0, x;
TCHAR szBuf[32] = { 0 };
__try {
// 会发生EXCEPTION_INT_DIVIDE_BY_ZERO除零异常
x = n / m;
wsprintf(szBuf, TEXT("%d / %d = %d"), n, m, x);
MessageBox(g_hwndDlg, szBuf, TEXT("已从异常中恢复"), MB_OK);
}
__except (StructuredExceptionFilter(GetExceptionInformation())) {
// 除非SEH异常过滤程序返回EXCEPTION_EXECUTE_HANDLER,否则这里不会执行
MessageBox(g_hwndDlg, TEXT("来自辅助线程:SEH异常处理程序"), TEXT("SEH提示"), MB_OK);
};
return 0;
};