Dave Zitney
Dave Zitney

Reputation: 17

ARC4 Decryption succeeding on SMSG_AUTH_RESPONSE but failing on SMSG_CHAR_ENUM. What am I missing?

I am working on scripting a player bot for my private AC server in python. I have worked my way through SRP6 authentication through the acore auth server and through the first couple of world server packets.

When I receive the SMSG_AUTH_RESPONSE packet I am correctly decrypting and receiving 0x1EE as the opcode. However, when I try to decrypt the header for SMSG_CHAR_ENUM it fails. I can see the username in the packet so I know that the packet is correct.

I have tried pushing extra bytes through the decryptor (up to 2024) to see if it was a sync issue but no luck. I have also tried re-initializing the ARC4 cipher with the same session key between decryptions and no luck. After looking through the documentation in the azerothcore repo I am unable to come to a conclusion for why this is failing.

Here is the function for reading the world packets and decrypting the header.

def data_received(self, data):
        self.remaining = -1
        if len(data) < self.HEADER_SIZE_INCOMING:
            self.logger.debug(f"Received partial header: {data}")
                # Not enough data for a header
            return
                
        header = data[:self.HEADER_SIZE_INCOMING]
        data = data[self.HEADER_SIZE_INCOMING:]

        if self.arc4_decryptor:
            header = self.arc4_decryptor.decrypt(header)

        self.remaining = int.from_bytes(header[:2], "big")
        size = self.remaining
        data_size = size - self.OPCODE_SIZE_INCOMING
        # Check if we have enough data for the remaining packet
        if self.remaining > 0 and len(data) >= data_size:
            opcode = int.from_bytes(header[2:], "little")
            if opcode in gameopcodes.values():
                self.logger.debug(f"Received {opcode_names[opcode]}")
                return data
        elif self.remaining != 0:
            self.logger.debug(f"couldn't decrypt: {header}")

Here is the ARC4 implementation:

# https://github.com/timelostprototype/wow-client/blob/92e45a4eeb9008b225d55568322056ac7908a275/src/common/crypto/arc4.ts
from Crypto.Cipher import ARC4
from Crypto.Hash import HMAC, SHA1


class Arc4:
    ENCRYPTION_KEY = bytes.fromhex("C2B3723CC6AED9B5343C53EE2F4367CE")
    DECRYPTION_KEY = bytes.fromhex("CC98AE04E897EACA12DDC09342915357")

    def __init__(self, key):
        # Assuming key is a bytes-like object
        self.key = key
        self.enc_key = self.hash_key(self.ENCRYPTION_KEY, key)
        self.dec_key = self.hash_key(self.DECRYPTION_KEY, key)

        self.enc_cipher = ARC4.new(self.enc_key)
        self.dec_cipher = ARC4.new(self.dec_key)

        # Arc4-drop1024 synchronization
        sync_data = bytes([0] * 1024)
        self.enc_cipher.encrypt(sync_data)
        self.dec_cipher.encrypt(sync_data)

    def encrypt(self, data):
        return self.enc_cipher.encrypt(data)

    def decrypt(self, data):
        return self.dec_cipher.decrypt(data)

    @staticmethod
    def hash_key(key, data_key):
        return HMAC.new(key, data_key, SHA1).digest()

I am expecting this function to return the packet data when it matches the opcode for SMSG_CHAR_ENUM (0x03B)

Upvotes: 0

Views: 125

Answers (1)

Dave Zitney
Dave Zitney

Reputation: 17

The issue ended up being a few different things.

  1. The information I was using from WoWdev.wiki was not correct for the version of the game AC is running. This ended up just being a red herring and the real issue was number 2.
  2. I did not realize that multiple server messages could be packed into one packet.

The solution ended up being to re-write the function to decrypt the header, check the length of the message, consume those bytes, and repeat until the entire packet is consumed.

Upvotes: 0

Related Questions