program Loader;
(***********************************)
(* *)
(* Memory Access Breakpoint Loader *)
(* by tt.t *)
(* *)
(* *)
(***********************************)
{
目标:拦截对指定地址的写操作,nop掉写地址的指令。
难点:在要补丁地址设Memory Write Breakpoint,查了半天没找到现成的东西,只好自己写。
思路:将要补丁的地址设为不可写,当写时会发生AV错误,然后进行Patch。但VirtualProtectEx会将整
个Page设为不可写属性,所有写Page的操作都会产生Access violation,无法直接找到要Patch的代码。
解决方法:要保存地址所在Page的地址范围,发生AV错误时判断是否因为VirtualProtect产生的Exceptio
n,如不是将Page的属性设为可写,同时设置单步标志,写操作完成后恢复Page为不可写属性,继续执
行,直至找到需Patch代码。
不知道有没有更好的方法。
}
uses
JwaWinType,
JwaWinNt,
JwaWinBase,
JwaWinUser;
{$R *.res}
var
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
function MyExtractFilePath(f: String): String;
var
i, l: integer;
begin
l := Length(f);
for i := l downto 1 do
if f[i] = '' then Break;
result := copy(f, 0, i);
end;
procedure CreateVictimProcess(Path: String);
const
Nop: PChar = Chr($90) + Chr($90) + Chr($90);
var
DbgEvent: TDebugEvent;
DbgParam: DWORD;
OldPrt, NewPrt: DWORD;
pPatch: PByte;
PgMin, PgMax: DWORD;
MemInfo: TMemoryBasicInformation;
WExpAddr: DWORD;
DbgContext: TContext;
rm: Boolean;
hThread: DWORD;
begin
ZeroMemory(@si, sizeof(STARTUPINFO));
si.cb := sizeof(STARTUPINFO);
if not CreateProcess(PChar(Path), nil, nil, nil, False, CREATE_SUSPENDED or
CREATE_DEFAULT_ERROR_MODE, nil,
PChar(MyExtractFilePath(Path)), si, pi) then
begin
MessageBox(0, 'CreateProcess failed! ', 'Error!', 0);
exit;
end;
ResumeThread(pi.hThread);
if WaitForInputIdle(pi.hProcess, INFINITE) <> 0 then
begin
MessageBox(0, 'WaitForInputIdle failed! ', 'Error!', 0);
exit;
end;
if not DebugActiveProcess(pi.dwProcessId) then
begin
MessageBox(0, 'DebugActiveProcess failed! ', 'Error!', 0);
exit;
end;
if VirtualQueryEx(pi.hProcess, Pointer($9c66BC), MemInfo, SizeOf(MemInfo)) = 0 then
begin
MessageBox(0, 'VirtualQueryEx failed! ', 'Error!', 0);
exit;
end;
PgMin := DWORD(MemInfo.BaseAddress);
pgMax := PgMin + MemInfo.RegionSize;
{VirtualProtect会将整个Page设为不可写属性,所有写Page的操作都会产生Access violation,要保存
整个Page的地址范围,
后面可以判断是否因为VirtualProtect产生的Exception}
if not VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @OldPrt) then
begin
MessageBox(0, 'VirtualProtectEx failed! ', 'Error!', 0);
exit;
end;
{改写Page属性为不可写}
rm := false;
while WaitForDebugEvent(DbgEvent, INFINITE) do
begin
DbgParam := DBG_CONTINUE;
case DbgEvent.dwDebugEventCode of
EXCEPTION_DEBUG_EVENT:
begin
if DbgEvent.Exception.ExceptionRecord.ExceptionCode <> EXCEPTION_BREAKPOINT then
case DbgEvent.Exception.ExceptionRecord.ExceptionCode of
EXCEPTION_SINGLE_STEP: {单步中断}
begin
if rm then {由于EXCEPTION_ACCESS_VIOLATION产生的单步中断,恢复Page为不可写属性}
begin
rm := false;
VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, PAGE_EXECUTE_READ, @NewPrt);
end;
end;
EXCEPTION_ACCESS_VIOLATION: {AV中断}
begin
DbgParam := DBG_EXCEPTION_NOT_HANDLED;
if DbgEvent.Exception.ExceptionRecord.ExceptionInformation[0] = 1 then {写操作}
begin
WExpAddr := DbgEvent.Exception.ExceptionRecord.ExceptionInformation[1]; {写操作的目标地
址}
if (WExpAddr >= PgMin) and (WExpAddr <= PgMax) then {目标地址在Page范围}
begin
DbgParam := DBG_CONTINUE;
if(WExpAddr <> $9c66BC) then {不是写指定地址}
begin
VirtualProtectEx(pi.hProcess, Pointer($9c66BC), 1, OldPrt, @NewPrt);
DbgContext.ContextFlags := CONTEXT_CONTROL;
hThread := OpenThread(THREAD_ALL_ACCESS, false, DbgEvent.dwThreadId);
GetThreadContext(hThread, DbgContext);
DbgContext.EFlags := DbgContext.EFlags or $100;
{设单步标志,会触发EXCEPTION_SINGLE_STEP}
SetThreadContext(hThread, DbgContext);
rm := true; {标志,表明是EXCEPTION_ACCESS_VIOLATION产生的单步中断}
end
else
begin {Patch}
pPatch := DbgEvent.Exception.ExceptionRecord.ExceptionAddress;
VirtualProtectEx(pi.hProcess, pPatch, 3, PAGE_READWRITE, @NewPrt);
WriteProcessMemory(pi.hProcess, pPatch, Nop, 3, nil);
VirtualProtectEx(pi.hProcess, pPatch, 3, NewPrt, @NewPrt);
end;
end;
end;
end;
else
DbgParam := DBG_EXCEPTION_NOT_HANDLED;
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 := MyExtractFilePath(ParamStr(0)) + 'Wise***.exe'; //受害程序
CreateVictimProcess(Victim);
halt;
end.