Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
WardenWin.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "AccountMgr.h"
7#include "ByteBuffer.h"
8#include "Common.h"
9#include "CryptoRandom.h"
11#include "HMAC.h"
12#include "Log.h"
13#include "MD5.h"
14#include "Opcodes.h"
15#include "Player.h"
16#include "SessionKeyGenerator.h"
17#include "Util.h"
18#include "WardenCheckMgr.h"
19#include "WardenModuleWin.h"
20#include "WardenWin.h"
21#include "World.h"
22#include "WorldPacket.h"
23#include "WorldSession.h"
24#include <shared_mutex>
25
27
29
30void WardenWin::Init(WorldSession* session, SessionKey const& k)
31{
32 _session = session;
33 // Generate Warden Key
35 WK.Generate(_inputKey, 16);
36 WK.Generate(_outputKey, 16);
37
38 memcpy(_seed, Module.Seed, 16);
39
40 _inputCrypto.Init(_inputKey, 16);
41 _outputCrypto.Init(_outputKey, 16);
42 SF_LOG_DEBUG("warden", "Server side warden for client %u initializing...", session->GetAccountId());
43 SF_LOG_DEBUG("warden", "C->S Key: %s", SkyFire::Impl::ByteArrayToHexStr(_inputKey, 16).c_str());
44 SF_LOG_DEBUG("warden", "S->C Key: %s", SkyFire::Impl::ByteArrayToHexStr(_outputKey, 16).c_str());
45 SF_LOG_DEBUG("warden", " Seed: %s", SkyFire::Impl::ByteArrayToHexStr(_seed, 16).c_str());
46 SF_LOG_DEBUG("warden", "Loading Module...");
47
49
50 SF_LOG_DEBUG("warden", "Module Key: %s", SkyFire::Impl::ByteArrayToHexStr(_module->Key, 16).c_str());
51 SF_LOG_DEBUG("warden", "Module ID: %s", SkyFire::Impl::ByteArrayToHexStr(_module->Id, 16).c_str());
53}
54
56{
58
59 uint32 length = sizeof(Module.Module);
60
61 // data assign
62 mod->CompressedSize = length;
63 mod->CompressedData = new uint8[length];
64 memcpy(mod->CompressedData, Module.Module, length);
65 memcpy(mod->Key, Module.ModuleKey, 16);
66
67 MD5Hash md5;
68 md5.UpdateData(mod->CompressedData, length);
69 md5.Finalize(mod->CompressedData, length);
70
71 return mod;
72}
73
75{
76 SF_LOG_DEBUG("warden", "Initialize module");
77
78 // Create packet structure
81 Request.Size1 = 20;
82 Request.Unk1 = 1;
83 Request.Unk2 = 0;
84 Request.Type = 1;
85 Request.String_library1 = 0;
86 Request.Function1[0] = 0x00024F80; // 0x00400000 + 0x00024F80 SFileOpenFile
87 Request.Function1[1] = 0x000218C0; // 0x00400000 + 0x000218C0 SFileGetFileSize
88 Request.Function1[2] = 0x00022530; // 0x00400000 + 0x00022530 SFileReadFile
89 Request.Function1[3] = 0x00022910; // 0x00400000 + 0x00022910 SFileCloseFile
90 Request.CheckSumm1 = BuildChecksum(&Request.Unk1, 20);
91
93 Request.Size2 = 8;
94 Request.Unk3 = 4;
95 Request.Unk4 = 0;
96 Request.String_library2 = 0;
97 Request.Function2 = 0x00419D40; // 0x00400000 + 0x00419D40 FrameScript::GetText
98 Request.Function2_set = 1;
99 Request.CheckSumm2 = BuildChecksum(&Request.Unk2, 8);
100
102 Request.Size3 = 8;
103 Request.Unk5 = 1;
104 Request.Unk6 = 1;
105 Request.String_library3 = 0;
106 Request.Function3 = 0x0046AE20; // 0x00400000 + 0x0046AE20 PerformanceCounter
107 Request.Function3_set = 1;
108 Request.CheckSumm3 = BuildChecksum(&Request.Unk5, 8);
109
110 // Encrypt with warden RC4 key.
111 EncryptData((uint8*)&Request, sizeof(WardenInitModuleRequest));
112
114 pkt.append((uint8*)&Request, sizeof(WardenInitModuleRequest));
115 _session->SendPacket(&pkt);
116}
117
119{
120 SF_LOG_DEBUG("warden", "Request hash");
121
122 // Create packet structure
123 WardenHashRequest Request;
125 memcpy(Request.Seed, _seed, 16);
126
127 // Encrypt with warden RC4 key.
128 EncryptData((uint8*)&Request, sizeof(WardenHashRequest));
129
131 pkt.append((uint8*)&Request, sizeof(WardenHashRequest));
132 _session->SendPacket(&pkt);
133}
134
136{
137 buff.rpos(buff.wpos());
138
139 // Verify key
140 if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, 20) != 0)
141 {
142 SF_LOG_WARN("warden", "%s failed hash reply. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
143 return;
144 }
145
146 SF_LOG_DEBUG("warden", "Request hash reply: succeed");
147
148 // Change keys here
149 memcpy(_inputKey, Module.ClientKeySeed, 16);
150 memcpy(_outputKey, Module.ServerKeySeed, 16);
151
152 _inputCrypto.Init(_inputKey, 16);
153 _outputCrypto.Init(_outputKey, 16);
154
155 _initialized = true;
156
158}
159
161{
162 SF_LOG_DEBUG("warden", "Request data");
163
164 // If all checks were done, fill the todo list again
165 if (_memChecksTodo.empty())
166 _memChecksTodo.assign(sWardenCheckMgr->MemChecksIdPool.begin(), sWardenCheckMgr->MemChecksIdPool.end());
167
168 if (_otherChecksTodo.empty())
169 _otherChecksTodo.assign(sWardenCheckMgr->OtherChecksIdPool.begin(), sWardenCheckMgr->OtherChecksIdPool.end());
170
172
173 uint16 id;
174 uint8 type;
175 WardenCheck* wd;
176 _currentChecks.clear();
177
178 // Build check request
179 for (uint32 i = 0; i < sWorld->getIntConfig(WorldIntConfigs::CONFIG_WARDEN_NUM_MEM_CHECKS); ++i)
180 {
181 // If todo list is done break loop (will be filled on next Update() run)
182 if (_memChecksTodo.empty())
183 break;
184
185 // Get check id from the end and remove it from todo
186 id = _memChecksTodo.back();
187 _memChecksTodo.pop_back();
188
189 // Add the id to the list sent in this cycle
190 _currentChecks.push_back(id);
191 }
192
193 ByteBuffer buff;
195
196 std::shared_lock<Skyfire::SharedMutex> guard(sWardenCheckMgr->_checkStoreLock);
197
198 for (uint32 i = 0; i < sWorld->getIntConfig(WorldIntConfigs::CONFIG_WARDEN_NUM_OTHER_CHECKS); ++i)
199 {
200 // If todo list is done break loop (will be filled on next Update() run)
201 if (_otherChecksTodo.empty())
202 break;
203
204 // Get check id from the end and remove it from todo
205 id = _otherChecksTodo.back();
206 _otherChecksTodo.pop_back();
207
208 // Add the id to the list sent in this cycle
209 _currentChecks.push_back(id);
210
211 wd = sWardenCheckMgr->GetWardenDataById(id);
212
213 switch (wd->Type)
214 {
215 case MPQ_CHECK:
216 case LUA_STR_CHECK:
217 case DRIVER_CHECK:
218 buff << uint8(wd->Str.size());
219 buff.append(wd->Str.c_str(), wd->Str.size());
220 break;
221 default:
222 break;
223 }
224 }
225
226 uint8 xorByte = _inputKey[0];
227
228 // Add TIMING_CHECK
229 buff << uint8(0x00);
230 buff << uint8(TIMING_CHECK ^ xorByte);
231
232 uint8 index = 1;
233
234 for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
235 {
236 wd = sWardenCheckMgr->GetWardenDataById(*itr);
237
238 type = wd->Type;
239 buff << uint8(type ^ xorByte);
240 switch (type)
241 {
242 case MEM_CHECK:
243 {
244 buff << uint8(0x00);
245 buff << uint32(wd->Address);
246 buff << uint8(wd->Length);
247 break;
248 }
249 case PAGE_CHECK_A:
250 case PAGE_CHECK_B:
251 {
252 std::vector<uint8> data = wd->Data.ToByteVector(0, false);
253 buff.append(data.data(), data.size());
254 buff << uint32(wd->Address);
255 buff << uint8(wd->Length);
256 break;
257 }
258 case MPQ_CHECK:
259 case LUA_STR_CHECK:
260 {
261 buff << uint8(index++);
262 break;
263 }
264 case DRIVER_CHECK:
265 {
266 std::vector<uint8> data = wd->Data.ToByteVector(0, false);
267 buff.append(data.data(), data.size());
268 buff << uint8(index++);
269 break;
270 }
271 case MODULE_CHECK:
272 {
273 std::array<uint8, 4> seed = SkyFire::Crypto::GetRandomBytes<4>();
274 buff.append(seed);
276 break;
277 }
278 /*case PROC_CHECK:
279 {
280 buff.append(wd->i.AsByteArray(0, false).get(), wd->i.GetNumBytes());
281 buff << uint8(index++);
282 buff << uint8(index++);
283 buff << uint32(wd->Address);
284 buff << uint8(wd->Length);
285 break;
286 }*/
287 default:
288 break; // Should never happen
289 }
290 }
291 buff << uint8(xorByte);
292 buff.hexlike();
293
294 // Encrypt with warden RC4 key
295 EncryptData(buff.contents(), buff.size());
296
297 WorldPacket pkt(SMSG_WARDEN_DATA, buff.size());
298 pkt.append(buff);
299 _session->SendPacket(&pkt);
300
301 _dataSent = true;
302
303 std::stringstream stream;
304 stream << "Sent check id's: ";
305 for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
306 stream << *itr << " ";
307
308 SF_LOG_DEBUG("warden", "%s", stream.str().c_str());
309}
310
312{
313 SF_LOG_DEBUG("warden", "Handle data");
314
315 _dataSent = false;
317
318 uint16 Length;
319 buff >> Length;
320 uint32 Checksum;
321 buff >> Checksum;
322
323 if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length))
324 {
325 buff.rpos(buff.wpos());
326 SF_LOG_WARN("warden", "%s failed checksum. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
327 return;
328 }
329
330 // TIMING_CHECK
331 {
332 uint8 result;
333 buff >> result;
335 if (result == 0x00)
336 {
337 SF_LOG_WARN("warden", "%s failed timing check. Action: %s", _session->GetPlayerInfo().c_str(), Penalty().c_str());
338 return;
339 }
340
341 uint32 newClientTicks;
342 buff >> newClientTicks;
343
344 uint32 ticksNow = getMSTime();
345 uint32 ourTicks = newClientTicks + (ticksNow - _serverTicks);
346
347 SF_LOG_DEBUG("warden", "ServerTicks %u", ticksNow); // Now
348 SF_LOG_DEBUG("warden", "RequestTicks %u", _serverTicks); // At request
349 SF_LOG_DEBUG("warden", "Ticks %u", newClientTicks); // At response
350 SF_LOG_DEBUG("warden", "Ticks diff %u", ourTicks - newClientTicks);
351 }
352
354 WardenCheck* rd;
355 uint8 type;
356 uint16 checkFailed = 0;
357
358 std::shared_lock<Skyfire::SharedMutex> guard(sWardenCheckMgr->_checkStoreLock);
359
360 for (std::list<uint16>::iterator itr = _currentChecks.begin(); itr != _currentChecks.end(); ++itr)
361 {
362 rd = sWardenCheckMgr->GetWardenDataById(*itr);
363 rs = sWardenCheckMgr->GetWardenResultById(*itr);
364
365 type = rd->Type;
366 switch (type)
367 {
368 case MEM_CHECK:
369 {
370 uint8 Mem_Result;
371 buff >> Mem_Result;
372
373 if (Mem_Result != 0)
374 {
375 SF_LOG_DEBUG("warden", "RESULT MEM_CHECK not 0x00, CheckId %u account Id %u", *itr, _session->GetAccountId());
376 checkFailed = *itr;
377 continue;
378 }
379
380 std::vector<uint8> result = rs->Result.ToByteVector(0, false);
381 if (memcmp(buff.contents() + buff.rpos(), result.data(), rd->Length) != 0)
382 {
383 SF_LOG_DEBUG("warden", "RESULT MEM_CHECK fail CheckId %u account Id %u", *itr, _session->GetAccountId());
384 checkFailed = *itr;
385 buff.rpos(buff.rpos() + rd->Length);
386 continue;
387 }
388
389 buff.rpos(buff.rpos() + rd->Length);
390 SF_LOG_DEBUG("warden", "RESULT MEM_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
391 break;
392 }
393 case PAGE_CHECK_A:
394 case PAGE_CHECK_B:
395 case DRIVER_CHECK:
396 case MODULE_CHECK:
397 {
398 const uint8 byte = 0xE9;
399 if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8)) != 0)
400 {
401 if (type == PAGE_CHECK_A || type == PAGE_CHECK_B)
402 SF_LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
403 if (type == MODULE_CHECK)
404 SF_LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
405 if (type == DRIVER_CHECK)
406 SF_LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
407 checkFailed = *itr;
408 buff.rpos(buff.rpos() + 1);
409 continue;
410 }
411
412 buff.rpos(buff.rpos() + 1);
413 if (type == PAGE_CHECK_A || type == PAGE_CHECK_B)
414 SF_LOG_DEBUG("warden", "RESULT PAGE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
415 else if (type == MODULE_CHECK)
416 SF_LOG_DEBUG("warden", "RESULT MODULE_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
417 else if (type == DRIVER_CHECK)
418 SF_LOG_DEBUG("warden", "RESULT DRIVER_CHECK passed CheckId %u account Id %u", *itr, _session->GetAccountId());
419 break;
420 }
421 case LUA_STR_CHECK:
422 {
423 uint8 Lua_Result;
424 buff >> Lua_Result;
425
426 if (Lua_Result != 0)
427 {
428 SF_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
429 checkFailed = *itr;
430 continue;
431 }
432
433 uint8 luaStrLen;
434 buff >> luaStrLen;
435
436 if (luaStrLen != 0)
437 {
438 char* str = new char[luaStrLen + 1];
439 memcpy(str, buff.contents() + buff.rpos(), luaStrLen);
440 str[luaStrLen] = '\0'; // null terminator
441 SF_LOG_DEBUG("warden", "Lua string: %s", str);
442 delete[] str;
443 }
444 buff.rpos(buff.rpos() + luaStrLen); // Skip string
445 SF_LOG_DEBUG("warden", "RESULT LUA_STR_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId());
446 break;
447 }
448 case MPQ_CHECK:
449 {
450 uint8 Mpq_Result;
451 buff >> Mpq_Result;
452
453 if (Mpq_Result != 0)
454 {
455 SF_LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id %u", _session->GetAccountId());
456 checkFailed = *itr;
457 continue;
458 }
459
460 if (memcmp(buff.contents() + buff.rpos(), rs->Result.ToByteArray<20>(false).data(), SkyFire::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0) // SHA1
461 {
462 SF_LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId %u account Id %u", *itr, _session->GetAccountId());
463 checkFailed = *itr;
464 buff.rpos(buff.rpos() + SkyFire::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1
465 continue;
466 }
467
468 buff.rpos(buff.rpos() + SkyFire::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1
469 SF_LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId %u account Id %u", *itr, _session->GetAccountId());
470 break;
471 }
472 default: // Should never happen
473 break;
474 }
475 }
476
477 if (checkFailed > 0)
478 {
479 WardenCheck* check = sWardenCheckMgr->GetWardenDataById(checkFailed);
480 SF_LOG_WARN("warden", "%s failed Warden check %u. Action: %s", _session->GetPlayerInfo().c_str(), checkFailed, Penalty(check).c_str());
481 }
482
483 // Set hold off timer, minimum timer should at least be 1 second
485 _checkTimer = (holdOff < 1 ? 1 : holdOff) * IN_MILLISECONDS;
486}
std::array< uint8, SESSION_KEY_LENGTH > SessionKey
Definition AuthDefines.h:8
@ IN_MILLISECONDS
Definition Common.h:125
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint16_t uint16
Definition Define.h:78
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_WARN(filterType__,...)
Definition Log.h:140
uint32 getMSTime()
Definition Timer.h:12
@ DRIVER_CHECK
Definition Warden.h:41
@ PAGE_CHECK_A
Definition Warden.h:37
@ PAGE_CHECK_B
Definition Warden.h:38
@ MPQ_CHECK
Definition Warden.h:39
@ MEM_CHECK
Definition Warden.h:36
@ TIMING_CHECK
Definition Warden.h:42
@ MODULE_CHECK
Definition Warden.h:44
@ LUA_STR_CHECK
Definition Warden.h:40
@ WARDEN_SMSG_HASH_REQUEST
Definition Warden.h:31
@ WARDEN_SMSG_MODULE_INITIALIZE
Definition Warden.h:29
@ WARDEN_SMSG_CHEAT_CHECKS_REQUEST
Definition Warden.h:28
#define sWardenCheckMgr
struct Module_79C0768D657977D697E10BAD956CCED1 Module
std::vector< uint8 > ToByteVector(int32 minSize=0, bool littleEndian=true) const
std::array< uint8, Size > ToByteArray(bool littleEndian=true) const
Definition BigNumber.h:95
size_t rpos() const
Definition ByteBuffer.h:470
void hexlike() const
void append(T value)
Definition ByteBuffer.h:147
size_t wpos() const
Definition ByteBuffer.h:483
size_t size() const
Definition ByteBuffer.h:609
uint8 * contents()
Definition ByteBuffer.h:605
Definition MD5.h:14
void UpdateData(const std::string &str)
Definition MD5.cpp:19
void Finalize(const uint8 *data, uint32 len)
Definition MD5.cpp:29
void Generate(uint8 *buf, uint32 sz)
static Digest GetDigestOf(Container const &seed, uint8 const *data, size_t len)
Definition HMAC.h:40
Warden()
Definition Warden.cpp:20
void EncryptData(uint8 *buffer, uint32 length)
Definition Warden.cpp:118
uint32 _checkTimer
Definition Warden.h:126
SkyFire::Crypto::ARC4 _outputCrypto
Definition Warden.h:125
uint32 _clientResponseTimer
Definition Warden.h:127
SkyFire::Crypto::ARC4 _inputCrypto
Definition Warden.h:124
uint8 _inputKey[16]
Definition Warden.h:121
uint32 _previousTimestamp
Definition Warden.h:129
uint8 _outputKey[16]
Definition Warden.h:122
uint8 _seed[16]
Definition Warden.h:123
void RequestModule()
Definition Warden.cpp:56
ClientWardenModule * _module
Definition Warden.h:130
static bool IsValidCheckSum(uint32 checksum, const uint8 *data, const uint16 length)
Definition Warden.cpp:123
bool _dataSent
Definition Warden.h:128
bool _initialized
Definition Warden.h:131
static uint32 BuildChecksum(const uint8 *data, uint32 length)
Definition Warden.cpp:154
std::string Penalty(WardenCheck *check=NULL)
Definition Warden.cpp:165
WorldSession * _session
Definition Warden.h:120
void RequestHash()
void HandleHashResult(ByteBuffer &buff)
void RequestData()
void HandleData(ByteBuffer &buff)
std::list< uint16 > _memChecksTodo
Definition WardenWin.h:77
void Init(WorldSession *session, SessionKey const &K)
Definition WardenWin.cpp:30
ClientWardenModule * GetModuleForClient()
Definition WardenWin.cpp:55
uint32 _serverTicks
Definition WardenWin.h:75
std::list< uint16 > _otherChecksTodo
Definition WardenWin.h:76
void InitializeModule()
Definition WardenWin.cpp:74
std::list< uint16 > _currentChecks
Definition WardenWin.h:78
Player session in the World.
uint32 GetAccountId() const
@ SMSG_WARDEN_DATA
Definition Opcodes.h:1064
#define sWorld
Definition World.h:910
@ CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF
Definition World.h:334
@ CONFIG_WARDEN_NUM_MEM_CHECKS
Definition World.h:337
@ CONFIG_WARDEN_NUM_OTHER_CHECKS
Definition World.h:338
std::array< uint8, S > GetRandomBytes()
std::string ByteArrayToHexStr(uint8 const *bytes, size_t length, bool reverse=false)
Definition Util.cpp:515
uint8 * CompressedData
Definition Warden.h:85
uint32 CompressedSize
Definition Warden.h:84
uint8 Key[16]
Definition Warden.h:83
static constexpr size_t SHA1_DIGEST_LENGTH_BYTES
std::string Str
BigNumber Data
uint8 Seed[16]
Definition Warden.h:71