功能简述:可以跟踪exe执行的序列,并记录到文件里。作用主要可以比较exe在不同环境中执行的差异处。从而可以得到差异点。
思路:首先需要用DEBUG_PROCESS标志CreateProcess加载目标程序。然后在每一步下断点,同时得到eip的值,写入文件里(同时记录寄存器的值也可以)。 如何下断点呢?其实普通的中断是Int3,也就是在需要中断的地方插入Int3,然后再WaitForDebugEvent循环里判断中断类型。对于每一步都中断的情况,只需要在入口插入int3,然后单步执行就可以了。那么如何插入int3呢?其实也就是修改中断地址的头一个字节为CC,同时保存原来的字节。中断的时候再把原来的字节写回去。调用的api主要是WriteProcessMemory。中断后如何得到eip和寄存器的值呢?使用的是GetThreadContext函数,思路基本清楚了,开始动手把。首先设计一个TBreakPointMgr类,用于管理断点,因为断点可能多个。然后新建一个线程来启动目标程序(你总不希望调试的时候我们的程序挂起吧)。
先来看看TBreakPointMgr类。
/// <summary>
/// 断点管理的类,主要作用是设置断点,清除断点,查找断点。里面定义了一个TBreakPointRec
/// 结构的数组,用于存放断点。
/// </summary>
/// <author>bohe</author>
unit untBreakPointMgr;
interface
uses
Classes,Windows;
type
TBreakPointRec = packed record
pAddr: Cardinal;
Code: char;
end;
TBreakPointMgr = class(TObject)
private
fhDebug: THandle; //debug进程结构
fBreakList: array of TBreakPointRec; //breakpoint list
public
//breakpoint has been setted at pAddr?
function FindBreakPoint(pAddr: Cardinal; var code: char): boolean;
constructor Create(hDebug: THandle);
//adding or deleting a breakpoint will call this function
function SetBreakPoint(pAdd: Cardinal;Code: char): char;
//delete a breakpoint
procedure DelBreakPoint(pAdd: Cardinal);
//clear all breakpoints
procedure ClearBreakPoint;
//add a breakpoint at pAddr
procedure AddBreakPointAt(pAddr: Cardinal);
end;
implementation
{ TMyDebug }
{ TMyDebug }
procedure TBreakPointMgr.AddBreakPointAt(pAddr: Cardinal);
var
Code,tmp: char;
begin
Code := SetBreakPoint(paddr,chr($CC));
//断点列表里没有这个地址
if not FindBreakPoint(pAddr,Code) then
begin
SetLength(fBreakList,Length(fBreakList)+1);
fBreakList[Length(fBreakList)-1].pAddr := pAddr;
fBreakList[Length(fBreakList)-1].Code := Code;
end;
end;
procedure TBreakPointMgr.ClearBreakPoint;
var
i: Integer;
begin
for i := 0 to Length(fBreakList) - 1 do
begin
DelBreakPoint(fbreaklist[i].pAddr);
end;
end;
constructor TBreakPointMgr.Create(hDebug: THandle);
begin
fhDebug := hDebug;
end;
procedure TBreakPointMgr.DelBreakPoint(pAdd: Cardinal);
var
Code: char;
i,j: Integer;
begin
if FindBreakPoint(pAdd,code) then
begin
SetBreakPoint(pAdd,Code);
end;
end;
function TBreakPointMgr.FindBreakPoint(pAddr: Cardinal; var Code: char): Boolean;
var
i: Integer;
begin
Result := false; //chr(0);
for i := 0 to Length(fBreakList)-1 do
begin
if fBreakList[i].pAddr = pAddr then
begin
Code := fBreaklist[i].Code;
result := true;
exit;
end;
end;
end;
function TBreakPointMgr.SetBreakPoint(pAdd: Cardinal; Code: char): char;
var
b: byte;
rc: boolean;
dwRead,dwOldFlg: Cardinal;
begin
Result := code;
// 0x80000000以上的地址为系统共有区域,不可以修改
if((pAdd >= $80000000) or (pAdd = 0)) then exit;
// 取得原来的代码
rc := ReadProcessMemory(fhDebug, Pointer(pAdd), @b, sizeof(BYTE), dwRead);
// 原来的代码和准备修改的代码相同,没有必要再修改
if((not rc) or (Chr(b) = code)) then exit;
// 修改页码保护属性
VirtualProtectEx(fhDebug, Pointer(pAdd), sizeof(char), PAGE_READWRITE,
@dwOldFlg);
// 修改目标代码
if WriteProcessMemory(fhDebug, Pointer(pAdd), @code, sizeof(char), dwRead) then
result := Chr(b)
else Result := Chr(0);
// 恢复页码保护属性
VirtualProtectEx(fhDebug, Pointer(pAdd), sizeof(char), dwOldFlg, @dwOldFlg);
end;
end.
接着看TTraceDebug类,这是一个线程类。
/// <summary>
/// 线程类,在里面加载目标程序,同时在waitDebugEvent循环里监测目标进程。
/// </summary>
/// <author>bohe</author>
unit untWaitDebug;
interface
uses
Classes,windows,SysUtils ,forms, untBreakPointMgr, ComCtrls;
type
TThreadInfo = record
ThreadId: Cardinal;
hThread: THandle;
end;
TTraceDebug = class(TThread)
private
{ Private declarations }
fLogStr: string;
m_hDebug: THandle;
m_hThread: THandle;
BPMgr: TBreakPointMgr;
//hThread: THandle;
//ThreadList: TStringList;
bStartRec: Boolean;
NumOfCodeLine: integer;
MaxCodeLines: Integer;
LowAddr: DWORD;
HighAddr: DWORD;
BreakPointAddress: DWORD;
flog: TextFile; // trace file
logfile: string; //file name of trace
//lst: TListview;
function OnDebugEvent(pEvent: DEBUG_EVENT): Boolean;
function OnDebugException(pEvent: DEBUG_EVENT): Boolean;
procedure ResumeAllThread(dwThrdID: Cardinal);
procedure SuspendAllThread(dwThrdID: Cardinal);
procedure ClearLog;
procedure AddResultLog();
//procedure writeTrace(str);
procedure SyncAddLog(logstr: string);
protected
procedure Execute; override;
public
exeName: string;
procedure AddBreakPoint(Addr: Cardinal);
end;
implementation
uses untMyDebug, main;
{ WaitDebug }
procedure TTraceDebug.AddBreakPoint(Addr: Cardinal);
begin
BPMgr.AddBreakPointAt(Addr);
end;
procedure TTraceDebug.AddResultLog();
var
lst: TListView;
begin
//if flog<>0 then
begin
if not FileExists(logfile) then
Rewrite(flog)
else
Append(flog);
Writeln(flog,flogstr);
end;
end;
procedure TTraceDebug.ClearLog;
begin
// frmDebug.lstLast.Items.Clear;
// frmDebug.lstFirst.Items.Clear;
end;
procedure TTraceDebug.Execute;
var
si: _STARTUPINFOA; {进程启动信息}
pi: _PROCESS_INFORMATION; {进程信息}
Flage: DWORD;
DebugEvent: DEBUG_EVENT; {调试事件}
Rc: Boolean;
CodeCount: DWORD;{运行的指令数}
ThreadHandle: Thandle;{主线程句柄}
ThreadIDtoAttach: DWORD; {主线程句柄}
ThreadContext: TContext;
bWriteMem: boolean;
//ProcessHandle: THandle;
//ProcessIDtoAttach: DWORD;
begin
Synchronize(ClearLog);
bStartRec := true;
NumOfCodeLine :=0;
{建立调试进程}
CodeCount := 0;
//ThreadList := TStringList.Create; //创建线程列表,
//ConText.ContextFlags := CONTEXT_CONTROL;
if frmDebug.rgpTrace.ItemIndex=0 then
logfile := 'trace1.log'
else logfile := 'trace2.log';
AssignFile(flog,logfile);
Rewrite(flog);
Flage := DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS;
LowAddr := StrToInt('$'+frmDebug.edtLowAddr.Text);
HighAddr := StrToInt('$'+frmDebug.edtHighAddr.Text);
BreakPointAddress := StrToInt('$'+frmDebug.edtEOP.Text); //入口断点
MaxCodeLines := StrToInt(frmDebug.edtMaxNum.Text);
GetStartupInfo(si); {初始化si结构,不然无法正常建立进程}
NumOfCodeLine := 0;
ThreadHandle:=0;
{if frmDebug.rgpTrace.ItemIndex=0 then
lst := frmDebug.lstFirst
else lst := frmDebug.lstLast; }
if not CreateProcess(nil, Pchar(exeName), nil, nil,
False, Flage, nil, nil, si, pi) then
begin
fLogStr := '建立被调试进程失败';
Synchronize(AddResultLog);
exit;
end;
//ProcessHandle := pi.hProcess;
CloseHandle(pi.hProcess);
Closehandle(pi.hThread);
while WaitForDebugEvent(DebugEvent, INFINITE) do
begin {根据事件代码进行相应处理}
rc := OnDebugEvent(DebugEvent);
if Rc then
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE)
else
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);
end;
end;
function TTraceDebug.OnDebugEvent(pEvent: DEBUG_EVENT): Boolean;
begin
Result := true;
case (pEvent.dwDebugEventCode) of
CREATE_PROCESS_DEBUG_EVENT:
begin
m_hDebug := pEvent.CreateProcessInfo.hProcess;
m_hThread := pEvent.CreateProcessInfo.hThread;
//hThread := pEvent.CreateProcessInfo.hThread;
//ThreadList.Clear;
//ThreadList.AddObject(IntToStr(pEvent.dwThreadId),Pointer(pEvent.CreateProcessInfo.hThread));
BPMgr := TBreakPointMgr.Create(m_hDebug);
AddBreakPoint(BreakPointAddress);
fLogStr := '被调试进程建立';
//Synchronize(AddResultLog);
//m_hDebug := pEvent->u.CreateProcessInfo.hProcess;
// 记录线程ID和线程句柄的关系
//m_gthreads[pEvent->dwThreadId] = pEvent->u.CreateProcessInfo.hThread;
end;
CREATE_THREAD_DEBUG_EVENT:
begin
//ThreadList.AddObject(IntToStr(pEvent.dwThreadId),Pointer(pEvent.CreateThread.hThread));
// 记录线程ID和线程句柄的关系
//m_gthreads [pEvent->dwThreadId] = pEvent->u.CreateThread.hThread;
end;
EXIT_THREAD_DEBUG_EVENT:
begin
// 线程退出时清除线程ID
//m_gthreads.erase (pEvent->dwThreadId);
end;
EXCEPTION_DEBUG_EVENT:
begin
// 中断处理程序
Result := OnDebugException(pEvent);
end;
end;
end;
function TTraceDebug.OnDebugException(pEvent: DEBUG_EVENT): Boolean;
var
pBreakAdd: Pointer;
ThreadContext: CONTEXT;
//hThread: THandle;
Code: Cardinal;
tmp: Char;
callMem: pByte;
dwRead: Cardinal;
nMemLen: Integer;
pMem: Cardinal;
idx: integer;
PwdBuf: pchar;
//dwRead: Integer;
tmpStr: string;
bWriteMem: boolean;
begin
Result := false;
pBreakAdd := pEvent.Exception.ExceptionRecord.ExceptionAddress;
code := pEvent.Exception.ExceptionRecord.ExceptionCode;
//ThreadList.Find(IntToStr(pEvent.dwThreadId),idx);
//hThread := Cardinal(ThreadList.Objects[idx]);
ThreadContext.ContextFlags := CONTEXT_FULL;
GetThreadContext(m_hthread, ThreadContext);
if(pEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT) then
begin
Result := true;
if not BPMgr.FindBreakPoint(Cardinal(pBreakAdd),tmp) then exit;
ThreadContext.ContextFlags:=CONTEXT_CONTROL; //important
GetThreadContext(m_hThread,ThreadContext);
if(abs(ThreadContext.Eip-BreakPointAddress)<2) then
begin
ThreadContext.EFlags:=ThreadContext.EFlags or $100;
BPMgr.DelBreakPoint(BreakPointAddress);
ThreadContext.Eip:=BreakPointAddress;
SetThreadContext(m_hThread, ThreadContext);
//bWriteMem:=WriteProcessMemory(ProcessHandle,BreakPointAddress,OldCode,1,ByteWriten);
bStartRec:=true;
Result := true;
end;
end
else if((pevent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_SINGLE_STEP) and bStartRec) then
begin
Result := true;
GetThreadContext(m_hThread,ThreadContext);
if(Threadcontext.Eip>=LowAddr) and (ThreadContext.Eip <= $ 2c 01000) then
begin
//跟踪结束,次数或者结束地址
if(NumofCodeLine>=MaxCodeLines) or (ThreadContext.Eip=HighAddr) then
begin
bStartRec:=false;
frmDebug.sbar.SimpleText := 'finished!';
//LoadLstData;
CloseFile(flog);
//lst.Items.EndUpdate;
frmDebug.btnRun.Enabled := true;
//frmDebug.btnCmp.Enabled := frmDebug.rgpTrace.ItemIndex=1;
frmDebug.rgpTrace.ItemIndex := 1-frmDebug.rgpTrace.ItemIndex;
//break;
end
else begin
//pArraytoDump[NumofCodeLine]=ThreadContext.Eip;
fLogStr := IntToHex(ThreadContext.Eip,8);
Synchronize(AddResultLog);
inc(NumofCodeLine);
end;
end;
ThreadContext.EFlags:=ThreadContext.EFlags or $100;
SetThreadContext(m_hthread, ThreadContext);
end
else if(pEvent.Exception.ExceptionRecord.ExceptionCode=EXIT_PROCESS_DEBUG_EVENT) then
begin
// DebugSetProcessKillOnExit(false);
result := true;
exit;
end
end;
procedure TTraceDebug.ResumeAllThread(dwThrdID: Cardinal);
var
i: Integer;
dwID: Cardinal;
begin
{ for i := 0 to ThreadList.Count - 1 do
begin
dwID := StrToInt(ThreadList[i]);
if dwID<>dwThrdID then
ResumeThread(Cardinal(ThreadList.Objects[i]));
end; }
end;
procedure TTraceDebug.SuspendAllThread(dwThrdID: Cardinal);
var
i: Integer;
dwID: Cardinal;
begin
{for i := 0 to ThreadList.Count - 1 do
begin
dwID := StrToInt(ThreadList[i]);
if dwID<>dwThrdID then
SuspendThread(Cardinal(ThreadList.Objects[i]));
end; }
end;
procedure TTraceDebug.SyncAddLog(logstr: string);
begin
fLogStr := logstr;
Synchronize(AddResultLog);
end;
end.
主界面就没有什么好说的了。。。。。。