Skip to content

Instantly share code, notes, and snippets.

@JJTech0130
Forked from axelkar/usbmuxd.lua
Last active May 7, 2026 20:30
Show Gist options
  • Select an option

  • Save JJTech0130/da77af43269076f6ea78f69471d1df6e to your computer and use it in GitHub Desktop.

Select an option

Save JJTech0130/da77af43269076f6ea78f69471d1df6e to your computer and use it in GitHub Desktop.
Usbmuxd protocol dissector for Wireshark. Passes TCP to Wireshark's built-in TCP dissector
local proto_usbmuxd = Proto("usbmuxd", "Usbmuxd Protocol")
proto_usbmuxd.fields.protocol = ProtoField.uint32("usbmuxd.protocol", "Message Kind", base.DEC)
proto_usbmuxd.fields.length = ProtoField.uint32("usbmuxd.length", "Length", base.DEC)
proto_usbmuxd.fields.magic = ProtoField.uint32("usbmuxd.magic", "Magic", base.HEX)
proto_usbmuxd.fields.tx_seq = ProtoField.uint16("usbmuxd.tx_seq", "Transmit sequence", base.DEC)
proto_usbmuxd.fields.rx_seq = ProtoField.uint16("usbmuxd.rx_seq", "Receive sequence", base.DEC)
proto_usbmuxd.fields.version_major = ProtoField.uint32("usbmuxd.version_major", "Major version", base.DEC)
proto_usbmuxd.fields.version_minor = ProtoField.uint32("usbmuxd.version_minor", "Minor version", base.DEC)
proto_usbmuxd.fields.data = ProtoField.bytes("usbmuxd.data", "Data", base.NONE)
-- Version + packet number by device ID
local usbmuxd_version = nil
local tcp_dissector = Dissector.get("tcp")
local f_usb_ep_dir = Field.new("usb.endpoint_address.direction")
function proto_usbmuxd.dissector(buf,pinfo,tree)
pinfo.cols.protocol = proto_usbmuxd.name;
local version = 1
if usbmuxd_version and pinfo.number > usbmuxd_version.packet then
version = usbmuxd_version.version
end
local subtree = tree:add(proto_usbmuxd, buf()):append_text(" V" .. tostring(version))
mkind = buf(0, 4):uint()
local mkind_str = " (Unknown)"
if mkind == 0 then
mkind_str = " (Version)"
elseif mkind == 1 then
mkind_str = " (Control)"
elseif mkind == 2 then
mkind_str = " (Setup)"
elseif mkind == 6 then
mkind_str = " (TCP)"
end
subtree:add(proto_usbmuxd.fields.protocol, buf(0, 4)):append_text(mkind_str)
pinfo.cols.info:append(mkind_str)
subtree:add(proto_usbmuxd.fields.length, buf(4, 4))
local data_offset = 8
if version == 2 then
subtree:add(proto_usbmuxd.fields.magic, buf(8, 4))
subtree:add(proto_usbmuxd.fields.tx_seq, buf(12, 2))
subtree:add(proto_usbmuxd.fields.rx_seq, buf(14, 2))
data_offset = 16
end
if mkind == 0 then
subtree:add(proto_usbmuxd.fields.version_major, buf(data_offset, 4))
subtree:add(proto_usbmuxd.fields.version_minor, buf(data_offset+4, 4))
-- Version response comes from the device (IN endpoint)
if tostring(pinfo.dst) == "host" then
usbmuxd_version = {
version = buf(data_offset, 4):uint(),
packet = pinfo.number
}
end
elseif mkind == 1 then
subtree:add(proto_usbmuxd.fields.data, buf(data_offset))
elseif mkind == 2 then
subtree:add(proto_usbmuxd.fields.data, buf(data_offset))
elseif mkind == 6 then
-- USB addresses confuse the TCP conversation tracker. Override with stable
-- fake IPs derived from bus/device so streams are uniquely keyed per device.
-- Device IP: <bus>.<dev>.0.2, Host IP: <bus>.0.0.1
local src_str = tostring(pinfo.src)
local usb_str = src_str ~= "host" and src_str or tostring(pinfo.dst)
local bus, dev = usb_str:match("^(%d+)%.(%d+)%.")
if bus then
local ep_dir = f_usb_ep_dir()
local dev_ip = bus .. "." .. dev .. ".0.2"
local host_ip = bus .. ".0.0.1"
-- use the endpoint direction bit to determine actual flow direction.
pinfo.src = Address.ip(ep_dir and ep_dir.value == 1 and dev_ip or host_ip)
pinfo.dst = Address.ip(ep_dir and ep_dir.value == 1 and host_ip or dev_ip)
pinfo.net_src = pinfo.src
pinfo.net_dst = pinfo.dst
end
tcp_dissector:call(buf(data_offset):tvb(), pinfo, tree)
else
subtree:add(proto_usbmuxd.fields.data, buf(data_offset))
end
end
local usb_bulk_table = DissectorTable.get("usb.bulk")
usb_bulk_table:add(0xff, proto_usbmuxd)
usb_bulk_table:add(0xffff, proto_usbmuxd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment