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.
checksum_salt
is sent by the server inCMD_AUTH_LOGON_CHALLENGE_Server
in the field just after the SRP6 salt.all_files
is all platform specific files concatenated together. See the platform specific versions for which specific files.client_public_key
is the client public key provided by the client.+
is array concatenation.
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_files | checksum_salt | client_public_key | expected |
---|---|---|---|
Big endian Hex | Big Endian Hex | Big Endian Hex | Big Endian Hex |
Windows
The windows integrity calculation uses the generic version, but it specifically enumerates the files required for Windows.
wow_exe
, the bytes of theWoW.exe
file.fmod_dll
, the bytes of thefmod.dll
file.ijl15_dll
, the bytes of theijl15.dll
file.dbghelp_dll
, the bytes of thedbghelp.dll
file.unicows_dll
, the bytes of theunicows.dll
file.checksum_salt
is sent by the server inCMD_AUTH_LOGON_CHALLENGE_Server
in the field just after the SRP6 salt.client_public_key
is the client public key provided by the client.+
is array concatenation.
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_exe | fmod_dll | ijl15_dll | dbghelp_dll | unicows_dll | checksum_salt | client_public_key | expected |
---|---|---|---|---|---|---|---|
Big endian Hex | Big endian Hex | Big endian Hex | Big endian Hex | Big Endian Hex | Big Endian Hex | Big Endian Hex |
Mac
The windows integrity calculation uses the generic version, but it specifically enumerates the files required for Windows.
world_of_warcraft
, the bytes of theMacOS/World of Warcraft
file.info_plist
, the bytes of theInfo.plist
file.objects_xib
, the bytes of theResources/Main.nib/objects.xib
file.wow_icns
, the bytes of theResources/wow.icns
file.pkg_info
, the bytes of thePkgInfo
file.checksum_salt
is sent by the serverCMD_AUTH_LOGON_CHALLENGE_Server
in the field just after the SRP6 salt.client_public_key
is the client public key provided by the client.+
is array concatenation.
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_warcraft | info_plist | objects_xib | wow_icns | pkg_info | checksum_salt | client_public_key | expected |
---|---|---|---|---|---|---|---|
Big endian Hex | Big endian Hex | Big endian Hex | Big endian Hex | Big Endian Hex | Big Endian Hex | Big 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.
checksum_salt
is sent by the serverCMD_AUTH_RECONNECT_CHALLENGE_Server
in the field just after the SRP6 salt.+
is array concatenation.
Calculating the reconnect integrity hash in pseudo code would look like:
function integrity_check_reconnect
argument checksum_salt: array of an arbitrary amount of bytes
# Array of 20 bytes, filled with zero
all_zero = [0; 20]
return SHA1(checksum_salt + all_zero)
Verification values
checksum_salt | expected |
---|---|
Big endian Hex | Big endian Hex |