Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Gunz 2 Packet Structure

Newbie Spellweaver
Joined
Apr 4, 2014
Messages
23
Reaction score
12
Figured some stuff out. The packet format is:
Code:
uint32 flags;
uint32 pktCounter;
ushort pktID;
ushort checksum;
uint32 datalen;
ushort pktID2;
uchar nullseperator;
uchar[datalen - 7] data;
Example packet dump:
Code:
29 03 00 00 6D 64 9A 02 26 0C A6 08 0D 00 00 00 26 0C 00 11 04 00 00 00 00
29 03 00 00 -> Flags
6D 64 9A 02 -> Counter
26 0C -> ID (C26, 3110)
A6 08 -> Checksum
0D 00 00 00 -> Data size
26 0C -> ID (again.)
00 -> (start data section)
11 04 00 00 00 00 -> Data (CPT_USHORT, 2 bytes (11 04, 0x411) -> Empty blob, 2 bytes type size (00 00) , 2 bytes blob count (00 00), 0 bytes data (invalid login))
If the packet is compressed:
dataSize appears to be equal to decompressedStreamSize + hdrSize so it is the full size of the packet after it has been decompressed. The data, as well as the 7 byte header that is prefixed to it is compressed with a classic zlib inflate (15 windowbits).
Decompression (should be done after decryption, requires Ionic.Zlib):
Code:
                    var fullSize = BitConverter.ToUInt32(buffer, 12);
                    byte[] decompressedBuffer = new byte[fullSize - 12];
                    byte[] cpyArray = new byte[size - 16];
                    Buffer.BlockCopy(buffer, 16, cpyArray, 0, buffer.Length - 16);
                    Console.WriteLine("This packet is compressed. Length: {0}", fullSize);
                    using (ZlibStream zlib = new ZlibStream(new MemoryStream(cpyArray), CompressionMode.Decompress))
                    {
                        zlib.Read(decompressedBuffer, 0, (int)fullSize - 12);
                    }
                    Array.Resize(ref buffer, (int)fullSize);
                    Buffer.BlockCopy(decompressedBuffer, 0, buffer, 12, (int)fullSize - 12);
Some information regarding the flags (partial):
Code:
                this.size = (uint)(0x7FFFFF & (temp >> 5));
                this.normal = (byte)((temp >> 0) & 1) == 1;
                this.ping = (byte)((temp >> 1) & 1) == 1; //used for keepalive. Normal is set to false for keepalive packets.
                this.unkFlag3 = (byte)((temp >> 2) & 1) == 1;
                this.encrypted = (byte)((temp >> 3) & 1) == 1;
                this.compressed = (byte)((temp >> 4) & 1) == 1;
Full protocol dump from the Gunz2 NA client:
 
Last edited:
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
Figured some stuff out. The packet format is:
Code:
uint32 flags;
uint32 pktCounter;
ushort pktID;
ushort checksum;
uint32 datalen;
ushort pktID2;
uchar nullseperator;
unsigned char[datalen - 7] data;
Example packet dump:
Code:
29 03 00 00 6D 64 9A 02 26 0C A6 08 0D 00 00 00 26 0C 00 11 04 00 00 00 00
29 03 00 00 -> Flags
6D 64 9A 02 -> Counter
26 0C -> ID (C26, 3110)
A6 08 -> Checksum
0D 00 00 00 -> Data size
26 0C -> ID (again.)
00 -> (start data section)
11 04 00 00 00 00 -> Data (CPT_USHORT, 2 bytes (11 04, 0x411) -> Empty blob, 2 bytes type size (00 00) , 2 bytes blob count (00 00), 0 bytes data (invalid login))
If the packet is compressed:
dataSize appears to be equal to decompressedStreamSize + hdrSize so it is the full size of the packet after it has been decompressed. The data, as well as the 7 byte header that is prefixed to it is compressed with a classic zlib inflate (15 windowbits).
Decompression (should be done after decryption, requires Ionic.Zlib):
Code:
                    var fullSize = BitConverter.ToUInt32(buffer, 12);
                    byte[] decompressedBuffer = new byte[fullSize - 12];
                    byte[] cpyArray = new byte[size - 16];
                    Buffer.BlockCopy(buffer, 16, cpyArray, 0, buffer.Length - 16);
                    Console.WriteLine("This packet is compressed. Length: {0}", fullSize);
                    using (ZlibStream zlib = new ZlibStream(new MemoryStream(cpyArray), CompressionMode.Decompress))
                    {
                        zlib.Read(decompressedBuffer, 0, (int)fullSize - 12);
                    }
                    Array.Resize(ref buffer, (int)fullSize);
                    Buffer.BlockCopy(decompressedBuffer, 0, buffer, 12, (int)fullSize - 12);
Some information regarding the flags (partial):
Code:
                this.size = (uint)(0x7FFFFF & (temp >> 5));
                this.normal = (byte)((temp >> 0) & 1) == 1;
                this.ping = (byte)((temp >> 1) & 1) == 1; //used for keepalive. Normal is set to false for keepalive packets.
                this.unkFlag3 = (byte)((temp >> 2) & 1) == 1;
                this.encrypted = (byte)((temp >> 3) & 1) == 1;
                this.compressed = (byte)((temp >> 4) & 1) == 1;
Full protocol dump from the Gunz2 NA client:

Damn dude, nice work. I'm poop when it comes to reversing, so props to you.
 
Newbie Spellweaver
Joined
Apr 4, 2014
Messages
23
Reaction score
12
Damn dude, nice work. I'm poop when it comes to reversing, so props to you.

Thank you. Glad to see that people are still interested in GunZ development.

Also, Phail's checksum generation algorithm is off. It should be
Code:
        public UInt16 CalculateChecksum(byte[] buf, int length)
        {
            uint value = 0;
            var header = BitConverter.ToUInt32(buf, 0);

            for (var index = 12; index < length; ++index)
                value += (uint)(buf[index]);
           
            uint result = (uint)(value - (length + (header << 29 >> 29)));
            return (ushort)(result + (result >> 16));
        }
header << 29 >> 29 returns the packet type (1 for normal packets).
 
Joined
Sep 1, 2011
Messages
453
Reaction score
191
Thank you. Glad to see that people are still interested in GunZ development.

Also, Phail's checksum generation algorithm is off. It should be
Code:
        public UInt16 CalculateChecksum(byte[] buf, int length)
        {
            uint value = 0;
            var header = BitConverter.ToUInt32(buf, 0);

            for (var index = 12; index < length; ++index)
                value += (uint)(buf[index]);
           
            uint result = (uint)(value - (length + (header << 29 >> 29)));
            return (ushort)(result + (result >> 16));
        }
header << 29 >> 29 returns the packet type (1 for normal packets).

Nice to see Cosmos is back to Gunz Development.
 
Newbie Spellweaver
Joined
Apr 4, 2014
Messages
23
Reaction score
12
Flag encoding:
Code:
        public static UInt32 SetFlags(byte[] buf, bool encrypted = true, bool ping = false, bool compressed = false)
        {
            UInt32 retval = 0;
            retval ^= (retval ^ 32 * (uint)buf.Length & 0xFFFFFE0);
            if (buf.Length <= 2048)
            {
                retval &= 0xFFFFFFEF;
            }
            if (ping)
            {
                retval ^= (retval << 29 >> 29) ^ (2) & 7; //2 = Packet type
                Buffer.BlockCopy(BitConverter.GetBytes(retval), 0, buf, 0, 4);
                return retval;
            }
            if (encrypted) retval = retval & 0xFFFFFFF9 | 9;
            else retval = retval & 0xFFFFFFF1 | 1;
            if (compressed) retval = retval & 0xF0000017 | 8 * (4 * (((uint)buf.Length) & 0x7FFFFF) | (retval >> 3) & 1 | 2);
            Buffer.BlockCopy(BitConverter.GetBytes(retval), 0, buf, 0, 4);
            return retval;
        }
 
Newbie Spellweaver
Joined
Mar 26, 2014
Messages
77
Reaction score
1

I opened the package, but do not pre-package again

How can I packing MRF files?
 
Back
Top