Delphi中的COM方法偏移量

9
在Delphi中,如何查找COM方法的地址?我可以硬编码偏移量。
//0 is the offset of the QueryInterface method
p := TPonterArray(pointer(SomeInterface)^)[0];

但我更愿意使用符号名称。以下显然不起作用:

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

谢谢!


3
Delphi会处理大部分繁琐的COM细节,我认为你想要自己做太多事情了。你想要实现什么?是创建自己的COM服务器还是使用已有的服务器? - The_Fox
你想要接口方法的数字偏移量(例如,IUnknown.QueryInterface为0),实现该接口方法的类中方法的地址(例如,@TInterfacedObject.QueryInterface),还是链接接口调用到相应对象方法的存根代码的地址?后者存储在类的接口表中。 - Rob Kennedy
@The_Fox:不是的:我正在使用Win32Hook.pas拦截对外部COM对象的调用。@Rob Kennedy:没有TInterfacedObject类 - 我只有一个由外部dll实现的接口。 - Dmitry Streblechenko
4个回答

6

您可以使用vmtoffset汇编指令来获取接口方法相对于接口方法表开始的字节偏移量。例如,可以查看System.pas_IntfCast的实现:

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

第一个表达式加0;第二个表达式加8。

但是你无法对这些表达式进行参数化。它们是编译时常量,因此你不能在运行时选择想要的方法。你需要预先准备所有可能的方法名称。

实际上,你只需要钩取 QueryInterface 方法。一旦你拥有了它,你就可以返回任何代理对象,可以拦截对所有其他方法的调用。


这看起来很有前途...我今天稍后会尝试并发布结果。谢谢! - Dmitry Streblechenko

3
我不认为Delphi支持这个。硬编码偏移量可能是唯一可行的方法,因为编译器不将接口方法视为其值可以分配给函数指针的符号,就像对象方法或独立函数一样。顺便问一下,你为什么要这样做呢?

1
我正在使用Win32Hook.pas拦截对外部COM对象的调用 - 这只是我OutlookSpy(http://www.dimastr.com/outspy/)的日志记录功能。 - Dmitry Streblechenko
1
顺便说一句,Delphi编译器显然知道方法的偏移量,我只是试图找到一种方法来代替使用硬编码的偏移量。 - Dmitry Streblechenko
是的,编译器知道它,但据我所知,没有直接获取它的方法。 - Mason Wheeler

2

你的代码有误,因为接口引用不是指向接口方法表的指针,而是指向指向接口方法表的指针。这就是Delphi接口在二进制层面上的实现方式。很难进一步指出你代码中的错误,因为你没有提供可以编译的代码示例。使用以下代码将接口引用正确转换为方法指针,该想法来自Barry Kelly演示如何从方法引用创建方法指针

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;

如果您更喜欢使用符号名称来代替MethNo,最好自己声明它们作为偏移常量。

不,Delphi中的接口变量是指向虚函数表的指针。 - Dmitry Streblechenko
不,Dmitry,Serg是正确的。接口变量不能仅仅是指向vtable的指针。类的所有实例共享一个vtable,就像非接口类一样。考虑C++,在其中接口和普通类之间没有区别。对象指针不仅仅是vtable指针,因此接口指针也不是。 - Rob Kennedy
@Rob Kennedy:谢谢,这个问题促使我写了一篇关于Delphi接口的博客文章:http://sergworks.wordpress.com/2010/07/06/delphi-interfaces-on-binary-level/ - kludg

1
两个额外的指令允许汇编代码访问动态和虚拟方法:VMTOFFSET和DMTINDEX。
VMTOFFSET检索从虚拟方法表(VMT)开头到虚拟方法参数的虚拟方法指针表项的偏移量(以字节为单位)。此指令需要一个完全指定的类名和方法名作为参数(例如,TExample.VirtualMethod),或接口名称和接口方法名称。
DMTINDEX检索传递的动态方法的动态方法表索引。此指令还需要一个完全指定的类名和方法名作为参数,例如,TExample.DynamicMethod。要调用动态方法,请使用System.@CallDynaInst调用,并将(E)SI寄存器包含从DMTINDEX获得的值。

docwiki.embarcadero.com

这里是获取所需方法指针的代码。
function GetMethodPointer(const IntRef: IInterface): Pointer; assembler;
asm
  mov eax, [IntRef] 
  add eax, vmtoffset ISomeInterface.MemberMethod
  mov eax, [eax]
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接