nexmon – Blame information for rev 1

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