Run-time determination of VC++ virtual member function addresses: Take II
I wrote about this tricky little problem a while ago and wasn’t too happy with the desperate methods that seemed necessary. Since then, I’ve been shown a much cleaner way to do the same thing, by manipulating the vTable manually. It seems that Microsoft haven’t changed their vTable implementation since Visual Studio 6 (at least) and so with a little modification, the following piece of inline-assembly will do the trick: no muss, no fuss.
__declspec(naked) void* ResolveVirtualFunction(IDirect3DDevice9* pDevice, ...) { __asm { mov eax, dword ptr ss:[esp+0x08] add eax, 0x8 cmp byte ptr ds:[eax-1], 0xA0 mov eax, dword ptr ds:[eax] je normal_index and eax, 0xFF normal_index: mov ecx, eax mov eax, dword ptr ss:[esp+0x4] mov eax, dword ptr ds:[eax] mov eax, dword ptr ds:[eax+ecx] retn } } // ... // The function should be invoked like this: void* address_device_present = ResolveVirtualFunction(device, &IDirect3DDevice9::Present);
Thanks go to Vuurvlieg for this function. The beauty (or horror), here, is the use of a variadic parameter-list to overcome C++’s strong-typing that would otherwise make this operation very difficult. Obviously, this implementation will only work for objects of type IDirect3DDevice9, but the method extends to any other class by simply replacing the class name in the function declaration. Don’t be tempted to generalise this function to IUnknown or some other common base-class, as you’ll quickly run into problems with object-slicing. A final warning to those still using Visual C++ 6 (not that you deserve any help for such a crime): you’ll need to drop the ampersand from the second argument in the function call, as VC++6 handles function pointers slightly differently.
February 8th, 2008 at 3:22 pm
Hi Greg,
I tried using this method to get a member function address but I believe it only works on virtual methods right? I’m trying to retrieve the address, in runtime, of one of my hook functions which is a member function in opposite to you where you used a global method. Do you have any hints on how this can be achieved? a simple &Class:MethodName doesn’t work because as you know, member functions are no ordinary func ptrs. They point to a sort of struct containing many info.
Also, regarding the run-time determination of a virtual method explained in this article, what would be the member values of the class once you’re in the method? I’m trying to understand how all of this works. Any articles/books you recommend for reading?
Thanks,
Alexandre
February 8th, 2008 at 3:50 pm
Hi Alexandre.
C++ offers a means of doing this so it’s best to avoid nasty inline assembly hacks, despite the terrifying syntax. At least, this is the case for function-pointers alone - if you need the address in void* or integral form then direct conversion will require some assembly, as C++ strictly disallows casting from a function pointer.
Suppose we have a class DLLInjection with a static function, CallThreadProc. Then if the prototype is given by the following (I picked something general);
Then we can define a function pointer using:
We define a type FPTR to hold the member-function-pointer and instantiate it as fptr. You can do this without a typedef, but that only serves to make things even messier. If you then need this as a plain address, use something like:
That covers the address resolution (a good explanation can be found on goingware). As for usage of the pointer, you’ll probably want to read up on the thiscall calling convention. Simply put (in this - static - case), the function is called just like any stdcall function (the default for externally linked functions) only the this pointer is loaded into ECX prior to calling.
So to use this function from assembly, you could invoke it thus:
February 8th, 2008 at 10:36 pm
Hi again Greg,
Have you tested this code on your end? Does it require to be in a specific scope? I tried it with a Direct3DDevice9 I created from IDirect3D9::Direct3DCreate9 which wasn’t null, and I tried with an instance of one of my classes that has a pure virtual method that is overridden by a subclass. i.e. MainClass* pInst = new SubClass(); (SubClass inherits from MainClass). I changed declaration of the resolveaddr method to accept a MainClass* and I still got an access violation. :\ I am using VS2005. I will do more tests this weekend.
Thank you,
Alexandre
February 9th, 2008 at 5:38 pm
Right, sorry about that. I did test the code, but I decided to ‘clean’ it up a bit before posting and neglected to update the stack offsets in the process.
I’ve corrected that little error and it now works just fine :).
March 30th, 2008 at 11:01 am
Hi,
is it possible that the function is broken in VC++ 2008 Express?
Doing this:
HWND hWnd = ::GetTopWindow(NULL);
if (hWnd == NULL) MessageBox(NULL, “Can get Window handle”, “D3DHOOK.dll”, MB_OK);
d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&dp,
&d3dDevice
);
if (d3dDevice == NULL) MessageBox(NULL, “Can get 3D Device”, “D3DHOOK.dll”, MB_OK);
char buffer[100];
address_DevicePresent = ResolveVirtualFunction(d3dDevice, &IDirect3DDevice9::Present);
address_SwapChainPresent = ResolveVirtualFunction(d3dDevice, &IDirect3DSwapChain9::Present);
address_Clear = ResolveVirtualFunction(d3dDevice, &IDirect3DDevice9::Clear);
sprintf(buffer,”Device present from vtable at %p”,address_DevicePresent);
MessageBox(NULL, buffer,”D3DHOOK.dll”, MB_OK);
sprintf(buffer,”Swapchain present from vtable at %p”,address_SwapChainPresent);
MessageBox(NULL, buffer,”D3DHOOK.dll”, MB_OK);
sprintf(buffer,”Clear from vtable at %p”,address_Clear);
MessageBox(NULL, buffer,”D3DHOOK.dll”, MB_OK);
d3dDevice->Release();
i get exactly the same pointer for all three functions (which looks strange to me).
Thanks,
Frank
March 30th, 2008 at 11:04 am
Vista64 SP1 btw (if thats makes a difference??)
March 30th, 2008 at 4:20 pm
Hi Frank.
Yes, it is quite likely that something’s up on my end. I’ve had a couple of problem reports that I have been too lazy to investigate fully, and your code looks fine. I’m afraid I can’t recommend anything at the moment, other than manual investigation under a debugger. At least until I can get to the root of the problem.
August 19th, 2008 at 5:51 pm
This can probably be done in 4 lines of C++ with a template function and a couple of unsafe casts
August 19th, 2008 at 6:43 pm
That’s what we all thought first time ’round, Anonymouse. I’d love to see your implementation
August 19th, 2008 at 8:29 pm
Note that the function in the blog post does not work in all cases (eg. when the vtable offset is 0) and the below function only works on thunks produced by Visual C++ (and probably not on the type of thunks that the original function examines), so this is only a proof-of-concept.
template
void* ResolveVirtualFunction(C obj, VF fun) {
void* code = *(void**)&fun;
int offset = *((char*)code + 4);
void* vtable = *(void**)obj;
void* entry = *((void**)vtable + offset);
return entry;
}
August 19th, 2008 at 8:33 pm
Your blog software destroyed the template arguments in the last post, the first line was supposed to be:
template < typename C, typename VF >
You can modify the code to work exactly as the assembly code, for that you need to modify the “int offset = …” to use an int* and conditionally do the offset & 0xFF.
August 19th, 2008 at 8:42 pm
As for hooking methods in COM interfaces: the easiest way to do this is to use eg. the DirectX headers in C mode (by a define which I can not remember right now), so the vtable is exposed directly. The hook then basically boils down to patching the vtable:
device->lpVtbl->IDirect3DDevice9_Present = pointer_to_my_function;
August 20th, 2008 at 9:49 am
Four lines of C then :P. That’s certainly more readable than the inline assembly, but there’s no way to use a C++ cast (even a reinterpret_cast) for that first line, as far as I could tell. Nevertheless, that’s some useful information, particularly regarding the vTable hooking.
Thanks
Greg