aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2020-08-21 01:19:28 +0200
committerGitHub <noreply@github.com>2020-08-21 01:19:28 +0200
commit0531463a3c02d98ed77e9c7689ad7237ecaddc09 (patch)
treeea90cdae6712a1f8245e1e55ff052a7ef8e859f6 /src
parentdb8bdaa08fbce1f2481ce79cb97a6f03725c5389 (diff)
Core/Warden: Add Lua checks to Warden (PR #25286)
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp7
-rw-r--r--src/server/game/Warden/Warden.cpp26
-rw-r--r--src/server/game/Warden/Warden.h1
-rw-r--r--src/server/game/Warden/WardenCheckMgr.cpp21
-rw-r--r--src/server/game/Warden/WardenCheckMgr.h6
-rw-r--r--src/server/game/Warden/WardenWin.cpp46
-rw-r--r--src/server/game/Warden/enuminfo_WardenCheckMgr.cpp18
7 files changed, 93 insertions, 32 deletions
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index a43707d0ef6..c9e69ba6c0a 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -39,6 +39,7 @@
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "Util.h"
+#include "Warden.h"
#include "World.h"
#include "WorldPacket.h"
#include <algorithm>
@@ -221,6 +222,12 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
if (msg.size() > 255)
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) && (lang == LANG_ADDON))
+ {
+ if (_warden && _warden->ProcessLuaCheckResponse(msg))
+ return;
+ }
// no chat commands in AFK/DND autoreply, and it can be empty
if (!(type == CHAT_MSG_AFK || type == CHAT_MSG_DND))
diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp
index 4a99ae9155e..8f109e387df 100644
--- a/src/server/game/Warden/Warden.cpp
+++ b/src/server/game/Warden/Warden.cpp
@@ -30,6 +30,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::HandleWardenDataOpcode(WorldPacket& recvData)
{
if (!_warden || recvData.empty())
diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h
index d404622af17..1a81a6be422 100644
--- a/src/server/game/Warden/Warden.h
+++ b/src/server/game/Warden/Warden.h
@@ -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;
diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp
index 5cbda5d1645..8aeef16d6c2 100644
--- a/src/server/game/Warden/WardenCheckMgr.cpp
+++ b/src/server/game/Warden/WardenCheckMgr.cpp
@@ -71,6 +71,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;
@@ -88,13 +94,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));
diff --git a/src/server/game/Warden/WardenCheckMgr.h b/src/server/game/Warden/WardenCheckMgr.h
index 9583684705c..7b0f81cf422 100644
--- a/src/server/game/Warden/WardenCheckMgr.h
+++ b/src/server/game/Warden/WardenCheckMgr.h
@@ -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
diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp
index 5e3cc00e895..41ba180d381 100644
--- a/src/server/game/Warden/WardenWin.cpp
+++ b/src/server/game/Warden/WardenWin.cpp
@@ -36,6 +36,14 @@
#include "WorldPacket.h"
#include "WorldSession.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);
+
+
WardenWin::WardenWin() : Warden(), _serverTicks(0)
{
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
@@ -103,7 +111,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);
@@ -212,7 +220,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());
@@ -254,7 +271,7 @@ void WardenWin::RequestChecks()
break;
}
case MPQ_CHECK:
- case LUA_STR_CHECK:
+ case LUA_EVAL_CHECK:
{
buff << uint8(index++);
break;
@@ -409,26 +426,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:
diff --git a/src/server/game/Warden/enuminfo_WardenCheckMgr.cpp b/src/server/game/Warden/enuminfo_WardenCheckMgr.cpp
index 8496c4ca8aa..7a0eadda436 100644
--- a/src/server/game/Warden/enuminfo_WardenCheckMgr.cpp
+++ b/src/server/game/Warden/enuminfo_WardenCheckMgr.cpp
@@ -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;