win32除錯API學習心得(三) (轉)

gugu99發表於2007-08-16
win32除錯API學習心得(三) (轉)[@more@]

要學習如何修改被程式,先讓我們來了解幾個與此有關的.
一.讀指定程式:ReadProcessMemory
 此函式的定義為:function ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: D; var lpNumberOfBytesRead: DWORD): BOOL; stdcall;
  hProcess指向被讀取記憶體的程式的控制程式碼,此控制程式碼必須有PROCESS_VM_READ.
  lpBaseAddress:指向被讀取的記憶體在程式中基地址的指標.
  lpBuffer:指向用於儲存讀出資料的緩衝區的指標.
  nSize:指定從指定程式中要讀取的位元組數.
  lpNumberOfBytesRead:指向讀出資料的實際位元組數.

二.寫指定程式記憶體:WriteProcessMemory
  此函式的定義為:function WriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
引數含義同ReadProcessMemory,其中hProcess控制程式碼要有對程式的PROCESS_VM_WRITE和PROCESS_VM_OPERATION許可權.lpBuffer為要寫到指定程式的資料的指標.

  注意,如果要修改的記憶體所在的頁面的存取保護屬性為只讀,如程式碼段,要修改頁面的存取保護才能夠正常修改.可使用VirtualProtectEx函式,請參考下面的程式碼片段:
VirtualProtectEx(hPHandle, Address, SizeOf(BYTE), PAGE_READWRITE, OldFlg);
WriteProcessMemory(hPHandle, Address, @BreakCode, SizeOf(BYTE), Read);
VirtualProtectEx(hPhandle, Address, SizeOf(BYTE), OldFlg, OldFlg); // 恢復頁碼保護屬性
  hPHandle為目標程式控制程式碼,Address為要修改的記憶體的基址,SizeOf(BYTE)表示要修改的區域長度,如果這個長度跨過一個或幾個頁面邊界時,將修改跨過的所有頁面的存取保護屬性,OldFlg用來存放原來的存取保護屬性,以便WriteProcessMemory後恢復頁面保護屬性.

三.得到指定執行緒的上下文結構:GetThreadContext
  此函式的定義為:function GetThreadContext(hThread: THandle; var lpContext: TContext): BOOL; stdcall;
  hThread:要取得上下文結構的執行緒的控制程式碼,可以在發生CREATE_THEAD_DE_EVENT除錯事件時儲存執行緒ID和執行緒控制程式碼的關聯以便呼叫GetThreadContext時得到執行緒控制程式碼.
  lpContext:用來儲存指定執行緒上下資訊的結構.
  在象這樣的多工操作中,同一時間裡可能執行著幾個.Windows分配給每個執行緒一個時間片,當時間片結束後,Windows將凍結當前執行緒並切換到下一具有最高優先順序的執行緒.在切換之前,Windows將儲存當前程式的暫存器的內容,這樣當在該執行緒再次恢復執行時,Windows可以恢復最近一次執行緒執行的環境.儲存的暫存器內容總稱為程式上下文.上下檔案的結構取決於的型別.
  在呼叫GetThreadContext之前,要先設定TContext的ContextFlags標誌來指明要檢索的暫存器.例如只想得到CPU的段暫存器的值,可以設定ContextFlags標誌為CONTEXT_SEGMENTS.其它可能的標誌如下:
  CONTEXT_CONTROL:包含CPU的控制暫存器,比如指今指標,堆疊指標,標誌和函式返回地址.
  CONTEXT_INTEGER:用於標識CPU的整數暫存器.
  CONTEXT_FLOATING_POINT:用於標識CPU的浮點暫存器.
  CONTEXT_SEGMENTS:用於標識CPU的段暫存器.
  CONTEXT_DEBUG_REGISTER:用於標識CPU的除錯暫存器.
  CONTEXT_EXTENDED_REGISTERS:用於標識CPU的擴充套件暫存器.
  CONTEXT_FULL:相當於CONTEXT_CONTROL or CONTEXT_INTEGER or  CONTEXT_SEGMENTS,即這三個標誌的組合.

四.設定指定執行緒的上下文結構:SetThreadContext
  此函式的定義為:function SetThreadContext(hThread: THandle; const lpContext: TContext): BOOL; stdcall;
  引數同GetThreadContext.
  有了這二個函式可以實現很多功能,比如用WriteProcessMemory在被除錯程式的某個函式入口處寫一個除錯中斷(int 3,即$cc),然後在執行到此除錯中斷時會產生中斷,再用GetThreadContext得到當前執行緒的上下文,就可以根據Esp的值得到函式的引數等資訊.你甚至可以修改Eip的值來讓被除錯程式跳到任何地址來,或是修改標誌暫存器的值來修改程式的執行方式.

  瞭解了以上函式後我們就可以用來修改被除錯程式了,具體能實現怎樣的功能只侷限於自己的想像力了,但運用不得當被除錯程式通常會當得很慘。當然這幾個函式不止可以用於被除錯程式,用於其它程式一樣適用(可用OpenProcess根據程式識別符號得到程式控制程式碼),例如用它們來做出你自己的遊戲修改器等等.

下面的例子演示瞭如何其得執行緒的上下文並將CPU置為單步來執行程式,注意由於單步模式比較慢,執行一個大的被除錯程式時可能會等很久時間.
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
  Button1: TButton;
  Label1: TLabel;
  procedure Button1Click(Sender: T);
  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;
  CodeCount: DWORD;  {執行的指令數}
  ThreadHandle: Thandle;  {主執行緒控制程式碼}
  Context: TContext;
begin
  {建立除錯程式}
  CodeCount := 0;
  ConText.ContextFlags := CONTEXT_CONTROL;
  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:
  begin
  ThreadHandle := DebugD.CreateProcessInfo.hThread;
  MessageBox(Application.Handle, '被除錯程式建立', '!!!', MB_OK or MB_ICONERROR);
  end;
  EXCEPTION_DEBUG_EVENT:
  begin
  if (DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_SINGLE_STEP) and
  (DebugD.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT) then
  Rc := False  {如果被除錯程式產生了異常,讓它自己處理}
  else
  begin
  GetThreadContext(ThreadHandle, Context);
  {將標誌暫存器的陷井標誌設為TRUE,這樣CPU將會處於單步模式}
  Context.EFlags := Context.EFlags or $100;
  Inc(CodeCount);
  Form1.Label1.Caption := IntToStr(CodeCount);
  SetThreadContext(ThreadHandle, Context);
  Rc := True;
  end;
  end;
  end;
  if Rc then
  ContinueDebugEvent(DebugD.dwProcessId, DebugD.dwThreadId,
  G_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.

最後附上其它的除錯.
一. procedure DebugBreak; stdcall;
  該函式在當前程式中產生斷點,以便呼叫的執行緒能夠向偵錯程式發訊號.
二. procedure alExit(ExitCode: Integer); stdcall;
  該函式把執行控制交給偵錯程式,偵錯程式的行為隨後被指定為所用偵錯程式的型別.
三. function FlushInstructionCache(hProcess: THandle; const lpBaseAddress: Pointer; dwSize: DWORD): BOOL; stdcall;
  該函式為指定程式重新整理指令快取記憶體器,此函式僅在多程式上是有效的.
  hProcess:要重新整理的快取記憶體器的程式控制程式碼.
  lpBaseAddress:要重新整理區域的基地址指標,可以為0
  dwSize:要重新整理區域的長度.
四. function isDebuggerPresent; BOOL; stdcall;
  該函式表明呼叫的程式是否在偵錯程式描述表下執行,此函式從KERNEL32.DLL輸出.
五. procedure OutputDebugString(lpOutputString: PChar); stdcall;
  該函式為當前的應用程式傳送一個字串到偵錯程式中,lpOutputString為要傳送的字串.
  在中可以通用View->Debug Windows->Event Log開啟Event Log視窗檢視被除錯程式傳送的字串.
六. procedure SetDebugErrorLevel(dwLevel: DWORD); stdcall;
  該函式設定最小錯誤級別,在該錯誤級別中系統中將產生除錯事件並把它傳遞給偵錯程式.
  dwLevel:指定除錯事件的最小錯誤除錯程式,如果錯誤相等於或大於此程式,系統產生一個除錯事件,此引數必須是下列值中的一個.
  0: 不記錄任何錯誤. SLE_ERROR:僅記錄ERROR級別的除錯事件.
  SLE_MINORERROR:僅記錄MINORERROR級別和ERROR級別的除錯事件.
  SLE_WARNING:記錄WARNING級別,MINORERROR和ERROR級別的除錯事件.


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

相關文章