aboutsummaryrefslogtreecommitdiff
path: root/wireshark/pictochat.lua
blob: ba16ee0c7580dfc726889c304d68d039ff6bf1ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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