mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Warden: Warden refactors (PR #25235)
This commit is contained in:
@@ -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!')
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
5
sql/updates/auth/3.3.5/2020_08_11_00_auth.sql
Normal file
5
sql/updates/auth/3.3.5/2020_08_11_00_auth.sql
Normal 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;
|
||||
15
sql/updates/world/3.3.5/2020_08_11_00_world_335.sql
Normal file
15
sql/updates/world/3.3.5/2020_08_11_00_world_335.sql
Normal 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';
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 =
|
||||
{
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
100
src/server/game/Warden/enuminfo_WardenCheckMgr.cpp
Normal file
100
src/server/game/Warden/enuminfo_WardenCheckMgr.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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!");
|
||||
|
||||
Reference in New Issue
Block a user