From 492703be363652c9171b9f93ca4d055d724ba230 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 13 Feb 2026 01:41:59 -0800 Subject: [PATCH] Auth: include CRC in legacy proof; extend Turtle integrity set --- include/auth/auth_packets.hpp | 3 +++ src/auth/auth_handler.cpp | 20 +++++++++++++------- src/auth/auth_packets.cpp | 12 +++++++++++- src/auth/integrity.cpp | 24 ++++++++++++++++++++---- tools/auth_login_probe/main.cpp | 2 +- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/include/auth/auth_packets.hpp b/include/auth/auth_packets.hpp index a6ff0ed0..fda305a7 100644 --- a/include/auth/auth_packets.hpp +++ b/include/auth/auth_packets.hpp @@ -64,6 +64,9 @@ public: // Legacy (protocol < 8): A(32) + M1(20) + crc(20) + number_of_keys(1). No securityFlags byte. static network::Packet buildLegacy(const std::vector& A, const std::vector& M1); + static network::Packet buildLegacy(const std::vector& A, + const std::vector& M1, + const std::array* crcHash); static network::Packet build(const std::vector& A, const std::vector& M1, uint8_t securityFlags, diff --git a/src/auth/auth_handler.cpp b/src/auth/auth_handler.cpp index e9fa8b46..489c0bb5 100644 --- a/src/auth/auth_handler.cpp +++ b/src/auth/auth_handler.cpp @@ -283,14 +283,20 @@ void AuthHandler::sendLogonProof() { } } - auto packet = LogonProofPacket::build(A, M1, securityFlags_, crcHashPtr, pinClientSaltPtr, pinHashPtr); - socket->send(packet); + if (clientInfo.protocolVersion < 8) { + // Legacy proof format: no securityFlags byte on the wire, but CRC/integrity hash still applies. + auto packet = LogonProofPacket::buildLegacy(A, M1, crcHashPtr); + socket->send(packet); + } else { + auto packet = LogonProofPacket::build(A, M1, securityFlags_, crcHashPtr, pinClientSaltPtr, pinHashPtr); + socket->send(packet); - if ((securityFlags_ & 0x04) && clientInfo.protocolVersion >= 8) { - // TrinityCore-style Google Authenticator token: send immediately after proof. - const std::string token = pendingSecurityCode_; - auto tokPkt = AuthenticatorTokenPacket::build(token); - socket->send(tokPkt); + if (securityFlags_ & 0x04) { + // TrinityCore-style Google Authenticator token: send immediately after proof. + const std::string token = pendingSecurityCode_; + auto tokPkt = AuthenticatorTokenPacket::build(token); + socket->send(tokPkt); + } } setState(AuthState::PROOF_SENT); diff --git a/src/auth/auth_packets.cpp b/src/auth/auth_packets.cpp index 924a15b5..a33ea6d2 100644 --- a/src/auth/auth_packets.cpp +++ b/src/auth/auth_packets.cpp @@ -167,6 +167,12 @@ network::Packet LogonProofPacket::build(const std::vector& A, network::Packet LogonProofPacket::buildLegacy(const std::vector& A, const std::vector& M1) { + return buildLegacy(A, M1, nullptr); +} + +network::Packet LogonProofPacket::buildLegacy(const std::vector& A, + const std::vector& M1, + const std::array* crcHash) { if (A.size() != 32) { LOG_ERROR("Invalid A size: ", A.size(), " (expected 32)"); } @@ -177,7 +183,11 @@ network::Packet LogonProofPacket::buildLegacy(const std::vector& A, network::Packet packet(static_cast(AuthOpcode::LOGON_PROOF)); packet.writeBytes(A.data(), A.size()); packet.writeBytes(M1.data(), M1.size()); - for (int i = 0; i < 20; ++i) packet.writeUInt8(0); // CRC hash + if (crcHash) { + packet.writeBytes(crcHash->data(), crcHash->size()); + } else { + for (int i = 0; i < 20; ++i) packet.writeUInt8(0); // CRC hash + } packet.writeUInt8(0); // number of keys return packet; } diff --git a/src/auth/integrity.cpp b/src/auth/integrity.cpp index 8b569c14..cdf7d146 100644 --- a/src/auth/integrity.cpp +++ b/src/auth/integrity.cpp @@ -36,19 +36,35 @@ bool computeIntegrityHashWin32WithExe(const std::array& checksumSal std::string& outError) { // Files expected by 1.12.x Windows clients for the integrity check. // If this needs to vary by build, make it data-driven in expansion.json later. - const char* kFiles[] = { + // + // Turtle WoW ships a custom loader DLL. Some Turtle auth servers appear to validate integrity against + // that distribution rather than a stock 1.12.1 client, so when using Turtle's executable we include + // Turtle-specific DLLs as well. + const bool isTurtleExe = (exeName == "TurtleWoW.exe"); + const char* kFilesBase[] = { nullptr, // exeName "fmod.dll", "ijl15.dll", "dbghelp.dll", "unicows.dll", }; + const char* kFilesTurtleExtra[] = { + "twloader.dll", + "twdiscord.dll", + }; + + std::vector files; + files.reserve(1 + 4 + (isTurtleExe ? (sizeof(kFilesTurtleExtra) / sizeof(kFilesTurtleExtra[0])) : 0)); + for (const char* f : kFilesBase) { + files.push_back(f ? std::string(f) : exeName); + } + if (isTurtleExe) { + for (const char* f : kFilesTurtleExtra) files.push_back(std::string(f)); + } std::vector allFiles; std::string err; - for (size_t idx = 0; idx < (sizeof(kFiles) / sizeof(kFiles[0])); ++idx) { - const char* name = kFiles[idx]; - std::string nameStr = name ? std::string(name) : exeName; + for (const auto& nameStr : files) { std::vector bytes; std::string path = miscDir; if (!path.empty() && path.back() != '/') path += '/'; diff --git a/tools/auth_login_probe/main.cpp b/tools/auth_login_probe/main.cpp index 5e597b0b..e458f6ad 100644 --- a/tools/auth_login_probe/main.cpp +++ b/tools/auth_login_probe/main.cpp @@ -231,7 +231,7 @@ int main(int argc, char** argv) { } if (fmt == ProofFormat::Legacy) { - auto pkt = auth::LogonProofPacket::buildLegacy(A, M1); + auto pkt = auth::LogonProofPacket::buildLegacy(A, M1, crcHashPtr); sock.send(pkt); std::cerr << "Sent LOGON_PROOF legacy (proto=" << (int)info.protocolVersion << ")\n"; } else {