Implementation Guide for the World of Warcraft client integrity check

2024-04-10

Tags: Rust World of Warcraft WoW Cryptography

When an original World of Warcraft client (1.2 through to 3.3.5) authenticates with the server the client will send a hash of some local game files. This post details how the client calculates the hash for World of Warcraft 1.12 and provides replication values in order to test your own implementation.

Background

The server sends a checksum salt in CMD_AUTH_LOGON_CHALLENGE_Server in the field just after the SRP6 salt. The client sends the final hash in CMD_AUTH_LOGON_PROOF_Client in the field just after the client proof.

The client does not require the server to make this calculation like it does for the SRP6 algorithm and malicious clients can just fake the proof, so there’s no real security benefits to implementing this check.

The integrity check is different for login and reconnection. The integrity check is not platform specific, but the specific files and contents of those files are platform and version specific.

This guide is specifically for 1.12, although it will probably work on other versions as well.

Implementation details for login

The implementation is a HMAC-SHA1 of game files together with the server provided checksum salt, and then a SHA1 of the resulting HMAC along with the client public key. The implementation is not different for Mac/Windows, it only differs in the files that are checked.

Generic

This describes a function that can be used for both Windows and Mac without any modifications. The algorithm is the same as the platform specific versions, but all the checked files are just concatenated into a single slice to make the function generic.

Calculating the integrity hash in pseudo code would look like:

function integrity_check_generic
    argument all_files: array of an arbitrary amount of bytes
    argument checksum_salt: array of 16 bytes
    argument client_public_key: array of 32 bytes
    returns array of 20 bytes

    checksum_hmac = HmacSha1(checksum_salt)
    checksum_hmac.update(all_files)

    checksum = checksum_hmac.finalize()

    return SHA1(client_public_key + checksum)

Implementations

Verification values

all_fileschecksum_saltclient_public_keyexpected
Little endian HexLittle Endian HexLittle Endian HexLittle Endian Hex

Windows

The windows integrity calculation uses the generic version, but it specifically enumerates the files required for Windows.

Calculating the integrity hash in pseudo code would look like:

function integrity_check_windows
    argument wow_exe: array of an arbitrary amount of bytes
    argument fmod_dll: array of an arbitrary amount of bytes
    argument ijl15_dll: array of an arbitrary amount of bytes
    argument dbghelp_dll: array of an arbitrary amount of bytes
    argument unicows_dll: array of an arbitrary amount of bytes
    argument checksum_salt: array of 16 bytes
    argument client_public_key: array of 32 bytes
    returns array of 20 bytes

    all_files = wow_exe + fmod_dll + ijl15_dll + dbghelp_dll + unicows_dll

    return integrity_check_generic(all_files, checksum_salt, client_public_key)

Verification values

wow_exefmod_dllijl15_dlldbghelp_dllunicows_dllchecksum_saltclient_public_keyexpected
Little endian HexLittle endian HexLittle endian HexLittle endian HexLittle Endian HexLittle Endian HexLittle Endian Hex

Mac

The windows integrity calculation uses the generic version, but it specifically enumerates the files required for Windows.

Calculating the integrity hash in pseudo code would look like:

function integrity_check_windows
    argument world_of_warcraft: array of an arbitrary amount of bytes
    argument info_plist: array of an arbitrary amount of bytes
    argument objects_xib: array of an arbitrary amount of bytes
    argument wow_icns: array of an arbitrary amount of bytes
    argument pkg_info: array of an arbitrary amount of bytes
    argument checksum_salt: array of 16 bytes
    argument client_public_key: array of 32 bytes
    returns array of 20 bytes

    all_files = world_of_warcraft + info_plist + objects_xib + ow_icns + pkg_info

    return integrity_check_generic(all_files, checksum_salt, client_public_key)

Verification values

world_of_warcraftinfo_plistobjects_xibwow_icnspkg_infochecksum_saltclient_public_keyexpected
Little endian HexLittle endian HexLittle endian HexLittle endian HexLittle Endian HexLittle Endian HexLittle Endian Hex

Implementation details for reconnect

The reconnection integrity check does away with any local files and simply gets the SHA1 of the provided salt along with a zero buffer. This is likely to save on resources since it would be very unlikely for the files to change in between login and reconnecting.

Calculating the reconnect integrity hash in pseudo code would look like:

function integrity_check_windows
    argument checksum_salt: array of an arbitrary amount of bytes

    # Array of 16 bytes, filled with zero
    all_zero = [0; 16]

    return SHA1(checksum_salt + all_zero)

Verification values

checksum_saltexpected
Little endian HexLittle endian Hex

Implementations