/SAFESEH
vs2019需要打开此选项才可以正常进行异常处理
https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers?redirectedfrom=MSDN&view=msvc-170
https://docs.microsoft.com/en-us/windows/win32/dxtecharts/best-security-practices-in-game-development
回调函数
// SEH handler
#if defined(_M_IX86) && !defined(_CHPE_X86_ARM64_EH_)
struct _EXCEPTION_RECORD;
struct _CONTEXT;
EXCEPTION_DISPOSITION __cdecl _except_handler(
_In_ struct _EXCEPTION_RECORD* _ExceptionRecord,
_In_ void* _EstablisherFrame,
_Inout_ struct _CONTEXT* _ContextRecord,
_Inout_ void* _DispatcherContext
);
#elif defined _M_X64 || defined _M_ARM || defined _M_ARM64 || defined _CHPE_X86_ARM64_EH_
#ifndef _M_CEE_PURE
struct _EXCEPTION_RECORD;
struct _CONTEXT;
struct _DISPATCHER_CONTEXT;
_VCRTIMP EXCEPTION_DISPOSITION __cdecl __C_specific_handler(
_In_ struct _EXCEPTION_RECORD* ExceptionRecord,
_In_ void* EstablisherFrame,
_Inout_ struct _CONTEXT* ContextRecord,
_Inout_ struct _DISPATCHER_CONTEXT* DispatcherContext
);
#endif
#endif
可以在vs2019include\excpt.h
此头文件下找到。此处分为x86
和x64
。
第一个参数**_EXCEPTION_RECORD**
结构体内容为
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; // 异常代码
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
可以在winnt.h
文件中搜索到错误代码
更详细在Windows NT DDK
的NTSTATUS.H
中。
调试也可以看到
第二个参数**EstablisherFrame**
文档原文
The address of the base of the fixed stack allocation for this function.
一个指向Establisher
帧结构的指针。
第三个参数 **_CONTEXT**
结构
typedef struct DECLSPEC_NOINITALL _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;
此结构存放异常发生时,寄存器中的值。
第四个参数**DispatcherContext**
文档原文
Points to the exception context at the time the exception was raised (in the exception handler case) or the current “unwind” context (in the termination handler case).
文档只搜到一个定义。(寄)
_EXCEPTION_DISPOSITION 结构
此结构指明异常发生时调用的callback函数的位置
typedef struct _EXCEPTION_REGISTRATION
{
struct _EXCEPTION_REGISTRATION* prev;
PEXCEPTION_HANDLER handler;
} EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION;
typedef EXCEPTION_REGISTRATION EXCEPTION_REGISTRATION_RECORD;
typedef PEXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION_RECORD;
此结构位于fs:[00000000h]
处。
形成
单个异常
//==================================================
// MYSEH - Matt Pietrek 1997
// Microsoft Systems Journal, January 1997
// FILE: MYSEH.CPP
// 用命令行CL MYSEH.CPP编译
//==================================================
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
DWORD scratch;
EXCEPTION_DISPOSITION
__cdecl
_except_handler(struct _EXCEPTION_RECORD* ExceptionRecord,
void* EstablisherFrame,
struct _CONTEXT* ContextRecord,
void* DispatcherContext)
{
unsigned i;
// 指明是我们让流程转到我们的异常处理程序的
printf("Hello from an exception handler\n");
// 改变CONTEXT结构中EAX的值,以便它指向可以成功进写操作的位置
ContextRecord->Eax = (DWORD)&scratch;
// 告诉操作系统重新执行出错的指令
return ExceptionContinueExecution;
}
int main()
{
DWORD handler = (DWORD)_except_handler;
__asm
{
// 创建EXCEPTION_REGISTRATION结构:
push handler // handler函数的地址
push FS : [0] // 前一个handler函数的地址
mov FS : [0] , ESP // 安装新的EXECEPTION_REGISTRATION结构
}
printf("Hei Hei!\n");
__asm
{
mov eax, 0 // 将EAX清零
mov[eax], 1 // 写EAX指向的内存从而故意引发一个错误
}
printf("After writing!\n");
__asm
{
// 移去我们的EXECEPTION_REGISTRATION结构
mov eax, [ESP] // 获取前一个结构
mov FS : [0] , EAX // 安装前一个结构
add esp, 8 // 将我们的EXECEPTION_REGISTRATION弹出堆栈
}
printf("MO MO!\n");
return 0;
}
多个异常
参考链接
https://bytepointer.com/resources/pietrek_crash_course_depths_of_win32_seh.htm
https://docs.microsoft.com/en-us/archive/msdn-magazine/2001/september/under-the-hood-new-vectored-exception-handling-in-windows-xp
http://www.openrce.org/articles/full_view/21