Sign in to follow this  
Emmanuel Deloget

address of a C++ member function

Recommended Posts

Hello everybody, This is a C++ question. Not Managed C++, just C++, using VC6 and VC7. Before anything, I want to say that I do not search the "pointer to member" syntax. I do know it, and I do know boost::function - and a lot of other things that are related to pointer to functions and pointer to members. What I search is really simply that : the address of a member function. I need to get it in order to be able to compute a checksum on the current running code, and I do not know how to get it. I already found this : (from boost mailing list). It cannot help me since it uses a static function proxy in order to get the whole thing running. The key fact is that I do not need to execute some code, I just need to be know where code is located in memory. It does not matter wether the code is only working on VC++ or not, since this is not a portable software. In case you want to know the reason why I want that : we want to check wether our code has been patched/cracked or not. Since this will protect a $20000 software, we have to find good protection schemes - and this is just one. Hope I'm clear (if not, I'll give more explainations), and thanks a lot,

Share this post


Link to post
Share on other sites
There's several articles floating around the net - I'm assuming that something like this is what you are looking to do? Also CodeProject also has one. There's a host of resources on Function-pointer.org that will be useful for you too, assuming this is what you're talking about doing?

Share this post


Link to post
Share on other sites
Thanks for the links evolutional (especially the function-pointer one, since I lost it some weeks ago).

Well, seems I'm not that clear :)

Thoose article only deal with pointer to member function. Hélas, pointer to members are not pointers - they do not represent a memory block, since they carry a class context and so on. What I need is a real pointer which will point to the begining of a member function.

For virtual functions, I can find some vtbl hacks (some are lying out there, so it is not that difficult) that will give me the real address of the function. But I cannot find any reference that deal with non-virtuals.

For instance, I want to know where the code of a particular (not inlined) method is located in memory. I thought this was a very low level task, one which would be easily done, but it seems that this is not the case and that our C++ compilers do not allow us to know where it locates the method code.

Yours,

Share this post


Link to post
Share on other sites
Experiments...

I got this code working in both debug and release mode using VC6. It do not get the pointers to the code of virtual function but it seems to work for anything else.

Do not use the return of operator() to actually run some code, it will not work (unless you correctly setup the environment).


template <class CLASS> class NonVirtualFunctionAddressFunctor
{
public:
typedef void (CLASS::*mftype)();

char *operator()(mftype mf)
{
mftype localmf = mf;
unsigned char opcodebase;
int displacement;
char *pcode;

__asm {
push eax
push ebx
mov eax, localmf
mov pcode, eax
mov bl, [eax]
mov opcodebase, bl
inc eax
mov ebx, [eax]
mov displacement, ebx
pop ebx
pop eax
};

switch (opcodebase)
{
case 0xe9: // jmp rel32
pcode += (displacement + 5);
break;
case 0xe8: // jmp rel8
pcode += ((displacement&0xFF) + 2);
break;
default:
break;
}

return pcode;
}
};

class C
{
public:
void method_0() { }
int method_1(int a, int b) { return 0; }
};

int main(int argc, char* argv[])
{
NonVirtualFunctionAddressFunctor<C> fa_c;

pcode = fa_c((NonVirtualFunctionAddressFunctor<C>::mftype)(C::method_0));
printf("fa_c pcode=%p\n", pcode);

pcode = fa_c((NonVirtualFunctionAddressFunctor<C>::mftype)(C::method_1));
printf("fa_c pcode=%p\n", pcode);

getch();

return 0;
}




Of course, this will only work with VC6 (and maybe VC7, not tested). This is Dirty Experimental Code (tm).

Explaination : the use of assembly is required here to bypass the compiler error you get when you cast a pointer to member function to a generic pointer (this is "localmf" to "pcode"). Then I get the first instruction which is located at address "localmf". This is done because in debug mode (yeah, I want my code to work in debug mode too :) the VC6 compiler implements proxy for all the non virtual member functions. If the instruction is a jmp instruction then I am probably in debug mode. I get the rel32 value (I did not found any "jmp rel8" here) and I offset pcode accordingly. As a sidenote, once you get the original pcode, you do not need assembly anymore (the 5 bytes after pcode are easily read). I suppose the code was not complex enough, and I tried to add a little more complexity. Just plain stupid :/

In release mode, I don't get this jmp - and terefore I do not need to modify pcode.

More can be added to track virtual functions. The code which is finnaly pointed by "pcode" can be analyzed to see wether it is a virtual function proxy or not. If it is a virtual function, then I must read the vtable in order to find the correct address.

As I said, this should not be used to get a C function pointer - it is not intended to this. I did this to compute a code checksum - in order to see wether the code has been patched or not before exec.

Thanks Evolutional for your help (I finally found informations about how the vtbl is encoded in VC6 using your links :)

Yours,

(edit : typos)

Share this post


Link to post
Share on other sites
I was curious so I did this myself. Here are my source files. You have to split into two objects and make the function pointer dependent on an input variable, otherwise everything gets optimized-out:

// foo.h:
class Foo
{
public:
double One( long inVal );
double Two( long inVal );
};

// foo.cpp:
#include "foo.h"

double Foo::One( long inVal )
{
return 1.0 * inVal;
}

double Foo::Two( long inVal )
{
return 2.0 * inVal;
}

// main.cpp:
#include "foo.h"

int main(int argc)
{
double (Foo::*funcPtr)( long ) = argc == 1 ? &Foo::One : &Foo::Two;

Foo aFoo;

double result =(aFoo.*funcPtr)( 2 );

return static_cast<int> (result);
}



In both debug and release cases (VS.NET), the variable funcPtr does in fact point to the address of the function to be called, but in the debug case it points there indirectly. Single-stepping through the assembly, the pointer is actually a pointer to a jump table, which looks like all the functions in the program:

// The call:
double result =(aFoo.*funcPtr)( 2 );
00411B65 push 2
00411B67 lea ecx,[aFoo]
00411B6A call dword ptr [funcPtr]
00411B6D fstp qword ptr [result]

// funcPtr = 0x411ba:
004114B0 jmp ExitProcess (424B8Ch)
004114B5 jmp HeapAlloc (424C22h)
004114BA jmp Foo::One (411B80h) // jumps here
004114BF jmp HeapReAlloc (424C2Eh)
004114C4 jmp _amsg_exit (411F90h)

// that jumps to...
double Foo::One( long inVal )
{
00411B80 push ebp // 411b80h from the jump table..
00411B81 mov ebp,esp
00411B83 sub esp,44h
00411B86 push ebx




In Release mode, the pointer just jumps there directly. It's kept on the stack of course, but the value loaded is the target address:

double (Foo::*funcPtr)( long ) = argc == 1 ? &Foo::One : &Foo::Two;
00401001 cmp dword ptr [esp+8],1
00401006 mov dword ptr [esp+8],offset Foo::One (401030h)
0040100E je main+18h (401018h)
00401010 mov dword ptr [esp+8],offset Foo::Two (401040h)

// so 401030h & 401040h are the ::One and ::Two functions
// The call:
double result =(aFoo.*funcPtr)( 2 );
00401018 push 2
0040101A lea ecx,[esp+4]
0040101E call dword ptr [esp+0Ch]

// [esp+8] = 401030h, which jumps to..
double Foo::One( long inVal )
{
return 1.0 * inVal;
00401030 fild dword ptr [esp+4]
}
00401034 ret 4




So you see, the member pointer _is_ the address, but VS.NET is using an indirect jump table in debug mode. It goes without saying that if you're using the actual value of this guy, you're going way outside the realm of defined behavior, but as long as you know your compiler you should have consistent behavior in your app.

Edit: looks like we cross-posted. Hope you have your questions answered.

Share this post


Link to post
Share on other sites
Thanks Stoffel, I see from your post that both VC6 and VC7 flavours are using the same kind of jump tables in debug mode (bad... I did not have time to create a VC7 version of my code :/)

I was curious about the C++ cast (reinterpret_cast<> and so on). I tried them on VC6 at work but none of them seems to solve the "pointer to member" to "generic pointer" problem, so I guess I'll have to stick with assembly.

Do anyone found a similar construct which do not uses assembly at all ? Does C++ allows it (seems to be a limitation of the C++ standard but since I do not have the text here I cannot check) ?

Now I have to work harder to get the VirtualFonctionAddressFunctor work :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Do anyone found a similar construct which do not uses assembly at all ? Does C++ allows it (seems to be a limitation of the C++ standard but since I do not have the text here I cannot check) ?


Sorry, but no. Pointers-to-members are implementation-defined. Even just in VC, the actual implementation varies depending on the class' properties. Manipulation might be possible by reinterpret_casting a pointer to a pointer-to-member into a pointer to some data structure, just like you do with basic types (you don't cast an int to a Foo, you cast an int* to a Foo*).

There is no general way to find the address of a member function (especially a virtual member function). You will have to reverse-engineer your specific compiler's implementation of them (or look on the net, who knows, it might be out there).

Share this post


Link to post
Share on other sites
You do not have to be sorry, I'm pretty sure it is not your fault :)

I tried to reinterpret_cast<> but the compiler continues to generate an error.

I forgot all these pragma. Bad, very bad. Since I won't be able to know what is the current pointer to member representation, I guess I will not be able to write code that will work for all representation.

Bad :/

Anyway, thanks a lot everybody.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this