7

In c++ is there any way to get the real address of member function, or the index in vTable ?

Updated:

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

Here's why I want to know this:

I want to hook the function ID3DXFont->DrawText of DirectX. If I know the index of the DrawText in the vTable, I can replace it to do the hook. But how to get the index? If it's able to get the the real address, I can search it in the vTable to get the index.

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

Here's what I've 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 code above outputs the address of jump table. And in release mode, the &cls::fn2 returns 0x00401058, which points to 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 the real address. Anyway to do that?

Thanks.

aj3423
  • 2,003
  • 3
  • 32
  • 70
  • 3
    This is a quite trick question. Since you are using virtual method it depend on the compiler you are using. – mathk Jul 09 '14 at 09:06
  • Not possible in general. However, read about [RTTI](http://en.wikipedia.org/wiki/Run-time_type_information), and explain why you are asking your question.... (why does the machine address of functions matters to you?)! – Basile Starynkevitch Jul 09 '14 at 09:09
  • @aj3423 what do you mean by the term "real address"? How about virtual memory concept? – spin_eight Jul 09 '14 at 09:29
  • I think it's good to add it on next c++ standard .. – ikh Jul 09 '14 at 09:45
  • 1
    Why do you want that? – John Dvorak Jul 09 '14 at 09:45
  • Thanks, thread updated, comments added for why doing this. – aj3423 Jul 09 '14 at 10:16
  • The values you show for release mode looks like a trampoline to the actual function. In both release mode and debug mode, you'll have to get the address of the object you want to call the function on into ECX (assuming MSC, which seems safe, given the use of ECX in the code and your use of DWORD). I don't know how you expect to do that in C++. – James Kanze Jul 09 '14 at 11:07

3 Answers3

5

Don't give up so easily!

While the other answers are correct in saying that the C++ language doesn't allow you to do this in a portable way, there's an important factor in your particular case that may make this a more reasonable thing to do.

The key is that ID3DXFont is a COM interface and the exact binary details of how those work are specified separately from the language used to access them. So while C++ doesn't say what you'll find at the other end of that pointer, COM does say that there's a vtable there with an array of function pointers in a specified order and with a specified calling convention. This allows me to tell you that the index of the DrawText function is 314 (DrawTextA) or 15 (DrawTextW) and that this will still be true in Visual C++ 28.0 many years from now. Or in GCC 8.3.1 for that matter: since COM is a binary interface specification, all compilers are supposed to implement it the same way (if they claim to support COM).

Have a look at the second link below for a ready-made implementation of COM function hooking using two different methods. Approach#2 is the closest to what you're asking for but I think you may want to consider the first one instead because it involves 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]

slajoie
  • 440
  • 8
  • 20
  • Why the index of DrawText in vtable is 14 on my machine (win7 32bit + vs2010 + debug mode) – aj3423 Jul 10 '14 at 04:11
  • @aj3 Sorry, I assumed the functions were listed in interface order in MSDN but they are simply in alphabetical order. You are correct: it's 14 (or 15 for UTF-16 version). Answer updated. But I assure you, this does not depend on your machine, compiler or compiler settings. – slajoie Jul 10 '14 at 05:27
2

There's nothing anywhere near portable. Your attempt using &cls::fn2 can't work, since the results must work in cases like (pCls->*fn)() even when pCls points to a derived class which overrides the function. (Pointers to member functions are complicated beasts, which identify whether the function is virtual or not, and provide different information depending on this. And if you're experimenting with MSC, be aware that you have to specify /vmg for pointers to member functions to work correctly.)

Even for a given implementation, you need an instance of the correct type. Given that, if you know the class layout, and the layout of the virtual function table, you can track it down. Typically, the pointer to the virtual function table is the first word in the class, although this is not guaranteed. And usually, the functions will appear in the order they are declared. Along with additional information, however, like pointers to the RTTI, and possibly offset information required to fix up the this pointer when calling the function (although many compilers will use a trampoline for this). For 64 bit g++ under 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 of fn1 for the object passed to it; if the object is of type C, it returns the address of C::fn1, and if it is of a derived type which overrides fn1, it returns the address of the overriding function.

Whether this works all of the time or not, I cannot say; I think g++ uses trampolines in cases of multiple inheritance, for example (in which case, the returned address would be the address of the trampoline). And it might not work the next major release of g++. (For the version of MSC I have at hand, replacing the index 2 with 1 seems to work. But again, I only tried very simple cases. There are absolutely no guarantees.)

Basically, you would never want to do anything like this in production code. It can be useful, however, if you're trying to understand how the compiler works.

EDIT:

Re your edit with the why? Just because you have the address (maybe), it doesn't mean that you can call the function. You cannot call a member function without an object, and depending on any number of things, you may not be able to pass the function the object. (With MSC, for example, the object will usually be passed in ECX.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Sorry I didn't explain why at first, thread is now updated. I don't know the index either, if I know that I can get the address. – aj3423 Jul 09 '14 at 10:33
  • @aj3423 The index will vary from one compiler to the next, and as I point out, may _not_ be sufficient. For simple cases, the entries in the table are in the order of the virtual functions declared in the class (with any virtual functions it the class' base classes first). There may be some additional entries, either at the start or at the end, and each entry may contain some additional information. And of course, the calling sequence for a member function may be different than that of a normal function (the case with MSC, for example). – James Kanze Jul 09 '14 at 11:03
1

as mentioned in this wiki page:

Whenever a class defines a virtual function (or method), most compilers add a hidden member variable to the class which points to a 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 don't have access to the Vtable, the compiler doesn't even know the number of entries in the table.

eladm26
  • 517
  • 4
  • 23
  • 1
    Actually, the compiler knows more about the layout of the vtable than you do. – James Kanze Jul 09 '14 at 10:25
  • but on compile time the compiler doesn't know which function each entry in the vtable should point to, the base class function or the derived class function. the entries are set on runtime. – eladm26 Jul 09 '14 at 10:44
  • Clearly, since the entry can point to different functions at different times. But _you_ don't even know which entry, if any, is relevant to the function you are interested in. – James Kanze Jul 09 '14 at 11:11