3.调试事件的处理
調試器會有一個循環一直判斷EventList有沒有調試事件有就取出來處理。
調試器創建形式
每一種收集調試事件API都不一樣,是因為它要收集調試事件的類型是不一樣的。
這是異常要收集的調試事件 typedef struct _EXCEPTION_DEBUG_INFO {EXCEPTION_RECORD ExceptionRecord;DWORD dwFirstChance; } EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;這是創建線程要收集的調試事件 typedef struct _CREATE_THREAD_DEBUG_INFO {HANDLE hThread;LPVOID lpThreadLocalBase;LPTHREAD_START_ROUTINE lpStartAddress; } CREATE_THREAD_DEBUG_INFO, *LPCREATE_THREAD_DEBUG_INFO; ..... .....下面代碼就類似拖拽程序到OD的框架。(W10記得以管理員身份運行VS)
#define dbgProcessName L"C:\\Users\\Administrator\\Desktop\\012.exe"int main() {BOOL nIsConinue = TRUE;DEBUG_EVENT debugEvent = { 0 };//1.創建調試進程STARTUPINFO startupInfo = { 0 };PROCESS_INFORMATION pInfo = {0};GetStartupInfo(&startupInfo);BOOL bRet = CreateProcess(dbgProcessName,NULL,NULL,NULL,TRUE,DEBUG_PROCESS||DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&startupInfo,&pInfo);if (bRet == FALSE){printf("CreateProcess error:%d\n", GetLastError());return 0;}//調試循環while (nIsConinue){bRet = WaitForDebugEvent(&debugEvent, INFINITE);//取DEBUG_EVENTif (!bRet){printf("WaitForDebugEvent error:%d\n", GetLastError());return 0;}switch (debugEvent.dwDebugEventCode){case EXCEPTION_DEBUG_EVENT:printf("異常:發生異常的地址:%X \n",debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);break;case CREATE_THREAD_DEBUG_EVENT:printf("創建線程\n");break;case CREATE_PROCESS_DEBUG_EVENT:printf("創建進程\n");break;case EXIT_THREAD_DEBUG_EVENT:printf("退出線程\n");break;case EXIT_PROCESS_DEBUG_EVENT:printf("退出進程\n");break;case LOAD_DLL_DEBUG_EVENT:printf("加載DLL\n");break;case UNLOAD_DLL_DEBUG_EVENT:printf("卸載DLL\n");break;default:break;}//DBG_CONTINUE 表示調試器已處理該異常//DBG_EXCEPTION_NOT_HANDLED 表示調試器沒有處理該異常,轉回到用戶態中執行,尋找可以處理該異常的異常處理器bRet = ContinueDebugEvent(debugEvent.dwProcessId,debugEvent.dwThreadId,DBG_CONTINUE);//ContinueDebugEvent 告訴被調試程序讓被調試程序繼續執行}}運行會發現有一個異常我們取這個地址看看
異常來自ntdll 的int 3,它為什么會在來個int 3呢?
進程創建過程:
<1>任何進程都是別的進程創建的: CreateProcess()
<2>進程的創建過程
ntdll 比較特殊它在內核的時候就已經創建好了。
LdrInitalizeThunk()
判斷這個進程是不是這個進程的第一條線程,如果是先調用 LdrpInitializeProcess 初始化進程,再將這個進程用到的其他DLL映射進來。
追上去
如果被用戶模式調試器附加了就會call DbgBreakPoint() ,如果沒有調試器附加就不會調用這個int 3。
調試器附加
int main() {BOOL nIsConinue = TRUE;DEBUG_EVENT debugEvent = { 0 };PROCESS_INFORMATION pInfo = {0};//1.附加進程if (!DebugActiveProcess(14148)){return 0;}//調試循環while (nIsConinue){BOOL bRet = WaitForDebugEvent(&debugEvent, INFINITE);//取DEBUG_EVENTif (!bRet){printf("WaitForDebugEvent error:%d\n", GetLastError());return 0;}switch (debugEvent.dwDebugEventCode){case EXCEPTION_DEBUG_EVENT:printf("異常:發生異常的地址:%X \n",debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);break;case CREATE_THREAD_DEBUG_EVENT:printf("創建線程\n");break;case CREATE_PROCESS_DEBUG_EVENT:printf("創建進程\n");break;case EXIT_THREAD_DEBUG_EVENT:printf("退出線程\n");break;case EXIT_PROCESS_DEBUG_EVENT:printf("退出進程\n");break;case LOAD_DLL_DEBUG_EVENT:printf("加載DLL\n");break;case UNLOAD_DLL_DEBUG_EVENT:printf("卸載DLL\n");break;default:break;}//DBG_CONTINUE 表示調試器已處理該異常//DBG_EXCEPTION_NOT_HANDLED 表示調試器沒有處理該異常,轉回到用戶態中執行,尋找可以處理該異常的異常處理器bRet = ContinueDebugEvent(debugEvent.dwProcessId,debugEvent.dwThreadId,DBG_CONTINUE);//ContinueDebugEvent 告訴被調試程序讓被調試程序繼續執行}}
我們通過創建的形式能捕獲到他的創建信息,但現在我通過附加,這些信息都已經執行完了,為什么也能捕獲到這些信息呢?
杜撰的調試消息
當我們附加了一個進程后,有人給我們發送了假的消息。
DebugActiveProcess 最終到內核會調用 NtDebugActiveProcess 。
它為什么要發送這些假的消息?
調試器都能看到所屬進程的DLL,是因為每個模塊加載的時候都能收集到調試事件,這個API在它的路上加了這些假消息就是希望給調試器提供一些必要的信息。
但這些假信息并不靠譜,比如這個模塊別人有意隱藏,PEB+0xc的位置Ldr 0xc到0x1c也有模塊的信息 ,成員如下。
但OD它是通過搜索內存來查找你的模塊的,具體看《內存管理》的章節
要點
<1>事件
<2>系統斷點
<3> ContinueDebugEvent 函數
總結