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