hex/plugins/pcap.lua
Přemysl Eric Janouch 019c4302ad
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
Handle tiny files gracefully
Lua detection functions used to cause fatal errors on failure to read.

We could also reconsider treating detection errors as fatal.
2024-12-08 22:37:12 +01:00

255 lines
6.7 KiB
Lua

--
-- pcap.lua: libpcap file format
--
-- Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
local detect = function (c)
if #c < 4 then
return false
end
local magic = c:u32 ()
return magic == 0xa1b2c3d4 or magic == 0xd4c3b2a1
end
local detect_ng = function (c)
if #c < 8 then
return false
end
local magic = c (9):u32 ()
return c:u32 () == 0x0a0d0d0a
and (magic == 0x1a2b3c4d or magic == 0x4d3c2b1a)
end
-- Specified in http://www.tcpdump.org/linktypes.html
local link_types = {
[0] = "NULL",
[1] = "ETHERNET",
[3] = "AX25",
[6] = "IEEE802_5",
[7] = "ARCNET_BSD",
[8] = "SLIP",
[9] = "PPP",
[10] = "FDDI",
[50] = "PPP_HDLC",
[51] = "PPP_ETHER",
[100] = "ATM_RFC1483",
[101] = "RAW",
[104] = "C_HDLC",
[105] = "IEEE802_11",
[107] = "FRELAY",
[108] = "LOOP",
[113] = "LINUX_SLL",
[114] = "LTALK",
[117] = "PFLOG",
[119] = "IEEE802_11_PRISM",
[122] = "IP_OVER_FC",
[123] = "SUNATM",
[127] = "IEEE802_11_RADIOTAP",
[129] = "ARCNET_LINUX",
[138] = "APPLE_IP_OVER_IEEE1394",
[139] = "MTP2_WITH_PHDR",
[140] = "MTP2",
[141] = "MTP3",
[142] = "SCCP",
[143] = "DOCSIS",
[144] = "LINUX_IRDA",
[147] = "USER0",
[148] = "USER1",
[149] = "USER2",
[150] = "USER3",
[151] = "USER4",
[152] = "USER5",
[153] = "USER6",
[154] = "USER7",
[155] = "USER8",
[156] = "USER9",
[157] = "USER10",
[158] = "USER11",
[159] = "USER12",
[160] = "USER13",
[161] = "USER14",
[162] = "USER15",
[163] = "IEEE802_11_AVS",
[165] = "BACNET_MS_TP",
[166] = "PPP_PPPD",
[169] = "GPRS_LLC",
[170] = "GPF_T",
[171] = "GPF_F",
[177] = "LINUX_LAPD",
[187] = "BLUETOOTH_HCI_H4",
[189] = "USB_LINUX",
[192] = "PPI",
[195] = "IEEE802_15_4",
[196] = "SITA",
[197] = "ERF",
[201] = "BLUETOOTH_HCI_H4_WITH_PHDR",
[202] = "AX25_KISS",
[203] = "LAPD",
[204] = "PPP_WITH_DIR",
[205] = "C_HDLC_WITH_DIR",
[206] = "FRELAY_WITH_DIR",
[209] = "IPMB_LINUX",
[215] = "IEEE802_15_4_NONASK_PHY",
[220] = "USB_LINUX_MMAPPED",
[224] = "FC_2",
[225] = "FC_2_WITH_FRAME_DELIMS",
[226] = "IPNET",
[227] = "CAN_SOCKETCAN",
[228] = "IPV4",
[229] = "IPV6",
[230] = "IEEE802_15_4_NOFCS",
[231] = "DBUS",
[235] = "DVB_CI",
[236] = "MUX27010",
[237] = "STANAG_5066_D_PDU",
[239] = "NFLOG",
[240] = "NETANALYZER",
[241] = "NETANALYZER_TRANSPARENT",
[242] = "IPOIB",
[243] = "MPEG_2_TS",
[244] = "NG40",
[245] = "NFC_LLCP",
[247] = "INFINIBAND",
[248] = "SCTP",
[249] = "USBPCAP",
[250] = "RTAC_SERIAL",
[251] = "BLUETOOTH_LE_LL",
[253] = "NETLINK",
[254] = "BLUETOOTH_LINUX_MONITOR",
[255] = "BLUETOOTH_BREDR_BB",
[256] = "BLUETOOTH_LE_LL_WITH_PHDR",
[257] = "PROFIBUS_DL",
[258] = "PKTAP",
[259] = "EPON",
[260] = "IPMI_HPM_2",
[261] = "ZWAVE_R1_R2",
[262] = "ZWAVE_R3",
[263] = "WATTSTOPPER_DLM",
[264] = "ISO_14443",
[265] = "RDS",
[266] = "USB_DARWIN"
}
-- As described by https://wiki.wireshark.org/Development/LibpcapFileFormat
local decode = function (c)
if not detect (c ()) then error ("not a PCAP file") end
c.endianity = "le"
c:u32 ("PCAP magic: %s", function (u32)
if u32 == 0xa1b2c3d4 then return "little-endian" end
c.endianity = "be"
return "big-endian"
end)
local p, vmajor, vminor = c.position, c:u16 (), c:u16 ()
c (p, c.position - 1):mark ("PCAP version: %d.%d", vmajor, vminor)
local zone = c:s32 ("UTC to local TZ correction: %d seconds")
local sigfigs = c:u32 ("timestamp accuracy")
local snaplen = c:u32 ("max. length of captured packets: %d")
local network = c:u32 ("data link type: %s", function (u32)
name = link_types[u32]
if name then return name end
return "unknown: %d", u32
end)
local i = 0
while not c.eof do
c (c.position, c.position + 15):mark ("PCAP record %d header", i)
local p, ts_sec, ts_usec = c.position, c:u32 (), c:u32 ()
c (p, c.position - 1):mark ("timestamp: %s.%06d",
os.date ("!%F %T", ts_sec + zone), ts_usec)
local incl_len = c:u32 ("included record length: %d")
local orig_len = c:u32 ("original record length: %d")
local p = c.position
c.position = c.position + incl_len
-- TODO: also decode record contents as per the huge table
c (p, c.position - 1):mark ("PCAP record %d data", i)
i = i + 1
end
end
hex.register { type="pcap", detect=detect, decode=decode }
local block_types = {
[0x0a0d0d0a] = "Section Header Block",
[0x00000001] = "Interface Description Block",
[0x00000003] = "Simple Packet Block",
[0x00000004] = "Name Resolution Block",
[0x00000005] = "Interface Statistics Block",
[0x00000006] = "Enhanced Packet Block",
[0x00000BAD] = "Custom Block",
[0x40000BAD] = "Custom Block"
}
local decode_shb = function (c)
local magic = c:u32 ()
local p, vmajor, vminor = c.position, c:u16 (), c:u16 ()
c (p, c.position - 1):mark ("PCAPNG version: %d.%d", vmajor, vminor)
-- XXX: what exactly does section_len mean?
local section_len = c:u64 ("section length: %d")
while not c.eof do
-- TODO: decode the meaning of options as well
local type = c:u16 ("option type: %d")
local length = c:u16 ("option length: %d")
local p = c.position
c.position = c.position + length + (-length & 3)
c (p, c.position - 1):mark ("option value")
end
end
local block_decoders = {
-- TODO: also decode other types of blocks
[0x0a0d0d0a] = decode_shb
}
-- As described by https://github.com/pcapng/pcapng
local decode_ng = function (c)
assert (c.position == 1)
if not detect_ng (c ()) then error ("not a PCAPNG file") end
c.endianity = "le"
c (9):u32 ("byte-order magic: %s", function (u32)
if u32 == 0x1a2b3c4d then return "little-endian" end
c.endianity = "be"
return "big-endian"
end)
while not c.eof do
local block_start = c.position
local block_type = c:u32 ("PCAPNG block type: %s", function (u32)
local name = block_types[u32]
if name then return name end
return "unknown: %d", u32
end)
local block_len = c:u32 ("PCAPNG block length: %d")
local data_start = c.position
c.position = block_start + block_len - 4
local decoder = block_decoders[block_type]
if decoder then decoder (c (data_start, c.position - 1)) end
c:u32 ("PCAPNG trailing block length: %d")
end
end
hex.register { type="pcapng", detect=detect_ng, decode=decode_ng }