1 #include <windows.h> 2 #include <string> 3 #include <iostream> 4 5 using namespace std; 6 7 /*先声明一些Debug消息处理函数供调用*/ 8 void OnProcessCreated(const CREATE_PROCESS_DEBUG_INFO*); 9 void OnThreadCreated(const CREATE_THREAD_DEBUG_INFO*); 10 void OnException(const EXCEPTION_DEBUG_INFO*); 11 void OnProcessExited(const EXIT_PROCESS_DEBUG_INFO*); 12 void OnThreadExited(const EXIT_THREAD_DEBUG_INFO*); 13 void OnOutputDebugString(const OUTPUT_DEBUG_STRING_INFO*); 14 void OnRipEvent(const RIP_INFO*); 15 void OnDllLoaded(const LOAD_DLL_DEBUG_INFO*); 16 void OnDllUnloaded(const UNLOAD_DLL_DEBUG_INFO*); 17 18 19 void main() 20 { 21 //定义两个结构供api填值 22 STARTUPINFO si={0}; 23 24 PROCESS_INFORMATION pi = { 0 }; 25 26 if (!CreateProcess( 27 TEXT("C:\windows\notepad.exe"), //打开的程序名称,这里用TEXT宏包含了一下 28 NULL, 29 NULL, 30 NULL, 31 FALSE, 32 DEBUG_ONLY_THIS_PROCESS|CREATE_NEW_CONSOLE,//只调试当前进程 33 NULL, 34 NULL, //当前目录 35 &si, //启动信息 36 &pi)) //进程信息 37 { 38 std::cout<<GetLastError()<<endl; 39 return; 40 } 41 42 BOOL waitEvent = TRUE; 43 44 DEBUG_EVENT debugEvent; 45 while (waitEvent == TRUE && WaitForDebugEvent(&debugEvent, INFINITE)) { 46 //WaitForDebugEvent接收OS的调试消息事件 47 switch (debugEvent.dwDebugEventCode) { 48 49 case CREATE_PROCESS_DEBUG_EVENT: 50 OnProcessCreated(&debugEvent.u.CreateProcessInfo); 51 break; 52 53 case CREATE_THREAD_DEBUG_EVENT: 54 OnThreadCreated(&debugEvent.u.CreateThread); 55 break; 56 57 case EXCEPTION_DEBUG_EVENT: 58 OnException(&debugEvent.u.Exception); 59 break; 60 61 case EXIT_PROCESS_DEBUG_EVENT: 62 OnProcessExited(&debugEvent.u.ExitProcess); 63 //要注意这里是如何退出循环的:引入一个BOOL类型的waitEvent变量,在处理EXIT_PROCESS_DEBUG_EVENT之后将它的值改成FALSE 64 waitEvent = FALSE; 65 break; 66 67 case EXIT_THREAD_DEBUG_EVENT: 68 OnThreadExited(&debugEvent.u.ExitThread); 69 break; 70 71 case LOAD_DLL_DEBUG_EVENT: 72 OnDllLoaded(&debugEvent.u.LoadDll); 73 break; 74 75 case UNLOAD_DLL_DEBUG_EVENT: 76 OnDllUnloaded(&debugEvent.u.UnloadDll); 77 break; 78 79 case OUTPUT_DEBUG_STRING_EVENT: 80 OnOutputDebugString(&debugEvent.u.DebugString); 81 break; 82 83 case RIP_EVENT: 84 OnRipEvent(&debugEvent.u.RipInfo); 85 break; 86 87 default: 88 std::cout << TEXT("Unknown debug event.") << std::endl; 89 break; 90 } 91 92 if (waitEvent == TRUE) { 93 ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE); 94 } 95 else { 96 break;//waitEvent != TRUE 时退出循环 97 } 98 } 99 100 } 101 102 void OnProcessCreated(const CREATE_PROCESS_DEBUG_INFO* pInfo) { 103 104 std::cout<<TEXT("Debuggee was created.")<<endl; 105 } 106 107 108 109 void OnThreadCreated(const CREATE_THREAD_DEBUG_INFO* pInfo) { 110 111 std::cout<<TEXT("A new thread was created.")<<endl; 112 } 113 114 115 116 void OnException(const EXCEPTION_DEBUG_INFO* pInfo) { 117 118 std::cout<< TEXT("An exception was occured.")<<endl; 119 } 120 121 122 123 void OnProcessExited(const EXIT_PROCESS_DEBUG_INFO* pInfo) { 124 125 std::cout<<TEXT("Debuggee was terminated.")<<endl; 126 } 127 128 129 130 void OnThreadExited(const EXIT_THREAD_DEBUG_INFO* pInfo) { 131 132 std::cout<<TEXT("A thread was terminated.")<<endl; 133 } 134 135 136 137 void OnOutputDebugString(const OUTPUT_DEBUG_STRING_INFO* pInfo) { 138 139 std::cout<<TEXT("Debuggee outputed debug string.")<<endl; 140 } 141 142 143 144 void OnRipEvent(const RIP_INFO* pInfo) { 145 146 std::cout<<TEXT("A RIP_EVENT occured.")<<endl; 147 } 148 149 150 151 void OnDllLoaded(const LOAD_DLL_DEBUG_INFO* pInfo) { 152 153 std::cout<<TEXT("A dll was loaded.")<<endl; 154 } 155 156 157 158 void OnDllUnloaded(const UNLOAD_DLL_DEBUG_INFO* pInfo) { 159 160 std::cout<<TEXT("A dll was unloaded.")<<endl; 161 }
typedef struct _DEBUG_EVENT { DWORD dwDebugEventCode; //从这确定union联合域内是什么内容 DWORD dwProcessId; //PID DWORD dwThreadId; //线程ID 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;
//然后从各种event_info_struct中取得调试信息
--各种事件的处理--------------------------------------
RIP_EVENT
关于这种调试事件的文档资料非常少,只要输出一条信息或者干脆忽略它即可。
OUTPUT_DEBUG_STRING_EVENT
当被调试进程调用OutputDebugString()时就会引发该类调试事件,OUTPUT_DEBUG_STRING_INFO结构体描述了关于该事件的详细信息。
typedef struct _OUTPUT_DEBUG_STRING_INFO { LPSTR lpDebugStringData; //字符串在被调试进程的进程空间内的地址 WORD fUnicode; //是否Unicode编码 WORD nDebugStringLength; //以字符为单位的字符串的长度 } OUTPUT_DEBUG_STRING_INFO, *LPOUTPUT_DEBUG_STRING_INFO;
字符串是在被调试进程的地址空间内,我们就要使用ReadProcessMemory函数读取这个字符串。下面的代码展示了这个过程:
void OnOutputDebugString(const OUTPUT_DEBUG_STRING_INFO* pInfo) { BYTE* pBuffer = (BYTE*)malloc(pInfo->nDebugStringLength);//申请的大小刚好 SIZE_T bytesRead; //读取的字数 ReadProcessMemory( //读入buffer g_hProcess, //g_hProcess==pi.hProcess; pInfo->lpDebugStringData, pBuffer, pInfo->nDebugStringLength, &bytesRead); std::cout << TEXT("Debugger debug string: ") << pBuffer << std::endl; free(pBuffer); }
----------------读取寄存器和内存(OD的寄存器窗口和数据窗口)--------------------
获取寄存器的值
每个线程都有一个上下文环境(context),它包含了有关线程的大部分信息,例如线程栈的地址,线程当前正在执行的指令地址等。上下文环境保存在寄存器中,系统进行线程调度的时候会发生上下文切换,实际上就是将一个线程的上下文环境保存到内存中,然后将另一个线程的上下文环境装入寄存器。
在实际上下文中谈CONTEXT结构 CONTEXT结构包括以下部分: CONTEXT_CONTROL:包含CPU的控制寄存器,比如指今指针,堆栈指针,标志和函数返回地址..AX, BX, CX, DX, SI, DI CONTEXT_INTEGER:用于标识CPU的整数寄存器.DS, ES, FS, GS CONTEXT_FLOATING_POINT:用于标识CPU的浮点寄存器. CONTEXT_SEGMENTS:用于标识CPU的段寄存器.SS:SP, CS:IP, FLAGS, BP CONTEXT_DEBUG_REGISTER:用于标识CPU的调试寄存器. CONTEXT_EXTENDED_REGISTERS:用于标识CPU的扩展寄存器I CONTEXT_FULL:相当于CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS,即这三个标志的组合 我们可以使用GetThreadContext函数来查看线程内核对象的内部,并获取当前CPU寄存器状态的集合。 BOOL GetThreadContext ( HANDLE hThread, PCONTEXT pContext); 若要调用该函数,只需指定一个CONTEXT结构,对某些标志(该结构的ContextFlags成员)进行初始化,指明想要收回哪些寄存器,并将该结构的地址传递给GetThreadContext 。然后该函数将数据填入你要求的成员。 在调用GetThreadContext函数之前,应该调用SuspendThread,否则,线程可能刚好被调度,这样一来,线程的上下文就和所获取的信息不一致了。 示例代码如下: CONTEXT Context; //定义一个CONTEXT结构 Context.ContextFlags = CONTEXT_CONTROL; //告诉系统我们想获取线程控制寄存器的内容 GetThreadContext(hThread, &Context); //调用GetThreadContext获取相关信息 Ps:在调用GetThreadContext函数之前,必须首先初始化CONTEXT结构的ContextFlags成员。 要获得线程的所有重要的寄存器(也就是微软认为最常用的寄存器),应该像下面一样初始化ContextFlags: Context.ContextFlags = CONTEXT_FULL; 在WinNT. h头文件中,定义了CONTEXT_FULL为CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS。 当然,我们还可以通过调用SetThreadContext函数来改变结构中的成员,并把新的寄存器值放回线程的内核对象中 BOOL SetThreadContext ( HANDLE hThread, CONST CONTEXT *pContext); 同样,如果要改变哪个线程的上下文,应该先暂停该线程。 CONTEXT Context; //定义一个CONTEXT结构 SuspendThread(hThread); //挂起线程 Context.ContextFlags = CONTEXT_CONTROL; //获取当前上下文的值 GetThreadContext(hThread, &Context); Context.Eip = 0x00010000; //Eip字段存储的是指令指针,现在让指令指针指向地址 0x00010000; Context.ContextFlags = CONTEXT_CONTROL; SetThreadContext(hThread, &Context); //重新设置线程上下文 ResumeThread(hThread); //恢复线程,现在线程开始从0x00010000这个地方开始执行指令