获取虚拟成员函数的实际地址(或vTable中的索引) 在C++中,有没有办法获得成员函数的真地址< /强>,或者在VTAB.< /P>中索引< /强>?
更新: 我不知道vTable中的索引 我不知道地址 以下是我想知道的原因: 我想钩住DirectX的函数ID3DXFont->DrawText。如果我知道vTable中DrawText的索引,我可以替换它来执行挂钩。但是如何获得索引呢?如果它能够获得真实地址,我可以在vTable中搜索它以获得索引 而且不是特别的ID3DXFont->DrawText,将来可能还有其他一些函数,所以我尝试编写一个通用的钩子函数 以下是我迄今为止所尝试的:获取虚拟成员函数的实际地址(或vTable中的索引) 在C++中,有没有办法获得成员函数的真地址< /强>,或者在VTAB.< /P>中索引< /强>?,c++,member-functions,C++,Member Functions,更新: 我不知道vTable中的索引 我不知道地址 以下是我想知道的原因: 我想钩住DirectX的函数ID3DXFont->DrawText。如果我知道vTable中DrawText的索引,我可以替换它来执行挂钩。但是如何获得索引呢?如果它能够获得真实地址,我可以在vTable中搜索它以获得索引 而且不是特别的ID3DXFont->DrawText,将来可能还有其他一些函数,所以我尝试编写一个通用的钩子函数 以下是我迄今为止所尝试的: #include <iostream> usi
#include <iostream>
using namespace std;
struct cls {
virtual int fn1() {
cout << "fn1 called" << endl;
return 1;
}
virtual int fn2() {
cout << "fn2 called" << endl;
return 2;
}
};
template <typename fn_t>
DWORD fn_to_addr(fn_t fn) { // convert function to DWORD for printing
union U {
fn_t fn;
DWORD addr;
};
U u;
u.fn = fn;
return u.addr;
}
int main() {
cls c;
DWORD addr = fn_to_addr(&cls::fn2);
cout << hex << addr << endl;
}
两者都不是真实的地址。不管怎样,你想这么做吗
谢谢。如本文所述:
每当一个类定义一个虚函数(或方法)时,大多数
编译器将一个隐藏的成员变量添加到指向
所谓的虚拟方法表(VMT或Vtable)。这个VMT基本上是
指向(虚拟)函数的指针数组
据我所知,您没有访问Vtable的权限,编译器甚至不知道表中的条目数 附近什么都没有。您尝试使用
&cls::fn2
无法工作,因为结果必须在特定情况下工作
类似于(pCls->*fn)(
,即使pCls
指向派生类
它将覆盖该函数。(指向成员函数的指针是
复杂的野兽,用于识别函数是否为
虚拟或非虚拟,并根据需要提供不同的信息
如果你在用MSC做实验,请注意
必须为指向要工作的成员函数的指针指定/vmg
正确。)
即使对于给定的实现,也需要
正确的类型。鉴于此,如果你知道课堂布局
虚拟函数表的布局,您可以跟踪它。
通常,指向虚拟函数表的指针是
课堂上的第一个单词,尽管这不能保证。及
通常,函数将按其显示的顺序显示
宣布。但是,随着附加信息的增加,例如
指向RTTI的指针,以及可能需要的偏移量信息
在调用函数时修复此
指针(尽管
许多编译器会为此使用蹦床)。对于64位g++
在Windows(CygWin版本)下:
struct C
{
虚拟~C(){}
虚拟空间fn1()const{std::cout不要轻易放弃
虽然其他答案是正确的,因为C++语言不允许你以一种可移植的方式来做这件事,但是在你的特定情况下有一个重要的因素,这可能使这件事更合理。
<3>是ID3DXFooT是一个COM接口,以及那些工作是如何与访问它们的语言分开的精确二进制细节。因此,尽管C++没有说明在指针的另一端会发现什么,com确实表示有一个VTABLE,其中有一组函数指针,它们具有指定的顺序和一个SPECF。IED调用约定。这允许我告诉你,DRAWTEXT函数的索引是314(DrawTextA)或15(DRAWTEXW),而且在VisualC++ 28之后的许多年内,这仍然是正确的。或者在GCC 83.1中,因为COM是二进制接口规范,所有编译器都应该以同样的方式实现。(如果他们声称支持COM)
查看下面的第二个链接,使用两种不同的方法来实现COM函数挂钩。方法2最接近您所要求的,但我认为您可能会考虑第一个,而不是因为Vododo.
资料来源:
[
[
[这是一个相当棘手的问题。由于您使用的是虚拟方法,它取决于您使用的编译器。通常不可能。但是,请阅读并解释您提出问题的原因……(为什么函数的机器地址对您很重要?)@aj3423术语“真实地址”是什么意思虚拟内存概念如何?我认为在下一个C++标准上添加它是很好的。为什么要这样?实际上,编译器比VoD知道更多的VToT布局。但是编译时编译器不知道VToT中的每个条目应该指向哪个函数,基类函数或派生类函数。ries是在运行时设置的。很明显,因为条目可以在不同的时间指向不同的函数。但是您甚至不知道哪个条目(如果有的话)与您感兴趣的函数相关。很抱歉,我没有解释为什么一开始,线程现在被更新了。如果我知道我可以获得地址,我也不知道索引。@aj3423索引将被更新根据不同的编译器而有所不同,正如我指出的,这可能是不够的。对于简单的情况,表中的条目是按照类中声明的虚拟函数的顺序排列的(任何虚拟函数都是先声明类的基类)。可能在开始或结束时有一些附加条目,每个条目可能包含一些附加信息。当然,成员函数的调用顺序可能不同于普通函数的调用顺序(例如MSC)。为什么在我的机器上vtable中的DrawText索引为14(win7 32位+vs2010+调试模式)@aj3抱歉,我假设函数在MSDN中是按接口顺序列出的,但它们只是按字母顺序排列的。您是对的:它是14(对于UTF-16版本是15)。答案已更新。但我向您保证,这不取决于您的计算机、编译器或编译器设置。
00401058 . mov eax, dword ptr [ecx] // get vptr
0040105A . jmp dword ptr [eax+4] // jmp to the second function (fn2)
struct C
{
virtual ~C() {}
virtual void fn1() const { std::cout << "In C::fn1\n"; }
virtual void fn2() const {}
};
void const*
fn1ToAddr( C const* pC )
{
void const* const* vPtr = *reinterpret_cast<void const* const* const*>(pC);
return vPtr[2];
}