-- -- pcap.lua: libpcap file format -- -- Copyright (c) 2017, Přemysl Eric Janouch -- -- 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 }