*Windows only* - I'm switching to linux dev, so this is sorta a fair well to the win32 thiscall convention. Yet a fuckin gain, not for noobs, but don't be intimidated by its ugliness - it's perfectly safe, if used right
Wanna hook stuff? Well, back in hl1, all you needed to do was wrap the server dll and you could act as a proxy between the mod and the engine, as all the funcs were simple exports and struct memebers. Not anymore - valve has moved to interfaces. Understandable - much more flexible, faster, overall better. Sadly, hooking is a bit tougher. Lucky for you I've put together a little toolkit for doing so.
A little, tiny primer on what virtual funcs are. Lets take IVEngineServer engine for example. Now, the first thing inside engine is a pointer to a table full of addresses. These addresses are the addrs of the virtual funcs of that class. For example the 43rd address in IVEngineServer's virtual function table is a pointer to UserMessageBegin.
So lets say you want to hook all messages being sent from the server to clients. How can you do this? Well, first, slap this somewhere into your project:
Code:
class CCallGate {
public:
CCallGate(){
m_ulOrigFunc = 0;
m_ulPreCall = 0;
m_ulPostCall = 0;
m_ulOldRet = 0;
m_ulThis = 0;
m_ulOldESP = 0;
m_ulNewESP = 0;
}
CCallGate(void* pOrigFunc, void* pPreCall, void* pPostCall){
CCallGate();
BuildGate(pOrigFunc, pPreCall, pPostCall);
}
void Build(void* pOrigFunc, void* pPreCall, void* pPostCall){
BuildGate(pOrigFunc, pPreCall, pPostCall);
}
void* Gate(){
return (void*)m_szGate;
}
unsigned long RetAdd(){
return m_ulOldRet;
}
private:
void BuildGate(void* pOrigFunc, void* pPreCall, void* pPostCall){
m_ulOrigFunc = (unsigned long)pOrigFunc;
m_ulPreCall = (unsigned long)pPreCall;
m_ulPostCall = (unsigned long)pPostCall;
char* pCur = m_szGate;
pCur[0] = '\x8F'; pCur[1] = '\x05'; pCur += 2; //pop m32
*(unsigned long*)pCur = (unsigned long)&m_ulOldRet; pCur += 4;
pCur[0] = '\x89'; pCur[1] = '\x0D'; pCur += 2; //mov m32, ecx
*(unsigned long*)pCur = (unsigned long)&m_ulThis; pCur += 4;
if(m_ulPostCall != NULL){
pCur[0] = '\x89'; pCur[1] = '\x25'; pCur += 2; //mov m32, esp
*(unsigned long*)pCur = (unsigned long)&m_ulOldESP; pCur += 4;
}
if(m_ulPreCall != NULL){
*pCur = '\x51'; pCur++; //push ecx
*pCur = '\xA1'; pCur++; //mov eax, m32
*(unsigned long*)pCur = (unsigned long)&m_ulPreCall; pCur += 4;
pCur[0] = '\xFF'; pCur[1] = '\xD0'; pCur += 2; //call eax
}else{
pCur[0] = '\x83'; pCur[1] = '\xEC'; pCur[2] = '\x04'; pCur+= 3; //sub esp, 0x04
}
pCur[0] = '\x8B'; pCur[1] = '\x0D'; pCur += 2; //mov ecx, m32
*(unsigned long*)pCur = (unsigned long)&m_ulThis; pCur += 4;
*pCur = '\xB8'; pCur++; //mov eax, u32 this mov push ret
*(unsigned long*)pCur = (unsigned long)pCur + 4 + 3 + 5 + 1; pCur += 4;
pCur[0] = '\x89'; pCur[1] = '\x04'; pCur[2] = '\x24'; pCur += 3; //mov [esp], eax
*pCur = '\x68'; pCur++; //push u32
*(unsigned long*)pCur = m_ulOrigFunc; pCur += 4;
*pCur = '\xC3'; pCur++; //ret
if(m_ulPostCall != NULL){
pCur[0] = '\x89'; pCur[1] = '\x25'; pCur += 2; //mov m32, esp
*(unsigned long*)pCur = (unsigned long)&m_ulNewESP; pCur += 4;
pCur[0] = '\x8B'; pCur[1] = '\x25'; pCur += 2; //mov esp, m32
*(unsigned long*)pCur = (unsigned long)&m_ulOldESP; pCur += 4;
pCur[0] = '\xFF'; pCur[1] = '\x35'; pCur += 2; //push m32
*(unsigned long*)pCur = (unsigned long)&m_ulThis; pCur += 4;
*pCur = '\x50'; pCur++; //push eax
*pCur = '\xA1'; pCur++; //mov eax, m32
*(unsigned long*)pCur = (unsigned long)&m_ulPostCall; pCur += 4;
pCur[0] = '\xFF'; pCur[1] = '\xD0'; pCur += 2; //call eax
pCur[0] = '\x8B'; pCur[1] = '\x25'; pCur += 2; //mov esp, m32
*(unsigned long*)pCur = (unsigned long)&m_ulNewESP; pCur += 4;
}
pCur[0] = '\xFF'; pCur[1] = '\x35'; pCur += 2; //push m32
*(unsigned long*)pCur = (unsigned long)&m_ulOldRet; pCur += 4;
*pCur = '\xC3'; pCur++; //ret
}
char m_szGate[0x60];
unsigned long m_ulOrigFunc;
unsigned long m_ulPreCall;
unsigned long m_ulPostCall;
unsigned long m_ulOldRet;
unsigned long m_ulThis;
unsigned long m_ulOldESP;
unsigned long m_ulNewESP;
};
inline bool DeProtect(void* pMemory, unsigned int uiLen){
#ifndef __LINUX__
DWORD dwIDontCare;
return (VirtualProtect(pMemory, uiLen, PAGE_EXECUTE_READWRITE, &dwIDontCare) ? true : false);
#else
#error jackass
return (!mprotect(pMemory, uiLen, PROT_READ | PROT_WRITE | PROT_EXEC) ? true : false);
#endif
}
#define VTBL( classptr ) (*(DWORD*)classptr)
#define PVFN_( classptr , offset ) (VTBL( classptr ) + offset)
#define VFN_( classptr , offset ) *(DWORD*)PVFN_( classptr , offset )
#define PVFN( classptr , offset ) PVFN_( classptr , ( offset * sizeof(void*) ) )
#define VFN( classptr , offset ) VFN_( classptr , ( offset * sizeof(void*) ) )
Yich. What's all that? Well, it's a class that assembles a gate function for virtual functions that will call your functions before and/or after the original function is called. Wtf, you may ask? Basically, it handles the ugly crap required for you to hook virtual class funcs. The format for the callbacks are:
Code:
void __cdecl PreFunc(classtype* pclassinst, <args>){ }
returntype __cdecl PreFunc(returntype retval, classtype* pclassinst, <args>){ return retval; }
Back to our example:
Code:
CCallGate GateEngine_UserMessageBegin; //gates
CCallGate GateEngine_MessageEnd;
...
bf_write* bfw = NULL;
int imsgtype = 0;
//the funcs to call
bf_write* PostUserMessageBegin(bf_write* pBitBuf, IVEngineServer* pEngine, IRecipientFilter *filter, int msg_type){
bfw = pBitBuf;
imsgtype = msg_type;
return pBitBuf;
}
void PostMessageEnd(void* pNothing, IVEngineServer* pEngine){
if(!bfw)
return;
bf_read r(bfw->GetData(), bfw->GetNumBytesWritten());
//TODO: anything..
bfw = NULL;
}
...
Load:
DeProtect((void*)VTBL(engine), 1024); //deprotect the virtual function table, a length of some abritrary ass amount
DWORD* pUserMessageBegin = (DWORD*)PVFN(engine, 43); //get a pointer to usermessagebegin
DWORD* pMessageEnd = (DWORD*)PVFN(engine, 44); //get a pointer to messageend
GateEngine_UserMessageBegin.Build((void*)*pUserMessageBegin, NULL, PostUserMessageBegin); //build the gate for pUserMessageBegin with no precallback and PostUserMessageBegin as a postcallback
GateEngine_MessageEnd.Build((void*)*pMessageEnd, NULL, PostMessageEnd); // /s/UserMessageBegin/MessageEnd
*pUserMessageBegin = (DWORD)GateEngine_UserMessageBegin.Gate(); //set engine's vtable's pUserMessageBegin entry to the address of your newly constructed gate
*pMessageEnd = (DWORD)GateEngine_MessageEnd.Gate(); //restart the computer!one
Voila. Now your funcs are called every time *ANYTHING* calls engine->BeginUserMessage - beit server.dll, some plugin,
your plugin. How'd I get the addr's of Begin/End message? Well, I'm lazy, so I put engine->UserMessageBegin and MessageEnd in my load func, _asm int 03'd, and loaded a debugger. But you could really just count down the interface declaration, the first virtual func is 0, second is 1, third is 2, and I think you can guess the rest. Some other nifty funcs are 21 = CreateEdict and 22 = RemoveEdict. Please note that this works on absolutly any class with virtual funcs in it, including, for example, CBaseEntity. Go ahead, hook think. You know you want to.
__________________