获取虚成员函数的真实地址(或vTable中的索引)
get the real address(or index in vTable) of virtual member function
在c++中有没有办法得到成员函数的实际地址,或者vTable中的索引 ?
更新:
我不知道索引在vTable和不知道地址
这是为什么我想知道这个:
我想挂钩函数ID3DXFont->DrawText的DirectX。如果我知道vTable中DrawText的索引,我可以替换它来做钩子。但是如何得到索引呢?如果它能得到真正的地址,我可以在vTable中搜索它来得到索引。
而不是特别ID3DXFont->DrawText,也许在未来的一些其他函数,所以我试图写一个通用钩子函数。
以下是我到目前为止所做的尝试:
#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;
}
在调试模式下,上面的代码输出跳转表的地址。在发布模式下,&cls::fn2返回0x00401058,指向一些优化的代码:
00401058 . mov eax, dword ptr [ecx] // get vptr
0040105A . jmp dword ptr [eax+4] // jmp to the second function (fn2)
都不是真实地址。怎么做呢?
谢谢。
别轻言放弃!
虽然其他答案都说c++语言不允许以可移植的方式实现这一点是正确的,但在您的特定情况下,有一个重要因素可能使这成为更合理的事情。
关键是ID3DXFont是一个COM接口,这些工作的确切二进制细节是与用于访问它们的语言分开指定的。因此,虽然c++没有说明在指针的另一端会发现什么,但COM 确实说那里有一个虚函数表,其中包含按指定顺序和指定调用约定的函数指针数组。这允许我告诉你,DrawText函数的索引是
请查看下面的第二个链接,以获取使用两种不同方法的COM函数挂钩的现成实现。方法2最接近你的要求,但我认为你可能会考虑第一个方法,因为它涉及的巫毒较少。
来源:
[http://msdn.microsoft.com/en-us/library/ms680573 (v = vs.85)是[http://www.codeproject.com/Articles/153096/Intercepting-Calls-to-COM-Interfaces]div [http://goodrender.googlecode.com/svn/trunk/include/d3dx9core.h]
没有任何东西接近便携。你尝试使用&cls::fn2
不能工作,因为结果必须在情况下工作像(pCls->*fn)()
一样,即使pCls
指向派生类它覆盖了函数。(指向成员函数的指针是复杂的野兽,它识别是否功能是虚拟与否,并提供不同的信息取决于这一点。如果你在用MSC做实验,要注意必须指定/vmg
指针成员函数的工作正确。)
即使对于给定的实现,也需要正确的类型。如果知道类布局,则虚函数表的布局,可以自己跟踪。通常,指向虚函数表的指针是这是课堂上的第一个单词,虽然不能保证。和通常,函数会按顺序出现宣称。还有一些额外的信息,比如指向RTTI的指针,以及可能需要的偏移信息在调用函数时修复this
指针(尽管许多编译器将为此使用蹦床)。适用于64位g++在Windows下(CygWin版本):
struct C
{
virtual ~C() {}
virtual void fn1() const { std::cout << "In C::fn1n"; }
virtual void fn2() const {}
};
void const*
fn1ToAddr( C const* pC )
{
void const* const* vPtr = *reinterpret_cast<void const* const* const*>(pC);
return vPtr[2];
}
fn1ToAddr
返回传递对象的fn1
的地址它;如果对象的类型是C
,则返回的地址C::fn1
,如果是覆盖fn1
的派生类型,它返回重写函数的地址。
这是否一直有效,我不能说;我认为c++在多重继承的情况下使用了trampolines, for示例(在这种情况下,返回的地址将是蹦床地址)。也许下一个就行不通了g++的主要版本。(对于我手头的MSC版本,用1
替换索引2
似乎有效。但是再一次,我只尝试了非常简单的情况。绝对没有担保。)
你在编辑为什么吗?就因为你有地址(也许),这并不意味着你可以调用这个函数。你如果没有对象,就不能调用成员函数在任何一件事情上,你都可能无法通过函数对象。(例如,在MSC中,宾语will(通常在ECX中传递)
如本wiki页面所述:
每当类定义虚函数(或虚方法)时,大多数编译器将一个隐藏的成员变量添加到指向a的类中所谓的虚拟方法表(VMT或Vtable)。这个VMT基本上是指向(虚)函数的指针数组。
据我所知,你不能访问Vtable,编译器甚至不知道表中的条目数。
- 数组索引的值没有增加
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 查找最接近的大于当前数字的数字的索引
- 在C++中调整向量中的索引
- 重载元组索引运算符-C++
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 为std::string的某个索引赋值
- 并行用于C++17中数组索引范围内的循环
- 跟随整数索引列表的自定义类迭代器
- 如何在for循环中包含两个索引值的测试条件
- D3D11-将混合权重和索引传递到顶点着色器
- 将转换字符键入 int 以用作向量C++的索引
- 在 C++ 中访问数组负索引处的内存不会返回垃圾
- 如何为圆环创建索引
- 在子集化后将包含索引号的列表列表映射到标准索引序列
- 查找字符在两个索引之间出现的次数
- Azure Kinect 使用正文索引映射裁剪正文
- 如何查找哪个类对象位于数组的特定索引上(多态性)
- C++/编译:是否可以设置VPTR(全局vtable + 2字节索引)的大小
- 获取虚成员函数的真实地址(或vTable中的索引)