win32調試API學習心得(三)

來源:互聯網
上載者:User

要學習如何修改被調試進程,先讓我們來瞭解幾個與此有關的函數.
一.讀指定進程記憶體:ReadProcessMemory
  此函數的定義為:function ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; 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_DEBUG_EVENT調試事件時儲存線程ID和線程控制代碼的關聯以便調用GetThreadContext時得到線程控制代碼.
  lpContext:用來儲存指定線程上下檔案資訊的結構.
  在象Windows這樣的多任務作業系統中,同一時間裡可能運行著幾個程式.Windows分配給每個線程一個時間片,當時間片結束後,Windows將凍結當前線程並切換到下一具有最高優先順序的線程.在切換之前,Windows將儲存當前進程的寄存器的內容,這樣當在該線程再次恢複運行時,Windows可以恢複最近一次線程啟動並執行環境.儲存的寄存器內容總稱為進程上下文.上下檔案的結構取決於CPU的類型.
  在調用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: 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;
  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:/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:
      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,
         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.

最後附上其它的調試API.
一. procedure DebugBreak; stdcall;
  該函數在當前進程中產生斷點,以便調用的線程能夠向調試器發訊號.
二. procedure FatalExit(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為要發送的字串.
  在DELPHI中可以通用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層級的調試事件.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.