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