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!

ChatServer Protocol Research

Joined
Aug 6, 2005
Messages
552
Reaction score
298
Hi!
I'm currently researching the protocol between the ChatServer and the game client. I captured my packets in 2012 for season 6 episode 3 on GMO.
First of all, has this been done before? Are there any informations available already? I used the search alot and could not find anything related.

Actually, I've almost finished my ChatServer, just one vital part is unfinished: How is the client authenticating?
I can tell you what I know already about this mechanism:
1. Game client sends to the server that it wants to open a chat (C1 0D CA [Friend Name])
2. Game server answers this with a packet like this:

Code:
         |---------------------IP-------------------| |rid| |--ticket-| ix |----------NAME-------------| Success
C3 24 CA 32 31 31 2E 34 33 2E 31 35 36 2E 31 39 35 00 5B 00 71 00 A8 07 01 4B 4B 4B 4B 4B 4B 4B 00 00 00 01

ix: player index; 0 is the creator, 1 seems to be sent to the friend (or other way around).
rid: room id
ticket: a random number which is probably sent to the chat server later for authentication?
Success: 0: no success; 1: success; 2: chat server unavailable

3. Client connects to the chat server (ip which was sent in the packet, port 55980).
4. Client sends a authentication packet to the chat server (encrypted; all data coming from the client is encrypted, like Client -> GS communication)
The decrypted packets looks like this:

Code:
            |rid| | prob. ticket+ix (encrypted?) |
C1 10 00 00 5B 00 C5 FD 0A 33 C1 CC 66 67 21 F3 32

The last part has to contain the ticket and the player index, otherwise the chat server would not be able to tell which player actually connected.
I think it's encrypted by simple modulus, because it's 11 bytes long - the length of an encrypted block. Decrypted it would be max 8 bytes, enough to hold ticket and player index.

Do you have any hints, or am I on the right track?

The other packets are a non-brainer. I found the following packet types:

S -> C (all packets not encrypted):
c1 0f 01 ... : Player joined the room
c2 n n 02 ... : List of players currently in the room
c1 n 04 ix [Message length] [Message encrypted by XOR3-key] : Message of a player

C -> S (XOR32-encrypted):
c1 10 00 00 ... : Authentification, see above
c1 n 04 ix [Message length] [Message] : Message of a player
c1 03 05 : Keep connection alive packets, sent every few seconds. I guess you could implement something to disconnect a player if it did not send it for a specific period of time.

Do you know any other packets, or is this complete? I could imagine there is one with packet type 0x03, which I didn't find yet.
 
Last edited:
Joined
Aug 6, 2005
Messages
552
Reaction score
298
Thanks ashlay, but I think I found the authentication mechanism without it ;)

It seems like, I have decrypted the packets of my example from client to chat server with a wrong xor32 key back then. I remember in the last months of Season 6, Webzen changed them almost after every maintenance.

Now I tried it again with the most current one I have (AB 11 ...):

Encrypted packet: c1 10 00 fe bd 9e 96 c8 91 6a 51 06 aa 35 8c 83
Decrypted packet: C1 10 00 00 5B 00 CD FD 93 C8 FA 9B CA F8 98 FC

It's now pretty obvious from the last byte, that again the funny XOR-3-"Encryption" (FC CF AB) was used :)
Encrypted: CD FD 93 C8 FA 9B CA F8 98 FC
Decrypted: 31 32 38 34 35 30 36 37 33 00

So, what are my eyes seeing? The decrypted bytes contain a string of a number, here "128450673" which is 07A80071 in hex.
Do you remember, the ticket which was sent before to the client was 71 00 A8 07 ? :w00t:

I hope someone else can use this information, too ;)
 
Last edited:
Joined
Oct 29, 2007
Messages
1,289
Reaction score
1,308
ChatServer ProtocolCore Pseudo-Code (IDA 6.8) thanks to Ashlay (using PacketTwister yet T_T because is from: Ex702 Chs):

Code:
//----- (004294C0) --------------------------------------------------------int __cdecl ProtocolCore(unsigned __int8 a1, char *src, int a3, int a4){  int v4; // edi@1  char v5; // al@1  char v6; // dl@6  __int16 v8; // [sp+Ch] [bp-1034h]@1  char dst[4]; // [sp+830h] [bp-810h]@1  int v10; // [sp+103Ch] [bp-4h]@1  CStreamPacketEngine_Server::CStreamPacketEngine_Server((int)&v8);  LOWORD(v4) = 0;  v10 = 0;  v8 = 0;  memset(dst, 0, 0x800u);  v5 = *src;  if ( *src == -63 || v5 == -61 )  {    v6 = src[2];    LOWORD(v4) = (unsigned __int8)src[1];    *(_WORD *)dst = *(_WORD *)src;    dst[2] = v6;  }  else if ( v5 == -62 || v5 == -60 )  {    v4 = (unsigned __int8)src[2] | ((unsigned __int8)src[1] << 8);    *(_DWORD *)dst = *(_DWORD *)src;  }  if ( !CStreamPacketEngine_Server::AddData((CStreamPacketEngine_Server *)&v8, src, v4) )  {    CLogProc::Add((CLogProc *)&cLog, "error-L1 : CStreamPacketEngine Adding Error head:%x %d ", a1, a4);    CLogProc::Add(      (CLogProc *)&cLog,      "error-L1 : %x%x%x%x%x",      (unsigned __int8)*src,      (unsigned __int8)src[1],      (unsigned __int8)src[2],      (unsigned __int8)src[3],      (unsigned __int8)src[4]);LABEL_9:    v10 = -1;    CStreamPacketEngine_Server::~CStreamPacketEngine_Server((CStreamPacketEngine_Server *)&v8);    return 0;  }  if ( CStreamPacketEngine_Server::ExtractPacket((CStreamPacketEngine_Server *)&v8, dst) )  {    CLogProc::Add((CLogProc *)&cLog, "error-L1 : CStreamPacketEngine ExtractPacket Error : head:%x %d", a1, a4);    CLogProc::Add(      (CLogProc *)&cLog,      "error-L1 : %x%x%x%x%x",      (unsigned __int8)*src,      (unsigned __int8)src[1],      (unsigned __int8)src[2],      (unsigned __int8)src[3],      (unsigned __int8)src[4]);    goto LABEL_9;  }  switch ( a1 )  {    case 0u:      CSRoomJoinUser((struct PMSG_ROOMLOGIN *)dst, a4);      break;    case 4u:      CSRoomChatMsg((struct PMSG_ROOM_CHATMSG *)dst, a4);      break;    case 5u:      CSLiveCheck((struct PBMSG_HEAD *)dst, a4);      break;    default:      CLogProc::Add((CLogProc *)&cLog, "error-L1 : Unkonw protocol (%d) index : (%d)", a1, a4);      break;    case 1u:    case 2u:      break;  }  v10 = -1;  CStreamPacketEngine_Server::~CStreamPacketEngine_Server((CStreamPacketEngine_Server *)&v8);  return 1;}// 493450: using guessed type void CLogProc::Add(CLogProc *__hidden this, char *, ...);

PS: Is possible re-make ChatServer Protocol using this for old versions (like: Season 4) ? I'm thinking that Season 4.5/4.6 working with: 1.1.9.0 ChatServer
 
Junior Spellweaver
Joined
Mar 5, 2009
Messages
171
Reaction score
71
ChatServer ProtocolCore Pseudo-Code (IDA 6.8) thanks to Ashlay (using PacketTwister yet T_T because is from: Ex702 Chs):

Code:
//----- (004294C0) --------------------------------------------------------int __cdecl ProtocolCore(unsigned __int8 a1, char *src, int a3, int a4){  int v4; // edi@1  char v5; // al@1  char v6; // dl@6  __int16 v8; // [sp+Ch] [bp-1034h]@1  char dst[4]; // [sp+830h] [bp-810h]@1  int v10; // [sp+103Ch] [bp-4h]@1  CStreamPacketEngine_Server::CStreamPacketEngine_Server((int)&v8);  LOWORD(v4) = 0;  v10 = 0;  v8 = 0;  memset(dst, 0, 0x800u);  v5 = *src;  if ( *src == -63 || v5 == -61 )  {    v6 = src[2];    LOWORD(v4) = (unsigned __int8)src[1];    *(_WORD *)dst = *(_WORD *)src;    dst[2] = v6;  }  else if ( v5 == -62 || v5 == -60 )  {    v4 = (unsigned __int8)src[2] | ((unsigned __int8)src[1] << 8);    *(_DWORD *)dst = *(_DWORD *)src;  }  if ( !CStreamPacketEngine_Server::AddData((CStreamPacketEngine_Server *)&v8, src, v4) )  {    CLogProc::Add((CLogProc *)&cLog, "error-L1 : CStreamPacketEngine Adding Error head:%x %d ", a1, a4);    CLogProc::Add(      (CLogProc *)&cLog,      "error-L1 : %x%x%x%x%x",      (unsigned __int8)*src,      (unsigned __int8)src[1],      (unsigned __int8)src[2],      (unsigned __int8)src[3],      (unsigned __int8)src[4]);LABEL_9:    v10 = -1;    CStreamPacketEngine_Server::~CStreamPacketEngine_Server((CStreamPacketEngine_Server *)&v8);    return 0;  }  if ( CStreamPacketEngine_Server::ExtractPacket((CStreamPacketEngine_Server *)&v8, dst) )  {    CLogProc::Add((CLogProc *)&cLog, "error-L1 : CStreamPacketEngine ExtractPacket Error : head:%x %d", a1, a4);    CLogProc::Add(      (CLogProc *)&cLog,      "error-L1 : %x%x%x%x%x",      (unsigned __int8)*src,      (unsigned __int8)src[1],      (unsigned __int8)src[2],      (unsigned __int8)src[3],      (unsigned __int8)src[4]);    goto LABEL_9;  }  switch ( a1 )  {    case 0u:      CSRoomJoinUser((struct PMSG_ROOMLOGIN *)dst, a4);      break;    case 4u:      CSRoomChatMsg((struct PMSG_ROOM_CHATMSG *)dst, a4);      break;    case 5u:      CSLiveCheck((struct PBMSG_HEAD *)dst, a4);      break;    default:      CLogProc::Add((CLogProc *)&cLog, "error-L1 : Unkonw protocol (%d) index : (%d)", a1, a4);      break;    case 1u:    case 2u:      break;  }  v10 = -1;  CStreamPacketEngine_Server::~CStreamPacketEngine_Server((CStreamPacketEngine_Server *)&v8);  return 1;}// 493450: using guessed type void CLogProc::Add(CLogProc *__hidden this, char *, ...);

PS: Is possible re-make ChatServer Protocol using this for old versions (like: Season 4) ? I'm thinking that Season 4.5/4.6 working with: 1.1.9.0 ChatServer

U need protocol functions only, dont need to decompile PacketTwister.
 
Joined
Aug 6, 2005
Messages
552
Reaction score
298
what is the problem to use ida to retrieve the packets from chatserver? lol
Well, I don't know why you 'lol', but ofc there are some "problems":
First, IDA is not cheap (and I don't like warez). Second, I have no experience using it. Something simple like a chat server is not hard to rewrite, and the packets don't seem to be complicated.
 
Joined
Nov 4, 2012
Messages
928
Reaction score
545
Well, I don't know why you 'lol', but ofc there are some "problems":
First, IDA is not cheap (and I don't like warez). Second, I have no experience using it. Something simple like a chat server is not hard to rewrite, and the packets don't seem to be complicated.

The problem is not packets itself bro, but headers for it.
Also as far, chatserver do not changed packet headers or structs on new versions.

EDIT: Have fun with packets,
 
Last edited:
Joined
Aug 6, 2005
Messages
552
Reaction score
298
@SmileYzn: Thanks, I looked into it, but it doesn't contain additional information for me ;) I already have all packets which the client sends, and it seems like it doesn't use structs to send packets back to the client. The code is a big mess, so I won't even try to understand this.
 
Joined
Nov 4, 2012
Messages
928
Reaction score
545
SmileYzn: Thanks, I looked into it, but it doesn't contain additional information for me ;) I already have all packets which the client sends, and it seems like it doesn't use structs to send packets back to the client. The code is a big mess, so I won't even try to understand this.

what you need? Since chatserver connects with other server (is join server i guess) to retrieve information about accounts.

Also if you have a packed, you need to look the struct on both sides
 
Joined
Aug 6, 2005
Messages
552
Reaction score
298
Well, I'm just interested in the packets sent between chat server and game client. The other stuff is totally different on my servers.
I guess I already have all required packets sent to client, but would be cool if I would know it for sure.
 
Experienced Elementalist
Joined
Jun 17, 2014
Messages
242
Reaction score
107
any news?
I think packet C -> ChatServer has been encrypted. Maybe? some people tell me it wrong XOR key, but i don't believe it wrong. Because i have change it. :$::$:
Any new ide?
 
Joined
Aug 6, 2005
Messages
552
Reaction score
298
any news?
I think packet C -> ChatServer has been encrypted. Maybe? some people tell me it wrong XOR key, but i don't believe it wrong. Because i have change it. :$::$:
Any new ide?
That's offtopic, but I already wrote what's encrypted and how. If something is wrong, it's most likely the 32-byte XOR-Key.
Anyways... poor post of someone who sells "MU Services" :D
 
Junior Spellweaver
Joined
Sep 12, 2004
Messages
134
Reaction score
14
I have a fork from the SimpleModules and made some fixes, it now produces the same original SimpleModulus.lib.

You can take a look here, if you want...


Just load the keys and use it ;)

[]'s
 
Joined
Aug 6, 2005
Messages
552
Reaction score
298
@navossoc: SimpleModulus is not used by the ChatServer at all - everything uses C1 or C2-packets.

A little update:
After I found everything out about the communication between game client and chat server, I examined what happens in its log when I send stuff the original chat server due port 55906 and 55907. The chat server opens port 55907 and tries to connect to the ExDB-Server due port 55906.

The listener on port 55907 just seems to be some port to check if you can connect to the chat server. It processes no packet and sends nothing back, just prints some messages about "port check" to the log. It closes after connection to ExDB-Server has been established.

The ChatServer tries to connect to the ExDB-Server by Port 55906. When it connected, it sends a packet to the ExDB-Server, like this:
C1 3A 00 02 AC DA 43 68 61 74 53 65 72 76 65 72 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
3rd byte: 00 - packet type
4th byte: 02 - "ChatServer" server type value
5&6th byte: DAAC -> 55980 is the port for the chat client listener
7th bytes and following: "ChatServer" as string

This connection is responsible to receive chat room creations from ExDB-Server, and returns ticket and room ids back. I iterated every C1 packet type from 00 to FF and found reactions in the logs with A0 and A1 packet types.
A0 is used to create a chat room and registering the clients.
Example for A0:
I sent C1 16 A0 41 42 43 44 45 46 47 48 49 4A 50 51 52 53 54 55 56 57 58 59
It contains the first and second client name after A0 (each 10 bytes).
The chat server then returns two packets, one for each client with its index and ticket
Code:
         s  |rid| |-----client name-----------| |---------other client name-| |------???------| |-ticket--| |--------???----------|
C1 2C A0 01 00 00 41 42 43 44 45 46 47 48 49 4A 50 51 52 53 54 55 56 57 58 59 00 00 00 00 CC CC [B]00 00 11 04[/B] CC CC CC CC 00 CC CC CC
C1 2C A0 01 00 00 50 51 52 53 54 55 56 57 58 59 41 42 43 44 45 46 47 48 49 4A 00 00 00 00 CC CC [B]01 00 BB 05[/B] CC CC CC CC 01 CC CC CC
In the log you see two tickets; 6822976 for ABC... and 96141313 for PQR...
For example, to get the ticket of the second packet: 01 00 BB 05 -> 0x05BB0001 ---[hex to decimal]--> 96141313
What seems crazy is, that the ticket contains the index as well, and in the 4th last byte the index is there as well. But as you will see later, the 4th last byte is not useful.
So, the chat server creates just 2 random bytes for the tickets - not very secure ;)

Then there is only one packet type left: A1. It's used to register additional clients to an existing chat room. Not many people know that you can invite additional players to a chat room ;)
Example: C1 10 A1 00 00 00 61 62 63 64 65 66 67 68 69 6F
Index 4 and 5 is the room id, the rest behind is the client name.
The chat server answers this with the same packets as above (ticket 96862210):
C1 2C A0 01 00 00 61 62 63 64 65 66 67 68 69 6F CC CC CC CC CC CC CC CC CC CC 53 54 55 56 CC CC 02 00 C6 05 CC CC CC CC 57 CC CC CC

The main part of my chat server which speaks with the game clients is already fully working, and the part about registering clients is working internally within my other game server architecture.
As a spin-off project I could implement the listener which is responsible for the communication with the ExDB-Server, so it would be a full chat server which could replace the original one of Webzen.
I guess I will release a first version soon :)
 
Last edited:
Junior Spellweaver
Joined
Sep 12, 2004
Messages
134
Reaction score
14
@nevS

Sure, but as far I understand, you said you are cloning everything, right?
So.........

My ChatServer almost became useless after I made the whisper messages "cross server".
Anyway, use it if you want.

[]'s
 
Last edited:
Back
Top