aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-08-23 15:59:51 +0200
committerLoek Le Blansch <loek@pipeframe.xyz>2024-08-23 15:59:51 +0200
commitbab4ac11a0e5700f267405273b03436423bc5205 (patch)
tree58f99cc17d043d207e05461cda3adc30e47d95b2
parentd1755aa37e6d88c0b3681b28225732fd0da26424 (diff)
more WIP
-rw-r--r--wireshark/melon.lua49
-rw-r--r--wireshark/nifi.lua84
-rw-r--r--wireshark/pictochat.lua152
-rwxr-xr-xwireshark/wireshark9
4 files changed, 170 insertions, 124 deletions
diff --git a/wireshark/melon.lua b/wireshark/melon.lua
new file mode 100644
index 0000000..a431781
--- /dev/null
+++ b/wireshark/melon.lua
@@ -0,0 +1,49 @@
+local p = Proto("melon", "MelonDS packet header")
+local dt = DissectorTable.new("melon")
+
+p.fields.magic = ProtoField.uint32("melon.magic", "Magic", base.HEX)
+p.fields.src = ProtoField.int32("melon.src", "Instance ID", base.DEC)
+p.fields.type = ProtoField.new("Type", "melon.type", ftypes.UINT32)
+p.fields.type_enum = ProtoField.uint16("melon.type.enum", "Numeric message type enum", base.DEC, {
+ [0] = "Regular",
+ [1] = "CMD",
+ [2] = "Reply",
+ [3] = "ACK",
+})
+p.fields.type_aid = ProtoField.uint16("melon.type.aid", "Message type \"aid\" value")
+p.fields.length = ProtoField.uint32("melon.len", "Remaining message length", base.DEC)
+p.fields.timestamp = ProtoField.uint64("melon.timestamp", "Timestamp", base.DEC)
+
+local p_type_enum_field = Field.new("melon.type.enum")
+
+function p.dissector(buffer, pinfo, tree)
+ local header_size = 0x18
+
+ -- check magic ("NIFI")
+ if buffer(0x00, 4):uint() ~= 0x4e494649 then return end
+
+ local subtree = tree:add(p, buffer(0, header_size), string.format("%s: %d bytes", p.description, header_size))
+ subtree:add(p.fields.magic, buffer(0x00, 4))
+
+ local instance = buffer(0x04, 4):le_uint()
+ subtree:add_le(p.fields.src, buffer(0x04, 4))
+
+ local type_tree = subtree:add_le(p.fields.type, buffer(0x08, 4))
+ type_tree:add_le(p.fields.type_enum, buffer(0x08, 2))
+ type_tree:add_le(p.fields.type_aid, buffer(0x0a, 2))
+
+ subtree:add_le(p.fields.length, buffer(0x0c, 4))
+ subtree:add_le(p.fields.timestamp, buffer(0x10, 8))
+
+ -- pretty wireshark shit
+ pinfo.cols.protocol = p.name
+ pinfo.cols.src = string.format("instance %d", instance)
+ pinfo.cols.info = p_type_enum_field().display
+
+ -- melonds packets always contain NIFI packets, I use 0 as the pattern
+ -- because this function doesn't seem to like nil
+ dt:try(0, buffer(header_size):tvb(), pinfo, tree)
+
+ return header_size
+end
+
diff --git a/wireshark/nifi.lua b/wireshark/nifi.lua
index 345b5e3..94a563c 100644
--- a/wireshark/nifi.lua
+++ b/wireshark/nifi.lua
@@ -1,39 +1,57 @@
-local nifi = Proto("nifi", "MelonDS Ni-Fi")
-nifi.fields.magic = ProtoField.uint32("nifi.magic", "Magic", base.HEX)
-nifi.fields.src = ProtoField.int32("nifi.src", "Instance ID", base.DEC)
-nifi.fields.type = ProtoField.new("Type", "nifi.type", ftypes.UINT32)
-nifi.fields.type_enum = ProtoField.uint16("nifi.type.enum", "Numeric message type enum", base.DEC, {
- [0] = "Regular",
- [1] = "CMD",
- [2] = "Reply",
- [3] = "ACK",
-})
-nifi.fields.type_aid = ProtoField.uint16("nifi.type.aid", "Message type \"aid\" value")
-nifi.fields.length = ProtoField.uint32("nifi.length", "Length", base.DEC)
-nifi.fields.timestamp = ProtoField.uint64("nifi.timestamp", "Timestamp", base.DEC)
-
-local nifi_src_field = Field.new("nifi.src")
-local nifi_type_enum_field = Field.new("nifi.type.enum")
-
-function nifi.dissector(buffer, pinfo, tree)
- -- check magic ("NIFI")
- if buffer(0x00, 4):uint() ~= 0x4e494649 then return end
- local nifi_tree = tree:add(nifi, buffer(0, 0x18), "MelonDS Ni-Fi Header: 24 bytes")
- nifi_tree:add(nifi.fields.magic, buffer(0x00, 4))
-
- nifi_tree:add_le(nifi.fields.src, buffer(0x04, 4))
+local p = Proto("nifi", "Ni-Fi header")
+local dt = DissectorTable.new("nifi")
- local nifi_type_tree = nifi_tree:add_le(nifi.fields.type, buffer(0x08, 4))
- nifi_type_tree:add_le(nifi.fields.type_enum, buffer(0x08, 2))
- nifi_type_tree:add_le(nifi.fields.type_aid, buffer(0x0a, 2))
+p.fields.frame_type = ProtoField.uint16("nifi.type", "Frame type", base.DEC, {
+ [0] = "Normal", -- Used for actual messages, ack packets
+ [1] = "Announcement", -- TODO: send broadcast???
+})
+p.fields.original = ProtoField.bool("nifi.original", "Original") -- only 0 or 2
+p.fields.gameid = ProtoField.uint16("nifi.gid", "Game ID", base.HEX, {
+ [0xfffd] = "PictoChat (CMD)",
+ [0x7dcb] = "PictoChat (ACK)",
+ [0x5a5a] = "Mario Kart DS (CMD)",
+ [0x7c02] = "Mario Kart DS (ACK)",
+})
+p.fields.magic = ProtoField.bytes("nifi.magic", "Magic")
+p.fields.length = ProtoField.uint16("nifi.len", "Remaining message length")
- nifi_tree:add_le(nifi.fields.length, buffer(0x0c, 4))
- nifi_tree:add_le(nifi.fields.timestamp, buffer(0x10, 8))
+local melon_type_enum_field = Field.new("melon.type.enum")
- pinfo.cols.protocol = nifi.name
- pinfo.cols.src = "Instance " .. nifi_src_field().display
- pinfo.cols.info = "type:" .. nifi_type_enum_field().display
+function p.init()
+ -- register nifi as a subdissector for melon
+ DissectorTable.get("melon"):add(0, p)
+end
- return 0x18
+function p.dissector(buffer, pinfo, tree)
+ local header_size = 12
+
+ local subtree = tree:add(p, buffer(0, header_size), string.format("%s: %d bytes", p.description, header_size))
+ subtree:add_le(p.fields.frame_type, buffer(0x00, 2))
+ subtree:add_le(p.fields.original, buffer(0x02, 2))
+ local original = buffer(0x02, 2):le_uint() > 0
+
+ local melon_type = melon_type_enum_field()()
+ local gameid_buf = buffer(0x04, 4)
+ local gameid = 0
+ if melon_type == 1 then -- CMD
+ gameid_buf = gameid_buf(2, 2)
+ elseif melon_type == 3 then -- ACK
+ gameid_buf = gameid_buf(0, 2)
+ end
+ gameid = gameid_buf():le_uint()
+ subtree:add_le(p.fields.gameid, gameid_buf)
+
+ subtree:add_le(p.fields.magic, buffer(0x08, 2)) -- const 0x1401
+ subtree:add_le(p.fields.length, buffer(0x0a, 2))
+ local length = buffer(0x0a, 2):le_uint()
+
+ -- pretty wireshark shit
+ pinfo.cols.protocol = p.name
+ pinfo.cols.info = string.format("GID %04x %s, %s", gameid, pinfo.cols.info, (original and "Original" or "Resend"))
+
+ -- try to call subdissector for gameid
+ dt:try(gameid, buffer(header_size, length):tvb(), pinfo, tree)
+
+ return header_size
end
diff --git a/wireshark/pictochat.lua b/wireshark/pictochat.lua
index 34ca17e..ba16ee0 100644
--- a/wireshark/pictochat.lua
+++ b/wireshark/pictochat.lua
@@ -1,13 +1,8 @@
-local pc = Proto("pictochat", "Nintendo DS PictoChat")
+local p = Proto("pictochat", "PictoChat")
-pc.fields.unknown = ProtoField.uint16("pictochat.unknown", "Unknown")
+p.fields.unknown = ProtoField.uint16("pictochat.unknown", "Unknown")
-pc.fields.frame_type = ProtoField.uint16("pictochat.frame_type", "Frame type", base.DEC, {
- [0] = "Normal", -- Used for actual messages, ack packets
- [1] = "Announcement", -- TODO: send broadcast???
-})
-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, {
+p.fields.msg_type = ProtoField.uint8("pictochat.msg_type", "Message type", base.DEC, {
[0] = "???",
[1] = "???",
[10] = "Message start", -- sent before a message packet
@@ -17,23 +12,22 @@ pc.fields.msg_type = ProtoField.uint8("pictochat.msg_type", "Message type", base
[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")
-pc.fields.src = ProtoField.ether("pictochat.src", "Source")
-pc.fields.dst = ProtoField.ether("pictochat.dst", "Destination")
-pc.fields.payload_len = ProtoField.uint16("pictochat.payload_len", "Payload length")
-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, {
+p.fields.length = ProtoField.uint16("pictochat.length", "Length")
+p.fields.host = ProtoField.ether("pictochat.host", "Room host")
+p.fields.src = ProtoField.ether("pictochat.src", "Source")
+p.fields.dst = ProtoField.ether("pictochat.dst", "Destination")
+p.fields.payload_len = ProtoField.uint16("pictochat.payload_len", "Payload length")
+p.fields.data_len = ProtoField.uint8("pictochat.data_len", "Data length")
+p.fields.data_end = ProtoField.bool("pictochat.data_end", "Data end")
+p.fields.data_offset = ProtoField.uint16("pictochat.data_offset", "Data offset")
+p.fields.data_sequence = ProtoField.uint16("pictochat.data_sequence", "Data sequence")
+p.fields.data = ProtoField.bytes("pictochat.data", "Data")
+p.fields.sequence = ProtoField.uint16("pictochat.sequence", "Packet sequence")
+p.fields.magic_trailer = ProtoField.bytes("pictochat.magic_trailer", "Magic (trailer)")
+p.fields.user_mac = ProtoField.ether("pictochat.user.mac", "Address")
+p.fields.user_name = ProtoField.string("pictochat.user.name", "Nickname")
+p.fields.user_msg = ProtoField.string("pictochat.user.msg", "Message")
+p.fields.user_color = ProtoField.uint16("pictochat.user.color", "Color", base.DEC, {
[0] = "Greyish blue",
[1] = "Brown",
[2] = "Red",
@@ -51,80 +45,66 @@ pc.fields.user_color = ProtoField.uint16("pictochat.user.color", "Color", base.D
[14] = "Light purple",
[15] = "Dark 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")
-pc.fields.msg_start_len = ProtoField.uint8("pictochat.msg.start_len", "Total length")
+p.fields.user_bday_month = ProtoField.uint8("pictochat.user.bday_month", "Month")
+p.fields.user_bday_day = ProtoField.uint8("pictochat.user.bday_day", "Day")
+p.fields.msg_start_len = ProtoField.uint8("pictochat.msg.start_len", "Total length")
-local nifi_length_field = Field.new("nifi.length")
-local pc_msg_type_field = Field.new("pictochat.msg_type")
local pc_src_field = Field.new("pictochat.src")
local pc_dst_field = Field.new("pictochat.dst")
local data_remaining = 0
-function pc.dissector(buffer, pinfo, tree)
- local header_length = nifi_length_field()()
- if header_length == 0 then return end
-
- buffer = buffer(0x18) -- skip the Ni-Fi header
-
- local pc_tree = tree:add(pc, buffer(), "PictoChat: " .. header_length .. " bytes")
-
- -- 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.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.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
- buffer = buffer(0x0c)
-
- 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.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.sequence, buffer(0x16, 2))
- pc_tree:add_le(pc.fields.unknown, buffer(0x18, 2))
- pc_tree:add_le(pc.fields.unknown, buffer(0x1a, 2))
- pc_tree:add_le(pc.fields.msg_type, buffer(0x1c, 1))
+local state = {}
+
+function p.init()
+ -- register pictochat as a subdissector for nifi
+ local dt = DissectorTable.get("nifi")
+ dt:add(0xfffd, p) -- CMD messages
+ -- dt:add(0x7dcb, p) -- ACK messages
+end
+
+function p.dissector(buffer, pinfo, tree)
+ local subtree = tree:add(p, buffer(), string.format("%s: %d bytes", p.description, buffer():len()))
+ subtree:add_le(p.fields.unknown, buffer(0x00, 2))
+ subtree:add_le(p.fields.unknown, buffer(0x02, 2))
+ subtree:add_le(p.fields.dst, buffer(0x04, 6))
+ subtree:add_le(p.fields.src, buffer(0x0a, 6))
+ subtree:add_le(p.fields.host, buffer(0x10, 6))
+ subtree:add_le(p.fields.sequence, buffer(0x16, 2))
+ subtree:add_le(p.fields.unknown, buffer(0x18, 2))
+ subtree:add_le(p.fields.unknown, buffer(0x1a, 2))
+ subtree:add_le(p.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))
+ subtree:add_le(p.fields.unknown, buffer(0x1d, 1))
+ subtree:add_le(p.fields.unknown, buffer(0x1e, 2))
buffer = buffer(0x20)
-- pretty wireshark shit
- pinfo.cols.protocol = pc.name
+ pinfo.cols.protocol = p.name
pinfo.cols.src = tostring(pc_src_field())
pinfo.cols.dst = tostring(pc_dst_field())
- pinfo.cols.info = pc_msg_type_field().display .. ", " .. (original and "Original" or "Resend")
-
- pc_tree:add_le(pc.fields.payload_len, buffer(0x00, 2))
+ subtree:add_le(p.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 payload = subtree: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_le(p.fields.user_mac, buffer(0x0a, 6)) -- endianness is fucked
+ payload:add(p.fields.user_name, buffer(0x10, 20), buffer(0x10, 20):le_ustring())
+ payload:add(p.fields.user_msg, buffer(0x24, 52), buffer(0x24, 52):le_ustring())
+ payload:add_le(p.fields.user_color, buffer(0x58, 2))
local bday = payload:add(buffer(0x5a, 2), string.format("Birthday: %02d/%02d", buffer(0x5a, 1):uint(), buffer(0x5b, 1):uint()))
- bday:add(pc.fields.user_bday_month, buffer(0x5a, 1))
- bday:add(pc.fields.user_bday_day, buffer(0x5b, 1))
+ bday:add(p.fields.user_bday_month, buffer(0x5a, 1))
+ bday:add(p.fields.user_bday_day, buffer(0x5b, 1))
end
if
msg_type == 10 -- msg start
then
- payload:add_le(pc.fields.msg_start_len, buffer(0x04, 2))
+ payload:add_le(p.fields.msg_start_len, buffer(0x04, 2))
data_remaining = buffer(0x04, 2):le_uint()
local segment = buffer(0):bytes()
@@ -135,8 +115,8 @@ function pc.dissector(buffer, pinfo, tree)
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))
+ payload:add_le(p.fields.unknown, buffer(0x00, 2))
+ payload:add_le(p.fields.data_len, buffer(0x02, 1))
local data_length = buffer(0x02, 1):le_uint()
if data_remaining > 0 then
@@ -144,29 +124,27 @@ function pc.dissector(buffer, pinfo, tree)
pinfo.cols.info = string.format("Message body [remaining 0x%04x (%d) bytes]", data_remaining, data_remaining)
end
- payload:add_le(pc.fields.data_end, buffer(0x03, 1))
+ payload:add_le(p.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 (pc.fields.original ==
+ -- increment this value by 160 for each new original (p.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
+ payload:add_le(p.fields.data_offset, buffer(0x04, 2))
+ payload:add_le(p.fields.unknown, buffer(0x06, 2)) -- usually 0
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))
+ payload:add(p.fields.data, buffer(0, data_length))
buffer = buffer(data_length)
- payload:add_le(pc.fields.data_sequence, buffer(0x00, 2))
- payload:add_le(pc.fields.unknown, buffer(0x02, 2)) -- copy
- pc_tree:add_le(pc.fields.unknown, buffer(0x04, 2))
+ payload:add_le(p.fields.data_sequence, buffer(0x00, 2))
+ payload:add_le(p.fields.unknown, buffer(0x02, 2)) -- copy
+ subtree:add_le(p.fields.unknown, buffer(0x04, 2))
buffer = buffer(0x06)
end
buffer = buffer_next -- after payload
- pc_tree:add_le(pc.fields.magic_trailer, buffer(0x02, 2)) -- const 0xb8b6
+ subtree:add_le(p.fields.magic_trailer, buffer(0x02, 2)) -- const 0xb8b6
end
-register_postdissector(pc)
-
diff --git a/wireshark/wireshark b/wireshark/wireshark
index ee24518..d6a285b 100755
--- a/wireshark/wireshark
+++ b/wireshark/wireshark
@@ -2,8 +2,9 @@
# simple wrapper to load lua scripts for DLT_USER0
here="$(dirname "$0")"
exec wireshark \
- -X "lua_script:$here/nifi.lua" \
- -X "lua_script:$here/pictochat.lua" \
- -o 'uat:user_dlts:"User 0 (DLT=147)","nifi","","","",""' \
- "$@"
+ -X "lua_script:$here/melon.lua" \
+ -X "lua_script:$here/nifi.lua" \
+ -X "lua_script:$here/pictochat.lua" \
+ -o 'uat:user_dlts:"User 0 (DLT=147)","melon","","","",""' \
+ "$@"