Новогодний трейсер
Автор: 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 заголовка. Но это жеж не прикольно :)
|