![Jeff Morriss](/assets/img/avatar_default.png)
There are a few things in here which could still use attention. Don't regenerate anything now. Change-Id: I283c224d3523212144707fca3d6265916cb11792 Reviewed-on: https://code.wireshark.org/review/205 Reviewed-by: Jeff Morriss <jeff.morriss.ws@gmail.com>
488 lines
14 KiB
Python
Executable file
488 lines
14 KiB
Python
Executable file
#!/usr/bin/env python
|
|
"""
|
|
Retrieve a packet from a wireshark/tshark core file
|
|
and save it in a packet-capture file.
|
|
"""
|
|
|
|
# Copyright (C) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import getopt
|
|
import os
|
|
import re
|
|
import sys
|
|
import tempfile
|
|
|
|
exec_file = None
|
|
core_file = None
|
|
output_file = None
|
|
|
|
verbose = 0
|
|
debug = 0
|
|
|
|
class BackTrace:
|
|
re_frame = re.compile(r"^#(?P<num>\d+) ")
|
|
re_func1 = re.compile(r"^#\d+\s+(?P<func>\w+) \(")
|
|
re_func2 = re.compile(r"^#\d+\s+0x[A-Fa-f\d]+ in (?P<func>\w+) \(")
|
|
|
|
def __init__(self, lines):
|
|
|
|
# In order; each item is the function name.
|
|
self.frames = []
|
|
found_non_bt_frame = 0
|
|
frame_will_be = 0
|
|
|
|
for line in lines:
|
|
m = self.re_frame.search(line)
|
|
if m:
|
|
# Skip the first frame that gdb shows,
|
|
# which is not part of the backtrace.
|
|
if not found_non_bt_frame:
|
|
found_non_bt_frame = 1
|
|
continue
|
|
|
|
# Get the frame number and make sure it's
|
|
# what we expect it should be.
|
|
frame_num = int(m.group("num"))
|
|
if frame_num != frame_will_be:
|
|
sys.exit("Found frame %d instead of %d" % \
|
|
(frame_num, frame_will_be))
|
|
|
|
# Find the function name. XXX - need to handle '???'
|
|
n = self.re_func1.search(line)
|
|
if not n:
|
|
n = self.re_func2.search(line)
|
|
|
|
if n:
|
|
func = n.group("func")
|
|
else:
|
|
sys.exit("Function name not found in %s" % (line,))
|
|
|
|
# Save the info
|
|
self.frames.append(func)
|
|
frame_will_be += 1
|
|
|
|
def Frames(self):
|
|
return self.frames
|
|
|
|
|
|
def HasFunction(self, func):
|
|
return func in self.frames
|
|
|
|
def Frame(self, func):
|
|
return self.frames.index(func)
|
|
|
|
|
|
# Some values from wiretap; wiretap should be a shared
|
|
# libray and a Python module should be created for it so
|
|
# this program could just write a libpcap file directly.
|
|
WTAP_ENCAP_PER_PACKET = -1
|
|
WTAP_ENCAP_UNKNOWN = 0
|
|
WTAP_ENCAP_ETHERNET = 1
|
|
WTAP_ENCAP_TOKEN_RING = 2
|
|
WTAP_ENCAP_SLIP = 3
|
|
WTAP_ENCAP_PPP = 4
|
|
WTAP_ENCAP_FDDI = 5
|
|
WTAP_ENCAP_FDDI_BITSWAPPED = 6
|
|
WTAP_ENCAP_RAW_IP = 7
|
|
WTAP_ENCAP_ARCNET = 8
|
|
WTAP_ENCAP_ATM_RFC1483 = 9
|
|
WTAP_ENCAP_LINUX_ATM_CLIP = 10
|
|
WTAP_ENCAP_LAPB = 11
|
|
WTAP_ENCAP_ATM_SNIFFER = 12
|
|
WTAP_ENCAP_NULL = 13
|
|
WTAP_ENCAP_ASCEND = 14
|
|
WTAP_ENCAP_LAPD = 15
|
|
WTAP_ENCAP_V120 = 16
|
|
WTAP_ENCAP_PPP_WITH_PHDR = 17
|
|
WTAP_ENCAP_IEEE_802_11 = 18
|
|
WTAP_ENCAP_SLL = 19
|
|
WTAP_ENCAP_FRELAY = 20
|
|
WTAP_ENCAP_CHDLC = 21
|
|
WTAP_ENCAP_CISCO_IOS = 22
|
|
WTAP_ENCAP_LOCALTALK = 23
|
|
WTAP_ENCAP_PRISM_HEADER = 24
|
|
WTAP_ENCAP_PFLOG = 25
|
|
WTAP_ENCAP_AIROPEEK = 26
|
|
WTAP_ENCAP_HHDLC = 27
|
|
# last WTAP_ENCAP_ value + 1
|
|
WTAP_NUM_ENCAP_TYPES = 28
|
|
|
|
wtap_to_pcap_map = {
|
|
WTAP_ENCAP_NULL : 0,
|
|
WTAP_ENCAP_ETHERNET : 1,
|
|
WTAP_ENCAP_TOKEN_RING : 6,
|
|
WTAP_ENCAP_ARCNET : 7,
|
|
WTAP_ENCAP_SLIP : 8,
|
|
WTAP_ENCAP_PPP : 9,
|
|
WTAP_ENCAP_FDDI_BITSWAPPED : 10,
|
|
WTAP_ENCAP_FDDI : 10,
|
|
WTAP_ENCAP_ATM_RFC1483 : 11,
|
|
WTAP_ENCAP_RAW_IP : 12,
|
|
WTAP_ENCAP_LINUX_ATM_CLIP : 16, # or 18, or 19...
|
|
WTAP_ENCAP_CHDLC : 104,
|
|
WTAP_ENCAP_IEEE_802_11 : 105,
|
|
WTAP_ENCAP_SLL : 113,
|
|
WTAP_ENCAP_LOCALTALK : 114,
|
|
WTAP_ENCAP_PFLOG : 117,
|
|
WTAP_ENCAP_CISCO_IOS : 118,
|
|
WTAP_ENCAP_PRISM_HEADER : 119,
|
|
WTAP_ENCAP_HHDLC : 121,
|
|
}
|
|
|
|
|
|
wtap_name = {
|
|
WTAP_ENCAP_UNKNOWN : "Unknown",
|
|
WTAP_ENCAP_ETHERNET : "Ethernet",
|
|
WTAP_ENCAP_TOKEN_RING : "Token-Ring",
|
|
WTAP_ENCAP_SLIP : "SLIP",
|
|
WTAP_ENCAP_PPP : "PPP",
|
|
WTAP_ENCAP_FDDI : "FDDI",
|
|
WTAP_ENCAP_FDDI_BITSWAPPED : "FDDI (Bitswapped)",
|
|
WTAP_ENCAP_RAW_IP : "Raw IP",
|
|
WTAP_ENCAP_ARCNET : "ARCNET",
|
|
WTAP_ENCAP_ATM_RFC1483 : "ATM RFC1483",
|
|
WTAP_ENCAP_LINUX_ATM_CLIP : "Linux ATM CLIP",
|
|
WTAP_ENCAP_LAPB : "LAPB",
|
|
WTAP_ENCAP_ATM_SNIFFER : "ATM Sniffer",
|
|
WTAP_ENCAP_NULL : "Null",
|
|
WTAP_ENCAP_ASCEND : "Ascend",
|
|
WTAP_ENCAP_LAPD : "LAPD",
|
|
WTAP_ENCAP_V120 : "V.120",
|
|
WTAP_ENCAP_PPP_WITH_PHDR : "PPP (with PHDR)",
|
|
WTAP_ENCAP_IEEE_802_11 : "IEEE 802.11",
|
|
WTAP_ENCAP_SLL : "SLL",
|
|
WTAP_ENCAP_FRELAY : "Frame Relay",
|
|
WTAP_ENCAP_CHDLC : "Cisco HDLC",
|
|
WTAP_ENCAP_CISCO_IOS : "Cisco IOS",
|
|
WTAP_ENCAP_LOCALTALK : "LocalTalk",
|
|
WTAP_ENCAP_PRISM_HEADER : "Prism Header",
|
|
WTAP_ENCAP_PFLOG : "PFLog",
|
|
WTAP_ENCAP_AIROPEEK : "AiroPeek",
|
|
WTAP_ENCAP_HHDLC : "HHDLC",
|
|
}
|
|
|
|
def wtap_to_pcap(wtap):
|
|
if not wtap_to_pcap_map.has_key(wtap):
|
|
sys.exit("Don't know how to convert wiretap encoding %d to libpcap." % \
|
|
(wtap))
|
|
|
|
return wtap_to_pcap_map[wtap]
|
|
|
|
|
|
def run_gdb(*commands):
|
|
if len(commands) == 0:
|
|
return []
|
|
|
|
# Create a temporary file
|
|
fname = tempfile.mktemp()
|
|
try:
|
|
fh = open(fname, "w")
|
|
except IOError, err:
|
|
sys.exit("Cannot open %s for writing: %s" % (fname, err))
|
|
|
|
# Put the commands in it
|
|
for cmd in commands:
|
|
fh.write(cmd)
|
|
fh.write("\n")
|
|
|
|
fh.write("quit\n")
|
|
try:
|
|
fh.close()
|
|
except IOError, err:
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
sys.exit("Cannot close %s: %s" % (fname, err))
|
|
|
|
|
|
# Run gdb
|
|
cmd = "gdb --nw --quiet --command=%s %s %s" % (fname, exec_file, core_file)
|
|
if verbose:
|
|
print "Invoking %s" % (cmd,)
|
|
try:
|
|
pipe = os.popen(cmd)
|
|
except OSError, err:
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
sys.exit("Cannot run gdb: %s" % (err,))
|
|
|
|
# Get gdb's output
|
|
result = pipe.readlines()
|
|
error = pipe.close()
|
|
if error != None:
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
sys.exit("gdb returned an exit value of %s" % (error,))
|
|
|
|
|
|
# Remove the temp file and return the results
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
return result
|
|
|
|
def get_value_from_frame(frame_num, variable, fmt=""):
|
|
cmds = []
|
|
if frame_num > 0:
|
|
cmds.append("up %d" % (frame_num,))
|
|
|
|
cmds.append("print %s %s" % (fmt, variable))
|
|
lines = apply(run_gdb, cmds)
|
|
|
|
LOOKING_FOR_START = 0
|
|
READING_VALUE = 1
|
|
state = LOOKING_FOR_START
|
|
result = ""
|
|
for line in lines:
|
|
if line[-1] == "\n":
|
|
line = line[0:-1]
|
|
if line[-1] == "\r":
|
|
line = line[0:-1]
|
|
|
|
if state == LOOKING_FOR_START:
|
|
if len(line) < 4:
|
|
continue
|
|
else:
|
|
if line[0:4] == "$1 =":
|
|
result = line[4:]
|
|
state = READING_VALUE
|
|
|
|
elif state == READING_VALUE:
|
|
result += line
|
|
|
|
return result
|
|
|
|
def get_int_from_frame(frame_num, variable):
|
|
text = get_value_from_frame(frame_num, variable)
|
|
try:
|
|
integer = int(text)
|
|
except ValueError:
|
|
sys.exit("Could not convert '%s' to integer." % (text,))
|
|
return integer
|
|
|
|
|
|
def get_byte_array_from_frame(frame_num, variable, length):
|
|
cmds = []
|
|
if frame_num > 0:
|
|
cmds.append("up %d" % (frame_num,))
|
|
|
|
cmds.append("print %s" % (variable,))
|
|
cmds.append("x/%dxb %s" % (length, variable))
|
|
lines = apply(run_gdb, cmds)
|
|
if debug:
|
|
print lines
|
|
|
|
bytes = []
|
|
|
|
LOOKING_FOR_START = 0
|
|
BYTES = 1
|
|
state = LOOKING_FOR_START
|
|
|
|
for line in lines:
|
|
if state == LOOKING_FOR_START:
|
|
if len(line) < 3:
|
|
continue
|
|
elif line[0:3] == "$1 ":
|
|
state = BYTES
|
|
elif state == BYTES:
|
|
line.rstrip()
|
|
fields = line.split('\t')
|
|
if fields[0][-1] != ":":
|
|
print "Failed to parse byte array from gdb:"
|
|
print line
|
|
sys.exit(1)
|
|
|
|
for field in fields[1:]:
|
|
val = int(field, 16)
|
|
bytes.append(val)
|
|
else:
|
|
assert 0
|
|
|
|
return bytes
|
|
|
|
def make_cap_file(pkt_data, lnk_t):
|
|
|
|
pcap_lnk_t = wtap_to_pcap(lnk_t)
|
|
|
|
# Create a temporary file
|
|
fname = tempfile.mktemp()
|
|
try:
|
|
fh = open(fname, "w")
|
|
except IOError, err:
|
|
sys.exit("Cannot open %s for writing: %s" % (fname, err))
|
|
|
|
print "Packet Data:"
|
|
|
|
# Put the hex dump in it
|
|
offset = 0
|
|
BYTES_IN_ROW = 16
|
|
for byte in pkt_data:
|
|
if (offset % BYTES_IN_ROW) == 0:
|
|
print >> fh, "\n%08X " % (offset,),
|
|
print "\n%08X " % (offset,),
|
|
|
|
print >> fh, "%02X " % (byte,),
|
|
print "%02X " % (byte,),
|
|
offset += 1
|
|
|
|
print >> fh, "\n"
|
|
print "\n"
|
|
|
|
try:
|
|
fh.close()
|
|
except IOError, err:
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
sys.exit("Cannot close %s: %s" % (fname, err))
|
|
|
|
|
|
# Run text2pcap
|
|
cmd = "text2pcap -q -l %s %s %s" % (pcap_lnk_t, fname, output_file)
|
|
# print "Command is %s" % (cmd,)
|
|
try:
|
|
retval = os.system(cmd)
|
|
except OSError, err:
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
sys.exit("Cannot run text2pcap: %s" % (err,))
|
|
|
|
# Remove the temp file
|
|
try:
|
|
os.unlink(fname)
|
|
except:
|
|
pass
|
|
|
|
if retval == 0:
|
|
print "%s created with %d bytes in packet, and %s encoding." % \
|
|
(output_file, len(pkt_data), wtap_name[lnk_t])
|
|
else:
|
|
sys.exit("text2pcap did not run succesfully.")
|
|
|
|
|
|
|
|
|
|
def try_frame(func_text, cap_len_text, lnk_t_text, data_text):
|
|
|
|
# Get the back trace
|
|
bt_text = run_gdb("bt")
|
|
bt = BackTrace(bt_text)
|
|
if not bt.HasFunction(func_text):
|
|
print "%s() not found in backtrace." % (func_text,)
|
|
return 0
|
|
else:
|
|
print "%s() found in backtrace." % (func_text,)
|
|
|
|
# Figure out where the call to epan_dissect_run is.
|
|
frame_num = bt.Frame(func_text)
|
|
|
|
# Get the capture length
|
|
cap_len = get_int_from_frame(frame_num, cap_len_text)
|
|
|
|
# Get the encoding type
|
|
lnk_t = get_int_from_frame(frame_num, lnk_t_text)
|
|
|
|
# Get the packet data
|
|
pkt_data = get_byte_array_from_frame(frame_num, data_text, cap_len)
|
|
|
|
if verbose:
|
|
print "Length=%d" % (cap_len,)
|
|
print "Encoding=%d" % (lnk_t,)
|
|
print "Data (%d bytes) = %s" % (len(pkt_data), pkt_data)
|
|
make_cap_file(pkt_data, lnk_t)
|
|
return 1
|
|
|
|
def run():
|
|
if try_frame("epan_dissect_run",
|
|
"fd->cap_len", "fd->lnk_t", "data"):
|
|
return
|
|
elif try_frame("add_packet_to_packet_list",
|
|
"fdata->cap_len", "fdata->lnk_t", "buf"):
|
|
return
|
|
else:
|
|
sys.exit("A packet cannot be pulled from this core.")
|
|
|
|
|
|
def usage():
|
|
print "pkt-from-core.py [-v] -w capture_file executable-file (core-file or process-id)"
|
|
print ""
|
|
print "\tGiven an executable file and a core file, this tool"
|
|
print "\tuses gdb to retrieve the packet that was being dissected"
|
|
print "\tat the time wireshark/tshark stopped running. The packet"
|
|
print "\tis saved in the capture_file specified by the -w option."
|
|
print ""
|
|
print "\t-v : verbose"
|
|
sys.exit(1)
|
|
|
|
def main():
|
|
global exec_file
|
|
global core_file
|
|
global output_file
|
|
global verbose
|
|
global debug
|
|
|
|
optstring = "dvw:"
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:], optstring)
|
|
except getopt.error:
|
|
usage()
|
|
|
|
for opt, arg in opts:
|
|
if opt == "-w":
|
|
output_file = arg
|
|
elif opt == "-v":
|
|
verbose = 1
|
|
elif opt == "-d":
|
|
debug = 1
|
|
else:
|
|
assert 0
|
|
|
|
if output_file == None:
|
|
usage()
|
|
|
|
if len(args) != 2:
|
|
usage()
|
|
|
|
exec_file = args[0]
|
|
core_file = args[1]
|
|
|
|
run()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
#
|
|
# Editor modelines - http://www.wireshark.org/tools/modelines.html
|
|
#
|
|
# Local variables:
|
|
# c-basic-offset: 4
|
|
# indent-tabs-mode: nil
|
|
# End:
|
|
#
|
|
# vi: set shiftwidth=4 expandtab:
|
|
# :indentSize=4:noTabs=true:
|
|
#
|