[Win32]一个调试器的实现(二)调试事件的处理
上一篇文章說到了調試循環的寫法,這回講一下調試器應該如何處理各種調試事件。
?
RIP_EVENT
關于這種調試事件的文檔資料非常少,即使提到也只是用“系統錯誤”或者“內部錯誤”一筆帶過。既然如此,我們也不需要對其進行什么處理,只要輸出一條信息或者干脆忽略它即可。
?
OUTPUT_DEBUG_STRING_EVENT
當被調試進程調用OutputDebugString時就會引發該類調試事件,OUTPUT_DEBUG_STRING_INFO結構體描述了關于該事件的詳細信息。在MSDN中,對該結構體各字段的解釋是:lpDebugStringData字段是字符串在被調試進程的進程空間內的地址;nDebugStringLength字段是以字符為單位的字符串的長度;fUnicode指示字符串是否Unicode編碼的。根據我個人的實驗觀察,發現只有第一個字段的解釋是對的。實際上,無論調用OutputDebugStringA還是OutputDebugStringW,字符串都會以ANSI編碼來表示。如果是調用OutputDebugStringW,那么會先將字符串轉換成ANSI編碼之后再調用OutputDebugStringA(這個過程在MSDN內有描述)。所以fUnicode的值永遠都是0,而nDebugStringLength是以字節為單位的字符串長度,而不是以字符為單位。
?
既然字符串是在被調試進程的地址空間內,我們就要使用ReadProcessMemory函數讀取這個字符串。下面的代碼展示了這個過程:
?1?void?OnOutputDebugString(const?OUTPUT_DEBUG_STRING_INFO*?pInfo)?{?2?
?3?????BYTE*?pBuffer?=?(BYTE*)malloc(pInfo->nDebugStringLength);
?4?
?5?????SIZE_T?bytesRead;
?6?
?7?????ReadProcessMemory(
?8?????????g_hProcess,
?9?????????pInfo->lpDebugStringData,
10?????????pBuffer,?
11?????????pInfo->nDebugStringLength,
12?????????&bytesRead);
13?
14?????int?requireLen?=?MultiByteToWideChar(
15?????????CP_ACP,
16?????????MB_PRECOMPOSED,
17?????????(LPCSTR)pBuffer,
18?????????pInfo->nDebugStringLength,
19?????????NULL,
20?????????0);
21?
22?????TCHAR*?pWideStr?=?(TCHAR*)malloc(requireLen?*?sizeof(TCHAR));
23?
24?????MultiByteToWideChar(
25?????????CP_ACP,
26?????????MB_PRECOMPOSED,
27?????????(LPCSTR)pBuffer,
28?????????pInfo->nDebugStringLength,
29?????????pWideStr,
30?????????requireLen);
31?
32?????std::wcout?<<?TEXT("Debuggee?debug?string:?")?<<?pWideStr?<<??std::endl;
33?
34?????free(pWideStr);
35?????free(pBuffer);
36?}
?
g_hProcess是類型為HANDLE的全局變量。在啟動被調試進程之后就將它的句柄賦給了g_hProcess。
?
LOAD_DLL_DEBUG_EVENT
加載一個DLL模塊之后引發該類調試事件,LOAD_DLL_DEBUG_INFO結構體描述了它的詳細信息。lpImageName這個字段可能會使你想在調試器中輸出DLL的文件名,然而這行不通。MSDN上的解釋是,lpImageName的值是文件名字符串在被調試進程的進程空間內的地址,但是這個值可能為NULL,即使不為NULL,通過ReadProcessMemory讀取到的內容也可能是NULL。所以,想通過這個字段獲取DLL的文件名并不可靠。
?
那么,通過hFile字段來獲取文件名如何?沒有Windows API可以直接通過文件句柄獲取文件名,想要這么做的話必須繞一個大圈子,詳細的方法請參考:http://blog.csdn.net/bodybo/archive/2006/08/28/1131346.aspx。
?
實際上hFile是與dwDebugInfoFileOffset和nDebugInfoSize一起使用的,用于獲取DLL文件的調試信息。一般情況下我們不需要這么做,所以只要調用CloseHandle關閉這個句柄即可。記住!關閉這個句柄非常重要,如果不這么做的話會引起資源泄漏。
?
我的想法是,先通過EnumProcessModules枚舉被調試進程的模塊,然后通過GetModuleInformation獲取模塊的基地址,將這個基地址與LOAD_DLL_DEBUG_INFO結構體的lpBaseOfDll字段進行比較,如果相等的話就通過GetModuleFileNameEx獲取DLL的文件名??墒俏以趯嶒炦@個方法的時候EnumProcessModules總是返回FALSE,GetLastError返回299,這是什么原因呢?
?
這可能是因為當調試器在處理這類調試事件時,被調試進程還沒有啟動完畢,所需要的模塊還未全部加載完成,所以無法獲取它的模塊信息。
?
UNLOAD_DLL_DEBUG_EVENT
卸載一個DLL模塊的時候引發該類調試事件。一般情況下只要輸出一條信息或者忽略它即可。
?
CREATE_PROCESS_DEBUG_EVENT
創建進程之后的第一個調試事件,CREATE_PROCESS_DEBUG_INFO結構體描述了該類調試事件的詳細信息。該結構體有三個字段是句柄,分別是hFile,hProcess和hThread,同樣要記得使用CloseHandle關閉它們!
?
EXIT_PROCESS_DEBUG_EVENT
被調試進程結束時引發此類調試事件,EXIT_PROCESS_DEBUG_INFO結構體描述了它的詳細信息?;蛟S你能做的只有輸出dwExitCode這個字段的值。
?
CREATE_THREAD_DEBUG_EVENT
創建一個線程之后引發此類調試事件,CREATE_THREAD_DEBUG_INFO結構體描述了它的詳細信息。同樣要記住用CloseHandle關閉hThread字段!
?
EXIT_THREAD_DEBUG_EVENT
一個線程結束之后引發此類調試事件,EXIT_THREAD_DEBUG_INFO結構體描述了它的詳細信息。對此同樣也只能輸出dwExitCode的值。
?
EXCEPTION_DEBUG_EVENT
發生異常時引發此類調試事件,EXCEPTION_DEBUG_INFO結構體描述了它的詳細信息。對這種調試事件的處理是最麻煩的,因為異常的種類非常多,對每種異常的處理也不相同。另外,此類調試事件也是實現斷點和單步執行的關鍵。
?
由于關于這類調試事件的篇幅太多,因此將其放到下一篇文章中講解。
?
示例代碼
這次的示例代碼基本上跟上次的示例代碼基本上一樣,只是添加了本文中描述的內容,改動非常有限,所以如果覺得沒必要的話就不要下載了。
http://files.cnblogs.com/zplutor/MiniDebugger2.rar
轉載于:https://www.cnblogs.com/zplutor/archive/2011/03/06/1972540.html
總結
以上是生活随笔為你收集整理的[Win32]一个调试器的实现(二)调试事件的处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 控制游戏中物体的移动速度
- 下一篇: Sonata 0.7