除錯篇——除錯物件與除錯事件

寂靜的羽夏發表於2022-03-02

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


除錯物件

  我們都知道在高2G的空間是共用的,除錯程式和被除錯程式就是通過核心物件建立起聯絡,示意圖如下:

除錯篇——除錯物件與除錯事件

  如何在偵錯程式與被除錯程式之間通過呼叫API建立起聯絡呢?一個是通過CreateProcess在引數帶上除錯標誌以建立程式的方式進行,另一個就是DebugActiveProcess,直接附加正在執行的程式。由於DebugActiveProcess比較簡單,就以該函式進行介紹,如下是它的執行流程:

graph TD kernel32!DebugActiveProcess --> 1[kernel32!DbgUiConnectToDbg] -..-> ntdll!ZwCreateDebugObject --> nt!NtCreateDebugObject 1 --> kernel32!DbgUiDebugActiveProcess -..-> ntdll!DbgUiDebugActiveProcess --> ntdll!NtDebugActiveProcess --> nt!NtDebugActiveProcess --> nt!DbgkpSetProcessDebugObject

  完整具體細節流程將會在總結與提升進行。我們先看看所謂的除錯物件是啥:

typedef struct _DEBUG_OBJECT {
    //
    // Event thats set when the EventList is populated.
    //
    KEVENT EventsPresent;
    //
    // Mutex to protect the structure
    //
    FAST_MUTEX Mutex;
    //
    // Queue of events waiting for debugger intervention
    //
    LIST_ENTRY EventList;
    //
    // Flags for the object
    //
    ULONG Flags;
} DEBUG_OBJECT, *PDEBUG_OBJECT;

  注意,該結構體符號裡面沒有,這個是從WRK獲得的。
  DEBUG_OBJECT這個的作用是啥,它是偵錯程式程式和被除錯程式的橋樑,如果這個橋被拆了,它們就無法通訊,也就無法除錯:

除錯篇——除錯物件與除錯事件

  接下來我們檢視它是如何搭起這座橋的:

BOOL __stdcall DebugActiveProcess(DWORD dwProcessId)
{
  int res; // eax
  BOOL result; // eax
  void *handle; // esi
  int res_1; // edi

  res = DbgUiConnectToDbg();                    // 建立結構體,與除錯物件建立連結
  if ( res >= 0 )
  {
    result = ProcessIdToHandle(dwProcessId);
    handle = result;
    if ( result )
    {
      res_1 = DbgUiDebugActiveProcess(result);
      NtClose(handle);
      if ( res_1 >= 0 )
      {
        result = 1;
      }
      else
      {
        BaseSetLastNTError(res_1);
        result = 0;
      }
    }
  }
  else
  {
    BaseSetLastNTError(res);
    result = 0;
  }
  return result;
}

  這個函式上來呼叫DbgUiConnectToDbg,通過名字猜測這個函式對於我們很重要,我們來跟過去看看:

int __stdcall DbgUiConnectToDbg()
{
  int result; // ecx
  OBJECT_ATTRIBUTES attr; // [esp+0h] [ebp-18h] BYREF

  result = 0;
  if ( !NtCurrentTeb()->DbgSsReserved[1] )
  {
    attr.Length = 24;
    attr.RootDirectory = 0;
    attr.Attributes = 0;
    attr.ObjectName = 0;
    attr.SecurityDescriptor = 0;
    attr.SecurityQualityOfService = 0;
    result = ZwCreateDebugObject(&NtCurrentTeb()->DbgSsReserved[1], 0x1F000Fu, &attr, 1u);
  }
  return result;
}

  可以看出,該函式會建立除錯物件,並把它放到TEBDbgSsReserved[1]成員,也就是該偏移0xF24位置,這時候偵錯程式與除錯物件的聯絡就搭建好了。
  下面就開始將被除錯物件與除錯物件建立起聯絡,我們來看看DbgUiDebugActiveProcess函式:

int __stdcall DbgUiDebugActiveProcess(HANDLE Handle)
{
  int res; // esi

  res = NtDebugActiveProcess(Handle, NtCurrentTeb()->DbgSsReserved[1]);
  if ( res >= 0 )
  {
    res = DbgUiIssueRemoteBreakin(Handle);
    if ( res < 0 )
      DbgUiStopDebugging(Handle);
  }
  return res;
}

  可以看到,該函式又會呼叫NtDebugActiveProcess實現建立起聯絡,如下是其虛擬碼:

NTSTATUS __stdcall NtDebugActiveProcess(HANDLE Process, HANDLE DebugObject)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  AccessMode = KeGetCurrentThread()->PreviousMode;
  result = ObReferenceObjectByHandle(Process, 0x800u, PsProcessType, AccessMode, &Process, 0);
  if ( result >= 0 )
  {
    process = Process;
    if ( Process == KeGetCurrentThread()->ApcState.Process || Process == PsInitialSystemProcess )
    {
      res = STATUS_ACCESS_DENIED;
    }
    else
    {
      res = ObReferenceObjectByHandle(DebugObject, 2u, DbgkDebugObjectType, AccessMode, &Process, 0);
      if ( res >= 0 )
      {
        if ( ExAcquireRundownProtection(&process->RundownProtect) )
        {
          v5 = DbgkpPostFakeProcessCreateMessages(&process->Pcb, Process, &DebugObject);
          res = DbgkpSetProcessDebugObject(process, Process, v5, DebugObject);
          ExReleaseRundownProtection(&process->RundownProtect);
        }
        else
        {
          res = STATUS_PROCESS_IS_TERMINATING;
        }
        ObfDereferenceObject(Process);
      }
    }
    ObfDereferenceObject(process);
    result = res;
  }
  return result;
}

  有了前置知識,我就不贅述前面的流程,我們把注意力放到DbgkpSetProcessDebugObject這個函式上:

NTSTATUS
DbgkpSetProcessDebugObject (
    IN PEPROCESS Process,
    IN PDEBUG_OBJECT DebugObject,
    IN NTSTATUS MsgStatus,
    IN PETHREAD LastThread
    )
/*++

Routine Description:

    Attach a debug object to a process.

Arguments:

    Process     - Process to be debugged
    DebugObject - Debug object to attach
    MsgStatus   - Status from queing the messages
    LastThread  - Last thread seen in attach loop.

Return Value:

    NTSTATUS - Status of call.

--*/
{
    NTSTATUS Status;
    PETHREAD ThisThread;
    LIST_ENTRY TempList;
    PLIST_ENTRY Entry;
    PDEBUG_EVENT DebugEvent;
    BOOLEAN First;
    PETHREAD Thread;
    BOOLEAN GlobalHeld;
    PETHREAD FirstThread;

    PAGED_CODE ();

    ThisThread = PsGetCurrentThread ();

    InitializeListHead (&TempList);

    First = TRUE;
    GlobalHeld = FALSE;

    if (!NT_SUCCESS (MsgStatus)) {
        LastThread = NULL;
        Status = MsgStatus;
    } else {
        Status = STATUS_SUCCESS;
    }

    //
    // Pick up any threads we missed
    //
    if (NT_SUCCESS (Status)) {

        while (1) {
            //
            // Acquire the debug port mutex so we know that any new threads will
            // have to wait to behind us.
            //
            GlobalHeld = TRUE;

            ExAcquireFastMutex (&DbgkpProcessDebugPortMutex);

            //
            // If the port has been set then exit now.
            //
            if (Process->DebugPort != NULL) {
                Status = STATUS_PORT_ALREADY_SET;
                break;
            }
            //
            // Assign the debug port to the process to pick up any new threads
            //
            Process->DebugPort = DebugObject;

            //
            // Reference the last thread so we can deref outside the lock
            //
            ObReferenceObject (LastThread);

            //
            // Search forward for new threads
            //
            Thread = PsGetNextProcessThread (Process, LastThread);
            if (Thread != NULL) {

                //
                // Remove the debug port from the process as we are
                // about to drop the lock
                //
                Process->DebugPort = NULL;

                ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);

                GlobalHeld = FALSE;

                ObDereferenceObject (LastThread);

                //
                // Queue any new thread messages and repeat.
                //

                Status = DbgkpPostFakeThreadMessages (Process,
                                                      DebugObject,
                                                      Thread,
                                                      &FirstThread,
                                                      &LastThread);
                if (!NT_SUCCESS (Status)) {
                    LastThread = NULL;
                    break;
                }
                ObDereferenceObject (FirstThread);
            } else {
                break;
            }
        }
    }

    //
    // Lock the debug object so we can check its deleted status
    //
    ExAcquireFastMutex (&DebugObject->Mutex);

    //
    // We must not propagate a debug port thats got no handles left.
    //

    if (NT_SUCCESS (Status)) {
        if ((DebugObject->Flags&DEBUG_OBJECT_DELETE_PENDING) == 0) {
            PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT|PS_PROCESS_FLAGS_CREATE_REPORTED);
            ObReferenceObject (DebugObject);
        } else {
            Process->DebugPort = NULL;
            Status = STATUS_DEBUGGER_INACTIVE;
        }
    }

    for (Entry = DebugObject->EventList.Flink;
         Entry != &DebugObject->EventList;
         ) {

        DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
        Entry = Entry->Flink;

        if ((DebugEvent->Flags&DEBUG_EVENT_INACTIVE) != 0 && DebugEvent->BackoutThread == ThisThread) {
            Thread = DebugEvent->Thread;

            //
            // If the thread has not been inserted by CreateThread yet then don't
            // create a handle. We skip system threads here also
            //
            if (NT_SUCCESS (Status) && Thread->GrantedAccess != 0 && !IS_SYSTEM_THREAD (Thread)) {
                //
                // If we could not acquire rundown protection on this
                // thread then we need to suppress its exit message.
                //
                if ((DebugEvent->Flags&DEBUG_EVENT_PROTECT_FAILED) != 0) {
                    PS_SET_BITS (&Thread->CrossThreadFlags,
                                 PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG);
                    RemoveEntryList (&DebugEvent->EventList);
                    InsertTailList (&TempList, &DebugEvent->EventList);
                } else {
                    if (First) {
                         DebugEvent->Flags &= ~DEBUG_EVENT_INACTIVE;
                        KeSetEvent (&DebugObject->EventsPresent, 0, FALSE);
                        First = FALSE;
                    }
                    DebugEvent->BackoutThread = NULL;
                    PS_SET_BITS (&Thread->CrossThreadFlags,
                                 PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG);

                }
            } else {
                RemoveEntryList (&DebugEvent->EventList);
                InsertTailList (&TempList, &DebugEvent->EventList);
            }

            if (DebugEvent->Flags&DEBUG_EVENT_RELEASE) {
                DebugEvent->Flags &= ~DEBUG_EVENT_RELEASE;
                ExReleaseRundownProtection (&Thread->RundownProtect);
            }

        }
    }

    ExReleaseFastMutex (&DebugObject->Mutex);

    if (GlobalHeld) {
        ExReleaseFastMutex (&DbgkpProcessDebugPortMutex);
    }

    if (LastThread != NULL) {
        ObDereferenceObject (LastThread);
    }

    while (!IsListEmpty (&TempList)) {
        Entry = RemoveHeadList (&TempList);
        DebugEvent = CONTAINING_RECORD (Entry, DEBUG_EVENT, EventList);
        DbgkpWakeTarget (DebugEvent);
    }

    if (NT_SUCCESS (Status)) {
        DbgkpMarkProcessPeb (Process);
    }

    return Status;
}

  由於虛擬碼的結果和它差不多,重新命名比較複雜,就用WRK好了。然後你就關注到關鍵程式碼:

//
// Assign the debug port to the process to pick up any new threads
//
Process->DebugPort = DebugObject;

  這樣被除錯程式和除錯物件建立起聯絡。

除錯事件的收集

  既然偵錯程式程式和被除錯程式是通過除錯物件建立聯絡的,如果要通訊就需要除錯事件,DEBUG_OBJECTEventList存放除錯事件。
  除錯事件不可能僅僅一種,如下是其列舉:

typedef enum _DBGKM_APINUMBER
{
    DbgKmExceptionApi = 0,  //異常
    DbgKmCreateThreadApi = 1,   //建立執行緒
    DbgKmCreateProcessApi = 2,  //建立程式
    DbgKmExitThreadApi = 3, //執行緒退出
    DbgKmExitProcessApi = 4,    //程式退出
    DbgKmLoadDllApi = 5,    //載入DLL
    DbgKmUnloadDllApi = 6,  //解除安裝DLL
    DbgKmErrorReportApi = 7,    //已廢棄
    DbgKmMaxApiNumber = 8,  //最大值
} DBGKM_APINUMBER; 

  前7個就是我們能夠使用的除錯事件種類,那麼這些除錯事件是如何採集的呢?
  在建立程式、執行緒的函式中,會走如下流程:

graph LR PspUserThreadStartup --> DbgkCreateThread --> DbgkpSendApiMessage

  在退出執行緒、程式的函式中,會走如下流程:

graph LR PspExitThread --> DbgkExitThread/DbgkExitProcess --> DbgkpSendApiMessage

  在載入模組的函式中,會走如下流程:

graph LR NtMapViewOfSection --> DbgkMapViewOfSection --> DbgkpSendApiMessage

  在解除安裝模組的函式中,會走如下流程:

graph LR NtUnMapViewOfSection --> DbgkUnMapViewOfSection --> DbgkpSendApiMessage

  在異常的函式中,會走如下流程:

graph LR KiDispatchException --> DbgkForwardException --> DbgkpSendApiMessage

  這一切的一切都是DbgkpSendApiMessage來實現的除錯事件的收集操作,我們來看看它的程式碼:

NTSTATUS
DbgkpSendApiMessage(
    IN OUT PDBGKM_APIMSG ApiMsg,
    IN BOOLEAN SuspendProcess
    )

/*++

Routine Description:

    This function sends the specified API message over the specified
    port. It is the caller's responsibility to format the API message
    prior to calling this function.

    If the SuspendProcess flag is supplied, then all threads in the
    calling process are first suspended. Upon receipt of the reply
    message, the threads are resumed.

Arguments:

    ApiMsg - Supplies the API message to send.

    SuspendProcess - A flag that if set to true, causes all of the
        threads in the process to be suspended prior to the call,
        and resumed upon receipt of a reply.

Return Value:

    NTSTATUS.

--*/

{
    NTSTATUS st;
    PEPROCESS Process;

    PAGED_CODE();

    if ( SuspendProcess ) {
        SuspendProcess = DbgkpSuspendProcess();
    }

    ApiMsg->ReturnedStatus = STATUS_PENDING;

    Process = PsGetCurrentProcess();

    PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATE_REPORTED);

    st = DbgkpQueueMessage (Process, PsGetCurrentThread (), ApiMsg, 0, NULL);

    ZwFlushInstructionCache (NtCurrentProcess (), NULL, 0);
    if ( SuspendProcess ) {
        DbgkpResumeProcess();
    }

    return st;
}

  通過交叉引用,我們發現了呼叫該函式的呼叫者:

除錯篇——除錯物件與除錯事件

  PDBGKM_APIMSG是個結構體,存放訊息結構,每種訊息都有自己的訊息結構。下面我們來看看它的成員:

typedef struct _DBGKM_APIMSG {
    PORT_MESSAGE h;
    DBGKM_APINUMBER ApiNumber;
    NTSTATUS ReturnedStatus;
    union {
        DBGKM_EXCEPTION Exception;
        DBGKM_CREATE_THREAD CreateThread;
        DBGKM_CREATE_PROCESS CreateProcessInfo;
        DBGKM_EXIT_THREAD ExitThread;
        DBGKM_EXIT_PROCESS ExitProcess;
        DBGKM_LOAD_DLL LoadDll;
        DBGKM_UNLOAD_DLL UnloadDll;
    } u;
} DBGKM_APIMSG, *PDBGKM_APIMSG;

  SuspendProcess引數指示要不要把本程式內除了自己之外的其他執行緒掛起,比如int3,但有些不需要比如載入模組等操作。
  綜上所述,DbgkSendApiMessage是除錯事件收集的總入口,如果在這裡掛鉤子,偵錯程式將無法除錯。
  剩下所有的與除錯事件的收集的細節將會在總結與提升篇進行介紹。

除錯事件的處理

  有關除錯事件的處理,我們簡單用API實現一個偵錯程式,如下是通過CreateProcess實現的偵錯程式:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    char filename[]= "C:\\WINDOWS\\NOTEPAD.EXE";
    STARTUPINFO si ={sizeof(STARTUPINFO)};
    PROCESS_INFORMATION pi ;
    bool isContinue = true;
    DEBUG_EVENT dbgEvent;
        
    BOOL ret =CreateProcess(NULL,filename,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi);

    if (ret)
    {
        while (isContinue)
        {
            ret = WaitForDebugEvent(&dbgEvent,INFINITE);
            if (!ret)
            {
                printf("WaitForDebugEvent 出錯:%d",GetLastError());
                break;
            }

            switch (dbgEvent.dwDebugEventCode)
            {
            case EXCEPTION_DEBUG_EVENT:
                puts("EXCEPTION_DEBUG_EVENT");
                break;
            case CREATE_THREAD_DEBUG_EVENT:
                puts("CREATE_THREAD_DEBUG_EVENT");
                break;
            case CREATE_PROCESS_DEBUG_EVENT:
                puts("CREATE_PROCESS_DEBUG_EVENT");
                break;
            case EXIT_THREAD_DEBUG_EVENT:
                puts("EXIT_THREAD_DEBUG_EVENT");
                break;
            case EXIT_PROCESS_DEBUG_EVENT:
                puts("EXIT_PROCESS_DEBUG_EVENT");
                break;
            case LOAD_DLL_DEBUG_EVENT:
                puts("LOAD_DLL_DEBUG_EVENT");
                break;     
            case UNLOAD_DLL_DEBUG_EVENT:
                puts("UNLOAD_DLL_DEBUG_EVENT");
                break;
            case OUTPUT_DEBUG_STRING_EVENT:
                puts("OUTPUT_DEBUG_STRING_EVENT");
                break;
            default:
                break;
            }

            ret = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,DBG_CONTINUE);

        }
    }
    else
    {
        printf("建立程式失敗:%d\n",GetLastError());
    }
        
    system("pause");
    return 0;
}

  編譯執行後,它的輸出如下:

CREATE_PROCESS_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
EXCEPTION_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT

  你如果細心的話可能會發現EXCEPTION_DEBUG_EVENT,這個我們記事本正常執行,沒有出現異常,為啥會有這條呢?我們來看看地址是什麼,修改程式碼區域如下:

case EXCEPTION_DEBUG_EVENT:
    printf("EXCEPTION_DEBUG_EVENT:0x%X\n",dbgEvent.u.Exception.ExceptionRecord.ExceptionAddress);
    break;

  再次編譯執行,結果如下:

CREATE_PROCESS_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
EXCEPTION_DEBUG_EVENT:0x7C92120E
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT

  0x7C92120E這個地址一定在Dll裡面,這個就是所謂的系統斷點,它在LdrpInitializeProcess函式實現的,如下是關鍵虛擬碼:

if ( peb->BeingDebugged )
{
  DbgBreakPoint();
  ShowSnaps = (peb->NtGlobalFlag & 2) != 0;
}

  我們回顧一下程式的建立過程:

  1. 對映EXE檔案
  2. 建立核心物件EPROCESS
  3. 對映ntdll.dll
  4. 建立執行緒核心物件ETHREAD
  5. 系統啟動執行緒,對映DLL(ntdll.LdrInitializeThunk)執行緒開始執行

  LdrInitializeThunk會判斷這個是不是建立的第一個執行緒,如果是就會呼叫LdrpInitializeProcess,然後判斷PEBBeingDebugged成員來決定是否下系統斷點。與此同時,我們可以通過這個實現反除錯。
  我們再看看偵錯程式是如何到系統斷點的:

除錯篇——除錯物件與除錯事件

  接下來,我們通過附加的形式進行,實驗程式碼如下:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    bool isContinue = true;
    DEBUG_EVENT dbgEvent;
    int pid;

    puts("請輸入除錯程式的 PID:");
    scanf("%d",&pid);
        
    BOOL ret =DebugActiveProcess(pid);

    if (ret)
    {
        while (isContinue)
        {
            ret = WaitForDebugEvent(&dbgEvent,INFINITE);
            if (!ret)
            {
                printf("WaitForDebugEvent 出錯:%d",GetLastError());
                break;
            }

            switch (dbgEvent.dwDebugEventCode)
            {
            case EXCEPTION_DEBUG_EVENT:
                puts("EXCEPTION_DEBUG_EVENT");
                break;
            case CREATE_THREAD_DEBUG_EVENT:
                puts("CREATE_THREAD_DEBUG_EVENT");
                break;
            case CREATE_PROCESS_DEBUG_EVENT:
                puts("CREATE_PROCESS_DEBUG_EVENT");
                break;
            case EXIT_THREAD_DEBUG_EVENT:
                puts("EXIT_THREAD_DEBUG_EVENT");
                break;
            case EXIT_PROCESS_DEBUG_EVENT:
                puts("EXIT_PROCESS_DEBUG_EVENT");
                break;
            case LOAD_DLL_DEBUG_EVENT:
                puts("LOAD_DLL_DEBUG_EVENT");
                break;     
            case UNLOAD_DLL_DEBUG_EVENT:
                puts("UNLOAD_DLL_DEBUG_EVENT");
                break;
            case OUTPUT_DEBUG_STRING_EVENT:
                puts("OUTPUT_DEBUG_STRING_EVENT");
                break;
            default:
                break;
            }

            ret = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,DBG_CONTINUE);

        }
    }
    else
    {
        printf("DebugActiveProcess 失敗:%d\n",GetLastError());
    }
        
    system("pause");
    return 0;
}

  這次除錯開啟的記事本,結果如下:

請輸入除錯程式的 PID:
1088
CREATE_PROCESS_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
CREATE_THREAD_DEBUG_EVENT
EXCEPTION_DEBUG_EVENT:0x7C92120E
EXIT_THREAD_DEBUG_EVENT

  這次你會奇怪的發現,既然我們都是已經建立好的執行緒了,為啥還有載入DLL,建立執行緒,系統斷點的訊息呢?因為這些訊息都是假的。這個功能是通過DbgkpPostFakeProcessCreateMessages實現的:

NTSTATUS
DbgkpPostFakeProcessCreateMessages (
    IN PEPROCESS Process,
    IN PDEBUG_OBJECT DebugObject,
    IN PETHREAD *pLastThread
    )
/*++

Routine Description:

    This routine posts the faked initial process create, thread create and mudule load messages

Arguments:

    ProcessHandle     - Handle to a process to be debugged
    DebugObjectHandle - Handle to a debug object

Return Value:

    None.

--*/
{
    NTSTATUS Status;
    KAPC_STATE ApcState;
    PETHREAD Thread;
    PETHREAD LastThread;

    PAGED_CODE ();

    //
    // Attach to the process so we can touch its address space
    //
    KeStackAttachProcess(&Process->Pcb, &ApcState);

    Status = DbgkpPostFakeThreadMessages (Process,
                                          DebugObject,
                                          NULL,
                                          &Thread,
                                          &LastThread);

    if (NT_SUCCESS (Status)) {
        Status = DbgkpPostFakeModuleMessages (Process, Thread, DebugObject);
        if (!NT_SUCCESS (Status)) {
            ObDereferenceObject (LastThread);
            LastThread = NULL;
        }
        ObDereferenceObject (Thread);
    } else {
        LastThread = NULL;
    }

    KeUnstackDetachProcess(&ApcState);

    *pLastThread = LastThread;

    return Status;
}

  具體的我就不贅述了,有關本篇文章就介紹這麼多。

下一篇

  除錯篇——斷點與單步

相關文章