diff --git a/data/sql/updates/pending_db_world/rev_1599925076493872561.sql b/data/sql/updates/pending_db_world/rev_1599925076493872561.sql new file mode 100644 index 000000000..ffeff101e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1599925076493872561.sql @@ -0,0 +1,9 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1599925076493872561'); +ALTER TABLE `warden_checks` MODIFY `str` VARCHAR(170); +DELETE FROM `warden_checks` WHERE `id` IN (788,789,790,791,792); +INSERT INTO `warden_checks` (`id`,`type`,`str`,`address`,`length`,`result`, `comment`) VALUES +(788, 139, 'forceinsecure() return issecure()', NULL, NULL, NULL, 'Detects naive Lua unlockers'), +(789, 139, 'return not not PQR_IsMoving', NULL, NULL, NULL, 'Detects PQR'), +(790, 139, 'local f=DEFAULT_CHAT_FRAME for i=1,f:GetNumMessages() do if (f:GetMessageInfo(i)):find("|cffffd200PQR|r") then return true end end', NULL, NULL, NULL, 'Detects PQR'), +(791, 139, 'local f=DEFAULT_CHAT_FRAME for i=1,f:GetNumMessages() do if (f:GetMessageInfo(i)):find("|cFF32CD32EWT|r") then return true end end',NULL,NULL,NULL,'Detects EWT'), +(792, 139, 'local f=DEFAULT_CHAT_FRAME for i=1,f:GetNumMessages() do if (f:GetMessageInfo(i)):find("|cFFFF4400WoWPlus|r") then return true end end',NULL,NULL,NULL,'Detects WoWPlus'); diff --git a/src/common/Containers.h b/src/common/Containers.h index 655ae71fc..ba47fb1a5 100644 --- a/src/common/Containers.h +++ b/src/common/Containers.h @@ -14,6 +14,7 @@ #include #include #include +#include //! Because circular includes are bad extern uint32 urand(uint32 min, uint32 max); @@ -107,6 +108,40 @@ namespace acore std::advance(it, urandweighted(weights.size(), weights.data())); return *it; } + + template + std::enable_if_t().begin())>, void> EraseIf(Container& c, Predicate p) + { + auto wpos = c.begin(); + for (auto rpos = c.begin(), end = c.end(); rpos != end; ++rpos) + { + if (!p(*rpos)) + { + if (rpos != wpos) + { + std::swap(*rpos, *wpos); + } + ++wpos; + } + } + c.erase(wpos, c.end()); + } + + template + std::enable_if_t().begin())>, void> EraseIf(Container& c, Predicate p) + { + for (auto it = c.begin(); it != c.end();) + { + if (p(*it)) + { + it = c.erase(it); + } + else + { + ++it; + } + } + } } //! namespace Containers } diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 07a37c37a..2939ef177 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -27,6 +27,7 @@ #include "Util.h" #include "ScriptMgr.h" #include "AccountMgr.h" +#include "Warden.h" #ifdef ELUNA #include "LuaEngine.h" @@ -280,6 +281,12 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) if (lang != LANG_ADDON && sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) stripLineInvisibleChars(msg); + // Our Warden module also uses SendAddonMessage as a way to communicate Lua check results to the server, see if this is that + if ((type == CHAT_MSG_GUILD) && (lang == LANG_ADDON) && _warden && _warden->ProcessLuaCheckResponse(msg)) + { + return; + } + // pussywizard: if (msg.length() > 255 || (lang != LANG_ADDON && msg.find("|0") != std::string::npos)) return; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 3ef92e726..4eefe5213 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -391,12 +391,16 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (updater.ProcessLogout()) { + if (m_Socket && !m_Socket->IsClosed() && _warden) + { + _warden->Update(diff); + } + time_t currTime = time(nullptr); if (ShouldLogOut(currTime) && !m_playerLoading) + { LogoutPlayer(true); - - if (m_Socket && !m_Socket->IsClosed() && _warden) - _warden->Update(); + } if (m_Socket && m_Socket->IsClosed()) { @@ -405,7 +409,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) } if (!m_Socket) + { return false; + } } return true; diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp index 40f20da38..727c0582d 100644 --- a/src/server/game/Warden/Warden.cpp +++ b/src/server/game/Warden/Warden.cpp @@ -18,9 +18,10 @@ #include "Warden.h" #include "AccountMgr.h" #include "BanManager.h" +#include "SharedDefines.h" Warden::Warden() : _session(nullptr), _inputCrypto(16), _outputCrypto(16), _checkTimer(10000/*10 sec*/), _clientResponseTimer(0), - _dataSent(false), _previousTimestamp(0), _module(nullptr), _initialized(false) + _dataSent(false), _module(nullptr), _initialized(false) { memset(_inputKey, 0, sizeof(_inputKey)); memset(_outputKey, 0, sizeof(_outputKey)); @@ -77,6 +78,8 @@ void Warden::RequestModule() memcpy(request.ModuleKey, _module->Key, 16); request.Size = _module->CompressedSize; + EndianConvert(request.Size); + // Encrypt with warden RC4 key. EncryptData((uint8*)&request, sizeof(WardenModuleUse)); @@ -85,31 +88,37 @@ void Warden::RequestModule() _session->SendPacket(&pkt); } -void Warden::Update() +void Warden::Update(uint32 const diff) { - if (_initialized) + if (!_initialized) { - uint32 currentTimestamp = World::GetGameTimeMS(); - uint32 diff = getMSTimeDiff(_previousTimestamp, currentTimestamp); - _previousTimestamp = currentTimestamp; + return; + } - if (_dataSent) + if (_dataSent) + { + uint32 maxClientResponseDelay = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY); + if (maxClientResponseDelay > 0) { - uint32 maxClientResponseDelay = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY); - if (maxClientResponseDelay > 0) + if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS) { - if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS) - _session->KickPlayer("clientResponseTimer > maxClientResponseDelay"); - else - _clientResponseTimer += diff; + _session->KickPlayer("Warden: clientResponseTimer > maxClientResponseDelay (Warden::Update)"); } + else + { + _clientResponseTimer += diff; + } + } + } + else + { + if (diff >= _checkTimer) + { + RequestChecks(); } else { - if (diff >= _checkTimer) - RequestData(); - else - _checkTimer -= diff; + _checkTimer -= diff; } } } @@ -166,136 +175,142 @@ uint32 Warden::BuildChecksum(const uint8* data, uint32 length) SHA1(data, length, hash.bytes.bytes); uint32 checkSum = 0; for (uint8 i = 0; i < 5; ++i) + { checkSum = checkSum ^ hash.ints.ints[i]; + } return checkSum; } -std::string Warden::Penalty(WardenCheck* check /*= NULL*/, uint16 checkFailed /*= 0*/) +static std::string GetWardenActionStr(uint32 action) { - WardenActions action; + switch (action) + { + case WARDEN_ACTION_LOG: + return "WARDEN_ACTION_LOG"; + case WARDEN_ACTION_KICK: + return "WARDEN_ACTION_KICK"; + case WARDEN_ACTION_BAN: + return "WARDEN_ACTION_BAN"; + } - if (check) - action = check->Action; - else - action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION)); + return "UNHANDLED ACTION"; +} - std::string banReason = "Anticheat violation"; - bool longBan = false; // 14d = 1209600s - if (checkFailed) - switch (checkFailed) +void Warden::ApplyPenalty(uint16 checkId, std::string const& reason) +{ + WardenCheck const* checkData = sWardenCheckMgr->GetWardenDataById(checkId); + + uint32 action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION)); + std::string causeMsg; + if (checkId && checkData) + { + action = checkData->Action; + + if (checkData->Comment.empty()) { - case 47: - banReason += " (FrameXML Signature Check)"; - break; - case 51: - banReason += " (Lua DoString)"; - break; - case 59: - banReason += " (Lua Protection Patch)"; - break; - case 72: - banReason += " (Movement State related)"; - break; - case 118: - banReason += " (Wall Climb)"; - break; - case 121: - banReason += " (No Fall Damage Patch)"; - break; - case 193: - banReason += " (Follow Unit Check)"; - break; - case 209: - banReason += " (WoWEmuHacker Injection)"; - longBan = true; - break; - case 237: - banReason += " (AddChatMessage)"; - break; - case 246: - banReason += " (Language Patch)"; - break; - case 260: - banReason += " (Jump Momentum)"; - break; - case 288: - banReason += " (Language Patch)"; - break; - case 308: - banReason += " (SendChatMessage)"; - break; - case 312: - banReason += " (Jump Physics)"; - break; - case 314: - banReason += " (GetCharacterInfo)"; - break; - case 329: - banReason += " (Wall Climb)"; - break; - case 343: - banReason += " (Login Password Pointer)"; - break; - case 349: - banReason += " (Language Patch)"; - break; - case 712: - banReason += " (WS2_32.Send)"; - break; - case 780: - banReason += " (Lua Protection Remover)"; - break; - case 781: - banReason += " (Walk on Water Patch)"; - break; - case 782: - banReason += " (Collision M2 Special)"; - longBan = true; - break; - case 783: - banReason += " (Collision M2 Regular)"; - longBan = true; - break; - case 784: - banReason += " (Collision WMD)"; - longBan = true; - break; - case 785: - banReason += " (Multi-Jump Patch)"; - break; - case 786: - banReason += " (WPE PRO)"; - longBan = true; - break; - case 787: - banReason += " (rEdoX Packet Editor)"; - break; + causeMsg = "Warden id " + std::to_string(checkId) + " violation"; } + else + { + causeMsg = "Warden: " + checkData->Comment; + } + } + else + { + // if its not warden check id based, reason must be always provided + ASSERT(!reason.empty()); + causeMsg = reason; + } switch (action) { case WARDEN_ACTION_LOG: - return "None"; break; case WARDEN_ACTION_KICK: - _session->KickPlayer("WARDEN_ACTION_KICK"); - return "Kick"; + { + _session->KickPlayer(causeMsg.find("Warden") != std::string::npos ? causeMsg : "Warden: " + causeMsg); break; + } case WARDEN_ACTION_BAN: - { - std::stringstream duration; - duration << sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_BAN_DURATION) << "s"; - std::string accountName; - AccountMgr::GetName(_session->GetAccountId(), accountName); - sBan->BanAccount(accountName, ((longBan && false /*ZOMG!*/) ? "1209600s" : duration.str()), banReason, "Server"); - - return "Ban"; - } - default: + { + std::stringstream duration; + duration << sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_BAN_DURATION) << "s"; + std::string accountName; + AccountMgr::GetName(_session->GetAccountId(), accountName); + sBan->BanAccount(accountName, duration.str(), causeMsg, "Server"); break; + } } - return "Undefined"; + + std::string reportMsg; + if (checkId) + { + if (Player const* plr = _session->GetPlayer()) + { + std::string const reportFormat = "Player %s (guid %u, account id: %u) failed warden %u check (%s). Action: %s"; + reportMsg = acore::StringFormat(reportFormat, plr->GetName().c_str(), plr->GetGUIDLow(), _session->GetAccountId(), + checkId, ((checkData && !checkData->Comment.empty()) ? checkData->Comment.c_str() : ""), + GetWardenActionStr(action).c_str()); + } + else + { + std::string const reportFormat = "Account id: %u failed warden %u check. Action: %s"; + reportMsg = acore::StringFormat(reportFormat, _session->GetAccountId(), checkId, GetWardenActionStr(action).c_str()); + } + } + else + { + if (Player const* plr = _session->GetPlayer()) + { + std::string const reportFormat = "Player %s (guid %u, account id: %u) triggered warden penalty by reason: %s. Action: %s"; + reportMsg = acore::StringFormat(reportFormat, plr->GetName().c_str(), plr->GetGUIDLow(), _session->GetAccountId(), causeMsg.c_str(), GetWardenActionStr(action).c_str()); + } + else + { + std::string const reportFormat = "Account id: %u failed warden %u check. Action: %s"; + reportMsg = acore::StringFormat(reportFormat, _session->GetAccountId(), causeMsg.c_str(), GetWardenActionStr(action).c_str()); + } + } + + reportMsg = "Warden: " + reportMsg; + sLog->outString(reportMsg.c_str()); +} + +bool Warden::ProcessLuaCheckResponse(std::string const& msg) +{ + static constexpr char WARDEN_TOKEN[] = "_TW\t"; + // if msg starts with WARDEN_TOKEN + if (!(msg.rfind(WARDEN_TOKEN, 0) == 0)) + { + return false; + } + + uint16 id = 0; + + { + std::stringstream msg2(msg); + std::string temp; + while (msg2 >> temp) + { + // Found check id - stop loop + if (std::stringstream(temp) >> id) + break; + } + } + + if (id < sWardenCheckMgr->GetMaxValidCheckId()) + { + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(id); + if (check && check->Type == LUA_EVAL_CHECK) + { + ApplyPenalty(id, ""); + return true; + } + } + + ApplyPenalty(0, "Sent bogus Lua check response for Warden"); + return true; } void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData) diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h index 103301195..691d9232e 100644 --- a/src/server/game/Warden/Warden.h +++ b/src/server/game/Warden/Warden.h @@ -38,7 +38,7 @@ enum WardenCheckType PAGE_CHECK_A = 0xB2, // 178: uint Seed + byte[20] SHA1 + uint Addr + byte Len (scans all pages for specified hash) PAGE_CHECK_B = 0xBF, // 191: uint Seed + byte[20] SHA1 + uint Addr + byte Len (scans only pages starts with MZ+PE headers for specified hash) MPQ_CHECK = 0x98, // 152: byte fileNameIndex (check to ensure MPQ file isn't modified) - LUA_STR_CHECK = 0x8B, // 139: byte luaNameIndex (check to ensure LUA string isn't used) + LUA_EVAL_CHECK = 139, // evaluate arbitrary Lua check DRIVER_CHECK = 0x71, // 113: uint Seed + byte[20] SHA1 + byte driverNameIndex (check to ensure driver isn't loaded) TIMING_CHECK = 0x57, // 87: empty (check to ensure GetTickCount() isn't detoured) PROC_CHECK = 0x7E, // 126: uint Seed + byte[20] SHA1 + byte moluleNameIndex + byte procNameIndex + uint Offset + byte Len (check to ensure proc isn't detoured) @@ -101,21 +101,22 @@ public: virtual ClientWardenModule* GetModuleForClient() = 0; virtual void InitializeModule() = 0; virtual void RequestHash() = 0; - virtual void HandleHashResult(ByteBuffer& buff) = 0; - virtual void RequestData() = 0; - virtual void HandleData(ByteBuffer& buff) = 0; + virtual void HandleHashResult(ByteBuffer &buff) = 0; + virtual void RequestChecks() = 0; + virtual void HandleData(ByteBuffer &buff) = 0; + bool ProcessLuaCheckResponse(std::string const& msg); void SendModuleToClient(); void RequestModule(); - void Update(); + void Update(uint32 const diff); void DecryptData(uint8* buffer, uint32 length); void EncryptData(uint8* buffer, uint32 length); - static bool IsValidCheckSum(uint32 checksum, const uint8* data, const uint16 length); - static uint32 BuildChecksum(const uint8* data, uint32 length); + static bool IsValidCheckSum(uint32 checksum, const uint8 *data, const uint16 length); + static uint32 BuildChecksum(const uint8 *data, uint32 length); // If no check is passed, the default action from config is executed - std::string Penalty(WardenCheck* check = NULL, uint16 checkFailed = 0); + void ApplyPenalty(uint16 checkId, std::string const& reason); private: WorldSession* _session; @@ -127,7 +128,6 @@ private: uint32 _checkTimer; // Timer for sending check requests uint32 _clientResponseTimer; // Timer for client response delay bool _dataSent; - uint32 _previousTimestamp; ClientWardenModule* _module; bool _initialized; }; diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp index 84beb4e37..a38e665e7 100644 --- a/src/server/game/Warden/WardenCheckMgr.cpp +++ b/src/server/game/Warden/WardenCheckMgr.cpp @@ -19,11 +19,6 @@ WardenCheckMgr::WardenCheckMgr() WardenCheckMgr::~WardenCheckMgr() { - for (uint16 i = 0; i < CheckStore.size(); ++i) - delete CheckStore[i]; - - for (CheckResultContainer::iterator itr = CheckResultStore.begin(); itr != CheckResultStore.end(); ++itr) - delete itr->second; } WardenCheckMgr* WardenCheckMgr::instance() @@ -67,6 +62,13 @@ void WardenCheckMgr::LoadWardenChecks() uint16 id = fields[0].GetUInt16(); uint8 checkType = fields[1].GetUInt8(); + + if (checkType == LUA_EVAL_CHECK && id > 9999) + { + sLog->outError("sql.sql: Warden Lua check with id %u found in `warden_checks`. Lua checks may have four-digit IDs at most. Skipped.", id); + continue; + } + std::string data = fields[2].GetString(); std::string checkResult = fields[3].GetString(); uint32 address = fields[4].GetUInt32(); @@ -74,66 +76,96 @@ void WardenCheckMgr::LoadWardenChecks() std::string str = fields[6].GetString(); std::string comment = fields[7].GetString(); - WardenCheck* wardenCheck = new WardenCheck(); - wardenCheck->Type = checkType; - wardenCheck->CheckId = id; + WardenCheck &wardenCheck = CheckStore.at(id); + wardenCheck.Type = checkType; + wardenCheck.CheckId = id; // Initialize action with default action from config - wardenCheck->Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION)); - - if (checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == DRIVER_CHECK) + wardenCheck.Action = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION); + if (wardenCheck.Action > MAX_WARDEN_ACTION) { - wardenCheck->Data.SetHexStr(data.c_str()); - int len = data.size() / 2; - - if (wardenCheck->Data.GetNumBytes() < len) - { - uint8 temp[24]; - memset(temp, 0, len); - memcpy(temp, wardenCheck->Data.AsByteArray().get(), wardenCheck->Data.GetNumBytes()); - std::reverse(temp, temp + len); - wardenCheck->Data.SetBinary((uint8*)temp, len); - } + wardenCheck.Action = WARDEN_ACTION_BAN; } - if (checkType == MEM_CHECK || checkType == MODULE_CHECK) - MemChecksIdPool.push_back(id); - else - OtherChecksIdPool.push_back(id); - if (checkType == MEM_CHECK || checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == PROC_CHECK) { - wardenCheck->Address = address; - wardenCheck->Length = length; + wardenCheck.Address = address; + wardenCheck.Length = length; } // PROC_CHECK support missing - if (checkType == MEM_CHECK || checkType == MPQ_CHECK || checkType == LUA_STR_CHECK || checkType == DRIVER_CHECK || checkType == MODULE_CHECK) - wardenCheck->Str = str; - - CheckStore[id] = wardenCheck; + if (checkType == MEM_CHECK || checkType == MPQ_CHECK || checkType == LUA_EVAL_CHECK || checkType == DRIVER_CHECK || checkType == MODULE_CHECK) + { + wardenCheck.Str = str; + } if (checkType == MPQ_CHECK || checkType == MEM_CHECK) { - WardenCheckResult* wr = new WardenCheckResult(); - wr->Result.SetHexStr(checkResult.c_str()); - int len = checkResult.size() / 2; - if (wr->Result.GetNumBytes() < len) + WardenCheckResult wr; + wr.Result.SetHexStr(checkResult.c_str()); + int len = static_cast(checkResult.size()) / 2; + if (wr.Result.GetNumBytes() < len) { uint8* temp = new uint8[len]; memset(temp, 0, len); - memcpy(temp, wr->Result.AsByteArray().get(), wr->Result.GetNumBytes()); + memcpy(temp, wr.Result.AsByteArray().get(), wr.Result.GetNumBytes()); std::reverse(temp, temp + len); - wr->Result.SetBinary((uint8*)temp, len); + wr.Result.SetBinary((uint8*)temp, len); delete [] temp; } CheckResultStore[id] = wr; } if (comment.empty()) - wardenCheck->Comment = "Undocumented Check"; + wardenCheck.Comment = "Undocumented Check"; else - wardenCheck->Comment = comment; + wardenCheck.Comment = comment; + + // Prepare check pools + switch (checkType) + { + case MEM_CHECK: + case MODULE_CHECK: + { + CheckIdPool[WARDEN_CHECK_MEM_TYPE].push_back(id); + break; + } + case LUA_EVAL_CHECK: + { + if (wardenCheck.Length > WARDEN_MAX_LUA_CHECK_LENGTH) + { + sLog->outError("sql.sql: Found over-long Lua check for Warden check with id %u in `warden_checks`. Max length is %u. Skipped.", id, WARDEN_MAX_LUA_CHECK_LENGTH); + continue; + } + + std::string str = fmt::sprintf("%04u", id); + ASSERT(str.size() == 4); + std::copy(str.begin(), str.end(), wardenCheck.IdStr.begin()); + + CheckIdPool[WARDEN_CHECK_LUA_TYPE].push_back(id); + break; + } + default: + { + if (checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == DRIVER_CHECK) + { + wardenCheck.Data.SetHexStr(data.c_str()); + int len = static_cast(data.size()) / 2; + + if (wardenCheck.Data.GetNumBytes() < len) + { + uint8 temp[24]; + memset(temp, 0, len); + memcpy(temp, wardenCheck.Data.AsByteArray().get(), wardenCheck.Data.GetNumBytes()); + std::reverse(temp, temp + len); + wardenCheck.Data.SetBinary((uint8*)temp, len); + } + } + + CheckIdPool[WARDEN_CHECK_OTHER_TYPE].push_back(id); + break; + } + } ++count; } while (result->NextRow()); @@ -164,8 +196,6 @@ void WardenCheckMgr::LoadWardenOverrides() uint32 count = 0; - ACE_WRITE_GUARD(ACE_RW_Mutex, g, _checkStoreLock); - do { Field* fields = result->Fetch(); @@ -181,7 +211,7 @@ void WardenCheckMgr::LoadWardenOverrides() sLog->outError("Warden check action override for non-existing check (ID: %u, action: %u), skipped", checkId, action); else { - CheckStore[checkId]->Action = WardenActions(action); + CheckStore.at(checkId).Action = WardenActions(action); ++count; } } while (result->NextRow()); @@ -190,18 +220,22 @@ void WardenCheckMgr::LoadWardenOverrides() sLog->outString(); } -WardenCheck* WardenCheckMgr::GetWardenDataById(uint16 Id) +WardenCheck const* WardenCheckMgr::GetWardenDataById(uint16 Id) { if (Id < CheckStore.size()) - return CheckStore[Id]; + return &CheckStore.at(Id); return nullptr; } -WardenCheckResult* WardenCheckMgr::GetWardenResultById(uint16 Id) +WardenCheckResult const* WardenCheckMgr::GetWardenResultById(uint16 Id) { CheckResultContainer::const_iterator itr = CheckResultStore.find(Id); if (itr != CheckResultStore.end()) - return itr->second; + { + return &itr->second; + } + return nullptr; } + diff --git a/src/server/game/Warden/WardenCheckMgr.h b/src/server/game/Warden/WardenCheckMgr.h index 5b55770dd..a1af675fe 100644 --- a/src/server/game/Warden/WardenCheckMgr.h +++ b/src/server/game/Warden/WardenCheckMgr.h @@ -12,11 +12,22 @@ enum WardenActions { - WARDEN_ACTION_LOG, - WARDEN_ACTION_KICK, - WARDEN_ACTION_BAN + WARDEN_ACTION_LOG = 0, + WARDEN_ACTION_KICK = 1, + WARDEN_ACTION_BAN = 2, }; +constexpr uint8 MAX_WARDEN_ACTION = 3; + +enum WardenCheckTypes +{ + WARDEN_CHECK_MEM_TYPE = 0, + WARDEN_CHECK_LUA_TYPE = 1, + WARDEN_CHECK_OTHER_TYPE = 2, +}; + +constexpr uint8 MAX_WARDEN_CHECK_TYPES = 3; + struct WardenCheck { uint8 Type; @@ -26,9 +37,12 @@ struct WardenCheck std::string Str; // LUA, MPQ, DRIVER std::string Comment; uint16 CheckId; - enum WardenActions Action; + std::array IdStr = {}; // LUA + uint32 Action; }; +constexpr uint8 WARDEN_MAX_LUA_CHECK_LENGTH = 170; + struct WardenCheckResult { BigNumber Result; // MEM_CHECK @@ -43,23 +57,21 @@ public: static WardenCheckMgr* instance(); // We have a linear key without any gaps, so we use vector for fast access - typedef std::vector CheckContainer; - typedef std::map CheckResultContainer; + typedef std::vector CheckContainer; + typedef std::map CheckResultContainer; - WardenCheck* GetWardenDataById(uint16 Id); - WardenCheckResult* GetWardenResultById(uint16 Id); + uint16 GetMaxValidCheckId() const { return static_cast(CheckStore.size()); } + WardenCheck const* GetWardenDataById(uint16 Id); + WardenCheckResult const* GetWardenResultById(uint16 Id); - std::vector MemChecksIdPool; - std::vector OtherChecksIdPool; + std::vector CheckIdPool[MAX_WARDEN_CHECK_TYPES]; void LoadWardenChecks(); void LoadWardenOverrides(); - ACE_RW_Mutex _checkStoreLock; - private: - CheckContainer CheckStore; - CheckResultContainer CheckResultStore; + std::vector CheckStore; + std::map CheckResultStore; }; #define sWardenCheckMgr WardenCheckMgr::instance() diff --git a/src/server/game/Warden/WardenMac.cpp b/src/server/game/Warden/WardenMac.cpp index 120684e4c..d1fb4fb13 100644 --- a/src/server/game/Warden/WardenMac.cpp +++ b/src/server/game/Warden/WardenMac.cpp @@ -166,7 +166,7 @@ void WardenMac::HandleHashResult(ByteBuffer& buff) #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request hash reply: failed"); #endif - Penalty(); + ApplyPenalty(0, "Request hash reply: failed"); return; } @@ -188,11 +188,9 @@ void WardenMac::HandleHashResult(ByteBuffer& buff) _outputCrypto.Init(_outputKey); _initialized = true; - - _previousTimestamp = World::GetGameTimeMS(); } -void WardenMac::RequestData() +void WardenMac::RequestChecks() { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request data"); diff --git a/src/server/game/Warden/WardenMac.h b/src/server/game/Warden/WardenMac.h index fda46ee9f..99415b9cc 100644 --- a/src/server/game/Warden/WardenMac.h +++ b/src/server/game/Warden/WardenMac.h @@ -27,7 +27,7 @@ public: void InitializeModule() override; void RequestHash() override; void HandleHashResult(ByteBuffer& buff) override; - void RequestData() override; + void RequestChecks() override; void HandleData(ByteBuffer& buff) override; }; diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp index 7f56afa22..5a9a2843b 100644 --- a/src/server/game/Warden/WardenWin.cpp +++ b/src/server/game/Warden/WardenWin.cpp @@ -22,23 +22,72 @@ #include "WardenCheckMgr.h" #include "AccountMgr.h" +// GUILD is the shortest string that has no client validation (RAID only sends if in a raid group) +static constexpr char _luaEvalPrefix[] = "local S,T,R=SendAddonMessage,function()"; +static constexpr char _luaEvalMidfix[] = " end R=S and T()if R then S('_TW',"; +static constexpr char _luaEvalPostfix[] = ",'GUILD')end"; + +static_assert((sizeof(_luaEvalPrefix)-1 + sizeof(_luaEvalMidfix)-1 + sizeof(_luaEvalPostfix)-1 + WARDEN_MAX_LUA_CHECK_LENGTH) == 255); + +static constexpr uint8 GetCheckPacketBaseSize(uint8 type) +{ + switch (type) + { + case DRIVER_CHECK: + case MPQ_CHECK: return 1; + case LUA_EVAL_CHECK: return 1 + sizeof(_luaEvalPrefix) - 1 + sizeof(_luaEvalMidfix) - 1 + 4 + sizeof(_luaEvalPostfix) - 1; + case PAGE_CHECK_A: return (4 + 1); + case PAGE_CHECK_B: return (4 + 1); + case MODULE_CHECK: return (4 + SHA_DIGEST_LENGTH); + case MEM_CHECK: return (1 + 4 + 1); + default: return 0; + } +} + +static uint16 GetCheckPacketSize(WardenCheck const* check) +{ + if (!check) + { + return 0; + } + + uint16 size = 1 + GetCheckPacketBaseSize(check->Type); // 1 byte check type + if (!check->Str.empty()) + { + size += (static_cast(check->Str.length()) + 1); // 1 byte string length + } + + BigNumber tempNumber = check->Data; + if (!tempNumber.GetNumBytes()) + { + size += tempNumber.GetNumBytes(); + } + return size; +} + +// Returns config id for specific type id +static WorldIntConfigs GetMaxWardenChecksForType(uint8 type) +{ + // Should never be higher type than defined + ASSERT(type < MAX_WARDEN_CHECK_TYPES); + + switch (type) + { + case WARDEN_CHECK_MEM_TYPE: + return CONFIG_WARDEN_NUM_MEM_CHECKS; + case WARDEN_CHECK_LUA_TYPE: + return CONFIG_WARDEN_NUM_LUA_CHECKS; + default: + break; + } + + return CONFIG_WARDEN_NUM_OTHER_CHECKS; +} + WardenWin::WardenWin() : Warden(), _serverTicks(0) { } WardenWin::~WardenWin() { - // Xinef: ZOMG! CRASH DEBUG INFO - uint32 otherSize = _otherChecksTodo.size(); - uint32 memSize = _memChecksTodo.size(); - uint32 curSize = _currentChecks.size(); - bool otherClear = _otherChecksTodo.empty(); - bool memClear = _memChecksTodo.empty(); - bool curClear = _currentChecks.empty(); - - sLog->outDebug(LOG_FILTER_POOLSYS, "IM DESTRUCTING MYSELF QQ, OTHERSIZE: %u, OTHEREM: %u, MEMSIZE: %u, MEMEM: %u, CURSIZE: %u, CUREM: %u!\n", otherSize, otherClear, memSize, memClear, curSize, curClear); - _otherChecksTodo.clear(); - _memChecksTodo.clear(); - _currentChecks.clear(); - sLog->outDebug(LOG_FILTER_POOLSYS, "IM DESTRUCTING MYSELF QQ, OTHERSIZE: %u, OTHEREM: %u, MEMSIZE: %u, MEMEM: %u, CURSIZE: %u, CUREM: %u!\n", otherSize, otherClear, memSize, memClear, curSize, curClear); } void WardenWin::Init(WorldSession* session, BigNumber* k) @@ -109,16 +158,16 @@ void WardenWin::InitializeModule() Request.Function1[1] = 0x000218C0; // 0x00400000 + 0x000218C0 SFileGetFileSize Request.Function1[2] = 0x00022530; // 0x00400000 + 0x00022530 SFileReadFile Request.Function1[3] = 0x00022910; // 0x00400000 + 0x00022910 SFileCloseFile - Request.CheckSumm1 = BuildChecksum(&Request.Unk1, 20); + Request.CheckSumm1 = BuildChecksum(&Request.Unk1, SHA_DIGEST_LENGTH); Request.Command2 = WARDEN_SMSG_MODULE_INITIALIZE; Request.Size2 = 8; Request.Unk3 = 4; Request.Unk4 = 0; Request.String_library2 = 0; - Request.Function2 = 0x00419D40; // 0x00400000 + 0x00419D40 FrameScript::GetText + Request.Function2 = 0x00419210; // 0x00400000 + 0x00419210 FrameScript::Execute Request.Function2_set = 1; - Request.CheckSumm2 = BuildChecksum(&Request.Unk2, 8); + Request.CheckSumm2 = BuildChecksum(&Request.Unk3, 8); Request.Command3 = WARDEN_SMSG_MODULE_INITIALIZE; Request.Size3 = 8; @@ -129,11 +178,24 @@ void WardenWin::InitializeModule() Request.Function3_set = 1; Request.CheckSumm3 = BuildChecksum(&Request.Unk5, 8); + EndianConvert(Request.Size1); + EndianConvert(Request.CheckSumm1); + EndianConvert(Request.Function1[0]); + EndianConvert(Request.Function1[1]); + EndianConvert(Request.Function1[2]); + EndianConvert(Request.Function1[3]); + EndianConvert(Request.Size2); + EndianConvert(Request.CheckSumm2); + EndianConvert(Request.Function2); + EndianConvert(Request.Size3); + EndianConvert(Request.CheckSumm3); + EndianConvert(Request.Function3); + // Encrypt with warden RC4 key. - EncryptData((uint8*)&Request, sizeof(WardenInitModuleRequest)); + EncryptData(reinterpret_cast(&Request), sizeof(WardenInitModuleRequest)); WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenInitModuleRequest)); - pkt.append((uint8*)&Request, sizeof(WardenInitModuleRequest)); + pkt.append(reinterpret_cast(&Request), sizeof(WardenInitModuleRequest)); _session->SendPacket(&pkt); } @@ -149,10 +211,10 @@ void WardenWin::RequestHash() memcpy(Request.Seed, _seed, 16); // Encrypt with warden RC4 key. - EncryptData((uint8*)&Request, sizeof(WardenHashRequest)); + EncryptData(reinterpret_cast(&Request), sizeof(WardenHashRequest)); WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenHashRequest)); - pkt.append((uint8*)&Request, sizeof(WardenHashRequest)); + pkt.append(reinterpret_cast(&Request), sizeof(WardenHashRequest)); _session->SendPacket(&pkt); } @@ -161,12 +223,12 @@ void WardenWin::HandleHashResult(ByteBuffer& buff) buff.rpos(buff.wpos()); // Verify key - if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, 20) != 0) + if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, SHA_DIGEST_LENGTH) != 0) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request hash reply: failed"); #endif - Penalty(); + ApplyPenalty(0, "Request hash reply: failed"); return; } @@ -182,83 +244,135 @@ void WardenWin::HandleHashResult(ByteBuffer& buff) _outputCrypto.Init(_outputKey); _initialized = true; - - _previousTimestamp = World::GetGameTimeMS(); } -void WardenWin::RequestData() +void WardenWin::RequestChecks() { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request data"); #endif // If all checks were done, fill the todo list again - if (_memChecksTodo.empty()) - _memChecksTodo.assign(sWardenCheckMgr->MemChecksIdPool.begin(), sWardenCheckMgr->MemChecksIdPool.end()); - - if (_otherChecksTodo.empty()) - _otherChecksTodo.assign(sWardenCheckMgr->OtherChecksIdPool.begin(), sWardenCheckMgr->OtherChecksIdPool.end()); + for (uint8 i = 0; i < MAX_WARDEN_CHECK_TYPES; ++i) + { + if (_ChecksTodo[i].empty()) + _ChecksTodo[i].assign(sWardenCheckMgr->CheckIdPool[i].begin(), sWardenCheckMgr->CheckIdPool[i].end()); + } _serverTicks = World::GetGameTimeMS(); + _CurrentChecks.clear(); - uint16 id; - uint8 type; - WardenCheck* wd; - _currentChecks.clear(); - - // Build check request - for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_MEM_CHECKS); ++i) + // No pending checks + if (_PendingChecks.empty()) { - // If todo list is done break loop (will be filled on next Update() run) - if (_memChecksTodo.empty()) - break; + for (uint8 checkType = 0; checkType < MAX_WARDEN_CHECK_TYPES; ++checkType) + { + for (uint32 y = 0; y < sWorld->getIntConfig(GetMaxWardenChecksForType(checkType)); ++y) + { + // If todo list is done break loop (will be filled on next Update() run) + if (_ChecksTodo[checkType].empty()) + { + break; + } - // Get check id from the end and remove it from todo - id = _memChecksTodo.back(); - _memChecksTodo.pop_back(); + // Get check id from the end and remove it from todo + uint16 const id = _ChecksTodo[checkType].back(); + _ChecksTodo[checkType].pop_back(); - // Add the id to the list sent in this cycle - if (id != 786 /*WPE PRO*/ && id != 209 /*WoWEmuHacker*/) - _currentChecks.push_back(id); + // Insert check to queue + if (checkType == WARDEN_CHECK_LUA_TYPE) + { + _CurrentChecks.push_front(id); + } + else + { + _CurrentChecks.push_back(id); + } + } + } } - _currentChecks.push_back(786); - _currentChecks.push_back(209); + else + { + bool hasLuaChecks = false; + for (uint16 const checkId : _PendingChecks) + { + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + if (!hasLuaChecks && check->Type == LUA_EVAL_CHECK) + { + hasLuaChecks = true; + } + + _CurrentChecks.push_back(checkId); + } + + // Always include lua checks + if (!hasLuaChecks) + { + for (uint32 i = 0; i < sWorld->getIntConfig(GetMaxWardenChecksForType(WARDEN_CHECK_LUA_TYPE)); ++i) + { + // If todo list is done break loop (will be filled on next Update() run) + if (_ChecksTodo[WARDEN_CHECK_LUA_TYPE].empty()) + { + break; + } + + // Get check id from the end and remove it from todo + uint16 const id = _ChecksTodo[WARDEN_CHECK_LUA_TYPE].back(); + _ChecksTodo[WARDEN_CHECK_LUA_TYPE].pop_back(); + + // Lua checks must be always in front + _CurrentChecks.push_front(id); + } + } + } + + // Filter too high checks queue + // Filtered checks will get passed in next checks + uint16 expectedSize = 4; + _PendingChecks.clear(); + acore::Containers::EraseIf(_CurrentChecks, + [this, &expectedSize](uint16 id) + { + uint16 const thisSize = GetCheckPacketSize(sWardenCheckMgr->GetWardenDataById(id)); + if ((expectedSize + thisSize) > 500) // warden packets are truncated to 512 bytes clientside + { + _PendingChecks.push_back(id); + return true; + } + expectedSize += thisSize; + return false; + } + ); ByteBuffer buff; buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST); - ACE_READ_GUARD(ACE_RW_Mutex, g, sWardenCheckMgr->_checkStoreLock); - - for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_OTHER_CHECKS); ++i) + for (uint16 const checkId : _CurrentChecks) { - // If todo list is done break loop (will be filled on next Update() run) - if (_otherChecksTodo.empty()) - break; - - // Get check id from the end and remove it from todo - id = _otherChecksTodo.back(); - _otherChecksTodo.pop_back(); - - // Add the id to the list sent in this cycle - _currentChecks.push_back(id); - - wd = sWardenCheckMgr->GetWardenDataById(id); - - if (wd) - switch (wd->Type) + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + switch (check->Type) + { + case LUA_EVAL_CHECK: { - case MPQ_CHECK: - case LUA_STR_CHECK: - case DRIVER_CHECK: - buff << uint8(wd->Str.size()); - buff.append(wd->Str.c_str(), wd->Str.size()); - break; - default: - break; + buff << uint8(sizeof(_luaEvalPrefix) - 1 + check->Str.size() + sizeof(_luaEvalMidfix) - 1 + check->IdStr.size() + sizeof(_luaEvalPostfix) - 1); + buff.append(_luaEvalPrefix, sizeof(_luaEvalPrefix) - 1); + buff.append(check->Str.data(), check->Str.size()); + buff.append(_luaEvalMidfix, sizeof(_luaEvalMidfix) - 1); + buff.append(check->IdStr.data(), check->IdStr.size()); + buff.append(_luaEvalPostfix, sizeof(_luaEvalPostfix) - 1); + break; } + case MPQ_CHECK: + case DRIVER_CHECK: + { + buff << uint8(check->Str.size()); + buff.append(check->Str.c_str(), check->Str.size()); + break; + } + } } - uint8 xorByte = _inputKey[0]; + uint8 const xorByte = _inputKey[0]; // Add TIMING_CHECK buff << uint8(0x00); @@ -266,51 +380,51 @@ void WardenWin::RequestData() uint8 index = 1; - for (std::list::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) + for (uint16 const checkId : _CurrentChecks) { - wd = sWardenCheckMgr->GetWardenDataById(*itr); - - type = wd->Type; - buff << uint8(type ^ xorByte); - switch (type) + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + buff << uint8(check->Type ^ xorByte); + switch (check->Type) { case MEM_CHECK: - { - buff << uint8(0x00); - buff << uint32(wd->Address); - buff << uint8(wd->Length); - break; - } + { + buff << uint8(0x00); + buff << uint32(check->Address); + buff << uint8(check->Length); + break; + } case PAGE_CHECK_A: case PAGE_CHECK_B: - { - buff.append(wd->Data.AsByteArray(0, false).get(), wd->Data.GetNumBytes()); - buff << uint32(wd->Address); - buff << uint8(wd->Length); - break; - } + { + BigNumber tempNumber = check->Data; + buff.append(tempNumber.AsByteArray(0, false).get(), tempNumber.GetNumBytes()); + buff << uint32(check->Address); + buff << uint8(check->Length); + break; + } case MPQ_CHECK: - case LUA_STR_CHECK: - { - buff << uint8(index++); - break; - } + case LUA_EVAL_CHECK: + { + buff << uint8(index++); + break; + } case DRIVER_CHECK: - { - buff.append(wd->Data.AsByteArray(0, false).get(), wd->Data.GetNumBytes()); - buff << uint8(index++); - break; - } + { + BigNumber tempNumber = check->Data; + buff.append(tempNumber.AsByteArray(0, false).get(), tempNumber.GetNumBytes()); + buff << uint8(index++); + break; + } case MODULE_CHECK: - { - uint32 seed = rand32(); - buff << uint32(seed); - HmacHash hmac(4, (uint8*)&seed); - hmac.UpdateData(wd->Str); - hmac.Finalize(); - buff.append(hmac.GetDigest(), hmac.GetLength()); - break; - } + { + uint32 seed = rand32(); + buff << uint32(seed); + HmacHash hmac(4, (uint8*)&seed); + hmac.UpdateData(check->Str); + hmac.Finalize(); + buff.append(hmac.GetDigest(), hmac.GetLength()); + break; + } /*case PROC_CHECK: { buff.append(wd->i.AsByteArray(0, false).get(), wd->i.GetNumBytes()); @@ -320,8 +434,6 @@ void WardenWin::RequestData() buff << uint8(wd->Length); break; }*/ - default: - break; // Should never happen } } buff << uint8(xorByte); @@ -336,12 +448,14 @@ void WardenWin::RequestData() _dataSent = true; +#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) std::stringstream stream; stream << "Sent check id's: "; - for (std::list::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) - stream << *itr << " "; + for (uint16 checkId : _currentChecks) + { + stream << checkId << " "; + } -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "%s", stream.str().c_str()); #endif } @@ -360,13 +474,20 @@ void WardenWin::HandleData(ByteBuffer& buff) uint32 Checksum; buff >> Checksum; + if (Length != (buff.size() - buff.rpos())) + { + buff.rfinish(); + ApplyPenalty(0, "Failed size checks in HandleData"); + return; + } + if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length)) { buff.rpos(buff.wpos()); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "CHECKSUM FAIL"); #endif - Penalty(); + ApplyPenalty(0, "Failed checksum in HandleData"); return; } @@ -380,7 +501,7 @@ void WardenWin::HandleData(ByteBuffer& buff) #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "TIMING CHECK FAIL result 0x00"); #endif - Penalty(); + ApplyPenalty(0, "TIMING CHECK FAIL result"); return; } @@ -398,51 +519,46 @@ void WardenWin::HandleData(ByteBuffer& buff) #endif } - WardenCheckResult* rs; - WardenCheck* rd; - uint8 type; uint16 checkFailed = 0; - ACE_READ_GUARD(ACE_RW_Mutex, g, sWardenCheckMgr->_checkStoreLock); - - for (std::list::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr) + for (uint16 const checkId : _CurrentChecks) { - rd = sWardenCheckMgr->GetWardenDataById(*itr); - rs = sWardenCheckMgr->GetWardenResultById(*itr); - - type = rd->Type; + WardenCheck const* rd = sWardenCheckMgr->GetWardenDataById(checkId); + uint8 const type = rd->Type; switch (type) { case MEM_CHECK: + { + uint8 Mem_Result; + buff >> Mem_Result; + + if (Mem_Result != 0) { - uint8 Mem_Result; - buff >> Mem_Result; - - if (Mem_Result != 0) - { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif - checkFailed = *itr; - continue; - } - - if (memcmp(buff.contents() + buff.rpos(), rs->Result.AsByteArray(0, false).get(), rd->Length) != 0) - { -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK fail CheckId %u account Id %u", *itr, _session->GetAccountId()); -#endif - checkFailed = *itr; - buff.rpos(buff.rpos() + rd->Length); - continue; - } - - buff.rpos(buff.rpos() + rd->Length); -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); -#endif - break; + checkFailed = checkId; + continue; } + + WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); + BigNumber tempNumber = rs->Result; + if (memcmp(buff.contents() + buff.rpos(), tempNumber.AsByteArray(0, false).get(), rd->Length) != 0) + { +#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK fail CheckId %u account Id %u", checkId, _session->GetAccountId()); +#endif + checkFailed = checkId; + buff.rpos(buff.rpos() + rd->Length); + continue; + } + + buff.rpos(buff.rpos() + rd->Length); +#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK passed CheckId %u account Id %u", checkId, _session->GetAccountId()); +#endif + break; + } case PAGE_CHECK_A: case PAGE_CHECK_B: case DRIVER_CHECK: @@ -453,15 +569,15 @@ void WardenWin::HandleData(ByteBuffer& buff) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT PAGE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT PAGE_CHECK fail, CheckId %u account Id %u", checkId, _session->GetAccountId()); if (type == MODULE_CHECK) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MODULE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MODULE_CHECK fail, CheckId %u account Id %u", checkId, _session->GetAccountId()); if (type == DRIVER_CHECK) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT DRIVER_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT DRIVER_CHECK fail, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif - checkFailed = *itr; + checkFailed = checkId; buff.rpos(buff.rpos() + 1); continue; } @@ -470,44 +586,24 @@ void WardenWin::HandleData(ByteBuffer& buff) #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT PAGE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT PAGE_CHECK passed CheckId %u account Id %u", checkId, _session->GetAccountId()); else if (type == MODULE_CHECK) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MODULE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MODULE_CHECK passed CheckId %u account Id %u", checkId, _session->GetAccountId()); else if (type == DRIVER_CHECK) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT DRIVER_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT DRIVER_CHECK passed CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif - break; - } - case LUA_STR_CHECK: + break; + } + case LUA_EVAL_CHECK: + { + uint8 const result = buff.read(); + if (result == 0) { - uint8 Lua_Result; - buff >> Lua_Result; + buff.read_skip(buff.read()); // discard attached string + } - if (Lua_Result != 0) - { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); -#endif - checkFailed = *itr; - continue; - } - - uint8 luaStrLen; - buff >> luaStrLen; - - if (luaStrLen != 0) - { - char* str = new char[luaStrLen + 1]; - memcpy(str, buff.contents() + buff.rpos(), luaStrLen); - str[luaStrLen] = '\0'; // null terminator -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "Lua string: %s", str); -#endif - delete[] str; - } - buff.rpos(buff.rpos() + luaStrLen); // Skip string -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "LUA_EVAL_CHECK CheckId %u account Id %u got in-warden dummy response", checkId, _session->GetAccountId()/* , result */); #endif break; } @@ -521,38 +617,37 @@ void WardenWin::HandleData(ByteBuffer& buff) #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK not 0x00 account id %u", _session->GetAccountId()); #endif - checkFailed = *itr; + checkFailed = checkId; continue; } - if (memcmp(buff.contents() + buff.rpos(), rs->Result.AsByteArray(0, false).get(), 20) != 0) // SHA1 + WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); + BigNumber tempNumber = rs->Result; + if (memcmp(buff.contents() + buff.rpos(), tempNumber.AsByteArray(0, false).get(), SHA_DIGEST_LENGTH) != 0) // SHA1 { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK fail, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif - checkFailed = *itr; - buff.rpos(buff.rpos() + 20); // 20 bytes SHA1 + checkFailed = checkId; + buff.rpos(buff.rpos() + SHA_DIGEST_LENGTH); // 20 bytes SHA1 continue; } - buff.rpos(buff.rpos() + 20); // 20 bytes SHA1 + buff.rpos(buff.rpos() + SHA_DIGEST_LENGTH); // 20 bytes SHA1 #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId()); + sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK passed, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif break; } - default: // Should never happen - break; } } if (checkFailed > 0) { - WardenCheck* check = sWardenCheckMgr->GetWardenDataById(checkFailed); - Penalty(check, checkFailed); + ApplyPenalty(checkFailed, ""); } // Set hold off timer, minimum timer should at least be 1 second - uint32 holdOff = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF); + uint32 const holdOff = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF); _checkTimer = (holdOff < 1 ? 1 : holdOff) * IN_MILLISECONDS; } diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h index f0f94c82f..1546018bd 100644 --- a/src/server/game/Warden/WardenWin.h +++ b/src/server/game/Warden/WardenWin.h @@ -69,14 +69,15 @@ public: void InitializeModule() override; void RequestHash() override; void HandleHashResult(ByteBuffer& buff) override; - void RequestData() override; + void RequestChecks() override; void HandleData(ByteBuffer& buff) override; private: uint32 _serverTicks; - std::list _otherChecksTodo; - std::list _memChecksTodo; - std::list _currentChecks; + std::list _ChecksTodo[MAX_WARDEN_CHECK_TYPES]; + + std::list _CurrentChecks; + std::list _PendingChecks; }; -#endif +#endif // _WARDEN_WIN_H diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index f359f3484..4c035569e 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -340,6 +340,7 @@ enum WorldIntConfigs CONFIG_WARDEN_CLIENT_FAIL_ACTION, CONFIG_WARDEN_CLIENT_BAN_DURATION, CONFIG_WARDEN_NUM_MEM_CHECKS, + CONFIG_WARDEN_NUM_LUA_CHECKS, CONFIG_WARDEN_NUM_OTHER_CHECKS, CONFIG_BIRTHDAY_TIME, CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 7fbb76d9d..7940b0302 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1315,6 +1315,7 @@ void World::LoadConfigSettings(bool reload) // Warden m_bool_configs[CONFIG_WARDEN_ENABLED] = sConfigMgr->GetBoolDefault("Warden.Enabled", false); m_int_configs[CONFIG_WARDEN_NUM_MEM_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumMemChecks", 3); + m_int_configs[CONFIG_WARDEN_NUM_LUA_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumLuaChecks", 1); m_int_configs[CONFIG_WARDEN_NUM_OTHER_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumOtherChecks", 7); m_int_configs[CONFIG_WARDEN_CLIENT_BAN_DURATION] = sConfigMgr->GetIntDefault("Warden.BanDuration", 86400); m_int_configs[CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF] = sConfigMgr->GetIntDefault("Warden.ClientCheckHoldOff", 30); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 80fdc19cf..2e223429a 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1590,7 +1590,7 @@ IsPreloadedContinentTransport.Enabled = 0 # Default: 0 - (Disabled) # 1 - (Enabled) -Warden.Enabled = 1 +Warden.Enabled = 0 # # Warden.NumMemChecks @@ -1598,7 +1598,15 @@ Warden.Enabled = 1 # Default: 3 - (Enabled) # 0 - (Disabled) -Warden.NumMemChecks = 2 +Warden.NumMemChecks = 3 + +# +# Warden.NumLuaChecks +# Description: Number of Warden LUA checks that are sent to the client each cycle. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Warden.NumLuaChecks = 1 # # Warden.NumOtherChecks @@ -1607,7 +1615,7 @@ Warden.NumMemChecks = 2 # Default: 7 - (Enabled) # 0 - (Disabled) -Warden.NumOtherChecks = 5 +Warden.NumOtherChecks = 7 # # Warden.LogFile @@ -1644,7 +1652,7 @@ Warden.ClientCheckHoldOff = 30 # 1 - (Kick) # 2 - (Ban) -Warden.ClientCheckFailAction = 2 +Warden.ClientCheckFailAction = 0 # # Warden.BanDuration