Win32除錯API學習心得(一) (轉)

gugu99發表於2007-11-27
Win32除錯API學習心得(一) (轉)[@more@]

  最近學習了一下的,並做了一個簡單的偵錯程式,略有心得,特寫出來希望對需要的朋友有所幫助.
參考資料:lczlion:<>>
  彭春華:<>

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

  先讓我們從一個有趣的小例子開始吧: 開啟,新建工程,然後雙擊主窗體,在主窗體的Create事件中寫下如下程式碼.
procedure TForm1.FormCreate(Sender: T);
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.Tenate;
  end;
end;
  按F9執行,程式得並不順利,在彈出來一個抱怨你除錯了它的視窗後就中止了.然後我們再在DELPHI的Projecs目錄下找到剛剛編釋出來的程式, 雙擊執行它,這次視窗就老老實實的出來了,這是怎麼回事呢?

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

得到一個供除錯的程式:
  由於我們的程式要扮演偵錯程式的角色,我們還必需要有一個供除錯的程式.這個程式可以透過二種方法獲得:
  1:使用DebugActiveProcess函式.
  這個函式的定義是DebugActiveProcess(dwProcessID: D):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是指明如何恢復執行緒,可能的取值有G_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:winntNOTEPAD.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.

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


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-985334/,如需轉載,請註明出處,否則將追究法律責任。

相關文章