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 |
异常代码 |
|
获取刚发生异常的相关信息,只能在异常过滤表达式中被调用。
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, _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; PEXCEPTION_ROUTINE Handler; } 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 );
|
当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 );
|
实例:利用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; BYTE g_bOriginalCodeByte; HWND g_hwndDlg;
BYTE SetBreakPoint(LPVOID lpCodeAddr);
VOID RemoveBreakPoint(LPVOID lpCodeAddr, BYTE bOriginalCodeByte);
LONG CALLBACK LoadLibraryExWBPHandler(PEXCEPTION_POINTERS ExceptionInfo);
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")); g_pfnLoadLibraryExWAddress = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("KernelBase.dll")), "LoadLibraryExW"); AddVectoredExceptionHandler(1, LoadLibraryExWBPHandler); 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) { if (ExceptionInfo->ExceptionRecord->ExceptionAddress != g_pfnLoadLibraryExWAddress) return EXCEPTION_CONTINUE_SEARCH; LoadLibraryExWCustomActions(ExceptionInfo->ExceptionRecord->ExceptionAddress, (LPVOID)(ExceptionInfo->ContextRecord->Esp)); RemoveBreakPoint(g_pfnLoadLibraryExWAddress, g_bOriginalCodeByte); ExceptionInfo->ContextRecord->EFlags |= 0x100; return EXCEPTION_CONTINUE_EXECUTION; } else if (dwExceptionCode == EXCEPTION_SINGLE_STEP) { if (ExceptionInfo->ExceptionRecord->ExceptionAddress != (LPBYTE)g_pfnLoadLibraryExWAddress + 2) return EXCEPTION_CONTINUE_SEARCH; SetBreakPoint(g_pfnLoadLibraryExWAddress); return EXCEPTION_CONTINUE_EXECUTION; }; return EXCEPTION_CONTINUE_SEARCH; }; BYTE SetBreakPoint(LPVOID lpCodeAddr) { BYTE bOriginalCodeByte; BYTE bInt3 = 0xCC; DWORD dwOldProtect; ReadProcessMemory(GetCurrentProcess(), lpCodeAddr, &bOriginalCodeByte, sizeof(bOriginalCodeByte), NULL); 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); 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); DWORD StructuredExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); LONG CALLBACK VectoredContinueHandler(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, lpHandlerContinue; HANDLE hThread = NULL; int n = 10, m = 0, x; TCHAR szBuf[32] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; lpHandler = AddVectoredExceptionHandler(1, VectoredExceptionHandler); lpHandlerContinue = AddVectoredContinueHandler(1, VectoredContinueHandler); SetUnhandledExceptionFilter(TopLevelUnhandledExceptionFilter); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OK: { __try { x = n / m; wsprintf(szBuf, TEXT("%d / %d = %d"), n, m, x); MessageBox(hwndDlg, szBuf, TEXT("已从异常中恢复"), MB_OK); } __except (StructuredExceptionFilter(GetExceptionInformation())) { 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); return EXCEPTION_CONTINUE_SEARCH; }; DWORD StructuredExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { MessageBox(g_hwndDlg, TEXT("SEH异常过滤程序"), TEXT("SEH提示"), MB_OK); return EXCEPTION_CONTINUE_SEARCH; }; LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { MessageBox(g_hwndDlg, TEXT("UEF顶层未处理异常过滤器程序"), TEXT("UEF提示"), MB_OK); 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 { x = n / m; wsprintf(szBuf, TEXT("%d / %d = %d"), n, m, x); MessageBox(g_hwndDlg, szBuf, TEXT("已从异常中恢复"), MB_OK); } __except (StructuredExceptionFilter(GetExceptionInformation())) { MessageBox(g_hwndDlg, TEXT("来自辅助线程:SEH异常处理程序"), TEXT("SEH提示"), MB_OK); }; return 0; };
|