编写调试器的主循环
调试器在其主循环的开头使用 WaitForDebugEvent 函数。 此函数会阻止调试器,直到发生调试事件为止。 发生调试事件时,系统将挂起正在调试的进程中的所有线程,并通知事件调试器。
调试器可以通过使用 GetThreadContext、GetThreadSelectorEntry、ReadProcessMemory、SetThreadContext 和 WriteProcessMemory 函数与用户交互,或操作正在调试的进程的状态。 GetThreadSelectorEntry 会返回指定选择器和线程的描述符表条目。 调试器使用描述符表条目将段相对地址转换为线性虚拟地址。 ReadProcessMemory 和 WriteProcessMemory 函数需要线性虚拟地址。
调试器会经常读取正在调试的进程内存,并将包含指令的内存写入指令缓存。 编写指令后,调试器会调用 FlushInstructionCache 函数来执行缓存指令。
调试器在其主循环的末尾使用 ContinueDebugEvent 函数。 此函数允许继续执行正在调试的进程。
以下示例使用 WaitForDebugEvent 和 ContinueDebugEvent 函数来说明如何组织简单的调试器。
#include <windows.h>
DWORD OnCreateThreadDebugEvent(const LPDEBUG_EVENT);
DWORD OnCreateProcessDebugEvent(const LPDEBUG_EVENT);
DWORD OnExitThreadDebugEvent(const LPDEBUG_EVENT);
DWORD OnExitProcessDebugEvent(const LPDEBUG_EVENT);
DWORD OnLoadDllDebugEvent(const LPDEBUG_EVENT);
DWORD OnUnloadDllDebugEvent(const LPDEBUG_EVENT);
DWORD OnOutputDebugStringEvent(const LPDEBUG_EVENT);
DWORD OnRipEvent(const LPDEBUG_EVENT);
void EnterDebugLoop(const LPDEBUG_EVENT DebugEv)
{
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
for(;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv->dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
switch(DebugEv->u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default:
// Handle other exceptions.
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
// As needed, examine or change the thread's registers
// with the GetThreadContext and SetThreadContext functions;
// and suspend and resume thread execution with the
// SuspendThread and ResumeThread functions.
dwContinueStatus = OnCreateThreadDebugEvent(DebugEv);
break;
case CREATE_PROCESS_DEBUG_EVENT:
// As needed, examine or change the registers of the
// process's initial thread with the GetThreadContext and
// SetThreadContext functions; read from and write to the
// process's virtual memory with the ReadProcessMemory and
// WriteProcessMemory functions; and suspend and resume
// thread execution with the SuspendThread and ResumeThread
// functions. Be sure to close the handle to the process image
// file with CloseHandle.
dwContinueStatus = OnCreateProcessDebugEvent(DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
dwContinueStatus = OnExitThreadDebugEvent(DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
dwContinueStatus = OnExitProcessDebugEvent(DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
dwContinueStatus = OnLoadDllDebugEvent(DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
dwContinueStatus = OnUnloadDllDebugEvent(DebugEv);
break;
case OUTPUT_DEBUG_STRING_EVENT:
// Display the output debugging string.
dwContinueStatus = OnOutputDebugStringEvent(DebugEv);
break;
case RIP_EVENT:
dwContinueStatus = OnRipEvent(DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
ContinueDebugEvent(DebugEv->dwProcessId,
DebugEv->dwThreadId,
dwContinueStatus);
}
}