The easiest way is to duplicate the VTable of an interface, exchange the function pointer of a function you want to hook with a function pointer to your own function and exchange the VTable pointer of the interface.
To write in the console of the game, you can do it through VTable functions or just get the exported functions in tier0.dll.
Tier0.dll has multiple functions to write to the console, for example Warning, DevWarning etc..
To output a usual white text ( same as the echo command ) use the exported function ConMsg. In order to get the exported function via GetProcAddress, you have to take the mangled name.
Code:
Globals::Functions::ConMsg = reinterpret_cast< Globals::Prototypes::ConMsg_ >( GetProcAddress( GetModuleHandle( "tier0.dll" ), "?ConMsg@@YAXPBDZZ" ) );
does the job.
ConMsg_ is a prototype:
Code:
typedef void* ( *ConMsg_ )( char*, ... );
You can use this function just like printf.
Code:
Globals::Functions::ConMsg( "IBaseClientDll: %X\n", Globals::Interfaces::BaseClientDll );
Globals::Functions::ConMsg( "IClientEntityList: %X\n", Globals::Interfaces::EntityList );
Globals::Functions::ConMsg( "IClientMode: %X\n", Globals::Interfaces::ClientMode );
Globals::Functions::ConMsg( "IVEngineClient: %X\n", Globals::Interfaces::EngineClient );
You don't need the mangled name for the exported function Warning.
Code:
Globals::Functions::Warning = reinterpret_cast< Globals::Warning_ >( GetProcAddress( GetModuleHandle( "tier0.dll" ), "Warning" ) );
Code:
typedef void* ( *Warning_ )( char*, ... );
Code:
Globals::Functions::Warning( "MA SUPER COPY PASTA\n" );
To know whether you need the mangled name or not you can just click on the exported function in IDA and see if the mangled name is present.
https://img.marcel-kirchhoff.de/2016-04-24_12-55-53.png
https://img.marcel-kirchhoff.de/2016-04-24_12-56-18.png