Core/Warden: More refactors lifted from #25286.

This commit is contained in:
Treeston
2020-08-20 17:56:10 +02:00
parent 5b4288f05f
commit a3971ca4b0
9 changed files with 215 additions and 125 deletions

View File

@@ -0,0 +1,16 @@
--
ALTER TABLE `warden_checks`
MODIFY COLUMN `str` VARCHAR(170) DEFAULT NULL,
MODIFY COLUMN `result` VARBINARY(24) DEFAULT NULL;
-- shift all warden checks down by 1 so we no longer have a gap at id 0
-- (left join is for reapply safety)
UPDATE `warden_checks` wc1
LEFT JOIN `warden_checks` wc2 ON `wc2`.`id` = 0
SET `wc1`.`id` = `wc1`.`id`-1
WHERE `wc2`.`id` IS NULL;
DELETE FROM `warden_checks` WHERE `id` IN (786,787);
INSERT INTO `warden_checks` (`id`,`type`,`str`,`address`,`length`,`result`, `comment`) VALUES
(786, 243, NULL, 0x819210, 16, UNHEX('558BEC518305A013D40001A19C13D400'), 'FrameScript::Execute'),
(787, 243, NULL, 0x84F860, 15, UNHEX('558BEC8B4D108B450C83EC0883F903'), 'FrameScript::lua_loadbuffer');

View File

@@ -51,7 +51,7 @@ void WardenCheckMgr::LoadWardenChecks()
uint16 maxCheckId = fields[0].GetUInt16();
_checks.resize(maxCheckId + 1);
_checks.resize(maxCheckId+1);
// 0 1 2 3 4 5 6 7
result = WorldDatabase.Query("SELECT id, type, data, result, address, length, str, comment FROM warden_checks ORDER BY id ASC");
@@ -63,14 +63,18 @@ void WardenCheckMgr::LoadWardenChecks()
uint16 const id = fields[0].GetUInt16();
WardenCheckType const type = static_cast<WardenCheckType>(fields[1].GetUInt8());
WardenCheckCategory const category = GetWardenCheckCategory(type);
if (category == NUM_CHECK_CATEGORIES)
{
TC_LOG_ERROR("sql.sql", "Warden check with id %u lists check type %u in `warden_checks`, which is not supported. Skipped.", id, type);
continue;
}
WardenCheck& wardenCheck = _checks[id];
wardenCheck.CheckId = id;
wardenCheck.Type = type;
// Initialize action with default action from config
wardenCheck.Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
if (type == PAGE_CHECK_A || type == PAGE_CHECK_B || type == DRIVER_CHECK)
wardenCheck.Data = fields[2].GetBinary();
@@ -91,11 +95,10 @@ void WardenCheckMgr::LoadWardenChecks()
if (wardenCheck.Comment.empty())
wardenCheck.Comment = "Undocumented Check";
if (type == MEM_CHECK || type == MODULE_CHECK)
MemChecksIdPool.push_back(id);
else
OtherChecksIdPool.push_back(id);
// initialize action with default action from config, this may be overridden later
wardenCheck.Action = WardenActions(sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
_pools[category].push_back(id);
++count;
}
while (result->NextRow());

View File

@@ -19,6 +19,7 @@
#define _WARDENCHECKMGR_H
#include "Define.h"
#include "World.h"
#include <shared_mutex>
#include <unordered_map>
#include <vector>
@@ -31,24 +32,62 @@ enum WardenActions : uint8
WARDEN_ACTION_BAN // TITLE Ban
};
// EnumUtils: DESCRIBE THIS
enum WardenCheckCategory : uint8
{
INJECT_CHECK_CATEGORY = 0, // checks that test whether the client's execution has been interfered with
LUA_CHECK_CATEGORY, // checks that test whether the lua sandbox has been modified
MODDED_CHECK_CATEGORY, // checks that test whether the client has been modified
NUM_CHECK_CATEGORIES // SKIP
};
// 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)
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
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
MODULE_CHECK = 217, // check to make sure module isn't injected
MEM_CHECK = 243, // retrieve specific memory
};
constexpr WardenCheckCategory GetWardenCheckCategory(WardenCheckType type)
{
switch (type)
{
case TIMING_CHECK: return NUM_CHECK_CATEGORIES;
case DRIVER_CHECK: return INJECT_CHECK_CATEGORY;
case PROC_CHECK: return NUM_CHECK_CATEGORIES;
case MPQ_CHECK: return MODDED_CHECK_CATEGORY;
case PAGE_CHECK_A: return INJECT_CHECK_CATEGORY;
case PAGE_CHECK_B: return INJECT_CHECK_CATEGORY;
case MODULE_CHECK: return INJECT_CHECK_CATEGORY;
case MEM_CHECK: return MODDED_CHECK_CATEGORY;
default: return NUM_CHECK_CATEGORIES;
}
}
constexpr WorldIntConfigs GetWardenCategoryCountConfig(WardenCheckCategory category)
{
switch (category)
{
case INJECT_CHECK_CATEGORY: return CONFIG_WARDEN_NUM_INJECT_CHECKS;
case LUA_CHECK_CATEGORY: return CONFIG_WARDEN_NUM_LUA_CHECKS;
case MODDED_CHECK_CATEGORY: return CONFIG_WARDEN_NUM_CLIENT_MOD_CHECKS;
default: return INT_CONFIG_VALUE_COUNT;
}
}
struct WardenCheck
{
uint16 CheckId;
WardenCheckType Type;
WardenCheckType Type = NONE_CHECK;
std::vector<uint8> Data;
uint32 Address; // PROC_CHECK, MEM_CHECK, PAGE_CHECK
uint8 Length; // PROC_CHECK, MEM_CHECK, PAGE_CHECK
@@ -67,11 +106,11 @@ class TC_GAME_API WardenCheckMgr
public:
static WardenCheckMgr* instance();
uint16 GetMaxValidCheckId() const { return static_cast<uint16>(_checks.size()); }
WardenCheck const& GetCheckData(uint16 Id) const;
WardenCheckResult const& GetCheckResult(uint16 Id) const;
std::vector<uint16> const& GetAvailableMemoryChecks() const { return MemChecksIdPool; }
std::vector<uint16> const& GetAvailableOtherChecks() const { return OtherChecksIdPool; }
std::vector<uint16> const& GetAvailableChecks(WardenCheckCategory category) { return _pools[category]; }
void LoadWardenChecks();
void LoadWardenOverrides();
@@ -79,8 +118,7 @@ class TC_GAME_API WardenCheckMgr
private:
std::vector<WardenCheck> _checks;
std::unordered_map<uint16, WardenCheckResult> _checkResults;
std::vector<uint16> MemChecksIdPool;
std::vector<uint16> OtherChecksIdPool;
std::array<std::vector<uint16>, NUM_CHECK_CATEGORIES> _pools;
};
#define sWardenCheckMgr WardenCheckMgr::instance()

View File

@@ -38,13 +38,13 @@
WardenWin::WardenWin() : Warden(), _serverTicks(0)
{
_memChecks = sWardenCheckMgr->GetAvailableMemoryChecks();
Trinity::Containers::RandomShuffle(_memChecks);
_memChecksIt = _memChecks.begin();
_otherChecks = sWardenCheckMgr->GetAvailableOtherChecks();
Trinity::Containers::RandomShuffle(_otherChecks);
_otherChecksIt = _otherChecks.begin();
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
{
auto& [checks, checksIt] = _checks[category];
checks = sWardenCheckMgr->GetAvailableChecks(category);
Trinity::Containers::RandomShuffle(checks);
checksIt = checks.begin();
}
}
void WardenWin::Init(WorldSession* session, SessionKey const& K)
@@ -183,52 +183,43 @@ void WardenWin::RequestChecks()
TC_LOG_DEBUG("warden", "Request data");
// If all checks were done, fill the todo list again
if (_memChecksIt == _memChecks.end())
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
{
TC_LOG_DEBUG("warden", "Finished all mem checks, re-shuffling");
Trinity::Containers::RandomShuffle(_memChecks);
_memChecksIt = _memChecks.begin();
}
if (_otherChecksIt == _otherChecks.end())
{
TC_LOG_DEBUG("warden", "Finished all other checks, re-shuffling");
Trinity::Containers::RandomShuffle(_otherChecks);
_otherChecksIt = _otherChecks.begin();
auto& [checks, checksIt] = _checks[category];
if (checksIt == checks.end())
{
TC_LOG_DEBUG("warden", "Finished all %s checks, re-shuffling", EnumUtils::ToConstant(category));
Trinity::Containers::RandomShuffle(checks);
checksIt = checks.begin();
}
}
_serverTicks = GameTime::GetGameTimeMS();
_currentChecks.clear();
// Build check request
ByteBuffer buff;
buff << uint8(WARDEN_SMSG_CHEAT_CHECKS_REQUEST);
for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_WARDEN_NUM_MEM_CHECKS); ++i)
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
{
// 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 (_otherChecksIt == _otherChecks.end())
break;
uint16 const id = *(_otherChecksIt++);
WardenCheck const& check = sWardenCheckMgr->GetCheckData(id);
if (!check.Str.empty())
auto& [checks, checksIt] = _checks[category];
for (uint32 i = 0, n = sWorld->getIntConfig(GetWardenCategoryCountConfig(category)); i < n; ++i)
{
buff << uint8(check.Str.size());
buff.append(check.Str.data(), check.Str.size());
if (checksIt == checks.end()) // all checks were already sent, list will be re-filled on next Update() run
break;
uint16 const id = *(checksIt++);
WardenCheck const& check = sWardenCheckMgr->GetCheckData(id);
if (!check.Str.empty())
{
buff << uint8(check.Str.size());
buff.append(check.Str.data(), check.Str.size());
}
_currentChecks.push_back(id);
}
_currentChecks.push_back(id);
}
uint8 xorByte = _inputKey[0];
@@ -388,6 +379,7 @@ void WardenWin::HandleCheckResult(ByteBuffer &buff)
std::vector<uint8> response;
response.resize(check.Length);
buff.read(response.data(), response.size());
WardenCheckResult const& expected = sWardenCheckMgr->GetCheckResult(id);
if (response != expected)
{
@@ -421,10 +413,10 @@ void WardenWin::HandleCheckResult(ByteBuffer &buff)
uint8 Lua_Result;
buff >> Lua_Result;
if (Lua_Result != 0)
if (Lua_Result == 0)
{
uint8 luaStrLen = buff.read<uint8>();
if (luaStrLen == 0)
if (luaStrLen != 0)
{
std::string str;
str.resize(luaStrLen);
@@ -482,28 +474,29 @@ void WardenWin::HandleCheckResult(ByteBuffer &buff)
size_t WardenWin::DEBUG_ForceSpecificChecks(std::vector<uint16> const& checks)
{
std::vector<uint16>::iterator memChecksIt = _memChecks.begin();
std::vector<uint16>::iterator otherChecksIt = _otherChecks.begin();
std::array<std::vector<uint16>::iterator, NUM_CHECK_CATEGORIES> swapPositions;
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
swapPositions[category] = _checks[category].first.begin();
size_t n = 0;
for (uint16 check : checks)
{
if (auto it = std::find(memChecksIt, _memChecks.end(), check); it != _memChecks.end())
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
{
std::iter_swap(it, memChecksIt);
++memChecksIt;
++n;
}
else if (auto it = std::find(otherChecksIt, _otherChecks.end(), check); it != _otherChecks.end())
{
std::iter_swap(it, otherChecksIt);
++otherChecksIt;
++n;
std::vector<uint16>& checks = _checks[category].first;
std::vector<uint16>::iterator& swapPos = swapPositions[category];
if (auto it = std::find(swapPos, checks.end(), check); it != checks.end())
{
std::iter_swap(swapPos, it);
++swapPos;
++n;
break;
}
}
}
_memChecksIt = _memChecks.begin();
_otherChecksIt = _otherChecks.begin();
for (WardenCheckCategory category : EnumUtils::Iterate<WardenCheckCategory>())
_checks[category].second = _checks[category].first.begin();
return n;
}

View File

@@ -22,7 +22,9 @@
#include "Cryptography/BigNumber.h"
#include "ByteBuffer.h"
#include "Warden.h"
#include <array>
#include <list>
#include <utility>
#pragma pack(push, 1)
@@ -79,10 +81,7 @@ class TC_GAME_API WardenWin : public Warden
private:
uint32 _serverTicks;
std::vector<uint16> _memChecks;
std::vector<uint16>::const_iterator _memChecksIt;
std::vector<uint16> _otherChecks;
std::vector<uint16>::const_iterator _otherChecksIt;
std::array<std::pair<std::vector<uint16>, std::vector<uint16>::const_iterator>, NUM_CHECK_CATEGORIES> _checks;
std::vector<uint16> _currentChecks;
};

View File

@@ -65,6 +65,48 @@ TC_API_EXPORT size_t EnumUtils<WardenActions>::ToIndex(WardenActions value)
}
}
/****************************************************************************\
|* data for enum 'WardenCheckCategory' in 'WardenCheckMgr.h' auto-generated *|
\****************************************************************************/
template <>
TC_API_EXPORT EnumText EnumUtils<WardenCheckCategory>::ToString(WardenCheckCategory value)
{
switch (value)
{
case INJECT_CHECK_CATEGORY: return { "INJECT_CHECK_CATEGORY", "INJECT_CHECK_CATEGORY", "checks that test whether the client's execution has been interfered with" };
case LUA_CHECK_CATEGORY: return { "LUA_CHECK_CATEGORY", "LUA_CHECK_CATEGORY", "checks that test whether the lua sandbox has been modified" };
case MODDED_CHECK_CATEGORY: return { "MODDED_CHECK_CATEGORY", "MODDED_CHECK_CATEGORY", "checks that test whether the client has been modified" };
default: throw std::out_of_range("value");
}
}
template <>
TC_API_EXPORT size_t EnumUtils<WardenCheckCategory>::Count() { return 3; }
template <>
TC_API_EXPORT WardenCheckCategory EnumUtils<WardenCheckCategory>::FromIndex(size_t index)
{
switch (index)
{
case 0: return INJECT_CHECK_CATEGORY;
case 1: return LUA_CHECK_CATEGORY;
case 2: return MODDED_CHECK_CATEGORY;
default: throw std::out_of_range("index");
}
}
template <>
TC_API_EXPORT size_t EnumUtils<WardenCheckCategory>::ToIndex(WardenCheckCategory value)
{
switch (value)
{
case INJECT_CHECK_CATEGORY: return 0;
case LUA_CHECK_CATEGORY: return 1;
case MODDED_CHECK_CATEGORY: return 2;
default: throw std::out_of_range("value");
}
}
/************************************************************************\
|* data for enum 'WardenCheckType' in 'WardenCheckMgr.h' auto-generated *|
\************************************************************************/
@@ -73,15 +115,15 @@ TC_API_EXPORT EnumText EnumUtils<WardenCheckType>::ToString(WardenCheckType valu
{
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)" };
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 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" };
case MODULE_CHECK: return { "MODULE_CHECK", "MODULE_CHECK", "check to make sure module isn't injected" };
case MEM_CHECK: return { "MEM_CHECK", "MEM_CHECK", "retrieve specific memory" };
default: throw std::out_of_range("value");
}
}
@@ -94,15 +136,15 @@ 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;
case 0: return LUA_STR_CHECK;
case 1: return TIMING_CHECK;
case 2: return DRIVER_CHECK;
case 3: return PROC_CHECK;
case 4: return MPQ_CHECK;
case 5: return PAGE_CHECK_A;
case 6: return PAGE_CHECK_B;
case 7: return MODULE_CHECK;
case 8: return MEM_CHECK;
default: throw std::out_of_range("index");
}
}
@@ -112,15 +154,15 @@ TC_API_EXPORT size_t EnumUtils<WardenCheckType>::ToIndex(WardenCheckType value)
{
switch (value)
{
case MEM_CHECK: return 0;
case PAGE_CHECK_A: return 1;
case PAGE_CHECK_B: return 2;
case MPQ_CHECK: return 3;
case LUA_STR_CHECK: return 4;
case DRIVER_CHECK: return 5;
case TIMING_CHECK: return 6;
case PROC_CHECK: return 7;
case MODULE_CHECK: return 8;
case LUA_STR_CHECK: return 0;
case TIMING_CHECK: return 1;
case DRIVER_CHECK: return 2;
case PROC_CHECK: return 3;
case MPQ_CHECK: return 4;
case PAGE_CHECK_A: return 5;
case PAGE_CHECK_B: return 6;
case MODULE_CHECK: return 7;
case MEM_CHECK: return 8;
default: throw std::out_of_range("value");
}
}

View File

@@ -1415,8 +1415,9 @@ void World::LoadConfigSettings(bool reload)
// Warden
m_bool_configs[CONFIG_WARDEN_ENABLED] = sConfigMgr->GetBoolDefault("Warden.Enabled", false);
m_int_configs[CONFIG_WARDEN_NUM_MEM_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumMemChecks", 3);
m_int_configs[CONFIG_WARDEN_NUM_OTHER_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumOtherChecks", 7);
m_int_configs[CONFIG_WARDEN_NUM_INJECT_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumInjectionChecks", 9);
m_int_configs[CONFIG_WARDEN_NUM_LUA_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumLuaSandboxChecks", 1);
m_int_configs[CONFIG_WARDEN_NUM_CLIENT_MOD_CHECKS] = sConfigMgr->GetIntDefault("Warden.NumClientModChecks", 1);
m_int_configs[CONFIG_WARDEN_CLIENT_BAN_DURATION] = sConfigMgr->GetIntDefault("Warden.BanDuration", 86400);
m_int_configs[CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF] = sConfigMgr->GetIntDefault("Warden.ClientCheckHoldOff", 30);
m_int_configs[CONFIG_WARDEN_CLIENT_FAIL_ACTION] = sConfigMgr->GetIntDefault("Warden.ClientCheckFailAction", 0);

View File

@@ -356,8 +356,9 @@ enum WorldIntConfigs
CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF,
CONFIG_WARDEN_CLIENT_FAIL_ACTION,
CONFIG_WARDEN_CLIENT_BAN_DURATION,
CONFIG_WARDEN_NUM_MEM_CHECKS,
CONFIG_WARDEN_NUM_OTHER_CHECKS,
CONFIG_WARDEN_NUM_INJECT_CHECKS,
CONFIG_WARDEN_NUM_LUA_CHECKS,
CONFIG_WARDEN_NUM_CLIENT_MOD_CHECKS,
CONFIG_WINTERGRASP_PLR_MAX,
CONFIG_WINTERGRASP_PLR_MIN,
CONFIG_WINTERGRASP_PLR_MIN_LVL,

View File

@@ -1474,21 +1474,18 @@ HotSwap.ReCompilerBuildType = ""
Warden.Enabled = 0
#
# Warden.NumMemChecks
# Description: Number of Warden memory checks that are sent to the client each cycle.
# Default: 3 - (Enabled)
# 0 - (Disabled)
# Warden.NumInjectionChecks
# Warden.NumLuaSandboxChecks
# Warden.NumClientModChecks
# Description: Number of Warden checks of the respective category that are sent per cycle.
# Default: 7 - (Warden.NumInjectionChecks)
# Default: 1 - (Warden.NumLuaSandboxChecks)
# Default: 1 - (Warden.NumClientModChecks)
# 0 - (Disable category)
Warden.NumMemChecks = 3
#
# Warden.NumOtherChecks
# Description: Number of Warden checks other than memory checks that are added to request
# each checking cycle.
# Default: 7 - (Enabled)
# 0 - (Disabled)
Warden.NumOtherChecks = 7
Warden.NumInjectionChecks = 7
Warden.NumLuaSandboxChecks = 1
Warden.NumClientModChecks = 1
#
# Warden.ClientResponseDelay