Both the MapleStory client and server use Network Packets, commonly referred to as Packets, to communicate between each other. The packets they send contain various information and data describing game play information. Some examples of packets sent are player movement information, monster movement information, item placements and much more. It is important to note that a MapleStory client never sends packets directly to another MapleStory client. Instead the server acts as a mediator between all MapleStory clients and selectively chooses and even modifies the information it receives by various clients to be send to clients that require the information. Moreover, much work in the private server development field deals with handling packets correctly.
The packets in MapleStory are in Little Endian Format. Because data types are represented in little endian, the logical ordering of bytes appears reversed in comparison to typical representation of hexadecimal. Given the integer number shown in hexadecimal below:
The corresponding number would be written into the packet:
78 56 34 12
Actions that are handled by packets on MapleStory are defined by an identifier called an Operation Code (opcode for short), which is the first 2 bytes of the packet (a short), though in earlier versions it used to be the first byte (a byte).
Definitions of Packets
MapleStory packets are performed though a TCP socket. The first packet sent / received will always be in plain-text (not encrypted). After the first packet, every subsequent packet is encrypted. MapleStory's base protocol is a minimalist design only containing two bytes of overhead for every packet. These two bytes represent the message type / Operation Code. The remaining data (if any), size and content, depend on the message type. Each packet contains common methods for pulling out basic data types. Here is a list of the data types used in MapleStory packets.
- Byte - 1 byte.
- Short - 2 bytes.
- Int (Integer) - 4 bytes.
- Long - 8 bytes.
- String - 2 bytes (denoting the length of the string) + length of the string in bytes
Additionally, packets may also include a concept known as bit fields. Private Severs will parse a portion of the packet as an unsigned integer, but the actual data consists of bits neighboring eachother. To pull out bits from the number, masking is used. For example, the number 0x68 (0b01101000 in binary) contains 3 bits to be discovered as booleans set to true. To check if the second bit (shown in bold) has been set, the number can be masked to isolate that bit and checked if the value was set. The mask in this example to check the corresponding bit would be 0x40 (0b01000000 in binary). The mask is then bitwise and-ed to the bitfield in question. If the result of the operation is zero, the value was 0, else the value was 1. The concept of bit fields is used primarily with buff stats.
The Packet Encryption in MapleStory is a combination of AES and MapleStory's own encryption.
Packet headers are 4 bytes long, and are generated with the use of the send IV, packet length (without header) and the version of MapleStory. The version is -(version + 1) when sending a packet to the client, otherwise just the plain version.
Packet opcodes are usually 2 bytes long. In older versions (around MapleStory Global v.28 and before) it was only a byte. It was changed to 2 bytes later because there was need for more definitions.
MapleStory's encryption cipher uses the well known algorithm, AES-OFB (Advanced Encryption Standard using Output Feed Buffer).
The cipher's key length is 256 bits, or 32 bytes. The key was static, until version 118, when Nexon changed it. This is the old key (before version .118):
0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00
Note: The 0x preceeding the number means the number is in hexadecimal.
Each Maplestory connection session uses two ciphers, each having a state containing an IV (Initialization Vector) which is passed to AES-OFB.
The Encryption key and IV are used to encrypt packets (data transmitted to and from the server). Each packet's format is like such:
4 bytes: bitshifted integer denoting the length of the packet. 2 bytes: operation code length bytes: data packet.
Of course, the initial packet (first packet sent from the server to the client) is not encrypted, and contains the initialization vector information, and the maple version information.
The first packet also has no header, therefore the first 2 bytes are used to determine the packet size.
Maple version is a short integer (2 bytes in the TCP stream) which tells the cipher by how much to shift the bits by. This provides Nexon/Wizet an easy way to modify packet encryptions when the version changes, and also an easy way to disconnect old clients which may have been hacked to bypass the initial version check.
Shanda is a simple algorithm that seems to be introduced by China. The first public notice of the real name of this encryption comes from the leaked BrazilMS servers:
if ( !v7 && !packet->m_bIsEncryptedByShanda )
The custom/modified Shanda encryption that is being used by some versions of the game, are static and do not use external information. The original Shanda code, that is still being used in China, is activated and configured by a packet that can exceed over 3000 bytes in length.
AES in MapleStory Global, after version 118
Since version 118, the AES key in MapleStory Global changes every version.
A pattern has been found after version 120, as you can see in the following list:
145 F981F775120E71D72BF6F89A9D225556467F019563AB79E180595D30AB298FDB 144 467225B53CF2DC46A3A490B9B61CE7702FFD81C8AE65F2CB570B46B2B7C8185D 143 FA3418B9E0A7F8AB436DA93DE837C3AEC9073D9B3F2EDC0C722DF03092E57327 142 6DCCFD99233E30431347A41FE954ABBCEE9B4FD3276059CFA8F2AB4BCFEB0031 141 5CFF9EAEC0941838C0FC378586DD411BEA73B1BC858C57AC0375C42C378F0203 125 F981F775120E71D72BF6F89A9D225556467F019563AB79E180595D30AB298FDB 124 467225B53CF2DC46A3A490B9B61CE7702FFD81C8AE65F2CB570B46B2B7C8185D 123 FA3418B9E0A7F8AB436DA93DE837C3AEC9073D9B3F2EDC0C722DF03092E57327 122 6DCCFD99233E30431347A41FE954ABBCEE9B4FD3276059CFA8F2AB4BCFEB0031 121 5CFF9EAEC0941838C0FC378586DD411BEA73B1BC858C57AC0375C42C378F0203
The keys used in 118, 119 and 120 have not been seen a second time.
Following is a list of used encryptions:
- Korea use their own, crafted, encryption
- China uses AES, and a library by Shanda Computer (Shanghai) Co., Ltd. provides additional protection, which is controlled by a single packet. This encryption is not reversed yet, but it seems to be a simple addition and/or xor operation (as identical packets, in the same connection session, do not have a different result).
- Taiwan and South East Asia uses AES only
- North America (Global) uses AES + custom Shanda encryption.
- Europe uses AES + custom Shanda encryption
Packets In Coding
Packets are commonly parsed in Private Servers with a MemoryStream for the data income, a BinaryWriter to write data, and a BinaryReader to read the data from the stream. These are convenience classes in Java to assist in parsing and writing information. Any TCP socket connected to the right IP address & port following MapleStory's protocol will due.
Here's a list of methods commonly used in Private Servers to write or read data:
- WriteByte - Writes a byte value to the writer.
- WriteBytes - Writes the bytes value to the writer.
- WriteBoolean - Writes a boolean value to the writer.
- WriteShort - Writes a short value to the writer.
- WriteInt - Writes an integer value to the writer.
- WriteLong - Writes a long value to the writer.
- WriteString - Writes a string value to the writer.
- WriteMapleString - Writes a short with the string's length and the string value to the writer.
- ReadByte - Reads the byte value from the reader.
- ReadBytes (amount) - Reads the amount of bytes from the reader.
- ReadBoolean - Reads the boolean value from the reader.
- ReadShort - Reads the short value from the reader.
- ReadInt - Reads the integer value from the reader.
- ReadLong - Reads the long value from the reader.
- ReadString (length) - Reads the string value from the reader with the length specified.
- ReadMapleString - Reads the string value with the length of short from the reader.