16

I have read these questions and answers

How to change the implementation (detour) of an externally declared function

Patch routine call in delphi

but i can't figere out how patch a private method of a class located in anoher unit.

Check this sample I want to patch the Bar procedure.

Unit ThidParty;
Interface
   Type
      TFoo =Class
        private
           procedure Bar;
       end;

I think which the key is find a way to obtain the address of the private method.

So, How I can patch a private method of a delphi class?

Community
  • 1
  • 1
Salvador
  • 16,132
  • 33
  • 143
  • 245
  • AFAIK this is NOT possible since private methods/procedure are NOT part of the VMT and thus are not available via RTTI etc. It might be possible to hack this but this would need you to disassemble the other unit and then do some pointer arithmetic plus some assembly to get there... – Yahia Apr 14 '12 at 19:13
  • See also [Access a strict protected property of a Delphi class?](http://stackoverflow.com/a/8330615/576719). Class Helpers as mentioned by David. – LU RD Apr 14 '12 at 19:45

2 Answers2

23

The solution outlined below works for versions up to and including Delphi Seattle. You can use a class helper to crack the class:

Unit1

type
  TTest = class
  private
    procedure Foo;
  end;

Unit2

type
  TMyTestHelper = class helper for TTest
    function GetFooAddress: Pointer;
  end;

function TMyTestHelper.GetFooAddress: Pointer;
var
  MethodPtr: procedure of object;
begin
  MethodPtr := Self.Foo;
  Result := TMethod(MethodPtr).Code;
end;

function FooAddress: Pointer;
begin
  Result := TTest(nil).GetFooAddress;//don't need to instantiate an object
end;

Pass the return value from FooAddress to one of your patching functions and you are golden.

However, starting with Delphi 10.1 Berlin, this no longer works! Class helpers can no longer access strict protected, strict private or private members. This "feature" was actually a compiler bug that Embarcadero has now fixed in Berlin. You are out of luck.

Johan
  • 74,508
  • 24
  • 191
  • 319
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 2
    Isn't that a hole in the compiler? What if it gets fixed? – Andreas Hausladen Apr 14 '12 at 22:55
  • @Andreas It's by design I think. Class helpers are meant to have access to privates. If it gets 'fixed' then you'll have to use other means to crack the privates. – David Heffernan Apr 15 '12 at 06:36
  • @David: If the class is in a unit that is in another package, the compiler may not have generated an export symbol for the private method, thus the code would fail. – Andreas Hausladen Apr 15 '12 at 11:21
  • @Andreas that would be compile time failure right? I never use packages myself. – David Heffernan Apr 15 '12 at 13:27
  • 1
    @David: Unfortunatly not. The DCP contains the private symbol but it isn't exported in the BPL. So the compiler would be happy to compile it but at runtime your application wouldn't start due to a missing symbol. That's why this is a hole in the compiler. – Andreas Hausladen Apr 16 '12 at 12:48
  • @AndreasHausladen Are there any specific circumstances for this to happen because I cannot reproduce it for either Delphis own packages (made a class helper for TButton and accessed a private member and used runtime packages and it worked) nor with some own package where I did the same (class with private method accessed by a class helper in the host application) – Stefan Glienke Apr 17 '12 at 04:51
  • 1
    @Stefan: I stand corrected. It looks like the compiler really exports all private methods in a BPL. – Andreas Hausladen Apr 17 '12 at 16:47
  • @DavidHeffernan Hi David, I've just found your code which looks just the trick I'm looking for. However I'm getting an Access Violation on the line MethodPtr := Self.Foo; Is this not because no object has been instantiated? I'm using Delphi XE6. – Jonathan Wareham Sep 10 '15 at 15:13
  • @Jonathan I don't think so. I bet your code has some other variation. After all, the code in my answer clearly doesn't instantiate an object. – David Heffernan Sep 10 '15 at 15:15
  • @DavidHeffernan Ok thanks. My code does vary in that my 'foo' method is a function rather than procedure so I'm declaring MethodPtr as function(Request: THttpRequest): THttpResponse of object. – Jonathan Wareham Sep 10 '15 at 15:37
  • That should not matter. Is the private virtual? – David Heffernan Sep 10 '15 at 15:40
  • Ah, the method I'm referencing is overriding a method in it's parent which is marked as virtual; abstract; – Jonathan Wareham Sep 10 '15 at 15:57
  • @JonathanWareham Yeah, that's a whole different ball game – David Heffernan Sep 10 '15 at 16:06
  • I've got it to work by creating a temporary object to use to call GetFooAddress to get the address and then free it. All appears to work fine but not sure if there are any side effects? – Jonathan Wareham Sep 10 '15 at 16:13
  • Well, there's likely a cleaner way to detour a virtual. Why not modify the VMT? – David Heffernan Sep 10 '15 at 16:44
  • 2
    It was considered a bug and from Delphi 10.1 Berlin it is no longer possible to access private members with class helpers. – LU RD Apr 19 '16 at 17:39
  • 1
    Ok, there is still a possible solution: `TMethod(MethodPtr).Code := @TTest.Foo;` – LU RD Jun 11 '16 at 08:40
0

Just Redefine the methode using Class interception , it work until Rad 10.4

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 27 '22 at 07:32