From 30ae02066e91471a5689e260198f71406159ebfc Mon Sep 17 00:00:00 2001 From: lonkaars Date: Fri, 3 May 2024 12:10:28 +0200 Subject: more dissector + protocol research --- docs/notes.md | 27 ++++++++++++++++++++++++- melonDS | 2 +- wireshark/ethers | 8 ++++++++ wireshark/pictochat.lua | 52 ++++++++++++++++++++++++++++++------------------- 4 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 wireshark/ethers diff --git a/docs/notes.md b/docs/notes.md index 6e515f9..5da8efe 100644 --- a/docs/notes.md +++ b/docs/notes.md @@ -188,6 +188,31 @@ Notable: message) - The completely filled message also shows an interesting pattern at the end, hinting at a slightly odd image codec +- Message length **can not** be used to consistently check which packets + contain PictoChat message data. + +#### MAC addresses + +The following table of addresses may be visible in the packet captures. An +[ethers](../wireshark/ethers) file is provided in the wireshark folder, which +may be symlinked to `~/.config/wireshark/ethers` to make Wireshark resolve the +MAC addresses to human-readable names. + +|address|label|source| +|-|-|-| +|`00:09:bf:11:22:33`|Default firmware MAC|[melonDS/src/SPI\_Firmware.h:37](../melonDS/src/SPI_Firmware.h)| +|`03:09:bf:00:00:00`|Multiplayer CMD|[melonDS/src/Wifi.cpp:42](../melonDS/src/Wifi.cpp)| +|`03:09:bf:00:00:10`|Multiplayer Reply|[melonDS/src/Wifi.cpp:43](../melonDS/src/Wifi.cpp)| +|`03:09:bf:00:00:03`|Multiplayer ACK|[melonDS/src/Wifi.cpp:44](../melonDS/src/Wifi.cpp)| +|`00:f0:77:77:77:77`|Access point|[melonDS/src/WifiAP.cpp:37](../melonDS/src/WifiAP.cpp)| +|`10:00:de:ad:be:ef`|Instance 1 (`lork`) firmware MAC|me| +|`20:00:de:ae:02:ff`|Instance 2 (`lork2`) firmware MAC|me| +|`ff:ff:ff:ff:ff:ff`|Broadcast|| + +In melonDS's settings, the firmware MAC addresses for `lork` and `lork2` are +set to `11:00:de:ad:be:ef` and `22:00:de:ad:be:ef` respectively, but these +always get mangled and sent as the addresses noted in the table above. I have +no idea why this happens. #### Analysis @@ -256,7 +281,6 @@ messages from the system that joined later) routers/APs, but is this encryption also used when the WiFi module is used in local multiplayer mode? Does this even matter inside the emulator? - ## TODO: - message reassembly field? (how does pictochat know which part a message index is) @@ -266,4 +290,5 @@ messages from the system that joined later) * resolution * pixel ordering * palette color indices (pixels are 1 nibble) +- what types of pictochat packets are there? diff --git a/melonDS b/melonDS index d529048..dcc3a74 160000 --- a/melonDS +++ b/melonDS @@ -1 +1 @@ -Subproject commit d529048e9ba286af275d9cefc0ebae9800c34f76 +Subproject commit dcc3a747360382857b1fbae05a06061f44e64033 diff --git a/wireshark/ethers b/wireshark/ethers new file mode 100644 index 0000000..b60d4ec --- /dev/null +++ b/wireshark/ethers @@ -0,0 +1,8 @@ +00:09:bf:11:22:33 Default_firmware_MAC +03:09:bf:00:00:00 Multiplayer_CMD +03:09:bf:00:00:10 Multiplayer_Reply +03:09:bf:00:00:03 Multiplayer_ACK +00:f0:77:77:77:77 Access_point +10:00:de:ad:be:ef Instance_1_(lork) +20:00:de:ae:02:ff Instance_2_(lork2) +ff:ff:ff:ff:ff:ff Broadcast diff --git a/wireshark/pictochat.lua b/wireshark/pictochat.lua index 4927a9b..13a2722 100644 --- a/wireshark/pictochat.lua +++ b/wireshark/pictochat.lua @@ -1,28 +1,29 @@ local pc = Proto("pictochat", "Nintendo DS PictoChat") + +pc.fields.unknown = ProtoField.bytes("pictochat.unknown", "Unknown") + pc.fields.msg_type = ProtoField.uint16("pictochat.msg_type", "Frame type", base.DEC, { - [0] = "Message", - [1] = "???", + [0] = "Normal", -- Used for actual messages, ack packets + [1] = "Announcement", -- TODO: send broadcast??? }) pc.fields.resend = ProtoField.uint16("pictochat.resend", "Resend", base.DEC, { [0] = "Resend", [2] = "Original", }) --- TODO: 6 bytes unknown pc.fields.length = ProtoField.uint16("pictochat.length", "Message length") --- TODO: 4 bytes unknown -pc.fields.mp_sender = ProtoField.ether("pictochat.mp_sender", "Multiplayer sender MAC") -pc.fields.sender = ProtoField.ether("pictochat.sender", "Sender MAC") -pc.fields.unknown_counter = ProtoField.uint16("pictochat.unknown_counter", "Unknown counter") --- TODO: 14 bytes unknown +pc.fields.host = ProtoField.ether("pictochat.host", "Room host") +pc.fields.src = ProtoField.ether("pictochat.src", "Source") +pc.fields.dst = ProtoField.ether("pictochat.dst", "dstination") pc.fields.content_offset = ProtoField.uint16("pictochat.content_offset", "Content offset") pc.fields.content = ProtoField.bytes("pictochat.content", "Content") pc.fields.sequence = ProtoField.uint16("pictochat.sequence", "Packet sequence") -pc.fields.unknown_constant = ProtoField.bytes("pictochat.unknown_constant", "unknown_constant") local nifi_length_field = Field.new("nifi.length") local pc_msg_type_field = Field.new("pictochat.msg_type") +local pc_length_field = Field.new("pictochat.length") local pc_resend_field = Field.new("pictochat.resend") -local pc_sender_field = Field.new("pictochat.sender") +local pc_src_field = Field.new("pictochat.src") +local pc_dst_field = Field.new("pictochat.dst") function pc.dissector(buffer, pinfo, tree) local header_length = nifi_length_field()() @@ -35,18 +36,29 @@ function pc.dissector(buffer, pinfo, tree) pc_tree:add_le(pc.fields.msg_type, buffer(0x00, 2)) pc_tree:add_le(pc.fields.resend, buffer(0x02, 2)) pc_tree:add_le(pc.fields.length, buffer(0x0a, 2)) - pc_tree:add_le(pc.fields.mp_sender, buffer(0x10, 6)) - pc_tree:add_le(pc.fields.sender, buffer(0x16, 6)) - pc_tree:add_le(pc.fields.sender, buffer(0x1c, 6)) -- copy - pc_tree:add_le(pc.fields.unknown_counter, buffer(0x22, 2)) - pc_tree:add_le(pc.fields.content_offset, buffer(0x32, 2)) - pc_tree:add(pc.fields.content, buffer(0x36, 0xa0)) - pc_tree:add_le(pc.fields.sequence, buffer(0xd6, 2)) - pc_tree:add_le(pc.fields.resend, buffer(0xd8, 2)) -- copy - pc_tree:add(pc.fields.unknown_constant, buffer(0xda, 4)) + pc_tree:add_le(pc.fields.host, buffer(0x10, 6)) + pc_tree:add_le(pc.fields.src, buffer(0x16, 6)) + pc_tree:add_le(pc.fields.dst, buffer(0x1c, 6)) + pc_tree:add_le(pc.fields.unknown, buffer(0x22, 2)) + pc_tree:add_le(pc.fields.unknown, buffer(0x24, 2)) + + local msg_type = pc_msg_type_field()() + if msg_type == 0 then -- type = Normal (TODO: this should be 'message = drawing') + pc_tree:add_le(pc.fields.content_offset, buffer(0x32, 2)) + + local content_length = pc_length_field()() - 50 -- TODO: why 50? + buffer = buffer(0x36) + pc_tree:add(pc.fields.content, buffer(0, content_length)) + buffer = buffer(content_length) + + pc_tree:add_le(pc.fields.sequence, buffer(0x00, 2)) + pc_tree:add_le(pc.fields.resend, buffer(0x02, 2)) -- copy + pc_tree:add(pc.fields.unknown, buffer(0x04, 4)) + end pinfo.cols.protocol = pc.name - pinfo.cols.src = tostring(pc_sender_field()) + pinfo.cols.src = tostring(pc_src_field()) + pinfo.cols.dst = tostring(pc_dst_field()) pinfo.cols.info = pc_msg_type_field().display .. ", " .. pc_resend_field().display end -- cgit v1.2.3