local p = Proto("pictochat", "PictoChat") p.fields.unknown = ProtoField.uint16("pictochat.unknown", "Unknown") p.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?? }) 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", [3] = "Light pink", [4] = "Orange", [5] = "Yellow", [6] = "Lime", [7] = "Light green", [8] = "Dark green", [9] = "Turqoise", [10] = "Light blue", [11] = "Blue", [12] = "Dark blue", [13] = "Dark purple", [14] = "Light purple", [15] = "Dark pink", }) 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 pc_src_field = Field.new("pictochat.src") local pc_dst_field = Field.new("pictochat.dst") local data_remaining = 0 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() 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 = p.name pinfo.cols.src = tostring(pc_src_field()) pinfo.cols.dst = tostring(pc_dst_field()) subtree:add_le(p.fields.payload_len, buffer(0x00, 2)) local payload_length = buffer(0x00, 2):le_uint() buffer = buffer(0x02) 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(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(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(p.fields.msg_start_len, buffer(0x04, 2)) data_remaining = buffer(0x04, 2):le_uint() local segment = buffer(0):bytes() local buf = ByteArray.tvb(segment, "Complete message???") end if msg_type == 24 or -- msg end msg_type == 86 -- msg body then 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 data_remaining = data_remaining - data_length pinfo.cols.info = string.format("Message body [remaining 0x%04x (%d) bytes]", data_remaining, data_remaining) end 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 (p.fields.original == -- True) message. 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(p.fields.data, buffer(0, data_length)) buffer = buffer(data_length) 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 subtree:add_le(p.fields.magic_trailer, buffer(0x02, 2)) -- const 0xb8b6 end