调式器原理
1. 对目标进程进行调试
-
创建进程调试:
//保存启动信息的结构体 第一个变量cb为结构体大小 STARTUPINFO si = { sizeof(STARTUPINFO) }; //保存进程信息的结构体 PROCESS_INFORMATION pi = {}; //以调式的方式创建进程 bool result = CreateProcess( path, //文件路径 NULL, //命令行 NULL, //进程安全属性 NULL, //线程安全属性 FALSE, //子进程是否继承句柄表 DEBUG_PROCESS | CREATE_NEW_CONSOLE,//创建标志 NULL, //环境变量 NULL, //当前工作路径 &si, //启动信息 &pi //进程信息 );
-
附加进程调试
DebugActiveProcess(pid);
2. 调试事件循环
-
等待调试事件WaitForDebugEvent(&DEBUG_EVENT,INFINITE)
-
处理调试事件
-
回复被调式子程序
typedef struct _DEBUG_EVENT { DWORD dwDebugEventCode; //调试事件代码 DWORD dwProcessId; //产生调试事件的进程ID,线程ID DWORD dwThreadId; //调试事件的信息 union { EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo; } u; } DEBUG_EVENT, *LPDEBUG_EVENT; //等待调试事件 void CDebugger::DebugEventLoop() { //异常处理状态 m_DebugStatus = DBG_CONTINUE; //循环标志 BOOL Isloop = true; //1.等待调式事件 while (Isloop) { //等待调试事件 参数1:接收事件信息的结构体 参数2:等待时长 WaitForDebugEvent(&m_DebugEventInfo, INFINITE); //获取到调式信息打开句柄 GetHandle(); //2.处理调试事件 switch (m_DebugEventInfo.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: // 异常调试事件 ExceptionHandler(); // 异常处理器 break; case CREATE_THREAD_DEBUG_EVENT: // 线程创建事件 break; case CREATE_PROCESS_DEBUG_EVENT:// 进程创建事件 { //初始化被调试进程信息 InitDebugProc(); break; } case EXIT_THREAD_DEBUG_EVENT: // 退出线程事件 break; case EXIT_PROCESS_DEBUG_EVENT: // 退出进程事件 Isloop = false; printf("进程退出事件触发\n\n"); break; case LOAD_DLL_DEBUG_EVENT: // 映射DLL事件 { //保存模块信息 DWORD pBase = (DWORD)m_DebugEventInfo.u.LoadDll.lpBaseOfDll; HANDLE hFile = m_DebugEventInfo.u.LoadDll.hFile; } break; case UNLOAD_DLL_DEBUG_EVENT: // 卸载DLL事件 break; case OUTPUT_DEBUG_STRING_EVENT: // 调试字符串输出事件 break; case RIP_EVENT: // RIP事件(内部错误) break; } //3.回复调试子系统 ContinueDebugEvent( m_DebugEventInfo.dwProcessId, //调试进程ID,必须从DEBUG_EVNET中获取 m_DebugEventInfo.dwThreadId, //调试线程ID,必须从DEBUG_EVNET中获取 m_DebugStatus); //异常是否处理,只对异常有效 //处理结束关闭句柄 GetHandle(); } // 回复调试子系统当前调试事件的处理结果,对于除 EXCEPTION_DEBUG_EVENT // 之外的所有事件,可以直接返回 DBG_CONTINUE,表示调试事件已经被接收 // 并且处理了,对于 EXCEPTION_DEBUG_EVENT,如果除了了就返回 DBG_CONTINUE // 否则返回 DBG_EXCEPTION_NOT_HANDLED,返回这两个值分别意味着内核函 // 数 DbgkForwardException 返回了 TRUE 和 FALSE。 // 在调用 ContinueDebugEvent 函数的时候,第一个参数和第二个参数[必须]是 // WaitForDebugEvent 函数返回的事件结构体中的进程和线程 id,原因是谁触发 // 了调试信息,就告诉系统是处理谁的调试信息 }
3. 处理异常事件
//异常调式事件处理
void CDebugger::ExceptionHandler()
{
//异常类型及发生异常所在地址
DWORD dwCode = m_DebugEventInfo.u.Exception.ExceptionRecord.ExceptionCode;
LPVOID pAddress = m_DebugEventInfo.u.Exception.ExceptionRecord.ExceptionAddress;
//第一个为系统软件的标志
static bool Is_SystemBreakPoint = TRUE;
//是否需要输入的标志
bool Is_Input = false;
//判断异常的类型
switch (dwCode)
{
//如果是软件断点异常 int3
case EXCEPTION_BREAKPOINT:
{
//第一个是系统软件断点
//系统断点,不处理,在接收到时创建OEP断点
if (Is_SystemBreakPoint)
{
//当操作系统在加载一个exe模块并执行的时候,会判断当前进程是否处于调试状态
//如果处于调试状态,那么就会通过 int 3 指令设置一个【系统断点】,否则正常
//执行,一般来讲系统断点是不需要我们进程修复的,直接允许程序继续执行
Is_SystemBreakPoint = false;
}
//修复这个断点
else
{
m_DebugStatus = CBreakPoint::FixSoftBreakPoint(m_hProc, m_hThread, pAddress);
}
break;
}
// 硬件断点(DrN & Tf) 都会触发这个异常
case EXCEPTION_SINGLE_STEP:
{
//处理状态
m_DebugStatus = DBG_CONTINUE;
//判断是否为单步执行断点且是否存在失效的断点(修复断点)
CBreakPoint::GetInvalidBp(m_hProc, m_hThread);
if (CBreakPoint::IsSingleStep)
{
//如果存在恢复断点直接跑起来
//如果在断点处执行单步p,需要等待接收收入
if (!Is_t)
{
printf("触发断点信息 >> 序号:无 为恢复其他永久性断点设置的单步执行断点\n");
}
else
{
printf("触发断点信息 >> 序号:无 按下 t 设置的单步执行断点\n");
//重置单步执行断点标志
Is_t = false;
Is_Input = true;
}
CBreakPoint::IsSingleStep = false;
}
//如果是硬件断点则修复
if (CBreakPoint::FixHardBreakPoint(m_hProc, m_hThread, pAddress) != 0)
{
Is_Input = true;
}
break;
}
// 非法访问异常
case EXCEPTION_ACCESS_VIOLATION:
{
//处理状态
m_DebugStatus = DBG_CONTINUE;
LPVOID pAddress = (LPVOID)m_DebugEventInfo.u.Exception.ExceptionRecord.ExceptionInformation[1];
DWORD type = m_DebugEventInfo.u.Exception.ExceptionRecord.ExceptionInformation[0];
printf(">>>>> %p\n", m_DebugEventInfo.u.Exception.ExceptionRecord.ExceptionAddress);
{
//获取修复的结果
//如果是自己设置的
DWORD ret = CBreakPoint::FixMemoryBreakPoint(m_hProc, m_hThread, pAddress);
//如果找到的不是自己设置的则不处理
if (ret == -1)
{
m_DebugStatus = DBG_EXCEPTION_NOT_HANDLED;
}
//如果是自己的且命中地址符合断点类型则等待输入显示汇编
else if (ret == 1 && CBreakPoint::MBpType == type)
{
Is_Input = true;
}
}
}
}
if (Is_Input)
{
//如果是源码调试显示源码
if (RcFlag)
{
char* p = pSc;
ShowRc(p, LineNum);
}
else
//显示反汇编
CCapStone::DisplayAsm(m_hProc, pAddress, 10);
Input_Command();
}
}
-
不同的异常事件对应不同的结构体信息
-
异常事件的返回值分两种
- 没有处理的(让被调试程序自己处理):m_DebugStatus = DBG_EXCEPTION_NOT_HANDLED;
- 处理了的:m_DebugStatus = DBG_CONTINUE;
-
异常事件也分两种:
- 被调式程序自己产生的异常(一般不处理,让调试子程序的处理器处理)
- 调式器主动制造的异常(需要处理)
-
最重要的异常类型EXCEPTTION_DEBUG_EVENT
//异常事件结构体 typedef struct _EXCEPTION_DEBUG_INFO { EXCEPTION_RECORD ExceptionRecord; //异常信息结构体 DWORD dwFirstChance; //不为0则第一次遇到异常 可以判断该值只处理第二次的异常从而将被调试子程序自己产生的异常忽略 } EXCEPTION_DEBUG_INFO; //异常信息结构体 typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; //异常码 DWORD ExceptionFlags; //异常标志 最低位为1表示不可恢复 struct _EXCEPTION_RECORD* ExceptionRecord;//异常记录关联,连续异常用到 PVOID ExceptionAddress;//异常发生的地址 DWORD NumberParameters;//参数 DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //内存访问异常用到 第一个元素为0读错误 1写错误 8执行错误 第二个元素表示异常地址 } EXCEPTION_RECORD; 异常代码 异常发生的原因。这是由硬件异常生成的代码,或软件生成的异常在 RaiseException函数中指定的代码。 EXCEPTION_INT_DIVIDE_BY_ZERO 线程尝试将整数值除以整数除数零。 //0x00 错误类 EXCEPTION_SINGLE_STEP 跟踪陷阱或其他单指令机制表示已执行一个指令。 //0x01 单步和硬件 除硬件执行是错误类,其余为陷阱类 EXCEPTION_BREAKPOINT 遇到断点。 //软件断点 0x03 陷阱类 EXCEPTION_INT_OVERFLOW 整数操作的结果导致执行结果最重要的位。 //溢出 0x04 陷阱类 //0x8 双重错误 0x12机器检查 属于中止类错误无法恢复 EXCEPTION_ACCESS_VIOLATION 线程尝试从它没有适当访问权限的虚拟地址读取或写入虚拟地址。 //内存断点错误类 0x0E EXCEPTION_PRIV_INSTRUCTION 线程尝试执行在当前计算机模式下不允许其操作的指令 //如STI EXCEPTION_ARRAY_BOUNDS_EXCEEDED 线程尝试访问超出边界的数组元素,基础硬件支持边界检查。 EXCEPTION_DATATYPE_MISALIGNMENT 线程尝试在不提供对齐的硬件上读取或写入未对齐的数据。 例如,16 位值必须在 2 字节边界上对齐;如果 16 位值必须在 2 字节边界上对齐,因此,在 2 字节边界上必须对齐 16 位值。4 字节边界上的 32 位值,等等。 EXCEPTION_FLT_DENORMAL_OPERAND 浮点操作中的操作法太小,无法表示为标准浮点值。 EXCEPTION_FLT_DIVIDE_BY_ZERO 线程尝试将浮点值除以浮点除数零。 EXCEPTION_FLT_INEXACT_RESULT 浮点操作的结果不能完全表示为小数分数。 EXCEPTION_FLT_INVALID_OPERATION 此列表中未包括的浮点异常。 EXCEPTION_FLT_OVERFLOW 浮点操作的指数大于相应类型允许的幅度。 EXCEPTION_FLT_STACK_CHECK 由于浮点操作,堆栈溢出或下溢。 EXCEPTION_FLT_UNDERFLOW 浮点操作的指数小于相应类型允许的幅度。 EXCEPTION_ILLEGAL_INSTRUCTION 线程尝试执行无效的指令。 EXCEPTION_IN_PAGE_ERROR 线程尝试访问不存在且系统无法加载该页的页面。//页错误 例如,如果网络连接在网络上运行程序时丢失,则可能发生此异常。 EXCEPTION_INVALID_DISPOSITION 异常处理程序向异常调度程序返回无效处置。 使用高级语言(如 C)的程序员不应遇到此异常。 EXCEPTION_NONCONTINUABLE_EXCEPTION 发生不可继续的异常后,线程尝试继续执行。 EXCEPTION_STACK_OVERFLOW 线程已使用其堆栈。
断点的实现
1. 设置单步执行断点
- 永久断点的实现就是断下后设置陷阱标志,在其中恢复断点
// CPU 中的 eflags 标志位中保留了一个 tf 标志位,通过
// 设置 tf 标志位,cpu 允许在执行一条指令之后产生异常,
// 在异常产生后,CPU 会自动重置 tf 标志为 0
// 0. 初始化结构体说明自己需要操作的寄存器
CONTEXT context = { CONTEXT_CONTROL };
// 1. 获取到目标线程的线程环境
GetThreadContext(thread, &context);
// 2. 设置目标的 eflags.tf 为 1
context.EFlags |= 0x00000100;
// 3. 将设置后的内容应用到目标线程
SetThreadContext(thread, &context);
异常类型: 硬件断点(DrN & Tf) 都会触发这个异常
case EXCEPTION_SINGLE_STEP:
// 硬件断点(DrN & Tf) 都会触发这个异常
case EXCEPTION_SINGLE_STEP:
{
m_DebugStatus = DBG_CONTINUE;
Is_Input = true;
break;
}
2. 软件断点
// 设置一个软件断点
void CBreakPoint::SetSoftBreakPoint(HANDLE process, LPVOID address, bool persistent)
{
// 0. 创建结构体,初始化断点的信息
BREAKPOINT_INFO BpInfo = { address };
// 1. 保存需要设置断点的地址上的原始内容
DWORD length = 0;
ReadProcessMemory(process, address, &BpInfo.Opcode, 1, &length);
// 2. 修改目标进程指定地址的内容为 0xCC
WriteProcessMemory(process, address, "\xCC", 1, &length);
//设置永久性
BpInfo.Is_Persistent = persistent;
// 3. 将设置好的断点添加到断点列表
breakpoint_list.push_back(BpInfo);
}
// 修复一个软件断点
DWORD CBreakPoint::FixSoftBreakPoint(HANDLE process, HANDLE thread, LPVOID address)
{
// 当调试器检测到一个软件断点断下的时候,首先需要获取异常地址,判断
// 这个软件断点是不是调试器用户设置的,如果是,就遍历断点列表找到
// 设置 cc 之前的内容,将原始内容写回目标地址,并且将 【eip - 1】
// 遍历用户设置的所有断点
for (DWORD i = 0; i < breakpoint_list.size(); ++i)
{
// 对比断点,找到对应的断点信息
if (address == breakpoint_list[i].Address && 软件断点 == breakpoint_list[i].dwkind)
{
// 1. 重新写入设置断点之前的内容
DWORD length = 0;
WriteProcessMemory(process, address, &breakpoint_list[i].Opcode, 1, &length);
// 2. 获取目标线程的线程环境
CONTEXT context = { CONTEXT_CONTROL };
GetThreadContext(thread, &context);
// 3. 由于是陷阱类异常,所以需要 eip - 1
context.Eip -= 1;
// 4. 将修改应用到目标线程
SetThreadContext(thread, &context);
// 5. 如果是永久断点在这里暂时置为无效,如果是临时,直接删除
if (breakpoint_list[i].Is_Persistent)
{
breakpoint_list[i].Valid = false;
}
else
{
breakpoint_list.erase(breakpoint_list.begin() + i);
}
//设置单步执行断点
CBreakPoint::SetStepBreakPoint(thread);
return DBG_CONTINUE;
}
}
return DBG_EXCEPTION_NOT_HANDLED;
}
3. 硬件断点
// 设置一个硬件断点
DWORD CBreakPoint::SetHardBreakPoint(HANDLE thread, LPVOID address, DWORD type, DWORD len)
{
// 不要在系统断点设置调试寄存器
// 硬件断点由CPU提供的 6 个调试寄存器实现,分别是 Dr0~Dr3,Dr6,Dr7
// 其中设置断点需要用到 Dr0~Dr3 以及 Dr7,Dr0~Dr3 用于保存断点的地址
// Dr7 用于设置断点的类型,以及标识断点的状态。
// Dr6的第四位标明哪个硬件断点触发了
//RW0~3 type读写域 0执行 1写 3读写
//LEN0~3 len长度域 0:1字节(执行必须为0),1:2字节,2:8字节或者未知 3:4字节
//L0~3 为 1 局部有效
//G0~3 为 1 全局有效(被调试进程无效)
// 1. 获取目标线程的寄存器
CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
GetThreadContext(thread, &context);
// 2. 获取 Dr7 寄存器,得到硬件断点的开关状态
PR7 dr7 = (PR7)&context.Dr7;
//增加的断点
BREAKPOINT_INFO bp = {};
CStringA sKind;
//判断len和type的值
//如果RW域为0 则一定为执行断点 len也为0
if (type == 0)
len = 0;
//根据len重置地址 设置对齐粒度
//len=1 地址为2的倍数
if (len == 1)
address = LPVOID(DWORD(address) - (DWORD)address % 2);
//len=3 地址为4的倍数
else if (len == 3)
address = LPVOID(DWORD(address) - (DWORD)address % 4);
//len=0 地址为1的倍数
else if (len == 0)
len = 0;
//len=2 地址为8的倍数
else if (len == 2)
address = LPVOID(DWORD(address) - (DWORD)address % 8);
else
return 0;
// 3. 判断没有使用的硬件断点,并设置
if (dr7->L0 == 0)
{
// 如果 L0 是 0 就意味这没有使用这个硬件断点
dr7->L0 = 1;
// 设置断点的地址
context.Dr0 = (DWORD)address;
// 设置断点的类型: 读1 写3 执行0 2
dr7->RW0 = type;
// 设置断点的触发长度,如果是执行就必须是 0
dr7->LEN0 = len;
sKind.Format("DR0(type:%u,len:%u)", type, len);
bp.strKind = sKind;
bp.Index = 1;
}
else if (dr7->L1 == 0)
{
// 如果 L0 是 0 就意味这没有使用这个硬件断点
dr7->L1 = 1;
// 设置断点的地址
context.Dr1 = (DWORD)address;
// 设置断点的类型: 读1 写3 执行0 2
dr7->RW1 = type;
// 设置断点的触发长度,如果是执行就必须是 0
dr7->LEN1 = len;
sKind.Format("DR1(type:%u,len:%u)", type, len);
bp.strKind = sKind;
bp.Index = 2;
}
else if (dr7->L2 == 0)
{
// 如果 L0 是 0 就意味这没有使用这个硬件断点
dr7->L2 = 1;
// 设置断点的地址
context.Dr2 = (DWORD)address;
// 设置断点的类型: 读1 写3 执行0 2
dr7->RW2 = type;
// 设置断点的触发长度,如果是执行就必须是 0
dr7->LEN2 = len;
sKind.Format("DR2(type:%u,len:%u)", type, len);
bp.strKind = sKind;
bp.Index = 4;
}
else if (dr7->L3 == 0)
{
// 如果 L0 是 0 就意味这没有使用这个硬件断点
dr7->L3 = 1;
// 设置断点的地址
context.Dr3 = (DWORD)address;
// 设置断点的类型: 读1 写3 执行0 2
dr7->RW3 = type;
// 设置断点的触发长度,如果是执行就必须是 0
dr7->LEN3 = len;
sKind.Format("DR3(type:%u,len:%u)", type, len);
bp.strKind = sKind;
bp.Index = 8;
}
else
{
printf("没有可用的硬件断点\n");
return 0;
}
bp.Address = address;
bp.Valid = true;
bp.dwkind = 硬件断点;
*(PWORD)(&bp.Attrbute) = type;
*((PWORD)(&bp.Attrbute)+1) = len;
//添加到断点列表
breakpoint_list.push_back(bp);
// 4. 将寄存器的修改应用到线程
SetThreadContext(thread, &context);
return 0;
}
//将一个硬件断点启动标志置零
DWORD CBreakPoint::SetHardBpInvalid(HANDLE thread, DWORD index)
{
//获取线程信息
CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
GetThreadContext(thread, &context);
//硬件断点的位置
DWORD Is_HardBp = 0;
PR7 dr7 = (PR7)(&(context.Dr7));
if ((context.Dr6 & 0xf) != 0)
{
index = (context.Dr6 & 0xf);
Is_HardBp = index;
}
//判断是哪个硬件调试寄存器被触发了 设置为无效
switch (index)
{
case 1:
dr7->L0 = 0; break;
case 2:
dr7->L1 = 0; break;
case 4:
dr7->L2 = 0; break;
case 8:
dr7->L3 = 0; break;
}
// 4. 将寄存器的修改应用到线程
SetThreadContext(thread, &context);
return Is_HardBp;
}
4. 内存断点
// 0. 创建结构体,初始化断点的信息
BREAKPOINT_INFO BpInfo = { address };
//dwNewProtect = PAGE_EXECUTE;//内存读写断点
//dwNewProtect = PAGE_EXECUTE_READ;//可执行可读不可写 内存写断点
//dwNewProtect = PAGE_READWRITE;//可读可写不可执行 内存执行断点
//原来的属性
DWORD dwOldProtect = 0;
//新的属性
DWORD dwNewProtect = 0;
switch (type)
{
case 'r':
MBpType = 0;
dwNewProtect = PAGE_NOACCESS;
break;
case 'w':
MBpType = 1;
dwNewProtect = PAGE_NOACCESS;
break;
case 'e':
MBpType = 8;
dwNewProtect = PAGE_NOACCESS;
break;
}
// 1. 根据断点类型设置
//在地址的页起始位置设置 1页的大小
//直接设置成没有任何访问属性
VirtualProtectEx(process, LPVOID((DWORD)address & 0xfffff000), 0x1000, dwNewProtect, &dwOldProtect);
BpInfo.Attrbute = dwOldProtect;
BpInfo.dwkind = 内存断点;
BpInfo.strKind.Format("内存%c型断点", type);
BpInfo.type = type & 0xFF;
// 3. 将设置好的断点添加到断点列表
breakpoint_list.push_back(BpInfo);
One = true;
return 0;
}
// 修复一个内存断点
DWORD CBreakPoint::FixMemoryBreakPoint(HANDLE process, HANDLE thread, LPVOID address)
{
//找到命中位置的标记
DWORD Find = -1 ;
//在断点列表中标记为失效
// 遍历用户设置的所有断点
for (DWORD i = 0; i < breakpoint_list.size(); ++i)
{
// 对比断点,找到对应的断点信息
if (内存断点 == breakpoint_list[i].dwkind)
{
Find = 0;
// 如果产生的异常不在一个分页上,那就不是自己要处理的内存异常
if ((((int)address & 0xFFFFF000) != ((int)breakpoint_list[i].Address & 0xfffff000)))
return -1;
//因为发生异常的位置为1页内4kb,所以需要判断是否是设置的位置命中
//如果是设置的位置
if (address == breakpoint_list[i].Address)
{
Find = 1;
}
//设置为失效
breakpoint_list[i].Valid = false;
//设置单步执行断点
CBreakPoint::SetStepBreakPoint(thread);
DWORD dwNewProtect = breakpoint_list[i].Attrbute;
//将内存属性恢复
VirtualProtectEx(process, address, 1, dwNewProtect, (PDWORD)&(breakpoint_list[i].Attrbute));
printf("触发断点信息 >> 序号:%d 地址:%p %s 触发中暂时失效\n",
i, breakpoint_list[i].Address, breakpoint_list[i].strKind);
}
}
return Find;
}
DR7结构体
// DR7寄存器结构体
typedef struct _DBG_REG7 {
unsigned L0 : 1; unsigned G0 : 1;
unsigned L1 : 1; unsigned G1 : 1;
unsigned L2 : 1; unsigned G2 : 1;
unsigned L3 : 1; unsigned G3 : 1;
unsigned LE : 1; unsigned GE : 1;
unsigned : 6;// 保留的无效空间
unsigned RW0 : 2; unsigned LEN0 : 2;
unsigned RW1 : 2; unsigned LEN1 : 2;
unsigned RW2 : 2; unsigned LEN2 : 2;
unsigned RW3 : 2; unsigned LEN3 : 2;
} R7, * PR7;
EFLAGS结构体
//EFLAGS结构体
typedef struct _EFLAGS {
unsigned CF : 1; unsigned R1 : 1;//进位
unsigned PF : 1; unsigned R2 : 1;//奇偶
unsigned AF : 1; unsigned R3 : 1;//辅助进位
unsigned ZF : 1; //零标志位
unsigned SF : 1; //符号标志位
unsigned TF : 1; //陷阱
unsigned IF : 1; //中断
unsigned DF : 1; //方向
unsigned OF : 1; //溢出
unsigned : 20; // 保留的无效空间
}EFLAGS, * PEFLAGS;
CONTEXT结构体
typedef struct DECLSPEC_NOINITALL _CONTEXT {
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;
//#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
//#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI
//#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L) // DS, ES, FS, GS
//#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L) // 387 state
//#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7
//#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L) // cpu specific extensions
//
//#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER |\
// CONTEXT_SEGMENTS)
//
//#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | \
// CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | \
// CONTEXT_EXTENDED_REGISTERS)
//
//#define CONTEXT_XSTATE (CONTEXT_i386 | 0x00000040L)