Delphi World - Новогодний трейсер часть 2. Брейкпоинты.
Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Новогодний трейсер часть 2. Брейкпоинты.

Автор: Hex

Все, кажись протрезвел... Снова готов к бою. Ну я там и наворотил в первой части. Будем исправлять. :)

Трассировать мы уже можем. Терь нам бы брейкпоинтов понаставить... Как ставятся брейкпоинты в обычных (не в айсе) дебагерах? Да очень просто! Вот к примеру решили мы поставить bpx на 401000h. Дебагер подрубается к процессу или запускает процесс для дебага. Потом читает байт по адресу 401000h, запоминает его и записывает на его место число CCh. А CCh - это в свою очередь опкод команды int 3h. Т.е. Debug Break. Как тока программа выполнит этот int 3h, будет сгенерено событие EXCEPTION_DEBUG_EVENT с кодом EXCEPTION_BREAKPOINT. В этот момент дебагер ставит байт по адресу 401000h на место и смещает eip на 1 назад (int 3 оно уже выполнило). Таким образом, скока брейк поинтов - стока и байт нужно будет запомнить. И так теперь можно сделать из нашего трейсера чо-нить типа SoftIce Symbol Loader. Т.е прога, которая открывает в дебагере нужный exe с адреса = Entry point ( тот что указан в PE заголовке). Собственно код:


unit Unit1;

interface

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

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

var
  Form1: TForm1;
  sti: tstartupinfo;
  lpPi: tprocessinformation;
  DE: _Debug_event;
  Cont: _Context;
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
const
  b: array[0..1] of byte = (0, $CC);
var
  f: file of longint;
  x: longint;
  i: cardinal;
begin

  assignfile(f, 'C:\w\notepad.exe');
  reset(f);
  seek(f, $2A); //читаем OEP из PE
  read(f, x);
  closefile(f);
  x := x + $400000; //типа imagebase

  //добавим еще Create_suspended чтобы процесс без нас никуда не убежал :)
  CreateProcess(nil, 'C:\w\notepad.exe', nil, nil, false, DEBUG_PROCESS
    or DEBUG_ONLY_THIS_PROCESS or Create_suspended, nil, nil, StI, lpPI);

  readprocessmemory(lppi.hProcess, pointer(x), @b[0], 1, i); //запоминаем байт
  writeprocessmemory(lppi.hProcess, pointer(x), @b[1], 1, i); //пишем $cc
  resumethread(lppi.hThread);

  {цикл ожидания EP}
  while true do
  begin
    if not WaitForDebugEvent(de, 0) then
      application.ProcessMessages;
    if de.dwDebugEventCode = EXCEPTION_DEBUG_EVENT then
      if DE.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT then
      begin
        cont.ContextFlags := CONTEXT_CONTROL;
        GetThreadContext(lppi.hThread, cont);
        {Эти брейкпоинты не тока мы генерим, но и маздай так что
        приходится проверку делать: EIP=Entry Point или нет}

        if cont.eip - 1 = x then
          // тошо EXCEPTION_BREAKPOINT генерится после int 3.
        begin
          cont.eip := cont.eip - 1;
          cont.EFlags := cont.EFlags or $100; //флаг T
          setThreadContext(lppi.hThread, cont);
          //ставим байт на место
          writeprocessmemory(lppi.hProcess, pointer(x), @b[0], 1, i);
          break;
        end;
        ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);
      end;
    ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);
  end;

  {tracing... 0% complete}
  while true do
  begin

    if not WaitForDebugEvent(de, 0) then
      application.ProcessMessages;

    if de.dwDebugEventCode = EXCEPTION_DEBUG_EVENT then
      if DE.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT then
      begin
        GetThreadContext(lppi.hThread, cont);
        cont.EFlags := cont.EFlags or $100;
        setThreadContext(lppi.hThread, cont);
        {Здесь мог бы быть ваш код :))) }
        ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);
      end
      else if DE.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_SINGLE_STEP
        then
      begin
        cont.ContextFlags := CONTEXT_CONTROL;
        GetThreadContext(lppi.hThread, cont);
        cont.EFlags := cont.EFlags or $100;
        setThreadContext(lppi.hThread, cont);
        {Здесь мог бы быть ваш код :))) }
        ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);
      end;
  end;
end;

end.

P.S. Я тут Reset и Read юзал для работы с файлом. Но лучше все сделать через апи (createfile, readfile и т.д.), потому что если будет запущена прога которую мы вот ща типа трейсить будем, то делфи будет орать про I/O error. Естественно, тошо делфа не умеет открывать файл для Shared доступа :(

Проект Delphi World © Выпуск 2002 - 2017
Автор проекта: Эксклюзивные курсы программирования