mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Vanilla/Turtle WoW support: M2 loading, bone parsing, textures, auth
- Vanilla M2 bone struct (108 bytes) with 28-byte animation tracks - Version-aware bone parsing (vanilla vs WotLK format detection) - Fix CharSections.dbc field layout for vanilla (variation/color at 4-5) - Remove broken CharSections.csv files (all fields marked as strings) - Expansion data reload on profile switch (DBC cache clear, layout reload) - Vanilla packet encryption (VanillaCrypt XOR-based header crypt) - Extended character preview geoset range (0-99) for vanilla models - DBC cache clear support in AssetManager
This commit is contained in:
parent
6729f66a37
commit
430c2bdcfa
34 changed files with 1066 additions and 24795 deletions
|
|
@ -130,7 +130,8 @@ struct RealmListResponse {
|
|||
// REALM_LIST response parser
|
||||
class RealmListResponseParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, RealmListResponse& response);
|
||||
// protocolVersion: 3 = vanilla (uint8 realmCount, uint32 icon), 8 = WotLK (uint16 realmCount, uint8 icon)
|
||||
static bool parse(network::Packet& packet, RealmListResponse& response, uint8_t protocolVersion = 8);
|
||||
};
|
||||
|
||||
} // namespace auth
|
||||
|
|
|
|||
58
include/auth/vanilla_crypt.hpp
Normal file
58
include/auth/vanilla_crypt.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
/**
|
||||
* Vanilla/TBC WoW Header Cipher
|
||||
*
|
||||
* Used for encrypting/decrypting World of Warcraft packet headers
|
||||
* in vanilla (1.x) and TBC (2.x) clients. This is a simple XOR+addition
|
||||
* chaining cipher that uses the raw 40-byte SRP session key directly.
|
||||
*
|
||||
* Algorithm (encrypt):
|
||||
* encrypted = (plaintext ^ key[index]) + previousEncrypted
|
||||
* index = (index + 1) % keyLen
|
||||
*
|
||||
* Algorithm (decrypt):
|
||||
* plaintext = (encrypted - previousEncrypted) ^ key[index]
|
||||
* index = (index + 1) % keyLen
|
||||
*/
|
||||
class VanillaCrypt {
|
||||
public:
|
||||
VanillaCrypt() = default;
|
||||
~VanillaCrypt() = default;
|
||||
|
||||
/**
|
||||
* Initialize the cipher with the raw session key
|
||||
* @param sessionKey 40-byte session key from SRP auth
|
||||
*/
|
||||
void init(const std::vector<uint8_t>& sessionKey);
|
||||
|
||||
/**
|
||||
* Encrypt outgoing header bytes (CMSG: 6 bytes)
|
||||
* @param data Pointer to header data to encrypt in-place
|
||||
* @param length Number of bytes to encrypt
|
||||
*/
|
||||
void encrypt(uint8_t* data, size_t length);
|
||||
|
||||
/**
|
||||
* Decrypt incoming header bytes (SMSG: 4 bytes)
|
||||
* @param data Pointer to header data to decrypt in-place
|
||||
* @param length Number of bytes to decrypt
|
||||
*/
|
||||
void decrypt(uint8_t* data, size_t length);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> key_;
|
||||
uint8_t sendIndex_ = 0;
|
||||
uint8_t sendPrev_ = 0;
|
||||
uint8_t recvIndex_ = 0;
|
||||
uint8_t recvPrev_ = 0;
|
||||
};
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
|
|
@ -57,6 +57,7 @@ public:
|
|||
game::ExpansionRegistry* getExpansionRegistry() { return expansionRegistry_.get(); }
|
||||
pipeline::DBCLayout* getDBCLayout() { return dbcLayout_.get(); }
|
||||
pipeline::HDPackManager* getHDPackManager() { return hdPackManager_.get(); }
|
||||
void reloadExpansionData(); // Reload DBC layouts, opcodes, etc. after expansion change
|
||||
|
||||
// Singleton access
|
||||
static Application& getInstance() { return *instance; }
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ struct ExpansionProfile {
|
|||
uint8_t majorVersion = 0;
|
||||
uint8_t minorVersion = 0;
|
||||
uint8_t patchVersion = 0;
|
||||
uint16_t build = 0;
|
||||
uint8_t protocolVersion = 0; // SRP auth protocol version byte
|
||||
uint16_t build = 0; // Realm build (sent in LOGON_CHALLENGE)
|
||||
uint16_t worldBuild = 0; // World build (sent in CMSG_AUTH_SESSION, defaults to build)
|
||||
uint8_t protocolVersion = 0; // SRP auth protocol version byte
|
||||
// Client header fields used in LOGON_CHALLENGE.
|
||||
// Defaults match a typical Windows x86 client.
|
||||
std::string game = "WoW";
|
||||
|
|
|
|||
|
|
@ -1214,6 +1214,19 @@ private:
|
|||
std::unique_ptr<WardenCrypto> wardenCrypto_;
|
||||
std::unique_ptr<WardenModuleManager> wardenModuleManager_;
|
||||
|
||||
// Warden module download state
|
||||
enum class WardenState {
|
||||
WAIT_MODULE_USE, // Waiting for first SMSG (MODULE_USE)
|
||||
WAIT_MODULE_CACHE, // Sent MODULE_MISSING, receiving module chunks
|
||||
WAIT_HASH_REQUEST, // Module received, waiting for HASH_REQUEST
|
||||
WAIT_CHECKS, // Hash sent, waiting for check requests
|
||||
};
|
||||
WardenState wardenState_ = WardenState::WAIT_MODULE_USE;
|
||||
std::vector<uint8_t> wardenModuleHash_; // 16 bytes MD5
|
||||
std::vector<uint8_t> wardenModuleKey_; // 16 bytes RC4
|
||||
uint32_t wardenModuleSize_ = 0;
|
||||
std::vector<uint8_t> wardenModuleData_; // Downloaded module chunks
|
||||
|
||||
// ---- XP tracking ----
|
||||
uint32_t playerXp_ = 0;
|
||||
uint32_t playerNextLevelXp_ = 0;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ namespace wowee {
|
|||
namespace game {
|
||||
|
||||
/**
|
||||
* Warden anti-cheat crypto handler for WoW 3.3.5a
|
||||
* Handles RC4 encryption/decryption of Warden packets
|
||||
* Warden anti-cheat crypto handler.
|
||||
* Derives RC4 keys from the 40-byte SRP session key using SHA1Randx,
|
||||
* then encrypts/decrypts Warden packet payloads.
|
||||
*/
|
||||
class WardenCrypto {
|
||||
public:
|
||||
|
|
@ -17,45 +18,40 @@ public:
|
|||
~WardenCrypto();
|
||||
|
||||
/**
|
||||
* Initialize Warden crypto with module seed
|
||||
* @param moduleData The SMSG_WARDEN_DATA payload containing seed
|
||||
* @return true if initialization succeeded
|
||||
* Initialize Warden crypto from the 40-byte SRP session key.
|
||||
* Derives encrypt (client->server) and decrypt (server->client) RC4 keys
|
||||
* using the SHA1Randx / WardenKeyGenerator algorithm.
|
||||
*/
|
||||
bool initialize(const std::vector<uint8_t>& moduleData);
|
||||
bool initFromSessionKey(const std::vector<uint8_t>& sessionKey);
|
||||
|
||||
/**
|
||||
* Decrypt an incoming Warden packet
|
||||
* @param data Encrypted data from server
|
||||
* @return Decrypted data
|
||||
* Replace RC4 keys (called after module hash challenge succeeds).
|
||||
* @param newEncryptKey 16-byte key for encrypting outgoing packets
|
||||
* @param newDecryptKey 16-byte key for decrypting incoming packets
|
||||
*/
|
||||
void replaceKeys(const std::vector<uint8_t>& newEncryptKey,
|
||||
const std::vector<uint8_t>& newDecryptKey);
|
||||
|
||||
/** Decrypt an incoming SMSG_WARDEN_DATA payload. */
|
||||
std::vector<uint8_t> decrypt(const std::vector<uint8_t>& data);
|
||||
|
||||
/**
|
||||
* Encrypt an outgoing Warden response
|
||||
* @param data Plaintext response data
|
||||
* @return Encrypted data
|
||||
*/
|
||||
/** Encrypt an outgoing CMSG_WARDEN_DATA payload. */
|
||||
std::vector<uint8_t> encrypt(const std::vector<uint8_t>& data);
|
||||
|
||||
/**
|
||||
* Check if crypto has been initialized
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
std::vector<uint8_t> inputKey_;
|
||||
std::vector<uint8_t> outputKey_;
|
||||
|
||||
// RC4 state for incoming packets
|
||||
std::vector<uint8_t> inputRC4State_;
|
||||
uint8_t inputRC4_i_;
|
||||
uint8_t inputRC4_j_;
|
||||
// RC4 state for decrypting incoming packets (server->client)
|
||||
std::vector<uint8_t> decryptRC4State_;
|
||||
uint8_t decryptRC4_i_;
|
||||
uint8_t decryptRC4_j_;
|
||||
|
||||
// RC4 state for outgoing packets
|
||||
std::vector<uint8_t> outputRC4State_;
|
||||
uint8_t outputRC4_i_;
|
||||
uint8_t outputRC4_j_;
|
||||
// RC4 state for encrypting outgoing packets (client->server)
|
||||
std::vector<uint8_t> encryptRC4State_;
|
||||
uint8_t encryptRC4_i_;
|
||||
uint8_t encryptRC4_j_;
|
||||
|
||||
void initRC4(const std::vector<uint8_t>& key,
|
||||
std::vector<uint8_t>& state,
|
||||
|
|
@ -63,6 +59,14 @@ private:
|
|||
|
||||
void processRC4(const uint8_t* input, uint8_t* output, size_t length,
|
||||
std::vector<uint8_t>& state, uint8_t& i, uint8_t& j);
|
||||
|
||||
/**
|
||||
* SHA1Randx / WardenKeyGenerator: generates pseudo-random bytes from a seed.
|
||||
* Used to derive the 16-byte encrypt and decrypt keys from the session key.
|
||||
*/
|
||||
static void sha1RandxGenerate(const std::vector<uint8_t>& seed,
|
||||
uint8_t* outputEncryptKey,
|
||||
uint8_t* outputDecryptKey);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ struct AuthChallengeData {
|
|||
uint32_t serverSeed; // Random seed from server
|
||||
// Note: 3.3.5a has additional data after this
|
||||
|
||||
bool isValid() const { return unknown1 != 0; }
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "network/packet.hpp"
|
||||
#include "network/net_platform.hpp"
|
||||
#include "auth/rc4.hpp"
|
||||
#include "auth/vanilla_crypt.hpp"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
|
@ -14,12 +15,13 @@ namespace network {
|
|||
/**
|
||||
* World Server Socket
|
||||
*
|
||||
* Handles WoW 3.3.5a world server protocol with RC4 header encryption.
|
||||
* Handles WoW world server protocol with header encryption.
|
||||
* Supports both vanilla/TBC (XOR+addition cipher) and WotLK (RC4).
|
||||
*
|
||||
* Key Differences from Auth Server:
|
||||
* - Outgoing: 6-byte header (2 bytes size + 4 bytes opcode, big-endian)
|
||||
* - Incoming: 4-byte header (2 bytes size + 2 bytes opcode)
|
||||
* - Headers are RC4-encrypted after CMSG_AUTH_SESSION
|
||||
* - Headers are encrypted after CMSG_AUTH_SESSION
|
||||
* - Packet bodies remain unencrypted
|
||||
* - Size field includes opcode bytes (payloadLen = size - 2)
|
||||
*/
|
||||
|
|
@ -56,12 +58,13 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize RC4 encryption for packet headers
|
||||
* Initialize header encryption for packet headers
|
||||
* Must be called after CMSG_AUTH_SESSION before further communication
|
||||
*
|
||||
* @param sessionKey 40-byte session key from auth server
|
||||
* @param build Client build number (determines cipher: <= 8606 = XOR, > 8606 = RC4)
|
||||
*/
|
||||
void initEncryption(const std::vector<uint8_t>& sessionKey);
|
||||
void initEncryption(const std::vector<uint8_t>& sessionKey, uint32_t build = 12340);
|
||||
|
||||
/**
|
||||
* Check if header encryption is enabled
|
||||
|
|
@ -77,10 +80,14 @@ private:
|
|||
socket_t sockfd = INVALID_SOCK;
|
||||
bool connected = false;
|
||||
bool encryptionEnabled = false;
|
||||
bool useVanillaCrypt = false; // true = XOR cipher, false = RC4
|
||||
|
||||
// RC4 ciphers for header encryption/decryption
|
||||
auth::RC4 encryptCipher; // For outgoing headers
|
||||
auth::RC4 decryptCipher; // For incoming headers
|
||||
// WotLK RC4 ciphers for header encryption/decryption
|
||||
auth::RC4 encryptCipher;
|
||||
auth::RC4 decryptCipher;
|
||||
|
||||
// Vanilla/TBC XOR+addition cipher
|
||||
auth::VanillaCrypt vanillaCrypt;
|
||||
|
||||
// Receive buffer
|
||||
std::vector<uint8_t> receiveBuffer;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,11 @@ public:
|
|||
*/
|
||||
void clearCache();
|
||||
|
||||
/**
|
||||
* Clear only DBC cache (forces reload on next loadDBC call)
|
||||
*/
|
||||
void clearDBCCache();
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
std::string dataPath;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue