def _GetVSSStoreIdentifiers(self, scan_node): """Determines the VSS store identifiers. Args: scan_node: the scan node (instance of dfvfs.ScanNode). Returns: A list of VSS store identifiers. Raises: ScannerError: if the format of or within the source is not supported, the the scan node is invalid or no mediator is provided and VSS store identifiers are found. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError(u'Invalid scan node.') volume_system = vshadow_volume_system.VShadowVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not self._mediator and not volume_identifiers: return [] if not self._mediator: raise errors.ScannerError( u'Unable to proceed VSS. Identifiers found but no mediator to ' u'determine how they should be used.') try: return self._mediator.GetVSSStoreIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort(u'File system scan aborted.')
def OpenFile(self, windows_path): """Opens the file specified by the Windows path. Args: windows_path (str): Windows path to the file. Returns: dfvfs.FileIO: file-like object or None if the file does not exist. Raises: ScannerError: if the scan node is invalid or the scanner does not know how to proceed. """ windows_path_upper = windows_path.upper() if windows_path_upper.startswith('%USERPROFILE%'): if not self._mediator: raise dfvfs_errors.ScannerError( 'Unable to proceed. %UserProfile% found in Windows path but no ' 'mediator to determine which user to select.') users_path_spec = self._path_resolver.ResolvePath('\\Users') # TODO: handle alternative users path locations if users_path_spec is None: raise dfvfs_errors.ScannerError( 'Unable to proceed. %UserProfile% found in Windows path but no ' 'users path found to determine which user to select.') users_file_entry = dfvfs_resolver.Resolver.OpenFileEntry(users_path_spec) self._mediator.PrintUsersSubDirectoriesOverview(users_file_entry) # TODO: list users and determine corresponding windows_path return super(WindowsRegistryVolumeScanner, self).OpenFile(windows_path)
def _ScanEncryptedVolume(self, scan_context, scan_node): """Scans an encrypted volume scan node for volume and file systems. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): volume scan node. Raises: ScannerError: if the format of or within the source is not supported, the scan node is invalid, there are no credentials defined for the format or no mediator is provided and a locked scan node was found, e.g. an encrypted volume, """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid or missing scan node.') credentials = credentials_manager.CredentialsManager.GetCredentials( scan_node.path_spec) if not credentials: raise errors.ScannerError('Missing credentials for scan node.') if not self._mediator: raise errors.ScannerError( 'Unable to proceed. Encrypted volume found but no mediator to ' 'determine how it should be unlocked.') if self._mediator.UnlockEncryptedVolume( self._source_scanner, scan_context, scan_node, credentials): self._source_scanner.Scan( scan_context, scan_path_spec=scan_node.path_spec)
def _GetTSKPartitionIdentifiers(self, scan_node): """Determines the TSK partition identifiers. Args: scan_node: the scan node (instance of dfvfs.ScanNode). Returns: A list of partition identifiers. Raises: ScannerError: if the format of or within the source is not supported or the the scan node is invalid or if the volume for a specific identifier cannot be retrieved. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError(u'Invalid scan node.') volume_system = tsk_volume_system.TSKVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: raise errors.ScannerError(u'No partitions found.') if not self._mediator or len(volume_identifiers) == 1: return volume_identifiers try: return self._mediator.GetPartitionIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.ScannerError(u'File system scan aborted.')
def _GetVSSStoreIdentifiers(self, scan_node, options): """Determines the VSS store identifiers. Args: scan_node (SourceScanNode): scan node. options (VolumeScannerOptions): volume scanner options. Returns: list[str]: VSS store identifiers. Raises: ScannerError: if the format the scan node is invalid or no mediator is provided and VSS store identifiers are found. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') volume_system = vshadow_volume_system.VShadowVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if options.snapshots: if options.snapshots == ['all']: snapshots = range(1, volume_system.number_of_volumes + 1) elif options.snapshots == ['none']: snapshots = [] else: snapshots = options.snapshots try: selected_volumes = self._NormalizedVolumeIdentifiers( volume_system, snapshots, prefix='vss') if not set(selected_volumes).difference(volume_identifiers): return selected_volumes except errors.ScannerError as exception: if self._mediator: self._mediator.PrintWarning('{0!s}'.format(exception)) if not self._mediator: raise errors.ScannerError( 'Unable to proceed. VSS stores found but no mediator to determine ' 'how they should be used.') try: volume_identifiers = self._mediator.GetVSSStoreIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='vss')
def _GetAPFSVolumeIdentifiers(self, scan_node, options): """Determines the APFS volume identifiers. Args: scan_node (SourceScanNode): scan node. options (VolumeScannerOptions): volume scanner options. Returns: list[str]: APFS volume identifiers. Raises: ScannerError: if the format of or within the source is not supported or the the scan node is invalid. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') volume_system = apfs_volume_system.APFSVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if options.volumes: if options.volumes == ['all']: volumes = range(1, volume_system.number_of_volumes + 1) else: volumes = options.volumes try: selected_volumes = self._NormalizedVolumeIdentifiers( volume_system, volumes, prefix='apfs') if not set(selected_volumes).difference(volume_identifiers): return selected_volumes except errors.ScannerError as exception: if self._mediator: self._mediator.PrintWarning('{0!s}'.format(exception)) if len(volume_identifiers) > 1: if not self._mediator: raise errors.ScannerError( 'Unable to proceed. APFS volumes found but no mediator to ' 'determine how they should be used.') try: volume_identifiers = self._mediator.GetAPFSVolumeIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='apfs')
def _ScanFileSystem(self, scan_node, base_path_specs): """Scans a file system scan node for file systems. This method checks if the file system contains a known Windows directory. Args: scan_node (SourceScanNode): file system scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError( 'Invalid or missing file system scan node.') file_system = resolver.Resolver.OpenFileSystem(scan_node.path_spec) if not file_system: return try: path_resolver = windows_path_resolver.WindowsPathResolver( file_system, scan_node.path_spec.parent) if self._ScanFileSystemForWindowsDirectory(path_resolver): base_path_specs.append(scan_node.path_spec) finally: file_system.Close()
def _PrintAPFSVolumeIdentifiersOverview(self, volume_system, volume_identifiers): """Prints an overview of APFS volume identifiers. Args: volume_system (APFSVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: ScannerError: if a volume cannot be resolved from the volume identifier. """ header = 'The following Apple File System (APFS) volumes were found:\n' self._output_writer.Write(header) column_names = ['Identifier', 'Name'] table_view = CLITabularTableView(column_names=column_names) for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) volume_attribute = volume.GetAttribute('name') table_view.AddRow([volume.identifier, volume_attribute.value]) self._output_writer.Write('\n') table_view.Write(self._output_writer)
def _PrintPartitionIdentifiersOverview(self, volume_system, volume_identifiers): """Prints an overview of TSK partition identifiers. Args: volume_system (TSKVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: ScannerError: if a volume cannot be resolved from the volume identifier. """ header = 'The following partitions were found:\n' self._output_writer.Write(header) column_names = ['Identifier', 'Offset (in bytes)', 'Size (in bytes)'] table_view = CLITabularTableView(column_names=column_names) for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( 'Partition missing for identifier: {0:s}.'.format( volume_identifier)) volume_extent = volume.extents[0] volume_offset = '{0:d} (0x{0:08x})'.format(volume_extent.offset) volume_size = self._FormatHumanReadableSize(volume_extent.size) table_view.AddRow([volume.identifier, volume_offset, volume_size]) self._output_writer.Write('\n') table_view.Write(self._output_writer)
def GetPartitionIdentifiers(self, volume_system, volume_identifiers): """Retrieves partition identifiers. This method can be used to prompt the user to provide partition identifiers. Args: volume_system: the volume system (instance of dfvfs.TSKVolumeSystem). volume_identifiers: a list of strings containing the volume identifiers. Returns: A list of strings containing the selected partition identifiers or None. Raises: ScannerError: if the source cannot be processed. """ print(u'The following partitions were found:') print(u'Identifier\tOffset (in bytes)\tSize (in bytes)') for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( u'Volume missing for identifier: {0:s}.'.format(volume_identifier)) volume_extent = volume.extents[0] print(u'{0:s}\t\t{1:d} (0x{1:08x})\t{2:s}'.format( volume.identifier, volume_extent.offset, self._FormatHumanReadableSize(volume_extent.size))) while True: print( u'Please specify the identifier of the partition that should be ' u'processed.') print( u'All partitions can be defined as: "all". Note that you ' u'can abort with Ctrl^C.') selected_volume_identifier = sys.stdin.readline() selected_volume_identifier = selected_volume_identifier.strip() if not selected_volume_identifier.startswith(u'p'): try: partition_number = int(selected_volume_identifier, 10) selected_volume_identifier = u'p{0:d}'.format(partition_number) except ValueError: pass if selected_volume_identifier == u'all': return volume_identifiers if selected_volume_identifier in volume_identifiers: break print(u'') print( u'Unsupported partition identifier, please try again or abort ' u'with Ctrl^C.') print(u'') return [selected_volume_identifier]
def _PrintLVMVolumeIdentifiersOverview( self, volume_system, volume_identifiers): """Prints an overview of LVM volume identifiers. Args: volume_system (LVMVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: ScannerError: if a volume cannot be resolved from the volume identifier. """ header = 'The following Logical Volume Manager (LVM) volumes were found:\n' self._output_writer.Write(header) column_names = ['Identifier'] table_view = CLITabularTableView(column_names=column_names) # Sort the volume identifiers in alphanumeric order. for volume_identifier in sorted(volume_identifiers, key=lambda string: int( ''.join([character for character in string if character.isdigit()]))): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) table_view.AddRow([volume.identifier]) self._output_writer.Write('\n') table_view.Write(self._output_writer)
def _ScanVolumeScanNodeEncrypted(self, scan_context, volume_scan_node, base_path_specs): """Scans an encrypted volume scan node for volume and file systems. Args: scan_context (SourceScannerContext): source scanner context. volume_scan_node (SourceScanNode): volume scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the scan node is invalid. """ if not volume_scan_node or not volume_scan_node.path_spec: raise errors.ScannerError(u'Invalid or missing volume scan node.') result = not scan_context.IsLockedScanNode(volume_scan_node.path_spec) if not result: credentials = credentials_manager.CredentialsManager.GetCredentials( volume_scan_node.path_spec) result = self._mediator.UnlockEncryptedVolume( self._source_scanner, scan_context, volume_scan_node, credentials) if result: self._source_scanner.Scan( scan_context, scan_path_spec=volume_scan_node.path_spec) self._ScanVolume(scan_context, volume_scan_node, base_path_specs)
def _ScanVolume(self, scan_context, volume_scan_node, base_path_specs): """Scans the volume scan node for volume and file systems. Args: scan_context (SourceScannerContext): source scanner context. volume_scan_node (SourceScanNode): volume scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the format of or within the source is not supported or the scan node is invalid. """ if not volume_scan_node or not volume_scan_node.path_spec: raise errors.ScannerError(u'Invalid or missing volume scan node.') if len(volume_scan_node.sub_nodes) == 0: self._ScanVolumeScanNode(scan_context, volume_scan_node, base_path_specs) else: # Some volumes contain other volume or file systems e.g. BitLocker ToGo # has an encrypted and unencrypted volume. for sub_scan_node in volume_scan_node.sub_nodes: self._ScanVolumeScanNode(scan_context, sub_scan_node, base_path_specs)
def _ScanVolumeSystemRoot( self, scan_context, scan_node, options, base_path_specs): """Scans a volume system root scan node for volume and file systems. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): volume system root scan node. options (VolumeScannerOptions): volume scanner options. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the scan node is invalid, the scan node type is not supported or if a sub scan node cannot be retrieved. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') if scan_node.type_indicator == definitions.TYPE_INDICATOR_APFS_CONTAINER: volume_identifiers = self._GetAPFSVolumeIdentifiers(scan_node, options) elif scan_node.type_indicator == definitions.TYPE_INDICATOR_GPT: volume_identifiers = self._GetPartitionIdentifiers(scan_node, options) elif scan_node.type_indicator == definitions.TYPE_INDICATOR_LVM: volume_identifiers = self._GetLVMVolumeIdentifiers(scan_node, options) elif scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW: volume_identifiers = self._GetVSSStoreIdentifiers(scan_node, options) # Process VSS stores (snapshots) starting with the most recent one. volume_identifiers.reverse() else: raise errors.ScannerError( 'Unsupported volume system type: {0:s}.'.format( scan_node.type_indicator)) for volume_identifier in volume_identifiers: location = '/{0:s}'.format(volume_identifier) sub_scan_node = scan_node.GetSubNodeByLocation(location) if not sub_scan_node: raise errors.ScannerError( 'Scan node missing for volume identifier: {0:s}.'.format( volume_identifier)) self._ScanVolume(scan_context, sub_scan_node, options, base_path_specs)
def _GetTSKPartitionIdentifiers(self, scan_node): """Determines the TSK partition identifiers. Args: scan_node (SourceScanNode): scan node. Returns: list[str]: TSK partition identifiers. Raises: ScannerError: if the format of or within the source is not supported or the scan node is invalid or if the volume for a specific identifier cannot be retrieved. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') volume_system = tsk_volume_system.TSKVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if len(volume_identifiers) == 1: return volume_identifiers if not self._mediator: raise errors.ScannerError( 'Unable to proceed. Partitions found but no mediator to determine ' 'how they should be used.') try: volume_identifiers = self._mediator.GetPartitionIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='p')
def _GetAPFSVolumeIdentifiers(self, scan_node): """Determines the APFS volume identifiers. Args: scan_node (SourceScanNode): scan node. Returns: list[str]: APFS volume identifiers. Raises: ScannerError: if the format of or within the source is not supported or the the scan node is invalid. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') volume_system = apfs_volume_system.APFSVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if len(volume_identifiers) > 1: if not self._mediator: raise errors.ScannerError( 'Unable to proceed. APFS volumes found but no mediator to ' 'determine how they should be used.') try: volume_identifiers = self._mediator.GetAPFSVolumeIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='apfs')
def _ScanVolumeScanNodeVSS(self, volume_scan_node, base_path_specs): """Scans a VSS volume scan node for volume and file systems. Args: volume_scan_node (SourceScanNode): volume scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if a VSS sub scan node scannot be retrieved or if the scan node is invalid. """ if not volume_scan_node or not volume_scan_node.path_spec: raise errors.ScannerError(u'Invalid or missing volume scan node.') # Do not scan inside individual VSS store scan nodes. location = getattr(volume_scan_node.path_spec, u'location', None) if location != u'/': return vss_store_identifiers = self._GetVSSStoreIdentifiers(volume_scan_node) self._vss_stores = list(vss_store_identifiers) # Process VSS stores starting with the most recent one. vss_store_identifiers.reverse() for vss_store_identifier in vss_store_identifiers: location = u'/vss{0:d}'.format(vss_store_identifier) sub_scan_node = volume_scan_node.GetSubNodeByLocation(location) if not sub_scan_node: raise errors.ScannerError( u'Scan node missing for VSS store identifier: {0:d}.'. format(vss_store_identifier)) # We "optimize" here for user experience, alternatively we could scan for # a file system instead of hard coding a TSK child path specification. path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_TSK, location=u'/', parent=sub_scan_node.path_spec) base_path_specs.append(path_spec)
def _GetVSSStoreIdentifiers(self, scan_node): """Determines the VSS store identifiers. Args: scan_node (SourceScanNode): scan node. Returns: list[str]: VSS store identifiers. Raises: ScannerError: if the format the scan node is invalid or no mediator is provided and VSS store identifiers are found. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') volume_system = vshadow_volume_system.VShadowVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if not self._mediator: raise errors.ScannerError( 'Unable to proceed. VSS stores found but no mediator to determine ' 'how they should be used.') try: volume_identifiers = self._mediator.GetVSSStoreIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='vss')
def _ScanFileSystem(self, scan_node, base_path_specs): """Scans a file system scan node for file systems. Args: scan_node (SourceScanNode): file system scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid or missing file system scan node.') base_path_specs.append(scan_node.path_spec)
def _GetBasePathSpecs(self, scan_context, options): """Determines the base path specifications. Args: scan_context (SourceScannerContext): source scanner context. options (VolumeScannerOptions): volume scanner options. Returns: list[PathSpec]: path specifications. Raises: dfvfs.ScannerError: if the format of or within the source is not supported or no partitions were found. """ # TODO: difference with dfVFS. self._snapshots_only = options.snapshots_only scan_node = scan_context.GetRootScanNode() if scan_context.source_type not in ( scan_context.SOURCE_TYPE_STORAGE_MEDIA_DEVICE, scan_context.SOURCE_TYPE_STORAGE_MEDIA_IMAGE): return [scan_node.path_spec] # Get the first node where where we need to decide what to process. while len(scan_node.sub_nodes) == 1: scan_node = scan_node.sub_nodes[0] base_path_specs = [] if scan_node.type_indicator not in ( dfvfs_definitions.TYPE_INDICATOR_GPT, dfvfs_definitions.TYPE_INDICATOR_TSK_PARTITION): self._ScanVolume(scan_context, scan_node, options, base_path_specs) else: # Determine which partition needs to be processed. partition_identifiers = self._GetPartitionIdentifiers( scan_node, options) # TODO: difference with dfVFS. if not partition_identifiers: raise dfvfs_errors.ScannerError('No partitions found.') for partition_identifier in partition_identifiers: location = '/{0:s}'.format(partition_identifier) sub_scan_node = scan_node.GetSubNodeByLocation(location) self._ScanVolume(scan_context, sub_scan_node, options, base_path_specs) return base_path_specs
def _ScanVolume(self, scan_context, scan_node, options, base_path_specs): """Scans a volume scan node for volume and file systems. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): volume scan node. options (VolumeScannerOptions): volume scanner options. base_path_specs (list[PathSpec]): file system base path specifications. Raises: ScannerError: if the format of or within the source is not supported or the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid or missing scan node.') if scan_context.IsLockedScanNode(scan_node.path_spec): # The source scanner found a locked volume and we need a credential to # unlock it. self._ScanEncryptedVolume(scan_context, scan_node) if scan_context.IsLockedScanNode(scan_node.path_spec): return if scan_node.IsVolumeSystemRoot(): if options.scan_mode in (options.SCAN_MODE_ALL, options.SCAN_MODE_SNAPSHOTS_ONLY): self._ScanVolumeSystemRoot(scan_context, scan_node, options, base_path_specs) elif scan_node.IsFileSystem(): self._ScanFileSystem(scan_node, base_path_specs) elif scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW: # TODO: look into building VSS store on demand. # We "optimize" here for user experience, alternatively we could scan for # a file system instead of hard coding a TSK child path specification. path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_TSK, location='/', parent=scan_node.path_spec) base_path_specs.append(path_spec) else: for sub_scan_node in scan_node.sub_nodes: self._ScanVolume(scan_context, sub_scan_node, options, base_path_specs)
def _ScanFileSystem(self, file_system_scan_node, base_path_specs): """Scans a file system scan node for file systems. Args: file_system_scan_node: the file system scan node (instance of dfvfs.ScanNode). base_path_specs: a list of source path specification (instances of dfvfs.PathSpec). Raises: ScannerError: if the scan node is invalid. """ if not file_system_scan_node or not file_system_scan_node.path_spec: raise errors.ScannerError( u'Invalid or missing file system scan node.') base_path_specs.append(file_system_scan_node.path_spec)
def _NormalizedVolumeIdentifiers(self, volume_system, volume_identifiers, prefix='v'): """Normalizes volume identifiers. Args: volume_system (VolumeSystem): volume system. volume_identifiers (list[int|str]): allowed volume identifiers, formatted as an integer or string with prefix. prefix (Optional[str]): volume identifier prefix. Returns: list[str]: volume identifiers with prefix. Raises: ScannerError: if the volume identifier is not supported or no volume could be found that corresponds with the identifier. """ normalized_volume_identifiers = [] for volume_identifier in volume_identifiers: if isinstance(volume_identifier, int): volume_identifier = '{0:s}{1:d}'.format( prefix, volume_identifier) elif not volume_identifier.startswith(prefix): try: volume_identifier = int(volume_identifier, 10) volume_identifier = '{0:s}{1:d}'.format( prefix, volume_identifier) except (TypeError, ValueError): pass try: volume = volume_system.GetVolumeByIdentifier(volume_identifier) except KeyError: volume = None if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) normalized_volume_identifiers.append(volume_identifier) return normalized_volume_identifiers
def GetPartitionIdentifiers(self, volume_system, volume_identifiers): """Retrieves partition identifiers that should be scanned Args: volume_system: the volume system (instance of dfvfs.TSKVolumeSystem). volume_identifiers: a list of strings containing the volume identifiers. Returns: A list of strings containing the selected partition identifiers or None. Raises: ScannerError: if the source cannot be processed. """ LOGGER.info('The following partitions were found:') LOGGER.info('Identifier\tOffset (in bytes)\tSize (in bytes)') for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise dfvfs_errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) volume_extent = volume.extents[0] LOGGER.info('{0:s}\t\t{1:d} (0x{1:08x})\t{2:s}'.format( volume.identifier, volume_extent.offset, self._FormatHumanReadableSize(volume_extent.size))) selected_volume_identifier = self._partitions selected_volume_identifier = selected_volume_identifier.strip() if not selected_volume_identifier.startswith('p'): try: partition_number = int(selected_volume_identifier, 10) selected_volume_identifier = 'p{0:d}'.format(partition_number) except ValueError: pass LOGGER.info("Selected partition(s): %s", selected_volume_identifier) if selected_volume_identifier == 'all': return volume_identifiers return [selected_volume_identifier]
def _ScanFileSystem(self, scan_node, base_path_specs): """Scans a file system scan node for file systems. Args: scan_node (SourceScanNode): file system scan node. base_path_specs (list[PathSpec]): file system base path specifications. Raises: dfvfs.ScannerError: if the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise dfvfs_errors.ScannerError( 'Invalid or missing file system scan node.') # TODO: difference with dfVFS for current VSS volume support. if self._snapshots_only: if scan_node.parent_node.sub_nodes[0].type_indicator == ( dfvfs_definitions.TYPE_INDICATOR_VSHADOW): return base_path_specs.append(scan_node.path_spec)
def _ScanVolumeScanNode(self, scan_context, volume_scan_node, base_path_specs): """Scans an individual volume scan node for volume and file systems. Args: scan_context: the source scanner context (instance of SourceScannerContext). volume_scan_node: the volume scan node (instance of dfvfs.ScanNode). base_path_specs: a list of source path specification (instances of dfvfs.PathSpec). Raises: ScannerError: if the format of or within the source is not supported or the the scan node is invalid. """ if not volume_scan_node or not volume_scan_node.path_spec: raise errors.ScannerError(u'Invalid or missing volume scan node.') # Get the first node where where we need to decide what to process. scan_node = volume_scan_node while len(scan_node.sub_nodes) == 1: # Make sure that we prompt the user about VSS selection. if scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW: location = getattr(scan_node.path_spec, u'location', None) if location == u'/': break scan_node = scan_node.sub_nodes[0] # The source scanner found an encrypted volume and we need # a credential to unlock the volume. if scan_node.type_indicator in definitions.ENCRYPTED_VOLUME_TYPE_INDICATORS: self._ScanVolumeScanNodeEncrypted(scan_context, scan_node, base_path_specs) elif scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW: self._ScanVolumeScanNodeVSS(scan_node, base_path_specs) elif scan_node.type_indicator in definitions.FILE_SYSTEM_TYPE_INDICATORS: self._ScanFileSystem(scan_node, base_path_specs)
def _PrintVSSStoreIdentifiersOverview( self, volume_system, volume_identifiers): """Prints an overview of VSS store identifiers. Args: volume_system (VShadowVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: ScannerError: if a volume cannot be resolved from the volume identifier. """ header = 'The following Volume Shadow Snapshots (VSS) were found:\n' self._output_writer.Write(header) column_names = ['Identifier', 'Creation Time'] table_view = CLITabularTableView(column_names=column_names) # Sort the volume identifiers in alphanumeric order. for volume_identifier in sorted(volume_identifiers, key=lambda string: int( ''.join([character for character in string if character.isdigit()]))): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) volume_attribute = volume.GetAttribute('creation_time') filetime = dfdatetime_filetime.Filetime(timestamp=volume_attribute.value) creation_time = filetime.CopyToDateTimeString() if volume.HasExternalData(): creation_time = '{0:s}\tWARNING: data stored outside volume'.format( creation_time) table_view.AddRow([volume.identifier, creation_time]) self._output_writer.Write('\n') table_view.Write(self._output_writer)
def GetVSSStoreIdentifiers(self, volume_system, volume_identifiers): """Retrieves VSS store identifiers. This method can be used to prompt the user to provide VSS store identifiers. Args: volume_system (VShadowVolumeSystem): volume system. volume_identifiers (list[str]): volume identifiers. Returns: list[int]: selected VSS store numbers or None. Raises: ScannerError: if the source cannot be processed. """ normalized_volume_identifiers = [] for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) try: volume_identifier = int(volume.identifier[3:], 10) normalized_volume_identifiers.append(volume_identifier) except ValueError: pass print_header = True while True: if print_header: print( 'The following Volume Shadow Snapshots (VSS) were found:') print('Identifier\tVSS store identifier') for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier( volume_identifier) if not volume: raise errors.ScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) vss_identifier = volume.GetAttribute('identifier') print('{0:s}\t\t{1:s}'.format(volume.identifier, vss_identifier.value)) print('') print_header = False print('Please specify the identifier(s) of the VSS that should be ' 'processed:') print( 'Note that a range of stores can be defined as: 3..5. Multiple ' 'stores can') print( 'be defined as: 1,3,5 (a list of comma separated values). Ranges ' 'and lists can') print( 'also be combined as: 1,3..5. The first store is 1. All stores ' 'can be defined') print( 'as "all". If no stores are specified none will be processed. Yo' ) print('can abort with Ctrl^C.') selected_vss_stores = sys.stdin.readline() selected_vss_stores = selected_vss_stores.strip() if not selected_vss_stores: selected_vss_stores = [] break try: selected_vss_stores = self._ParseVSSStoresString( selected_vss_stores) except ValueError: selected_vss_stores = [] if selected_vss_stores == ['all']: # We need to set the stores to cover all vss stores. selected_vss_stores = range( 1, volume_system.number_of_volumes + 1) if not set(selected_vss_stores).difference( normalized_volume_identifiers): break print('') print( 'Unsupported VSS identifier(s), please try again or abort with ' 'Ctrl^C.') print('') return selected_vss_stores
def GetBasePathSpecs(self, source_path, options=None): """Determines the base path specifications. Args: source_path (str): source path. options (Optional[VolumeScannerOptions]): volume scanner options. If None the default volume scanner options are used, which are defined in the VolumeScannerOptions class. Returns: list[PathSpec]: path specifications. Raises: ScannerError: if the source path does not exists, or if the source path is not a file or directory, or if the format of or within the source file is not supported. """ if not options: options = VolumeScannerOptions() if not source_path: raise errors.ScannerError('Invalid source path.') # Note that os.path.exists() does not support Windows device paths. if (not source_path.startswith('\\\\.\\') and not os.path.exists(source_path)): raise errors.ScannerError( 'No such device, file or directory: {0:s}.'.format(source_path)) scan_context = source_scanner.SourceScannerContext() scan_context.OpenSourcePath(source_path) try: self._source_scanner.Scan(scan_context) except (ValueError, errors.BackEndError) as exception: raise errors.ScannerError( 'Unable to scan source with error: {0!s}'.format(exception)) self._source_path = source_path self._source_type = scan_context.source_type if self._source_type not in [ definitions.SOURCE_TYPE_STORAGE_MEDIA_DEVICE, definitions.SOURCE_TYPE_STORAGE_MEDIA_IMAGE]: scan_node = scan_context.GetRootScanNode() return [scan_node.path_spec] # Get the first node where where we need to decide what to process. scan_node = scan_context.GetRootScanNode() while len(scan_node.sub_nodes) == 1: scan_node = scan_node.sub_nodes[0] base_path_specs = [] if scan_node.type_indicator not in ( definitions.TYPE_INDICATOR_GPT, definitions.TYPE_INDICATOR_TSK_PARTITION): self._ScanVolume(scan_context, scan_node, options, base_path_specs) else: # Determine which partition needs to be processed. partition_identifiers = self._GetPartitionIdentifiers(scan_node, options) for partition_identifier in partition_identifiers: location = '/{0:s}'.format(partition_identifier) sub_scan_node = scan_node.GetSubNodeByLocation(location) self._ScanVolume(scan_context, sub_scan_node, options, base_path_specs) return base_path_specs
def _GetPartitionIdentifiers(self, scan_node, options): """Determines the partition identifiers. This function determines which partition identifiers need to be scanned based on the volume scanner options. If no options are provided and there is more than a single partition the mediator is used to ask the user. Args: scan_node (SourceScanNode): scan node. options (VolumeScannerOptions): volume scanner options. Returns: list[str]: partition identifiers. Raises: ScannerError: if the scan node is invalid or the scanner does not know how to proceed. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.ScannerError('Invalid scan node.') if scan_node.path_spec.type_indicator == definitions.TYPE_INDICATOR_GPT: volume_system = gpt_volume_system.GPTVolumeSystem() volume_system.Open(scan_node.path_spec) prefix = 'gpt' else: volume_system = tsk_volume_system.TSKVolumeSystem() volume_system.Open(scan_node.path_spec) prefix = 'p' volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] if options.partitions: if options.partitions == ['all']: partitions = range(1, volume_system.number_of_volumes + 1) else: partitions = options.partitions try: selected_volumes = self._NormalizedVolumeIdentifiers( volume_system, partitions, prefix=prefix) if not set(selected_volumes).difference(volume_identifiers): return selected_volumes except errors.ScannerError as exception: if self._mediator: self._mediator.PrintWarning('{0!s}'.format(exception)) if len(volume_identifiers) > 1: if not self._mediator: raise errors.ScannerError( 'Unable to proceed. More than one partitions found but no mediator ' 'to determine how they should be used.') try: volume_identifiers = self._mediator.GetPartitionIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers( volume_system, volume_identifiers, prefix=prefix)