继上一篇SEH相关数据结构,还有三个结构体很重要。
它们是EXCEPTION_POINTERS、EXCEPTION_RECORD、CONTEXT
当一个异常发生时,操作系统向引起异常的线程的堆栈里压入EXCEPTION_POINTERS结构:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
上篇文章的程序跟踪到此处时
00401017 8B06 mov eax,dword ptr ds:[esi]
Shift+F7进入异常刚发生时的系统代码处,EXCEPTION_POINTERS结构就在当前栈顶,那么当前堆栈的数据就是此结构的两个成员EXCEPTION_RECORD、CONTEXT。
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;
这个结构包含有关最近发生异常的详细信息,这些信息独立于CPU。
这个ExceptionCode字段定义了产生异常的原因,下表列出了一些常见的异常原因:
异常原因 | 对应值 | 说明 |
STATUS_GUARD_PAGE_VIOLATION | 080000001h | 读写属性为PAGE_GUARD的页面 |
EXCEPTION_BREAKPOINT | 080000003h | 断点异常 |
EXCEPTION_SINGLE_STEP | 080000004h | 单步中断 |
EXCEPTION_INVALID_HANDLE | 0C0000008h | 向一个函数传递了一个无效句柄 |
EXCEPTION_ACCESS_VIOLATION | 0C0000005h | 读写内存冲突 |
EXCEPTION_ILLEGAL_INSTRUCTION | 0C000001Dh | 遇到无效指令 |
EXCEPTION_IN_PAGE_ERROR | 0C0000006h | 存取不存在的页面 |
EXCEPTION_INT_DIVIDE_BY_ZERO | 0C0000094h | 除零错误 |
EXCEPTION_STACK_OVERFLOW | 0C00000FDh | 堆栈溢出 |
00401017 8B06 mov eax,dword ptr ds:[esi]
这句会引发一个STATUS_GUARD_PAGE_VIOLATION异常,异常代码是0C0000005h
在堆栈中数据窗口跟随,发现如下:
0018FAD0 05 00 00 C0 00 00 00 00 00 00 00 00 17 10 40 00 ..?.......@.
0018FAE0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
CONTEXT结构
CONTEXT结构是Win32API一个几乎唯一与处理器相关的结构,包括了线程运行时处理器各主要寄存器的完整镜像,用于保存线程运行环境。
其结构如下:
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;
我们可以看一下regEip处的内容,发现它正好是我们异常发生处的地址。又一个值得深入理解的结构~~~~