address of a C++ member function

Started by
6 comments, last by Emmanuel Deloget 19 years, 7 months ago
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,
Advertisement
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?
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,
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)
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 here004114BF  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.
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 :)

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).
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
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.

This topic is closed to new replies.

Advertisement