9

In Delphi, how do I find out the the address of a COM method? I can hardcode the offsets

//0 is the offset of the QueryInterface method
p := TPonterArray(pointer(SomeInterface)^)[0];

but I would prefer to use symbolic names. The folllowing obviously does not work:

var M : TMethod;
...
M := TMethod(SomeInterface.QueryInterface);

Thanks!

Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • 3
    Delphi takes away a lot of all the gory COM details, I think you want to do way too much yourself. What are you trying to achieve? Making your own COM server or using an existing one? – The_Fox Jul 01 '10 at 19:56
  • Do you want the numeric offset of the method in the interface (e.g., IUnknown.QueryInterface is 0), the address of the method in the class that implements that interface method (e.g., @TInterfacedObject.QueryInterface), or the address of the stub code generated to link an interface call to the corresponding object method? The latter is stored in the class's interface table. – Rob Kennedy Jul 01 '10 at 20:02
  • @The_Fox: Neither: I am intercepting calls to an external COM object using Win32Hook.pas. @Rob Kennedy: There is no TInterfacedObject class - I only have an interface implemented by an external dll. – Dmitry Streblechenko Jul 01 '10 at 20:27

4 Answers4

6

You can use the vmtoffset assembler directive to get the byte offset of an interface method relative to the start of the interface's method table. Take a look at the implementation of _IntfCast in System.pas, for example:

call dword ptr [eax] + vmtoffset IInterface.QueryInterface
...
call dword ptr [eax] + vmtoffset IInterface._Release

The first expression adds 0; the second, 8.

You cannot parameterize those expressions, though. They're compile-time constants, so you cannot choose which method you want at run time. You need to have all possible method names represented in advance.

All you really need to hook is QueryInterface. Once you have that, you can return whatever proxy object you want that can intercept calls to all the other methods.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
3

I don't think Delphi supports that. Hardcoding the offsets is probably the only thing that will work, since the compiler doesn't count interface methods as symbols whose value can be assigned to a function pointer, the way object methods or standalone functions can.

Why are you trying to do this, BTW?

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
2

Your code is wrong because an interface reference is not a pointer to an interface method table but a pointer to pointer to an interface method table. That is how Delphi interfaces are implemented on binary level. It is hard to say more and point out to the error in your code because you have not given a code example that can be compiled. Use the following code to convert interface reference to method pointer correctly, the idea was taken from Barry Kelly's demonstration of creating a method pointer from a method reference:

procedure IntRefToMethPtr(const IntRef; var MethPtr; MethNo: Integer);
type
  TVtable = array[0..999] of Pointer;
  PVtable = ^TVtable;
  PPVtable = ^PVtable;
begin
  // QI=0, AddRef=1, Release=2, etc
  TMethod(MethPtr).Code := PPVtable(IntRef)^^[MethNo];
  TMethod(MethPtr).Data := Pointer(IntRef);
end;

If you prefer symbolic names for MethNo you are better to declare them yourself as offset constants

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
kludg
  • 27,213
  • 5
  • 67
  • 118
  • No, an interface variable in Delphi is a pointer to a v-table. – Dmitry Streblechenko Jul 04 '10 at 20:15
  • No, Dmitry, Serg is right. An interface variable cannot just be a pointer to a vtable. All instances of the class share a single vtable, just like non-interface classes. Consider C++, in which there is no difference between interfaces and ordinary classes. An object pointer is not just a vtable pointer, so neither is an interface pointer. – Rob Kennedy Jul 06 '10 at 03:22
  • @Rob Kennedy: thank you, the question forced me to write a blog post about Delphi interfaces: http://sergworks.wordpress.com/2010/07/06/delphi-interfaces-on-binary-level/ – kludg Jul 06 '10 at 08:05
1

Two additional directives allow assembly code to access dynamic and virtual methods: VMTOFFSET and DMTINDEX.

VMTOFFSET retrieves the offset in bytes of the virtual method pointer table entry of the virtual method argument from the beginning of the virtual method table (VMT). This directive needs a fully specified class name with a method name as a parameter (for example, TExample.VirtualMethod), or an interface name and an interface method name.

DMTINDEX retrieves the dynamic method table index of the passed dynamic method. This directive also needs a fully specified class name with a method name as a parameter, for example, TExample.DynamicMethod. To invoke the dynamic method, call System.@CallDynaInst with the (E)SI register containing the value obtained from DMTINDEX.

docwiki.embarcadero.com

Here the code to get the needed method pointer

function GetMethodPointer(const IntRef: IInterface): Pointer; assembler;
asm
  mov eax, [IntRef] 
  add eax, vmtoffset ISomeInterface.MemberMethod
  mov eax, [eax]
end;
Pax Beach
  • 2,059
  • 1
  • 20
  • 27