調試一個程序分兩種情況:
1 打開這個程序。
2:這個程序已經是一個運行狀態了,將其進程進行附加。
打開進程
通過打開運行進程方式來調試進程需要調用一個API:
BOOL CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); //dwCreatetionFlags需要設置為DEBUG_PROCESS //打開進程示例程序 STARTUPINFOA sw{ 0 }; PROCESS_INFORMATION pInfo{ 0 }; auto retCP = CreateProcessA("F:\\Sublime Text 3\\sublime_text.exe", NULL,NULL,NULL,FALSE, DEBUG_PROCESS,NULL,NULL,&sw,&pInfo); if (retCP == 00) { cout << "打開進程失敗" << endl; return; }
附加進程
通過DebugActiveProcess這個API來附加到進程。
BOOL DebugActiveProcess(
DWORD dwProcessId
);
細節
無論是通過打開進程還是附加進程來實現調試,都只是開始調用的方式不一樣,在調試器和操作系統之間的交互方式都是相同的。
創建了調試進程后接下來就是死循環等待調試事件:
當調試進程時,被調試進程執行的一些操作事件將會被通知給調試器,比如dll的加載和卸載,thread的創建和銷毀,異常信息等等。當這些事件需要被發送到調試器時,Windows內核將首先掛起進程中的所有線程,然后把發生的事件通知給調試器,等待調試器的處理。
調試器通過WaitForDebugEvent API來等待調試事件,調試事件被封裝到了DEBUG_EVENT結構體中,調試器需要處理的就是循環接受調試事件然后處理DEBUG_EVENT結構體中傳遞過來的不同調試信息。
在發送事件event給調試器debugger時,被調試進程會被掛起,直到調試器調用了continueDebugEvent函數。
利用調試器原理實現附加反調試
利用調試器的原理,我們可以通過創建一個調試模式下的進程,那么這個以調試模式創建的進程就不能被其它進程拿去調試了,因為它已經在被一個我們自己的進程以調試模式創建了。
#include<iostream> #include<Windows.h> using namespace std; void TestDebugger() { STARTUPINFOA sw{ 0 }; PROCESS_INFORMATION pInfo{ 0 }; auto retCP = CreateProcessA("E:\\test\\Debug\\02 CStaticText.exe", NULL, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &sw, &pInfo); if (retCP == 0) { cout << "打開進程失敗" << endl; return; } while (TRUE) { DEBUG_EVENT debugEvent{ 0 }; auto rDebugEvent = WaitForDebugEvent(&debugEvent, -1); if (rDebugEvent) { cout << debugEvent.dwDebugEventCode << endl; //dwDebugEventCode是用來區分不同事件的事件碼,用來判斷事件 } ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);//在發送事件event給調試器debugger時,被調試進程會被掛起,直到調試器調用了continueDebugEvent函數 } } int main() { TestDebugger(); system("pause"); return 0; }
然后來測試一下,這樣啟動后,是否還能被調試器附加上:
這樣一來就不會被調試器附加上了。