VMT Hooking Library
Download:
Sedo Domain Parking - Sedo GmbH
Doesn't have a license so use however you like, but I appreciate some credits.
Long-ass documentation post incoming
Introduction
I assume you have a decent knowledge of how virtual functions are implemented and what vtables are in C++, if not I suggest you read
this first.
Virtual functions can be hooked in 2 distinct manners:
- Overwrite the function pointers of the vtable in .rdata. The result is that every instance using that vtable will have its functions hooked. However due to the vtable being located in read-only memory anti-cheats may have an easy time to detect these.
- Replace the vtable pointer of a specific instance. The result is that only virtual functions of that specific instance will be hooked. Depending on the circumstances it may be VERY difficult for an anti-cheat to detect these.
However there are 3 different use cases for hooking virtual functions (2 for instance hooking, 1 for .rdata hooking).
Use case 1: Hook virtual functions of a global instance.
Use the VMTManager class.
This is intended to hook global instances (like the interfaces in the Source Engine).
Methods:
VMTManager::VMTManager( void* inst, size_t offset = 0, uint vfuncs = 0 );
Constructor.
offset is the offset of the vtable in the instance, can be different from 0 when it is using multiple inheritance.
vfuncs Leave 0 if you want it to figure out the number of vfuncs itself.
VMTManager::~VMTManager();
Destructor. Will call Unhook() before destructing. If the instance you're hooking is gone at this point it'll crash. See Poof().
void VMTManager::HookMethod( void* newfunc, size_t index );
Hook a function by index.
void VMTManager::UnhookMethod( size_t index );
Unhook a function by index.
void VMTManager::Unhook();
Puts the original vtable pointer back in place. This means the instance looks like it's NOT hooked at all.
void VMTManager::Rehook();
Puts our vtable with the hooks back.
bool VMTManager::Hooked() const;
Returns true if the instance is hooked.
void VMTManager::EraseHooks();
Removes all the currently placed hooks. The instance will still appear hooked, however all vfuncs will redirect to their original functions.
uint VMTManager::NumFuncs() const;
Access the vfunc count member.
void VMTManager::Poof();
If the instance is somehow destroyed before you get a chance to unhook it or destruct this hook object, call this. It'll prevent the destructor from crashing.
Do NOT attempt to use the this object after calling Poof(), bad things will happen.
template< typename Fn > Fn VMTManager::GetMethod( size_t index ) const;
Get the original function. I highly suggest this over unhooking.
Use a function prototype for the template argument to make it very easy to call this function. Example syntax: hook.GetMethod<bool (__thiscall*)( void*, int )>( 12 )( inst, arg );
How I use it in my hack:
[php]#include "vmthooks.h"
// Indices of the virtual functions
enum
{
CLIENT_HUDUPDATE = 9,
CLIENT_DISPATCHUSERMESSAGE = 34,
CLIENT_CREATEMOVE = 19,
};
// Hooked function prototypes
typedef void (__thiscall* CreateMoveFn)( void* thisptr, int sequence_number, float input_sample_frametime, bool active );
typedef void (__thiscall* HudUpdateFn)( void* thisptr, bool active );
typedef bool (__thiscall* DispatchUserMessageFn)( void* thisptr, int msg_type, bf_read& msg_data );
// Hook Manager (just a small example)
class CHookMgr
{
public:
// Initializing
void Init();
void Close();
// Hooks in pClient
static void __fastcall Hooked_CreateMove( void* thisptr, int edx, int sequence_number, float input_sample_frametime, bool active );
static void __fastcall Hooked_HudUpdate( void* thisptr, int edx, bool active );
static bool __fastcall Hooked_DispatchUserMessage( void* thisptr, int edx, int msg_type, bf_read& msg_data );
// Stored here for easy access
typedef toolkit::VMTManager hook_t;
hook_t* pClientHook;
};
extern CHookMgr* HookMgr;
// Source file
#include "hookmgr.h"
CHookMgr* HookMgr = NULL;
void CHookMgr::Init()
{
pClientHook = NULL;
if ( Interfaces->pClient )
{
static hook_t hook( Interfaces->pClient );
pClientHook = &hook;
hook.HookMethod( &Hooked_HudUpdate, CLIENT_HUDUPDATE );
hook.HookMethod( &Hooked_CreateMove, CLIENT_CREATEMOVE );
hook.HookMethod( &Hooked_DispatchUserMessage, CLIENT_DISPATCHUSERMESSAGE );
}
// Etc for other interfaces...
}
void CHookMgr::Close()
{
// Because the hook was a local static member it'll keep on existing until our .dll is effectively unloaded.
// Hence why I call ->Poof() to ensure that once we unload, it won't attempt to access it.
if ( pClientHook ) { pClientHook->Unhook(); pClientHook->Poof(); }
// Etc...
}
// Hooked functions abusing the __fastcall calling convention to grab the 'this' pointer without inline ASM.
// Do not worry about saving registers as microsoft's compiler will do that for us.
void __fastcall CHookMgr::Hooked_HudUpdate( void* thisptr, int edx, bool active )
{
HookMgr->pClientHook->GetMethod<HudUpdateFn>( CLIENT_HUDUPDATE )( thisptr, active );
}
void __fastcall CHookMgr::Hooked_CreateMove( void* thisptr, int edx, int sequence_number, float input_sample_frametime, bool active )
{
HookMgr->pClientHook->GetMethod<CreateMoveFn>( CLIENT_CREATEMOVE )( thisptr, sequence_number, input_sample_frametime, active );
}
bool __fastcall CHookMgr::Hooked_DispatchUserMessage( void* thisptr, int edx, int msg_type, bf_read& msg_data )
{
return HookMgr->pClientHook->GetMethod<DispatchUserMessageFn>( CLIENT_DISPATCHUSERMESSAGE )( thisptr, msg_type, msg_data );
}
[/php]
Use case 2: Hooking virtual functions of dynamically created instances (like entitites).
Use the DynManager<type,guard> class.
A nice side effect is that this method allows us to attach extra bits of data we may want to use.
This class exploits the idea that you can add some more entries to the vtable to hold whatever extra data you might want. Let's take a look at how that would work:
Code:
v Actual vtable comes here ->
+----------+----------+----------+----------+----------+--------
| hook ptr | guard | RTTI ptr | vfunc #1 | vfunc #2 | ...
+----------+----------+----------+----------+----------+--------
hook ptr: A pointer back to the hook object
guard: Allows us to detect if this instance has been hooked or not.
RTTI ptr: Let's not forget about this guy.
Extending the VMTManager class it adds some extra functionality. Important however is that, should you Unhook() the instance there's no way we can figure out if the instance was hooked! Hence Unhook() and Rehook() are disabled.
Methods:
DynManager<T,S>:
ynManager( void* inst, size_t offset = 0, uint vfuncs = 0 );
Same as VMTManager.
void DynManager<T,S>::Unhook();
void DynManager<T,S>::Rehook();
Shouldn't be used. While you can bypass this restriction I still highly discourage it.
T* DynManager<T,S>::UserData() const;
void DynManager<T,S>::UserData( T* data );
Retrieve or set the UserData pointer you've associated with this instance.
static bool DynManager<T,S>::HookPresent( void* inst, size_t offset = 0 );
Checks if the instance is already hooked,
offset is the offset of the vtable in the instance.
static DynManager<T,S>* DynManager<T,S>::GetHook( void* inst, size_t offset = 0 );
Gets the hook object for an instance, returns NULL if there isn't.
static DynManager<T,S>* DynManager<T,S>::GetOrCreateHook( void* inst, size_t offset = 0, size_t vfuncs = 0 );
Gets the hook object for an instance, creates a new one if there isn't already one. I consider this one deprecated.
How I use it in my hack:
entdata_t is a struct which contains some extra data I wish to use with the entity. This can be anything you want, I use it for some resident aimbot vars and others.
At any point you can request the entdata_t for an entity with this:
entdata_t* edata = EntData->GetOrCreateData( pEnt );
Since entities in Source Engine use multiple inheritance I need more than 1 hook object to hook it. The destructor in IClientEntity needs to be hooked as well as I want to hook DrawModel in IClientRenderable. Instead of specifying the vtable offset I let the compiler figure that out by simply casting it.
These 2 hook objects are hook_client and hook_render, both their UserData points back to the entdata_t* object. This is how GetOrCreateData gets back at it.
A simplified overview:
[php]
// Declarations
struct entdata_t;
typedef toolkit:

ynManager<entdata_t,0x08192A3B> enthook_t;
// Manages entity data
class CEntMgr
{
public:
void Init();
void Close();
// Get the data for an entity
entdata_t* GetOrCreateData( IClientEntity* pEnt );
// Hooked destructor cleans up
static void __fastcall Hooked_Destructor( IClientEntity* thisptr, int edx, unsigned int flags );
private:
typedef std::set<entdata_t*> mgr_t;
mgr_t _datamgr;
};
extern CEntMgr* EntData;
// Data attached to entities
struct entdata_t
{
entdata_t( IClientEntity* pEnt );
// Entity we're attached to
IClientEntity* pEnt;
// The hooks
enthook_t hook_client;
enthook_t hook_render;
// Data members
float spawntime;
// Velocity
bool calcvel;
Vector velocity;
Vector accel;
// Aimbot vars
float priority;
float viewfov;
float distance;
// etc...
};
// Source file
#include "entdata.h"
CEntMgr* EntData = NULL;
void CEntMgr::Close()
{
// Clean up as there might be some hooks remaining when we're unloaded
mgr_t::iterator end = _datamgr.end();
for ( mgr_t::iterator it = _datamgr.begin(); it!=end; ++it )
{
// Cleanup the entdata_t
delete (*it);
}
_datamgr.clear();
}
entdata_t* CEntMgr::GetOrCreateData( IClientEntity* pEnt )
{
// Get the hook
enthook_t* pHook = enthook_t::GetHook( pEnt );
if ( !pHook )
{
entdata_t* edata = new entdata_t( pEnt );
// Hook the destructor to allow us to clean up
edata->hook_client.HookMethod( &Hooked_Destructor, 0 );
// Register this edata in the manager
_datamgr.insert( edata );
return edata;
}
return pHook->UserData();
}
void __fastcall CEntMgr::Hooked_Destructor( IClientEntity* thisptr, int edx, unsigned int flags )
{
// This hook would not be called if it wasn't hooked. So GetHook() will always return something.
entdata_t* edata = enthook_t::GetHook( thisptr )->UserData();
// Remove it
EntData->_datamgr.remove( edata );
delete edata;
// Hook is now deleted, call original destructor
toolkit::getvfunc<void (__thiscall*)( IClientEntity*, unsigned int )>( thisptr, 0 )( thisptr, flags );
}
entdata_t::entdata_t( IClientEntity* pEnt ) : hook_client( pEnt ), hook_render( static_cast<IClientRenderable*>( pEnt ) ),
spawntime( Interfaces->pGlobals->curtime ), calcvel( IsPlayer( pEnt ) )
{
this->pEnt = pEnt;
// Allow is to connect back to this data
hook_client.UserData( this );
hook_render.UserData( this );
}
[/php]
Use case 3: Hooking vtables directly.
Use the VMTHook class.
Every instance using the hooked vtable will have its functions hooked.
This means only instances of the EXACT type you're hooking, derived or base classes won't be hooked.
Methods:
VMTHook::VMTHook( void* inst, size_t offset = 0, uint vfuncs = 0 );
Hook the vtable of this instance.
offset and
vfuncs are again optional.
[u]VMTHook::VMTHook( void** vmt, uint vfuncs = 0 );[/i]
Hook the given vtable.
VMTHook::~VMTHook();
Destructor
void VMTHook::HookMethod( void* new_func, size_t index );
Hooks a function by index.
void VMTHook::UnhookMethod( size_t index );
Unhooks a function by index.
void VMTHook::Unhook();
Unhook the vtable. This requires the vtable to by physically swapped with a backup.
void VMTHook::Rehook();
Rehook the vtable.
bool VMTHook::Hooked();
Returns if the hooks are currently active.
template< typename Fn > Fn VMTHook::GetMethod( size_t index ) const;
Get the original function.
Use a function prototype for the template argument to make it very easy to call this function.
Example syntax: hook.GetMethod<bool (__thiscall*)( void*, int )>( 0x5 )( inst, arg );
Idk, just use it the same as you would VMTManager. I don't personally use it due to its detectability but I wanted to include it for the sake of completeness anyway.
Utility funcs
uint CountFuncs( void** pVMT );
Queries memory using VirtualQuery and as long as vfunc pointers land in PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE memory it keeps counting.
This is more accurate than my previous method of using module bounds (which would fail if the vtable was already hooked).
uint CountFuncs( void* begin, void* end, void** pVMT );
Simply count funcs as long as the vfunc ptr lies in [begin;end[
int FindFunc( void** pVMT, void* pFunc, uint vfuncs = 0 );
Simply iterates trough the vfuncs and returns the index of pFunc. Leave vfuncs 0 if you want it to count the number of vfuncs itself.
Conclusion
While chatting with Xeno he suggested to use a 'database' thingy and dynamically retrieve the indices to make your hack more resistent to updates.
I think this is everything you can possibly do with vmt hooking. Have fun!