From 505ef87c124674900be49ffda46dbbcde3ad881b Mon Sep 17 00:00:00 2001 From: Olion Date: Sat, 25 Apr 2015 15:34:04 +0300 Subject: [PATCH] [Warden] Support of several client builds --- src/game/Server/WorldSession.cpp | 6 +- src/game/Server/WorldSession.h | 6 +- src/game/Server/WorldSocket.cpp | 2 +- src/game/Warden/Warden.cpp | 2 +- src/game/Warden/Warden.h | 3 + src/game/Warden/WardenCheckMgr.cpp | 98 +++++++++++++++++++++++++++++- src/game/Warden/WardenCheckMgr.h | 21 +++++-- src/game/Warden/WardenMac.cpp | 2 +- src/game/Warden/WardenWin.cpp | 12 ++-- src/game/Warden/WardenWin.h | 2 +- src/mangosd/Master.cpp | 2 +- src/shared/revision.h | 2 +- 12 files changed, 134 insertions(+), 24 deletions(-) diff --git a/src/game/Server/WorldSession.cpp b/src/game/Server/WorldSession.cpp index 322231dc..d4c9de73 100644 --- a/src/game/Server/WorldSession.cpp +++ b/src/game/Server/WorldSession.cpp @@ -97,7 +97,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) /// WorldSession constructor WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, time_t mute_time, LocaleConstant locale) : m_muteTime(mute_time), - _player(NULL), m_Socket(sock), _security(sec), _accountId(id), _warden(NULL), _logoutTime(0), + _player(NULL), m_Socket(sock), _security(sec), _accountId(id), _warden(NULL), _build(0), _logoutTime(0), m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_playerSave(false), m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(sObjectMgr.GetIndexForLocale(locale)), m_latency(0), m_tutorialState(TUTORIALDATA_UNCHANGED) @@ -823,8 +823,10 @@ void WorldSession::SendPlaySpellVisual(ObjectGuid guid, uint32 spellArtKit) SendPacket(&data); } -void WorldSession::InitWarden(BigNumber* k, std::string const& os) +void WorldSession::InitWarden(uint16 build, BigNumber* k, std::string const& os) { + _build = build; + if (os == "Win" && sWorld.getConfig(CONFIG_BOOL_WARDEN_WIN_ENABLED)) { _warden = new WardenWin(); diff --git a/src/game/Server/WorldSession.h b/src/game/Server/WorldSession.h index eabc446f..e44760ee 100644 --- a/src/game/Server/WorldSession.h +++ b/src/game/Server/WorldSession.h @@ -194,7 +194,7 @@ class WorldSession } // Warden - void InitWarden(BigNumber* k, std::string const& os); + void InitWarden(uint16 build, BigNumber* k, std::string const& os); /// Session in auth.queue currently void SetInQueue(bool state) @@ -737,6 +737,9 @@ class WorldSession void HandleBotPackets(); #endif + // for Warden + uint16 GetClientBuild() const { return _build; } + private: // private trade methods void moveItems(Item* myItems[], Item* hisItems[]); @@ -759,6 +762,7 @@ class WorldSession // Warden Warden* _warden; // Remains NULL if Warden system is not enabled by config + uint16 _build; // connected client build time_t _logoutTime; bool m_inQueue; // session wait in auth.queue diff --git a/src/game/Server/WorldSocket.cpp b/src/game/Server/WorldSocket.cpp index e6d5c4cf..658ae0af 100644 --- a/src/game/Server/WorldSocket.cpp +++ b/src/game/Server/WorldSocket.cpp @@ -864,7 +864,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Initialize Warden system only if it is enabled by config if (wardenActive) - m_Session->InitWarden(&K, os); + m_Session->InitWarden(uint16(BuiltNumberClient), &K, os); sWorld.AddSession(m_Session); diff --git a/src/game/Warden/Warden.cpp b/src/game/Warden/Warden.cpp index 470ae96f..2f26d1af 100644 --- a/src/game/Warden/Warden.cpp +++ b/src/game/Warden/Warden.cpp @@ -230,7 +230,7 @@ std::string Warden::Penalty(WardenCheck* check /*= NULL*/) banReason << "Warden Anticheat Violation"; // Check can be NULL, for example if the client sent a wrong signature in the warden packet (CHECKSUM FAIL) if (check) - banReason << ": " << check->Comment << " (CheckId: " << check->CheckId << ")"; + banReason << ": " << (check->Comment.empty() ? std::string("Undocumented Check") : check->Comment) << " (CheckId: " << check->CheckId << ")"; sWorld.BanAccount(BAN_ACCOUNT, accountName, sWorld.getConfig(CONFIG_UINT32_WARDEN_CLIENT_BAN_DURATION), banReason.str(), "Warden"); diff --git a/src/game/Warden/Warden.h b/src/game/Warden/Warden.h index 9046a070..1a09fbc3 100644 --- a/src/game/Warden/Warden.h +++ b/src/game/Warden/Warden.h @@ -33,6 +33,9 @@ #include "WardenCheckMgr.h" #include "Database/DatabaseEnv.h" +// the default client version with info in warden_checks; for other version checks, see warden_build_specific +#define DEFAULT_CLIENT_BUILD 5875 + enum WardenOpcodes { // Client->Server diff --git a/src/game/Warden/WardenCheckMgr.cpp b/src/game/Warden/WardenCheckMgr.cpp index 287d8653..d451638f 100644 --- a/src/game/Warden/WardenCheckMgr.cpp +++ b/src/game/Warden/WardenCheckMgr.cpp @@ -66,6 +66,7 @@ void WardenCheckMgr::LoadWardenChecks() CheckStore.resize(maxCheckId + 1); + delete result; // 0 1 2 3 4 5 6 7 result = WorldDatabase.Query("SELECT id, type, data, result, address, length, str, comment FROM warden_checks ORDER BY id ASC"); @@ -147,7 +148,7 @@ void WardenCheckMgr::LoadWardenChecks() } if (comment.empty()) - wardenCheck->Comment = "Undocumented Check"; + wardenCheck->Comment = ""; else wardenCheck->Comment = comment; @@ -155,6 +156,81 @@ void WardenCheckMgr::LoadWardenChecks() } while (result->NextRow()); sLog.outWarden(">> Loaded %u warden checks.", count); + + delete result; + // 0 1 2 3 4 5 6 + result = WorldDatabase.Query("SELECT id, build, data, result, address, length, str FROM warden_build_specific ORDER BY id ASC"); + + if (!result) + { + sLog.outString("[Warden]: >> Loaded 0 warden client build-specific data."); + return; + } + + count = 0; + do + { + fields = result->Fetch(); + + uint16 id = fields[0].GetUInt16(); + if (id >= CheckStore.size()) + { + sLog.outWarden("ERROR: Build-specific, check is missing in warden_checks, skipping id %u.", id); + continue; + } + + uint16 build = fields[1].GetUInt16(); + if (build == DEFAULT_CLIENT_BUILD) + { + sLog.outWarden("ERROR: Build-specific table may not contain checks for default %u build, skipping id %u.", DEFAULT_CLIENT_BUILD, id); + continue; + } + + //std::string data = fields[2].GetString(); //unused for now + std::string checkResult = fields[3].GetString(); + uint32 address = fields[4].GetUInt32(); + uint8 length = fields[5].GetUInt8(); + std::string str = fields[6].GetString(); + + WardenCheck* wardenCheck = new WardenCheck(); + wardenCheck->CheckId = id; + wardenCheck->Type = CheckStore[id]->Type; + + WardenCheckResult* wr = new WardenCheckResult(); + switch (wardenCheck->Type) + { + case MEM_CHECK: + wardenCheck->Address = address; + wardenCheck->Length = length; + wardenCheck->Str = str; + wr->Result.SetHexStr(checkResult.c_str()); + { + int len = checkResult.size() / 2; + if (wr->Result.GetNumBytes() < len) + { + uint8 *temp = new uint8[len]; + memset(temp, 0, len); + memcpy(temp, wr->Result.AsByteArray(), wr->Result.GetNumBytes()); + std::reverse(temp, temp + len); + wr->Result.SetBinary((uint8*)temp, len); + delete[] temp; + } + } + break; + default: + sLog.outWarden("The check type %u is considered as build-independent, skipping id %u.", wardenCheck->Type, id); + delete wr; + delete wardenCheck; + continue; + } + + MCheckStore[ComposeMultiCheckKey(build, id)] = wardenCheck; + MCheckResultStore[ComposeMultiCheckKey(build, id)] = wr; + ++count; + + } while (result->NextRow()); + + sLog.outString(">> Loaded %u warden client build-specific check overrides.", count); } void WardenCheckMgr::LoadWardenOverrides() @@ -203,18 +279,34 @@ void WardenCheckMgr::LoadWardenOverrides() sLog.outWarden(">> Loaded %u warden action overrides.", count); } -WardenCheck* WardenCheckMgr::GetWardenDataById(uint16 Id) +WardenCheck* WardenCheckMgr::GetWardenDataById(uint16 build, uint16 Id) { if (Id < CheckStore.size()) + { + if (build != DEFAULT_CLIENT_BUILD) + { + MultiCheckContainer::const_iterator it = MCheckStore.find(ComposeMultiCheckKey(build, Id)); + if (it != MCheckStore.end()) + return it->second; + } return CheckStore[Id]; + } return NULL; } -WardenCheckResult* WardenCheckMgr::GetWardenResultById(uint16 Id) +WardenCheckResult* WardenCheckMgr::GetWardenResultById(uint16 build, uint16 Id) { + if (build != DEFAULT_CLIENT_BUILD) + { + MultiResultContainer::const_iterator it = MCheckResultStore.find(ComposeMultiCheckKey(build, Id)); + if (it != MCheckResultStore.end()) + return it->second; + } + CheckResultContainer::const_iterator itr = CheckResultStore.find(Id); if (itr != CheckResultStore.end()) return itr->second; + return NULL; } diff --git a/src/game/Warden/WardenCheckMgr.h b/src/game/Warden/WardenCheckMgr.h index db874633..9723bda0 100644 --- a/src/game/Warden/WardenCheckMgr.h +++ b/src/game/Warden/WardenCheckMgr.h @@ -66,12 +66,8 @@ class WardenCheckMgr return &instance; } - // We have a linear key without any gaps, so we use vector for fast access - typedef std::vector CheckContainer; - typedef std::map CheckResultContainer; - - WardenCheck* GetWardenDataById(uint16 Id); - WardenCheckResult* GetWardenResultById(uint16 Id); + WardenCheck* GetWardenDataById(uint16 build, uint16 Id); + WardenCheckResult* GetWardenResultById(uint16 build, uint16 Id); std::vector MemChecksIdPool; std::vector OtherChecksIdPool; @@ -82,8 +78,21 @@ class WardenCheckMgr ACE_RW_Mutex _checkStoreLock; private: + uint32 inline ComposeMultiCheckKey(uint16 clientBuild, uint16 checkID) { return (uint32(checkID) << 16) | clientBuild; } + + // We have a linear key without any gaps, so we use vector for fast access + typedef std::vector CheckContainer; + typedef std::map CheckResultContainer; + CheckContainer CheckStore; CheckResultContainer CheckResultStore; + + // here we have just few checks, vector is not appropriate; key is from ComposeMultiCheckKey + typedef std::map MultiCheckContainer; + typedef std::map MultiResultContainer; + + MultiCheckContainer MCheckStore; + MultiResultContainer MCheckResultStore; }; #define sWardenCheckMgr WardenCheckMgr::instance() diff --git a/src/game/Warden/WardenMac.cpp b/src/game/Warden/WardenMac.cpp index f501839a..6a147c29 100644 --- a/src/game/Warden/WardenMac.cpp +++ b/src/game/Warden/WardenMac.cpp @@ -61,7 +61,7 @@ void WardenMac::Init(WorldSession* pClient, BigNumber* K) _inputCrypto.Init(_inputKey); _outputCrypto.Init(_outputKey); - sLog.outWarden("Server side warden for client %u initializing...", pClient->GetAccountId()); + sLog.outWarden("Server side Mac warden for client %u (build %u) initializing...", pClient->GetAccountId(), _session->GetClientBuild()); sLog.outWarden("C->S Key: %s", ByteArrayToHexStr(_inputKey, 16).c_str()); sLog.outWarden("S->C Key: %s", ByteArrayToHexStr(_outputKey, 16).c_str()); sLog.outWarden(" Seed: %s", ByteArrayToHexStr(_seed, 16).c_str()); diff --git a/src/game/Warden/WardenWin.cpp b/src/game/Warden/WardenWin.cpp index 56f6ef48..047398d6 100644 --- a/src/game/Warden/WardenWin.cpp +++ b/src/game/Warden/WardenWin.cpp @@ -56,7 +56,7 @@ void WardenWin::Init(WorldSession* session, BigNumber* k) _inputCrypto.Init(_inputKey); _outputCrypto.Init(_outputKey); - sLog.outWarden("Server side warden for client %u initializing...", session->GetAccountId()); + sLog.outWarden("Server side warden for client %u (build %u) initializing...", session->GetAccountId(), _session->GetClientBuild()); sLog.outWarden("C->S Key: %s", ByteArrayToHexStr(_inputKey, 16).c_str()); sLog.outWarden("S->C Key: %s", ByteArrayToHexStr(_outputKey, 16).c_str()); sLog.outWarden(" Seed: %s", ByteArrayToHexStr(_seed, 16).c_str()); @@ -210,7 +210,7 @@ void WardenWin::RequestData() // Add the id to the list sent in this cycle _currentChecks.push_back(id); - wd = sWardenCheckMgr->GetWardenDataById(id); + wd = sWardenCheckMgr->GetWardenDataById(_session->GetClientBuild(), id); switch (wd->Type) { @@ -235,7 +235,7 @@ void WardenWin::RequestData() for (std::list::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) { - wd = sWardenCheckMgr->GetWardenDataById(*itr); + wd = sWardenCheckMgr->GetWardenDataById(_session->GetClientBuild(), *itr); type = wd->Type; buff << uint8(type ^ xorByte); @@ -360,8 +360,8 @@ void WardenWin::HandleData(ByteBuffer &buff) for (std::list::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) { - rd = sWardenCheckMgr->GetWardenDataById(*itr); - rs = sWardenCheckMgr->GetWardenResultById(*itr); + rd = sWardenCheckMgr->GetWardenDataById(_session->GetClientBuild(), *itr); + rs = sWardenCheckMgr->GetWardenResultById(_session->GetClientBuild(), *itr); type = rd->Type; switch (type) @@ -475,7 +475,7 @@ void WardenWin::HandleData(ByteBuffer &buff) if (checkFailed > 0) { - WardenCheck* check = sWardenCheckMgr->GetWardenDataById(checkFailed); //note it IS NOT NULL here + WardenCheck* check = sWardenCheckMgr->GetWardenDataById(_session->GetClientBuild(), checkFailed); //note it IS NOT NULL here sLog.outWarden("%s failed Warden check %u. Action: %s", _session->GetPlayerName(), checkFailed, Penalty(check).c_str()); LogPositiveToDB(check); } diff --git a/src/game/Warden/WardenWin.h b/src/game/Warden/WardenWin.h index 16e33f01..d7b28c66 100644 --- a/src/game/Warden/WardenWin.h +++ b/src/game/Warden/WardenWin.h @@ -71,7 +71,7 @@ struct WardenInitModuleRequest #endif class WorldSession; -class Warden; +//class Warden; class WardenWin : public Warden { diff --git a/src/mangosd/Master.cpp b/src/mangosd/Master.cpp index 95c40b3f..c7e4a628 100644 --- a/src/mangosd/Master.cpp +++ b/src/mangosd/Master.cpp @@ -570,7 +570,7 @@ void Master::clearOnlineAccounts() { // Cleanup online status for characters hosted at current realm /// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'? - LoginDatabase.PExecute("UPDATE account SET active_realm_id = 0 WHERE active_realm_id = '%u'", realmID); + LoginDatabase.PExecute("UPDATE account SET active_realm_id = 0, os = '' WHERE active_realm_id = '%u'", realmID); CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0"); diff --git a/src/shared/revision.h b/src/shared/revision.h index 13d99f60..7e0f6d5f 100644 --- a/src/shared/revision.h +++ b/src/shared/revision.h @@ -26,6 +26,6 @@ #define MANGOS_H_REVISION #define REVISION_NR "21000" #define REVISION_DB_CHARACTERS "required_21000_01_warden_action" -#define REVISION_DB_MANGOS "required_21000_08_warden_checks" +#define REVISION_DB_MANGOS "required_21000_10_warden_multiversion" #define REVISION_DB_REALMD "required_20150420_warden_db_log" #endif // __REVISION_H__