Difference between revisions of "PRUDP"

From 3dbrew
Jump to navigation Jump to search
m (Correct PRUDP version)
Line 3: Line 3:
 
At least two different versions of PRUDP seem to be used by Nintendo software: an older version used in the [[Friend Services|Friend services]] and a newer one used in Pokémon X/Y.
 
At least two different versions of PRUDP seem to be used by Nintendo software: an older version used in the [[Friend Services|Friend services]] and a newer one used in Pokémon X/Y.
  
== Version 1 ==
+
== Version 0 ==
 
This is the version of the protocol used by the [[Friend Services|Friend services]].
 
This is the version of the protocol used by the [[Friend Services|Friend services]].
  

Revision as of 19:18, 12 February 2018

PRUDP is a third-party network protocol used for network communications by some Nintendo software on the 3DS. It presumably stands for "pretty reliable UDP" or "partially reliable UDP" or some such, and essentially implements TCP-like reliability via sequence numbers and acknowledgement within UDP. Optionally it also adds security (prudps), via RC4 encryption + hashing. This is the protocol used in Nintendo 3DS and Wii U games for online multiplayer comms etc. This can be used with UDS local-WLAN too.

At least two different versions of PRUDP seem to be used by Nintendo software: an older version used in the Friend services and a newer one used in Pokémon X/Y.

Version 0

This is the version of the protocol used by the Friend services.

Packet structure

Type Name Description
u16 src_dst Magic: client-to-server (afa1) or server-to-client (a1af)
u16 op Type of packet
u8 unk1
u32 unk2
u16 seqno Incrementing ID to pair up data packets and acknowledgement
u8[*] payload
u8 checksum seems to be related to the sum of the rest of the fields (mod 256)

Different kinds of packets have different values for the different fields:

  • Handshake (1): op=0x40 or op=0x10 (cli-to-serv or serv-to-cli)
    • unk1, unk2, seqno are 0
    • payload: u32 unk3;
  • Handshake (2): op=0x61 or op=0x11
    • unk1, seqno are 0
    • payload: u32 unk3;
  • Hangup: op=0x63 or op=0x13
  • Heartbeat: op=0x44 or op=0x14
    • seqno = increasing
  • Data packet: op=0x62
    • seqno = increasing
    • payload: u8[?] payload;
  • Data ack: op=0x12
    • unk2 = 0x12345678
    • seqno = corresponding to the data packet being acknowledged

Sample session

Handshake 1   → af a1 40 00 00 00 00 00 00 00 00 00 00 00 00 97
Handshake 1   ← a1 af 10 00 00 00 00 00 00 00 00 5f 22 68 ea 3a
Handshake 2   → af a1 61 00 18 5f 22 68 ea 01 00 d4 d6 91 e8 c9
Handshake 2   ← a1 af 11 00 50 d4 d6 91 e8 01 00 00 00 00 00 de
Send data     → af a1 62 00 18 26 b4 01 a1 02 00 00 (25 bytes of encrypted payload) ef
Acknowledge   ← a1 af 12 00 50 78 56 34 12 02 00 00 d1
Send data     ← a1 af 62 00 50 67 dd f9 c3 01 00 00 (255 bytes of encrypted payload) 03
Acknowledge   → af a1 12 00 18 78 56 34 12 01 00 00 97
Send data     → af a1 62 00 18 8d 58 91 c0 03 00 00 (21 bytes of encrypted payload) fa
Acknowledge   ← a1 af 12 00 50 78 56 34 12 03 00 00 d2
Send data     ← a1 af 62 00 50 a9 c5 fa 2e 02 00 00 (130 bytes of encrypted payload) 54
Acknowledge   → af a1 12 00 18 78 56 34 12 02 00 00 98
Hangup        → af a1 63 00 18 5f 22 68 ea 04 00 aa
Hangup        ← a1 af 13 00 50 d4 d6 91 e8 04 00 e2

During the handshake process, the unk2 and unk3 fields exhibit a pattern where the unk3 of the preceding packet is used as the unk2 of the next one. The parties might be agreeing upon a key using a key-exchange scheme?

Payload

The plaintext form of the RC4 encrypted payload consists of a small header that looks like:

Type Name Description
u32 length Length of the remaining payload (excluding this field)
u8 op Type of packet
u8 unk1 (Only for server-to-client messages, seemingly always 1)
u32 msgid Seemingly used to pair up requests and responses
u32 unk2

The encrypted payloads exchanged when first connecting look something like (excluding the header):

→ string deviceID;
← u16 unk1; u16 unk2; u32 deviceID; u32 length; u8[length] blob; string connectionString; u8[7] unk3; string buildString;
→ u32 deviceID; u32 unk1;
← u16 unk1; u16 unk2; u32 length; u8[length] blob;

where string is both length-prefixed (u16) and zero-terminated (the terminating zero is not included in the length).

The connectionString looks like 'prudps:/address=203.180.85.79;port=50051;CID=1;PID=2;sid=1;stream=10;type=2' and the buildString is always 'branch:unknown build:2_24_13767_0'. The deviceID seems to be unique to each 3DS or perhaps online account (it seems to roughly scale with the age of the 3DS, so that an older 3DS sends a lower ID).