我有一个第三方函数
function DataCompare(const S1, S2: string; APartial: Boolean): Boolean;
begin
...
end;
这个函数被用在第三方单元中。
我希望在运行时将函数体替换为另一个新实现。是否可能?我猜需要一些技巧(比如VirtualMemoryUnprotect)。非汇编解决方案也可以。
我有一个第三方函数
function DataCompare(const S1, S2: string; APartial: Boolean): Boolean;
begin
...
end;
这个函数被用在第三方单元中。
我希望在运行时将函数体替换为另一个新实现。是否可能?我猜需要一些技巧(比如VirtualMemoryUnprotect)。非汇编解决方案也可以。
是的,你可以这样做,使用 ReadProcessMemory
和 WriteProcessMemory
函数来修补当前进程的代码。基本上,你需要获取要修补的过程或函数的地址,然后插入一个跳转指令到新过程的地址。
查看此代码
Uses
uThirdParty; //this is the unit where the original DataCompare function is declarated
type
//strctures to hold the address and instructions to patch
TJumpOfs = Integer;
PPointer = ^Pointer;
PXRedirCode = ^TXRedirCode;
TXRedirCode = packed record
Jump: Byte;
Offset: TJumpOfs;
end;
PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp;
TAbsoluteIndirectJmp = packed record
OpCode: Word;
Addr: PPointer;
end;
var
DataCompareBackup: TXRedirCode; //Store the original address of the function to patch
//this is the implementation of the new function
function DataCompareHack(const S1, S2: string; APartial: Boolean): Boolean;
begin
//here write your own code
end;
//get the address of a procedure or method of a function
function GetActualAddr(Proc: Pointer): Pointer;
begin
if Proc <> nil then
begin
if (Win32Platform = VER_PLATFORM_WIN32_NT) and (PAbsoluteIndirectJmp(Proc).OpCode = $25FF) then
Result := PAbsoluteIndirectJmp(Proc).Addr^
else
Result := Proc;
end
else
Result := nil;
end;
//patch the original function or procedure
procedure HookProc(Proc, Dest: Pointer; var BackupCode: TXRedirCode);
var
n: {$IFDEF VER230}NativeUInt{$ELSE}DWORD{$ENDIF};
Code: TXRedirCode;
begin
Proc := GetActualAddr(Proc);
Assert(Proc <> nil);
//store the address of the original procedure to patch
if ReadProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n) then
begin
Code.Jump := $E9;
Code.Offset := PAnsiChar(Dest) - PAnsiChar(Proc) - SizeOf(Code);
//replace the target procedure address with the new one.
WriteProcessMemory(GetCurrentProcess, Proc, @Code, SizeOf(Code), n);
end;
end;
//restore the original address of the hooked function or procedure
procedure UnhookProc(Proc: Pointer; var BackupCode: TXRedirCode);
var
n: {$IFDEF VER230}NativeUInt{$ELSE}Cardinal{$ENDIF};
begin
if (BackupCode.Jump <> 0) and (Proc <> nil) then
begin
Proc := GetActualAddr(Proc);
Assert(Proc <> nil);
WriteProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n);
BackupCode.Jump := 0;
end;
end;
//Patch the original procedure or function
procedure HookDataCompare;
begin
//look how is passed the address of the original procedure (including the unit name)
HookProc(@uThirdParty.DataCompare, @DataCompareHack, DataCompareBackup);
end;
//restore the address of the original procedure or function
procedure UnHookDataCompare;
begin
UnhookProc(@uThirdParty.DataCompare, DataCompareBackup);
end;
initialization
HookDataCompare;
finalization
UnHookDataCompare;
end.
现在每次你执行应用程序并调用 DataCompare
函数时,跳转指令(到新地址)将被执行,导致 DataCompareHack
函数将被调用。
VirtualProtect
的调用,这通常需要覆盖可执行代码。我是对的,Rruz吗?否则,为什么不只使用普通的Move
?” - Rob Kennedy我认为JCL有一些适用于这种情况的工具...我自己没有使用过,但是快速浏览后发现以下几项看起来很有希望:
jclSysUtils.WriteProtectedMemory()
jclPeImage.TJclPeMapImgHooks.ReplaceImport()
jclHookExcept.JclHookExceptions()
演示了如何使用它们。