Need help?Hooking the unreal engine - MPGH - MultiPlayer Game Hacking & Cheats
Hooking the unreal engine
Posts 1–5 of 5 · Page 1 of 1
Hooking the unreal engine
Now onto some examples of how to hook into the unreal engine using vtables. I will be using the vmthooks library written by Casual_Hacker over at Game/Deception to abstract the whole hooking process. If you wish to adapt your own feel free to do so.
You will also need an SDK for the game you are hacking (uNrEaL has released alot for the unreal engine).
First we would create a new DLL. The OnAttach function is what will be called when you inject your DLL into the game.
Basic stuff really. Then we will need to declare some global variables and includes that we will be using later one. You can do this in a separate header if you like, or not.
Code:
#pragma once //Header guard
#include <windows.h> // duh
#include "vmthooks.h" //vmt hooking class
#include "SDKheaders.h" //SDK headers
toolkit::VMTHook* hook; //Pointer to the VMTHook class
typedef void (__stdcall *ProcessEvent ) ( UFunction*, void*, void* ); //typedef for the processevent function pointer
ProcessEvent pProcessEvent = NULL; //pointer to original processevent
UFunction *pUFunc = NULL; //pointers to processevent arguements
void *pParms = NULL;
void *pResult = NULL;
If you aren't sure what these will be used for, I will get to that later. Next you will need to start by implementing the OnAttach function and the first thing you need to do is find an instance of the object you want to hook. Good classes to hook for the unreal engine are the "HUD class", the "Viewport class" and the "PlayerController". In my example I'll be hooking the viewport class, as it allows engine drawing in postrender.
Code:
UObject* viewport = getViewport(); //get a pointer to the viewport
hook = new toolkit::VMTHook(viewport); //hook object
pProcessEvent = hook->GetMethod<tProcessEvent>(60); //save the orginal funtion in global variable
hook->HookMethod(&ProcessEventHooked, 60); //replace the orginal function with the hooked function
I'll leave it up to you to find the viewport, it shouldn't be hard and there are many ways to do it. (tip: use the object dump (GObjects). Next we hook the object using the VMThook class. This will allow you to manipulate teh vtable. Make sure you store a pointer to the old pProcesEvent. Its at index 60 for APB, not sure about other unreal games.
Then proceed by replacing the Vtable with a copy( Don't modify the orginal or your hack will be much easier to detect) and now replace the pointer at the proper index with a pointer to your own hooked function, which we still need to make.
Code:
void __declspec(naked) ProcessEventHooked ()
{
__asm mov pCallObject, ecx; //get caller from ecx register and save it in pCallObject
__asm
{
push eax
mov eax, dword ptr [esp + 0x8]
mov pUFunc, eax
mov eax, dword ptr [esp + 0xC]
mov pParms, eax
mov eax, dword ptr [esp + 0x10]
mov pResult, eax
pop eax
} // Manually get the proper parameters for the function
__asm pushad //Save registers on stack
//Do stuff here!
__asm popad //restore registers from stack
__asm
{
push pResult
push pParms
push pUFunc
call pProcessEvent
retn 0xC
} //put parameters on stack and call the orginal function
}
This is an empty template for the a hooked processevent function (thanks lowHertz!). I'll try to explain briefly what goes on here (as far as my understanding of it goes).
__declspec(naked) is a calling convention that basicly leaves it to the programmer to prepare and clean the stack for the function. Don't ask me for details, I basicly c&p this from lowHertz :P.
With the first line we take a pointer from the ECX register, this pointer points to the object that called the function. Since in my method we already have a pointer to the correct object this is not really necessary.
The block that follows is asm for putting the arguments (that the orginal function got called with) into the global variables you declared earlier. You also need these to call the orginal function later.
pushad saves the registers on the stack. So now you can execute whatever code you want and afterwards it restores the registers to their orginal state by popping them off the stack with popad.
The last bit pushes the arguments onto the stack and calls the orginal function, which you stored earlier. If all is well then the orginal function executes properly and the game resumes as it would usually.
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;
};
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.
// 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 );
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!