I’ve been programming with detours lately and all that comes with it. I have detoured a lot of different functions; thiscall, stdcall, cdecl, virtual functions etc. But there is one thing I haven’t managed (which might not even be possible), and that is to hook a base class virtual function. For example; there is a Car class which declares a virtual function (empty) Drive. Then there are 3 other car classes which inherits car and implements Drive.
If I hook the Car’s (base class) Drive function (using a simple ‘jmp’ hook) would it be triggered by the descendants of Car, when they trigger Drive, if they do not call the base function?
To explain even more thoroughly:
class Car
{
virtual void Drive(void) { } // Empty virtual function
}
class Lamborghini : public Car
{
void Drive(void) { // does lots of stuff, but does NOT call base function }
}
So I’m wondering whether the base method get’s called or if it can be hooked somehow? Does the function exectution jump directly to Lamborghini::Drive or does it somehow pass through the Car class so it’s detectable whenever a descendant calls Drive?
EDIT: And if the base class function is empty, is it even possible to hook it, since it requires 5 bytes of space?
No, the base method does not get automagically called. The dynamic dispatch mechanism will detect which override it needs to call and that will be the function that gets called. This is usually implemented by means of a virtual table (vtable) that stores the pointers to the final overriders for each virtual function in a class. When dynamic dispatch is used, the compiler injects an indirect call through that table and jumps to the proper function.
Note that the vtable actually holds pointers to thunks or trampolines that can potentially modify
this(implicit first argument) before forwarding the call. The advantage of using this approach is that ifthisdoes not need to be updated, the compiler can jump directly into the final overrider. At any rate, you can take advantage of this feature and modify the vtables to point to your own code (i.e. you can update the pointer in each vtable –one per type– to refer to your own thunk or function)