Last active
April 8, 2022 05:54
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- 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