調試篇——調試對象與調試事件

寫在前面

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

你如果是從中間插過來看的,請仔細閱讀 羽夏看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實現的:

iNTSTATUS
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;
}

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

下一篇

  調試篇——斷點與單步