regen pidl all: rm epan/dissectors/pidl/*-stamp; pushd epan/dissectors/pidl/ && make...
[wireshark-sm.git] / tools / pkt-from-core.py
blobefc252d369a36bd1f540f7212a41590f71e4de39
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 """
7 # Copyright (C) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
9 # SPDX-License-Identifier: GPL-2.0-or-later
11 import getopt
12 import os
13 import re
14 import sys
15 import tempfile
17 exec_file = None
18 core_file = None
19 output_file = None
21 verbose = 0
22 debug = 0
24 class BackTrace:
25 re_frame = re.compile(r"^#(?P<num>\d+) ")
26 re_func1 = re.compile(r"^#\d+\s+(?P<func>\w+) \(")
27 re_func2 = re.compile(r"^#\d+\s+0x[A-Fa-f\d]+ in (?P<func>\w+) \(")
29 def __init__(self, lines):
31 # In order; each item is the function name.
32 self.frames = []
33 found_non_bt_frame = 0
34 frame_will_be = 0
36 for line in lines:
37 m = self.re_frame.search(line)
38 if m:
39 # Skip the first frame that gdb shows,
40 # which is not part of the backtrace.
41 if not found_non_bt_frame:
42 found_non_bt_frame = 1
43 continue
45 # Get the frame number and make sure it's
46 # what we expect it should be.
47 frame_num = int(m.group("num"))
48 if frame_num != frame_will_be:
49 sys.exit("Found frame %d instead of %d" %
50 (frame_num, frame_will_be))
52 # Find the function name. XXX - need to handle '???'
53 n = self.re_func1.search(line)
54 if not n:
55 n = self.re_func2.search(line)
57 if n:
58 func = n.group("func")
59 else:
60 sys.exit("Function name not found in %s" % (line,))
62 # Save the info
63 self.frames.append(func)
64 frame_will_be += 1
66 def Frames(self):
67 return self.frames
70 def HasFunction(self, func):
71 return func in self.frames
73 def Frame(self, func):
74 return self.frames.index(func)
77 # Some values from wiretap; wiretap should be a shared
78 # libray and a Python module should be created for it so
79 # this program could just write a libpcap file directly.
80 WTAP_ENCAP_NONE = -2
81 WTAP_ENCAP_PER_PACKET = -1
82 WTAP_ENCAP_UNKNOWN = 0
83 WTAP_ENCAP_ETHERNET = 1
84 WTAP_ENCAP_TOKEN_RING = 2
85 WTAP_ENCAP_SLIP = 3
86 WTAP_ENCAP_PPP = 4
87 WTAP_ENCAP_FDDI = 5
88 WTAP_ENCAP_FDDI_BITSWAPPED = 6
89 WTAP_ENCAP_RAW_IP = 7
90 WTAP_ENCAP_ARCNET = 8
91 WTAP_ENCAP_ATM_RFC1483 = 9
92 WTAP_ENCAP_LINUX_ATM_CLIP = 10
93 WTAP_ENCAP_LAPB = 11
94 WTAP_ENCAP_ATM_SNIFFER = 12
95 WTAP_ENCAP_NULL = 13
96 WTAP_ENCAP_ASCEND = 14
97 WTAP_ENCAP_LAPD = 15
98 WTAP_ENCAP_V120 = 16
99 WTAP_ENCAP_PPP_WITH_PHDR = 17
100 WTAP_ENCAP_IEEE_802_11 = 18
101 WTAP_ENCAP_SLL = 19
102 WTAP_ENCAP_FRELAY = 20
103 WTAP_ENCAP_CHDLC = 21
104 WTAP_ENCAP_CISCO_IOS = 22
105 WTAP_ENCAP_LOCALTALK = 23
106 WTAP_ENCAP_PRISM_HEADER = 24
107 WTAP_ENCAP_PFLOG = 25
108 WTAP_ENCAP_AIROPEEK = 26
109 WTAP_ENCAP_HHDLC = 27
110 # last WTAP_ENCAP_ value + 1
111 WTAP_NUM_ENCAP_TYPES = 28
113 wtap_to_pcap_map = {
114 WTAP_ENCAP_NULL : 0,
115 WTAP_ENCAP_ETHERNET : 1,
116 WTAP_ENCAP_TOKEN_RING : 6,
117 WTAP_ENCAP_ARCNET : 7,
118 WTAP_ENCAP_SLIP : 8,
119 WTAP_ENCAP_PPP : 9,
120 WTAP_ENCAP_FDDI_BITSWAPPED : 10,
121 WTAP_ENCAP_FDDI : 10,
122 WTAP_ENCAP_ATM_RFC1483 : 11,
123 WTAP_ENCAP_RAW_IP : 12,
124 WTAP_ENCAP_LINUX_ATM_CLIP : 16, # or 18, or 19...
125 WTAP_ENCAP_CHDLC : 104,
126 WTAP_ENCAP_IEEE_802_11 : 105,
127 WTAP_ENCAP_SLL : 113,
128 WTAP_ENCAP_LOCALTALK : 114,
129 WTAP_ENCAP_PFLOG : 117,
130 WTAP_ENCAP_CISCO_IOS : 118,
131 WTAP_ENCAP_PRISM_HEADER : 119,
132 WTAP_ENCAP_HHDLC : 121,
136 wtap_name = {
137 WTAP_ENCAP_NONE : "None",
138 WTAP_ENCAP_UNKNOWN : "Unknown",
139 WTAP_ENCAP_ETHERNET : "Ethernet",
140 WTAP_ENCAP_TOKEN_RING : "Token-Ring",
141 WTAP_ENCAP_SLIP : "SLIP",
142 WTAP_ENCAP_PPP : "PPP",
143 WTAP_ENCAP_FDDI : "FDDI",
144 WTAP_ENCAP_FDDI_BITSWAPPED : "FDDI (Bitswapped)",
145 WTAP_ENCAP_RAW_IP : "Raw IP",
146 WTAP_ENCAP_ARCNET : "ARCNET",
147 WTAP_ENCAP_ATM_RFC1483 : "ATM RFC1483",
148 WTAP_ENCAP_LINUX_ATM_CLIP : "Linux ATM CLIP",
149 WTAP_ENCAP_LAPB : "LAPB",
150 WTAP_ENCAP_ATM_SNIFFER : "ATM Sniffer",
151 WTAP_ENCAP_NULL : "Null",
152 WTAP_ENCAP_ASCEND : "Ascend",
153 WTAP_ENCAP_LAPD : "LAPD",
154 WTAP_ENCAP_V120 : "V.120",
155 WTAP_ENCAP_PPP_WITH_PHDR : "PPP (with PHDR)",
156 WTAP_ENCAP_IEEE_802_11 : "IEEE 802.11",
157 WTAP_ENCAP_SLL : "SLL",
158 WTAP_ENCAP_FRELAY : "Frame Relay",
159 WTAP_ENCAP_CHDLC : "Cisco HDLC",
160 WTAP_ENCAP_CISCO_IOS : "Cisco IOS",
161 WTAP_ENCAP_LOCALTALK : "LocalTalk",
162 WTAP_ENCAP_PRISM_HEADER : "Prism Header",
163 WTAP_ENCAP_PFLOG : "PFLog",
164 WTAP_ENCAP_AIROPEEK : "AiroPeek",
165 WTAP_ENCAP_HHDLC : "HHDLC",
168 def wtap_to_pcap(wtap):
169 if not wtap_to_pcap_map.has_key(wtap):
170 sys.exit("Don't know how to convert wiretap encoding %d to libpcap." % \
171 (wtap))
173 return wtap_to_pcap_map[wtap]
176 def run_gdb(*commands):
177 if len(commands) == 0:
178 return []
180 # Create a temporary file
181 fname = tempfile.mktemp()
182 try:
183 fh = open(fname, "w")
184 except IOError, err:
185 sys.exit("Cannot open %s for writing: %s" % (fname, err))
187 # Put the commands in it
188 for cmd in commands:
189 fh.write(cmd)
190 fh.write("\n")
192 fh.write("quit\n")
193 try:
194 fh.close()
195 except IOError, err:
196 try:
197 os.unlink(fname)
198 except Exception:
199 pass
200 sys.exit("Cannot close %s: %s" % (fname, err))
203 # Run gdb
204 cmd = "gdb --nw --quiet --command=%s %s %s" % (fname, exec_file, core_file)
205 if verbose:
206 print "Invoking %s" % (cmd,)
207 try:
208 pipe = os.popen(cmd)
209 except OSError, err:
210 try:
211 os.unlink(fname)
212 except Exception:
213 pass
214 sys.exit("Cannot run gdb: %s" % (err,))
216 # Get gdb's output
217 result = pipe.readlines()
218 error = pipe.close()
219 if error is not None:
220 try:
221 os.unlink(fname)
222 except Exception:
223 pass
224 sys.exit("gdb returned an exit value of %s" % (error,))
227 # Remove the temp file and return the results
228 try:
229 os.unlink(fname)
230 except Exception:
231 pass
232 return result
234 def get_value_from_frame(frame_num, variable, fmt=""):
235 cmds = []
236 if frame_num > 0:
237 cmds.append("up %d" % (frame_num,))
239 cmds.append("print %s %s" % (fmt, variable))
240 lines = apply(run_gdb, cmds)
242 LOOKING_FOR_START = 0
243 READING_VALUE = 1
244 state = LOOKING_FOR_START
245 result = ""
246 for line in lines:
247 if line[-1] == "\n":
248 line = line[0:-1]
249 if line[-1] == "\r":
250 line = line[0:-1]
252 if state == LOOKING_FOR_START:
253 if len(line) < 4:
254 continue
255 else:
256 if line[0:4] == "$1 =":
257 result = line[4:]
258 state = READING_VALUE
260 elif state == READING_VALUE:
261 result += line
263 return result
265 def get_int_from_frame(frame_num, variable):
266 text = get_value_from_frame(frame_num, variable)
267 try:
268 integer = int(text)
269 except ValueError:
270 sys.exit("Could not convert '%s' to integer." % (text,))
271 return integer
274 def get_byte_array_from_frame(frame_num, variable, length):
275 cmds = []
276 if frame_num > 0:
277 cmds.append("up %d" % (frame_num,))
279 cmds.append("print %s" % (variable,))
280 cmds.append("x/%dxb %s" % (length, variable))
281 lines = apply(run_gdb, cmds)
282 if debug:
283 print lines
285 bytes = []
287 LOOKING_FOR_START = 0
288 BYTES = 1
289 state = LOOKING_FOR_START
291 for line in lines:
292 if state == LOOKING_FOR_START:
293 if len(line) < 3:
294 continue
295 elif line[0:3] == "$1 ":
296 state = BYTES
297 elif state == BYTES:
298 line.rstrip()
299 fields = line.split('\t')
300 if fields[0][-1] != ":":
301 print "Failed to parse byte array from gdb:"
302 print line
303 sys.exit(1)
305 for field in fields[1:]:
306 val = int(field, 16)
307 bytes.append(val)
308 else:
309 assert 0
311 return bytes
313 def make_cap_file(pkt_data, lnk_t):
315 pcap_lnk_t = wtap_to_pcap(lnk_t)
317 # Create a temporary file
318 fname = tempfile.mktemp()
319 try:
320 fh = open(fname, "w")
321 except IOError, err:
322 sys.exit("Cannot open %s for writing: %s" % (fname, err))
324 print "Packet Data:"
326 # Put the hex dump in it
327 offset = 0
328 BYTES_IN_ROW = 16
329 for byte in pkt_data:
330 if (offset % BYTES_IN_ROW) == 0:
331 print >> fh, "\n%08X " % (offset,),
332 print "\n%08X " % (offset,),
334 print >> fh, "%02X " % (byte,),
335 print "%02X " % (byte,),
336 offset += 1
338 print >> fh, "\n"
339 print "\n"
341 try:
342 fh.close()
343 except IOError, err:
344 try:
345 os.unlink(fname)
346 except Exception:
347 pass
348 sys.exit("Cannot close %s: %s" % (fname, err))
351 # Run text2pcap
352 cmd = "text2pcap -q -l %s %s %s" % (pcap_lnk_t, fname, output_file)
353 # print "Command is %s" % (cmd,)
354 try:
355 retval = os.system(cmd)
356 except OSError, err:
357 try:
358 os.unlink(fname)
359 except Exception:
360 pass
361 sys.exit("Cannot run text2pcap: %s" % (err,))
363 # Remove the temp file
364 try:
365 os.unlink(fname)
366 except Exception:
367 pass
369 if retval == 0:
370 print "%s created with %d bytes in packet, and %s encoding." % \
371 (output_file, len(pkt_data), wtap_name[lnk_t])
372 else:
373 sys.exit("text2pcap did not run successfully.")
378 def try_frame(func_text, cap_len_text, lnk_t_text, data_text):
380 # Get the back trace
381 bt_text = run_gdb("bt")
382 bt = BackTrace(bt_text)
383 if not bt.HasFunction(func_text):
384 print "%s() not found in backtrace." % (func_text,)
385 return 0
386 else:
387 print "%s() found in backtrace." % (func_text,)
389 # Figure out where the call to epan_dissect_run is.
390 frame_num = bt.Frame(func_text)
392 # Get the capture length
393 cap_len = get_int_from_frame(frame_num, cap_len_text)
395 # Get the encoding type
396 lnk_t = get_int_from_frame(frame_num, lnk_t_text)
398 # Get the packet data
399 pkt_data = get_byte_array_from_frame(frame_num, data_text, cap_len)
401 if verbose:
402 print "Length=%d" % (cap_len,)
403 print "Encoding=%d" % (lnk_t,)
404 print "Data (%d bytes) = %s" % (len(pkt_data), pkt_data)
405 make_cap_file(pkt_data, lnk_t)
406 return 1
408 def run():
409 if try_frame("epan_dissect_run",
410 "fd->cap_len", "fd->lnk_t", "data"):
411 return
412 elif try_frame("add_packet_to_packet_list",
413 "fdata->cap_len", "fdata->lnk_t", "buf"):
414 return
415 else:
416 sys.exit("A packet cannot be pulled from this core.")
419 def usage():
420 print "pkt-from-core.py [-v] -w capture_file executable-file (core-file or process-id)"
421 print ""
422 print "\tGiven an executable file and a core file, this tool"
423 print "\tuses gdb to retrieve the packet that was being dissected"
424 print "\tat the time wireshark/tshark stopped running. The packet"
425 print "\tis saved in the capture_file specified by the -w option."
426 print ""
427 print "\t-v : verbose"
428 sys.exit(1)
430 def main():
431 global exec_file
432 global core_file
433 global output_file
434 global verbose
435 global debug
437 optstring = "dvw:"
438 try:
439 opts, args = getopt.getopt(sys.argv[1:], optstring)
440 except getopt.error:
441 usage()
443 for opt, arg in opts:
444 if opt == "-w":
445 output_file = arg
446 elif opt == "-v":
447 verbose = 1
448 elif opt == "-d":
449 debug = 1
450 else:
451 assert 0
453 if output_file is None:
454 usage()
456 if len(args) != 2:
457 usage()
459 exec_file = args[0]
460 core_file = args[1]
462 run()
464 if __name__ == '__main__':
465 main()
468 # Editor modelines - https://www.wireshark.org/tools/modelines.html
470 # Local variables:
471 # c-basic-offset: 4
472 # indent-tabs-mode: nil
473 # End:
475 # vi: set shiftwidth=4 expandtab:
476 # :indentSize=4:noTabs=true: