mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Implement Warden Phase 4: Executable Loader (partial)
Added module memory allocation and skip/copy parsing: Executable Format Parser: - Read 4-byte little-endian final code size - Parse alternating skip/copy sections (2-byte length + data) - Skip sections: advance offset without copying - Copy sections: memcpy x86 code to allocated memory - Boundary validation and sanity checks (max 5MB code) Memory Allocation: - Linux: mmap() with PROT_READ|WRITE|EXEC permissions - Windows: VirtualAlloc() with PAGE_EXECUTE_READWRITE - Proper cleanup in unload() (munmap/VirtualFree) - Zero-initialize allocated memory Address Relocations (STUB): - Framework in place for delta-encoded offset parsing - Needs real Warden module data to implement correctly - Currently returns true to continue loading pipeline Load Pipeline Status: ✅ Step 1-5: MD5, RC4, RSA, zlib, exe parsing ⚠️ Step 6: Relocations (stub - needs real module) ⏳ Step 7-8: API binding, initialization Progress: 4/7 phases underway (~1.5 months remaining) Next: Phase 5 (API Binding) - kernel32.dll/user32.dll imports
This commit is contained in:
parent
68a66a02a4
commit
82d0b211fb
2 changed files with 215 additions and 49 deletions
|
|
@ -102,13 +102,13 @@ Step 1: Verify MD5 ✅ Implemented (uses auth::Crypto::md5)
|
||||||
Step 2: RC4 Decrypt ✅ Implemented (standalone RC4 in WardenModule)
|
Step 2: RC4 Decrypt ✅ Implemented (standalone RC4 in WardenModule)
|
||||||
Step 3: RSA Verify ✅ Implemented (OpenSSL, placeholder modulus)
|
Step 3: RSA Verify ✅ Implemented (OpenSSL, placeholder modulus)
|
||||||
Step 4: zlib Decompress ✅ Implemented (zlib library)
|
Step 4: zlib Decompress ✅ Implemented (zlib library)
|
||||||
Step 5: Parse Exe ❌ TODO (custom skip/copy format)
|
Step 5: Parse Exe ✅ Implemented (custom skip/copy format, mmap/VirtualAlloc)
|
||||||
Step 6: Relocations ❌ TODO (delta-encoded offsets)
|
Step 6: Relocations ⚠️ STUB (needs real module data for delta decoding)
|
||||||
Step 7: Bind APIs ❌ TODO (kernel32.dll, user32.dll imports)
|
Step 7: Bind APIs ❌ TODO (kernel32.dll, user32.dll imports)
|
||||||
Step 8: Initialize ❌ TODO (call module entry point)
|
Step 8: Initialize ❌ TODO (call module entry point)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Current Behavior**: Steps 1-4 succeed (validation layer complete), steps 5-8 are logged as "NOT IMPLEMENTED". Module is marked as NOT loaded (`loaded_ = false`) until execution layer is complete.
|
**Current Behavior**: Steps 1-6 implemented (steps 5-6 need real module data), steps 7-8 are logged as "NOT IMPLEMENTED". Module is marked as NOT loaded (`loaded_ = false`) until execution layer is complete.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -356,17 +356,22 @@ SHA1(module_data + "MAIEV.MOD") padded with 0xBB bytes
|
||||||
- PRGA (Pseudo-Random Generation Algorithm)
|
- PRGA (Pseudo-Random Generation Algorithm)
|
||||||
- Used for module decryption (separate from WardenCrypto)
|
- Used for module decryption (separate from WardenCrypto)
|
||||||
|
|
||||||
### Phase 4: Executable Loader (TODO - 2-3 weeks)
|
### Phase 4: Executable Loader (PARTIALLY COMPLETE ⚠️)
|
||||||
|
|
||||||
- [ ] Parse custom skip/copy executable format
|
- [x] Parse custom skip/copy executable format
|
||||||
- Read alternating skip/copy sections (2-byte length + data)
|
- Read alternating skip/copy sections (2-byte length + data)
|
||||||
- Allocate executable memory region
|
- Allocate executable memory region (mmap on Linux, VirtualAlloc on Windows)
|
||||||
- Copy code sections to memory
|
- Copy code sections to memory
|
||||||
- [ ] Implement address relocation
|
- Sanity checks (max 5MB code size, boundary validation)
|
||||||
|
- [x] Memory allocation with execution permissions
|
||||||
|
- Linux: mmap with PROT_READ | PROT_WRITE | PROT_EXEC
|
||||||
|
- Windows: VirtualAlloc with PAGE_EXECUTE_READWRITE
|
||||||
|
- Proper cleanup in unload() (munmap/VirtualFree)
|
||||||
|
- [ ] Implement address relocation (STUB)
|
||||||
- Parse delta-encoded offsets (multi-byte with high-bit continuation)
|
- Parse delta-encoded offsets (multi-byte with high-bit continuation)
|
||||||
- Fix absolute references relative to module base address
|
- Fix absolute references relative to module base address
|
||||||
- Update pointer tables
|
- Update pointer tables
|
||||||
- [ ] Set memory permissions (VirtualProtect equivalent)
|
- ⚠️ Needs real Warden module data to implement correctly
|
||||||
|
|
||||||
### Phase 5: API Binding (TODO - 1 week)
|
### Phase 5: API Binding (TODO - 1 week)
|
||||||
|
|
||||||
|
|
@ -408,12 +413,12 @@ SHA1(module_data + "MAIEV.MOD") padded with 0xBB bytes
|
||||||
|-------|----------|------------|--------|
|
|-------|----------|------------|--------|
|
||||||
| Phase 1: Crypto | - | ⭐⭐ | ✅ DONE |
|
| Phase 1: Crypto | - | ⭐⭐ | ✅ DONE |
|
||||||
| Phase 2: Foundation | - | ⭐ | ✅ DONE |
|
| Phase 2: Foundation | - | ⭐ | ✅ DONE |
|
||||||
| Phase 3: Validation | 1 week | ⭐⭐⭐ | ✅ DONE |
|
| Phase 3: Validation | - | ⭐⭐⭐ | ✅ DONE |
|
||||||
| Phase 4: Executable Loader | 2-3 weeks | ⭐⭐⭐⭐⭐ | 🔜 NEXT |
|
| Phase 4: Executable Loader | Partial | ⭐⭐⭐⭐⭐ | ⚠️ PARTIAL (needs real module) |
|
||||||
| Phase 5: API Binding | 1 week | ⭐⭐⭐ | ⏳ TODO |
|
| Phase 5: API Binding | 1 week | ⭐⭐⭐ | 🔜 NEXT |
|
||||||
| Phase 6: Execution Engine | 2-3 weeks | ⭐⭐⭐⭐⭐ | ⏳ TODO |
|
| Phase 6: Execution Engine | 2-3 weeks | ⭐⭐⭐⭐⭐ | ⏳ TODO |
|
||||||
| Phase 7: Testing | 1-2 weeks | ⭐⭐⭐⭐ | ⏳ TODO |
|
| Phase 7: Testing | 1-2 weeks | ⭐⭐⭐⭐ | ⏳ TODO |
|
||||||
| **TOTAL** | **~2 months remaining** | **Very High** | **3/7 done** |
|
| **TOTAL** | **~1.5 months remaining** | **Very High** | **4/7 underway** |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -535,6 +540,8 @@ sendWardenResponse(encrypted);
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-02-12
|
**Last Updated**: 2026-02-12
|
||||||
**Status**: Phase 3 (Validation Layer) COMPLETE ✅
|
**Status**: Phase 4 (Executable Loader) PARTIAL ⚠️
|
||||||
**Next Step**: Phase 4 (Executable Loader) - Parse skip/copy format, allocate memory
|
**What Works**: Module parsing, memory allocation, skip/copy sections
|
||||||
**Remaining**: ~2 months (phases 4-7)
|
**What's Stubbed**: Relocations (needs real module data to test)
|
||||||
|
**Next Step**: Phase 5 (API Binding) - Resolve kernel32.dll/user32.dll imports
|
||||||
|
**Remaining**: ~1.5 months (phases 5-7)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
#include <openssl/bn.h>
|
#include <openssl/bn.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace game {
|
namespace game {
|
||||||
|
|
||||||
|
|
@ -66,22 +71,16 @@ bool WardenModule::load(const std::vector<uint8_t>& moduleData,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Parse custom executable format
|
// Step 5: Parse custom executable format
|
||||||
// TODO: Parse skip/copy section structure
|
if (!parseExecutableFormat(decompressedData_)) {
|
||||||
// - Read alternating [2-byte length][data] sections
|
std::cerr << "[WardenModule] Executable format parsing failed!" << std::endl;
|
||||||
// - Skip sections are ignored, copy sections loaded
|
return false;
|
||||||
std::cout << "[WardenModule] ⏸ Executable format parsing (NOT IMPLEMENTED)" << std::endl;
|
}
|
||||||
// if (!parseExecutableFormat(decompressedData_)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Step 6: Apply relocations
|
// Step 6: Apply relocations
|
||||||
// TODO: Fix absolute address references
|
if (!applyRelocations()) {
|
||||||
// - Delta-encoded offsets with high-bit continuation
|
std::cerr << "[WardenModule] Address relocations failed!" << std::endl;
|
||||||
// - Update all pointers relative to module base
|
return false;
|
||||||
std::cout << "[WardenModule] ⏸ Address relocations (NOT IMPLEMENTED)" << std::endl;
|
}
|
||||||
// if (!applyRelocations()) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Step 7: Bind APIs
|
// Step 7: Bind APIs
|
||||||
// TODO: Resolve kernel32.dll, user32.dll imports
|
// TODO: Resolve kernel32.dll, user32.dll imports
|
||||||
|
|
@ -157,13 +156,28 @@ void WardenModule::generateRC4Keys(uint8_t* packet) {
|
||||||
|
|
||||||
void WardenModule::unload() {
|
void WardenModule::unload() {
|
||||||
if (moduleMemory_) {
|
if (moduleMemory_) {
|
||||||
// TODO: Free executable memory region
|
// Call module's Unload() function if loaded
|
||||||
// - Call module's Unload() function first
|
if (loaded_ && funcList_.unload) {
|
||||||
// - Free allocated memory
|
std::cout << "[WardenModule] Calling module unload callback..." << std::endl;
|
||||||
// - Clear function pointers
|
// TODO: Implement callback when execution layer is complete
|
||||||
|
// funcList_.unload(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free executable memory region
|
||||||
|
std::cout << "[WardenModule] Freeing " << moduleSize_ << " bytes of executable memory" << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
VirtualFree(moduleMemory_, 0, MEM_RELEASE);
|
||||||
|
#else
|
||||||
|
munmap(moduleMemory_, moduleSize_);
|
||||||
|
#endif
|
||||||
|
|
||||||
moduleMemory_ = nullptr;
|
moduleMemory_ = nullptr;
|
||||||
|
moduleSize_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear function pointers
|
||||||
|
funcList_ = {};
|
||||||
|
|
||||||
loaded_ = false;
|
loaded_ = false;
|
||||||
moduleData_.clear();
|
moduleData_.clear();
|
||||||
decryptedData_.clear();
|
decryptedData_.clear();
|
||||||
|
|
@ -404,26 +418,171 @@ bool WardenModule::decompressZlib(const std::vector<uint8_t>& compressed,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WardenModule::parseExecutableFormat(const std::vector<uint8_t>& exeData) {
|
bool WardenModule::parseExecutableFormat(const std::vector<uint8_t>& exeData) {
|
||||||
// TODO: Parse custom skip/copy executable format
|
if (exeData.size() < 4) {
|
||||||
//
|
std::cerr << "[WardenModule] Executable data too small for header" << std::endl;
|
||||||
// Format:
|
return false;
|
||||||
// [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
|
// Read final code size (little-endian 4 bytes)
|
||||||
|
uint32_t finalCodeSize =
|
||||||
|
exeData[0] |
|
||||||
|
(exeData[1] << 8) |
|
||||||
|
(exeData[2] << 16) |
|
||||||
|
(exeData[3] << 24);
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] Final code size: " << finalCodeSize << " bytes" << std::endl;
|
||||||
|
|
||||||
|
// Sanity check (executable shouldn't be larger than 5MB)
|
||||||
|
if (finalCodeSize > 5 * 1024 * 1024 || finalCodeSize == 0) {
|
||||||
|
std::cerr << "[WardenModule] Invalid final code size: " << finalCodeSize << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate executable memory
|
||||||
|
// Note: On Linux, we'll use mmap with PROT_EXEC
|
||||||
|
// On Windows, would use VirtualAlloc with PAGE_EXECUTE_READWRITE
|
||||||
|
#ifdef _WIN32
|
||||||
|
moduleMemory_ = VirtualAlloc(
|
||||||
|
nullptr,
|
||||||
|
finalCodeSize,
|
||||||
|
MEM_COMMIT | MEM_RESERVE,
|
||||||
|
PAGE_EXECUTE_READWRITE
|
||||||
|
);
|
||||||
|
if (!moduleMemory_) {
|
||||||
|
std::cerr << "[WardenModule] VirtualAlloc failed" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
moduleMemory_ = mmap(
|
||||||
|
nullptr,
|
||||||
|
finalCodeSize,
|
||||||
|
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||||
|
-1,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (moduleMemory_ == MAP_FAILED) {
|
||||||
|
std::cerr << "[WardenModule] mmap failed: " << strerror(errno) << std::endl;
|
||||||
|
moduleMemory_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
moduleSize_ = finalCodeSize;
|
||||||
|
std::memset(moduleMemory_, 0, moduleSize_); // Zero-initialize
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] Allocated " << moduleSize_ << " bytes of executable memory at "
|
||||||
|
<< moduleMemory_ << std::endl;
|
||||||
|
|
||||||
|
// Parse skip/copy sections
|
||||||
|
size_t pos = 4; // Skip 4-byte size header
|
||||||
|
size_t destOffset = 0;
|
||||||
|
bool isSkipSection = true; // Alternates: skip, copy, skip, copy, ...
|
||||||
|
int sectionCount = 0;
|
||||||
|
|
||||||
|
while (pos + 2 <= exeData.size()) {
|
||||||
|
// Read 2-byte section length (little-endian)
|
||||||
|
uint16_t sectionLength = exeData[pos] | (exeData[pos + 1] << 8);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
if (sectionLength == 0) {
|
||||||
|
break; // End of sections
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos + sectionLength > exeData.size()) {
|
||||||
|
std::cerr << "[WardenModule] Section extends beyond data bounds" << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
VirtualFree(moduleMemory_, 0, MEM_RELEASE);
|
||||||
|
#else
|
||||||
|
munmap(moduleMemory_, moduleSize_);
|
||||||
|
#endif
|
||||||
|
moduleMemory_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSkipSection) {
|
||||||
|
// Skip section - advance destination offset without copying
|
||||||
|
destOffset += sectionLength;
|
||||||
|
std::cout << "[WardenModule] Skip section: " << sectionLength << " bytes (dest offset now "
|
||||||
|
<< destOffset << ")" << std::endl;
|
||||||
|
} else {
|
||||||
|
// Copy section - copy code to module memory
|
||||||
|
if (destOffset + sectionLength > moduleSize_) {
|
||||||
|
std::cerr << "[WardenModule] Copy section exceeds module size" << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
VirtualFree(moduleMemory_, 0, MEM_RELEASE);
|
||||||
|
#else
|
||||||
|
munmap(moduleMemory_, moduleSize_);
|
||||||
|
#endif
|
||||||
|
moduleMemory_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(
|
||||||
|
static_cast<uint8_t*>(moduleMemory_) + destOffset,
|
||||||
|
exeData.data() + pos,
|
||||||
|
sectionLength
|
||||||
|
);
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] Copy section: " << sectionLength << " bytes to offset "
|
||||||
|
<< destOffset << std::endl;
|
||||||
|
|
||||||
|
destOffset += sectionLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += sectionLength;
|
||||||
|
isSkipSection = !isSkipSection; // Alternate
|
||||||
|
sectionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] ✓ Parsed " << sectionCount << " sections, final offset: "
|
||||||
|
<< destOffset << "/" << finalCodeSize << std::endl;
|
||||||
|
|
||||||
|
if (destOffset != finalCodeSize) {
|
||||||
|
std::cerr << "[WardenModule] WARNING: Final offset " << destOffset
|
||||||
|
<< " doesn't match expected size " << finalCodeSize << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WardenModule::applyRelocations() {
|
bool WardenModule::applyRelocations() {
|
||||||
// TODO: Fix absolute address references
|
if (!moduleMemory_ || moduleSize_ == 0) {
|
||||||
// - Delta-encoded offsets (multi-byte with high-bit continuation)
|
std::cerr << "[WardenModule] No module memory allocated for relocations" << std::endl;
|
||||||
// - Update pointers relative to moduleMemory_ base address
|
return false;
|
||||||
return false; // Not implemented
|
}
|
||||||
|
|
||||||
|
// Relocations are embedded in the decompressed data after the executable sections
|
||||||
|
// Format: Delta-encoded offsets with high-bit continuation
|
||||||
|
//
|
||||||
|
// Each offset is encoded as variable-length bytes:
|
||||||
|
// - If high bit (0x80) is set, read next byte and combine
|
||||||
|
// - Continue until byte without high bit
|
||||||
|
// - Final value is delta from previous relocation offset
|
||||||
|
//
|
||||||
|
// For each relocation offset:
|
||||||
|
// - Read 4-byte pointer at that offset
|
||||||
|
// - Add module base address to make it absolute
|
||||||
|
// - Write back to memory
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] Applying relocations to module at " << moduleMemory_ << std::endl;
|
||||||
|
|
||||||
|
// NOTE: Relocation data format and location varies by module
|
||||||
|
// Without a real module to test against, we can't implement this accurately
|
||||||
|
// This is a placeholder that would need to be filled in with actual logic
|
||||||
|
// once we have real Warden module data to analyze
|
||||||
|
|
||||||
|
std::cout << "[WardenModule] ⚠ Relocation application is STUB (needs real module data)" << std::endl;
|
||||||
|
std::cout << "[WardenModule] Would parse delta-encoded offsets and fix absolute references" << std::endl;
|
||||||
|
|
||||||
|
// Placeholder: Assume no relocations or already position-independent
|
||||||
|
// Real implementation would:
|
||||||
|
// 1. Find relocation table in module data
|
||||||
|
// 2. Decode delta offsets
|
||||||
|
// 3. For each offset: *(uint32_t*)(moduleMemory + offset) += (uintptr_t)moduleMemory_
|
||||||
|
|
||||||
|
return true; // Return true to continue (stub implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WardenModule::bindAPIs() {
|
bool WardenModule::bindAPIs() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue