Witness: enum witness_notifyResponse_type
[wireshark-wip.git] / tools / pkt-from-core.py
blob58934f4ffcf52293115766386b11f94b171c1d63
1 #!/usr/bin/env python
2 """
3 Retrieve a packet from a wireshark/tshark core file
4 and save it in a packet-capture file.
5 """
8 # $Id$
10 # Copyright (C) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 2
15 # of the License, or (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 import getopt
27 import os
28 import re
29 import sys
30 import tempfile
32 exec_file = None
33 core_file = None
34 output_file = None
36 verbose = 0
37 debug = 0
39 class BackTrace:
40 re_frame = re.compile(r"^#(?P<num>\d+) ")
41 re_func1 = re.compile(r"^#\d+\s+(?P<func>\w+) \(")
42 re_func2 = re.compile(r"^#\d+\s+0x[A-Fa-f\d]+ in (?P<func>\w+) \(")
44 def __init__(self, lines):
46 # In order; each item is the function name.
47 self.frames = []
48 found_non_bt_frame = 0
49 frame_will_be = 0
51 for line in lines:
52 m = self.re_frame.search(line)
53 if m:
54 # Skip the first frame that gdb shows,
55 # which is not part of the backtrace.
56 if not found_non_bt_frame:
57 found_non_bt_frame = 1
58 continue
60 # Get the frame number and make sure it's
61 # what we expect it should be.
62 frame_num = int(m.group("num"))
63 if frame_num != frame_will_be:
64 sys.exit("Found frame %d instead of %d" % \
65 (frame_num, frame_will_be))
67 # Find the function name. XXX - need to handle '???'
68 n = self.re_func1.search(line)
69 if not n:
70 n = self.re_func2.search(line)
72 if n:
73 func = n.group("func")
74 else:
75 sys.exit("Function name not found in %s" % (line,))
77 # Save the info
78 self.frames.append(func)
79 frame_will_be += 1
81 def Frames(self):
82 return self.frames
85 def HasFunction(self, func):
86 return func in self.frames
88 def Frame(self, func):
89 return self.frames.index(func)
92 # Some values from wiretap; wiretap should be a shared
93 # libray and a Python module should be created for it so
94 # this program could just write a libpcap file directly.
95 WTAP_ENCAP_PER_PACKET = -1
96 WTAP_ENCAP_UNKNOWN = 0
97 WTAP_ENCAP_ETHERNET = 1
98 WTAP_ENCAP_TOKEN_RING = 2
99 WTAP_ENCAP_SLIP = 3
100 WTAP_ENCAP_PPP = 4
101 WTAP_ENCAP_FDDI = 5
102 WTAP_ENCAP_FDDI_BITSWAPPED = 6
103 WTAP_ENCAP_RAW_IP = 7
104 WTAP_ENCAP_ARCNET = 8
105 WTAP_ENCAP_ATM_RFC1483 = 9
106 WTAP_ENCAP_LINUX_ATM_CLIP = 10
107 WTAP_ENCAP_LAPB = 11
108 WTAP_ENCAP_ATM_SNIFFER = 12
109 WTAP_ENCAP_NULL = 13
110 WTAP_ENCAP_ASCEND = 14
111 WTAP_ENCAP_LAPD = 15
112 WTAP_ENCAP_V120 = 16
113 WTAP_ENCAP_PPP_WITH_PHDR = 17
114 WTAP_ENCAP_IEEE_802_11 = 18
115 WTAP_ENCAP_SLL = 19
116 WTAP_ENCAP_FRELAY = 20
117 WTAP_ENCAP_CHDLC = 21
118 WTAP_ENCAP_CISCO_IOS = 22
119 WTAP_ENCAP_LOCALTALK = 23
120 WTAP_ENCAP_PRISM_HEADER = 24
121 WTAP_ENCAP_PFLOG = 25
122 WTAP_ENCAP_AIROPEEK = 26
123 WTAP_ENCAP_HHDLC = 27
124 # last WTAP_ENCAP_ value + 1
125 WTAP_NUM_ENCAP_TYPES = 28
127 wtap_to_pcap_map = {
128 WTAP_ENCAP_NULL : 0,
129 WTAP_ENCAP_ETHERNET : 1,
130 WTAP_ENCAP_TOKEN_RING : 6,
131 WTAP_ENCAP_ARCNET : 7,
132 WTAP_ENCAP_SLIP : 8,
133 WTAP_ENCAP_PPP : 9,
134 WTAP_ENCAP_FDDI_BITSWAPPED : 10,
135 WTAP_ENCAP_FDDI : 10,
136 WTAP_ENCAP_ATM_RFC1483 : 11,
137 WTAP_ENCAP_RAW_IP : 12,
138 WTAP_ENCAP_LINUX_ATM_CLIP : 16, # or 18, or 19...
139 WTAP_ENCAP_CHDLC : 104,
140 WTAP_ENCAP_IEEE_802_11 : 105,
141 WTAP_ENCAP_SLL : 113,
142 WTAP_ENCAP_LOCALTALK : 114,
143 WTAP_ENCAP_PFLOG : 117,
144 WTAP_ENCAP_CISCO_IOS : 118,
145 WTAP_ENCAP_PRISM_HEADER : 119,
146 WTAP_ENCAP_HHDLC : 121,
150 wtap_name = {
151 WTAP_ENCAP_UNKNOWN : "Unknown",
152 WTAP_ENCAP_ETHERNET : "Ethernet",
153 WTAP_ENCAP_TOKEN_RING : "Token-Ring",
154 WTAP_ENCAP_SLIP : "SLIP",
155 WTAP_ENCAP_PPP : "PPP",
156 WTAP_ENCAP_FDDI : "FDDI",
157 WTAP_ENCAP_FDDI_BITSWAPPED : "FDDI (Bitswapped)",
158 WTAP_ENCAP_RAW_IP : "Raw IP",
159 WTAP_ENCAP_ARCNET : "ARCNET",
160 WTAP_ENCAP_ATM_RFC1483 : "ATM RFC1483",
161 WTAP_ENCAP_LINUX_ATM_CLIP : "Linux ATM CLIP",
162 WTAP_ENCAP_LAPB : "LAPB",
163 WTAP_ENCAP_ATM_SNIFFER : "ATM Sniffer",
164 WTAP_ENCAP_NULL : "Null",
165 WTAP_ENCAP_ASCEND : "Ascend",
166 WTAP_ENCAP_LAPD : "LAPD",
167 WTAP_ENCAP_V120 : "V.120",
168 WTAP_ENCAP_PPP_WITH_PHDR : "PPP (with PHDR)",
169 WTAP_ENCAP_IEEE_802_11 : "IEEE 802.11",
170 WTAP_ENCAP_SLL : "SLL",
171 WTAP_ENCAP_FRELAY : "Frame Relay",
172 WTAP_ENCAP_CHDLC : "Cisco HDLC",
173 WTAP_ENCAP_CISCO_IOS : "Cisco IOS",
174 WTAP_ENCAP_LOCALTALK : "LocalTalk",
175 WTAP_ENCAP_PRISM_HEADER : "Prism Header",
176 WTAP_ENCAP_PFLOG : "PFLog",
177 WTAP_ENCAP_AIROPEEK : "AiroPeek",
178 WTAP_ENCAP_HHDLC : "HHDLC",
181 def wtap_to_pcap(wtap):
182 if not wtap_to_pcap_map.has_key(wtap):
183 sys.exit("Don't know how to convert wiretap encoding %d to libpcap." % \
184 (wtap))
186 return wtap_to_pcap_map[wtap]
189 def run_gdb(*commands):
190 if len(commands) == 0:
191 return []
193 # Create a temporary file
194 fname = tempfile.mktemp()
195 try:
196 fh = open(fname, "w")
197 except IOError, err:
198 sys.exit("Cannot open %s for writing: %s" % (fname, err))
200 # Put the commands in it
201 for cmd in commands:
202 fh.write(cmd)
203 fh.write("\n")
205 fh.write("quit\n")
206 try:
207 fh.close()
208 except IOError, err:
209 try:
210 os.unlink(fname)
211 except:
212 pass
213 sys.exit("Cannot close %s: %s" % (fname, err))
216 # Run gdb
217 cmd = "gdb --nw --quiet --command=%s %s %s" % (fname, exec_file, core_file)
218 if verbose:
219 print "Invoking %s" % (cmd,)
220 try:
221 pipe = os.popen(cmd)
222 except OSError, err:
223 try:
224 os.unlink(fname)
225 except:
226 pass
227 sys.exit("Cannot run gdb: %s" % (err,))
229 # Get gdb's output
230 result = pipe.readlines()
231 error = pipe.close()
232 if error != None:
233 try:
234 os.unlink(fname)
235 except:
236 pass
237 sys.exit("gdb returned an exit value of %s" % (error,))
240 # Remove the temp file and return the results
241 try:
242 os.unlink(fname)
243 except:
244 pass
245 return result
247 def get_value_from_frame(frame_num, variable, fmt=""):
248 cmds = []
249 if frame_num > 0:
250 cmds.append("up %d" % (frame_num,))
252 cmds.append("print %s %s" % (fmt, variable))
253 lines = apply(run_gdb, cmds)
255 LOOKING_FOR_START = 0
256 READING_VALUE = 1
257 state = LOOKING_FOR_START
258 result = ""
259 for line in lines:
260 if line[-1] == "\n":
261 line = line[0:-1]
262 if line[-1] == "\r":
263 line = line[0:-1]
265 if state == LOOKING_FOR_START:
266 if len(line) < 4:
267 continue
268 else:
269 if line[0:4] == "$1 =":
270 result = line[4:]
271 state = READING_VALUE
273 elif state == READING_VALUE:
274 result += line
276 return result
278 def get_int_from_frame(frame_num, variable):
279 text = get_value_from_frame(frame_num, variable)
280 try:
281 integer = int(text)
282 except ValueError:
283 sys.exit("Could not convert '%s' to integer." % (text,))
284 return integer
287 def get_byte_array_from_frame(frame_num, variable, length):
288 cmds = []
289 if frame_num > 0:
290 cmds.append("up %d" % (frame_num,))
292 cmds.append("print %s" % (variable,))
293 cmds.append("x/%dxb %s" % (length, variable))
294 lines = apply(run_gdb, cmds)
295 if debug:
296 print lines
298 bytes = []
300 LOOKING_FOR_START = 0
301 BYTES = 1
302 state = LOOKING_FOR_START
304 for line in lines:
305 if state == LOOKING_FOR_START:
306 if len(line) < 3:
307 continue
308 elif line[0:3] == "$1 ":
309 state = BYTES
310 elif state == BYTES:
311 line.rstrip()
312 fields = line.split('\t')
313 if fields[0][-1] != ":":
314 print "Failed to parse byte array from gdb:"
315 print line
316 sys.exit(1)
318 for field in fields[1:]:
319 val = int(field, 16)
320 bytes.append(val)
321 else:
322 assert 0
324 return bytes
326 def make_cap_file(pkt_data, lnk_t):
328 pcap_lnk_t = wtap_to_pcap(lnk_t)
330 # Create a temporary file
331 fname = tempfile.mktemp()
332 try:
333 fh = open(fname, "w")
334 except IOError, err:
335 sys.exit("Cannot open %s for writing: %s" % (fname, err))
337 print "Packet Data:"
339 # Put the hex dump in it
340 offset = 0
341 BYTES_IN_ROW = 16
342 for byte in pkt_data:
343 if (offset % BYTES_IN_ROW) == 0:
344 print >> fh, "\n%08X " % (offset,),
345 print "\n%08X " % (offset,),
347 print >> fh, "%02X " % (byte,),
348 print "%02X " % (byte,),
349 offset += 1
351 print >> fh, "\n"
352 print "\n"
354 try:
355 fh.close()
356 except IOError, err:
357 try:
358 os.unlink(fname)
359 except:
360 pass
361 sys.exit("Cannot close %s: %s" % (fname, err))
364 # Run text2pcap
365 cmd = "text2pcap -q -l %s %s %s" % (pcap_lnk_t, fname, output_file)
366 # print "Command is %s" % (cmd,)
367 try:
368 retval = os.system(cmd)
369 except OSError, err:
370 try:
371 os.unlink(fname)
372 except:
373 pass
374 sys.exit("Cannot run text2pcap: %s" % (err,))
376 # Remove the temp file
377 try:
378 os.unlink(fname)
379 except:
380 pass
382 if retval == 0:
383 print "%s created with %d bytes in packet, and %s encoding." % \
384 (output_file, len(pkt_data), wtap_name[lnk_t])
385 else:
386 sys.exit("text2pcap did not run succesfully.")
391 def try_frame(func_text, cap_len_text, lnk_t_text, data_text):
393 # Get the back trace
394 bt_text = run_gdb("bt")
395 bt = BackTrace(bt_text)
396 if not bt.HasFunction(func_text):
397 print "%s() not found in backtrace." % (func_text,)
398 return 0
399 else:
400 print "%s() found in backtrace." % (func_text,)
402 # Figure out where the call to epan_dissect_run is.
403 frame_num = bt.Frame(func_text)
405 # Get the capture length
406 cap_len = get_int_from_frame(frame_num, cap_len_text)
408 # Get the encoding type
409 lnk_t = get_int_from_frame(frame_num, lnk_t_text)
411 # Get the packet data
412 pkt_data = get_byte_array_from_frame(frame_num, data_text, cap_len)
414 if verbose:
415 print "Length=%d" % (cap_len,)
416 print "Encoding=%d" % (lnk_t,)
417 print "Data (%d bytes) = %s" % (len(pkt_data), pkt_data)
418 make_cap_file(pkt_data, lnk_t)
419 return 1
421 def run():
422 if try_frame("epan_dissect_run",
423 "fd->cap_len", "fd->lnk_t", "data"):
424 return
425 elif try_frame("add_packet_to_packet_list",
426 "fdata->cap_len", "fdata->lnk_t", "buf"):
427 return
428 else:
429 sys.exit("A packet cannot be pulled from this core.")
432 def usage():
433 print "pkt-from-core.py [-v] -w capture_file executable-file (core-file or process-id)"
434 print ""
435 print "\tGiven an executable file and a core file, this tool"
436 print "\tuses gdb to retrieve the packet that was being dissected"
437 print "\tat the time wireshark/tshark stopped running. The packet"
438 print "\tis saved in the capture_file specified by the -w option."
439 print ""
440 print "\t-v : verbose"
441 sys.exit(1)
443 def main():
444 global exec_file
445 global core_file
446 global output_file
447 global verbose
448 global debug
450 optstring = "dvw:"
451 try:
452 opts, args = getopt.getopt(sys.argv[1:], optstring)
453 except getopt.error:
454 usage()
456 for opt, arg in opts:
457 if opt == "-w":
458 output_file = arg
459 elif opt == "-v":
460 verbose = 1
461 elif opt == "-d":
462 debug = 1
463 else:
464 assert 0
466 if output_file == None:
467 usage()
469 if len(args) != 2:
470 usage()
472 exec_file = args[0]
473 core_file = args[1]
475 run()
477 if __name__ == '__main__':
478 main()