Get the real address (or index in vTable) of a virtual member function

In C ++, is there a way to get a valid member function address or index in vTable?

Updated:

I don’t know INDEX in vTable and I don’t know the address

Here's why I want to know this:

I want to bind the function ID3DXFont-> DrawText DirectX. If I know the DrawText index in vTable, I can replace it with a hook. But how to get an index? If it can get the real address, I can find it in vTable to get the index.

And not especially ID3DXFont-> DrawText , maybe some other functions in the future, so I'm trying to write a generic hook function.

Here is what I have tried so far:

#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; } 

In debug mode, the above code displays the address of the jump table. And in release mode & cls :: fn2 returns 0x00401058 , which indicates some optimized code:

 00401058 . mov eax, dword ptr [ecx] // get vptr 0040105A . jmp dword ptr [eax+4] // jmp to the second function (fn2) 

Both are not a real address. Anyway to do this?

Thanks.

+6
source share
3 answers

Do not give up so easily!

While the other answers are correct, saying that C ++ does not allow you to do this in a portable way, in your particular case there is an important factor that can make this a more sensible thing.

The key is that ID3DXFont is a COM interface and accurate binary data on how these works are specified separately from the language used to access them. Therefore, while C ++ does not say what you will find at the other end of this pointer, COM says that there is a vtable with an array of function pointers in the specified order and with the specified calling convention. This allows me to tell you that the index of the DrawText function is 3 14 (DrawTextA) or 15 (DrawTextW), and that this will still be true in Visual C ++ 28.0 in many years. Or in GCC 8.3.1: if COM is a binary interface specification, all compilers should implement it the same way (if they claim to support COM).

Look at the second link below for a ready-made implementation of connecting to a COM function using two different methods. Approach # 2 is close to what you are asking for, but I think you can consider the first one first, because it includes less voodoo.

Sources:

[ http://msdn.microsoft.com/en-us/library/ms680573 (v = vs .85) .aspx] [ http://www.codeproject.com/Articles/153096/Intercepting-Calls-to-COM -Interfaces] [ http://goodrender.googlecode.com/svn/trunk/include/d3dx9core.h]

+3
source

There is nothing portable. Your attempt to use &cls::fn2 cannot work, since the results should work in cases like (pCls->*fn)() , even if pCls points to a derived class that overrides the function. (Pointers to member functions are complex beasts that determine whether this function is virtual or not, and provide different information depending on this. And if you are experimenting with MSC, keep in mind that you must specify /vmg for function pointers members to work correctly.)

Even for this implementation, you need an instance of the correct type. Given this, if you know the layout of the class and the location of the virtual function table, you can track it. Typically, a pointer to a virtual function table is the first word in a class, although this is not guaranteed. And also usually functions will be displayed in the order in which they are declared. Together with additional information, for example, pointers to RTTI and, possibly, the shift of the necessary information to fix the this pointer when calling the function (although many compilers will use a trampoline for this). For 64 bit g ++ on Windows (CygWin version):

 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]; } 

fn1ToAddr returns the address fn1 for the passed object to it; if the object is of type C , it returns the address C::fn1 , and if it has a derived type that overrides fn1 , it returns the address of the overriding function.

Whether it is all the time or not, I cannot say; I think g ++ uses trampolines in cases of multiple inheritance, for example (in this case, the trampoline address will be the return address). And this may not work for the main release of g ++. (For the MSC version that I have on hand, replacing the 2 by 1 index seems to work. But then again, I just tried very simple cases. There is absolutely no guarantee.)

Basically, you will never want to do anything like this in production code. However, it can be useful if you are trying to understand how the compiler works.

EDIT:

Repeat your editing with what? Just because you have an address (possibly) does not mean that you can call a function. You cannot call a member function without an object, and depending on any number of things, you cannot pass an object function. (For example, with MSC, the object will usually be transferred to ECX.)

+2
source

as indicated on this wiki page :

Whenever a class defines a virtual function (or method), most compilers add a hidden member variable to the class that points to the so-called virtual method table (VMT or Vtable). This VMT is basically an array of pointers to (virtual) functions.

As far as I know, you do not have access to Vtable, the compiler does not even know the number of records in the table.

+1
source

Source: https://habr.com/ru/post/971958/


All Articles