Professional Documents
Culture Documents
Warden Win
Warden Win
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "WardenWin.h"
#include "ByteBuffer.h"
#include "Common.h"
#include "CryptoRandom.h"
#include "GameTime.h"
#include "HMAC.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
#include "SessionKeyGenerator.h"
#include "Util.h"
#include "WardenCheckMgr.h"
#include "WardenModuleWin.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <openssl/md5.h>
// GUILD is the shortest string that has no client validation (RAID only sends if
in a raid group)
static constexpr char _luaEvalPrefix[] = "local S,T,R=SendAddonMessage,function()";
static constexpr char _luaEvalMidfix[] = " end R=S and T()if R then S('_TW',";
static constexpr char _luaEvalPostfix[] = ",'GUILD')end";
static_assert((sizeof(_luaEvalPrefix)-1 + sizeof(_luaEvalMidfix)-1 +
sizeof(_luaEvalPostfix)-1 + WARDEN_MAX_LUA_CHECK_LENGTH) == 255);
switch (type)
{
case WARDEN_CHECK_MEM_TYPE:
return CONFIG_WARDEN_NUM_MEM_CHECKS;
case WARDEN_CHECK_LUA_TYPE:
return CONFIG_WARDEN_NUM_LUA_CHECKS;
default:
break;
}
return CONFIG_WARDEN_NUM_OTHER_CHECKS;
}
WardenWin::~WardenWin()
{
}
_module = GetModuleForClient();
ClientWardenModule* WardenWin::GetModuleForClient()
{
ClientWardenModule* mod = new ClientWardenModule;
// 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;
}
void WardenWin::InitializeModule()
{
LOG_DEBUG("warden", "Initialize module");
Request.Command2 = WARDEN_SMSG_MODULE_INITIALIZE;
Request.Size2 = 8;
Request.Unk3 = 4;
Request.Unk4 = 0;
Request.String_library2 = 0;
Request.Function2 = 0x00419210; // 0x00400000 +
0x00419210 FrameScript::Execute
Request.Function2_set = 1;
Request.CheckSumm2 = BuildChecksum(&Request.Unk3, 8);
Request.Command3 = WARDEN_SMSG_MODULE_INITIALIZE;
Request.Size3 = 8;
Request.Unk5 = 1;
Request.Unk6 = 1;
Request.String_library3 = 0;
Request.Function3 = 0x0046AE20; // 0x00400000 +
0x0046AE20 PerformanceCounter
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);
void WardenWin::RequestHash()
{
LOG_DEBUG("warden", "Request hash");
// Verify key
if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash,
Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0)
{
LOG_DEBUG("warden", "Request hash reply: failed");
ApplyPenalty(0, "Request hash reply: failed");
return;
}
_inputCrypto.Init(_inputKey);
_outputCrypto.Init(_outputKey);
_initialized = true;
}
void WardenWin::RequestChecks()
{
LOG_DEBUG("warden", "Request data");
_serverTicks = GameTime::GetGameTimeMS().count();
_CurrentChecks.clear();
// No pending checks
if (_PendingChecks.empty())
{
for (uint8 checkType = 0; checkType < MAX_WARDEN_CHECK_TYPES; ++checkType)
{
for (uint32 y = 0; y < sWorld-
>getIntConfig(GetMaxWardenChecksForType(checkType)); ++y)
{
// If todo list is done break loop (will be filled on next Update()
run)
if (_ChecksTodo[checkType].empty())
{
break;
}
_CurrentChecks.push_back(checkId);
}
ByteBuffer buff;
buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST);
// Add TIMING_CHECK
buff << uint8(0x00);
buff << uint8(TIMING_CHECK ^ xorByte);
uint8 index = 1;
std::stringstream stream;
stream << "Sent check id's: ";
for (uint16 checkId : _CurrentChecks)
{
stream << checkId << " ";
}
_dataSent = false;
_clientResponseTimer = 0;
uint16 Length;
buff >> Length;
uint32 Checksum;
buff >> Checksum;
// TIMING_CHECK
{
uint8 result;
buff >> result;
// TODO: test it.
if (result == 0x00)
{
LOG_DEBUG("warden", "TIMING CHECK FAIL result 0x00");
// ApplyPenalty(0, "TIMING CHECK FAIL result"); Commented out because
of too many false postives. Mostly caused by client stutter.
return;
}
uint32 newClientTicks;
buff >> newClientTicks;
uint16 checkFailed = 0;
if (Mem_Result != 0)
{
LOG_DEBUG("warden", "RESULT MEM_CHECK not 0x00, CheckId {}
account Id {}", checkId, _session->GetAccountId());
checkFailed = checkId;
continue;
}
buff.rpos(buff.rpos() + rd->Length);
LOG_DEBUG("warden", "RESULT MEM_CHECK passed CheckId {} account Id
{}", checkId, _session->GetAccountId());
break;
}
case PAGE_CHECK_A:
case PAGE_CHECK_B:
case DRIVER_CHECK:
case MODULE_CHECK:
{
const uint8 byte = 0xE9;
if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8))
!= 0)
{
if (type == PAGE_CHECK_A || type == PAGE_CHECK_B)
LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId {}
account Id {}", checkId, _session->GetAccountId());
if (type == MODULE_CHECK)
LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId
{} account Id {}", checkId, _session->GetAccountId());
if (type == DRIVER_CHECK)
LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId
{} account Id {}", checkId, _session->GetAccountId());
checkFailed = checkId;
buff.rpos(buff.rpos() + 1);
continue;
}
buff.rpos(buff.rpos() + 1);
if (Mpq_Result != 0)
{
LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id
{}", _session->GetAccountId());
checkFailed = checkId;
continue;
}
buff.rpos(buff.rpos() +
Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes
SHA1
LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId {}
account Id {}", checkId, _session->GetAccountId());
break;
}
}
}
if (checkFailed > 0)
{
ApplyPenalty(checkFailed, "");
}