diff options
author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-08-23 15:59:51 +0200 |
---|---|---|
committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-08-23 15:59:51 +0200 |
commit | bab4ac11a0e5700f267405273b03436423bc5205 (patch) | |
tree | 58f99cc17d043d207e05461cda3adc30e47d95b2 | |
parent | d1755aa37e6d88c0b3681b28225732fd0da26424 (diff) |
more WIP
-rw-r--r-- | wireshark/melon.lua | 49 | ||||
-rw-r--r-- | wireshark/nifi.lua | 84 | ||||
-rw-r--r-- | wireshark/pictochat.lua | 152 | ||||
-rwxr-xr-x | wireshark/wireshark | 9 |
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","","","",""' \ + "$@" |