Jump to content

Need help in packet decryption


/dev/not/null

Recommended Posts

After success SMSG_AUTH_RESPONSE client sends packet, after decryption it equals to b'\x00\x047\x00\x00\x00\x18\xb7\xce\xa7/Y\xff\xff\xff\xff',

What is the client packet structure ? First two bytes are opcode, aren't it ? But if so, I can't find this opcode in list. Maybe I'm doing something wrong ?

This is my enc/decrypt code:

    def encrypt(self, data):
        assert len(data) >= HeaderCrypt.ENCRYPT_HEADER_SIZE
        encrypted_header = [0] * HeaderCrypt.ENCRYPT_HEADER_SIZE

        for index in range(HeaderCrypt.ENCRYPT_HEADER_SIZE):
            enc = (data[index] ^ self.crypt_key[self.send_i]) + self.send_j
            enc %= 0x100
            encrypted_header[index] = self.send_j = enc
            self.send_i = (self.send_i + 1) % len(self.crypt_key)

        return bytes(encrypted_header) + data[HeaderCrypt.ENCRYPT_HEADER_SIZE:]

    def decrypt(self, data):
        assert len(data) >= HeaderCrypt.DECRYPT_HEADER_SIZE
        decrypted_header = [0] * HeaderCrypt.DECRYPT_HEADER_SIZE

        for index in range(self.DECRYPT_HEADER_SIZE):
            dec = (data[index] - self.recv_j) ^ self.crypt_key[self.recv_i]
            dec %= 0x100
            decrypted_header[index] = dec
            self.recv_j = data[index]
            self.recv_i = (self.recv_i + 1) % len(self.crypt_key)

        return bytes(decrypted_header) + data[HeaderCrypt.DECRYPT_HEADER_SIZE:]

Encryption works fine (because I can send SMSG_AUTH_RESPONSE and go to Character list loading), but I'm not sure about decryption. So, the question - what is the CMSG_* packets structure and what packet client send under 0x047 opcode after SMSG_AUTH_RESPONSE ?

Link to comment
Share on other sites

  • 2 weeks later...

Well, currently I getting another problem: my HeaderCrypt class decrypts only first packet correctly - next packets decrypts with incorrect opcodes.

My current HeaderCrypt implementation below:

class HeaderCrypt(object):

    ENCRYPT_HEADER_SIZE = 4
    DECRYPT_HEADER_SIZE = 6

    def __init__(self, session_key: bytes):
        self.crypt_key = self._generate_key(session_key)
        self.send_i = 0
        self.send_j = 0
        self.recv_i = 0
        self.recv_j = 0

    def encrypt(self, data: bytes):
        assert len(data) >= self.ENCRYPT_HEADER_SIZE
        encrypted_header = [0] * self.ENCRYPT_HEADER_SIZE

        for index in range(self.ENCRYPT_HEADER_SIZE):
            self.send_i %= len(self.crypt_key)
            enc = (data[index] ^ self.crypt_key[self.send_i]) + self.send_j
            enc %= 0x100
            self.send_i += 1
            encrypted_header[index] = self.send_j = enc

        return bytes(encrypted_header) + data[self.ENCRYPT_HEADER_SIZE:]

    def decrypt(self, data: bytes):
        assert len(data) >= self.DECRYPT_HEADER_SIZE
        decrypted_header = [0] * self.DECRYPT_HEADER_SIZE

        for index in range(self.DECRYPT_HEADER_SIZE):
            self.recv_i %= len(self.crypt_key)
            dec = (data[index] - self.recv_j) ^ self.crypt_key[self.recv_i]
            dec %= 0x100
            self.recv_i += 1
            self.recv_j = data[index]
            decrypted_header[index] = dec

        return bytes(decrypted_header) + data[self.DECRYPT_HEADER_SIZE:]

    def _generate_key(self, session_key):
        seed = b'8\xa7\x83\x15\xf8\x92%0q\x98g\xb1\x8c\x04\xe2\xaa'
        hashed = hmac.new(seed, session_key, sha1)
        return hashed.digest()

After success SMSG_AUTH_RESPONSE my server receives CMSG_CHAR_ENUM and decrypts it successfully. Next packets like CMSG_PING, CMSG_PLAYER_LOGIN etc decrypts incorrect. If I understands it correctly, the send_i, send_j, recv_i, recv_j values stores for the next decrypt operation. I got some experiments with this and some packets decrypting successfully only when I re-initialize HeaderCrypt, but this is wrong behavior, isn't it ?

Can anybody helps me with enc/decryption alg ? I don't asking for code, just explain me what should be implemented or what I have missed ?

Currently only first packet (CMSG_CHAR_ENUM) decrypts successfully. Also I tried to decrypt one packet for 10 times and only after that packet decrypts successfully. Something wrong with my current decryption alg, but I don't know what exactly. 

BTW, does opcode always in 3rd and 4th bytes ?

Link to comment
Share on other sites

Hello, 

Which client is it? If it's vanilla, you can have a look here how to decode. https://github.com/Warkdev/JaNGOSRealm/blob/master/src/main/java/eu/jangos/realm/network/decoder/RealmPacketDecoder.java

Crypt attribute is set for the session. And its implementation is https://github.com/Warkdev/JaNGOSRealm/blob/master/src/main/java/eu/jangos/realm/utils/VanillaCrypt.java

Link to comment
Share on other sites

Well... Currently I created the workaround for solving this problem. I hope this is temporary workaround.

I noticed some packets decrypts correctly only after calling decrypt method several times. So:

        def decrypt(packet: bytes):
            result = packet
            for index in range(20):
          	# I calls this method several time until packet will be decrypted successfully
                enc = session.header_crypt.decrypt(packet)
                try:
               	    # this code checks if packet decrypts successfully
                    opcode = WorldOpCode(int.from_bytes(enc[2:6], 'little')).value
                except ValueError:
                    continue
                else:
                    result = enc
                    break

            return result

        packet = decrypt(packet)

Some packets like CMSG_CHAR_ENUM are always decrypts the first time. Some packets like CMSG_PING decrypts on 9th iteration.

When I entering the world, packets with next opcodes (CMSG_VOICE_SESSION_ENABLECMSG_VOICE_SESSION_ENABLE) always decrypts on 1st iteration.

Another packets like CMSG_OPT_OUT_OF_LOOTCMSG_CHAR_CREATE, CMSG_PLAYER_LOGIN decrypts the first time.

Maybe somebody understands why decryption works so weird in my case ?

I checked - session_key generates correctly, hash for checking if session_key same on client and server also generates correctly. And I'm sure decrypting algorithm also correct. Maybe exists some differences between Python and C++ which I didn't noticed ?

Link to comment
Share on other sites

Not being an expert on handling the packet at this level, I would still advise do not take a risk with this operation:

dec %= 0x100

The modulo op in Python is quite a thing. Use bitwise operation instead:

dec &= 0xFF

Unsure if it helps, but it is the only difference from the referenced Java code I've managed to spot.

Link to comment
Share on other sites

11 hours ago, Olion said:

Not being an expert on handling the packet at this level, I would still advise do not take a risk with this operation:


dec %= 0x100

The modulo op in Python is quite a thing. Use bitwise operation instead:


dec &= 0xFF

Unsure if it helps, but it is the only difference from the referenced Java code I've managed to spot.

Thanks for your answer. I tested, unfortunately this not helps. Btw, %= 0x100 and &= 0xFF returns same result.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use