Implement Warden module execution foundation (Phase 1 & 2)

Added architecture for loading and executing native x86 Warden modules:

New classes:
- WardenModule: Individual module loader with 8-step pipeline
   MD5 verification (working)
   RC4 decryption (working)
   RSA/zlib/exe-parsing/relocation/API-binding/execution (TODOs)
- WardenModuleManager: Module lifecycle and disk caching
  ~/.local/share/wowee/warden_cache/<MD5>.wdn
- WardenFuncList: Callback structure for module execution

Integration:
- Added wardenModuleManager_ to GameHandler
- Module manager initialized on startup
- Foundation ready for phases 3-7 (validation → execution)

Documentation:
- WARDEN_MODULE_ARCHITECTURE.md (comprehensive 7-phase roadmap)
- Estimated 2-3 months for full native code execution
- Alternative: packet capture approach (1-2 weeks)

Status: Crypto layer complete, execution layer TODO
This commit is contained in:
Kelsi 2026-02-12 02:43:20 -08:00
parent 615efd01b7
commit 4b425f1225
6 changed files with 1141 additions and 0 deletions

View file

@ -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

View file

@ -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<uint8_t>& moduleData,
const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& rc4Key);
// Check if module is ready for execution
bool isLoaded() const;
// Process check request (calls module's PacketHandler)
bool processCheckRequest(const std::vector<uint8_t>& checkData,
std::vector<uint8_t>& 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<uint8_t>& md5Hash);
// Get or create module instance
std::shared_ptr<WardenModule> getModule(const std::vector<uint8_t>& md5Hash);
// Receive module data chunk (multi-packet download)
bool receiveModuleChunk(const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& chunkData,
bool isComplete);
// Cache module to disk
bool cacheModule(const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& moduleData);
// Load cached module from disk
bool loadCachedModule(const std::vector<uint8_t>& md5Hash,
std::vector<uint8_t>& moduleDataOut);
};
```
#### Module Caching
Modules are cached at:
```
~/.local/share/wowee/warden_cache/<MD5_HASH>.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<WardenModuleManager>();
}
```
### 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<uint8_t> 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<WardenModuleManager>();
// Check if module cached
std::vector<uint8_t> md5Hash = { /* 16 bytes */ };
if (moduleManager->hasModule(md5Hash)) {
std::cout << "Module cached, loading..." << std::endl;
std::vector<uint8_t> moduleData;
moduleManager->loadCachedModule(md5Hash, moduleData);
}
// Get module instance
auto module = moduleManager->getModule(md5Hash);
// Load module
std::vector<uint8_t> 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<uint8_t> decrypted = wardenCrypto_->decrypt(packet.getData());
// Try module execution
std::vector<uint8_t> 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)

View file

@ -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> wardenCrypto_;
std::unique_ptr<WardenModuleManager> wardenModuleManager_;
// ---- XP tracking ----
uint32_t playerXp_ = 0;

View file

@ -0,0 +1,215 @@
#pragma once
#include <vector>
#include <cstdint>
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include <map>
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<void(uint8_t* packet)>;
using UnloadFunc = std::function<void(uint8_t* rc4Keys)>;
using PacketHandlerFunc = std::function<void(uint8_t* data, size_t length)>;
using TickFunc = std::function<uint32_t(uint32_t deltaMs)>;
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<uint8_t>& moduleData,
const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& rc4Key);
/**
* Check if module is loaded and ready
*/
bool isLoaded() const { return loaded_; }
/**
* Get module MD5 identifier
*/
const std::vector<uint8_t>& 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<uint8_t>& checkData,
std::vector<uint8_t>& 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<uint8_t> md5Hash_; // Module identifier
std::vector<uint8_t> moduleData_; // Raw encrypted data
std::vector<uint8_t> decryptedData_; // RC4 decrypted data
std::vector<uint8_t> 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<uint8_t>& data,
const std::vector<uint8_t>& expectedHash);
bool decryptRC4(const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& key,
std::vector<uint8_t>& decryptedOut);
bool verifyRSASignature(const std::vector<uint8_t>& data);
bool decompressZlib(const std::vector<uint8_t>& compressed,
std::vector<uint8_t>& decompressedOut);
bool parseExecutableFormat(const std::vector<uint8_t>& 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<uint8_t>& md5Hash);
/**
* Get or create module instance
*
* @param md5Hash Module identifier
* @return Module instance (may not be loaded yet)
*/
std::shared_ptr<WardenModule> getModule(const std::vector<uint8_t>& 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<uint8_t>& md5Hash,
const std::vector<uint8_t>& 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<uint8_t>& md5Hash,
const std::vector<uint8_t>& moduleData);
/**
* Load module from disk cache
*/
bool loadCachedModule(const std::vector<uint8_t>& md5Hash,
std::vector<uint8_t>& moduleDataOut);
private:
std::map<std::vector<uint8_t>, std::shared_ptr<WardenModule>> modules_;
std::map<std::vector<uint8_t>, std::vector<uint8_t>> downloadBuffer_; // Partial downloads
std::string cacheDirectory_;
std::string getCachePath(const std::vector<uint8_t>& md5Hash);
};
} // namespace game
} // namespace wowee

View file

@ -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<TransportManager>();
// Initialize Warden module manager
wardenModuleManager_ = std::make_unique<WardenModuleManager>();
// Default spells always available
knownSpells.push_back(6603); // Attack
knownSpells.push_back(8690); // Hearthstone

387
src/game/warden_module.cpp Normal file
View file

@ -0,0 +1,387 @@
#include "game/warden_module.hpp"
#include "auth/crypto.hpp"
#include <cstring>
#include <fstream>
#include <filesystem>
#include <iostream>
namespace wowee {
namespace game {
// ============================================================================
// WardenModule Implementation
// ============================================================================
WardenModule::WardenModule()
: loaded_(false)
, moduleMemory_(nullptr)
, moduleSize_(0)
{
}
WardenModule::~WardenModule() {
unload();
}
bool WardenModule::load(const std::vector<uint8_t>& moduleData,
const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& 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<uint8_t>& checkData,
std::vector<uint8_t>& 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<uint8_t>& data,
const std::vector<uint8_t>& expectedHash) {
std::vector<uint8_t> 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<uint8_t>& encrypted,
const std::vector<uint8_t>& key,
std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& compressed,
std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& md5Hash) {
// Check in-memory cache
if (modules_.find(md5Hash) != modules_.end()) {
return modules_[md5Hash]->isLoaded();
}
// Check disk cache
std::vector<uint8_t> dummy;
return loadCachedModule(md5Hash, dummy);
}
std::shared_ptr<WardenModule> WardenModuleManager::getModule(const std::vector<uint8_t>& md5Hash) {
auto it = modules_.find(md5Hash);
if (it != modules_.end()) {
return it->second;
}
// Create new module instance
auto module = std::make_shared<WardenModule>();
modules_[md5Hash] = module;
return module;
}
bool WardenModuleManager::receiveModuleChunk(const std::vector<uint8_t>& md5Hash,
const std::vector<uint8_t>& chunkData,
bool isComplete) {
// Append to download buffer
std::vector<uint8_t>& 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<uint8_t>& md5Hash,
const std::vector<uint8_t>& 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<const char*>(moduleData.data()), moduleData.size());
file.close();
std::cout << "[WardenModuleManager] Cached module to: " << cachePath << std::endl;
return true;
}
bool WardenModuleManager::loadCachedModule(const std::vector<uint8_t>& md5Hash,
std::vector<uint8_t>& 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<char*>(moduleDataOut.data()), fileSize);
file.close();
std::cout << "[WardenModuleManager] Loaded cached module (" << fileSize << " bytes)" << std::endl;
return true;
}
std::string WardenModuleManager::getCachePath(const std::vector<uint8_t>& 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