From b11d7e3c9b1555673d6cb490e60eca19153195e9 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:08:28 +0200 Subject: [PATCH] Engine: add new netmsg 'SVC_SetClassVar' Netmessage SVC_SetClassVar allows the server to change class settings securely on the client. This was implemented due to popular demand, and previous approaches using a combination of ClientCommands (Cbuf and NET_StringCmd) were deemed insecure and unreliable. --- src/common/callback.h | 6 ++++ src/common/netmessages.cpp | 34 ++++++++++++++++++- src/common/netmessages.h | 54 ++++++++++++++++++++++++++++++- src/engine/client/clientstate.cpp | 1 + 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/common/callback.h b/src/common/callback.h index 9ca4d1ed..89dfe958 100644 --- a/src/common/callback.h +++ b/src/common/callback.h @@ -4,6 +4,8 @@ inline bool(*v_SetupGamemode)(const char* pszPlayList); /* ==== CONCOMMANDCALLBACK ============================================================================================================================================== */ inline void(*v__Cmd_Exec_f)(const CCommand& args); +inline bool(*v__setClassVarServer_f)(const CCommand& args); +inline bool(*v__setClassVarClient_f)(const CCommand& args); #ifndef DEDICATED inline void(*v__UIScript_Reset_f)(); #endif // !DEDICATED @@ -41,6 +43,8 @@ class VCallback : public IDetour { LogFunAdr("SetupGamemode", v_SetupGamemode); LogFunAdr("Cmd_Exec_f", v__Cmd_Exec_f); + LogFunAdr("SetClassVarServer_f", v__setClassVarServer_f); + LogFunAdr("SetClassVarClient_f", v__setClassVarClient_f); #ifndef DEDICATED LogFunAdr("UIScript_Reset_f", v__UIScript_Reset_f); #endif // !DEDICATED @@ -49,6 +53,8 @@ class VCallback : public IDetour { g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 C7 C0 ?? ?? ?? ??").GetPtr(v_SetupGamemode); g_GameDll.FindPatternSIMD("40 55 53 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9").GetPtr(v__Cmd_Exec_f); + g_GameDll.FindPatternSIMD("41 56 48 83 EC ?? 8B 05 ?? ?? ?? ?? 4C 8B F1 FF C0").GetPtr(v__setClassVarServer_f); + g_GameDll.FindPatternSIMD("4C 8B DC 57 48 81 EC ?? ?? ?? ?? 8B 05").GetPtr(v__setClassVarClient_f); #ifndef DEDICATED g_GameDll.FindPatternSIMD("40 55 41 54 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 45 33 E4 48 8D 0D").GetPtr(v__UIScript_Reset_f); #endif // !DEDICATED diff --git a/src/common/netmessages.cpp b/src/common/netmessages.cpp index 2e76f72f..29973460 100644 --- a/src/common/netmessages.cpp +++ b/src/common/netmessages.cpp @@ -12,6 +12,7 @@ #include "tier1/cvar.h" #include "engine/net.h" #include "common/netmessages.h" +#include "common/callback.h" #include "game/shared/usermessages.h" /////////////////////////////////////////////////////////////////////////////////// @@ -64,6 +65,37 @@ bool SVC_UserMessage::ProcessImpl() return SVC_UserMessage_Process(this); // Need to return original. } +/////////////////////////////////////////////////////////////////////////////////// +// Net message to change class settings vars on the client +/////////////////////////////////////////////////////////////////////////////////// +bool SVC_SetClassVar::ReadFromBuffer(bf_read* buffer) +{ + const bool set = buffer->ReadString(m_szSetting, sizeof(m_szSetting)); + const bool var = buffer->ReadString(m_szVariable, sizeof(m_szVariable)); + + return set && var; +} +bool SVC_SetClassVar::WriteToBuffer(bf_write* buffer) +{ + const bool set = buffer->WriteString(m_szSetting); + const bool var = buffer->WriteString(m_szVariable); + + return set && var; +} +bool SVC_SetClassVar::Process(void) +{ + const char* pArgs[3] = { + "_setClassVarClient", + m_szSetting, + m_szVariable + }; + + CCommand command((int)V_ARRAYSIZE(pArgs), pArgs, cmd_source_t::kCommandSrcCode); + v__setClassVarClient_f(command); + + return true; +} + /////////////////////////////////////////////////////////////////////////////////// // Below functions are hooked as playlist overrides can be abused from the client. // The client could basically manage the server's playlists. Only allow read/write @@ -122,7 +154,7 @@ bool Base_CmdKeyValues::WriteToBufferImpl(Base_CmdKeyValues* thisptr, bf_write* // determine whether or not the message should be copied into the replay buffer, // regardless of the 'CNetMessage::m_Group' type. /////////////////////////////////////////////////////////////////////////////////// -bool ShouldReplayMessage(const CNetMessage* msg) +bool ShouldReplayMessage(const CNetMessage* msg) // todo(amos): rename to 'CanReplayMessage' { switch (msg->GetType()) { diff --git a/src/common/netmessages.h b/src/common/netmessages.h index 7e226b04..d6a2f79e 100644 --- a/src/common/netmessages.h +++ b/src/common/netmessages.h @@ -162,6 +162,15 @@ enum NetMessageType clc_AntiCheat = 63, clc_AntiCheatChallenge = 64, clc_GamepadMsg = 65, + + // NOTE: if you make new netmessages, the order should be as follows: + // - NET (messages shared between server & client). + // - SVC (messages originating from the server). + // - CLC (messages originating from the client. + // Reorder message indices to retain consistency here. + // -------------------------------------------------------------------- + // From here on, SDK netmessages are enumerated. + svc_SetClassVar = 66, }; //------------------------------------------------------------------------- @@ -176,10 +185,16 @@ enum NetMessageGroup class CNetMessage : public INetMessage { public: + virtual void SetNetChannel(CNetChan* netchan) { m_NetChannel = netchan; } + virtual void SetReliable(bool state) { m_bReliable = state; } + virtual bool IsReliable(void) const { return m_bReliable; } + virtual int GetGroup(void) const { return m_nGroup; } + virtual CNetChan* GetNetChannel(void) const { return m_NetChannel; } + int m_nGroup; bool m_bReliable; CNetChan* m_NetChannel; - INetMessageHandler* m_pMessageHandler; + INetChannelHandler* m_pMessageHandler; }; /////////////////////////////////////////////////////////////////////////////////////// @@ -445,6 +460,43 @@ private: bf_write m_DataOut; }; +class SVC_SetClassVar : public CNetMessage +{ +public: + SVC_SetClassVar() = default; + SVC_SetClassVar(const char* setting, const char* var) + { + V_strncpy(m_szSetting, setting, sizeof(m_szSetting)); + V_strncpy(m_szVariable, var, sizeof(m_szVariable)); + + m_szSetting[sizeof(m_szSetting) - 1] = '\0'; + m_szVariable[sizeof(m_szVariable) - 1] = '\0'; + + m_nGroup = 2; // must be set to 2 to avoid being copied into replay buffer + } + + virtual bool ReadFromBuffer(bf_read* buffer); + virtual bool WriteToBuffer(bf_write* buffer); + + virtual bool Process(void); + + virtual int GetType(void) const { return svc_SetClassVar; }; + virtual const char* GetName(void) const { return "svc_SetClassVar"; }; + + virtual const char* ToString(void) const + { + static char szBuf[4096]; + V_snprintf(szBuf, sizeof(szBuf), "%s: setting \"%s\", variable \"%s\"", this->GetName(), m_szSetting, m_szVariable); + + return szBuf; + }; + + virtual size_t GetSize(void) const { return sizeof(SVC_SetClassVar); } + + char m_szSetting[128]; + char m_szVariable[128]; +}; + /////////////////////////////////////////////////////////////////////////////////////// // Client messages: /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/engine/client/clientstate.cpp b/src/engine/client/clientstate.cpp index b978f44b..058aa1fa 100644 --- a/src/engine/client/clientstate.cpp +++ b/src/engine/client/clientstate.cpp @@ -509,6 +509,7 @@ void CClientState::Reconnect() //--------------------------------------------------------------------------------- void CClientState::RegisterNetMsgs(CNetChan* chan) { + REGISTER_SVC_MSG(SetClassSettingsVar); } void VClientState::Detour(const bool bAttach) const