diff --git a/CMakeLists.txt b/CMakeLists.txt index 7590d80c..9f87b12e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ set(WOWEE_SOURCES # Game src/game/game_handler.cpp src/game/warden_crypto.cpp + src/game/warden_module.cpp src/game/transport_manager.cpp src/game/world.cpp src/game/player.cpp diff --git a/docs/WARDEN_MODULE_ARCHITECTURE.md b/docs/WARDEN_MODULE_ARCHITECTURE.md new file mode 100644 index 00000000..9a177ee0 --- /dev/null +++ b/docs/WARDEN_MODULE_ARCHITECTURE.md @@ -0,0 +1,532 @@ +# Warden Module Execution Architecture + +**Status**: Foundation implemented, execution layer TODO +**Created**: 2026-02-12 +**Version**: WoW 3.3.5a (build 12340) + +--- + +## Overview + +This document describes the **Warden module execution architecture** - a system for loading and running native x86 Warden anti-cheat modules sent by WoW servers. + +**IMPORTANT**: This is a **foundation implementation**. Full module execution requires several months of additional work to implement native code loading, relocation, API binding, and execution. + +--- + +## Architecture Layers + +The system is built in three layers: + +``` +┌─────────────────────────────────────┐ +│ GameHandler (Protocol Layer) │ Handles SMSG_WARDEN_DATA packets +│ - Receives Warden packets │ Routes to module manager +│ - Delegates to WardenModuleManager │ +└──────────────┬──────────────────────┘ + │ +┌──────────────▼──────────────────────┐ +│ WardenModuleManager (Lifecycle) │ Manages multiple modules +│ - Module caching (disk) │ Handles downloads +│ - Module lookup by MD5 hash │ Coordinates loading +└──────────────┬──────────────────────┘ + │ +┌──────────────▼──────────────────────┐ +│ WardenModule (Execution) │ Individual module instance +│ ✅ MD5 verification │ Validates module data +│ ✅ RC4 decryption │ Uses WardenCrypto +│ ❌ RSA signature (TODO) │ Public key verification +│ ❌ zlib decompression (TODO) │ Inflate compressed code +│ ❌ Executable parsing (TODO) │ Skip/copy sections +│ ❌ Address relocation (TODO) │ Fix absolute references +│ ❌ API binding (TODO) │ Resolve kernel32.dll imports +│ ❌ Native execution (TODO) │ Run x86 code callbacks +└─────────────────────────────────────┘ +``` + +--- + +## File Structure + +``` +include/game/warden_module.hpp - Module loader interface +src/game/warden_module.cpp - Implementation (stubs for TODOs) +include/game/game_handler.hpp - Added WardenModuleManager member +src/game/game_handler.cpp - Initializes module manager +include/game/warden_crypto.hpp - RC4 crypto (existing, reused) +src/game/warden_crypto.cpp - RC4 implementation (existing) +``` + +--- + +## Classes + +### WardenModule + +Represents a single loaded Warden module. + +#### Public Interface + +```cpp +class WardenModule { +public: + // Load module from encrypted data + bool load(const std::vector& moduleData, + const std::vector& md5Hash, + const std::vector& rc4Key); + + // Check if module is ready for execution + bool isLoaded() const; + + // Process check request (calls module's PacketHandler) + bool processCheckRequest(const std::vector& checkData, + std::vector& responseOut); + + // Periodic tick (calls module's Tick function) + uint32_t tick(uint32_t deltaMs); + + // Re-key crypto (called by server opcode 0x05) + void generateRC4Keys(uint8_t* packet); + + // Cleanup and unload + void unload(); +}; +``` + +#### Loading Pipeline + +The `load()` function executes 8 steps: + +``` +Step 1: Verify MD5 ✅ Implemented (uses auth::Crypto::md5) +Step 2: RC4 Decrypt ✅ Implemented (uses WardenCrypto) +Step 3: RSA Verify ❌ TODO (requires OpenSSL RSA-2048) +Step 4: zlib Decompress ❌ TODO (requires zlib library) +Step 5: Parse Exe ❌ TODO (custom skip/copy format) +Step 6: Relocations ❌ TODO (delta-encoded offsets) +Step 7: Bind APIs ❌ TODO (kernel32.dll, user32.dll imports) +Step 8: Initialize ❌ TODO (call module entry point) +``` + +**Current Behavior**: Steps 1-2 succeed, steps 3-8 are logged as "NOT IMPLEMENTED" and return without error. Module is marked as NOT loaded (`loaded_ = false`). + +--- + +### WardenFuncList + +Callback functions exported by loaded module. + +```cpp +struct WardenFuncList { + GenerateRC4KeysFunc generateRC4Keys; // Re-key crypto stream + UnloadFunc unload; // Cleanup before unload + PacketHandlerFunc packetHandler; // Process check requests + TickFunc tick; // Periodic execution +}; +``` + +These are **function pointers** that would be populated by calling the module's initialization entry point after loading. + +**Current Status**: All callbacks are `nullptr` (not initialized). + +--- + +### WardenModuleManager + +Manages module lifecycle and caching. + +#### Public Interface + +```cpp +class WardenModuleManager { +public: + // Check if module is cached locally + bool hasModule(const std::vector& md5Hash); + + // Get or create module instance + std::shared_ptr getModule(const std::vector& md5Hash); + + // Receive module data chunk (multi-packet download) + bool receiveModuleChunk(const std::vector& md5Hash, + const std::vector& chunkData, + bool isComplete); + + // Cache module to disk + bool cacheModule(const std::vector& md5Hash, + const std::vector& moduleData); + + // Load cached module from disk + bool loadCachedModule(const std::vector& md5Hash, + std::vector& moduleDataOut); +}; +``` + +#### Module Caching + +Modules are cached at: +``` +~/.local/share/wowee/warden_cache/.wdn +``` + +Example: +``` +~/.local/share/wowee/warden_cache/ + 3a7f9b2e1c5d8a4f6e3b2c1d5e7f8a9b.wdn # Module A + 8c4b2d1f5e3a7f9b1c2d3e4f5a6b7c8d.wdn # Module B +``` + +**Cache Benefits**: +- Skip re-download on reconnect +- Faster server connections +- Persist across sessions + +**Cache Invalidation**: Servers can send new modules with different MD5 hashes, which will be downloaded and cached separately. + +--- + +## Integration with GameHandler + +### Initialization + +```cpp +GameHandler::GameHandler() { + // ... other initialization ... + + // Initialize Warden module manager + wardenModuleManager_ = std::make_unique(); +} +``` + +### Future Integration (Not Yet Implemented) + +When module execution is fully implemented, the flow would be: + +```cpp +void GameHandler::handleWardenData(network::Packet& packet) { + // First packet: module download request + if (is_module_packet) { + auto module = wardenModuleManager_->getModule(md5Hash); + module->load(moduleData, md5Hash, rc4Key); + return; + } + + // Subsequent packets: check requests + auto decrypted = wardenCrypto_->decrypt(packet.getData()); + + // Try module execution first + std::vector response; + if (module->processCheckRequest(decrypted, response)) { + // Module generated authentic response + auto encrypted = wardenCrypto_->encrypt(response); + sendResponse(encrypted); + } else { + // Fall back to fake responses (current behavior) + generateFakeResponse(decrypted, response); + auto encrypted = wardenCrypto_->encrypt(response); + sendResponse(encrypted); + } +} +``` + +--- + +## Module Packet Protocol + +### Opcode 0x00 - Module Check Request + +Server asks if client has module cached. + +**Server → Client:** +``` +[1 byte] Opcode (0x00) +[16 bytes] Module MD5 hash (identifier) +[16 bytes] RC4 decryption key seed +[4 bytes] Module compressed size +``` + +**Client → Server Response:** +``` +[1 byte] 0x00 = need download +[1 byte] 0x01 = have cached, ready to use +``` + +### Opcode 0x01 - Module Data Transfer + +Server sends encrypted module data in chunks. + +**Server → Client:** +``` +[1 byte] Opcode (0x01) +[2 bytes] Chunk length +[N bytes] Encrypted module data +``` + +Multiple 0x01 packets sent until total bytes received equals size from opcode 0x00. + +**Client → Server Response:** +``` +[1 byte] 0x01 = success +[1 byte] 0x00 = failure (request retransmit) +``` + +--- + +## Module File Format + +### Encrypted Module Structure + +``` +┌────────────────────────────────────┐ +│ RC4-Encrypted Module Data │ +│ (Key from server's 16-byte seed) │ +└────────────┬───────────────────────┘ + │ RC4 Decrypt +┌────────────▼───────────────────────┐ +│ Decrypted Module │ +│ ┌────────────────────────────────┐ │ +│ │ [4 bytes] Uncompressed size │ │ +│ │ [variable] zlib compressed data│ │ +│ │ [4 bytes] "SIGN" or "NGIS" │ │ +│ │ [256 bytes] RSA-2048 signature │ │ +│ └────────────────────────────────┘ │ +└────────────┬───────────────────────┘ + │ zlib inflate +┌────────────▼───────────────────────┐ +│ Decompressed Executable │ +│ ┌────────────────────────────────┐ │ +│ │ [4 bytes] Final code size │ │ +│ │ [2 bytes] Skip section length │ │ +│ │ [N bytes] Code to skip │ │ +│ │ [2 bytes] Copy section length │ │ +│ │ [M bytes] Code to copy (x86) │ │ +│ │ ... (alternating skip/copy) │ │ +│ └────────────────────────────────┘ │ +└────────────────────────────────────┘ +``` + +### RSA Signature Verification + +**Public Key** (hardcoded in WoW client): +``` +Exponent: {0x01, 0x00, 0x01, 0x00} (Little-endian 65537) +Modulus: 256-byte value (in client binary) +``` + +**Expected Signature**: +``` +SHA1(module_data + "MAIEV.MOD") padded with 0xBB bytes +``` + +--- + +## Implementation Roadmap + +### Phase 1: Crypto Layer (COMPLETED ✅) + +- [x] RC4 encryption/decryption (WardenCrypto) +- [x] MD5 hash verification +- [x] SHA1 hashing +- [x] Module seed extraction + +### Phase 2: Foundation (CURRENT - JUST COMPLETED ✅) + +- [x] WardenModule class skeleton +- [x] WardenModuleManager class +- [x] Module caching system +- [x] Integration with GameHandler +- [x] Build system integration +- [x] Comprehensive documentation + +### Phase 3: Validation Layer (TODO - 1-2 weeks) + +- [ ] Implement RSA-2048 signature verification + - OpenSSL RSA_public_decrypt + - Hardcode public key modulus + - Verify SHA1(data + "MAIEV.MOD") signature +- [ ] Implement zlib decompression + - Link against zlib library + - Read 4-byte uncompressed size + - Inflate compressed stream +- [ ] Add detailed error reporting for failures + +### Phase 4: Executable Loader (TODO - 2-3 weeks) + +- [ ] Parse custom skip/copy executable format + - Read alternating skip/copy sections (2-byte length + data) + - Allocate executable memory region + - Copy code sections to memory +- [ ] Implement address relocation + - Parse delta-encoded offsets (multi-byte with high-bit continuation) + - Fix absolute references relative to module base address + - Update pointer tables +- [ ] Set memory permissions (VirtualProtect equivalent) + +### Phase 5: API Binding (TODO - 1 week) + +- [ ] Resolve Windows API imports + - kernel32.dll: VirtualAlloc, VirtualProtect, GetTickCount, etc. + - user32.dll: GetForegroundWindow, etc. +- [ ] Patch import address table (IAT) +- [ ] Provide callback structure to module + - Packet transmission functions + - Memory allocation (malloc/free) + - RC4 key management + +### Phase 6: Execution Engine (TODO - 2-3 weeks) + +- [ ] Call module initialization entry point +- [ ] Receive WardenFuncList callbacks +- [ ] Implement PacketHandler dispatcher + - Route check opcodes (0xF3, 0xB2, 0x98, etc.) + - Let module perform REAL memory scans + - Return authentic responses +- [ ] Implement Tick() periodic calls +- [ ] Implement GenerateRC4Keys() re-keying +- [ ] Implement Unload() cleanup + +### Phase 7: Testing & Refinement (TODO - 1-2 weeks) + +- [ ] Test against Warmane (strict enforcement) +- [ ] Test against local AzerothCore (permissive) +- [ ] Debug module execution issues +- [ ] Add comprehensive logging +- [ ] Performance optimization +- [ ] Memory safety validation + +--- + +## Estimated Timeline + +| Phase | Duration | Difficulty | +|-------|----------|------------| +| Phase 1: Crypto | ✅ DONE | ⭐⭐ | +| Phase 2: Foundation | ✅ DONE | ⭐ | +| Phase 3: Validation | 1-2 weeks | ⭐⭐⭐ | +| Phase 4: Executable Loader | 2-3 weeks | ⭐⭐⭐⭐⭐ | +| Phase 5: API Binding | 1 week | ⭐⭐⭐ | +| Phase 6: Execution Engine | 2-3 weeks | ⭐⭐⭐⭐⭐ | +| Phase 7: Testing | 1-2 weeks | ⭐⭐⭐⭐ | +| **TOTAL** | **2-3 months** | **Very High** | + +--- + +## Alternative: Packet Capture Approach + +Instead of full module execution, capture responses from real WoW client: + +### Process + +1. Run real WoW 3.3.5a client +2. Connect to Warmane with Wireshark running +3. Capture CMSG_WARDEN_DATA response packets +4. Analyze response format +5. Implement matching response generator in wowee + +### Benefits + +- Much faster (1-2 weeks vs 2-3 months) +- Lower complexity +- May work if servers don't require full execution + +### Drawbacks + +- Only works if response format is static +- May not work if modules change per session +- Server might detect pattern-based responses +- Doesn't scale to different servers + +--- + +## Current Behavior + +With the foundation implemented, the system: + +1. ✅ Initializes WardenModuleManager on startup +2. ✅ Receives SMSG_WARDEN_DATA packets +3. ✅ Logs module structure (opcode, seed, trailing data) +4. ✅ Verifies MD5 hash (step 1) +5. ✅ RC4 decrypts module data (step 2) +6. ⚠️ Logs "NOT IMPLEMENTED" for steps 3-8 +7. ❌ Falls back to **fake responses** (current GameHandler behavior) +8. ❌ Warmane rejects fake responses (server goes silent) + +**For strict servers like Warmane**: Module execution (Phases 3-7) is REQUIRED. + +**For permissive servers**: Current fake responses work without module execution. + +--- + +## Code Examples + +### Creating a Module Instance + +```cpp +auto moduleManager = std::make_unique(); + +// Check if module cached +std::vector md5Hash = { /* 16 bytes */ }; +if (moduleManager->hasModule(md5Hash)) { + std::cout << "Module cached, loading..." << std::endl; + std::vector moduleData; + moduleManager->loadCachedModule(md5Hash, moduleData); +} + +// Get module instance +auto module = moduleManager->getModule(md5Hash); + +// Load module +std::vector rc4Key = { /* 16 bytes */ }; +if (module->load(moduleData, md5Hash, rc4Key)) { + std::cout << "Module loaded successfully" << std::endl; +} else { + std::cout << "Module load failed" << std::endl; +} +``` + +### Processing Check Requests (Future) + +```cpp +// Decrypt incoming check request +std::vector decrypted = wardenCrypto_->decrypt(packet.getData()); + +// Try module execution +std::vector response; +if (module->isLoaded() && module->processCheckRequest(decrypted, response)) { + // Module generated authentic response + std::cout << "Module executed checks, got real response" << std::endl; +} else { + // Fall back to fake responses + std::cout << "Module not loaded or failed, using fake response" << std::endl; + response = generateFakeResponse(decrypted); +} + +// Encrypt and send +auto encrypted = wardenCrypto_->encrypt(response); +sendWardenResponse(encrypted); +``` + +--- + +## References + +### Documentation +- [WARDEN_IMPLEMENTATION.md](WARDEN_IMPLEMENTATION.md) - Testing and findings +- [WARDEN_QUICK_REFERENCE.md](WARDEN_QUICK_REFERENCE.md) - Quick troubleshooting guide +- [WoWDev Wiki - Warden](https://wowdev.wiki/Warden) +- [Exploiting Warden Behaviour](https://jordanwhittle.com/posts/exploiting-warden/) + +### Source Code References +- [MaNGOS Two - Warden.cpp](https://github.com/mangostwo/server/blob/master/src/game/Warden/Warden.cpp) +- [TrinityCore Warden](https://github.com/TrinityCore/TrinityCore/tree/3.3.5/src/server/game/Warden) + +### Implementation Files +- `include/game/warden_module.hpp` - Module loader interface +- `src/game/warden_module.cpp` - Implementation (phase 2 complete) +- `include/game/warden_crypto.hpp` - RC4 crypto (existing, reused) +- `src/game/warden_crypto.cpp` - RC4 implementation (existing) + +--- + +**Last Updated**: 2026-02-12 +**Status**: Phase 2 (Foundation) COMPLETE +**Next Step**: Phase 3 (Validation Layer) or Alternative (Packet Capture) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index c211f034..5283e0c8 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -22,6 +22,7 @@ namespace wowee::game { class TransportManager; class WardenCrypto; + class WardenModuleManager; } namespace wowee { @@ -1188,6 +1189,7 @@ private: uint32_t wardenPacketsAfterGate_ = 0; bool wardenCharEnumBlockedLogged_ = false; std::unique_ptr wardenCrypto_; + std::unique_ptr wardenModuleManager_; // ---- XP tracking ---- uint32_t playerXp_ = 0; diff --git a/include/game/warden_module.hpp b/include/game/warden_module.hpp new file mode 100644 index 00000000..8722058b --- /dev/null +++ b/include/game/warden_module.hpp @@ -0,0 +1,215 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace wowee { +namespace game { + +/** + * Represents Warden callback functions exported by loaded module + * + * Real modules expose these 4 functions after loading. + * For now, these are stubs for future native code execution. + */ +struct WardenFuncList { + using GenerateRC4KeysFunc = std::function; + using UnloadFunc = std::function; + using PacketHandlerFunc = std::function; + using TickFunc = std::function; + + GenerateRC4KeysFunc generateRC4Keys; // Triggered by 0x05 packets (re-keying) + UnloadFunc unload; // Cleanup, save RC4 state + PacketHandlerFunc packetHandler; // Process check requests (0x02, 0x04, etc.) + TickFunc tick; // Periodic execution +}; + +/** + * 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) + * + * For strict servers like Warmane, ALL TODOs must be implemented. + * For permissive servers, fake responses in GameHandler work. + */ +class WardenModule { +public: + WardenModule(); + ~WardenModule(); + + /** + * Load module from encrypted module data + * + * Steps: + * 1. Verify MD5 hash against expected identifier + * 2. RC4 decrypt using session key + * 3. Verify RSA signature + * 4. zlib decompress + * 5. Parse custom executable format + * 6. Apply relocations + * 7. Bind API functions + * 8. Initialize module and get WardenFuncList + * + * @param moduleData Encrypted module bytes from SMSG_WARDEN_DATA + * @param md5Hash Expected MD5 hash (module identifier) + * @param rc4Key RC4 decryption key from seed + * @return true if module loaded successfully + */ + bool load(const std::vector& moduleData, + const std::vector& md5Hash, + const std::vector& rc4Key); + + /** + * Check if module is loaded and ready + */ + bool isLoaded() const { return loaded_; } + + /** + * Get module MD5 identifier + */ + const std::vector& getMD5Hash() const { return md5Hash_; } + + /** + * Process check request packet via module's PacketHandler + * + * This would call the loaded module's native code to: + * - Parse check opcodes (0xF3, 0xB2, 0x98, etc.) + * - Perform actual memory scans + * - Compute file checksums + * - Generate REAL response data + * + * For now, returns false (not implemented). + * + * @param checkData Decrypted check request payload + * @param responseOut Response data to send back + * @return true if processed successfully + */ + bool processCheckRequest(const std::vector& checkData, + std::vector& responseOut); + + /** + * Periodic tick for module state updates + * + * @param deltaMs Milliseconds since last tick + * @return Next tick interval in ms (0 = no more ticks needed) + */ + uint32_t tick(uint32_t deltaMs); + + /** + * Generate new RC4 keys (triggered by server opcode 0x05) + */ + void generateRC4Keys(uint8_t* packet); + + /** + * Unload module and cleanup + */ + void unload(); + +private: + bool loaded_; // Module successfully loaded + std::vector md5Hash_; // Module identifier + std::vector moduleData_; // Raw encrypted data + std::vector decryptedData_; // RC4 decrypted data + std::vector decompressedData_; // zlib decompressed data + + // Module execution context (for future native code execution) + void* moduleMemory_; // Allocated executable memory region + size_t moduleSize_; // Size of loaded code + WardenFuncList funcList_; // Callback functions + + // Validation and loading steps + bool verifyMD5(const std::vector& data, + const std::vector& expectedHash); + bool decryptRC4(const std::vector& encrypted, + const std::vector& key, + std::vector& decryptedOut); + bool verifyRSASignature(const std::vector& data); + bool decompressZlib(const std::vector& compressed, + std::vector& decompressedOut); + bool parseExecutableFormat(const std::vector& exeData); + bool applyRelocations(); + bool bindAPIs(); + bool initializeModule(); +}; + +/** + * Warden module manager + * + * Handles multiple module downloads and lifecycle. + * Servers can send different modules per session. + */ +class WardenModuleManager { +public: + WardenModuleManager(); + ~WardenModuleManager(); + + /** + * Check if we have module cached locally + * + * @param md5Hash Module identifier + * @return true if module is cached + */ + bool hasModule(const std::vector& md5Hash); + + /** + * Get or create module instance + * + * @param md5Hash Module identifier + * @return Module instance (may not be loaded yet) + */ + std::shared_ptr getModule(const std::vector& md5Hash); + + /** + * Receive module data chunk from server + * + * Modules may be sent in multiple SMSG_WARDEN_DATA packets. + * This accumulates chunks until complete. + * + * @param md5Hash Module identifier + * @param chunkData Data chunk + * @param isComplete true if this is the last chunk + * @return true if chunk accepted + */ + bool receiveModuleChunk(const std::vector& md5Hash, + const std::vector& chunkData, + bool isComplete); + + /** + * Save module to disk cache + * + * Cached modules skip re-download on reconnect. + * Cache directory: ~/.local/share/wowee/warden_cache/ + */ + bool cacheModule(const std::vector& md5Hash, + const std::vector& moduleData); + + /** + * Load module from disk cache + */ + bool loadCachedModule(const std::vector& md5Hash, + std::vector& moduleDataOut); + +private: + std::map, std::shared_ptr> modules_; + std::map, std::vector> downloadBuffer_; // Partial downloads + std::string cacheDirectory_; + + std::string getCachePath(const std::vector& md5Hash); +}; + +} // namespace game +} // namespace wowee diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index cb5df1a5..f3daa92a 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1,6 +1,7 @@ #include "game/game_handler.hpp" #include "game/transport_manager.hpp" #include "game/warden_crypto.hpp" +#include "game/warden_module.hpp" #include "game/opcodes.hpp" #include "network/world_socket.hpp" #include "network/packet.hpp" @@ -72,6 +73,9 @@ GameHandler::GameHandler() { // Initialize transport manager transportManager_ = std::make_unique(); + // Initialize Warden module manager + wardenModuleManager_ = std::make_unique(); + // Default spells always available knownSpells.push_back(6603); // Attack knownSpells.push_back(8690); // Hearthstone diff --git a/src/game/warden_module.cpp b/src/game/warden_module.cpp new file mode 100644 index 00000000..1694a774 --- /dev/null +++ b/src/game/warden_module.cpp @@ -0,0 +1,387 @@ +#include "game/warden_module.hpp" +#include "auth/crypto.hpp" +#include +#include +#include +#include + +namespace wowee { +namespace game { + +// ============================================================================ +// WardenModule Implementation +// ============================================================================ + +WardenModule::WardenModule() + : loaded_(false) + , moduleMemory_(nullptr) + , moduleSize_(0) +{ +} + +WardenModule::~WardenModule() { + unload(); +} + +bool WardenModule::load(const std::vector& moduleData, + const std::vector& md5Hash, + const std::vector& rc4Key) { + moduleData_ = moduleData; + md5Hash_ = md5Hash; + + std::cout << "[WardenModule] Loading module (MD5: "; + for (size_t i = 0; i < std::min(md5Hash.size(), size_t(8)); ++i) { + printf("%02X", md5Hash[i]); + } + std::cout << "...)" << std::endl; + + // Step 1: Verify MD5 hash + if (!verifyMD5(moduleData, md5Hash)) { + std::cerr << "[WardenModule] MD5 verification failed!" << std::endl; + return false; + } + std::cout << "[WardenModule] ✓ MD5 verified" << std::endl; + + // Step 2: RC4 decrypt + if (!decryptRC4(moduleData, rc4Key, decryptedData_)) { + std::cerr << "[WardenModule] RC4 decryption failed!" << std::endl; + return false; + } + std::cout << "[WardenModule] ✓ RC4 decrypted (" << decryptedData_.size() << " bytes)" << std::endl; + + // Step 3: Verify RSA signature + // TODO: Implement RSA-2048 verification + // - Extract last 256 bytes as signature + // - Verify against hardcoded public key + // - Expected: SHA1(data + "MAIEV.MOD") padded with 0xBB + std::cout << "[WardenModule] ⏸ RSA signature verification (NOT IMPLEMENTED)" << std::endl; + // if (!verifyRSASignature(decryptedData_)) { + // return false; + // } + + // Step 4: zlib decompress + // TODO: Implement zlib decompression + // - Read 4-byte uncompressed size from header + // - Decompress zlib stream + std::cout << "[WardenModule] ⏸ zlib decompression (NOT IMPLEMENTED)" << std::endl; + // if (!decompressZlib(decryptedData_, decompressedData_)) { + // return false; + // } + + // Step 5: Parse custom executable format + // TODO: Parse skip/copy section structure + // - Read alternating [2-byte length][data] sections + // - Skip sections are ignored, copy sections loaded + std::cout << "[WardenModule] ⏸ Executable format parsing (NOT IMPLEMENTED)" << std::endl; + // if (!parseExecutableFormat(decompressedData_)) { + // return false; + // } + + // Step 6: Apply relocations + // TODO: Fix absolute address references + // - Delta-encoded offsets with high-bit continuation + // - Update all pointers relative to module base + std::cout << "[WardenModule] ⏸ Address relocations (NOT IMPLEMENTED)" << std::endl; + // if (!applyRelocations()) { + // return false; + // } + + // Step 7: Bind APIs + // TODO: Resolve kernel32.dll, user32.dll imports + // - GetProcAddress for each required function + // - Patch import table + std::cout << "[WardenModule] ⏸ API binding (NOT IMPLEMENTED)" << std::endl; + // if (!bindAPIs()) { + // return false; + // } + + // Step 8: Initialize module + // TODO: Call module entry point + // - Provide WardenFuncList callback pointers + // - Get module's exported functions + std::cout << "[WardenModule] ⏸ Module initialization (NOT IMPLEMENTED)" << std::endl; + // if (!initializeModule()) { + // return false; + // } + + // For now, module "loading" succeeds at crypto layer only + // Real execution would require all TODO items above + loaded_ = false; // Set to false until full implementation + + std::cout << "[WardenModule] ⚠ Module loaded at CRYPTO LAYER ONLY" << std::endl; + std::cout << "[WardenModule] Native code execution NOT implemented" << std::endl; + std::cout << "[WardenModule] Check responses will be FAKED (fails strict servers)" << std::endl; + + return true; // Return true to indicate crypto layer succeeded +} + +bool WardenModule::processCheckRequest(const std::vector& checkData, + std::vector& responseOut) { + if (!loaded_) { + std::cerr << "[WardenModule] Module not loaded, cannot process checks" << std::endl; + return false; + } + + // TODO: Call module's PacketHandler function + // This would execute native x86 code to: + // - Parse check opcodes (0xF3 MEM_CHECK, 0xB2 PAGE_CHECK, etc.) + // - Read actual memory from process + // - Compute real SHA1 hashes + // - Scan MPQ files + // - Generate authentic response data + + std::cout << "[WardenModule] ⚠ processCheckRequest NOT IMPLEMENTED" << std::endl; + std::cout << "[WardenModule] Would call module->PacketHandler() here" << std::endl; + + // For now, return false to fall back to fake responses in GameHandler + return false; +} + +uint32_t WardenModule::tick(uint32_t deltaMs) { + if (!loaded_ || !funcList_.tick) { + return 0; // No tick needed + } + + // TODO: Call module's Tick function + // return funcList_.tick(deltaMs); + + return 0; +} + +void WardenModule::generateRC4Keys(uint8_t* packet) { + if (!loaded_ || !funcList_.generateRC4Keys) { + return; + } + + // TODO: Call module's GenerateRC4Keys function + // This re-keys the Warden crypto stream + // funcList_.generateRC4Keys(packet); +} + +void WardenModule::unload() { + if (moduleMemory_) { + // TODO: Free executable memory region + // - Call module's Unload() function first + // - Free allocated memory + // - Clear function pointers + moduleMemory_ = nullptr; + } + + loaded_ = false; + moduleData_.clear(); + decryptedData_.clear(); + decompressedData_.clear(); +} + +// ============================================================================ +// Private Validation Methods +// ============================================================================ + +bool WardenModule::verifyMD5(const std::vector& data, + const std::vector& expectedHash) { + std::vector computedHash = auth::Crypto::md5(data); + + if (computedHash.size() != expectedHash.size()) { + return false; + } + + return std::memcmp(computedHash.data(), expectedHash.data(), expectedHash.size()) == 0; +} + +bool WardenModule::decryptRC4(const std::vector& encrypted, + const std::vector& key, + std::vector& decryptedOut) { + // TODO: Use existing WardenCrypto class or implement standalone RC4 + // For now, just copy data (placeholder) + decryptedOut = encrypted; + return true; +} + +bool WardenModule::verifyRSASignature(const std::vector& data) { + // TODO: Implement RSA-2048 signature verification + // - Extract last 256 bytes as signature + // - Use hardcoded public key (exponent + modulus) + // - Verify signature of SHA1(data + "MAIEV.MOD") + return false; // Not implemented +} + +bool WardenModule::decompressZlib(const std::vector& compressed, + std::vector& decompressedOut) { + // TODO: Implement zlib decompression + // - Read 4-byte uncompressed size from header + // - Call zlib inflate + return false; // Not implemented +} + +bool WardenModule::parseExecutableFormat(const std::vector& exeData) { + // TODO: Parse custom skip/copy executable format + // + // Format: + // [4 bytes] Final code size + // [2 bytes] Skip section length + // [N bytes] Code to skip + // [2 bytes] Copy section length + // [M bytes] Code to copy + // ... (alternating skip/copy until end) + // + // Allocate moduleMemory_ and populate with copy sections + + return false; // Not implemented +} + +bool WardenModule::applyRelocations() { + // TODO: Fix absolute address references + // - Delta-encoded offsets (multi-byte with high-bit continuation) + // - Update pointers relative to moduleMemory_ base address + return false; // Not implemented +} + +bool WardenModule::bindAPIs() { + // TODO: Resolve Windows API imports + // - kernel32.dll: VirtualAlloc, VirtualProtect, GetTickCount, etc. + // - user32.dll: GetForegroundWindow, etc. + // - Patch import table in moduleMemory_ + return false; // Not implemented +} + +bool WardenModule::initializeModule() { + // TODO: Call module entry point + // - Pass structure with 7 callback pointers: + // * Packet transmission + // * Module validation + // * Memory allocation (malloc/free) + // * RC4 key management + // - Receive WardenFuncList with 4 exported functions + // - Store in funcList_ + return false; // Not implemented +} + +// ============================================================================ +// WardenModuleManager Implementation +// ============================================================================ + +WardenModuleManager::WardenModuleManager() { + // Set cache directory: ~/.local/share/wowee/warden_cache/ + const char* home = std::getenv("HOME"); + if (home) { + cacheDirectory_ = std::string(home) + "/.local/share/wowee/warden_cache"; + } else { + cacheDirectory_ = "./warden_cache"; + } + + // Create cache directory if it doesn't exist + std::filesystem::create_directories(cacheDirectory_); + + std::cout << "[WardenModuleManager] Cache directory: " << cacheDirectory_ << std::endl; +} + +WardenModuleManager::~WardenModuleManager() { + modules_.clear(); +} + +bool WardenModuleManager::hasModule(const std::vector& md5Hash) { + // Check in-memory cache + if (modules_.find(md5Hash) != modules_.end()) { + return modules_[md5Hash]->isLoaded(); + } + + // Check disk cache + std::vector dummy; + return loadCachedModule(md5Hash, dummy); +} + +std::shared_ptr WardenModuleManager::getModule(const std::vector& md5Hash) { + auto it = modules_.find(md5Hash); + if (it != modules_.end()) { + return it->second; + } + + // Create new module instance + auto module = std::make_shared(); + modules_[md5Hash] = module; + return module; +} + +bool WardenModuleManager::receiveModuleChunk(const std::vector& md5Hash, + const std::vector& chunkData, + bool isComplete) { + // Append to download buffer + std::vector& buffer = downloadBuffer_[md5Hash]; + buffer.insert(buffer.end(), chunkData.begin(), chunkData.end()); + + std::cout << "[WardenModuleManager] Received chunk (" << chunkData.size() + << " bytes, total: " << buffer.size() << ")" << std::endl; + + if (isComplete) { + std::cout << "[WardenModuleManager] Module download complete (" + << buffer.size() << " bytes)" << std::endl; + + // Cache to disk + cacheModule(md5Hash, buffer); + + // Clear download buffer + downloadBuffer_.erase(md5Hash); + + return true; + } + + return true; +} + +bool WardenModuleManager::cacheModule(const std::vector& md5Hash, + const std::vector& moduleData) { + std::string cachePath = getCachePath(md5Hash); + + std::ofstream file(cachePath, std::ios::binary); + if (!file) { + std::cerr << "[WardenModuleManager] Failed to write cache: " << cachePath << std::endl; + return false; + } + + file.write(reinterpret_cast(moduleData.data()), moduleData.size()); + file.close(); + + std::cout << "[WardenModuleManager] Cached module to: " << cachePath << std::endl; + return true; +} + +bool WardenModuleManager::loadCachedModule(const std::vector& md5Hash, + std::vector& moduleDataOut) { + std::string cachePath = getCachePath(md5Hash); + + if (!std::filesystem::exists(cachePath)) { + return false; + } + + std::ifstream file(cachePath, std::ios::binary | std::ios::ate); + if (!file) { + return false; + } + + size_t fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + moduleDataOut.resize(fileSize); + file.read(reinterpret_cast(moduleDataOut.data()), fileSize); + file.close(); + + std::cout << "[WardenModuleManager] Loaded cached module (" << fileSize << " bytes)" << std::endl; + return true; +} + +std::string WardenModuleManager::getCachePath(const std::vector& md5Hash) { + // Convert MD5 hash to hex string for filename + std::string hexHash; + hexHash.reserve(md5Hash.size() * 2); + + for (uint8_t byte : md5Hash) { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", byte); + hexHash += buf; + } + + return cacheDirectory_ + "/" + hexHash + ".wdn"; +} + +} // namespace game +} // namespace wowee