Home > other >  Delphi 2010 - patch/redirect RTL record method
Delphi 2010 - patch/redirect RTL record method

Time:06-04

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.

CodePudding user response:

First of all the code to patch would be this:

RedirectFunction(@TValue.TryCast, @TValue.TryCastFixed);

But your TryCastFixed implementation is broken and will lead to a stack overflow or worse. Redirecting the way you did simple scribbles a jmp instruction into the first 5 bytes of the executable code of TryCast. This means whenever you call or jump to the original method it will jump to your method. If your method jumps back you have an endless loop of jumping back and forth. Your jmp instruction also happens after some code that was already executed within the begin of your method that the compiler created. This means values in registers are possibly not the same anymore and you cannot simple use the jmp instruction here.

If you want to still use the original method then you need to use libraries such as DDetours or madCodeHook (I assume there are others).

Otherwise I suggest you simply copy the code from the RTL TryCast into your routine and add the fix so you can get away with the redirect as you don't need the original method anymore then.

CodePudding user response:

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.

  • Related