uint32 flags;
uint32 pktCounter;
ushort pktID;
ushort checksum;
uint32 datalen;
ushort pktID2;
uchar nullseperator;
uchar[datalen - 7] data;
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))
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);
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;
Figured some stuff out. The packet format is:
Example packet dump:Code:uint32 flags; uint32 pktCounter; ushort pktID; ushort checksum; uint32 datalen; ushort pktID2; uchar nullseperator; unsigned char[datalen - 7] data;
If the packet is compressed: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))
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):
Some information regarding the flags (partial):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);
Full protocol dump from the Gunz2 NA client: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;
You must be registered to see links
Damn dude, nice work. I'm poop when it comes to reversing, so props to you.
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));
}
Thank you. Glad to see that people are still interested in GunZ development.
Also, Phail's checksum generation algorithm is off. It should be
header << 29 >> 29 returns the packet type (1 for normal packets).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)); }
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;
}
Still got far more work done than you did. Also, don't you ever get tired of shitposting?itt cosmos still can't even finish a single project let alone reverse correctly.