首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

Win32 除錯 API 實戰(一):核心架構與基本除錯器實作

     編輯:組合語言

前言

Win32 提供一組完整的除錯原語,讓開發者能撰寫自己的除錯器。本系列第一篇將系統性介紹 Win32 除錯 API 的核心概念、事件模型、基本流程,並實作一個可顯示程序基礎資訊的簡易除錯器。

Win32 除錯 API 能做什麼?

  • 建立或附著至目標程序進行除錯
  • 取得目標程序的低層資訊(PID、入口點、映像基址等)
  • 接收除錯事件通知(程序/執行緒建立/結束、DLL 載入/卸載、例外發生等)
  • 修改目標程序的記憶體、暫存器、執行流程

簡言之:這些 API 提供了打造除錯器的所有基礎塊。

使用 Win32 除錯 API 的標準流程

  1. 取得除錯目標:
    • CreateProcess(..., DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, ...):建立新程序並除錯。DEBUG_ONLY_THIS_PROCESS 可避免同時除錯子程序。
    • 或 DebugActiveProcess(dwProcessId):附著至已執行中的程序。
  2. 等待除錯事件:呼叫 WaitForDebugEvent(&DebugEvent, INFINITE) 阻塞等待。事件資訊填入 DEBUG_EVENT 結構。
  3. 處理除錯事件:檢查 dwDebugEventCode 判斷事件類型,依 u 聯合體的對應成員取得詳細資訊。
  4. 讓目標繼續執行:呼叫 ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus)。dwContinueStatus 通常為 DBG_CONTINUE(忽略例外)或 DBG_EXCEPTION_NOT_HANDLED(交由目標自身例外處理機制)。
  5. 循環至程序結束:重複步驟 2-4,直到收到 EXIT_PROCESS_DEBUG_EVENT。

DEBUG_EVENT 結構詳細解析

DEBUG_EVENT STRUCT
    dwDebugEventCode dd ?   ; 事件類型
    dwProcessId      dd ?   ; 事件所屬程序 ID
    dwThreadId       dd ?   ; 事件所屬執行緒 ID
    u                DEBUGSTRUCT <> ; 聯合體,依事件類型解釋
DEBUG_EVENT ENDS

主要事件類型

事件代碼意義對應聯合體成員
CREATE_PROCESS_DEBUG_EVENT程序建立(第一個事件)u.CreateProcessInfo (CREATE_PROCESS_DEBUG_INFO)
EXIT_PROCESS_DEBUG_EVENT程序結束u.ExitProcess (EXIT_PROCESS_DEBUG_INFO)
CREATE_THREAD_DEBUG_EVENT執行緒建立u.CreateThread (CREATE_THREAD_DEBUG_INFO)
EXIT_THREAD_DEBUG_EVENT執行緒結束u.ExitThread (EXIT_THREAD_DEBUG_INFO)
LOAD_DLL_DEBUG_EVENTDLL 載入u.LoadDll (LOAD_DLL_DEBUG_INFO)
UNLOAD_DLL_DEBUG_EVENTDLL 卸載u.UnloadDll (UNLOAD_DLL_DEBUG_INFO)
EXCEPTION_DEBUG_EVENT例外發生u.Exception (EXCEPTION_DEBUG_INFO)
OUTPUT_DEBUG_STRING_EVENT目標呼叫 OutputDebugStringu.DebugString (OUTPUT_DEBUG_STRING_INFO)
RIP_EVENT系統除錯錯誤u.RipInfo (RIP_INFO)

重點:CREATE_PROCESS_DEBUG_INFO

此結構包含:hFile(檔案控制代碼)、hProcess、hThread、lpBaseOfImage(映像基址)、lpStartAddress(入口點位址)等關鍵資訊,是除錯器啟動時最重要的資料來源。

ContinueDebugEvent 與例外處理策略

ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueStatus:DWORD
  • DBG_CONTINUE:告知系統除錯器已處理例外,目標程式繼續執行。必須用於第一個 EXCEPTION_BREAKPOINT(程式入口點前的 INT 3),否則 Windows NT 會拒絕讓目標執行。
  • DBG_EXCEPTION_NOT_HANDLED:將例外交回目標程式自身的例外處理鏈。適用於大多數非中斷點例外。

標準除錯迴路架構

.while TRUE
    invoke WaitForDebugEvent, addr DebugEvent, INFINITE
    .break .if DebugEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT
    ; 此處處理各類事件...
    invoke ContinueDebugEvent, DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw

完整範例:顯示程序基礎資訊的除錯器

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\user32.lib

.data
AppName db "Win32 Debug Example no.1",0
ofn OPENFILENAME <>
FilterString db "Executable Files",0,"*.exe",0
db "All Files",0,"*.*",0,0
ExitProc db "The debuggee exits",0
NewThread db "A new thread is created",0
EndThread db "A thread is destroyed",0
ProcessInfo db "File Handle: %lx ",0dh,0Ah
db "Process Handle: %lx",0Dh,0Ah
db "Thread Handle: %lx",0Dh,0Ah
db "Image Base: %lx",0Dh,0Ah
db "Start Address: %lx",0

.data?
buffer db 512 dup(?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>

.code
start:
    ; 開啟檔案對話框選擇目標程式
    mov ofn.lStructSize, sizeof ofn
    mov ofn.lpstrFilter, offset FilterString
    mov ofn.lpstrFile, offset buffer
    mov ofn.nMaxFile, 512
    mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    invoke GetOpenFileName, ADDR ofn

    .if eax==TRUE
        invoke GetStartupInfo, addr startinfo
        invoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE, \
                DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS, NULL, NULL, \
                addr startinfo, addr pi

        .while TRUE
            invoke WaitForDebugEvent, addr DBEvent, INFINITE

            .if DBEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT
                invoke MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION
                .break

            .elseif DBEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT
                invoke wsprintf, addr buffer, addr ProcessInfo, \
                        DBEvent.u.CreateProcessInfo.hFile, \
                        DBEvent.u.CreateProcessInfo.hProcess, \
                        DBEvent.u.CreateProcessInfo.hThread, \
                        DBEvent.u.CreateProcessInfo.lpBaseOfImage, \
                        DBEvent.u.CreateProcessInfo.lpStartAddress
                invoke MessageBox, 0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION

            .elseif DBEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
                .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT
                    invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
                    .continue
                .endif

            .elseif DBEvent.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT
                invoke MessageBox, 0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION
            .elseif DBEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT
                invoke MessageBox, 0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION
            .endif

            invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
        .endw

        invoke CloseHandle, pi.hProcess
        invoke CloseHandle, pi.hThread
    .endif
    invoke ExitProcess, 0
end start

程式碼走勢解析

  1. 檔案選擇與建立程序:GetOpenFileName 讓使用者選目標 EXE,CreateProcess 攜帶 DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS 建立除錯目標。
  2. 進入除錯迴路:WaitForDebugEvent 以 INFINITE 參數無限期等待事件。
  3. 處理程序建立事件:從 u.CreateProcessInfo 取得檔案/程序/執行緒控制代碼、映像基址、入口點,格式化顯示。
  4. 處理初始中斷點:收到 EXCEPTION_BREAKPOINT 時以 DBG_CONTINUE 讓程式跨過 INT 3 開始真正執行。
  5. 執行緒建立/結束事件:單純彈出訊息框通知。
  6. 預設繼續狀態:其他事件(含例外)一律用 DBG_EXCEPTION_NOT_HANDLED 交回目標自行處理。
  7. 清理:迴路結束後關閉程序與執行緒控制代碼(不會終止程序,僅釋放控制代碼)。

總結

Win32 除錯 API 的核心是一個「等待事件 → 處理 → 繼續」的無限迴圈。掌握 DEBUG_EVENT 結構、各事件類型對應的資料成員、以及 ContinueDebugEvent 的兩種狀態碼用法,即可建構出功能完整的除錯器骨架。下一篇將進入「修改目標程序」的進階主題:記憶體讀寫與執行緒上下文操作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved