Auth: include CRC in legacy proof; extend Turtle integrity set

This commit is contained in:
Kelsi 2026-02-13 01:41:59 -08:00
parent 5435796a98
commit 492703be36
5 changed files with 48 additions and 13 deletions

View file

@ -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<uint8_t>& A,
const std::vector<uint8_t>& M1);
static network::Packet buildLegacy(const std::vector<uint8_t>& A,
const std::vector<uint8_t>& M1,
const std::array<uint8_t, 20>* crcHash);
static network::Packet build(const std::vector<uint8_t>& A,
const std::vector<uint8_t>& M1,
uint8_t securityFlags,

View file

@ -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);

View file

@ -167,6 +167,12 @@ network::Packet LogonProofPacket::build(const std::vector<uint8_t>& A,
network::Packet LogonProofPacket::buildLegacy(const std::vector<uint8_t>& A,
const std::vector<uint8_t>& M1) {
return buildLegacy(A, M1, nullptr);
}
network::Packet LogonProofPacket::buildLegacy(const std::vector<uint8_t>& A,
const std::vector<uint8_t>& M1,
const std::array<uint8_t, 20>* 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<uint8_t>& A,
network::Packet packet(static_cast<uint16_t>(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;
}

View file

@ -36,19 +36,35 @@ bool computeIntegrityHashWin32WithExe(const std::array<uint8_t, 16>& 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<std::string> 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<uint8_t> 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<uint8_t> bytes;
std::string path = miscDir;
if (!path.empty() && path.back() != '/') path += '/';

View file

@ -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 {