Skip to content

Instantly share code, notes, and snippets.

@mgeeky
Last active April 8, 2022 05:54
Show Gist options
  • Save mgeeky/c0990bfea69a2c0ece3ae7f297650848 to your computer and use it in GitHub Desktop.
Save mgeeky/c0990bfea69a2c0ece3ae7f297650848 to your computer and use it in GitHub Desktop.
My first Wireshark LUA dissector (UDP): dissecting James Forshaw's SuperFunkyChat (https://github.com/tyranid/ExampleChatApplication) network protocol.
--
-- SuperFunkyChat (https://github.com/tyranid/ExampleChatApplication) by James Forshaw - Wireshark UDP dissector
--
-- On Fedora one has to install wireshark-devel first:
-- $ sudo dnf install wireshark-devel -y
--
-- Then it can be used as following:
-- $ wireshark -X lua_script:udp-superfunkychat-dissector.lua -r capture.pcap -Y "udp.port == 12345"
--
-- Wrriten by: Mariusz B. / mgeeky, '18
--
SUPERFUNKYCHAT = Proto ("chat", "SuperFunkyChat protocol dissector")
local fields = SUPERFUNKYCHAT.fields
fields.chksum = ProtoField.uint32("chat.chksum", "Checksum", base.HEX)
fields.command = ProtoField.uint8("chat.command", "Command")
fields.data = ProtoField.bytes("chat.data", "Data")
commands = {
[0] = "New user joined",
[1] = "Server accepted new client",
[2] = "/quit command",
[3] = "Message",
[5] = "Private Message",
[6] = "User requested /list",
[7] = "/list result",
}
-- Helps us read string preceded with it's length.
function read_string(buffer, start)
local length = buffer(start, 1):uint()
local data = buffer(start + 1, length):string()
return data, (length + 1)
end
function parse_0(datatree, data)
local info = ""
local username, pos0 = read_string(data, 0)
local hostname, pos1 = read_string(data, pos0)
datatree:add(SUPERFUNKYCHAT, data(0, pos0), "Username: " .. username)
datatree:add(SUPERFUNKYCHAT, data(pos0, pos1), "Hostname: '" .. hostname .. "'")
info = "New user joined: " .. username .. ", hostname: '" .. hostname .. "'"
return info
end
function parse_1(datatree, data)
local info = "Server accepted new incoming client."
datatree:add(SUPERFUNKYCHAT, data(0, 1), info)
return info
end
function parse_2(datatree, data)
local info = "/quit '"
local msg, len = read_string(data, 0)
info = info .. msg .. "'"
datatree:add(SUPERFUNKYCHAT, data(0, len), info)
return info
end
function parse_3(datatree, data)
local info = ""
local username, pos0 = read_string(data, 0)
local message, pos1 = read_string(data, pos0)
datatree:add(SUPERFUNKYCHAT, data(0, pos0), "Username: " .. username)
datatree:add(SUPERFUNKYCHAT, data(pos0, pos1), "Message: '" .. message .. "'")
info = "Username: " .. username .. ", Message: '" .. message .. "'"
return info
end
function parse_5(datatree, data)
local info = ""
local toUser, pos0 = read_string(data, 0)
local unknown = data(pos0, 4):uint()
local username, pos1 = read_string(data, pos0 + 4)
local message, pos2 = read_string(data, pos0 + 4 + pos1)
info = "Private Message from: " .. username .. ", to: " .. toUser .. ". Message: '" .. message .. "'"
datatree:add(SUPERFUNKYCHAT, data(0, pos0 + 4 + pos1 + pos2), info)
return info
end
function parse_6(datatree, data)
local info = "User requested /list of other users."
datatree:add(SUPERFUNKYCHAT, data(0, 1), info)
return info
end
function parse_7(datatree, data)
local info = ""
local numOfUsers = data(0, 4):uint()
local offset = 4
info = "Returned list of " .. numOfUsers .. " users."
datatree:add(SUPERFUNKYCHAT, data(0, pos0 + 4 + pos1 + pos2), info)
for i=1, numOfUsers do
local offset0 = offset
local username, pos0 = read_string(data, offset)
offset = offset + pos0
local hostname, pos1 = read_string(data, offset)
offset = offset + pos1
local info1 = "User: " .. username .. ", hostname: " .. hostname
datatree:add(SUPERFUNKYCHAT, data(offset0, offset), info1)
end
return info
end
function calc_checksum(tag, data)
local checksum = tag
local datastr = data:string()
for i=1, #datastr do
checksum = checksum + string.byte(datastr, i)
end
return checksum
end
-- Dissecting main function
-- * buffer - input packet buffer, UDP as "Testy Virtual Buffer" (TVB)
-- * pinfo - packet info
-- * tree - protocol dissecting tree's handle
function SUPERFUNKYCHAT.dissector (buffer, pinfo, tree)
-- Set the name in the "Protocol" column
pinfo.cols.protocol = "CHAT"
-- Create subtree with protocol dissection
local subtree = tree:add(SUPERFUNKYCHAT, buffer(), "SuperFunkyChat protocol data")
local checksum = buffer(0, 4):uint()
local command = buffer(4, 1)
local data = buffer(5):tvb()
local calculated_checksum = calc_checksum(command:uint(), buffer(5))
local checksumtext = ""
if calculated_checksum == checksum then
checksumtext = "Checksum fine"
subtree:add_expert_info(PI_CHECKSUM, PI_INFO, checksumtext)
else
checksumtext = "Checksum incorrect, should be: " .. calculated_checksum .. ", instead of: " .. checksum .. ""
subtree:add_expert_info(PI_CHECKSUM, PI_WARN, checksumtext)
end
subtree:add(SUPERFUNKYCHAT.fields.chksum, checksum)
subtree:add(SUPERFUNKYCHAT.fields.command, command)
command = command:uint()
subtree:append_text(", Command: " .. command .. " (" .. commands[command] .. ")")
pinfo.cols.info = "Command: " .. command .. " (" .. commands[command] .. ")"
-- Parse the packet further.
local datatree = subtree:add(SUPERFUNKYCHAT.fields.data, data())
local info = ""
if command == 0 then
info = parse_0(datatree, data)
elseif command == 1 then
info = parse_1(datatree, data)
elseif command == 2 then
info = parse_2(datatree, data)
elseif command == 3 then
info = parse_3(datatree, data)
elseif command == 5 then
info = parse_5(datatree, data)
elseif command == 6 then
info = parse_6(datatree, data)
elseif command == 7 then
info = parse_7(datatree, data)
end
pinfo.cols.info:append("; " .. info)
end
udp_table = DissectorTable.get("udp.port")
udp_table:add(12345, SUPERFUNKYCHAT)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment