Jump to content
pyscripter

Patch a private virtual method

Recommended Posts

In case this of use to anyone:

 

Quite often you find a bug in Delphi RTL :classic_angry: and you come up with a fix.  Patching involves replacing the RTL procedure with a new patched one.  To do that you can use your favorite patching routine or library (I use Detours), but you need the address of the original function/method.

 

a) Patching a non-virtual public method

This is quite straight-forward:    

type
  TMethodType = procedure ... of object

function GetAddress: Pointer;
var
  MethodPtr : TMethodType;
begin
  MethodPtr := TRTLClass(nil).PublicMethod;
  Result := TMethod(MethodPtr).Code;
end;

Note the type cast TRTLClass(nil).

 

b) Patching a virtual public method

If for example PublicMethod is virtual the above type cast TRTLClass(nil) with result in access violation, since to resolve the virtual method you need to access Self which is nil.  You could create a class instance and use that instead of TRTLClass(nil), but apart from not being elegant, in some cases this has side-effects (for example it may require a valid windows handle).  The trick is described in this Stackoverflow question.

 

function GetAddress: Pointer;
var
  VMT : NativeInt;
  MethodPtr: TMethodType;
begin
  VMT := NativeInt(TRTLClass);
  MethodPtr := TRTLClass(@VMT).PublicMethod;
  Result := TMethod(MethodPtr).Code;
end;

This is based on two facts.  A class is a pointer to the Virtual Method table (VMT) and an Object structure has as the first field a pointer to the VMT of its class.

 

c) Patching a private virtual method

 

The trick this time involves using a class helper to access the private method of the TRTLClass

 

type
  TPrivateMethodType = procedure ... of object;

TRTLClassHookFix = class helper for TRTLCLass
  function GetPriveMethodAddr: Pointer;
end;

function TRTLClassHookFix.GetPriveMethodAddr: Pointer;
var
  VMT : NativeInt;
  MethodPtr: TPrivateMethodType;
begin
  //  Adjust Self to point to the VMT
  VMT := NativeInt(TRTLCLass);
  Self := TRTLCLass(@VMT);

  with Self do MethodPtr := PrivateMethod;
  Result := TMethod(MethodPtr).Code;
end;

That's it.

Edited by pyscripter
  • Like 6
  • Thanks 1

Share this post


Link to post

When you patch a virtual method you don't need to patch it using the technique to place a jmp into the original code you can directly patch the address in the vmt slot.

For that you just need to have the index of the virtual method.

Share this post


Link to post
21 hours ago, Stefan Glienke said:

When you patch a virtual method you don't need to patch it using the technique to place a jmp into the original code you can directly patch the address in the vmt slot.

For that you just need to have the index of the virtual method.

That's very interesting, could you show a small example?

Share this post


Link to post
{$APPTYPE CONSOLE}

uses
  Windows;

type
  TFoo = class
    procedure Bar; virtual;
  end;

procedure TFoo.Bar;
begin
  Writeln('broken');
end;

procedure FixedBar(Self: TFoo);
begin
  Writeln('fixed');
end;

var
  f: TFoo;
  p: Pointer;
  n: UINT_PTR;
begin
{$POINTERMATH ON}
  p := @FixedBar;
  WriteProcessMemory(GetCurrentProcess, @PPointer(TFoo)[0], @p, SizeOf(Pointer), n); // 0 is the virtual index of the method to be replaced

  f := TFoo.Create;
  f.Bar;
end.

The virtual method index can also be found out programmatically.

Edited by Stefan Glienke
  • Like 1
  • Thanks 1

Share this post


Link to post

@Stefan Glienke Your code above (and in spring4d) uses WriteProcessMemory to modify executable pages. This relies on an undocumented implementation detail of WriteProcessMemory. Namely that it will handle the protection flags on your behalf.  Personally I would prefer to use VirtualProtect with this sort of pattern:
 

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if not VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin
    Fail;
  end;
  Move(NewCode, Address^, Size);
  FlushInstructionCache(GetCurrentProcess, nil, 0);
  if not VirtualProtect(Address, Size, OldProtect, @OldProtect) then begin
    Fail;
  end;
end;

 

  • Thanks 1

Share this post


Link to post
2 hours ago, Stefan Glienke said:

{$APPTYPE CONSOLE}

uses
  Windows;

type
  TFoo = class
    procedure Bar; virtual;
  end;

procedure TFoo.Bar;
begin
  Writeln('broken');
end;

procedure FixedBar(Self: TFoo);
begin
  Writeln('fixed');
end;

var
  f: TFoo;
  p: Pointer;
  n: UINT_PTR;
begin
{$POINTERMATH ON}
  p := @FixedBar;
  WriteProcessMemory(GetCurrentProcess, @PPointer(TFoo)[0], @p, SizeOf(Pointer), n); // 0 is the virtual index of the method to be replaced

  f := TFoo.Create;
  f.Bar;
end.

The virtual method index can also be found out programmatically.

What I meant specifically is, how do I find out the index of a method in the VMT?

 

 

 

 

 

Share this post


Link to post
Just now, A.M. Hoornweg said:

What I meant specifically is, how do I find out the index of a method in the VMT?

Either you know it by looking into the assembly for its call or look for the method pointer in the VMT.

Share this post


Link to post

@Stefan GlienkeWould this approach work with classes derived from TFoo?  Wouldn't you have to patch the VMT of derived classes?

 

Edited by pyscripter

Share this post


Link to post

Yes, and I never stated otherwise - I just pointed out that you can patch virtual methods selectively by replacing them in the VMT. inherited calls would also still call into the old method with that approach.

FWIW the approach you wrote for private virtual method works as well for a non virtual method.

In fact the technique of hooking methods by placing a jmp has nothing to do with how the methods are being called.

 

Especially for patching known code (i.e. RTL and alike) I rather write byte scanning code for finding the place I want to patch - yes such code has to be changed when you migrate to another version.

But with that you can patch any code even if it's hidden deep within the private section of some classes or in the implementation part of a unit.

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
3 hours ago, Stefan Glienke said:

Well, not if the method is private or protected

I can see that being true for Standard RTTI (TypInfo.pas), which is only generated for published members.  But that should not be true for Extended RTTI (Rtti.pas), which sees everything, even private/protected stuff.  That is why TRttiMember has a Visibility property.

Share this post


Link to post

Check the $RTTI directive in System.pas ... - also there is no this or that RTTI. Stuff from Rtti.pas just puts an easier to use layer ontop of the pointer stuff from typInfo.pas

Edited by Stefan Glienke

Share this post


Link to post
1 hour ago, Stefan Glienke said:

Check the $RTTI directive in System.pas

OK, so the default is to expose only Public and Protected properties and methods, and all available data fields.  That is still way more RTTI available than TypInfo.pas provides (only Published properties/methods).

1 hour ago, Stefan Glienke said:

there is no this or that RTTI. Stuff from Rtti.pas just puts an easier to use layer ontop of the pointer stuff from typInfo.pas

Rtti.pas uses TypInfo.pas internally for some of its RTTI, but there is a lot more RTTI made accessible by Rtti.pas than TypInfo.pas ever provided.  Much of the RTTI in Rtti.pas does not go through TypInfo.pas at all.

Share this post


Link to post

Hi,

 

I recently started learning and experimenting with method patching due to a crazy idea I had and wanted to implement in my framework, but I'm not sure

I fully got how it works. I thought that by patching a class method, then any new instances of that class created would have a copy of the new patched method,

but that's not the case, right? All new instances point to one single pointer and the information of the 'self' pointer is simply not accessible, correct?

And that is why it doesn't matter whether the new method is a unit method or a class method. What I was trying to achieve, is to have a wrapper object

around a third party class object and patch the wrapped object's method pointing to a wrapper's method.

What I'm also wondering, is this even possible? And if yes, any of the ways discussed here us the solution or do I need the DDetour library, and if this library is able of such task?

 

Just a simple example of what I would like to achieve, if it is possible though :

TFoo = class
protected
  procedure DoSomething;virtual;
public
  procedure Something;
end;

TFooOpener = class(TFoo)
end;

TWrapper = class
private
  FFoo : TFoo;
public
  constructor Create(pFoo : TFoo);
  procedure HackedDoSomething;
end;

implementation

constructor TWrapper.Create(pFoo : TFoo);
begin
  FFoo := pFoo;
end;

procedure TWrapper.HackedDoSomething;
begin
  TFooOpener(FFoo).DoSomething();
end;

initialization
  Patch(@TFoo.DoSomething, @TWrapper.HackedDoSomething)
  
------------------------------------------------------------------
 
  ...
  ...

fFoo : TFoo;
fWrapper : TWrapper; 

fFoo := TFoo.Create;
fWrapper := TWrapper.Create(fFoo);

  ...
  
// And when fFoo.Something then the HackedDoSomething would be called.
fFoo.Something(); 

Thank you!

Share this post


Link to post
On 12/6/2022 at 1:06 AM, kokoslolos said:

I thought that by patching a class method, then any new instances of that class created would have a copy of the new patched method,

but that's not the case, right?

Not a copy, but the original. All object instances of a class have their own copies of data members, but they all share a single set of code, vtables, etc in memory. So, patching a method of a class will affect all object instances of that class.

On 12/6/2022 at 1:06 AM, kokoslolos said:

All new instances point to one single pointer and the information of the 'self' pointer is simply not accessible, correct?

Not sure I understand what you are asking.

 

 

Share this post


Link to post
On 12/9/2022 at 1:42 AM, Remy Lebeau said:

Not a copy, but the original. All object instances of a class have their own copies of data members, but they all share a single set of code, vtables, etc in memory. So, patching a method of a class will affect all object instances of that class.

I always wanted to know how all these work in memory, if you have any readings or links to articles on the subject that you can suggest I would very much appreciate it, I'm very eager to dive in these topics and learn more. 

i.e. The instantiation of a class to an object, you say they share the same code but different data members, so when multiple threads are using different objects of the same class how does the OS handles it? Does it load a copy of the methods/instructions of the class in the CPU with the data members of an object? Or how are unit methods are thread safe when only accessing local variables, or how dll methods are loaded and also thread safe as long are accessing local variable.

I don't know if it makes sense what I'm asking :)!

 

On 12/9/2022 at 1:42 AM, Remy Lebeau said:

Not sure I understand what you are asking.

I'll try to explain what I was asking at the end of the day, it's what the pseudo code is trying to achieve, having an A instance of TFoo method hooked and patched by another B instance's of TWrapper method, and when I call A.Method() it would actually

call B.HackedMethod, but during my tests I realized that it isn't that simple and that's why I wrote my previous post asking for confirmation if what I realized was correct :).

 

During my tests I did a patching this way,

Patch(@TFoo.DoSomething, @TWrapper.HackedDoSomething)

and during the call of A.Method(), it would execute without access violation even if I hadn't created an instance of TWrapper, and there is when I realized I was going the right direction and that I couldn't access the data members of the instances,

for example the self pointer or FFoo of TWrapper.

 

I finally managed to achieve this using the DDetours library, it's pretty amazing of what it can do :)!

 

Hope what I wrote makes more sense now :).

 

Share this post


Link to post
5 hours ago, kokoslolos said:

i.e. The instantiation of a class to an object, you say they share the same code but different data members, so when multiple threads are using different objects of the same class how does the OS handles it?

The OS is not involved in this process.

5 hours ago, kokoslolos said:

Does it load a copy of the methods/instructions of the class in the CPU with the data members of an object?

Instantiating an object is simply a matter of allocating a memory block to hold the data members, and then calling the class constructor on that memory block to initialize the data members. Any methods called on an object (including the constructor and destructor) is simply a matter of making a normal function call with a pointer to the object as a (hidden) parameter so the code knows which object is being act on.  There are no copies of the method instructions being made.  There is only 1 copy in memory that all objects share.  Calling the same method on multiple objects will execute the same code instructions just with a different object parameter.

5 hours ago, kokoslolos said:

During my tests I did a patching this way,


Patch(@TFoo.DoSomething, @TWrapper.HackedDoSomething)

and during the call of A.Method(), it would execute without access violation even if I hadn't created an instance of TWrapper

It will fail if the patched method tries to access any data members that don't actually belong to the actual object that the method is being called on.

Share this post


Link to post
On 11/5/2019 at 11:14 AM, Stefan Glienke said:

{$APPTYPE CONSOLE}

uses
  Windows;

type
  TFoo = class
    procedure Bar; virtual;
  end;

procedure TFoo.Bar;
begin
  Writeln('broken');
end;

procedure FixedBar(Self: TFoo);
begin
  Writeln('fixed');
end;

var
  f: TFoo;
  p: Pointer;
  n: UINT_PTR;
begin
{$POINTERMATH ON}
  p := @FixedBar;
  WriteProcessMemory(GetCurrentProcess, @PPointer(TFoo)[0], @p, SizeOf(Pointer), n); // 0 is the virtual index of the method to be replaced

  f := TFoo.Create;
  f.Bar;
end.

The virtual method index can also be found out programmatically.

@Stefan Glienke Should this also work for 'AfterConstruction'?

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×