nexmon – Blame information for rev 1
?pathlinks?
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 | # |