【问题标题】:COM method offsets in DelphiDelphi 中的 COM 方法偏移量
【发布时间】:2017-09-18 09:38:47
【问题描述】:

在Delphi中,我如何找出COM方法的地址? 我可以硬编码偏移量

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

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

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

谢谢!

【问题讨论】:

  • Delphi 带走了很多血腥的 COM 细节,我认为你自己想做的太多了。你想达到什么目的?制作自己的 COM 服务器还是使用现有的?
  • 是否要接口中方法的数字偏移量(如IUnknown.QueryInterface为0),实现该接口方法的类中方法的地址(如@TInterfacedObject.QueryInterface ),或者生成的存根代码的地址,以将接口调用链接到相应的对象方法?后者存储在类的接口表中。
  • @The_Fox:两者都不是:我正在使用 Win32Hook.pas 拦截对外部 COM 对象的调用。 @Rob Kennedy:没有 TInterfacedObject 类 - 我只有一个由外部 dll 实现的接口。

标签: delphi com vtable


【解决方案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;

【讨论】:

    【解决方案2】:

    您的代码是错误的,因为接口引用不是指向接口方法表的指针,而是指向接口方法表的指针。这就是 Delphi 接口在二进制级别上实现的方式。很难说更多并指出代码中的错误,因为您没有给出可以编译的代码示例。使用以下代码将接口引用正确转换为方法指针,思路取自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;
    

    如果您更喜欢 MethNo 的符号名称,您最好自己将它们声明为偏移常量

    【讨论】:

    • 不,Delphi 中的接口变量是指向 v-table 的指针。
    • 不,Dmitry,Serg 是对的。接口变量不能只是指向 vtable 的指针。类的所有实例共享一个 vtable,就像非接口类一样。考虑 C++,其中接口和普通类没有区别。对象指针不仅仅是一个 vtable 指针,所以也不是一个接口指针。
    • @Rob Kennedy:谢谢,这个问题迫使我写了一篇关于 Delphi 接口的博文:sergworks.wordpress.com/2010/07/06/…
    【解决方案3】:

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

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

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

    但是,您不能参数化这些表达式。它们是编译时常量,因此您无法在运行时选择所需的方法。您需要提前表示所有可能的方法名称。

    您真正需要挂钩的只是QueryInterface。一旦你有了它,你就可以返回任何你想要的代理对象,它可以拦截对所有其他方法的调用。

    【讨论】:

    • 这看起来很有希望...我将在今天晚些时候尝试并发布结果。谢谢!
    【解决方案4】:

    我认为 Delphi 不支持这一点。硬编码偏移量可能是唯一可行的方法,因为编译器不会将接口方法视为符号,其值可以分配给函数指针,就像对象方法或独立函数一样。

    你为什么要这样做,顺便说一句?

    【讨论】:

    • 我正在使用 Win32Hook.pas 拦截对外部 COM 对象的调用 - 只是我的 OutlookSpy (dimastr.com/outspy) 的一个日志记录功能
    • 顺便说一句,Delphi 编译器显然知道方法偏移量,我只是想找到一种方法来做同样的事情,而不是使用硬编码的偏移量。
    • 是的,编译器知道,但据我所知,没有办法直接得到它。
    猜你喜欢
    • 2013-01-05
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    相关资源
    最近更新 更多