This is what I am using at the moment...It is linux only as I got frightened by how much work I would have to do scanning for sigs in pe's D: This method is signature free, as it seemed unnecessary for a linux only implementation. I've already taken a lot of abuse for this being linux only, so need need to tell me again!
To find the functions, I take several steps, firstly:
1) Identify the function and it's context within the Source SDK. Check the proto etc.
2) Find the symbols in the server object file. For example, CBE Teleport:
Code:
[robot@medic bin]$ cd ~/srcds_l/cstrike/bin/
[robot@medic bin]$ nm -n server_i486.so | c++filt | grep CBaseEntity::Teleport
00616f80 T CBaseEntity::Teleport(Vector const*, QAngle const*, Vector const*)
3) Now the mangled name:
Code:
[robot@medic bin]$ nm -n server_i486.so | grep 00616f80
00616f80 T _ZN11CBaseEntity8TeleportEPK6VectorPK6QAngleS2_
4) In the plugin code, declare the prototype:
Code:
#define VFUNC
#define HDEFVFUNC( funcname , returntype , proto ) \
typedef returntype ( VFUNC * funcname##_Function ) proto ; \
extern funcname##_Function funcname ;
#define DEFVFUNC( funcname , returntype , proto ) \
funcname##_Function funcname = NULL;
#define DEFVFUNC_( funcname , returntype , proto ) \
HDEFVFUNC(funcname, returntype, proto); \
DEFVFUNC(funcname, returntype, proto)
// forward decl
class CBaseEntity;
class CBasePlayer;
class CBaseAnimating;
DEFVFUNC_(Teleport, void, (CBaseEntity *, Vector *, QAngle *, Vector *));
DEFVFUNC_(Ignite, void, (CBaseEntity *, float, bool));
DEFVFUNC_(SetModel, void, (CBaseAnimating *, char const *));
DEFVFUNC_(CommitSuicide, void, (CBasePlayer *));
5) Get the function pointer from the symbol table and assign the function:
Code:
#define LINKVFUNC( symbol, func ) \
sym_addr = dlsym(handle, symbol); \
if (sym_addr == NULL) Msg("dlsym failure : Error [%s]\n", dlerror()); \
else func = (func##_Function) sym_addr;
void *handle;
void *sym_addr;
handle = dlopen("./cstrike/bin/server_i486.so", RTLD_NOW);
if (handle == NULL)
{
Msg("Failed to open server image, error [%s]\n", dlerror());
}
else
{
Msg("server_i486.so = [%p]\n", handle);
// virtual functions
LINKVFUNC( "_ZN11CBaseEntity8TeleportEPK6VectorPK6QAngleS2_", Teleport )
LINKVFUNC( "_ZN14CBaseAnimating6IgniteEfbfb", Ignite )
LINKVFUNC( "_ZN14CBaseAnimating8SetModelEPKc", SetModel )
LINKVFUNC( "_ZN11CBasePlayer13CommitSuicideEv", CommitSuicide )
Msg( "linked vfuncs...\n" );
dlclose(handle);
6) Then call the functions to your hearts content i.e.
Code:
void CZHPlugin::Burninate(CPluginPlayer *player, float flTime)
{
Ignite(player->GetBaseEnt(), flTime, false);
}
I do wonder if these are safer for linux, since they only rely on the return types and parameters being the same (highly likely)?? It is also trivial to get at the vtables and iterate through it, comparing your function pointers from the symbol table to the the addresses in the vtable for a particular class. This gives you a way to dynamically scan for vtable indexes, which makes for easier hooking ie
1) Get the unmangled symbol:
Code:
[robot@medic bin]$ nm -n server_i486.so | c++filt | grep CBaseEntity | grep vtable
....blah
....blah
00988520 V vtable for CBaseEntity <-- we are interested in this one!
....
....
....
2) Now the mangled name:
Code:
[robot@medic bin]$ nm -n server_i486.so | grep 00988520
00988520 V _ZTV11CBaseEntity
3) Getting vtables symbol names like that allows you to get indexes for stuff...
Code:
<--snip-->
// virtual tables
void *vtblCBaseEntity = NULL;
void *vtblCBaseCombatCharacter = NULL;
void *vtblCCSPlayer = NULL;
// virtual function table indice
unsigned long idxTraceAttack = NULL;
unsigned long idxEvent_Killed = NULL;
unsigned long idxWeapon_Switch = NULL;
<--snip-->
<--snip-->
#define ADDRTYPE unsigned long
#define LINKVTABLE( symbol, table ) \
table = dlsym(handle, symbol); \
if (table == NULL) Msg("dlsym failure : Error [%s]\n", dlerror());
#define SCANVFUNC( index, table, func ) \
for ( index = 0; index < 256; index++) { \
void * vfnaddr = *((ADDRTYPE *)table + index); \
if (vfnaddr == func) { \
index -= 2; \
Msg("ZH: Got Index %d | table %p | func %p\n", index, table, func); \
break; } }
<--snip-->
<--snip-->
// virtual function tables
LINKVTABLE( "_ZTV11CBaseEntity", vtblCBaseEntity )
LINKVTABLE( "_ZTV20CBaseCombatCharacter", vtblCBaseCombatCharacter )
LINKVTABLE( "_ZTV9CCSPlayer", vtblCCSPlayer )
SCANVFUNC( idxTraceAttack, vtblCCSPlayer, TraceAttack )
SCANVFUNC( idxEvent_Killed, vtblCBaseCombatCharacter, Event_Killed )
SCANVFUNC( idxWeapon_Switch, vtblCBaseCombatCharacter, Weapon_Switch )
<--snip-->
btw, i am working on moving my plugin over to source:mm so sorry if some of this is irrelevant code
Thanks vancelorgin, bailopan, xad and mani and co!
robot