Binkp dissector for Wireshark
From
Dmitry Protasoff@2:5001/100.1 to
All on Sat Jun 28 22:10:39 2025
Hello, All!
� ����-� ��ᯨ稫�, ��� ���, 㤮��� ᬮ���� �� ��䨪 � wireshark, � ��� lua �ਯ�:
-+-
-- Binkp Protocol Dissector for Wireshark
-- Version: 2025-06-28
-- ====== Command/Option Tables ======
local binkp_commands = {
[0x00] = "M_NUL", [0x01] = "M_ADR", [0x02] = "M_PWD", [0x03] = "M_FILE",
[0x04] = "M_OK", [0x05] = "M_EOB", [0x06] = "M_GOT", [0x07] = "M_ERR",
[0x08] = "M_BSY", [0x09] = "M_GET", [0x0A] = "M_SKIP", [0x0B] = "M_CHAT",
[0x0C] = "M_OPM", [0x0D] = "M_OPT", [0x0E] = "M_VER", [0x0F] = "M_EXTCMD"
}
local binkp_options = {
["CRAM-MD5"] = "CRAM-MD5 authentication", ["CRYPT"] = "Encryption support",
["ND"] = "No dupes mode", ["NR"] = "Non-reliable mode", ["CHAT"] = "Chat capability",
["ZIPPER"] = "On-the-fly compression", ["ZLIB"] = "ZLIB compression",
["BZP2"] = "BZIP2 compression", ["GZ"] = "GZIP compression", ["NDA"] = "No dupes asymmetric",
["EXTCMD"] = "Extended commands support", ["CHUNKING"] = "File chunking support",
["CRAM-SHA1"] = "CRAM-SHA1 authentication", ["CRAM-SHA256"] = "CRAM-SHA256 authentication"
}
-- ====== Protocol Registration ======
local binkp_proto = Proto("BINKP", "BinkP Protocol")
local fields = binkp_proto.fields
fields.header = ProtoField.uint16("binkp.header", "Header", base.HEX) fields.command_flag = ProtoField.bool("binkp.command", "Command Flag", 16, nil, 0x8000)
fields.length = ProtoField.uint16("binkp.length", "Length", base.DEC, nil, 0x7FFF)
fields.command = ProtoField.uint8("binkp.cmd", "Command", base.HEX) fields.arguments = ProtoField.string("binkp.args", "Arguments")
fields.data = ProtoField.bytes("binkp.data", "Data")
-- ====== Utility Functions ======
local function bit_and(a, b)
if bit32 then return bit32.band(a, b)
elseif bit then return bit.band(a, b)
else return a & b
end
end
local function safe_buffer_read(buffer, offset, length)
if offset < 0 or length < 0 or offset + length > buffer:len() then return nil end
return buffer(offset, length)
end
-- ====== Argument Parsing ======
local function format_command_args(cmd, args)
if not args or args == "" then return "" end
if cmd == 0x01 then -- M_ADR
return "Addresses: " .. args
elseif cmd == 0x03 then -- M_FILE
return "File: " .. args
elseif cmd == 0x0D then -- M_OPT
local opts, descs = {}, {}
for opt in args:gmatch("%S+") do
table.insert(opts, opt)
table.insert(descs, opt .. " (" .. (binkp_options[opt] or "Unknown") .. ")")
end
return "Options: " .. table.concat(descs, ", ")
elseif cmd == 0x0E then -- M_VER
return "Version: " .. args
else
return args
end
end
-- ====== Main Dissector ======
function binkp_proto.dissector(buffer, pinfo, tree)
if buffer:len() == 0 then return end
pinfo.cols.protocol = "Binkp"
local subtree = tree:add(binkp_proto, buffer(), "Binkp Protocol Data")
local offset = 0
local count = 0
while offset + 2 <= buffer:len() do
local hdrbuf = safe_buffer_read(buffer, offset, 2)
if not hdrbuf then break end
local header = hdrbuf:uint()
local is_command = bit_and(header, 0x8000) ~= 0
local packet_length = bit_and(header, 0x7FFF)
if offset + 2 + packet_length > buffer:len() then
-- Not enough data for full packet; add as raw data and stop
subtree:add(fields.data, buffer(offset, buffer:len()-offset))
break
end
local packet_tree = subtree:add(buffer(offset, 2 + packet_length), string.format(
"Binkp %s (Length: %d)", is_command and "Command" or "Data", packet_length))
packet_tree:add(fields.header, buffer(offset, 2))
packet_tree:add(fields.command_flag, buffer(offset, 2))
packet_tree:add(fields.length, buffer(offset, 2))
offset = offset + 2
if is_command and packet_length > 0 then
local cmdbuf = safe_buffer_read(buffer, offset, 1)
local cmd = cmdbuf and cmdbuf:uint() or 0
local cmd_name = binkp_commands[cmd] or string.format("Unknown (0x%02X)", cmd)
packet_tree:add(fields.command, buffer(offset, 1)):append_text(" (" .. cmd_name .. ")")
if packet_length > 1 then
local argsbuf = safe_buffer_read(buffer, offset + 1, packet_length - 1)
local args = argsbuf and argsbuf:string() or ""
packet_tree:add(fields.arguments, buffer(offset + 1, packet_length - 1)):set_text(format_command_args(cmd, args))
if count == 0 then
pinfo.cols.info = string.format("Command: %s (%s)", cmd_name, args)
end
else
if count == 0 then
pinfo.cols.info = string.format("Command: %s", cmd_name)
end
end
elseif not is_command and packet_length > 0 then
packet_tree:add(fields.data, buffer(offset, packet_length))
if count == 0 then
pinfo.cols.info = string.format("Data (%d bytes)", packet_length)
end
end
offset = offset + packet_length
count = count + 1
if count > 100 then break end
end
end
-- ====== Heuristic Registration ======
local function heuristic_checker(buffer, pinfo, tree)
if pinfo.dst_port == 24554 or pinfo.src_port == 24554 then
return false -- let static registration handle
end
if buffer:len() >= 3 then
local header = buffer(0, 2):uint()
local is_command = bit_and(header, 0x8000) ~= 0
local length = bit_and(header, 0x7FFF)
if length <= buffer:len() - 2 and length < 32768 then
if is_command and length > 0 then
local cmd = buffer(2, 1):uint()
if binkp_commands[cmd] then
binkp_proto.dissector(buffer, pinfo, tree)
return true
end
elseif not is_command then
binkp_proto.dissector(buffer, pinfo, tree)
return true
end
end
end
return false
end
-- ====== Registration ======
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(24554, binkp_proto) -- Only static registration for standard port binkp_proto:register_heuristic("tcp", heuristic_checker) tcp_port:add_for_decode_as(binkp_proto)
Best regards,
dp.
--- GoldED+/OSX 1.1.5-b20250409
* Origin: All is good in St. John's Wood (2:5001/100.1)