diff --git a/include/game/warden_module.hpp b/include/game/warden_module.hpp index e11bc4f9..04029adf 100644 --- a/include/game/warden_module.hpp +++ b/include/game/warden_module.hpp @@ -13,6 +13,7 @@ namespace game { // Forward declarations class WardenEmulator; +class WardenCrypto; /** * Represents Warden callback functions exported by loaded module @@ -36,18 +37,19 @@ struct WardenFuncList { * Warden module loader and executor * * IMPLEMENTATION STATUS: - * ✅ Module metadata parsing - * ✅ Basic validation framework - * ⏳ RC4 decryption (uses existing WardenCrypto) - * ❌ RSA signature verification (TODO - requires OpenSSL RSA) - * ❌ zlib decompression (TODO - requires zlib library) - * ❌ Custom executable format parsing (TODO - major reverse engineering) - * ❌ Address relocation (TODO - x86 address fixups) - * ❌ API binding (TODO - kernel32/user32 function resolution) - * ❌ Native code execution (TODO - execute loaded x86 code) + * ✅ Module metadata parsing and validation + * ✅ RC4 decryption (WardenCrypto) + * ✅ RSA-2048 signature verification (OpenSSL EVP — placeholder modulus) + * ✅ zlib decompression + * ✅ Custom executable format parsing (3 pair-format variants) + * ✅ Address relocation (delta-encoded fixups) + * ✅ x86 emulation via Unicorn Engine (cross-platform) + * ✅ Client callbacks (sendPacket, validateModule, generateRC4) + * ⏳ API binding / IAT patching (stub — module imports not yet resolved) + * ⏳ RSA modulus needs verification against real WoW.exe build * - * For strict servers like Warmane, ALL TODOs must be implemented. - * For permissive servers, fake responses in GameHandler work. + * For strict servers, the API binding stub may cause module init to fail. + * For permissive servers, fake responses in WardenHandler work. */ class WardenModule { public: @@ -126,6 +128,12 @@ public: size_t getModuleSize() const { return moduleSize_; } const std::vector& getDecompressedData() const { return decompressedData_; } + // Inject dependencies for module callbacks (sendPacket, generateRC4). + // Must be called before initializeModule() so callbacks can reach the + // network layer and crypto state. + using SendPacketFunc = std::function; + void setCallbackDependencies(WardenCrypto* crypto, SendPacketFunc sendFunc); + private: bool loaded_; // Module successfully loaded std::vector md5Hash_; // Module identifier @@ -142,6 +150,11 @@ private: std::unique_ptr emulator_; // Cross-platform x86 emulator uint32_t emulatedPacketHandlerAddr_ = 0; // Raw emulated VA for 4-arg PacketHandler call + // Dependencies injected via setCallbackDependencies() for module callbacks. + // These are NOT owned — the handler owns the crypto and socket lifetime. + WardenCrypto* callbackCrypto_ = nullptr; + SendPacketFunc callbackSendPacket_; + // Validation and loading steps bool verifyMD5(const std::vector& data, const std::vector& expectedHash); diff --git a/src/game/warden_handler.cpp b/src/game/warden_handler.cpp index 8b999a51..1bd28a73 100644 --- a/src/game/warden_handler.cpp +++ b/src/game/warden_handler.cpp @@ -440,8 +440,21 @@ void WardenHandler::handleWardenData(network::Packet& packet) { } } - // Load the module (decrypt, decompress, parse, relocate) + // Load the module (decrypt, decompress, parse, relocate, init) wardenLoadedModule_ = std::make_shared(); + // Inject crypto and socket so module callbacks (sendPacket, generateRC4) + // can reach the network layer during initializeModule(). + wardenLoadedModule_->setCallbackDependencies( + wardenCrypto_.get(), + [this](const uint8_t* data, size_t len) { + if (!wardenCrypto_ || !owner_.socket) return; + std::vector plaintext(data, data + len); + auto encrypted = wardenCrypto_->encrypt(plaintext); + network::Packet pkt(wireOpcode(Opcode::CMSG_WARDEN_DATA)); + for (uint8_t b : encrypted) pkt.writeUInt8(b); + owner_.socket->send(pkt); + LOG_DEBUG("Warden: Module sendPacket callback sent ", len, " bytes"); + }); if (wardenLoadedModule_->load(wardenModuleData_, wardenModuleHash_, wardenModuleKey_)) { // codeql[cpp/weak-cryptographic-algorithm] LOG_INFO("Warden: Module loaded successfully (image size=", wardenLoadedModule_->getModuleSize(), " bytes)"); diff --git a/src/game/warden_module.cpp b/src/game/warden_module.cpp index e49007e2..428831c5 100644 --- a/src/game/warden_module.cpp +++ b/src/game/warden_module.cpp @@ -1,4 +1,5 @@ #include "game/warden_module.hpp" +#include "game/warden_crypto.hpp" #include "auth/crypto.hpp" #include "core/logger.hpp" #include @@ -30,9 +31,19 @@ namespace wowee { namespace game { // ============================================================================ +// Thread-local pointer to the active WardenModule instance during initializeModule(). +// C function pointer callbacks (sendPacket, validateModule, generateRC4) can't capture +// state, so they use this to reach the module's crypto and socket dependencies. +static thread_local WardenModule* tl_activeModule = nullptr; + // WardenModule Implementation // ============================================================================ +void WardenModule::setCallbackDependencies(WardenCrypto* crypto, SendPacketFunc sendFunc) { + callbackCrypto_ = crypto; + callbackSendPacket_ = std::move(sendFunc); +} + WardenModule::WardenModule() : loaded_(false) , moduleMemory_(nullptr) @@ -867,33 +878,54 @@ bool WardenModule::initializeModule() { void (*logMessage)(const char* msg); }; - // Setup client callbacks (used when calling module entry point below) + // Setup client callbacks (used when calling module entry point below). + // These are C function pointers (no captures), so they access the active + // module instance via tl_activeModule thread-local set below. [[maybe_unused]] ClientCallbacks callbacks = {}; - // Stub callbacks (would need real implementations) - callbacks.sendPacket = []([[maybe_unused]] uint8_t* data, size_t len) { + callbacks.sendPacket = [](uint8_t* data, size_t len) { LOG_DEBUG("WardenModule Callback: sendPacket(", len, " bytes)"); - // TODO: Send CMSG_WARDEN_DATA packet + auto* mod = tl_activeModule; + if (mod && mod->callbackSendPacket_ && data && len > 0) { + mod->callbackSendPacket_(data, len); + } }; - callbacks.validateModule = []([[maybe_unused]] uint8_t* hash) { + callbacks.validateModule = [](uint8_t* hash) { LOG_DEBUG("WardenModule Callback: validateModule()"); - // TODO: Validate module hash + auto* mod = tl_activeModule; + if (!mod || !hash) return; + // Compare provided 16-byte MD5 against the hash we received from the server + // during module download. Mismatch means the module was corrupted in transit. + const auto& expected = mod->md5Hash_; + if (expected.size() == 16 && std::memcmp(hash, expected.data(), 16) != 0) { + LOG_ERROR("WardenModule: validateModule hash MISMATCH — module may be corrupted"); + } else { + LOG_DEBUG("WardenModule: validateModule hash OK"); + } }; callbacks.allocMemory = [](size_t size) -> void* { - LOG_DEBUG("WardenModule Callback: allocMemory(", size, ")"); return malloc(size); }; callbacks.freeMemory = [](void* ptr) { - LOG_DEBUG("WardenModule Callback: freeMemory()"); free(ptr); }; - callbacks.generateRC4 = []([[maybe_unused]] uint8_t* seed) { + callbacks.generateRC4 = [](uint8_t* seed) { LOG_DEBUG("WardenModule Callback: generateRC4()"); - // TODO: Re-key RC4 cipher + auto* mod = tl_activeModule; + if (!mod || !mod->callbackCrypto_ || !seed) return; + // Module requests RC4 re-key: derive new encrypt/decrypt keys from the + // 16-byte seed using SHA1Randx, then replace the active RC4 state. + uint8_t newEncryptKey[16], newDecryptKey[16]; + std::vector seedVec(seed, seed + 16); + WardenCrypto::sha1RandxGenerate(seedVec, newEncryptKey, newDecryptKey); + mod->callbackCrypto_->replaceKeys( + std::vector(newEncryptKey, newEncryptKey + 16), + std::vector(newDecryptKey, newDecryptKey + 16)); + LOG_INFO("WardenModule: RC4 keys re-derived from module seed"); }; callbacks.getTime = []() -> uint32_t { @@ -904,6 +936,9 @@ bool WardenModule::initializeModule() { LOG_INFO("WardenModule Log: ", msg); }; + // Set thread-local context so C callbacks can access this module's state + tl_activeModule = this; + // Module entry point is typically at offset 0 (first bytes of loaded code) // Function signature: WardenFuncList* (*entryPoint)(ClientCallbacks*) @@ -1087,8 +1122,11 @@ bool WardenModule::initializeModule() { // 3. Exception handling for crashes // 4. Sandboxing for security - LOG_WARNING("WardenModule: Module initialization is STUB"); - return true; // Stub implementation + // Clear thread-local context — callbacks are only valid during init + tl_activeModule = nullptr; + + LOG_WARNING("WardenModule: Module initialization complete (callbacks wired)"); + return true; } // ============================================================================