Core/Warden: Warden refactors (PR #25235)

This commit is contained in:
Treeston
2020-08-11 18:04:36 +02:00
committed by GitHub
parent cce1ccdd5c
commit 6116e5b385
19 changed files with 515 additions and 461 deletions

View File

@@ -19,7 +19,7 @@ notice = ('''/*
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
''' % datetime.now().year)
''')
if not getcwd().endswith('src'):
print('Run this from the src directory!')

View File

@@ -842,7 +842,6 @@ INSERT INTO `rbac_linked_permissions` VALUES
(196,702),
(196,703),
(196,704),
(196,705),
(196,706),
(196,707),
(196,708),
@@ -1665,7 +1664,6 @@ INSERT INTO `rbac_permissions` VALUES
(702,'Command: reload spell_threats'),
(703,'Command: reload spell_group_stack_rules'),
(704,'Command: reload trinity_string'),
(705,'Command: reload warden_action'),
(706,'Command: reload waypoint_scripts'),
(707,'Command: reload waypoint_data'),
(708,'Command: reload vehicle_accessory'),
@@ -1972,7 +1970,8 @@ INSERT INTO `updates` VALUES
('2020_07_15_00_auth.sql','56748440894EA78C3BE72C4A3F2E97E256E6EE40','RELEASED','2020-07-15 00:00:00',0),
('2020_08_02_00_auth.sql','B0290F6558C59262D9DDD8071060A8803DD56930','RELEASED','2020-08-02 00:00:00',0),
('2020_08_03_00_auth.sql','492CA77C0FAEEEF3E0492121B3A92689373ECFA3','RELEASED','2020-08-03 00:00:00',0),
('2020_08_03_01_auth.sql','EC1063396CA20A2303D83238470D41EF4439EC72','RELEASED','2020-08-03 00:00:01',0);
('2020_08_03_01_auth.sql','EC1063396CA20A2303D83238470D41EF4439EC72','RELEASED','2020-08-03 00:00:01',0),
('2020_08_11_00_auth.sql','14C99177E43003D83A4D6F2227722F15FC15A1D0','RELEASED','2020-08-11 00:00:00',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;

View File

@@ -0,0 +1,5 @@
--
DELETE FROM `rbac_permissions` WHERE `id`=705;
DELETE FROM `rbac_linked_permissions` WHERE `linkedId`=705;
DELETE FROM `rbac_account_permissions` WHERE `permissionId`=705;
DELETE FROM `rbac_default_permissions` WHERE `permissionId`=705;

View File

@@ -0,0 +1,15 @@
--
ALTER TABLE `warden_checks`
CHANGE COLUMN `data` `oldData` VARCHAR(48),
CHANGE COLUMN `result` `oldResult` VARCHAR(24),
ADD COLUMN `data` BINARY(24) DEFAULT NULL,
ADD COLUMN `result` VARBINARY(12) DEFAULT NULL;
UPDATE `warden_checks` SET `data`=UNHEX(`oldData`) WHERE `type` IN (113,178,191);
UPDATE `warden_checks` SET `result`=UNHEX(`oldResult`) WHERE `type` IN (152,243);
ALTER TABLE `warden_checks`
DROP COLUMN `oldData`,
DROP COLUMN `oldResult`;
DELETE FROM `command` WHERE `name`='reload warden_action';

View File

@@ -330,6 +330,15 @@ std::array<uint8, Size> HexStrToByteArray(std::string const& str, bool reverse =
return arr;
}
inline std::vector<uint8> HexStrToByteVector(std::string const& str, bool reverse = false)
{
std::vector<uint8> buf;
size_t const sz = (str.size() / 2);
buf.resize(sz);
Trinity::Impl::HexStrToByteArray(str, buf.data(), sz, reverse);
return buf;
}
TC_COMMON_API bool StringToBool(std::string const& str);
TC_COMMON_API bool StringContainsStringI(std::string const& haystack, std::string const& needle);

View File

@@ -606,7 +606,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS = 702,
RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES = 703,
RBAC_PERM_COMMAND_RELOAD_TRINITY_STRING = 704,
RBAC_PERM_COMMAND_RELOAD_WARDEN_ACTION = 705,
// 705 unused
RBAC_PERM_COMMAND_RELOAD_WAYPOINT_SCRIPTS = 706,
RBAC_PERM_COMMAND_RELOAD_WAYPOINT_DATA = 707,
RBAC_PERM_COMMAND_RELOAD_VEHICLE_ACCESORY = 708,

View File

@@ -422,7 +422,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (m_Socket && m_Socket->IsOpen() && _warden)
_warden->Update();
_warden->Update(diff);
if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter
{
@@ -446,12 +446,12 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
if (ShouldLogOut(currentTime) && !m_playerLoading)
LogoutPlayer(true);
if (m_Socket && GetPlayer() && _warden)
_warden->Update();
///- Cleanup socket pointer if need
if (m_Socket && !m_Socket->IsOpen())
{
if (GetPlayer() && _warden)
_warden->Update(diff);
expireTime -= expireTime > diff ? diff : expireTime;
if (expireTime < diff || forceExit || !GetPlayer())
{

View File

@@ -18,7 +18,7 @@
#ifndef _WARDEN_MODULE_MAC_H
#define _WARDEN_MODULE_MAC_H
uint8 Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data[9318] =
std::array<uint8, 9318> Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data =
{
0x07, 0x0C, 0x44, 0xCD, 0xC9, 0xFB, 0x99, 0xBC, 0x7C, 0x77, 0xDC, 0xE8, 0x8D, 0x07, 0xBE, 0x55,
0x37, 0x5C, 0x84, 0x10, 0x23, 0xE1, 0x36, 0x5B, 0xF1, 0xBC, 0x60, 0xF3, 0x68, 0xBA, 0x60, 0x69,

View File

@@ -18,22 +18,25 @@
#ifndef _WARDEN_MODULE_WIN_H
#define _WARDEN_MODULE_WIN_H
#include "CryptoHash.h"
#include <array>
/*
Seed: 4D808D2C77D905C41A6380EC08586AFE (0x05 packet)
Hash: 568C054C781A972A6037A2290C22B52571A06F4E (0x04 packet)
Module MD5: 79C0768D657977D697E10BAD956CCED1
New Client Key: 7F 96 EE FD A5 B6 3D 20 A4 DF 8E 00 CB F4 83 04
New Cerver Key: C2 B7 AD ED FC CC A9 C2 BF B3 F8 56 02 BA 80 9B
New Server Key: C2 B7 AD ED FC CC A9 C2 BF B3 F8 56 02 BA 80 9B
*/
struct Module_79C0768D657977D697E10BAD956CCED1
{
uint8 Module[18756];
uint8 ModuleKey[16];
uint8 Seed[16];
uint8 ServerKeySeed[16];
uint8 ClientKeySeed[16];
uint8 ClientKeySeedHash[20];
std::array<uint8, 18756> Module;
std::array<uint8, 16> ModuleKey;
std::array<uint8, 16> Seed;
std::array<uint8, 16> ServerKeySeed;
std::array<uint8, 16> ClientKeySeed;
Trinity::Crypto::SHA1::Digest ClientKeySeedHash;
} Module =
{
{

View File

@@ -28,23 +28,29 @@
#include "AccountMgr.h"
#include <openssl/sha.h>
#include <openssl/md5.h>
Warden::Warden() : _session(nullptr), _checkTimer(10000/*10 sec*/), _clientResponseTimer(0),
_dataSent(false), _previousTimestamp(0), _module(nullptr), _initialized(false)
Warden::Warden() : _session(nullptr), _checkTimer(10 * IN_MILLISECONDS), _clientResponseTimer(0),
_dataSent(false), _initialized(false)
{
memset(_inputKey, 0, sizeof(_inputKey));
memset(_outputKey, 0, sizeof(_outputKey));
memset(_seed, 0, sizeof(_seed));
}
Warden::~Warden()
{
delete[] _module->CompressedData;
delete _module;
_module = nullptr;
_initialized = false;
}
void Warden::MakeModuleForClient()
{
TC_LOG_DEBUG("warden", "Make module for client");
InitializeModuleForClient(_module.emplace());
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, _module->CompressedData, _module->CompressedSize);
MD5_Final(_module->Id.data(), &ctx);
}
void Warden::SendModuleToClient()
{
TC_LOG_DEBUG("warden", "Send module to client");
@@ -60,13 +66,15 @@ void Warden::SendModuleToClient()
burstSize = sizeLeft < 500 ? sizeLeft : 500;
packet.Command = WARDEN_SMSG_MODULE_CACHE;
packet.DataSize = burstSize;
memcpy(packet.Data, &_module->CompressedData[pos], burstSize);
memcpy(packet.Data, _module->CompressedData + pos, burstSize);
sizeLeft -= burstSize;
pos += burstSize;
EncryptData((uint8*)&packet, burstSize + 3);
EndianConvert(packet.DataSize);
EncryptData(reinterpret_cast<uint8*>(&packet), burstSize + 3);
WorldPacket pkt1(SMSG_WARDEN_DATA, burstSize + 3);
pkt1.append((uint8*)&packet, burstSize + 3);
pkt1.append(reinterpret_cast<uint8*>(&packet), burstSize + 3);
_session->SendPacket(&pkt1);
}
}
@@ -79,53 +87,49 @@ void Warden::RequestModule()
WardenModuleUse request;
request.Command = WARDEN_SMSG_MODULE_USE;
memcpy(request.ModuleId, _module->Id, 16);
memcpy(request.ModuleKey, _module->Key, 16);
request.ModuleId = _module->Id;
request.ModuleKey = _module->Key;
request.Size = _module->CompressedSize;
EndianConvert(request.Size);
// Encrypt with warden RC4 key.
EncryptData((uint8*)&request, sizeof(WardenModuleUse));
EncryptData(reinterpret_cast<uint8*>(&request), sizeof(WardenModuleUse));
WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenModuleUse));
pkt.append((uint8*)&request, sizeof(WardenModuleUse));
pkt.append(reinterpret_cast<uint8*>(&request), sizeof(WardenModuleUse));
_session->SendPacket(&pkt);
}
void Warden::Update()
void Warden::Update(uint32 diff)
{
if (_initialized)
if (!_initialized)
return;
if (_dataSent)
{
uint32 currentTimestamp = GameTime::GetGameTimeMS();
uint32 diff = currentTimestamp - _previousTimestamp;
_previousTimestamp = currentTimestamp;
uint32 maxClientResponseDelay = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY);
if (_dataSent)
if (maxClientResponseDelay > 0)
{
uint32 maxClientResponseDelay = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY);
if (maxClientResponseDelay > 0)
// Kick player if client response delays more than set in config
if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS)
{
// Kick player if client response delays more than set in config
if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS)
{
TC_LOG_WARN("warden", "%s (latency: %u, IP: %s) exceeded Warden module response delay for more than %s - disconnecting client",
_session->GetPlayerInfo().c_str(), _session->GetLatency(), _session->GetRemoteAddress().c_str(), secsToTimeString(maxClientResponseDelay, TimeFormat::ShortText).c_str());
_session->KickPlayer("Warden::Update Warden module response delay exceeded");
}
else
_clientResponseTimer += diff;
}
}
else
{
if (diff >= _checkTimer)
{
RequestData();
TC_LOG_WARN("warden", "%s (latency: %u, IP: %s) exceeded Warden module response delay (%s) - disconnecting client",
_session->GetPlayerInfo().c_str(), _session->GetLatency(), _session->GetRemoteAddress().c_str(), secsToTimeString(maxClientResponseDelay, TimeFormat::ShortText).c_str());
_session->KickPlayer("Warden::Update Warden module response delay exceeded");
}
else
_checkTimer -= diff;
_clientResponseTimer += diff;
}
}
else
{
if (diff >= _checkTimer)
RequestChecks();
else
_checkTimer -= diff;
}
}
void Warden::DecryptData(uint8* buffer, uint32 length)
@@ -180,7 +184,7 @@ uint32 Warden::BuildChecksum(uint8 const* data, uint32 length)
return checkSum;
}
std::string Warden::Penalty(WardenCheck* check /*= nullptr*/)
char const* Warden::ApplyPenalty(WardenCheck const* check)
{
WardenActions action;
@@ -191,17 +195,11 @@ std::string Warden::Penalty(WardenCheck* check /*= nullptr*/)
switch (action)
{
case WARDEN_ACTION_LOG:
return "None";
break;
case WARDEN_ACTION_KICK:
_session->KickPlayer("Warden::Penalty");
return "Kick";
break;
case WARDEN_ACTION_BAN:
case WARDEN_ACTION_KICK:
_session->KickPlayer("Warden::Penalty");
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);
std::stringstream banReason;
@@ -210,14 +208,50 @@ std::string Warden::Penalty(WardenCheck* check /*= nullptr*/)
if (check)
banReason << ": " << check->Comment << " (CheckId: " << check->CheckId << ")";
sWorld->BanAccount(BAN_ACCOUNT, accountName, duration.str(), banReason.str(),"Server");
sWorld->BanAccount(BAN_ACCOUNT, accountName, sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_BAN_DURATION), banReason.str(),"Server");
return "Ban";
break;
}
case WARDEN_ACTION_LOG:
default:
return "None";
}
return EnumUtils::ToTitle(action);
}
void Warden::HandleData(ByteBuffer& buff)
{
DecryptData(buff.contents(), buff.size());
uint8 opcode;
buff >> opcode;
TC_LOG_DEBUG("warden", "Got packet, opcode %02X, size %u", opcode, uint32(buff.size() - 1));
buff.hexlike();
switch (opcode)
{
case WARDEN_CMSG_MODULE_MISSING:
SendModuleToClient();
break;
case WARDEN_CMSG_MODULE_OK:
RequestHash();
break;
case WARDEN_CMSG_CHEAT_CHECKS_RESULT:
HandleCheckResult(buff);
break;
case WARDEN_CMSG_MEM_CHECKS_RESULT:
TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MEM_CHECKS_RESULT received!");
break;
case WARDEN_CMSG_HASH_RESULT:
HandleHashResult(buff);
InitializeModule();
break;
case WARDEN_CMSG_MODULE_FAILED:
TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MODULE_FAILED received!");
break;
default:
TC_LOG_WARN("warden", "Got unknown warden opcode %02X of size %u.", opcode, uint32(buff.size() - 1));
break;
}
return "Undefined";
}
void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData)
@@ -225,35 +259,5 @@ void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData)
if (!_warden || recvData.empty())
return;
_warden->DecryptData(recvData.contents(), recvData.size());
uint8 opcode;
recvData >> opcode;
TC_LOG_DEBUG("warden", "Got packet, opcode %02X, size %u", opcode, uint32(recvData.size()));
recvData.hexlike();
switch (opcode)
{
case WARDEN_CMSG_MODULE_MISSING:
_warden->SendModuleToClient();
break;
case WARDEN_CMSG_MODULE_OK:
_warden->RequestHash();
break;
case WARDEN_CMSG_CHEAT_CHECKS_RESULT:
_warden->HandleData(recvData);
break;
case WARDEN_CMSG_MEM_CHECKS_RESULT:
TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MEM_CHECKS_RESULT received!");
break;
case WARDEN_CMSG_HASH_RESULT:
_warden->HandleHashResult(recvData);
_warden->InitializeModule();
break;
case WARDEN_CMSG_MODULE_FAILED:
TC_LOG_DEBUG("warden", "NYI WARDEN_CMSG_MODULE_FAILED received!");
break;
default:
TC_LOG_DEBUG("warden", "Got unknown warden opcode %02X of size %u.", opcode, uint32(recvData.size() - 1));
break;
}
_warden->HandleData(recvData);
}

View File

@@ -21,6 +21,7 @@
#include "ARC4.h"
#include "AuthDefines.h"
#include "ByteBuffer.h"
#include "Optional.h"
#include "WardenCheckMgr.h"
#include <array>
@@ -43,28 +44,16 @@ enum WardenOpcodes
WARDEN_SMSG_HASH_REQUEST = 5
};
enum WardenCheckType
{
MEM_CHECK = 0xF3, // 243: byte moduleNameIndex + uint Offset + byte Len (check to ensure memory isn't modified)
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)
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)
MODULE_CHECK = 0xD9 // 217: uint Seed + byte[20] SHA1 (check to ensure module isn't injected)
};
#pragma pack(push, 1)
struct WardenModuleUse
{
uint8 Command;
uint8 ModuleId[16];
uint8 ModuleKey[16];
std::array<uint8, 16> ModuleId;
std::array<uint8, 16> ModuleKey;
uint32 Size;
};
static_assert(sizeof(WardenModuleUse) == (1 + 16 + 16 + 4));
struct WardenModuleTransfer
{
@@ -72,66 +61,68 @@ struct WardenModuleTransfer
uint16 DataSize;
uint8 Data[500];
};
static_assert(sizeof(WardenModuleTransfer) == (1 + 2 + 500));
struct WardenHashRequest
{
uint8 Command;
uint8 Seed[16];
std::array<uint8, 16> Seed;
};
static_assert(sizeof(WardenHashRequest) == (1 + 16));
#pragma pack(pop)
struct ClientWardenModule
{
uint8 Id[16];
uint8 Key[16];
uint32 CompressedSize;
uint8* CompressedData;
std::array<uint8, 16> Id;
std::array<uint8, 16> Key;
uint8 const* CompressedData;
size_t CompressedSize;
};
class WorldSession;
class TC_GAME_API Warden
{
friend class WardenWin;
friend class WardenMac;
public:
Warden();
virtual ~Warden();
virtual void Init(WorldSession* session, SessionKey const& K) = 0;
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;
void Update(uint32 diff);
void HandleData(ByteBuffer& buff);
void SendModuleToClient();
void RequestModule();
void Update();
protected:
void DecryptData(uint8* buffer, uint32 length);
void EncryptData(uint8* buffer, uint32 length);
virtual void InitializeModule() = 0;
virtual void RequestHash() = 0;
virtual void HandleHashResult(ByteBuffer& buff) = 0;
virtual void HandleCheckResult(ByteBuffer& buff) = 0;
virtual void InitializeModuleForClient(ClientWardenModule& module) = 0;
virtual void RequestChecks() = 0;
void MakeModuleForClient();
void SendModuleToClient();
void RequestModule();
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 = nullptr);
// If nullptr is passed, the default action from config is executed
char const* ApplyPenalty(WardenCheck const* check);
private:
WorldSession* _session;
uint8 _inputKey[16];
uint8 _outputKey[16];
uint8 _seed[16];
std::array<uint8, 16> _inputKey = {};
std::array<uint8, 16> _outputKey = {};
std::array<uint8, 16> _seed = {};
Trinity::Crypto::ARC4 _inputCrypto;
Trinity::Crypto::ARC4 _outputCrypto;
uint32 _checkTimer; // Timer for sending check requests
uint32 _clientResponseTimer; // Timer for client response delay
bool _dataSent;
uint32 _previousTimestamp;
ClientWardenModule* _module;
Optional<ClientWardenModule> _module;
bool _initialized;
};

View File

@@ -15,26 +15,19 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Log.h"
#include "Database/DatabaseEnv.h"
#include "WardenCheckMgr.h"
#include "Common.h"
#include "DatabaseEnv.h"
#include "Errors.h"
#include "Log.h"
#include "Warden.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
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;
}
void WardenCheckMgr::LoadWardenChecks()
{
uint32 oldMSTime = getMSTime();
@@ -68,53 +61,40 @@ void WardenCheckMgr::LoadWardenChecks()
{
fields = result->Fetch();
uint16 id = fields[0].GetUInt16();
uint8 checkType = fields[1].GetUInt8();
std::string data = fields[2].GetString();
std::string checkResult = fields[3].GetString();
uint32 address = fields[4].GetUInt32();
uint8 length = fields[5].GetUInt8();
std::string str = fields[6].GetString();
std::string comment = fields[7].GetString();
uint16 const id = fields[0].GetUInt16();
uint8 const type = fields[1].GetUInt8();
WardenCheck* wardenCheck = new WardenCheck();
wardenCheck->Type = checkType;
wardenCheck->CheckId = id;
WardenCheck& wardenCheck = CheckStore[id];
wardenCheck.CheckId = id;
wardenCheck.Type = WardenCheckType(type);
// Initialize action with default action from config
wardenCheck->Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
wardenCheck.Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
if (checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == DRIVER_CHECK)
wardenCheck->Data.SetHexStr(data.c_str());
if (type == PAGE_CHECK_A || type == PAGE_CHECK_B || type == DRIVER_CHECK)
wardenCheck.Data = fields[2].GetBinary();
if (checkType == MEM_CHECK || checkType == MODULE_CHECK)
MemChecksIdPool.push_back(id);
else
OtherChecksIdPool.push_back(id);
if (type == MPQ_CHECK || type == MEM_CHECK)
CheckResultStore.emplace(id, fields[3].GetBinary());
if (checkType == MEM_CHECK || checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == PROC_CHECK)
if (type == MEM_CHECK || type == PAGE_CHECK_A || type == PAGE_CHECK_B || type == PROC_CHECK)
{
wardenCheck->Address = address;
wardenCheck->Length = length;
wardenCheck.Address = fields[4].GetUInt32();
wardenCheck.Length = fields[5].GetUInt8();
}
// PROC_CHECK support missing
if (checkType == MEM_CHECK || checkType == MPQ_CHECK || checkType == LUA_STR_CHECK || checkType == DRIVER_CHECK || checkType == MODULE_CHECK)
wardenCheck->Str = str;
if (type == MEM_CHECK || type == MPQ_CHECK || type == LUA_STR_CHECK || type == DRIVER_CHECK || type == MODULE_CHECK)
wardenCheck.Str = fields[6].GetString();
CheckStore[id] = wardenCheck;
wardenCheck.Comment = fields[7].GetString();
if (wardenCheck.Comment.empty())
wardenCheck.Comment = "Undocumented Check";
if (checkType == MPQ_CHECK || checkType == MEM_CHECK)
{
WardenCheckResult* wr = new WardenCheckResult();
wr->Result.SetHexStr(checkResult.c_str());
CheckResultStore[id] = wr;
}
if (comment.empty())
wardenCheck->Comment = "Undocumented Check";
if (type == MEM_CHECK || type == MODULE_CHECK)
MemChecksIdPool.push_back(id);
else
wardenCheck->Comment = comment;
OtherChecksIdPool.push_back(id);
++count;
}
@@ -145,8 +125,6 @@ void WardenCheckMgr::LoadWardenOverrides()
uint32 count = 0;
std::unique_lock<std::shared_mutex> lock(sWardenCheckMgr->_checkStoreLock);
do
{
Field* fields = result->Fetch();
@@ -162,7 +140,7 @@ void WardenCheckMgr::LoadWardenOverrides()
TC_LOG_ERROR("warden", "Warden check action override for non-existing check (ID: %u, action: %u), skipped", checkId, action);
else
{
CheckStore[checkId]->Action = WardenActions(action);
CheckStore[checkId].Action = WardenActions(action);
++count;
}
}
@@ -177,18 +155,15 @@ WardenCheckMgr* WardenCheckMgr::instance()
return &instance;
}
WardenCheck* WardenCheckMgr::GetWardenDataById(uint16 Id)
WardenCheck const& WardenCheckMgr::GetCheckDataById(uint16 Id) const
{
if (Id < CheckStore.size())
return CheckStore[Id];
return nullptr;
ASSERT(Id < CheckStore.size(), "Requested Warden data for invalid check ID %u", uint32(Id));
return CheckStore[Id];
}
WardenCheckResult* WardenCheckMgr::GetWardenResultById(uint16 Id)
WardenCheckResult const& WardenCheckMgr::GetCheckResultById(uint16 Id) const
{
CheckResultContainer::const_iterator itr = CheckResultStore.find(Id);
if (itr != CheckResultStore.end())
return itr->second;
return nullptr;
auto it = CheckResultStore.find(Id);
ASSERT(it != CheckResultStore.end(), "Requested Warden result for invalid check ID %u", uint32(Id));
return it->second;
}

View File

@@ -18,61 +18,69 @@
#ifndef _WARDENCHECKMGR_H
#define _WARDENCHECKMGR_H
#include <map>
#include "Define.h"
#include <shared_mutex>
#include "Cryptography/BigNumber.h"
#include <unordered_map>
#include <vector>
enum WardenActions
// EnumUtils: DESCRIBE THIS
enum WardenActions : uint8
{
WARDEN_ACTION_LOG,
WARDEN_ACTION_KICK,
WARDEN_ACTION_BAN
WARDEN_ACTION_LOG, // TITLE Log
WARDEN_ACTION_KICK, // TITLE Kick
WARDEN_ACTION_BAN // TITLE Ban
};
// EnumUtils: DESCRIBE THIS
enum WardenCheckType : uint8
{
MEM_CHECK = 0xF3, // 243: byte moduleNameIndex + uint Offset + byte Len (check to ensure memory isn't modified)
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)
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)
MODULE_CHECK = 0xD9 // 217: uint Seed + byte[20] SHA1 (check to ensure module isn't injected)
};
struct WardenCheck
{
uint8 Type;
BigNumber Data;
WardenCheckType Type;
std::vector<uint8> Data;
uint32 Address; // PROC_CHECK, MEM_CHECK, PAGE_CHECK
uint8 Length; // PROC_CHECK, MEM_CHECK, PAGE_CHECK
std::string Str; // LUA, MPQ, DRIVER
std::string Comment;
uint16 CheckId;
enum WardenActions Action;
WardenActions Action;
};
struct WardenCheckResult
{
BigNumber Result; // MEM_CHECK
};
using WardenCheckResult = std::vector<uint8>;
class TC_GAME_API WardenCheckMgr
{
private:
WardenCheckMgr();
~WardenCheckMgr();
public:
static WardenCheckMgr* instance();
// We have a linear key without any gaps, so we use vector for fast access
typedef std::vector<WardenCheck*> CheckContainer;
typedef std::map<uint32, WardenCheckResult*> CheckResultContainer;
WardenCheck const& GetCheckDataById(uint16 Id) const;
WardenCheckResult const& GetCheckResultById(uint16 Id) const;
WardenCheck* GetWardenDataById(uint16 Id);
WardenCheckResult* GetWardenResultById(uint16 Id);
std::vector<uint16> MemChecksIdPool;
std::vector<uint16> OtherChecksIdPool;
std::vector<uint16> const& GetAvailableMemoryChecks() const { return MemChecksIdPool; }
std::vector<uint16> const& GetAvailableOtherChecks() const { return OtherChecksIdPool; }
void LoadWardenChecks();
void LoadWardenOverrides();
std::shared_mutex _checkStoreLock;
private:
CheckContainer CheckStore;
CheckResultContainer CheckResultStore;
std::vector<WardenCheck> CheckStore;
std::unordered_map<uint32, WardenCheckResult> CheckResultStore;
std::vector<uint16> MemChecksIdPool;
std::vector<uint16> OtherChecksIdPool;
};
#define sWardenCheckMgr WardenCheckMgr::instance()

View File

@@ -29,6 +29,7 @@
#include "WorldSession.h"
#include <openssl/md5.h>
#include <array>
WardenMac::WardenMac() : Warden() { }
@@ -39,8 +40,8 @@ void WardenMac::Init(WorldSession* pClient, SessionKey const& K)
_session = pClient;
// Generate Warden Key
SessionKeyGenerator<Trinity::Crypto::SHA1> WK(K);
WK.Generate(_inputKey, 16);
WK.Generate(_outputKey, 16);
WK.Generate(_inputKey.data(), 16);
WK.Generate(_outputKey.data(), 16);
/*
Seed: 4D808D2C77D905C41A6380EC08586AFE (0x05 packet)
Hash: <?> (0x04 packet)
@@ -48,9 +49,8 @@ void WardenMac::Init(WorldSession* pClient, SessionKey const& K)
New Client Key: <?>
New Cerver Key: <?>
*/
uint8 mod_seed[16] = { 0x4D, 0x80, 0x8D, 0x2C, 0x77, 0xD9, 0x05, 0xC4, 0x1A, 0x63, 0x80, 0xEC, 0x08, 0x58, 0x6A, 0xFE };
memcpy(_seed, mod_seed, 16);
_seed = { 0x4D, 0x80, 0x8D, 0x2C, 0x77, 0xD9, 0x05, 0xC4, 0x1A, 0x63, 0x80, 0xEC, 0x08, 0x58, 0x6A, 0xFE };
_inputCrypto.Init(_inputKey);
_outputCrypto.Init(_outputKey);
@@ -60,32 +60,18 @@ void WardenMac::Init(WorldSession* pClient, SessionKey const& K)
TC_LOG_DEBUG("warden", " Seed: %s", ByteArrayToHexStr(_seed).c_str());
TC_LOG_DEBUG("warden", "Loading Module...");
_module = GetModuleForClient();
MakeModuleForClient();
TC_LOG_DEBUG("warden", "Module Key: %s", ByteArrayToHexStr(_module->Key).c_str());
TC_LOG_DEBUG("warden", "Module ID: %s", ByteArrayToHexStr(_module->Id).c_str());
RequestModule();
}
ClientWardenModule* WardenMac::GetModuleForClient()
void WardenMac::InitializeModuleForClient(ClientWardenModule& module)
{
ClientWardenModule *mod = new ClientWardenModule;
uint32 len = sizeof(Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data);
// data assign
mod->CompressedSize = len;
mod->CompressedData = new uint8[len];
memcpy(mod->CompressedData, Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data, len);
memcpy(mod->Key, Module_0DBBF209A27B1E279A9FEC5C168A15F7_Key, 16);
// md5 hash
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, mod->CompressedData, len);
MD5_Final((uint8*)&mod->Id, &ctx);
return mod;
module.CompressedData = Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.data();
module.CompressedSize = Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.size();
}
void WardenMac::InitializeModule()
@@ -100,7 +86,7 @@ void WardenMac::RequestHash()
// Create packet structure
WardenHashRequest Request;
Request.Command = WARDEN_SMSG_HASH_REQUEST;
memcpy(Request.Seed, _seed, 16);
Request.Seed = _seed;
// Encrypt with warden RC4 key.
EncryptData((uint8*)&Request, sizeof(WardenHashRequest));
@@ -152,18 +138,15 @@ void WardenMac::HandleHashResult(ByteBuffer &buff)
keyOut[3] = 0x1337F00D * keyIn[3];
// end test
buff.rpos(buff.wpos());
Trinity::Crypto::SHA1 sha1;
sha1.UpdateData((uint8*)keyIn, 16);
sha1.Finalize();
//const uint8 validHash[20] = { 0x56, 0x8C, 0x05, 0x4C, 0x78, 0x1A, 0x97, 0x2A, 0x60, 0x37, 0xA2, 0x29, 0x0C, 0x22, 0xB5, 0x25, 0x71, 0xA0, 0x6F, 0x4E };
// Verify key
if (memcmp(buff.contents() + 1, sha1.GetDigest().data(), 20) != 0)
Trinity::Crypto::SHA1::Digest result;
buff.read(result);
if (result != Trinity::Crypto::SHA1::GetDigestOf(reinterpret_cast<uint8*>(keyIn), 16))
{
TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
char const* penalty = ApplyPenalty(nullptr);
TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
return;
}
@@ -176,18 +159,16 @@ void WardenMac::HandleHashResult(ByteBuffer &buff)
//const uint8 server_key[16] = { 0xC2, 0xB7, 0xAD, 0xED, 0xFC, 0xCC, 0xA9, 0xC2, 0xBF, 0xB3, 0xF8, 0x56, 0x02, 0xBA, 0x80, 0x9B };
// change keys here
memcpy(_inputKey, keyIn, 16);
memcpy(_outputKey, keyOut, 16);
memcpy(_inputKey.data(), keyIn, 16);
memcpy(_outputKey.data(), keyOut, 16);
_inputCrypto.Init(_inputKey);
_outputCrypto.Init(_outputKey);
_initialized = true;
_previousTimestamp = GameTime::GetGameTimeMS();
}
void WardenMac::RequestData()
void WardenMac::RequestChecks()
{
TC_LOG_DEBUG("warden", "Request data");
@@ -211,7 +192,7 @@ void WardenMac::RequestData()
_dataSent = true;
}
void WardenMac::HandleData(ByteBuffer &buff)
void WardenMac::HandleCheckResult(ByteBuffer &buff)
{
TC_LOG_DEBUG("warden", "Handle data");
@@ -253,17 +234,17 @@ void WardenMac::HandleData(ByteBuffer &buff)
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, str.c_str(), str.size());
uint8 ourMD5Hash[16];
MD5_Final(ourMD5Hash, &ctx);
std::array<uint8, 16> ourMD5Hash;
MD5_Final(ourMD5Hash.data(), &ctx);
uint8 theirsMD5Hash[16];
buff.read(theirsMD5Hash, 16);
std::array<uint8, 16> theirsMD5Hash;
buff.read(theirsMD5Hash);
if (memcmp(ourMD5Hash, theirsMD5Hash, 16) != 0)
if (ourMD5Hash != theirsMD5Hash)
{
TC_LOG_DEBUG("warden", "Handle data failed: MD5 hash is wrong!");
//found = true;
}
_session->KickPlayer("WardenMac::HandleData");
_session->KickPlayer("WardenMac::HandleCheckResult");
}

View File

@@ -32,12 +32,12 @@ class TC_GAME_API WardenMac : public Warden
~WardenMac();
void Init(WorldSession* session, SessionKey const& k) override;
ClientWardenModule* GetModuleForClient() override;
void InitializeModuleForClient(ClientWardenModule& module) override;
void InitializeModule() override;
void RequestHash() override;
void HandleHashResult(ByteBuffer& buff) override;
void RequestData() override;
void HandleData(ByteBuffer& buff) override;
void RequestChecks() override;
void HandleCheckResult(ByteBuffer& buff) override;
};
#endif

View File

@@ -18,6 +18,7 @@
#include "WardenWin.h"
#include "Common.h"
#include "ByteBuffer.h"
#include "Containers.h"
#include "CryptoRandom.h"
#include "DatabaseEnv.h"
#include "GameTime.h"
@@ -27,27 +28,34 @@
#include "Player.h"
#include "Random.h"
#include "SessionKeyGenerator.h"
#include "SmartEnum.h"
#include "Util.h"
#include "WardenModuleWin.h"
#include "WardenCheckMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <openssl/md5.h>
WardenWin::WardenWin() : Warden(), _serverTicks(0) {}
WardenWin::WardenWin() : Warden(), _serverTicks(0)
{
_memChecks = sWardenCheckMgr->GetAvailableMemoryChecks();
Trinity::Containers::RandomShuffle(_memChecks);
_memChecksIt = _memChecks.begin();
WardenWin::~WardenWin() { }
_otherChecks = sWardenCheckMgr->GetAvailableOtherChecks();
Trinity::Containers::RandomShuffle(_otherChecks);
_otherChecksIt = _otherChecks.begin();
}
void WardenWin::Init(WorldSession* session, SessionKey const& K)
{
_session = session;
// Generate Warden Key
SessionKeyGenerator<Trinity::Crypto::SHA1> WK(K);
WK.Generate(_inputKey, 16);
WK.Generate(_outputKey, 16);
WK.Generate(_inputKey.data(), _inputKey.size());
WK.Generate(_outputKey.data(), _outputKey.size());
memcpy(_seed, Module.Seed, 16);
_seed = Module.Seed;
_inputCrypto.Init(_inputKey);
_outputCrypto.Init(_outputKey);
@@ -57,32 +65,19 @@ void WardenWin::Init(WorldSession* session, SessionKey const& K)
TC_LOG_DEBUG("warden", " Seed: %s", ByteArrayToHexStr(_seed).c_str());
TC_LOG_DEBUG("warden", "Loading Module...");
_module = GetModuleForClient();
MakeModuleForClient();
TC_LOG_DEBUG("warden", "Module Key: %s", ByteArrayToHexStr(_module->Key).c_str());
TC_LOG_DEBUG("warden", "Module ID: %s", ByteArrayToHexStr(_module->Id).c_str());
RequestModule();
}
ClientWardenModule* WardenWin::GetModuleForClient()
void WardenWin::InitializeModuleForClient(ClientWardenModule& module)
{
ClientWardenModule *mod = new ClientWardenModule;
uint32 length = sizeof(Module.Module);
// data assign
mod->CompressedSize = length;
mod->CompressedData = new uint8[length];
memcpy(mod->CompressedData, Module.Module, length);
memcpy(mod->Key, Module.ModuleKey, 16);
// md5 hash
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, mod->CompressedData, length);
MD5_Final((uint8*)&mod->Id, &ctx);
return mod;
module.CompressedData = Module.Module.data();
module.CompressedSize = Module.Module.size();
module.Key = Module.ModuleKey;
}
void WardenWin::InitializeModule()
@@ -121,11 +116,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<uint8*>(&Request), sizeof(WardenInitModuleRequest));
WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenInitModuleRequest));
pkt.append((uint8*)&Request, sizeof(WardenInitModuleRequest));
pkt.append(reinterpret_cast<uint8*>(&Request), sizeof(WardenInitModuleRequest));
_session->SendPacket(&pkt);
}
@@ -136,105 +144,91 @@ void WardenWin::RequestHash()
// Create packet structure
WardenHashRequest Request;
Request.Command = WARDEN_SMSG_HASH_REQUEST;
memcpy(Request.Seed, _seed, 16);
Request.Seed = _seed;
// Encrypt with warden RC4 key.
EncryptData((uint8*)&Request, sizeof(WardenHashRequest));
EncryptData(reinterpret_cast<uint8*>(&Request), sizeof(WardenHashRequest));
WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenHashRequest));
pkt.append((uint8*)&Request, sizeof(WardenHashRequest));
pkt.append(reinterpret_cast<uint8*>(&Request), sizeof(WardenHashRequest));
_session->SendPacket(&pkt);
}
void WardenWin::HandleHashResult(ByteBuffer &buff)
{
buff.rpos(buff.wpos());
// Verify key
if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, 20) != 0)
Trinity::Crypto::SHA1::Digest response;
buff.read(response);
if (response != Module.ClientKeySeedHash)
{
TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
char const* penalty = ApplyPenalty(nullptr);
TC_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
return;
}
TC_LOG_DEBUG("warden", "Request hash reply: succeed");
// Change keys here
memcpy(_inputKey, Module.ClientKeySeed, 16);
memcpy(_outputKey, Module.ServerKeySeed, 16);
_inputKey = Module.ClientKeySeed;
_outputKey = Module.ServerKeySeed;
_inputCrypto.Init(_inputKey);
_outputCrypto.Init(_outputKey);
_initialized = true;
_previousTimestamp = GameTime::GetGameTimeMS();
}
void WardenWin::RequestData()
void WardenWin::RequestChecks()
{
TC_LOG_DEBUG("warden", "Request data");
// If all checks were done, fill the todo list again
if (_memChecksTodo.empty())
_memChecksTodo.assign(sWardenCheckMgr->MemChecksIdPool.begin(), sWardenCheckMgr->MemChecksIdPool.end());
if (_memChecksIt == _memChecks.end())
{
TC_LOG_DEBUG("warden", "Finished all mem checks, re-shuffling");
Trinity::Containers::RandomShuffle(_memChecks);
_memChecksIt = _memChecks.begin();
}
if (_otherChecksTodo.empty())
_otherChecksTodo.assign(sWardenCheckMgr->OtherChecksIdPool.begin(), sWardenCheckMgr->OtherChecksIdPool.end());
if (_otherChecksIt == _otherChecks.end())
{
TC_LOG_DEBUG("warden", "Finished all other checks, re-shuffling");
Trinity::Containers::RandomShuffle(_otherChecks);
_otherChecksIt = _otherChecks.begin();
}
_serverTicks = GameTime::GetGameTimeMS();
uint16 id;
uint8 type;
WardenCheck* wd;
_currentChecks.clear();
// Build check request
for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_MEM_CHECKS); ++i)
{
// If todo list is done break loop (will be filled on next Update() run)
if (_memChecksTodo.empty())
break;
// Get check id from the end and remove it from todo
id = _memChecksTodo.back();
_memChecksTodo.pop_back();
// Add the id to the list sent in this cycle
_currentChecks.push_back(id);
}
ByteBuffer buff;
buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST);
std::shared_lock<std::shared_mutex> lock(sWardenCheckMgr->_checkStoreLock);
for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_MEM_CHECKS); ++i)
{
// If todo list is done break loop (will be filled on next Update() run)
if (_memChecksIt == _memChecks.end())
break;
_currentChecks.push_back(*(_memChecksIt++));
}
for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_OTHER_CHECKS); ++i)
{
// If todo list is done break loop (will be filled on next Update() run)
if (_otherChecksTodo.empty())
if (_otherChecksIt == _otherChecks.end())
break;
// Get check id from the end and remove it from todo
id = _otherChecksTodo.back();
_otherChecksTodo.pop_back();
uint16 const id = *(_otherChecksIt++);
// Add the id to the list sent in this cycle
_currentChecks.push_back(id);
wd = sWardenCheckMgr->GetWardenDataById(id);
switch (wd->Type)
WardenCheck const& check = sWardenCheckMgr->GetCheckDataById(id);
if (!check.Str.empty())
{
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(check.Str.size());
buff.append(check.Str.data(), check.Str.size());
}
_currentChecks.push_back(id);
}
uint8 xorByte = _inputKey[0];
@@ -245,28 +239,27 @@ void WardenWin::RequestData()
uint8 index = 1;
for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
for (uint16 const id : _currentChecks)
{
wd = sWardenCheckMgr->GetWardenDataById(*itr);
WardenCheck const& check = sWardenCheckMgr->GetCheckDataById(id);
type = wd->Type;
WardenCheckType const type = check.Type;
buff << uint8(type ^ xorByte);
switch (type)
{
case MEM_CHECK:
{
buff << uint8(0x00);
buff << uint32(wd->Address);
buff << uint8(wd->Length);
buff << uint32(check.Address);
buff << uint8(check.Length);
break;
}
case PAGE_CHECK_A:
case PAGE_CHECK_B:
{
std::vector<uint8> data = wd->Data.ToByteVector(0, false);
buff.append(data.data(), data.size());
buff << uint32(wd->Address);
buff << uint8(wd->Length);
buff.append(check.Data.data(), check.Data.size());
buff << uint32(check.Address);
buff << uint8(check.Length);
break;
}
case MPQ_CHECK:
@@ -277,8 +270,7 @@ void WardenWin::RequestData()
}
case DRIVER_CHECK:
{
std::vector<uint8> data = wd->Data.ToByteVector(0, false);
buff.append(data.data(), data.size());
buff.append(check.Data.data(), check.Data.size());
buff << uint8(index++);
break;
}
@@ -286,16 +278,16 @@ void WardenWin::RequestData()
{
std::array<uint8, 4> seed = Trinity::Crypto::GetRandomBytes<4>();
buff.append(seed);
buff.append(Trinity::Crypto::HMAC_SHA1::GetDigestOf(seed, wd->Str));
buff.append(Trinity::Crypto::HMAC_SHA1::GetDigestOf(seed, check.Str));
break;
}
/*case PROC_CHECK:
{
buff.append(wd->i.AsByteArray(0, false).get(), wd->i.GetNumBytes());
buff.append(check->i.AsByteArray(0, false).get(), check->i.GetNumBytes());
buff << uint8(index++);
buff << uint8(index++);
buff << uint32(wd->Address);
buff << uint8(wd->Length);
buff << uint32(check->Address);
buff << uint8(check->Length);
break;
}*/
default:
@@ -316,13 +308,13 @@ void WardenWin::RequestData()
std::stringstream stream;
stream << "Sent check id's: ";
for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
stream << *itr << " ";
for (uint16 const id : _currentChecks)
stream << id << " ";
TC_LOG_DEBUG("warden", "%s", stream.str().c_str());
}
void WardenWin::HandleData(ByteBuffer &buff)
void WardenWin::HandleCheckResult(ByteBuffer &buff)
{
TC_LOG_DEBUG("warden", "Handle data");
@@ -334,10 +326,19 @@ void WardenWin::HandleData(ByteBuffer &buff)
uint32 Checksum;
buff >> Checksum;
if (Length != (buff.size() - buff.rpos()))
{
buff.rfinish();
char const* penalty = ApplyPenalty(nullptr);
TC_LOG_WARN("warden", "%s sends manipulated warden packet. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
return;
}
if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length))
{
buff.rpos(buff.wpos());
TC_LOG_WARN("warden", "%s failed checksum. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
buff.rfinish();
char const* penalty = ApplyPenalty(nullptr);
TC_LOG_WARN("warden", "%s failed checksum. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
return;
}
@@ -348,7 +349,8 @@ void WardenWin::HandleData(ByteBuffer &buff)
/// @todo test it.
if (result == 0x00)
{
TC_LOG_WARN("warden", "%s failed timing check. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
char const* penalty = ApplyPenalty(nullptr);
TC_LOG_WARN("warden", "%s failed timing check. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
return;
}
@@ -358,26 +360,18 @@ void WardenWin::HandleData(ByteBuffer &buff)
uint32 ticksNow = GameTime::GetGameTimeMS();
uint32 ourTicks = newClientTicks + (ticksNow - _serverTicks);
TC_LOG_DEBUG("warden", "ServerTicks %u", ticksNow); // Now
TC_LOG_DEBUG("warden", "RequestTicks %u", _serverTicks); // At request
TC_LOG_DEBUG("warden", "Ticks %u", newClientTicks); // At response
TC_LOG_DEBUG("warden", "Ticks diff %u", ourTicks - newClientTicks);
TC_LOG_DEBUG("warden", "Server tick count now: %u", ticksNow);
TC_LOG_DEBUG("warden", "Server tick count at req: %u", _serverTicks);
TC_LOG_DEBUG("warden", "Client ticks in response: %u", newClientTicks);
TC_LOG_DEBUG("warden", "Round trip response time: %u ms", ourTicks - newClientTicks);
}
WardenCheckResult* rs;
WardenCheck *rd;
uint8 type;
uint16 checkFailed = 0;
std::shared_lock<std::shared_mutex> lock(sWardenCheckMgr->_checkStoreLock);
for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
for (uint16 const id : _currentChecks)
{
rd = sWardenCheckMgr->GetWardenDataById(*itr);
rs = sWardenCheckMgr->GetWardenResultById(*itr);
WardenCheck const& check = sWardenCheckMgr->GetCheckDataById(id);
type = rd->Type;
switch (type)
switch (check.Type)
{
case MEM_CHECK:
{
@@ -386,22 +380,22 @@ void WardenWin::HandleData(ByteBuffer &buff)
if (Mem_Result != 0)
{
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", *itr, _session->GetAccountId());
checkFailed = *itr;
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", id, _session->GetAccountId());
checkFailed = id;
continue;
}
std::vector<uint8> result = rs->Result.ToByteVector(0, false);
if (memcmp(buff.contents() + buff.rpos(), result.data(), rd->Length) != 0)
std::vector<uint8> response;
response.resize(check.Length);
buff.read(response.data(), response.size());
if (response != sWardenCheckMgr->GetCheckResultById(id))
{
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK fail CheckId %u account Id %u", *itr, _session->GetAccountId());
checkFailed = *itr;
buff.rpos(buff.rpos() + rd->Length);
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK fail CheckId %u account Id %u", id, _session->GetAccountId());
checkFailed = id;
continue;
}
buff.rpos(buff.rpos() + rd->Length);
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
TC_LOG_DEBUG("warden", "RESULT MEM_CHECK passed CheckId %u account Id %u", id, _session->GetAccountId());
break;
}
case PAGE_CHECK_A:
@@ -409,27 +403,14 @@ void WardenWin::HandleData(ByteBuffer &buff)
case DRIVER_CHECK:
case MODULE_CHECK:
{
const uint8 byte = 0xE9;
if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8)) != 0)
if (buff.read<uint8>() != 0xE9)
{
if (type == PAGE_CHECK_A || type == PAGE_CHECK_B)
TC_LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
if (type == MODULE_CHECK)
TC_LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
if (type == DRIVER_CHECK)
TC_LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
checkFailed = *itr;
buff.rpos(buff.rpos() + 1);
TC_LOG_DEBUG("warden", "RESULT %s fail, CheckId %u account Id %u", EnumUtils::ToConstant(check.Type), id, _session->GetAccountId());
checkFailed = id;
continue;
}
buff.rpos(buff.rpos() + 1);
if (type == PAGE_CHECK_A || type == PAGE_CHECK_B)
TC_LOG_DEBUG("warden", "RESULT PAGE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
else if (type == MODULE_CHECK)
TC_LOG_DEBUG("warden", "RESULT MODULE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
else if (type == DRIVER_CHECK)
TC_LOG_DEBUG("warden", "RESULT DRIVER_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
TC_LOG_DEBUG("warden", "RESULT %s passed CheckId %u account Id %u", EnumUtils::ToConstant(check.Type), id, _session->GetAccountId());
break;
}
case LUA_STR_CHECK:
@@ -439,24 +420,20 @@ void WardenWin::HandleData(ByteBuffer &buff)
if (Lua_Result != 0)
{
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
checkFailed = *itr;
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", id, _session->GetAccountId());
checkFailed = id;
continue;
}
uint8 luaStrLen;
buff >> luaStrLen;
uint8 luaStrLen = buff.read<uint8>();
if (luaStrLen != 0)
{
char *str = new char[luaStrLen + 1];
memcpy(str, buff.contents() + buff.rpos(), luaStrLen);
str[luaStrLen] = '\0'; // null terminator
TC_LOG_DEBUG("warden", "Lua string: %s", str);
delete[] str;
std::string str;
str.resize(luaStrLen);
buff.read(reinterpret_cast<uint8*>(str.data()), luaStrLen);
TC_LOG_DEBUG("warden", "Lua string: %s", str.c_str());
}
buff.rpos(buff.rpos() + luaStrLen); // Skip string
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId());
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", id, _session->GetAccountId());
break;
}
case MPQ_CHECK:
@@ -467,20 +444,21 @@ void WardenWin::HandleData(ByteBuffer &buff)
if (Mpq_Result != 0)
{
TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id %u", _session->GetAccountId());
checkFailed = *itr;
checkFailed = id;
continue;
}
if (memcmp(buff.contents() + buff.rpos(), rs->Result.ToByteArray<20>(false).data(), 20) != 0) // SHA1
std::vector<uint8> result;
result.resize(Trinity::Crypto::SHA1::DIGEST_LENGTH);
buff.read(result.data(), result.size());
if (result != sWardenCheckMgr->GetCheckResultById(id)) // SHA1
{
TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
checkFailed = *itr;
buff.rpos(buff.rpos() + 20); // 20 bytes SHA1
TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId %u account Id %u", id, _session->GetAccountId());
checkFailed = id;
continue;
}
buff.rpos(buff.rpos() + 20); // 20 bytes SHA1
TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId());
TC_LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId %u account Id %u", id, _session->GetAccountId());
break;
}
default: // Should never happen
@@ -490,8 +468,9 @@ void WardenWin::HandleData(ByteBuffer &buff)
if (checkFailed > 0)
{
WardenCheck* check = sWardenCheckMgr->GetWardenDataById(checkFailed);
TC_LOG_WARN("warden", "%s failed Warden check %u. Action: %s", _session->GetPlayerInfo().c_str(), checkFailed, Penalty(check).c_str());
WardenCheck const& check = sWardenCheckMgr->GetCheckDataById(checkFailed);
char const* penalty = ApplyPenalty(&check);
TC_LOG_WARN("warden", "%s failed Warden check %u (%s). Action: %s", _session->GetPlayerInfo().c_str(), checkFailed, EnumUtils::ToConstant(check.Type), penalty);
}
// Set hold off timer, minimum timer should at least be 1 second

View File

@@ -55,6 +55,7 @@ struct WardenInitModuleRequest
uint32 Function3;
uint8 Function3_set;
};
static_assert(sizeof(WardenInitModuleRequest) == (1 + 2 + 4 + 1 + 1 + 1 + 1 + (4 * 4) + 1 + 2 + 4 + 1 + 1 + 1 + 4 + 1 + 1 + 2 + 4 + 1 + 1 + 1 + 4 + 1));
#pragma pack(pop)
@@ -65,21 +66,22 @@ class TC_GAME_API WardenWin : public Warden
{
public:
WardenWin();
~WardenWin();
void Init(WorldSession* session, SessionKey const& K) override;
ClientWardenModule* GetModuleForClient() override;
void InitializeModuleForClient(ClientWardenModule& module) override;
void InitializeModule() override;
void RequestHash() override;
void HandleHashResult(ByteBuffer &buff) override;
void RequestData() override;
void HandleData(ByteBuffer &buff) override;
void RequestChecks() override;
void HandleCheckResult(ByteBuffer &buff) override;
private:
uint32 _serverTicks;
std::list<uint16> _otherChecksTodo;
std::list<uint16> _memChecksTodo;
std::list<uint16> _currentChecks;
std::vector<uint16> _memChecks;
std::vector<uint16>::const_iterator _memChecksIt;
std::vector<uint16> _otherChecks;
std::vector<uint16>::const_iterator _otherChecksIt;
std::vector<uint16> _currentChecks;
};
#endif

View File

@@ -0,0 +1,100 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WardenCheckMgr.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
namespace Trinity
{
namespace Impl
{
/**********************************************************************\
|* data for enum 'WardenActions' in 'WardenCheckMgr.h' auto-generated *|
\**********************************************************************/
template <>
TC_API_EXPORT EnumText EnumUtils<WardenActions>::ToString(WardenActions value)
{
switch (value)
{
case WARDEN_ACTION_LOG: return { "WARDEN_ACTION_LOG", "Log", "" };
case WARDEN_ACTION_KICK: return { "WARDEN_ACTION_KICK", "Kick", "" };
case WARDEN_ACTION_BAN: return { "WARDEN_ACTION_BAN", "Ban", "" };
default: throw std::out_of_range("value");
}
}
template <>
TC_API_EXPORT size_t EnumUtils<WardenActions>::Count() { return 3; }
template <>
TC_API_EXPORT WardenActions EnumUtils<WardenActions>::FromIndex(size_t index)
{
switch (index)
{
case 0: return WARDEN_ACTION_LOG;
case 1: return WARDEN_ACTION_KICK;
case 2: return WARDEN_ACTION_BAN;
default: throw std::out_of_range("index");
}
}
/************************************************************************\
|* data for enum 'WardenCheckType' in 'WardenCheckMgr.h' auto-generated *|
\************************************************************************/
template <>
TC_API_EXPORT EnumText EnumUtils<WardenCheckType>::ToString(WardenCheckType value)
{
switch (value)
{
case MEM_CHECK: return { "MEM_CHECK", "MEM_CHECK", "243: byte moduleNameIndex + uint Offset + byte Len (check to ensure memory isn't modified)" };
case PAGE_CHECK_A: return { "PAGE_CHECK_A", "PAGE_CHECK_A", "178: uint Seed + byte[20] SHA1 + uint Addr + byte Len (scans all pages for specified hash)" };
case PAGE_CHECK_B: return { "PAGE_CHECK_B", "PAGE_CHECK_B", "191: uint Seed + byte[20] SHA1 + uint Addr + byte Len (scans only pages starts with MZ+PE headers for specified hash)" };
case MPQ_CHECK: return { "MPQ_CHECK", "MPQ_CHECK", "152: byte fileNameIndex (check to ensure MPQ file isn't modified)" };
case LUA_STR_CHECK: return { "LUA_STR_CHECK", "LUA_STR_CHECK", "139: byte luaNameIndex (check to ensure LUA string isn't used)" };
case DRIVER_CHECK: return { "DRIVER_CHECK", "DRIVER_CHECK", "113: uint Seed + byte[20] SHA1 + byte driverNameIndex (check to ensure driver isn't loaded)" };
case TIMING_CHECK: return { "TIMING_CHECK", "TIMING_CHECK", "87: empty (check to ensure GetTickCount() isn't detoured)" };
case PROC_CHECK: return { "PROC_CHECK", "PROC_CHECK", "126: uint Seed + byte[20] SHA1 + byte moluleNameIndex + byte procNameIndex + uint Offset + byte Len (check to ensure proc isn't detoured)" };
case MODULE_CHECK: return { "MODULE_CHECK", "MODULE_CHECK", "217: uint Seed + byte[20] SHA1 (check to ensure module isn't injected)" };
default: throw std::out_of_range("value");
}
}
template <>
TC_API_EXPORT size_t EnumUtils<WardenCheckType>::Count() { return 9; }
template <>
TC_API_EXPORT WardenCheckType EnumUtils<WardenCheckType>::FromIndex(size_t index)
{
switch (index)
{
case 0: return MEM_CHECK;
case 1: return PAGE_CHECK_A;
case 2: return PAGE_CHECK_B;
case 3: return MPQ_CHECK;
case 4: return LUA_STR_CHECK;
case 5: return DRIVER_CHECK;
case 6: return TIMING_CHECK;
case 7: return PROC_CHECK;
case 8: return MODULE_CHECK;
default: throw std::out_of_range("index");
}
}
}
}

View File

@@ -43,7 +43,6 @@ EndScriptData */
#include "SmartAI.h"
#include "SpellMgr.h"
#include "TicketMgr.h"
#include "WardenCheckMgr.h"
#include "WaypointManager.h"
#include "World.h"
@@ -158,7 +157,6 @@ public:
{ "spell_group_stack_rules", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES, true, &HandleReloadSpellGroupStackRulesCommand, "" },
{ "trainer", rbac::RBAC_PERM_COMMAND_RELOAD_TRAINER, true, &HandleReloadTrainerCommand, "" },
{ "trinity_string", rbac::RBAC_PERM_COMMAND_RELOAD_TRINITY_STRING, true, &HandleReloadTrinityStringCommand, "" },
{ "warden_action", rbac::RBAC_PERM_COMMAND_RELOAD_WARDEN_ACTION, true, &HandleReloadWardenactionCommand, "" },
{ "waypoint_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_WAYPOINT_SCRIPTS, true, &HandleReloadWpScriptsCommand, "" },
{ "waypoint_data", rbac::RBAC_PERM_COMMAND_RELOAD_WAYPOINT_DATA, true, &HandleReloadWpCommand, "" },
{ "vehicle_accessory", rbac::RBAC_PERM_COMMAND_RELOAD_VEHICLE_ACCESORY, true, &HandleReloadVehicleAccessoryCommand, "" },
@@ -696,21 +694,6 @@ public:
return true;
}
static bool HandleReloadWardenactionCommand(ChatHandler* handler, char const* /*args*/)
{
if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
{
handler->SendSysMessage("Warden system disabled by config - reloading warden_action skipped.");
handler->SetSentErrorMessage(true);
return false;
}
TC_LOG_INFO("misc", "Re-Loading warden_action Table!");
sWardenCheckMgr->LoadWardenOverrides();
handler->SendGlobalGMSysMessage("DB table `warden_action` reloaded.");
return true;
}
static bool HandleReloadTrainerCommand(ChatHandler* handler, char const* /*args*/)
{
TC_LOG_INFO("misc", "Re-Loading `trainer` Table!");