如何更改外部声明的函数的实现(绕行)

时间:2011-08-01 22:34:21

标签: delphi function replace implementation delphi-xe

我有第三方功能

function DataCompare(const S1, S2: string; APartial: Boolean): Boolean;
begin
   ...
end;

它用于另一个第三方单位。

我希望在运行时用另一个新实现替换函数体。

这可能吗?我想需要一些黑客(ala VirtualMemoryUnprotect)。非汇编解决方案非常受欢迎。

2 个答案:

答案 0 :(得分:36)

是的,您可以使用ReadProcessMemoryWriteProcessMemory函数来修补当前进程的代码。基本上,您获取要修补的过程或函数的地址,然后将Jump指令插入到新过程的地址中。

检查此代码

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函数。

答案 1 :(得分:2)

我认为JCL对于这种东西有一些功能......我自己没有使用它,但是快速查看并且以下项目看起来很有希望:

jclSysUtils.WriteProtectedMemory()
jclPeImage.TJclPeMapImgHooks.ReplaceImport()

我认为jclHookExcept.JclHookExceptions()演示了如何使用它们。