program Loader;
(********************************)
(* 无 - 责 - 任 *)
(* 应用硬件写断点的loader例子 *)
(* by tt.t *)
(* *)
(********************************)
uses
JwaWinType,
JwaWinNt,
JwaWinBase,
JwaWinUser;
{$R *.res}
const
VictimName = 'test.exe';
(*
test.exe为测试程序,会写$44fbc0(0x44fbc0)这个地址,我们的目标是当它改写$44fbc0后,
重新将$44fbc0的值设为0。
上次用设页面属性配合debug api实现了这个目标(参见:http://bbs.pediy.com/showthread.php?s=&threadid=17939),
这次尝试用硬件断点实现同样功能,可以看出用硬件断点可以少打很多字,我喜欢。
硬件断点的介绍请参阅hume的相关文章和intel手册,我没看过intel手册,下面代码或许
有问题,功力实在有限,而且懒得看书,测试通过就拿出来了,所以不能保证下面代码的
规范稳定。如果出现数据丢失、死机、硬盘损坏、房屋倒塌、地球爆炸等严重后果本人概不负责。
*)
MemPos: DWORD = $44fbc0;
RegFlag: DWORD = 0;
var
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
function SimpleExtractFilePath(f: String): String;
var
i: integer;
begin
for i := Length(f) downto 1 do
if f[i] = '' then Break;
result := copy(f, 0, i);
end;
procedure CreateVictimProcess(Path: String);
var
DbgEvent: TDebugEvent;
DbgParam: DWORD;
NewPrt: DWORD;
DbgContext: TContext;
begin
ZeroMemory(@si, SizeOf(STARTUPINFO));
si.cb := SizeOf(STARTUPINFO);
if not CreateProcess(PChar(Path), nil, nil, nil, False, CREATE_DEFAULT_ERROR_MODE, nil,
PChar(SimpleExtractFilePath(Path)), si, pi) then
begin
MessageBox(0, 'CreateProcess failed! ', 'Error!', 0);
exit;
end;
if WaitForInputIdle(pi.hProcess, INFINITE) <> 0 then
begin
MessageBox(0, 'WaitForInputIdle failed! ', 'Error!', 0);
exit;
end;
if not DebugActiveProcess(pi.dwProcessId) then
//这里用DebugActiveProcess来附加到目标进程,如果用CreateProcess时加上DEBUG_ONLY_THIS_PROCESS
//方式调试,设置硬件断点方法参阅hume的文章。
begin
MessageBox(0, 'DebugActiveProcess failed! ', 'Error!', 0);
exit;
end;
DbgContext.ContextFlags := CONTEXT_ALL;
while WaitForDebugEvent(DbgEvent, INFINITE) do
begin
DbgParam := DBG_CONTINUE;
case DbgEvent.dwDebugEventCode of
CREATE_PROCESS_DEBUG_EVENT:
(*
对DebugActiveProcess附加debugee的情况,必须在CREATE_PROCESS_DEBUG_EVENT中,
不可在第一个EXCEPTION_BREAKPOINT处设置硬件断点。这是试验出的结论,不知道为什么。
没有试验deguber挂起debugee后设硬件断点情况。
*)
begin
GetThreadContext(pi.hThread, DbgContext);
DbgContext.Dr0 := MemPos;
DbgContext.Dr7 := $D0501; //Dr7 flag:对dr0指向地址设写中断
SetThreadContext(pi.hThread, DbgContext);
end;
EXCEPTION_DEBUG_EVENT:
begin
case DbgEvent.Exception.ExceptionRecord.ExceptionCode of
EXCEPTION_ACCESS_VIOLATION:
begin
DbgParam := DBG_EXCEPTION_NOT_HANDLED;
end;
EXCEPTION_SINGLE_STEP:
begin
begin
GetThreadContext(pi.hThread, DbgContext);
if DbgContext.Dr6 and 1 = 1 then
begin //触发了dr0断点,这时dr0指向的地址已经被改写,这里将其恢复为0
VirtualProtectEx(pi.hProcess, Pointer(MemPos), 4, PAGE_READWRITE, @NewPrt);
WriteProcessMemory(pi.hProcess, Pointer(MemPos), @RegFlag, 4, nil);
VirtualProtectEx(pi.hProcess, Pointer(MemPos), 4, NewPrt, @NewPrt);
end
else
DbgParam := DBG_EXCEPTION_NOT_HANDLED;
end;
end;
end;
end;
EXIT_PROCESS_DEBUG_EVENT:
begin //收工
ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
Break;
end;
end;
ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, DbgParam);
end;
end;
var
Victim: string;
begin
Victim := SimpleExtractFilePath(ParamStr(0)) + VictimName;
CreateVictimProcess(Victim);
halt;
end.