From 10d4665466689918eed07d9edd6e38c6183d93e3 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 21 Aug 2024 19:29:47 +0200 Subject: more dissector for user join packets --- docs/notes.md | 8 ++-- wireshark/pictochat.lua | 121 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 87 insertions(+), 42 deletions(-) diff --git a/docs/notes.md b/docs/notes.md index 3473ee4..204ac57 100644 --- a/docs/notes.md +++ b/docs/notes.md @@ -221,7 +221,9 @@ no idea why this happens. #### Analysis -![](../assets/ws-msg-fill-mid.png) +See [the wireshark dissector](../wireshark/pictochat.lua) + + +|0x1a|`u16`|PictoChat: Resend (2=New, 0=Resend)|< !-- I assume u16 because the next byte is always 0x00 -- > |0x22|`u16`|PictoChat: Length (offset 36 of complete packet length)| |0x28|`u8[6]`|PictoChat: multiplayer CMD MAC (melonDS Wifi::MPCmdMAC)| |0x2e|`u8[6]`|PictoChat: sender MAC| @@ -244,7 +246,6 @@ no idea why this happens. |0xf0|`u16`|PictoChat: copy of 0x1a (Resend)| |0xf2|`u32`|Unknown: constant 0x93ffb8b6| - @@ -323,4 +324,5 @@ messages from the system that joined later) * palette color indices (pixels are 1 nibble) - what types of pictochat packets are there? (i.e. how are room join/leave events broadcast?) +- `pictochat.msg_type in {10, 24, 86}` message reassembly in dissector diff --git a/wireshark/pictochat.lua b/wireshark/pictochat.lua index 756fe45..82e6a31 100644 --- a/wireshark/pictochat.lua +++ b/wireshark/pictochat.lua @@ -6,18 +6,16 @@ pc.fields.frame_type = ProtoField.uint16("pictochat.frame_type", "Frame type", b [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", -}) -pc.fields.msg_type = ProtoField.uint16("pictochat.msg_type", "Message type", base.DEC, { - [0] = "????", -- sent around room creation time?? - [1] = "????", -- sent around room creation time?? - [2] = "Room event", -- Used for drawing payload packets and join/leave stuff - [4] = "????", -- sent around room creation time?? - [5] = "Spam", -- generic spam shit - -- msg_type 3 is never sent and a few arbitrary 'high' types (>10000) are - -- sent around startup +pc.fields.original = ProtoField.bool("pictochat.original", "Original") -- only 0 or 2 +pc.fields.msg_type = ProtoField.uint8("pictochat.msg_type", "Message type", base.DEC, { + [0] = "???", + [1] = "???", + [10] = "Message start", -- sent before a message packet + [24] = "Message end", -- sent after multi-frame message body packets + [48] = "User join", -- these packets contain a username + [52] = "Keepalive", -- constantly spammed (keep alive?) + [86] = "Message body", -- contains tile data for drawing + [184] = "???", -- user leave?? }) pc.fields.length = ProtoField.uint16("pictochat.length", "Length") pc.fields.host = ProtoField.ether("pictochat.host", "Room host") @@ -27,12 +25,37 @@ pc.fields.payload_len = ProtoField.uint16("pictochat.payload_len", "Payload leng pc.fields.data_len = ProtoField.uint8("pictochat.data_len", "Data length") pc.fields.data_end = ProtoField.bool("pictochat.data_end", "Data end") pc.fields.data_offset = ProtoField.uint16("pictochat.data_offset", "Data offset") +pc.fields.data_sequence = ProtoField.uint16("pictochat.data_sequence", "Data sequence") pc.fields.data = ProtoField.bytes("pictochat.data", "Data") pc.fields.sequence = ProtoField.uint16("pictochat.sequence", "Packet sequence") +pc.fields.magic_header = ProtoField.bytes("pictochat.magic_header", "Magic (header)") +pc.fields.magic_trailer = ProtoField.bytes("pictochat.magic_trailer", "Magic (trailer)") +pc.fields.user_mac = ProtoField.ether("pictochat.user.mac", "Address") +pc.fields.user_name = ProtoField.string("pictochat.user.name", "Nickname") +pc.fields.user_msg = ProtoField.string("pictochat.user.msg", "Message") +pc.fields.user_color = ProtoField.uint16("pictochat.user.color", "Color", base.DEC, { + [0] = "Gray", + [1] = "Brown", + [2] = "Red", + [3] = "Pink", + [4] = "Orange", + [5] = "Yellow", + [6] = "Lime", + [7] = "Green", + [8] = "Other green?", + [9] = "Cyan", + [10] = "Aqua", + [11] = "Indigo", + [12] = "Purple", + [13] = "Violet", + [14] = "Hot pink", + [15] = "Hotter pink?", +}) +pc.fields.user_bday_month = ProtoField.uint8("pictochat.user.bday_month", "Month") +pc.fields.user_bday_day = ProtoField.uint8("pictochat.user.bday_day", "Day") local nifi_length_field = Field.new("nifi.length") local pc_msg_type_field = Field.new("pictochat.msg_type") -local pc_resend_field = Field.new("pictochat.resend") local pc_src_field = Field.new("pictochat.src") local pc_dst_field = Field.new("pictochat.dst") @@ -47,10 +70,11 @@ function pc.dissector(buffer, pinfo, tree) -- pictochat header local pc_header = pc_tree:add(buffer(0x00, 12), "Header: 12 bytes") pc_header:add_le(pc.fields.frame_type, buffer(0x00, 2)) - pc_header:add_le(pc.fields.resend, buffer(0x02, 2)) + pc_header:add_le(pc.fields.original, buffer(0x02, 2)) + local original = buffer(0x02, 2):le_uint() > 0 pc_header:add_le(pc.fields.unknown, buffer(0x04, 2)) pc_header:add_le(pc.fields.unknown, buffer(0x06, 2)) - pc_header:add_le(pc.fields.unknown, buffer(0x08, 2)) + pc_header:add_le(pc.fields.magic_header, buffer(0x08, 2)) -- const 0x1401 pc_header:add_le(pc.fields.length, buffer(0x0a, 2)) -- above length represents that of the remaining message so this info is -- grouped and the buffer is advanced 12 bytes @@ -61,48 +85,67 @@ function pc.dissector(buffer, pinfo, tree) pc_tree:add_le(pc.fields.dst, buffer(0x04, 6)) pc_tree:add_le(pc.fields.src, buffer(0x0a, 6)) pc_tree:add_le(pc.fields.host, buffer(0x10, 6)) - pc_tree:add_le(pc.fields.unknown, buffer(0x16, 2)) -- counting? + pc_tree:add_le(pc.fields.sequence, buffer(0x16, 2)) pc_tree:add_le(pc.fields.unknown, buffer(0x18, 2)) - pc_tree:add_le(pc.fields.resend, buffer(0x1a, 2)) - pc_tree:add_le(pc.fields.unknown, buffer(0x1c, 2)) - pc_tree:add_le(pc.fields.msg_type, buffer(0x1e, 2)) - local msg_type = buffer(0x1e, 2):le_uint() + pc_tree:add_le(pc.fields.unknown, buffer(0x1a, 2)) + pc_tree:add_le(pc.fields.msg_type, buffer(0x1c, 1)) + local msg_type = buffer(0x1c, 1):le_uint() + pc_tree:add_le(pc.fields.unknown, buffer(0x1d, 1)) + pc_tree:add_le(pc.fields.unknown, buffer(0x1e, 2)) buffer = buffer(0x20) - if msg_type == 2 then -- type = room event? - pc_tree:add_le(pc.fields.payload_len, buffer(0x00, 2)) - local payload_length = buffer(0x00, 2):le_uint() - local payload = pc_tree:add(buffer(0x02, payload_length), "Payload: " .. payload_length .. " bytes") - payload:add_le(pc.fields.unknown, buffer(0x02, 2)) - payload:add_le(pc.fields.data_len, buffer(0x04, 1)) - local data_length = buffer(0x04, 1):le_uint() - payload:add_le(pc.fields.data_end, buffer(0x05, 1)) + pc_tree:add_le(pc.fields.payload_len, buffer(0x00, 2)) + local payload_length = buffer(0x00, 2):le_uint() + buffer = buffer(0x02) + local payload = pc_tree:add(buffer(0x00, payload_length), "Payload: " .. payload_length .. " bytes") + local buffer_next = buffer(payload_length) + + if + msg_type == 48 -- user join + then + payload:add_le(pc.fields.user_mac, buffer(0x0a, 6)) -- endianness is fucked + payload:add(pc.fields.user_name, buffer(0x10, 20), buffer(0x10, 20):le_ustring()) + payload:add(pc.fields.user_msg, buffer(0x24, 52), buffer(0x24, 52):le_ustring()) + payload:add_le(pc.fields.user_color, buffer(0x58, 2)) + payload:add(pc.fields.user_bday_month, buffer(0x5a, 1)) + payload:add(pc.fields.user_bday_day, buffer(0x5b, 1)) + + elseif + msg_type == 10 or -- msg start + msg_type == 24 or -- msg end + msg_type == 86 -- msg body + then + payload:add_le(pc.fields.unknown, buffer(0x00, 2)) + payload:add_le(pc.fields.data_len, buffer(0x02, 1)) + local data_length = buffer(0x02, 1):le_uint() + payload:add_le(pc.fields.data_end, buffer(0x03, 1)) -- This appears to be some kind of offset for indicating where to store the -- current frame's data in a larger buffer. Messages sent in multiple parts - -- increment this value by 160 for each new original (pictochat.resend == - -- 2) message. - payload:add_le(pc.fields.data_offset, buffer(0x06, 2)) - payload:add_le(pc.fields.unknown, buffer(0x08, 2)) -- usually 0 + -- increment this value by 160 for each new original (pc.fields.original == + -- True) message. + payload:add_le(pc.fields.data_offset, buffer(0x04, 2)) + payload:add_le(pc.fields.unknown, buffer(0x06, 2)) -- usually 0 - buffer = buffer(0x0a) + buffer = buffer(0x08) -- This appears to be the actual message data (the drawing) sent as an -- array of 8x8 tiles. - payload:add(pc.fields.data, buffer(0, data_length)) - buffer = buffer(data_length) + -- payload:add(pc.fields.data, buffer(0, data_length)) + -- buffer = buffer(data_length) - payload:add_le(pc.fields.sequence, buffer(0x00, 2)) - payload:add_le(pc.fields.resend, buffer(0x02, 2)) -- copy + payload:add_le(pc.fields.data_sequence, buffer(0x00, 2)) + payload:add_le(pc.fields.unknown, buffer(0x02, 2)) -- copy buffer = buffer(0x04) end + buffer = buffer_next -- after payload pc_tree:add_le(pc.fields.unknown, buffer(0x00, 2)) - pc_tree:add_le(pc.fields.unknown, buffer(0x02, 2)) + pc_tree:add_le(pc.fields.magic_trailer, buffer(0x02, 2)) -- const 0xb8b6 -- pretty wireshark shit pinfo.cols.protocol = pc.name 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 + pinfo.cols.info = pc_msg_type_field().display .. ", " .. (original and "Original" or "Resend") end register_postdissector(pc) -- cgit v1.2.3