如何获取给定类的虚拟重载过程的地址

时间:2016-01-05 17:03:33

标签: delphi

一些上下文......我需要为a bug in the VCL's TField.CopyData routine编写修复程序。

不是重新编译VCL的忠实粉丝,我通常选择method hooks

通常,它非常简单,写下替换方法并将其挂钩代替原始方法:

HookProc(@TMenu.ProcessMenuChar, @TPatchMenu.ProcessMenuChar, Backup);

但这一次,该方法是虚拟的并且过载了。我似乎无法找到一种可靠的方法来获得正确的方法地址。

如果它没有超载,我可以简单地使用@TField.CopyData并且它会起作用,但是不能保证为重载函数提供正确的地址。

如果它不是虚拟的,我可以扩展the method described here并执行以下操作

type
  TCopyDataMethod = procedure(Source, Dest : TValueBuffer) of object;

procedure DoHook;
var vOldMethod : TCopyDataMethod;
begin
  vOldMethod := TField(nil).CopyData;
  HookProc(TMethod(vOldMethod).Code, @TSomeClass.NewMethod, Backup);
end;

但是这会产生访问冲突(它使用nil引用在VMT中查找正确的CopyData地址)。

我尝试了各种语法,这些语法都给了我"不兼容的类型"在编译时。

我确实提出了一个解决方案(发布为答案),但它并不理想。

3 个答案:

答案 0 :(得分:1)

受David Heffernan的回答启发,这是我正在寻找/希望的解决方案。

type
  TCopyDataMethod = procedure(Source, Dest : TValueBuffer) of object;

procedure Proc;
var VMT : NativeInt;
    vMethod : TCopyDataMethod;
begin
  VMT := NativeInt(TField);
  vMethod := TField(@Vmt).CopyData;
  uOriginalAddress := @vMethod;
    [...]
end;

我相信这是最直接的。

答案 1 :(得分:0)

我发现此问题的唯一解决方案是创建类

的实例
procedure Proc;
var vMethod : TCopyDataMethod;
    vFieldOrig : TField;
    [...]
begin
  vFieldNew := nil;
  vFieldOrig := TField.Create(nil);
  try

    vMethod := vFieldOrig.CopyData;
    uOriginalAddress := @vMethod;
    [...]
  finally
    vFieldOrig.Free;
  end;
end;

希望有一个更优雅的解决方案。

答案 2 :(得分:0)

如果您知道VMT中的索引,则可以直接从该类获取地址。如果您在调试器下查看对CopyData的调用,您会看到以下内容:

Project1.dpr.19: THackField(Field).CopyData(TValueBuffer(nil), TValueBuffer(nil));
0053886B 33C9             xor ecx,ecx
0053886D 33D2             xor edx,edx
0053886F A104345400       mov eax,[$00543404]
00538874 8B18             mov ebx,[eax]
00538876 FF93A4000000     call dword ptr [ebx+$000000a4]

在这里我们可以看到,在我的XE7中,方法点与VMT的偏移量为$000000a4个字节。该类是指向VMT的指针。

所以,你可以得到这样的方法地址:

Pointer(NativeUInt(TField) + $00a4)

我希望您能够使用增强型RTTI提出更优雅的解决方案。但是,当您想要修补特定库版本中的错误时,通常会这样做。所以你硬编码特定版本的值,否则抛出编译器错误。至少,这就是我的工作!

说完所有这些之后,在你的答案中证明,在启动时实例化实例并没有什么可羞愧的。只要这没有不必要的副作用,我就看不到任何真正的缺点。