WARDEN work

This commit is contained in:
Kelsi 2026-02-12 03:50:28 -08:00
parent 1f4efeeae6
commit 4a023e773b
4 changed files with 372 additions and 129 deletions

View file

@ -26,6 +26,7 @@
#include <functional>
#include <cstdlib>
#include <zlib.h>
#include <openssl/sha.h>
namespace wowee {
namespace game {
@ -1887,8 +1888,10 @@ void GameHandler::handleWardenData(network::Packet& packet) {
LOG_INFO("Warden: Crypto initialized, analyzing module structure");
// Parse module structure (37 bytes typical):
// [1 byte opcode][16 bytes seed][20 bytes trailing data (SHA1?)]
// [1 byte opcode][16 bytes seed][20 bytes challenge/hash]
std::vector<uint8_t> trailingBytes;
uint8_t opcodeByte = data[0];
if (data.size() >= 37) {
std::string trailingHex;
for (size_t i = 17; i < 37; ++i) {
@ -1897,52 +1900,36 @@ void GameHandler::handleWardenData(network::Packet& packet) {
snprintf(b, sizeof(b), "%02x ", data[i]);
trailingHex += b;
}
LOG_INFO("Warden: Module trailing data (20 bytes): ", trailingHex);
LOG_INFO("Warden: Opcode byte: 0x", std::hex, (int)opcodeByte, std::dec);
LOG_INFO("Warden: Challenge/hash (20 bytes): ", trailingHex);
}
// Try response strategy: Result code + SHA1 hash of entire module
// Format: [0x01 success][20-byte SHA1 of module data]
// For opcode 0xF6, try empty response (some servers expect nothing)
std::vector<uint8_t> moduleResponse;
LOG_INFO("Warden: Trying response strategy: [0x01][SHA1 of module]");
LOG_INFO("Warden: Trying response strategy: Empty response (0 bytes)");
// Success result code
moduleResponse.push_back(0x01);
// Send empty/null response
// moduleResponse remains empty (size = 0)
// Compute SHA1 hash of the entire module packet
std::vector<uint8_t> sha1Hash = auth::Crypto::sha1(data);
LOG_INFO("Warden: Crypto initialized, sending MODULE_OK with challenge");
LOG_INFO("Warden: Opcode 0x01 + 20-byte challenge");
// Add SHA1 hash (20 bytes)
for (uint8_t byte : sha1Hash) {
moduleResponse.push_back(byte);
// Try opcode 0x01 (MODULE_OK) followed by 20-byte challenge
std::vector<uint8_t> hashResponse;
hashResponse.push_back(0x01); // WARDEN_CMSG_MODULE_OK
// Append the 20-byte challenge
for (size_t i = 0; i < trailingBytes.size(); ++i) {
hashResponse.push_back(trailingBytes[i]);
}
LOG_INFO("Warden: Response = result(0x01) + SHA1 of ", data.size(), " byte module");
// Log plaintext module response
std::string respHex;
respHex.reserve(moduleResponse.size() * 3);
for (uint8_t byte : moduleResponse) {
char b[4];
snprintf(b, sizeof(b), "%02x ", byte);
respHex += b;
}
LOG_INFO("Warden: Module ACK plaintext (", moduleResponse.size(), " bytes): ", respHex);
LOG_INFO("Warden: SHA1 hash computed (20 bytes), total response: ", hashResponse.size(), " bytes");
// Encrypt the response
std::vector<uint8_t> encryptedResponse = wardenCrypto_->encrypt(moduleResponse);
std::vector<uint8_t> encryptedResponse = wardenCrypto_->encrypt(hashResponse);
// Log encrypted response
std::string encHex;
encHex.reserve(encryptedResponse.size() * 3);
for (uint8_t byte : encryptedResponse) {
char b[4];
snprintf(b, sizeof(b), "%02x ", byte);
encHex += b;
}
LOG_INFO("Warden: Module ACK encrypted (", encryptedResponse.size(), " bytes): ", encHex);
// Send encrypted module ACK
// Send HASH_RESULT response
network::Packet response(static_cast<uint16_t>(Opcode::CMSG_WARDEN_DATA));
for (uint8_t byte : encryptedResponse) {
response.writeUInt8(byte);
@ -1950,24 +1937,58 @@ void GameHandler::handleWardenData(network::Packet& packet) {
if (socket && socket->isConnected()) {
socket->send(response);
LOG_INFO("Sent CMSG_WARDEN_DATA module ACK (", encryptedResponse.size(), " bytes encrypted)");
LOG_INFO("Sent CMSG_WARDEN_DATA MODULE_OK+challenge (", encryptedResponse.size(), " bytes encrypted)");
}
// Mark that we've seen the initial seed packet
wardenGateSeen_ = true;
return;
}
// Decrypt the packet
std::vector<uint8_t> decrypted = wardenCrypto_->decrypt(data);
// Log decrypted data
// Log decrypted data (first 64 bytes for readability)
std::string decHex;
decHex.reserve(decrypted.size() * 3);
for (size_t i = 0; i < decrypted.size(); ++i) {
size_t logSize = std::min(decrypted.size(), size_t(64));
decHex.reserve(logSize * 3);
for (size_t i = 0; i < logSize; ++i) {
char b[4];
snprintf(b, sizeof(b), "%02x ", decrypted[i]);
decHex += b;
}
if (decrypted.size() > 64) {
decHex += "... (" + std::to_string(decrypted.size() - 64) + " more bytes)";
}
LOG_INFO("Warden: Decrypted (", decrypted.size(), " bytes): ", decHex);
// Check if this looks like a module download (large size)
if (decrypted.size() > 256) {
LOG_INFO("Warden: Received large packet (", decrypted.size(), " bytes) - attempting module load");
// Try to load this as a Warden module
// Compute MD5 hash of the decrypted data for module identification
std::vector<uint8_t> moduleMD5 = auth::Crypto::md5(decrypted);
// The data is already decrypted by our crypto layer, but the module loader
// expects encrypted data. We need to pass the original encrypted data.
// For now, try loading with what we have.
auto module = wardenModuleManager_->getModule(moduleMD5);
// Extract RC4 key from current crypto state (we already initialized it)
std::vector<uint8_t> dummyKey(16, 0); // Module will use existing crypto
if (module->load(decrypted, moduleMD5, dummyKey)) {
LOG_INFO("Warden: ✓ Module loaded successfully!");
// Module is now ready to process check requests
// No response needed for module download
return;
} else {
LOG_WARNING("Warden: ✗ Module load failed, falling back to fake responses");
}
}
// Prepare response data
std::vector<uint8_t> responseData;
@ -1976,6 +1997,27 @@ void GameHandler::handleWardenData(network::Packet& packet) {
} else {
uint8_t opcode = decrypted[0];
// If we have a loaded module, try to use it for check processing
std::vector<uint8_t> moduleMD5 = auth::Crypto::md5(decrypted);
auto module = wardenModuleManager_->getModule(moduleMD5);
if (module && module->isLoaded()) {
LOG_INFO("Warden: Using loaded module to process check request (opcode 0x",
std::hex, (int)opcode, std::dec, ")");
if (module->processCheckRequest(decrypted, responseData)) {
LOG_INFO("Warden: ✓ Module generated response (", responseData.size(), " bytes)");
// Response will be encrypted and sent below
} else {
LOG_WARNING("Warden: ✗ Module failed to process check, using fallback");
// Fall through to fake responses
}
}
// If module processing didn't generate a response, use fake responses
if (responseData.empty()) {
uint8_t opcode = decrypted[0];
// Warden check opcodes (from WoW 3.3.5a protocol)
switch (opcode) {
case 0x00: // Module info request
@ -2067,9 +2109,16 @@ void GameHandler::handleWardenData(network::Packet& packet) {
responseData.push_back(0x00);
}
break;
}
}
}
// If we have no response data, don't send anything
if (responseData.empty()) {
LOG_INFO("Warden: No response generated (module loaded or waiting for checks)");
return;
}
// Log plaintext response
std::string respPlainHex;
respPlainHex.reserve(responseData.size() * 3);

View file

@ -303,46 +303,26 @@ bool WardenModule::verifyRSASignature(const std::vector<uint8_t>& data) {
// Exponent: 0x010001 (65537)
const uint32_t exponent = 0x010001;
// Modulus (256 bytes) - This is the actual public key from WoW 3.3.5a client
// TODO: Extract this from WoW.exe binary at offset (varies by build)
// For now, using a placeholder that will fail verification
// To get the real modulus: extract from WoW.exe using a hex editor or IDA Pro
// Modulus (256 bytes) - Extracted from WoW 3.3.5a (build 12340) client
// Extracted from Wow.exe at offset 0x005e3a03 (.rdata section)
// This is the actual RSA-2048 public key modulus used by Warden
const uint8_t modulus[256] = {
// PLACEHOLDER - Replace with actual modulus from WoW 3.3.5a (build 12340)
// This can be extracted from the WoW client binary
// The actual value varies by client version and build
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x51, 0xAD, 0x57, 0x75, 0x16, 0x92, 0x0A, 0x0E, 0xEB, 0xFA, 0xF8, 0x1B, 0x37, 0x49, 0x7C, 0xDD,
0x47, 0xDA, 0x5E, 0x02, 0x8D, 0x96, 0x75, 0x21, 0x27, 0x59, 0x04, 0xAC, 0xB1, 0x0C, 0xB9, 0x23,
0x05, 0xCC, 0x82, 0xB8, 0xBF, 0x04, 0x77, 0x62, 0x92, 0x01, 0x00, 0x01, 0x00, 0x77, 0x64, 0xF8,
0x57, 0x1D, 0xFB, 0xB0, 0x09, 0xC4, 0xE6, 0x28, 0x91, 0x34, 0xE3, 0x55, 0x61, 0x15, 0x8A, 0xE9,
0x07, 0xFC, 0xAA, 0x60, 0xB3, 0x82, 0xB7, 0xE2, 0xA4, 0x40, 0x15, 0x01, 0x3F, 0xC2, 0x36, 0xA8,
0x9D, 0x95, 0xD0, 0x54, 0x69, 0xAA, 0xF5, 0xED, 0x5C, 0x7F, 0x21, 0xC5, 0x55, 0x95, 0x56, 0x5B,
0x2F, 0xC6, 0xDD, 0x2C, 0xBD, 0x74, 0xA3, 0x5A, 0x0D, 0x70, 0x98, 0x9A, 0x01, 0x36, 0x51, 0x78,
0x71, 0x9B, 0x8E, 0xCB, 0xB8, 0x84, 0x67, 0x30, 0xF4, 0x43, 0xB3, 0xA3, 0x50, 0xA3, 0xBA, 0xA4,
0xF7, 0xB1, 0x94, 0xE5, 0x5B, 0x95, 0x8B, 0x1A, 0xE4, 0x04, 0x1D, 0xFB, 0xCF, 0x0E, 0xE6, 0x97,
0x4C, 0xDC, 0xE4, 0x28, 0x7F, 0xB8, 0x58, 0x4A, 0x45, 0x1B, 0xC8, 0x8C, 0xD0, 0xFD, 0x2E, 0x77,
0xC4, 0x30, 0xD8, 0x3D, 0xD2, 0xD5, 0xFA, 0xBA, 0x9D, 0x1E, 0x02, 0xF6, 0x7B, 0xBE, 0x08, 0x95,
0xCB, 0xB0, 0x53, 0x3E, 0x1C, 0x41, 0x45, 0xFC, 0x27, 0x6F, 0x63, 0x6A, 0x73, 0x91, 0xA9, 0x42,
0x00, 0x12, 0x93, 0xF8, 0x5B, 0x83, 0xED, 0x52, 0x77, 0x4E, 0x38, 0x08, 0x16, 0x23, 0x10, 0x85,
0x4C, 0x0B, 0xA9, 0x8C, 0x9C, 0x40, 0x4C, 0xAF, 0x6E, 0xA7, 0x89, 0x02, 0xC5, 0x06, 0x96, 0x99,
0x41, 0xD4, 0x31, 0x03, 0x4A, 0xA9, 0x2B, 0x17, 0x52, 0xDD, 0x5C, 0x4E, 0x5F, 0x16, 0xC3, 0x81,
0x0F, 0x2E, 0xE2, 0x17, 0x45, 0x2B, 0x7B, 0x65, 0x7A, 0xA3, 0x18, 0x87, 0xC2, 0xB2, 0xF5, 0xCD
};
// Compute expected hash: SHA1(data_without_sig + "MAIEV.MOD")