mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Warden: Add Lua checks to Warden (PR #25286)
(cherry picked from commit 0531463a3c)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
--
|
||||
DELETE FROM `warden_checks` WHERE `id` IN (788,789,790);
|
||||
INSERT INTO `warden_checks` (`id`,`type`,`str`,`address`,`length`,`result`, `comment`) VALUES
|
||||
(788, 139, 'forceinsecure() return issecure()', NULL, NULL, NULL, 'Detects naive Lua unlockers'),
|
||||
(789, 139, 'return not not PQR_IsMoving', NULL, NULL, NULL, 'Detects PQR'),
|
||||
(790, 139, 'local f=DEFAULT_CHAT_FRAME for i=1,f:GetNumMessages() do if (f:GetMessageInfo(i)):find("|cffffd200PQR|r") then return true end end', NULL, NULL, NULL, 'Detects PQR');
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "Util.h"
|
||||
#include "Warden.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include <algorithm>
|
||||
@@ -217,7 +218,6 @@ void WorldSession::HandleChatMessage(ChatMsg type, Language lang, std::string ms
|
||||
if (msg.size() > 255)
|
||||
return;
|
||||
|
||||
|
||||
if (msg.empty())
|
||||
return;
|
||||
|
||||
@@ -466,6 +466,13 @@ void WorldSession::HandleChatAddonMessage(ChatMsg type, std::string prefix, std:
|
||||
if (prefix.empty() || prefix.length() > 16)
|
||||
return;
|
||||
|
||||
// Our Warden module also uses SendAddonMessage as a way to communicate Lua check results to the server, see if this is that
|
||||
if (type == CHAT_MSG_GUILD)
|
||||
{
|
||||
if (_warden && _warden->ProcessLuaCheckResponse(text))
|
||||
return;
|
||||
}
|
||||
|
||||
// Disabled addon channel?
|
||||
if (!sWorld->getBoolConfig(CONFIG_ADDON_CHANNEL))
|
||||
return;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
|
||||
#include <charconv>
|
||||
|
||||
Warden::Warden() : _session(nullptr), _checkTimer(10 * IN_MILLISECONDS), _clientResponseTimer(0),
|
||||
_dataSent(false), _initialized(false)
|
||||
{
|
||||
@@ -254,6 +256,30 @@ void Warden::HandleData(ByteBuffer& buff)
|
||||
}
|
||||
}
|
||||
|
||||
bool Warden::ProcessLuaCheckResponse(std::string const& msg)
|
||||
{
|
||||
static constexpr char WARDEN_TOKEN[] = "_TW\t";
|
||||
if (!StringStartsWith(msg, WARDEN_TOKEN))
|
||||
return false;
|
||||
|
||||
uint16 id = 0;
|
||||
std::from_chars(msg.data() + sizeof(WARDEN_TOKEN) - 1, msg.data() + msg.size(), id, 10);
|
||||
if (id < sWardenCheckMgr->GetMaxValidCheckId())
|
||||
{
|
||||
WardenCheck const& check = sWardenCheckMgr->GetCheckData(id);
|
||||
if (check.Type == LUA_EVAL_CHECK)
|
||||
{
|
||||
char const* penalty = ApplyPenalty(&check);
|
||||
TC_LOG_WARN("warden", "%s failed Warden check %u (%s). Action: %s", _session->GetPlayerInfo().c_str(), id, EnumUtils::ToConstant(check.Type), penalty);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
char const* penalty = ApplyPenalty(nullptr);
|
||||
TC_LOG_WARN("warden", "%s sent bogus Lua check response for Warden. Action: %s", _session->GetPlayerInfo().c_str(), penalty);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldSession::HandleWardenData(WorldPackets::Warden::WardenData& packet)
|
||||
{
|
||||
if (!_warden || packet.Data.empty())
|
||||
|
||||
@@ -91,6 +91,7 @@ class TC_GAME_API Warden
|
||||
virtual void Init(WorldSession* session, SessionKey const& K) = 0;
|
||||
void Update(uint32 diff);
|
||||
void HandleData(ByteBuffer& buff);
|
||||
bool ProcessLuaCheckResponse(std::string const& msg);
|
||||
|
||||
virtual size_t DEBUG_ForceSpecificChecks(std::vector<uint16> const& checks) = 0;
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ void WardenCheckMgr::LoadWardenChecks()
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((type == LUA_EVAL_CHECK) && (id > 9999))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Warden Lua check with id %u found in `warden_checks`. Lua checks may have four-digit IDs at most. Skipped.", id);
|
||||
continue;
|
||||
}
|
||||
|
||||
WardenCheck& wardenCheck = _checks[id];
|
||||
wardenCheck.CheckId = id;
|
||||
wardenCheck.Type = type;
|
||||
@@ -90,13 +96,26 @@ void WardenCheckMgr::LoadWardenChecks()
|
||||
wardenCheck.Length = fields[5].GetUInt8();
|
||||
|
||||
// PROC_CHECK support missing
|
||||
if (type == MEM_CHECK || type == MPQ_CHECK || type == LUA_STR_CHECK || type == DRIVER_CHECK || type == MODULE_CHECK)
|
||||
if (type == MEM_CHECK || type == MPQ_CHECK || type == LUA_EVAL_CHECK || type == DRIVER_CHECK || type == MODULE_CHECK)
|
||||
wardenCheck.Str = fields[6].GetString();
|
||||
|
||||
wardenCheck.Comment = fields[7].GetString();
|
||||
if (wardenCheck.Comment.empty())
|
||||
wardenCheck.Comment = "Undocumented Check";
|
||||
|
||||
if (type == LUA_EVAL_CHECK)
|
||||
{
|
||||
if (wardenCheck.Str.size() > WARDEN_MAX_LUA_CHECK_LENGTH)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Found over-long Lua check for Warden check with id %u in `warden_checks`. Max length is %u. Skipped.", id, WARDEN_MAX_LUA_CHECK_LENGTH);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string str = fmt::sprintf("%04u", id);
|
||||
ASSERT(str.size() == 4);
|
||||
std::copy(str.begin(), str.end(), wardenCheck.IdStr.begin());
|
||||
}
|
||||
|
||||
// initialize action with default action from config, this may be overridden later
|
||||
wardenCheck.Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ enum WardenCheckCategory : uint8
|
||||
// EnumUtils: DESCRIBE THIS
|
||||
enum WardenCheckType : uint8
|
||||
{
|
||||
LUA_STR_CHECK = 0x8B, // 139: byte luaNameIndex (check to ensure LUA string isn't used)
|
||||
NONE_CHECK = 0, // SKIP
|
||||
TIMING_CHECK = 87, // nyi
|
||||
DRIVER_CHECK = 113, // uint Seed + byte[20] SHA1 + byte driverNameIndex (check to ensure driver isn't loaded)
|
||||
PROC_CHECK = 126, // nyi
|
||||
LUA_EVAL_CHECK = 139, // evaluate arbitrary Lua check
|
||||
MPQ_CHECK = 152, // get hash of MPQ file (to check it is not modified)
|
||||
PAGE_CHECK_A = 178, // scans all pages for specified SHA1 hash
|
||||
PAGE_CHECK_B = 191, // scans only pages starts with MZ+PE headers for specified hash
|
||||
@@ -64,6 +64,7 @@ constexpr WardenCheckCategory GetWardenCheckCategory(WardenCheckType type)
|
||||
case TIMING_CHECK: return NUM_CHECK_CATEGORIES;
|
||||
case DRIVER_CHECK: return INJECT_CHECK_CATEGORY;
|
||||
case PROC_CHECK: return NUM_CHECK_CATEGORIES;
|
||||
case LUA_EVAL_CHECK: return LUA_CHECK_CATEGORY;
|
||||
case MPQ_CHECK: return MODDED_CHECK_CATEGORY;
|
||||
case PAGE_CHECK_A: return INJECT_CHECK_CATEGORY;
|
||||
case PAGE_CHECK_B: return INJECT_CHECK_CATEGORY;
|
||||
@@ -93,9 +94,12 @@ struct WardenCheck
|
||||
uint8 Length; // PROC_CHECK, MEM_CHECK, PAGE_CHECK
|
||||
std::string Str; // LUA, MPQ, DRIVER
|
||||
std::string Comment;
|
||||
std::array<char, 4> IdStr = {}; // LUA
|
||||
WardenActions Action;
|
||||
};
|
||||
|
||||
constexpr uint8 WARDEN_MAX_LUA_CHECK_LENGTH = 170;
|
||||
|
||||
using WardenCheckResult = std::vector<uint8>;
|
||||
|
||||
class TC_GAME_API WardenCheckMgr
|
||||
|
||||
@@ -37,6 +37,14 @@
|
||||
#include "WorldSession.h"
|
||||
#include <sstream>
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
WardenWin::WardenWin() : Warden(), _serverTicks(0)
|
||||
{
|
||||
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
|
||||
@@ -104,7 +112,7 @@ void WardenWin::InitializeModule()
|
||||
Request.Unk3 = 4;
|
||||
Request.Unk4 = 0;
|
||||
Request.String_library2 = 0;
|
||||
Request.Function2 = 0x00419D40; // 0x00400000 + 0x00419D40 FrameScript::GetText
|
||||
Request.Function2 = 0x00419210; // 0x00400000 + 0x00419210 FrameScript::Execute
|
||||
Request.Function2_set = 1;
|
||||
Request.CheckSumm2 = BuildChecksum(&Request.Unk3, 8);
|
||||
|
||||
@@ -213,7 +221,16 @@ void WardenWin::RequestChecks()
|
||||
uint16 const id = *(checksIt++);
|
||||
|
||||
WardenCheck const& check = sWardenCheckMgr->GetCheckData(id);
|
||||
if (!check.Str.empty())
|
||||
if (check.Type == LUA_EVAL_CHECK)
|
||||
{
|
||||
buff << uint8(sizeof(_luaEvalPrefix)-1 + check.Str.size() + sizeof(_luaEvalMidfix)-1 + check.IdStr.size() + sizeof(_luaEvalPostfix)-1);
|
||||
buff.append(_luaEvalPrefix, sizeof(_luaEvalPrefix)-1);
|
||||
buff.append(check.Str.data(), check.Str.size());
|
||||
buff.append(_luaEvalMidfix, sizeof(_luaEvalMidfix)-1);
|
||||
buff.append(check.IdStr.data(), check.IdStr.size());
|
||||
buff.append(_luaEvalPostfix, sizeof(_luaEvalPostfix)-1);
|
||||
}
|
||||
else if (!check.Str.empty())
|
||||
{
|
||||
buff << uint8(check.Str.size());
|
||||
buff.append(check.Str.data(), check.Str.size());
|
||||
@@ -255,7 +272,7 @@ void WardenWin::RequestChecks()
|
||||
break;
|
||||
}
|
||||
case MPQ_CHECK:
|
||||
case LUA_STR_CHECK:
|
||||
case LUA_EVAL_CHECK:
|
||||
{
|
||||
buff << uint8(index++);
|
||||
break;
|
||||
@@ -410,26 +427,13 @@ void WardenWin::HandleCheckResult(ByteBuffer &buff)
|
||||
TC_LOG_DEBUG("warden", "RESULT %s passed CheckId %u account Id %u", EnumUtils::ToConstant(check.Type), id, _session->GetAccountId());
|
||||
break;
|
||||
}
|
||||
case LUA_STR_CHECK:
|
||||
case LUA_EVAL_CHECK:
|
||||
{
|
||||
uint8 Lua_Result;
|
||||
buff >> Lua_Result;
|
||||
uint8 const result = buff.read<uint8>();
|
||||
if (result == 0)
|
||||
buff.read_skip(buff.read<uint8>()); // discard attached string
|
||||
|
||||
if (Lua_Result == 0)
|
||||
{
|
||||
uint8 luaStrLen = buff.read<uint8>();
|
||||
if (luaStrLen != 0)
|
||||
{
|
||||
std::string str;
|
||||
str.resize(luaStrLen);
|
||||
buff.read(reinterpret_cast<uint8*>(str.data()), luaStrLen);
|
||||
TC_LOG_DEBUG("warden", "Lua string: %s", str.c_str());
|
||||
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", id, _session->GetAccountId());
|
||||
checkFailed = id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TC_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", id, _session->GetAccountId());
|
||||
TC_LOG_DEBUG("warden", "LUA_EVAL_CHECK CheckId %u account Id %u got in-warden dummy response (%u)", id, _session->GetAccountId(), result);
|
||||
break;
|
||||
}
|
||||
case MPQ_CHECK:
|
||||
|
||||
@@ -115,10 +115,10 @@ TC_API_EXPORT EnumText EnumUtils<WardenCheckType>::ToString(WardenCheckType valu
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case LUA_STR_CHECK: return { "LUA_STR_CHECK", "LUA_STR_CHECK", "139: byte luaNameIndex (check to ensure LUA string isn't used)" };
|
||||
case TIMING_CHECK: return { "TIMING_CHECK", "TIMING_CHECK", "nyi" };
|
||||
case DRIVER_CHECK: return { "DRIVER_CHECK", "DRIVER_CHECK", "uint Seed + byte[20] SHA1 + byte driverNameIndex (check to ensure driver isn't loaded)" };
|
||||
case PROC_CHECK: return { "PROC_CHECK", "PROC_CHECK", "nyi" };
|
||||
case LUA_EVAL_CHECK: return { "LUA_EVAL_CHECK", "LUA_EVAL_CHECK", "evaluate arbitrary Lua check" };
|
||||
case MPQ_CHECK: return { "MPQ_CHECK", "MPQ_CHECK", "get hash of MPQ file (to check it is not modified)" };
|
||||
case PAGE_CHECK_A: return { "PAGE_CHECK_A", "PAGE_CHECK_A", "scans all pages for specified SHA1 hash" };
|
||||
case PAGE_CHECK_B: return { "PAGE_CHECK_B", "PAGE_CHECK_B", "scans only pages starts with MZ+PE headers for specified hash" };
|
||||
@@ -136,10 +136,10 @@ TC_API_EXPORT WardenCheckType EnumUtils<WardenCheckType>::FromIndex(size_t index
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return LUA_STR_CHECK;
|
||||
case 1: return TIMING_CHECK;
|
||||
case 2: return DRIVER_CHECK;
|
||||
case 3: return PROC_CHECK;
|
||||
case 0: return TIMING_CHECK;
|
||||
case 1: return DRIVER_CHECK;
|
||||
case 2: return PROC_CHECK;
|
||||
case 3: return LUA_EVAL_CHECK;
|
||||
case 4: return MPQ_CHECK;
|
||||
case 5: return PAGE_CHECK_A;
|
||||
case 6: return PAGE_CHECK_B;
|
||||
@@ -154,10 +154,10 @@ TC_API_EXPORT size_t EnumUtils<WardenCheckType>::ToIndex(WardenCheckType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case LUA_STR_CHECK: return 0;
|
||||
case TIMING_CHECK: return 1;
|
||||
case DRIVER_CHECK: return 2;
|
||||
case PROC_CHECK: return 3;
|
||||
case TIMING_CHECK: return 0;
|
||||
case DRIVER_CHECK: return 1;
|
||||
case PROC_CHECK: return 2;
|
||||
case LUA_EVAL_CHECK: return 3;
|
||||
case MPQ_CHECK: return 4;
|
||||
case PAGE_CHECK_A: return 5;
|
||||
case PAGE_CHECK_B: return 6;
|
||||
|
||||
Reference in New Issue
Block a user