Новогодний трейсер

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: Hex

Очнулся я тут от новогодней пьянки и решил написать чо-нить полезное. Вот к примеру там свой дебагер или еще чо-нить… Ну про дебагер это я загнул. Ну хотя б трейсер.

Чо такое трейсер — прога, которая пошагово выполняет код. Какие перспективы не правда ли? Делаем на основе трейсера загрузчик и прям сказка — можно запросто ломать проги, которые проверяют целостность своих данных — например дойдем до места где jnz не надо выполнять и просто переместим EIP на следующую команду (типа не выполнилось), и все красиво. Целостность не нарушена 🙂

Итак, как же его писать… Для того чтобы сделать трейсер нам нужны будут Debug API. Вот я тут трейсер написал чтобы находить OEP. Значит ща код трейсера для поиска Entry Point в Notepad, а потом пояснения. КОД:

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);

begin

CreateProcess(nil, ‘c:\w\notepad.exe’, nil, nil, false, DEBUG_PROCESS

or DEBUG_ONLY_THIS_PROCESS, nil, nil, StI, lpPI);

while true do

begin

WaitForDebugEvent(de, INFINITE);

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);

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

GetThreadContext(lppi.hThread, cont);

cont.EFlags := cont.EFlags or $100;

setThreadContext(lppi.hThread, cont);

if (cont.eip > $400000) and (cont.eip < $600000) then

begin

Showmessage(‘OEP=’ inttohex(cont.eip, 8));

halt;

end;

ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);

end;

ContinueDebugEvent(lppi.dwProcessId, lppi.dwThreadid, DBG_CONTINUE);

end;

end;

end.

Оно работает так: Создается процесс с параметрами DEBUG_PROCESS и DEBUG_ONLY_THIS_PROCESS. DEBUG_ONLY_THIS_PROCESS — нужно для того чтобы перехватывать сообщения только от процесса, который создадим, а не все подряд. После этого создастся процесс который будет отсылать дебаговые сообщения. Делаем бесконечный цикл с WaitForDebugEvent, чтобы отлавливать дебаговые сообщения которые будет отсылать процесс. В этом цикле нам нужно будет обработать событие EXCEPTION_DEBUG_EVENT. Для этого события нам нужно будет обрабатывать 2 Exception кода: EXCEPTION_BREAKPOINT и EXCEPTION_SINGLE_STEP. Код остановки и код одиночного шага(выполнение одной команды). Когда процесс создан для дебага нам нужно будет включить флаг трассировки чтобы программа генерила EXCEPTION_BREAKPOINT и EXCEPTION_SINGLE_STEP после каждой выполненой команды. Получить данные о состоянии регистров выполняемого процесса можно через GetThreadContext, а установить их через SetThreadContext. GetThreadContext возвращает структуру _CONTEXT которая содержит да

нные о всех регистрах(в ключая EIP) и флагах выполняемого процесса. Но перед тем как читать данные о регистрах в эту структуру, нужно задать свойство ContextFlags=CONTEXT_CONTROL. Иначе будут возвращатся тока ноли. При обработке кодов EXCEPTION_BREAKPOINT и EXCEPTION_SINGLE_STEP нужно постоянно включать флаг трассировки через свойство EFlags структуры _CONTEXT (cont.EFlags:=cont.EFlags or $100;) После обработки каждого сообщения нужно разрешить программе выполняться дальше. Делается это через ContinueDebugEvent. Ну и наконец в обработке EXCEPTION_SINGLE_STEP я читаю текущий eip процесса, чтобы найти EP. Так как в реале, процесс начинает выполнятся не с Entry point а с загрузки всяких DLL и т.д.

Можно конечно было взять его из PE заголовка. Но это жеж не прикольно 🙂

{/codecitation}

Добавить комментарий