Win32調試API學習心得(一)

來源:互聯網
上載者:User

    最近學習了一下WIN32的調試API,並做了一個簡單的調試器,略有心得,特寫出來希望對需要的朋友有所協助.
參考資料:lczlion:<<win32組譯工具設計>>
               彭春華:<<用Debug函數實現API函數的跟蹤>>

概述:
   Windows提供了一組供程式員使用的API,使用這些API,我們能夠建立或捆綁到已啟動並執行程式上來對他們進行調試,能獲得程式的底層資訊和調試資訊.如果你原意的話,甚至可以對被偵錯工具進行任意的修改(用WriteProcessMemory).

    先讓我們從一個有趣的小例子開始吧: 開啟DELPHI,建立工程,然後雙擊主表單,在主表單的Create事件中寫下如下代碼.
procedure TForm1.FormCreate(Sender: TObject);
var
  isDebuggerPresent: function:Boolean;
  DllModule: THandle;
begin
  DllModule := LoadLibrary('kernel32.dll');
  isDebuggerPresent := GetProcAddress(DllModule, 'IsDebuggerPresent');
  if isDebuggerPresent then
  begin
    MessageBox(self.Handle, '請不要調試我!', '抗議', MB_OK or MB_ICONASTERISK);
    Application.Terminate;
  end;
end;
     按F9運行,程式執行得並不順利,在彈出來一個抱怨你調試了它的視窗後就中止了.然後我們再在DELPHI的Projecs目錄下找到剛剛編釋出來的程式, 雙擊執行它,這次視窗就老老實實的出來了,這是怎麼回事呢?

    原來上面的isDebuggerPresent就是Win32調試API中的一員,它的作用是判斷調用它的進程是否在調試描述表下運行(也就是是否處於被調試狀態),另一方面也說明了DELPHI的調試器也是用Win32調試API實現的.這下對調試API有興趣了吧?那讓我們來繼續深入調試API的世界!

得到一個供調試的程式:
    由於我們的程式要扮演調試器的角色,我們還必需要有一個供調試的程式.這個程式可以通過二種方法獲得:
   1:使用DebugActiveProcess函數.
     這個函數的定義是DebugActiveProcess(dwProcessID: DWORD):Bool; stdcall, dwProcessID用於指定被調試的進程的標識符,如果函數調用成功返回TRUE,失敗返回FALSE.注意,如果是在NT/2000/XP上,如果目標進程是由一個安全描述器建立的,而該安全性描述元使調試器沒有充分的訪問權,那麼此函數的調用可能失敗. 
   2:使用CreateProcess函數.
    這個函數的定義是CreateProcess(lpApplicationName: PChar; lpCommandLine:     PChar;lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall
    由於篇幅原因,這兒就不詳解CreateProcess的每個參數的含義,具體請參考API大全,我們這兒只談如何建立一個被調試的進程.即設定dwCreationFlags參數,你可以指定DEBUG_PROCESS標誌來建立一個被調試進程,同時被調試進程的子進程的調試資訊也將通知我們的調試器.還可以指定DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS標誌來表示只調試當前過程.

處理調試資訊:
    當我們用上面的方法之一開啟了被調試的程式後,我們的程式應調用WaitForDebugEvent等待處理調試事件.它阻塞調用線程直到調試的事件發生.此函數的定義是:
  WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; stdcall;
其中lpDebugEvent結構將在調試事件發生時返回傳生的調試事件資訊.dwMilliseconds值指定函數等待調試事件的時間,以毫秒為單位,一般設為INFINITE,表示一直等待直到調試事件發生.
    這有點於類似於一個訊息迴圈.我們一般都會建立一個線程,線上程中使用DebugActiveProcess或CreateProcess得到一個供調試的程式,然後用一個迴圈調用WaitForDebugEvent來處理隨後發生的調試事件.至於為什麼要在新的線程中處理呢?你不會想你的調試器一開啟被偵錯工具後就一動也不能動了吧

繼續運行被偵錯工具:
    當調試事件發生後,被偵錯工具會被WINDOWS掛起,當我們處理完了調試事件後,還要讓被偵錯工具繼續運行,這就要用到ContinueDebugEvent函數,定義如下:
  ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus: DWORD): BOOL; stdcall;
其中dwProcessID和dwThreadID是要被恢複的進程和線程ID,可以從lpDebugEvent結構中的dwProcessID和dwThreadID取得.dwContinueStatus是指明如何恢複線程,可能的取值有DBG_CONTINUE 和DBG_EXCEPTION_NOT_HANDLED,DBG_CONTINUE指明了如果被偵錯工具發生了異常,由調試器來處理異常.DBG_EXCEPTION_NOT_HANDLED則表示調試器不處理被偵錯工具的異常,由被偵錯工具的預設例外處理常式來處理異常.

下面是一個簡單的例子,實現了監視被偵錯工具的建立和退出.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{調試資訊處理過程} 
procedure DebugPro;
var
  si: _STARTUPINFOA;       {進程啟動資訊}
  pi: _PROCESS_INFORMATION; {進程資訊}
  Flage: DWORD;
  DebugD: DEBUG_EVENT;  {調試事件}
  Rc: Boolean;
begin
  {建立調試進程}
  Flage := DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS;
  GetStartupInfo(si);    {初始化si結構,不然無法正常建立進程}
  if not CreateProcess(nil, Pchar('C:/winnt/NOTEPAD.EXE C:/Boot.ini'), nil, nil,
    False, Flage, nil, nil, si, pi) then
  begin
    MessageBox(Application.Handle, '建立被調試進程失敗', '!!!', MB_OK or MB_ICONERROR);
    exit;
  end;
  while WaitForDebugEvent(DebugD, INFINITE) do
  begin  {根據事件代碼進行相應處理}
    case DebugD.dwDebugEventCode of
      EXIT_PROCESS_DEBUG_EVENT:
      begin
        MessageBox(Application.Handle, '被調試進程中止', '!!!', MB_OK or MB_ICONERROR);
        Break;
      end;
      CREATE_PROCESS_DEBUG_EVENT:
        MessageBox(Application.Handle, '被調試進程建立', '!!!', MB_OK or MB_ICONERROR);
      EXCEPTION_DEBUG_EVENT:
      begin
        if (DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_SINGLE_STEP) and
           (DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT) then
          Rc := False    {如果被偵錯工具產生了異常,讓它自己處理}
        else
          Rc := True;
      end;
    end;
    if Rc then
      ContinueDebugEvent(DebugD.dwProcessId, DebugD.dwThreadId,
         DBG_CONTINUE)
    else
      ContinueDebugEvent(DebugD.dwProcessId, DebugD.dwThreadId,
         DBG_EXCEPTION_NOT_HANDLED);
  end;
  CloseHandle(pi.hProcess);
  Closehandle(pi.hThread);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadHandle, ThreadID: THandle;
begin
  ThreadHandle := CreateThread(nil, 0, @DebugPro, nil, 0, ThreadID);
end;

end.

下一篇文章將祥細講解調試事件的處理和事件結構的含義.敬請關注.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.