By Ivelin Nikolaev


2011-08-01 22:34:21 8 Comments

I have a third party function

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

It is used in another third party unit.

I wish to replace the body of the function at runtime with another new implementation.

Is this possible? I guess there will be a need of some hack (ala VirtualMemoryUnprotect). A non-assembler solution is very welcome.

2 comments

@RRUZ 2011-08-01 22:59:08

Yes you can do that, using the ReadProcessMemory and WriteProcessMemory functions to patch the code of the current process. Basically, you get the address of the procedure or function to patch and then insert a Jump instruction to the address of the new procedure.

Check this code

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.

Now every time you execute your app and a call to the DataCompare function was made, the jump instruction (to he new address) will be executed causing which the DataCompareHack function will be called instead.

@Michael Riley - AKA Gunny 2011-08-01 23:23:54

Now where did you learn that? That's way to cool to be called a hack. Instead of calling it a "Hack" I think you should call it a "Ninja". DataCompareNinja

@Remy Lebeau 2011-08-02 01:09:03

This kind of redirection is known as a Detour. Microsoft has a research project about it: research.microsoft.com/en-us/projects/detours

@Rob Kennedy 2012-06-01 19:45:39

@Premature, I suspect it may have something to do with avoiding having to change memory protection. I see no calls to VirtualProtect here, which would ordinarily be required to overwrite executable code. Am I right, Rruz? Otherwise, why not just use plain old Move?

@ain 2011-08-01 23:12:30

I think JCL has some utils for this kind of stuff... I haven't used it myself but had a quick look and following items look promising:

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

I think the jclHookExcept.JclHookExceptions() demonstrates how to use them.

Related Questions

Sponsored Content

11 Answered Questions

[SOLVED] How to get a function name as a string?

9 Answered Questions

[SOLVED] Meaning of = delete after function declaration

10 Answered Questions

[SOLVED] How can I view the source code for a function?

  • 2013-10-07 13:58:00
  • Joshua Ulrich
  • 260605 View
  • 521 Score
  • 10 Answer
  • Tags:   r function r-faq

13 Answered Questions

[SOLVED] How can I get the source code of a Python function?

  • 2009-01-09 09:02:37
  • david
  • 203128 View
  • 375 Score
  • 13 Answer
  • Tags:   python function

7 Answered Questions

[SOLVED] How do you pass a function as a parameter in C?

18 Answered Questions

[SOLVED] How to return a string value from a Bash function

1 Answered Questions

2 Answered Questions

[SOLVED] VIM: how to replace a word

  • 2010-11-05 13:51:43
  • Stas
  • 4202 View
  • 12 Score
  • 2 Answer
  • Tags:   vim replace

Sponsored Content