dump packets in memory buffers with tshark without leaving gdb


I got tired of running my error-prone eyeball packet decoder on GDB buffers, so I took a few minutes to figure out how to send packet data to tshark to let the computer do what it’s good at. Here’s the ~/.gdbinit snippet if you want to use it:

define pktdump
   dump binary memory /tmp/dgoodell-pktdump.bin (char*)$arg0 (((char*)$arg0)+$arg1)
   shell od -Ax -tx1 -v /tmp/dgoodell-pktdump.bin | text2pcap - - | tshark -V -x -i -
end
document pktdump
Dump a network packet in memory with tshark.
Example usage: pktdump ADDRESS LENGTH
end

(obviously change “dgoodell” to your user name or stick the file in your home directory, environment variables don’t work in GDB, unfortunately) The results look like:

(gdb) x/16xb hdr_buf
0x60f5d0:       0xfc    0x99    0x47    0x25    0x2c    0x13    0xfc    0x99
0x60f5d8:       0x47    0x25    0x42    0xbd    0x08    0x00    0x45    0x88
(gdb) help pktdump
Dump a network packet in memory with tshark.
Example usage: pktdump ADDRESS LENGTH
(gdb) pktdump hdr_buf 64
Input from: Standard input
Output to: Standard output
Wrote packet of 64 bytes at 0
Read 1 potential packet, wrote 1 packet
Capturing on Standard input
Frame 1: 64 bytes on wire (512 bits), 64 bytes captured (512 bits) on interface 0
   Interface id: 0
   WTAP_ENCAP: 1
   Arrival Time: Oct  1, 2014 14:46:42.000000000 PDT
   [Time shift for this packet: 0.000000000 seconds]
   Epoch Time: 1412200002.000000000 seconds
   [Time delta from previous captured frame: 0.000000000 seconds]
   [Time delta from previous displayed frame: 0.000000000 seconds]
   [Time since reference or first frame: 0.000000000 seconds]
   Frame Number: 1
   Frame Length: 64 bytes (512 bits)
   Capture Length: 64 bytes (512 bits)
   [Frame is marked: False]
   [Frame is ignored: False]
   [Protocols in frame: eth:ip:udp:data]
Ethernet II, Src: Cisco_25:42:bd (fc:99:47:25:42:bd), Dst: Cisco_25:2c:13 (fc:99:47:25:2c:13)
   Destination: Cisco_25:2c:13 (fc:99:47:25:2c:13)
       Address: Cisco_25:2c:13 (fc:99:47:25:2c:13)
       .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
       .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
   Source: Cisco_25:42:bd (fc:99:47:25:42:bd)
       Address: Cisco_25:42:bd (fc:99:47:25:42:bd)
       .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
       .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
   Type: IP (0x0800)
   Padding: 0000
   Frame check sequence: 0x00000000 [incorrect, should be 0x2cf4146a]
       [FCS Good: False]
       [FCS Bad: True]
           [Expert Info (Error/Checksum): Bad checksum]
               [Message: Bad checksum]
               [Severity level: Error]
               [Group: Checksum]
Internet Protocol Version 4, Src: 10.10.0.1 (10.10.0.1), Dst: 10.10.0.2 (10.10.0.2)
   Version: 4
   Header length: 20 bytes
   Differentiated Services Field: 0x88 (DSCP 0x22: Assured Forwarding 41; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
       1000 10.. = Differentiated Services Codepoint: Assured Forwarding 41 (0x22)
       .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
   Total Length: 44
   Identification: 0x0000 (0)
   Flags: 0x00
       0... .... = Reserved bit: Not set
       .0.. .... = Don't fragment: Not set
       ..0. .... = More fragments: Not set
   Fragment offset: 0
   Time to live: 8
   Protocol: UDP (17)
   Header checksum: 0x9e23 [correct]
       [Good: True]
       [Bad: False]
   Source: 10.10.0.1 (10.10.0.1)
   Destination: 10.10.0.2 (10.10.0.2)
User Datagram Protocol, Src Port: 34066 (34066), Dst Port: 47559 (47559)
   Source port: 34066 (34066)
   Destination port: 47559 (47559)
   Length: 24
   Checksum: 0xf4fe [validation disabled]
       [Good Checksum: False]
       [Bad Checksum: False]
Data (16 bytes)
   Data: 00000000000000000000000000000000
   [Length: 16]

0000  fc 99 47 25 2c 13 fc 99 47 25 42 bd 08 00 45 88   ..G%,...G%B...E.
0010  00 2c 00 00 00 00 08 11 9e 23 0a 0a 00 01 0a 0a   .,.......#......
0020  00 02 85 12 b9 c7 00 18 f4 fe 00 00 00 00 00 00   ................
0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

That’s also way easier to read on x86 than casting to a struct ether_header *, etc. and dereferencing, especially on x86 because all the fields end up displayed in the wrong endianness.

Addendum 2017-05-07

If you’ve got GDB ≥v7.0 (and therefore python support), you can get around the lack of simple environment variable support with this version of the snippet:

python
import os

class PktDump (gdb.Command):
  """Dump a network packet in memory with tshark.
Example usage: pktdump ADDRESS LENGTH"""

  def __init__ (self):
    super (PktDump, self).__init__ ("pktdump", gdb.COMMAND_DATA)

  def invoke (self, arg, from_tty):
    argv = gdb.string_to_argv(arg)
    tmpfile = '/tmp/' + os.environ['USER'] + '-pktdump.bin'
    gdb.execute('dump binary memory ' + tmpfile + ' (char*)' + argv[0] + ' (((char*)' + argv[0] + ')+' + argv[1] + ')')
    gdb.execute('shell od -Ax -tx1 -v ' + tmpfile + ' | text2pcap - - | tshark -V -x -i -')

PktDump()
end