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!

(Mostly) Thorough explanation of packets

Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
Hey. Since everyone's been clamoring lately over how packets work, their involvement with everything, etc. I'll post this since Phail's thread is... lackluster, to say the least. I don't really have any interest in Gunz anymore as anything more than a sandbox for stuff I learn, so do what this info what you will.

If you wanna see the packets in action as an example, see the source to mine or Phail's server emu. I can post the structure though.

The first 10 bytes are the header, which goes as follows.

-Byte 1 -> Version. 0x64 if the packet isn't meant to be encrypted, 0x65 if the packet is meant to be encrypted, 0x0A if the packet is the key exchange packet.
-Byte 2 -> Always null (0x00).
-Bytes 3 and 4 -> The length of the whole packet (unsigned short).
-Bytes 5 and 6 -> The packet checksum (unsigned short).
-Bytes 7 and 8 -> The command size (packet length - 6; unsigned short).
-Bytes 9 and 10 -> The command ID/opcode (unsigned short). The entire protocol is mapped and public. If you can't find it, dump it from Olly.

After the header comes the actual data. In older versions of Gunz, each parameter is preceded by a byte indicating its type, but for newer versions (what everyone deals with now) there's no such byte, the client just knows what to expect, the parameter lists are hard-coded. So in most packets the structure is:

Header -> null byte (0x00) -> data.

Bytes are just one byte. Shorts are 2 bytes, ints are 4 bytes. Some of the possible data structures in Gunz packets are the following:

-Byte -> self explained
-Short -> self explained
-Int -> self explained
-Boolean -> one byte. 0x00 = false, 0x01 = true
-UID -> consists of 8 bytes, 4 for the low ID and 4 for the high ID
-String -> these can be tricky. Some strings don't contain a length indicator and are padded with null bytes to a certain length, others have an unsigned short with the length of the string + 2 (the +2 accounts for the null terminator and I don't know what the extra is for, error on MAIET's part?) before the actual string, then the string is written, followed by a null byte (0x00).
-Structure -> again, these can also be tricky. A structure is a collection of data all rolled into one data item. Think of it like a struct in C/C++. The struct first has an unsigned int indicating the size of all the members combined. This value can be no less than 8, so when calculating it do ((number of members * size of each member) + 8). After this is another unsigned int showing how many bytes each individual member is. Then there's another unsigned integer saying how many members to the struct there are. After these 3 ints, you write the actual members of the struct.


I think I covered everything. Might have missed something though, I don't know, it's late lol. Also, keep in mind that this doesn't cover the format of every single packet, just a good majority of them. I'll let you figure out the others, this should provide a good enough start.
 
Junior Spellweaver
Joined
Jan 3, 2007
Messages
145
Reaction score
27
Actually the blob part is one int for the lenght and just before the lenght is the packet data, the 8 byte thing is because of the MAIET Blob array format, you can check all this in my released source.

Also the packet checksum is only checked when the packet is sent via UDP, when is TCP there's not checksum check.
 
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
Eh. I just posted the way I make the blobs in my emu. I didn't really do any reversing, I just logged packets and looked at them. The way I posted is the way I work with them and the client doesn't seem to complain, lol.
 
Junior Spellweaver
Joined
Jan 3, 2007
Messages
145
Reaction score
27
Eh. I just posted the way I make the blobs in my emu. I didn't really do any reversing, I just logged packets and looked at them. The way I posted is the way I work with them and the client doesn't seem to complain, lol.

Yes, i know, i only have posted that to clarify ;D
 
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
Look at our emulator source codes for encryption and checksumming. I posted enough. If you're a developer that's even close to worth your salt, you'll figure it out.
 
Joined
Oct 8, 2006
Messages
446
Reaction score
27
Look at our emulator source codes for encryption and checksumming. I posted enough. If you're a developer that's even close to worth your salt, you'll figure it out.
I need more ASM to understand that :/ for somethings lik this:

public void writeInt(uint i)
{
if (index + 4 >= length)
resize();

data[index] = (byte)(i);
index++;

data[index] = (byte)(i / 256);
index++;

data[index] = (byte)(i / 65536);
index++;

data[index] = (byte)(i / 16777216);
index++;
}
 
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
I'm not even sure what you're asking or telling me. You posted a weirdly worded sentence and an irrelevant function from my emu.
 
Joined
Sep 10, 2007
Messages
970
Reaction score
815
Code:
struct Packet {
         short wHeader; //0x64 decrypted, 0x65 encrypted
         short wSizeOfPacket;
         short wChecksum;
         short wSizeOfData;
         char  *lpData;
};

Blobs:
Code:
        public void Write(int pSize, int pCount)
        {
            int size = 8 + (pSize * pCount);
            Write(size);
            Write(pSize);
            Write(pCount);
            Increase(pSize*pCount);
        }


Also:
Code:
		unsigned char *BuildKey(char *pPacket)
		{
			byte Xor[] = { 0x57, 0x02,0x5B,0x04,0x34,0x06,0x01,0x08,0x37,0x0A,0x12,0x69,0x41,0x38,0x0F,0x78 };
			byte mBytes[] = { 0x37,0x04,0x5D,0x2E,0x43,0x38,0x49,0x53,0x50,0x05,0x13,0xC9,0x28,0xA4,0x4D,0x05 }; //GameNao!
			unsigned int szTmp = 0,szTmp2 = 0,szTmp3 = 0;
			memcpy(mKey,pPacket+12,4);
			memcpy(mKey+4,pPacket,12);
			memcpy(mKey+16,mBytes,16);
			for(int i=0; i<4; ++i)
			{
				memcpy(&szTmp,Xor+(i*4),4);
				memcpy(&szTmp2,mKey+(i*4),4);
				szTmp3 = szTmp ^ szTmp2;
				memcpy(mKey+(i*4),&szTmp3,4);
			}
			return (unsigned char *)mKey;
		}
		void Encrypt(unsigned char *szBuffer, int iStart, int iLength)
		{
			for(int i=0; i < iLength; ++i)
			{
				unsigned short a = szBuffer[iStart+i];
				a ^=mKey[i%32];
				a <<= 5;
				
				byte b = (byte)(a >> 8);
				b |= (byte)(a & 0xFF);
				b ^= 0xF0;
				szBuffer[iStart + i] = (byte)b;
			}
		}


		void Decrypt(unsigned char *szBuffer, int iStart, int iLength)
		{           
			for (int i = 0; i < iLength; ++i)
			{
				byte a = szBuffer[iStart + i];
				a ^= 0x0F0;
				byte b = (byte)(0x1F & a);
				a >>= 5;
				b <<= 3;
				b = (byte)(a | b);
				szBuffer[iStart+i] = (byte)(b ^ mKey[i % 32]);
			}
		}



		unsigned short Checksum(unsigned char *szBuffer, int iStart, int iLength)
		{
			unsigned int uiSum = (unsigned int)(szBuffer[iStart] + szBuffer[iStart+1] + szBuffer[iStart+2] + szBuffer[iStart+3]),uiSum2=0,uiSum3=0,uiSum4=0;
			for(int i=6; i < iLength; ++i) uiSum2 += szBuffer[i];
			uiSum3 = uiSum2 - uiSum;
			uiSum4 = uiSum3 >> 0x10;
			uiSum3 += uiSum4;
			return (unsigned short)uiSum3;
		}

I just updated that for ijji today. You can hook using the IAT
 
Last edited:
2D > 3D
Loyal Member
Joined
Dec 19, 2008
Messages
2,413
Reaction score
1,193
wow... you are now my hero
 
Joined
Sep 10, 2007
Messages
970
Reaction score
815
-String -> these can be tricky. Some strings don't contain a length indicator and are padded with null bytes to a certain length, others have an unsigned short with the length of the string + 2 (the +2 accounts for the null terminator and I don't know what the extra is for, error on MAIET's part?) before the actual string, then the string is written, followed by a null byte (0x00).
The strings are padded inside of a structure. If it's a raw string you have:
<short>
<char *>
 
2D > 3D
Loyal Member
Joined
Dec 19, 2008
Messages
2,413
Reaction score
1,193
your spacing is very easy to identify Theoretical *winks*

wb
 
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
You should really study your Hungarian notation. *cough*
 
Joined
Sep 10, 2007
Messages
970
Reaction score
815
your spacing is very easy to identify Theoretical *winks*

wb

My spacing actually makes sources easy to ready and understand.

ijji RecvHook -
Code:
int WINAPI far recvHook (SOCKET sSocket, char *lpPacket, int nSize, int nFlags)
{
	sGunzServer = sSocket;
	int nRecieved = recv (sSocket, lpPacket, nSize, nFlags);

	if (nRecieved < 1) return nRecieved;

	Echo ("Recieved Packet: [%X]", lpPacket[0]);
	if (lpPacket[0] == 0xA)
	{
		char *realPacket = (char *)malloc (nRecieved);
		memset (realPacket, 0, nRecieved);
		memcpy (realPacket, lpPacket+10, nRecieved-10);

		CPacket *pPacket = new CPacket ();
		memcpy (&g_szKey,pPacket->BuildKey  (realPacket), 32);
		delete pPacket;

	}
	else if (lpPacket[0] == 0x65)
	{
		unsigned char *realPacket = (unsigned char *)malloc(nRecieved);
		memcpy (realPacket, lpPacket, nRecieved);
		CPacket *lpPacket = new CPacket (g_szKey);
		lpPacket->Decrypt (realPacket, 6, nRecieved - 6);

		//Time of truth
		short uCommandID;
		memcpy (&uCommandID, realPacket+8,2);
		Echo ("Recieved Packet[%X]", uCommandID);
	}
	
	return nRecieved;
}

You should really study your Hungarian notation. *cough*

I don't use full Hungarian. If I did:

m_lpPacketKey;
g_lpKey;
etc.

I know the full Hungarian notation, but I didn't use it back when that original source was made.
 
Last edited:
Experienced Elementalist
Joined
May 30, 2009
Messages
298
Reaction score
8
Code:
memcpy(mKey + (i * 4), &szTmp3, 4);

That's a good spacing in my opinion. :p
 
Experienced Elementalist
Joined
Mar 15, 2009
Messages
219
Reaction score
39
Well I'm not sure what the hell you think sz means, but it sure isn't what you're using it for. sz = null-terminated string.
 
Back
Top