- u32 - 32 bit unsigned int
- u16 - 16 bit unsigned int
- u8 - 8 bit unsigned int
- This gist includes inline hex, and it might not render correctly on mobile phones.
- All offsets mentioned here are inclusive at the left bound and exclusive at the right bound.
- All data blobs are examples of real data.
NGS's packet header differs slightly from pre-NGS:
Pre-NGS | NGS |
---|---|
u32: length | u32: length |
u8: packet_id | u8: flags1 |
u8: packet_subid | u8: packet_id |
u8: flags1 | u16: packet_subid |
u8: flags2? |
Update (11.07.2023): It seems that NGS's packet_subid
is actually u16.
All packets are unencrypted until the client sends a "key exchange" packet (id: 0x11, subid: 0x0b).
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 0C 01 00 00 00 11 0B 00 A2 58 C3 12 7A 27 C0 10
10: 42 2F 7A 12 7E 06 AB 0A EA F3 4B B6 6D EE 39 3B
20: 3C 2B 76 AD B5 B9 72 5E 04 29 FB F2 0F 78 A8 12
30: FC 66 18 0F BB D5 6F 26 70 AE B1 F4 AF 9F 93 3F
40: FB 36 A9 CA 7C 70 4A D4 5E 95 FF 4E 4E D9 B5 99
50: DF E1 2D 9F DD FB EC E1 1B EC 1D C2 A1 0E 5E BC
60: 92 F9 84 2B 84 5F D6 59 A1 3E C2 73 02 C7 3F 7A
70: ED 6F AA 39 90 E9 6D 85 C2 41 25 EE 2E 3D F5 15
80: B4 55 E4 24 56 7F 7D 83 00 00 00 00 00 00 00 00
90: ...
Listing 1: Key exchange packet (before decryption)
Data from 0x08 to 0x88 is an RSA-encrypted blob. After decrypting it, we get this block of data:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: DA B3 48 79 DC BA 24 9A 8D 52 AE 41 EA B1 C1 BC
10: 99 BD C5 81 CE 5E D5 21 24 C1 45 9D 20 26 EE B5
20: 42 FD 29 0D A3 17 4A 5E C9 EF 4B 91 03 05 15 8A
30: 22 69 4C 98 7A EB 6B 8C 67 A2 40 2D C2 3F 80 4C
40: A9 61 3D 56 8B CD 82 7B D0 6B D0 2C CD DC BD 8E
Listing 2: Key exchange packet (after RSA decryption)
This block is split into two parts:
- [0x00 - 0x30] AES256-CBC-encrypted data
- [0x30 - 0x50] AES key for the above part
To decrypt the data part, we take the key and IV, which is [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF]
.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10: 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
20: 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
Listing 3: Key exchange packet (after RSA and AES decryption)
This block consists of:
- [0x00 - 0x20] AES key for future packets
- [0x20 - 0x30] Pkcs7 padding (might not show up, depending on decryption implemenation)
In response to this packet, the server sends an encrypted "key response" packet:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: D2 FF 93 DC 9D 55 64 C8 FD 7E 37 51 2E 0A 24 CF
10: 36 70 9D 74 3C D4 4D 29 61 91 69 8F D8 A1 E5 2E
20: B0 6F 29 A5 1B 6E 92 DA 0E F1 D8 50 06 80 4E FE
30: 59 07 5F 17 DD 88 92 00 FB FC 79 BF 40 38 7B 6D
40: 01 00 FF FF 88 00 00 00 DE 9E A7 FB 46 D9 A3 8F
50: 55 A5 EC 42 CC 04 F0 E8 7C 5F B6 9E 65 AE 04 38
60: 4D 91 8C 96 44 77 91 61 2F E4 36 94 48 45 4F 5D
70: ED E3 34 57 BA 3E A6 DE DF 83 82 F9 92 B3 55 C3
80: 35 9E 48 29 F6 65 92 E7
Listing 4: Server response (before decryption)
The packet format is common for all future packets:
Offset | Description |
---|---|
0x00 - 0x20 | SHA256 of the header |
0x20 - 0x40 | SHA256 of the data |
0x40 - 0x44 | Magic number(?) |
0x44 - 0x48 | Packet length |
0x48 - ... | AES-encrypted data |
The initial IV for the client's and server's data is the same as above, but for the next packet, it is set to the previous packet's last 16 bytes (before decryption / after encryption).
So in this example, the server's next packet IV is set to [0xDF, 0x83, 0x82, 0xF9, 0x92, 0xB3, 0x55, 0xC3, 0x35, 0x9E, 0x48, 0x29, 0xF6, 0x65, 0x92, 0xE7]
.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 38 00 00 00 00 11 0C 00 00 01 02 03 04 05 06 07
10: 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
20: 18 19 1A 1B 1C 1D 1E 1F 10 10 10 10 10 10 10 10
30: 10 10 10 10 10 10 10 10
Listing 5: Server response (after decryption)
Note: Listings 2-5 can be used to check implementations of packet decryption.
Some packets may also look like this (after decryption):
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 28 B5 2F FD 20 38 85 01 00 94 02 38 00 00 00 00
10: 11 0C 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0C
20: 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C
30: 1D 1E 1F 10 01 00 E9 40 19
Listing 6: Example of packed data
If any packet starts with [0x28, 0xB5, 0x2F, 0xFD]
, then this packet is packed using zstd compression. When recompressing, be sure to pass the "pledged source size" to the compressor.
- Create a zero-filled array of length 0x40.
- Append
[0x01, 0x00, 0xFF, 0xFF]
. - Append encrypted data size + 0x48 as u32 little-endian.
- Append encrypted data.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40: 01 00 FF FF 88 00 00 00 DE 9E A7 FB 46 D9 A3 8F
50: 55 A5 EC 42 CC 04 F0 E8 7C 5F B6 9E 65 AE 04 38
60: 4D 91 8C 96 44 77 91 61 2F E4 36 94 48 45 4F 5D
70: ED E3 34 57 BA 3E A6 DE DF 83 82 F9 92 B3 55 C3
80: 35 9E 48 29 F6 65 92 E7
Listing 7: SHA calculation (stage 0)
- Calculate the SHA256 of the array from 0x44 to the end.
- Place the resulting hash at 0x20.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: B0 6F 29 A5 1B 6E 92 DA 0E F1 D8 50 06 80 4E FE
30: 59 07 5F 17 DD 88 92 00 FB FC 79 BF 40 38 7B 6D
40: 01 00 FF FF 88 00 00 00 DE 9E A7 FB 46 D9 A3 8F
50: 55 A5 EC 42 CC 04 F0 E8 7C 5F B6 9E 65 AE 04 38
60: 4D 91 8C 96 44 77 91 61 2F E4 36 94 48 45 4F 5D
70: ED E3 34 57 BA 3E A6 DE DF 83 82 F9 92 B3 55 C3
80: 35 9E 48 29 F6 65 92 E7
Listing 8: SHA calculation (stage 1)
- Calculate the SHA256 of the array from the beginning to 0x48.
- Place the resulting hash at the beginning.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00: D2 FF 93 DC 9D 55 64 C8 FD 7E 37 51 2E 0A 24 CF
10: 36 70 9D 74 3C D4 4D 29 61 91 69 8F D8 A1 E5 2E
20: B0 6F 29 A5 1B 6E 92 DA 0E F1 D8 50 06 80 4E FE
30: 59 07 5F 17 DD 88 92 00 FB FC 79 BF 40 38 7B 6D
40: 01 00 FF FF 88 00 00 00 DE 9E A7 FB 46 D9 A3 8F
50: 55 A5 EC 42 CC 04 F0 E8 7C 5F B6 9E 65 AE 04 38
60: 4D 91 8C 96 44 77 91 61 2F E4 36 94 48 45 4F 5D
70: ED E3 34 57 BA 3E A6 DE DF 83 82 F9 92 B3 55 C3
80: 35 9E 48 29 F6 65 92 E7
Listing 9: SHA calculation (result)
- Data is ready
Listing 2:
2rNIedy6JJqNUq5B6rHBvJm9xYHOXtUhJMFFnSAm7rVC/SkNoxdKXsnvS5EDBRWKImlMmHrra4xnokAtwj+ATKlhPVaLzYJ70GvQLM3cvY4=
Listing 3:
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8QEBAQEBAQEBAQEBAQEBAQ
Listing 4, 9:
0v+T3J1VZMj9fjdRLgokzzZwnXQ81E0pYZFpj9ih5S6wbymlG26S2g7x2FAGgE7+WQdfF92IkgD7/Hm/QDh7bQEA//+IAAAA3p6n+0bZo49VpexCzATw6Hxftp5lrgQ4TZGMlkR3kWEv5DaUSEVPXe3jNFe6Pqbe34OC+ZKzVcM1nkgp9mWS5w==
Listing 5:
OAAAAAARDAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHxAQEBAQEBAQEBAQEBAQEBA=
Listing 6:
KLUv/SA4hQEAlAI4AAAAABEMAAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fEAEA6UAZ
Listing 7:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA//+IAAAA3p6n+0bZo49VpexCzATw6Hxftp5lrgQ4TZGMlkR3kWEv5DaUSEVPXe3jNFe6Pqbe34OC+ZKzVcM1nkgp9mWS5w==
Listing 8:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwbymlG26S2g7x2FAGgE7+WQdfF92IkgD7/Hm/QDh7bQEA//+IAAAA3p6n+0bZo49VpexCzATw6Hxftp5lrgQ4TZGMlkR3kWEv5DaUSEVPXe3jNFe6Pqbe34OC+ZKzVcM1nkgp9mWS5w==
If something is unclear/confusing/wrong/etc., please write about it in the comments.