/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

回调函数

  1. // SEH handler
  2. #if defined(_M_IX86) && !defined(_CHPE_X86_ARM64_EH_)
  3. struct _EXCEPTION_RECORD;
  4. struct _CONTEXT;
  5. EXCEPTION_DISPOSITION __cdecl _except_handler(
  6. _In_ struct _EXCEPTION_RECORD* _ExceptionRecord,
  7. _In_ void* _EstablisherFrame,
  8. _Inout_ struct _CONTEXT* _ContextRecord,
  9. _Inout_ void* _DispatcherContext
  10. );
  11. #elif defined _M_X64 || defined _M_ARM || defined _M_ARM64 || defined _CHPE_X86_ARM64_EH_
  12. #ifndef _M_CEE_PURE
  13. struct _EXCEPTION_RECORD;
  14. struct _CONTEXT;
  15. struct _DISPATCHER_CONTEXT;
  16. _VCRTIMP EXCEPTION_DISPOSITION __cdecl __C_specific_handler(
  17. _In_ struct _EXCEPTION_RECORD* ExceptionRecord,
  18. _In_ void* EstablisherFrame,
  19. _Inout_ struct _CONTEXT* ContextRecord,
  20. _Inout_ struct _DISPATCHER_CONTEXT* DispatcherContext
  21. );
  22. #endif
  23. #endif

可以在vs2019include\excpt.h此头文件下找到。此处分为x86x64
第一个参数**_EXCEPTION_RECORD**结构体内容为

  1. typedef struct _EXCEPTION_RECORD {
  2. DWORD ExceptionCode; // 异常代码
  3. DWORD ExceptionFlags;
  4. struct _EXCEPTION_RECORD *ExceptionRecord;
  5. PVOID ExceptionAddress;
  6. DWORD NumberParameters;
  7. ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
  8. } EXCEPTION_RECORD;

可以在winnt.h文件中搜索到错误代码
image.png
更详细在Windows NT DDKNTSTATUS.H中。
image.png
调试也可以看到
image.png
第二个参数**EstablisherFrame**
文档原文
The address of the base of the fixed stack allocation for this function.
一个指向Establisher帧结构的指针。
第三个参数 **_CONTEXT**结构

  1. typedef struct DECLSPEC_NOINITALL _CONTEXT {
  2. //
  3. // The flags values within this flag control the contents of
  4. // a CONTEXT record.
  5. //
  6. // If the context record is used as an input parameter, then
  7. // for each portion of the context record controlled by a flag
  8. // whose value is set, it is assumed that that portion of the
  9. // context record contains valid context. If the context record
  10. // is being used to modify a threads context, then only that
  11. // portion of the threads context will be modified.
  12. //
  13. // If the context record is used as an IN OUT parameter to capture
  14. // the context of a thread, then only those portions of the thread's
  15. // context corresponding to set flags will be returned.
  16. //
  17. // The context record is never used as an OUT only parameter.
  18. //
  19. DWORD ContextFlags;
  20. //
  21. // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
  22. // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
  23. // included in CONTEXT_FULL.
  24. //
  25. DWORD Dr0;
  26. DWORD Dr1;
  27. DWORD Dr2;
  28. DWORD Dr3;
  29. DWORD Dr6;
  30. DWORD Dr7;
  31. //
  32. // This section is specified/returned if the
  33. // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
  34. //
  35. FLOATING_SAVE_AREA FloatSave;
  36. //
  37. // This section is specified/returned if the
  38. // ContextFlags word contians the flag CONTEXT_SEGMENTS.
  39. //
  40. DWORD SegGs;
  41. DWORD SegFs;
  42. DWORD SegEs;
  43. DWORD SegDs;
  44. //
  45. // This section is specified/returned if the
  46. // ContextFlags word contians the flag CONTEXT_INTEGER.
  47. //
  48. DWORD Edi;
  49. DWORD Esi;
  50. DWORD Ebx;
  51. DWORD Edx;
  52. DWORD Ecx;
  53. DWORD Eax;
  54. //
  55. // This section is specified/returned if the
  56. // ContextFlags word contians the flag CONTEXT_CONTROL.
  57. //
  58. DWORD Ebp;
  59. DWORD Eip;
  60. DWORD SegCs; // MUST BE SANITIZED
  61. DWORD EFlags; // MUST BE SANITIZED
  62. DWORD Esp;
  63. DWORD SegSs;
  64. //
  65. // This section is specified/returned if the ContextFlags word
  66. // contains the flag CONTEXT_EXTENDED_REGISTERS.
  67. // The format and contexts are processor specific
  68. //
  69. BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
  70. } 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).
image.png
文档只搜到一个定义。(寄)
_EXCEPTION_DISPOSITION 结构
此结构指明异常发生时调用的callback函数的位置
image.png

  1. typedef struct _EXCEPTION_REGISTRATION
  2. {
  3. struct _EXCEPTION_REGISTRATION* prev;
  4. PEXCEPTION_HANDLER handler;
  5. } EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION;
  6. typedef EXCEPTION_REGISTRATION EXCEPTION_REGISTRATION_RECORD;
  7. typedef PEXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION_RECORD;

此结构位于fs:[00000000h]处。

形成

单个异常

  1. //==================================================
  2. // MYSEH - Matt Pietrek 1997
  3. // Microsoft Systems Journal, January 1997
  4. // FILE: MYSEH.CPP
  5. // 用命令行CL MYSEH.CPP编译
  6. //==================================================
  7. #define _CRT_SECURE_NO_WARNINGS
  8. #define WIN32_LEAN_AND_MEAN
  9. #include <windows.h>
  10. #include <stdio.h>
  11. DWORD scratch;
  12. EXCEPTION_DISPOSITION
  13. __cdecl
  14. _except_handler(struct _EXCEPTION_RECORD* ExceptionRecord,
  15. void* EstablisherFrame,
  16. struct _CONTEXT* ContextRecord,
  17. void* DispatcherContext)
  18. {
  19. unsigned i;
  20. // 指明是我们让流程转到我们的异常处理程序的
  21. printf("Hello from an exception handler\n");
  22. // 改变CONTEXT结构中EAX的值,以便它指向可以成功进写操作的位置
  23. ContextRecord->Eax = (DWORD)&scratch;
  24. // 告诉操作系统重新执行出错的指令
  25. return ExceptionContinueExecution;
  26. }
  27. int main()
  28. {
  29. DWORD handler = (DWORD)_except_handler;
  30. __asm
  31. {
  32. // 创建EXCEPTION_REGISTRATION结构:
  33. push handler // handler函数的地址
  34. push FS : [0] // 前一个handler函数的地址
  35. mov FS : [0] , ESP // 安装新的EXECEPTION_REGISTRATION结构
  36. }
  37. printf("Hei Hei!\n");
  38. __asm
  39. {
  40. mov eax, 0 // 将EAX清零
  41. mov[eax], 1 // 写EAX指向的内存从而故意引发一个错误
  42. }
  43. printf("After writing!\n");
  44. __asm
  45. {
  46. // 移去我们的EXECEPTION_REGISTRATION结构
  47. mov eax, [ESP] // 获取前一个结构
  48. mov FS : [0] , EAX // 安装前一个结构
  49. add esp, 8 // 将我们的EXECEPTION_REGISTRATION弹出堆栈
  50. }
  51. printf("MO MO!\n");
  52. return 0;
  53. }

image.png

多个异常

参考链接

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