Recent

Author Topic: [SOLVED] Obtain VMT pointer without instance  (Read 1029 times)

Khrys

  • Full Member
  • ***
  • Posts: 105
[SOLVED] Obtain VMT pointer without instance
« on: July 25, 2024, 11:00:03 am »
Is it possible to find the virtual method table of a type without having to create an instance first (needed for  TypeOf)? The only workaround I can think of is hackily relying on assembler symbols:

Code: Pascal  [Select][+][-]
  1. var
  2.   VMT_TOBJECT: Pointer; external name 'VMT_$SYSTEM_$$_TOBJECT$indirect';
« Last Edit: July 25, 2024, 11:39:50 am by Khrys »

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • Censorship about opinions does not belong here.
Re: Obtain VMT pointer without instance
« Reply #1 on: July 25, 2024, 11:16:27 am »
Well, yes and no: the VMT is a relative offset so it really depends on an instance to where it really is. But the VMT offset itself is known. (0)
https://www.freepascal.org/docs-html/prog/progsu166.html
It does not need hackery.
TypeOf may not fail if a class is never used. That is because it uses typeinfo and that is guaranteed.
« Last Edit: July 25, 2024, 11:37:14 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Khrys

  • Full Member
  • ***
  • Posts: 105
Re: Obtain VMT pointer without instance
« Reply #2 on: July 25, 2024, 11:38:52 am »
Just found out that  TObject.ClassType() does the trick for me. I was wondering if it's possible to override a virtual method at runtime:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = class(TObject)
  3.     function ToString(): String; override;
  4.   end;
  5.  
  6. function TFoo.ToString(): String;
  7. begin
  8.   Result := 'Bar';
  9. end;
  10.  
  11. procedure Test();
  12. var
  13.   Instance: TObject;
  14. begin
  15.   Instance := TObject.Create();
  16.   PPVMT(Instance)^ := PVMT(TFoo.ClassType());
  17.   WriteLn(Instance.ToString()); // Prints "Bar"
  18.   Instance.Free();
  19. end;

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11944
  • FPC developer.
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #3 on: July 25, 2024, 12:05:27 pm »
You can also try to duplicate the VMT but modify it.

It is all possible but dangerous, and shouldn't be done run of the mill. The classic exception is (in Delphi) when you got some code/component only in binary form.

In later versions Delphi got a way to streamline that a bit using http://docwiki.embarcadero.com/CodeExamples/Athens/en/TVirtualMethodInterceptor_(Delphi)

http://blog.barrkel.com/2010/09/virtual-method-interception.html

Afaik there has been some study to implement them in FPC in the last, say two years, but afaik it is not there yet.
« Last Edit: July 25, 2024, 12:07:01 pm by marcov »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10553
  • Debugger - SynEdit - and more
    • wiki
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #4 on: July 25, 2024, 12:27:04 pm »
You can also try to duplicate the VMT but modify it.

It is all possible but dangerous, and shouldn't be done run of the mill.

There will be a few problems you may run into...

As Marcov said, copy before modify. Depending on Target platform the original class may or may not be in read only memory. So you may or may not get an access violation when you try to change the pointer to a virtual method.

But even if you copy, and then modify, you will hit issue
* inherited:  inherited calls are often de virtualized. So the will call the parent class directly, bypassing the VMT. Changes in the VMT wont work.
* Apps compiled with WPO => more calls can be de virtualized....
* not sure about current compiler, but future versions may be smarter, and do more de-virtualization...
* future FPC version could remove unused entries from the VMT, so make sure not to rely on them being present.
* ...

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • Censorship about opinions does not belong here.
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #5 on: July 25, 2024, 02:04:05 pm »
Or may even change the order of the vmt entries.
As Marco explained, you are playing basically Russian Roulette.
If I smell bad code it usually is bad code and that includes my own code.

ASerge

  • Hero Member
  • *****
  • Posts: 2337
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #6 on: July 26, 2024, 04:37:22 am »
Is it possible to find the virtual method table of a type without having to create an instance first (needed for  TypeOf)? The only workaround I can think of is hackily relying on assembler symbols:
Code: Pascal  [Select][+][-]
  1. function GetVMT(AClass: TClass): PVmt;
  2. begin
  3.   Result := Pointer(AClass);
  4. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • Censorship about opinions does not belong here.
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #7 on: July 26, 2024, 07:48:09 am »
But I already wrote the same: the offset to the vmt is zero, so you can indeed cast the class.
If that is wise is another matter.
If I smell bad code it usually is bad code and that includes my own code.

Khrys

  • Full Member
  • ***
  • Posts: 105
Re: [SOLVED] Obtain VMT pointer without instance
« Reply #8 on: July 26, 2024, 09:34:08 am »
Code: Pascal  [Select][+][-]
  1. function GetVMT(AClass: TClass): PVmt;
  2. begin
  3.   Result := Pointer(AClass);
  4. end;

Now I feel stupid for not trying to just cast the class/type name...  :-[   kinda obvious in hindsight, thanks a lot.



Regarding concerns about stability/safety, I'm aware that replacing an instance's VMT at runtime is anything but safe and reliable. Nonetheless, consider the following:

Code: Pascal  [Select][+][-]
  1. type
  2.   TModifiedApplication = class(TApplication)
  3.     procedure HandleException(Sender: TObject); override;
  4.   end;
  5.  
  6. procedure TModifiedApplication.HandleException(Sender: TObject);
  7. begin
  8.   // Some custom code
  9. end;
  10.  
  11. procedure ReplaceExceptionHandler();
  12. begin
  13.   PPVMT(Application)^ := PVMT(TModifiedApplication);
  14. end;

Replacing the  Application  singlet's exception handler method without recreating everything was the original reason why I started looking into this topic.
I would argue that (apart from devirtualization), this should be mostly safe:
  • Compatible VMT layout due to being a single-level subclass
  • No new data members; instance size stays the same
  • No new methods, only overrides
With  Application  being a mutable global variable, I'd also hope that the compiler isn't allowed to devirtualize it (and maybe pray that  TApplication  doesn't become  sealed  in the future)...

(PS: I made the original post out of general curiosity and not because I specifically wanted to replace  TApplication.HandleException  -  that'd be a painfully obvious example of an XY problem)

 

TinyPortal © 2005-2018