10.0 探索API除錯事件原理

lyshark發表於2023-10-03

本章筆者將透過Windows平臺下自帶的除錯API介面實現對特定程式的動態轉存功能,首先簡單介紹一下關於除錯事件的相關資訊,除錯事件的建立需要依賴於DEBUG_EVENT這個特有的資料結構,該結構用於向偵錯程式報告除錯事件。當一個程式發生異常事件或者被偵錯程式附加時,就會產生對應的DEBUG_EVENT除錯事件,通常DEBUG_EVENT包含了多種除錯型別,包括異常事件、程式建立事件、執行緒建立事件、程式退出事件和執行緒退出事件等等,我們只需要動態捕捉這些除錯事件並作相應的處理即可實現更多有用的功能。

除錯事件通常可以分為如下幾種型別;

  • 異常事件 (Exception Event) - 發生了異常,例如訪問非法的記憶體、除以零或呼叫了無效的函式。
  • 程式建立事件 (Process Creation Event) - 當一個新程式被建立時傳送此事件。
  • 程式退出事件 (Process Exit Event) - 當一個程式退出時傳送此事件。
  • 執行緒建立事件 (Thread Creation Event) - 當一個新執行緒被建立時傳送此事件。
  • 執行緒退出事件 (Thread Exit Event) - 當一個執行緒退出時傳送此事件。
  • 除錯字串事件 (Debug String Event) - 當一個程式向其偵錯程式傳送字串訊息時傳送此事件。
  • 輸出字串事件 (Output String Event) - 當輸出除錯字串時傳送此事件。
  • 動態連結庫載入事件(LOAD_DLL_DEBUG_EVENT) - 當程式裝載 DLL 時傳送此事件。

當我們需要除錯一個程式時有兩種方式可以實現,第一種方式是透過CreateProcess()函式建立一個程式,並在呼叫函式時指定DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS則當程式被執行起來後自動進入到除錯狀態,另一種方式則是透過DebugActiveProcess()函式,該函式接受一個正在執行的程式PID號,可動態附加到一個已執行程式上而對其進行除錯。

一旦偵錯程式透過CreateProcess()附加並執行,下一步則是透過WaitForDebugEvent()用於等待一個除錯事件,當有除錯事件到達後系統會將除錯型別儲存到debugEvent.dwDebugEventCode這個變數內,此時我們可以透過判斷該變數內的引數來對特定的事件做出自定義處理操作,接著會透過ContinueDebugEvent()繼續等待下一個除錯事件的到來,我們以開啟一個程式並建立除錯為例,看一下如下程式碼片段;

#include <iostream>
#include <windows.h>

int main(int argc, char* argv[])
{
    DEBUG_EVENT debugEvent = { 0 };
    BOOL bRet = TRUE;

    // 建立除錯程式
    STARTUPINFO startupInfo = { 0 };
    PROCESS_INFORMATION pInfo = { 0 };
    GetStartupInfo(&startupInfo);

    // 建立除錯程式並設定 DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS 除錯事件
    bRet = CreateProcess("d://lyshark.exe", NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);
    if (!bRet)
    {
        return 0;
    }

    // 附加除錯程式
    // DebugActiveProcess(13940)

    // 無限迴圈等待除錯事件
    while (WaitForDebugEvent(&debugEvent, INFINITE))
    {
        // 根據除錯事件判斷
        switch (debugEvent.dwDebugEventCode)
        {
            // 異常除錯事件
        case EXCEPTION_DEBUG_EVENT:
            printf("異常處理事件 \n");
            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;
            // 裝載DLL除錯事件
        case LOAD_DLL_DEBUG_EVENT:
            printf("裝載DLL除錯事件 \n");
            break;
            // 解除安裝DLL除錯事件
        case UNLOAD_DLL_DEBUG_EVENT:
            printf("解除安裝DLL除錯事件 \n");
            break;
            // 輸出除錯資訊事件
        case OUTPUT_DEBUG_STRING_EVENT:
            printf("輸出除錯資訊事件 \n");
            break;
        }

        // 使偵錯程式能夠繼續以前報告除錯事件的執行緒
        bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
    }

    system("pause");
    return 0;
}

當編譯並執行上述程式後,讀者應該能看到如下圖所示的輸出效果,其中包括了各類除錯事件被觸發時的提示資訊,由於在除錯事件內沒有做任何操作,程式在載入後就被自動執行起來了;

本文作者: 王瑞
本文連結: https://www.lyshark.com/post/b8eecce4.html
版權宣告: 本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

相關文章