Search code examples
delphiruntimedelphi-2010

Delphi 2010 - patch/redirect RTL record method


Is it possible to patch/redirect any method of a record defined in RTL? If it is, how to do it?

I'm trying to patch TValue.TryCast function, i want to redirect this function to my function definition from where i jump to orinal function and after that i check for additional stuff and exit.

To put some light on this topic, here is what i'm doing now and it did not work.
I declare:

type
  TValueHelper = record helper for TValue
  public
    function TryCastFixed(ATypeInfo: PTypeInfo; out AResult: TValue): Boolean;
  end;

var
  TValueTryCastOrgAddr: Pointer;

function TValueHelper.TryCastFixed(ATypeInfo: PTypeInfo; out AResult: TValue): Boolean;
begin
  asm
    JMP TValueTryCastOrgAddr
  end;
  // fix for conversion from TValue
  if not Result and (ATypeInfo <> Nil) and (ATypeInfo = System.TypeInfo(TValue)) then begin
    AResult := TValue.From<TValue>(Self);
    Exit(True);
  end;
end;

and then there is a patching stuff:

type
  PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp;
  TAbsoluteIndirectJmp = packed record
    OpCode: Word;   //$FF25(Jmp, FF /4)
    Addr: ^Pointer;
  end;

function WriteProtectedMemory(BaseAddress, Buffer: Pointer; Size: Cardinal; out WrittenBytes: Cardinal): Boolean;
var
  OldProtect, Dummy: Cardinal;
begin
  WrittenBytes := 0;
  if Size > 0 then begin // VirtualProtect for DEP issues
    OldProtect := 0;
    Result := VirtualProtect(BaseAddress, Size, PAGE_EXECUTE_READWRITE, OldProtect);
    if Result then try
      Move(Buffer^, BaseAddress^, Size);
      WrittenBytes := Size;
      if OldProtect in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY] then
        FlushInstructionCache(GetCurrentProcess, BaseAddress, Size);
    finally
      Dummy := 0;
      VirtualProtect(BaseAddress, Size, OldProtect, Dummy);
    end;
  end;
  Result := WrittenBytes = Size;
end;

function GetActualAddr(Proc: Pointer): Pointer;
begin
  if Proc <> Nil then begin
    if (PAbsoluteIndirectJmp(Proc).OpCode = $25FF) then
      Result := PAbsoluteIndirectJmp(Proc).Addr^
    else
      Result := Proc;
  end
  else
    Result := Nil;
end;

procedure RedirectFunction(OldP, DestP: Pointer);
type
  TJump = packed record
    Jmp: Byte; // $E9;
    Offset: Integer;
  end;

var
  Jump: TJump;
  WrittenBytes: Cardinal;
begin
  if IsLibrary then
    raise Exception.Create('RedirectFunction: Not allowed in a DLL');
  //
  OldP := GetActualAddr(OldP); 
  TValueTryCastOrgAddr := OldP;
  DestP := GetActualAddr(DestP);
  Jump.Jmp := $E9;
  Jump.Offset := Integer(DestP) - Integer(OldP) - SizeOf(TJump);
  WriteProtectedMemory(OldP, @Jump, SizeOf(TJump), WrittenBytes);
end;

procedure PatchTValueHelper_TryCast;
begin
  RedirectFunction(@@TValue.TryCast, @@TValueHelper.TryCastFixed); // this is not working, 
  // as it can't access undeclared record, how to do it correctly?
end;

As can be seen, code is completed from bits and pieces from internet, and PatchTValueHelper_TryCast is my main problem.
How to patch function from this record globally?

Thanks, NevTon.


Solution

  • Did you try https://github.com/MahdiSafsafi/DDetours? This is a library specifically designed for hooking Delphi functions. Disclaimer: I do know know if it can intercept record helper methods.