aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-08-21 19:29:47 +0200
committerLoek Le Blansch <loek@pipeframe.xyz>2024-08-21 19:29:47 +0200
commit10d4665466689918eed07d9edd6e38c6183d93e3 (patch)
tree5695c70bf5b73166638c831dc2c173708963ab9e
parent130aaf7c9f098ad4722483abbe1db2a2f54d8949 (diff)
more dissector for user join packets
-rw-r--r--docs/notes.md8
-rw-r--r--wireshark/pictochat.lua121
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)
+
+<!-- ![](../assets/ws-msg-fill-mid.png)
|offset|type|description|
|-|-|-|
@@ -232,7 +234,7 @@ no idea why this happens.
|0x10|`u64`|Ni-Fi: Timestamp|
|
|0x18|`u16`|PictoChat: Message type??? (0)|
-|0x1a|`u16`|PictoChat: Resend (2=New, 0=Resend)|<!-- I assume u16 because the next byte is always 0x00 -->
+|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|
-<!--
TODO: 0x4e(PictoChat msg data offset) - 0x18(Ni-Fi header length) == 0x36
-->
@@ -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)