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.