//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// const char GAMENAME[21] = "MODNAME - DOD:S"; const char WELCOME[64] = "MODNAME - Type 'cof_help' for options"; const char VERSION[10] = "2.11"; #include #include #ifdef _WIN32 #include #else #define MAX_FILE_PATH 64 #include #include #include #include #include #include int PAGESIZE = 4096; //Helpful macros for mprotect #define SH_LALIGN(x) (void*)((intptr_t)(x) & ~(PAGESIZE-1)) #define SH_LALDIF(x) ((intptr_t)(x) & (PAGESIZE-1)) #endif #include "convar.h" #include "interface.h" #include "eiface.h" #include "dlls/iplayerinfo.h" struct sig { BYTE* bData; char* cMask; DWORD loc; }; IVEngineServer *engine = NULL; IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players // function to initialize any cvars/command in this plugin void InitCVars( CreateInterfaceFn cvarFactory ); short plugStatus; BYTE origCode[30]; sig shotCode; sig retCode; sig gameName; void* baseAddrServ; DWORD servSize; void* baseAddrSelf; int errNum; void writeGameName(bool restore = false); bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask); DWORD dwFindPattern( DWORD dwAddress, DWORD dwLen, BYTE* bMask, char* szMask); bool WriteMemory( DWORD address, BYTE* data, int size ); void ReadMemory( DWORD address, BYTE* buffer, int size ); bool getBaseAddrs(void* servAddr, void* selfAddr); void removeCof(); bool addCof(); // useful helper func inline bool FStrEq(const char *sz1, const char *sz2) { return(Q_stricmp(sz1, sz2) == 0); } inline float randfloat() { return rand()/(float(RAND_MAX) + 1); } float myRand() { return (randfloat() - 0.5f); } #ifdef _WIN32 void _declspec(naked) myCOF() { __asm{ call myRand call myRand faddp st(1),st fstp dword ptr [esp+10h] call myRand call myRand faddp st(1),st fstp dword ptr [esp+14h] mov ecx, 0xAAAAAAAA jmp ecx } } #else void myCOF() { asm volatile ( "popl %ebp "); asm volatile ( "call _Z6myRandv " ); asm volatile ( "call _Z6myRandv " ); asm volatile ( "faddp %st(1),%st " ); asm volatile ( "fstps 32(%esp) " ); asm volatile ( "call _Z6myRandv " ); asm volatile ( "call _Z6myRandv " ); asm volatile ( "faddp %st(1),%st " ); asm volatile ( "fstps 36(%esp) " ); asm volatile ( "movl $0xAAAAAAAA, %ecx " ); asm volatile ( "jmp %ecx " ); } #endif //--------------------------------------------------------------------------------- // Purpose: a sample 3rd party plugin class //--------------------------------------------------------------------------------- class CPrododPlugin: public IServerPluginCallbacks { public: CPrododPlugin(); ~CPrododPlugin(); // IServerPluginCallbacks methods virtual bool Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory ); virtual void Unload( void ); virtual void Pause( void ) {} virtual void UnPause( void ) {} virtual const char *GetPluginDescription( void ); virtual void LevelInit( char const *pMapName ) {} virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) {} virtual void GameFrame( bool simulating ) {} virtual void LevelShutdown( void ) {} virtual void ClientActive( edict_t *pEntity ); virtual void ClientDisconnect( edict_t *pEntity ) {} virtual void ClientPutInServer( edict_t *pEntity, char const *playername ) {} virtual void SetCommandClient( int index ); virtual void ClientSettingsChanged( edict_t *pEdict ) {} virtual PLUGIN_RESULT ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) {return PLUGIN_CONTINUE;} virtual PLUGIN_RESULT ClientCommand( edict_t *pEntity ); virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID ) {return PLUGIN_CONTINUE;} virtual int GetCommandIndex() { return m_iClientCommandIndex; } private: int m_iClientCommandIndex; }; // // The plugin is a static singleton that is exported as an interface // CPrododPlugin g_PrododPlugin; EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPrododPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_PrododPlugin ); //--------------------------------------------------------------------------------- // Purpose: constructor/destructor //--------------------------------------------------------------------------------- CPrododPlugin::CPrododPlugin() { m_iClientCommandIndex = 0; } CPrododPlugin::~CPrododPlugin() { } //--------------------------------------------------------------------------------- // Purpose: called when the plugin is loaded, load the interface we need from the engine //--------------------------------------------------------------------------------- bool CPrododPlugin::Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory ) { srand(static_cast(time(NULL))); helpers = (IServerPluginHelpers*)interfaceFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL); engine = (IVEngineServer*)interfaceFactory(INTERFACEVERSION_VENGINESERVER, NULL); playerinfomanager = (IPlayerInfoManager *)gameServerFactory(INTERFACEVERSION_PLAYERINFOMANAGER,NULL); if( !getBaseAddrs(playerinfomanager, &myCOF) ){ errNum = 1; return false; } #ifdef _WIN32 shotCode.bData = (BYTE*)"\x8B\x0D\x20\x74\x54\x22\x8B\x01\x68\x00\x00\x80\x3F\x68\x00\x00\x80\xBF\xFF\x50\x04\xD9\x5C\x24\x10\x8B\x0D\x20\x74\x54\x22\x8B\x11\x68\x00\x00\x80\x3F\x68\x00\x00\x80\xBF\xFF\x52\x04\xD9\x54\x24\x14\xD8\x4C\x24\x14"; shotCode.cMask = "xx??????xxxxxxxxxx???????????????xxxxxxxxxxx??x???x???"; shotCode.loc = 0; retCode.bData = (BYTE*)"\x8D\x44\x24\x34\x50\x8D\x4C\x24\x44\x51\x8D\x54\x24\x54\x52\x53\xE8\x6E\x8C\x02\x00"; retCode.cMask = "xx???xx???xx????x????"; retCode.loc = 0; #else shotCode.bData = (BYTE*)"\x89\x14\x24\xC7\x44\x24\x08\x00\x00\x80\x3F\xC7\x44\x24\x04\x00\x00\x80\xBF\xFF\x51\x04\xA1\x94\x5D\xBD\x00\x8B\x28\xD9\x5C\x24\x24\xC7\x44\x24\x08\x00\x00\x80\x3F\xC7\x44\x24\x04\x00\x00\x80\xBF"; shotCode.cMask = "x??x???xxxxx???xxxx??????????????x???xxxxx???xxxx"; shotCode.loc = 0; retCode.bData = (BYTE*)"\x8B\x4C\x24\x28\x8D\x84\x24\x00\x01\x00\x00\x8D\x9C\x24\x10\x01\x00\x00\x89\x44\x24\x0C\x8D\xAC"; retCode.cMask = "x???x??????x??????x???x?"; retCode.loc = 0; #endif gameName.bData = (BYTE*)"Day of Defeat: Source"; gameName.cMask = "xxxxxxxxxxxxxxxxxxxxx"; InitCVars( interfaceFactory ); // register any cvars we have defined return true; } //--------------------------------------------------------------------------------- // Purpose: called when the plugin is unloaded (turned off) //--------------------------------------------------------------------------------- void CPrododPlugin::Unload( void ) { removeCof(); } //--------------------------------------------------------------------------------- // Purpose: the name of this plugin, returned in "plugin_print" command //--------------------------------------------------------------------------------- const char *CPrododPlugin::GetPluginDescription( void ) { return "Cof Plugin, Beta, by Prodod team"; } //--------------------------------------------------------------------------------- // Purpose: called when a client spawns into a server (i.e as they begin to play) //--------------------------------------------------------------------------------- void CPrododPlugin::ClientActive( edict_t *pEntity ) { KeyValues *kv = new KeyValues( "msg" ); kv->SetString( "title", WELCOME ); kv->SetString( "msg", "cof plugin" ); kv->SetColor( "color", Color( 255, 0, 0, 255 )); kv->SetInt( "level", 5); kv->SetInt( "time", 20); helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this ); kv->deleteThis(); } //--------------------------------------------------------------------------------- // Purpose: called on level start //--------------------------------------------------------------------------------- void CPrododPlugin::SetCommandClient( int index ) { m_iClientCommandIndex = index; } PLUGIN_RESULT CPrododPlugin::ClientCommand( edict_t *pEntity ) { const char *pcmd = engine->Cmd_Argv(0); if ( FStrEq( pcmd, "cof_status" ) ) { if( plugStatus ){ engine->ClientCommand( pEntity, "echo The COF is center weighted" ); }else{ engine->ClientCommand( pEntity, "echo The COF is default" ); } return PLUGIN_STOP; // we handled this function }else if( FStrEq( pcmd, "cof_version" ) ) { engine->ClientCommand( pEntity, "echo COF Plugin version: %s", VERSION ); return PLUGIN_STOP; // we handled this function }else if( FStrEq( pcmd, "cof_help" ) ) { engine->ClientCommand( pEntity, "echo" ); engine->ClientCommand( pEntity, "echo Cone of Fire Plugin" ); engine->ClientCommand( pEntity, "echo --------------------" ); engine->ClientCommand( pEntity, "echo cof_status - status of cof in the server" ); engine->ClientCommand( pEntity, "echo cof_version - version of cof plugin installed" ); engine->ClientCommand( pEntity, "echo" ); engine->ClientCommand( pEntity, "echo COF means 'Cone of Fire' and is a term to describe how a game spreads shot around from your gun. What this plugin does is replace DoD:S' completly random cone of fire with a center weighted one. Meaning bullets will have more of a tendacy to go where you are aiming." ); engine->ClientCommand( pEntity, "echo" ); engine->ClientCommand( pEntity, "echo \"Please visit http://www.prodod.net for news and updates!\"" ); return PLUGIN_STOP; // we handled this function } return PLUGIN_CONTINUE; } //--------------------------------------------------------------------------------- // Purpose: an example of how to implement a new command //--------------------------------------------------------------------------------- CON_COMMAND( cof_version, "Prints the version of the cof plugin" ) { Msg( "Version: %s\n", VERSION ); } CON_COMMAND( cof_toggle, "Set the type of cof" ) { if( plugStatus ) { removeCof(); Msg( "COF is now default\n"); } else { if(addCof()) Msg( "COF is now center weighted\n" ); else Msg( "There was a problem setting the COF, please report this error on the forums. #%d", errNum ); } } CON_COMMAND( cof_status, "Get current status of the cof" ) { if( plugStatus ) Msg("COF is center weighted\n"); else Msg("COF is default\n"); } CON_COMMAND( cof_error, "Get last error code" ) { if( errNum != 0 ) Msg("The last error code was #%d", errNum ); else Msg("No errors yet"); } void cof_switch(ConVar *var, char const *pOldString) { if( var->GetBool() ){ if(!addCof()){ Msg( "There was a problem setting the COF, please report this error on the forums. #%d", errNum ); } }else{ removeCof(); } } static ConVar client_cvar("cof_switch", "1", FCVAR_PLUGIN, "COF Plugin Enabled = 1 | Disabled = 0", (FnChangeCallback)cof_switch); /* Library Functions */ void writeGameName(bool restore) { //Write game type #ifdef _WIN32 DWORD offset = 0x400000; #else DWORD offset = 0x0; #endif if( gameName.loc == 0 ) gameName.loc = dwFindPattern((DWORD)baseAddrServ + offset,0x200000,gameName.bData,gameName.cMask); if( gameName.loc ){ if(!restore) WriteMemory(gameName.loc, (BYTE*)GAMENAME, sizeof(GAMENAME)); //Add my string else WriteMemory(gameName.loc, (BYTE*)"Day of Defeat: Source", sizeof(GAMENAME)); //restore the string } } bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { for(;*szMask;++szMask,++pData,++bMask) if(*szMask=='x' && *pData!=*bMask ) return false; return (*szMask) == NULL; } DWORD dwFindPattern( DWORD dwAddress, DWORD dwLen, BYTE* bMask, char* szMask) { for(DWORD i=0; i < dwLen; i++) { //if( (dwAddress + i) >= ((DWORD)baseAddrServ + servSize) ) { // Msg("Ran out of bounds at %08X\n", (dwAddress+i)); // return 0; //} if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) ) return (DWORD)(dwAddress+i); } return 0; } bool WriteMemory( DWORD address, BYTE* data, int size ) { //BYTE* temp = data; //Msg( "Writing: ");// \"%X\" to address: \"%X\"\n", *data, address ); //for( int i = 1; i<=size; i++ ){ // Msg( "%X", (BYTE)*temp++); //} //Msg("\n"); void* ptr = (void*)address; #ifdef _WIN32 DWORD dwBack; if( !VirtualProtect(ptr, size, PAGE_EXECUTE_READWRITE, &dwBack) ) return false; #else //if( mprotect((char*)address - ((long)address % getpagesize()), size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0 ) return false; PAGESIZE = getpagesize(); if( mprotect(SH_LALIGN(address), size + SH_LALDIF(address), PROT_READ | PROT_WRITE | PROT_EXEC) != 0 ) return false; #endif memset(ptr, 0, size); memcpy(ptr, data, size); #ifdef _WIN32 if( !VirtualProtect(ptr, size, dwBack, &dwBack) ) return false; #endif return true; } void ReadMemory( DWORD address, BYTE* buffer, int size ) { void* ptr = (void*)address; memcpy(buffer, ptr, size); } bool addCof() { bool result = false; #ifdef _WIN32 DWORD offset = 0x200000; #else DWORD offset = 0x0; #endif if( shotCode.loc == 0 ) shotCode.loc = dwFindPattern((DWORD)baseAddrServ + offset,0x1000000,shotCode.bData,shotCode.cMask); DWORD functionAddr = (DWORD)&myCOF; if( shotCode.loc == 0 ){ errNum = 2; return false; } #ifdef _WIN32 DWORD memAddr = shotCode.loc + 0x08; #else DWORD memAddr = shotCode.loc + 0x03; #endif ReadMemory( memAddr, origCode, sizeof(origCode) ); result = WriteMemory( memAddr, (BYTE*)"\xB9", 1 ); result = WriteMemory( memAddr + 1, (BYTE*)(DWORD*)&functionAddr, sizeof(DWORD) ) && result; result = WriteMemory( memAddr + sizeof(DWORD) + 1, (BYTE*)"\xFF\xE1\x90", 3 ) && result; #ifdef _WIN32 result = WriteMemory( memAddr + sizeof(DWORD) + 4, (BYTE*)"\x90\x90", 2 ) && result; #endif if( !result ){ errNum = 3; return false; } if( retCode.loc == 0 ) retCode.loc = dwFindPattern(shotCode.loc,0x1000,retCode.bData,retCode.cMask); DWORD stampAddr = dwFindPattern(functionAddr,0x2000000,(BYTE*)"\xAA\xAA\xAA\xAA","xxxx" ); if( retCode.loc == 0 || stampAddr == 0 ){ errNum = 4; return false; } if( !WriteMemory( stampAddr, (BYTE*)(DWORD*)&retCode.loc, sizeof(DWORD) ) ){ errNum = 5; return false; } //Msg("baseAddrServ: %08X baseAddrSelf: %08X shotCode: %08X functionAddr: %08X retCode: %08X\n",baseAddrServ,baseAddrSelf,shotCode.loc,functionAddr,retCode.loc); plugStatus = true; writeGameName(); return true; } void removeCof() { if( shotCode.loc == 0 ) return; #ifdef _WIN32 DWORD memAddr = shotCode.loc + 0x08; #else DWORD memAddr = shotCode.loc + 0x03; #endif if( !WriteMemory(memAddr,origCode,sizeof(origCode)) ){ errNum = 6; return; } writeGameName(true); plugStatus = false; } bool getBaseAddrs(void* servAddr, void* selfAddr) { #ifdef _WIN32 MEMORY_BASIC_INFORMATION info; if( !VirtualQuery(servAddr, &info, sizeof(info)) ) return false; baseAddrServ = info.AllocationBase; if( !VirtualQuery(selfAddr, &info, sizeof(info)) ) return false; baseAddrSelf = info.AllocationBase; //start checking the headers for the image size IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)(info.AllocationBase); IMAGE_NT_HEADERS* pe = (IMAGE_NT_HEADERS*)((unsigned long)dos + (unsigned long)(dos->e_lfanew)); //small little sanity check if (pe->Signature != IMAGE_NT_SIGNATURE) { errNum = 7; return false; } servSize = pe->OptionalHeader.SizeOfImage; #else void* handle = dlopen("dod/bin/server_i486.so", RTLD_LAZY ); void* loc = dlsym(handle, "_Z14FX_FireBulletsiRK6VectorRK6QAngleiiif" ); baseAddrServ = (DWORD)loc; dlclose(handle); //Not used baseAddrSelf = 0; #endif if( baseAddrServ == 0 ){ errNum = 8; return false; } return true; }