mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Enhanced Warden implementation with comprehensive documentation
Added MD5 hashing and extensive testing documentation for future attempts at supporting strict Warden servers like Warmane. Enhancements: - Added MD5 hash support to Crypto class (OpenSSL-based) - Tested 6 different module ACK response formats against Warmane - Analyzed module packet structure (37 bytes: opcode + seed + trailing) - Enhanced debug logging for plaintext and encrypted Warden data Documentation: - WARDEN_IMPLEMENTATION.md: Complete implementation guide with all attempts - WARDEN_QUICK_REFERENCE.md: Quick troubleshooting and testing guide Test Results (Warmane): - Empty ACK (0 bytes): Server silent - XOR/MD5 checksum (18 bytes): Server silent - Single byte (1 byte): Server disconnects (rejected) - Echo trailing (20 bytes): Server silent - Result + SHA1 (21 bytes): Server silent Conclusion: - Current implementation works with permissive/disabled Warden servers - Warmane requires module execution or undocumented response format - Full documentation provided for future reverse engineering attempts Next steps documented: 1. Capture packets from real WoW client (protocol analysis) 2. Implement module execution engine (months of work) 3. Test with local AzerothCore server
This commit is contained in:
parent
b9147baca6
commit
4b48fcdab2
5 changed files with 674 additions and 34 deletions
|
|
@ -4,6 +4,7 @@
|
|||
#include "game/opcodes.hpp"
|
||||
#include "network/world_socket.hpp"
|
||||
#include "network/packet.hpp"
|
||||
#include "auth/crypto.hpp"
|
||||
#include "core/coordinates.hpp"
|
||||
#include "core/application.hpp"
|
||||
#include "pipeline/asset_manager.hpp"
|
||||
|
|
@ -1838,30 +1839,41 @@ void GameHandler::handleWardenData(network::Packet& packet) {
|
|||
wardenCrypto_.reset();
|
||||
return;
|
||||
}
|
||||
LOG_INFO("Warden: Crypto initialized, sending module ACK with checksum");
|
||||
LOG_INFO("Warden: Crypto initialized, analyzing module structure");
|
||||
|
||||
// Build module acknowledgment response
|
||||
// Format: [0x00 opcode][16-byte MD5 hash of module][0x01 result = success]
|
||||
// Parse module structure (37 bytes typical):
|
||||
// [1 byte opcode][16 bytes seed][20 bytes trailing data (SHA1?)]
|
||||
std::vector<uint8_t> trailingBytes;
|
||||
if (data.size() >= 37) {
|
||||
std::string trailingHex;
|
||||
for (size_t i = 17; i < 37; ++i) {
|
||||
trailingBytes.push_back(data[i]);
|
||||
char b[4];
|
||||
snprintf(b, sizeof(b), "%02x ", data[i]);
|
||||
trailingHex += b;
|
||||
}
|
||||
LOG_INFO("Warden: Module trailing data (20 bytes): ", trailingHex);
|
||||
}
|
||||
|
||||
// Try response strategy: Result code + SHA1 hash of entire module
|
||||
// Format: [0x01 success][20-byte SHA1 of module data]
|
||||
std::vector<uint8_t> moduleResponse;
|
||||
|
||||
// Opcode 0x00 = module info response
|
||||
moduleResponse.push_back(0x00);
|
||||
LOG_INFO("Warden: Trying response strategy: [0x01][SHA1 of module]");
|
||||
|
||||
// Compute simple checksum of module data (16 bytes)
|
||||
// For a proper implementation, this would be MD5, but we'll use a simpler hash
|
||||
uint8_t checksum[16] = {0};
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
checksum[i % 16] ^= data[i];
|
||||
}
|
||||
|
||||
// Add checksum to response
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
moduleResponse.push_back(checksum[i]);
|
||||
}
|
||||
|
||||
// Result code: 0x01 = module loaded successfully
|
||||
// Success result code
|
||||
moduleResponse.push_back(0x01);
|
||||
|
||||
// Compute SHA1 hash of the entire module packet
|
||||
std::vector<uint8_t> sha1Hash = auth::Crypto::sha1(data);
|
||||
|
||||
// Add SHA1 hash (20 bytes)
|
||||
for (uint8_t byte : sha1Hash) {
|
||||
moduleResponse.push_back(byte);
|
||||
}
|
||||
|
||||
LOG_INFO("Warden: Response = result(0x01) + SHA1 of ", data.size(), " byte module");
|
||||
|
||||
// Log plaintext module response
|
||||
std::string respHex;
|
||||
respHex.reserve(moduleResponse.size() * 3);
|
||||
|
|
@ -1926,39 +1938,89 @@ void GameHandler::handleWardenData(network::Packet& packet) {
|
|||
responseData.push_back(0x00);
|
||||
break;
|
||||
|
||||
case 0x01: // Hash request
|
||||
LOG_INFO("Warden: Hash request");
|
||||
case 0x01: { // Hash request
|
||||
LOG_INFO("Warden: Hash request (file/memory hash check)");
|
||||
// Parse hash request structure
|
||||
// Format: [0x01][seed][hash_to_check][...]
|
||||
responseData.push_back(0x01);
|
||||
responseData.push_back(0x00); // Pass
|
||||
break;
|
||||
|
||||
case 0x02: // Lua string check
|
||||
LOG_INFO("Warden: Lua string check");
|
||||
// For each hash check, respond with matching result
|
||||
if (decrypted.size() > 1) {
|
||||
// Extract number of hash checks (varies by server)
|
||||
size_t pos = 1;
|
||||
while (pos < decrypted.size() && responseData.size() < 64) {
|
||||
responseData.push_back(0x00); // Hash matches (no violation)
|
||||
pos += 20; // Skip 20-byte hash (SHA1)
|
||||
}
|
||||
} else {
|
||||
responseData.push_back(0x00); // Pass
|
||||
}
|
||||
LOG_INFO("Warden: Hash check response with ", responseData.size() - 1, " results");
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x02: { // Lua string check
|
||||
LOG_INFO("Warden: Lua string check (anti-addon detection)");
|
||||
// Format: [0x02][lua_length][lua_string]
|
||||
responseData.push_back(0x02);
|
||||
responseData.push_back(0x00); // Empty = no detection
|
||||
break;
|
||||
|
||||
case 0x05: // Memory check
|
||||
LOG_INFO("Warden: Memory check");
|
||||
// Return empty string = no suspicious Lua found
|
||||
responseData.push_back(0x00); // String length = 0
|
||||
LOG_INFO("Warden: Lua check - returned empty (no detection)");
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x05: { // Memory/page check
|
||||
LOG_INFO("Warden: Memory page check (anti-cheat scan)");
|
||||
// Format: [0x05][seed][address_count][addresses...][length]
|
||||
|
||||
if (decrypted.size() >= 2) {
|
||||
uint8_t numChecks = decrypted[1];
|
||||
LOG_INFO("Warden: ", (int)numChecks, " memory checks");
|
||||
LOG_INFO("Warden: Processing ", (int)numChecks, " memory checks");
|
||||
|
||||
responseData.push_back(0x05);
|
||||
responseData.push_back(numChecks);
|
||||
|
||||
// For each memory region checked, return "clean" data
|
||||
for (uint8_t i = 0; i < numChecks; ++i) {
|
||||
responseData.push_back(0x00); // All pass
|
||||
// Return zeroed memory (appears clean/unmodified)
|
||||
responseData.push_back(0x00);
|
||||
}
|
||||
|
||||
LOG_INFO("Warden: Memory check - all regions clean");
|
||||
} else {
|
||||
responseData.push_back(0x05);
|
||||
responseData.push_back(0x00);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x04: { // Timing check
|
||||
LOG_INFO("Warden: Timing check (detect speedhacks)");
|
||||
// Return current timestamp
|
||||
responseData.push_back(0x04);
|
||||
uint32_t timestamp = static_cast<uint32_t>(std::time(nullptr));
|
||||
responseData.push_back((timestamp >> 0) & 0xFF);
|
||||
responseData.push_back((timestamp >> 8) & 0xFF);
|
||||
responseData.push_back((timestamp >> 16) & 0xFF);
|
||||
responseData.push_back((timestamp >> 24) & 0xFF);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_INFO("Warden: Unknown check opcode 0x", std::hex, (int)opcode, std::dec);
|
||||
// Send minimal response
|
||||
responseData.push_back(opcode);
|
||||
responseData.push_back(0x00);
|
||||
LOG_INFO("Warden: Packet dump: ", decHex);
|
||||
|
||||
// Try to detect packet type from size/structure
|
||||
if (decrypted.size() > 20) {
|
||||
LOG_INFO("Warden: Large packet - might be batched checks");
|
||||
// Some servers send multiple checks in one packet
|
||||
// Try to respond to each one
|
||||
responseData.push_back(0x00); // Generic "OK" response
|
||||
} else {
|
||||
// Small unknown packet - echo with success
|
||||
responseData.push_back(opcode);
|
||||
responseData.push_back(0x00);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue