def _GetTSKPartitionIdentifiers(self, scan_node): """Determines the TSK partition identifiers. This method first checks for the preferred partition number, then falls back to prompt the user if no usable preferences were specified. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: TSK partition identifiers. Raises: RuntimeError: if the volume for a specific identifier cannot be retrieved. SourceScannerError: 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.SourceScannerError('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 [] # TODO: refactor self._partitions to use scan options. if self._partitions: if self._partitions == 'all': partitions = range(1, volume_system.number_of_volumes + 1) else: partitions = self._ParseVolumeIdentifiersString( self._partitions, prefix='p') selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, partitions, prefix='p') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers if len(volume_identifiers) > 1: if self._unattended_mode: raise errors.SourceScannerError( 'More than 1 parition found but no paritions specified.') try: volume_identifiers = self._PromptUserForPartitionIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='p')
def _GetLVMVolumeIdentifiers(self, scan_node): """Determines the LVM volume identifiers. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: LVM volume identifiers. Raises: SourceScannerError: if the scan node is invalid or more than 1 volume was found but no volumes were specified. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError('Invalid scan node.') volume_system = lvm_volume_system.LVMVolumeSystem() volume_system.Open(scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return [] # TODO: refactor self._volumes to use scan options. if self._volumes: if self._volumes == 'all': volumes = volume_system.volume_identifiers else: volumes = self._mediator.ParseVolumeIdentifiersString( self._volumes, prefix='lvm') selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, volumes, prefix='lvm') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers if len(volume_identifiers) > 1: if self._unattended_mode: raise errors.SourceScannerError( 'More than 1 volume found but no volumes specified.') try: volume_identifiers = self._mediator.GetLVMVolumeIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='lvm')
def _GetAPFSVolumeIdentifiers(self, scan_node): """Determines the APFS volume identifiers. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: APFS volume identifiers. Raises: SourceScannerError: 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.SourceScannerError('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 [] # TODO: refactor self._volumes to use scan options. if self._volumes: if self._volumes == 'all': volumes = range(1, volume_system.number_of_volumes + 1) else: volumes = self._ParseVolumeIdentifiersString(self._volumes, prefix='apfs') selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, volumes, prefix='apfs') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers if len(volume_identifiers) > 1: if self._unattended_mode: raise errors.SourceScannerError( 'More than 1 volume found but no volumes specified.') try: volume_identifiers = self._PromptUserForAPFSVolumeIdentifiers( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort('File system scan aborted.') return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='apfs')
def ScanSource_CARPE(self, source_path, par_name): if os.path.islink(source_path): source_path = os.path.realpath(source_path) if (not source_path.startswith('\\\\.\\') and not os.path.exists(source_path)): raise errors.SourceScannerError( '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, dfvfs_errors.BackEndError) as exception: raise errors.SourceScannerError( 'Unable to scan source with error: {0!s}.'.format(exception)) if scan_context.source_type not in ( scan_context.SOURCE_TYPE_STORAGE_MEDIA_DEVICE, scan_context.SOURCE_TYPE_STORAGE_MEDIA_IMAGE): scan_node = scan_context.GetRootScanNode() self._source_path_specs.append(scan_node.path_spec) return scan_context # 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 != ( dfvfs_definitions.TYPE_INDICATOR_TSK_PARTITION): self._ScanVolume(scan_context, scan_node, base_path_specs) else: # Determine which partition needs to be processed. partition_identifier = par_name if not partition_identifier: raise errors.SourceScannerError('No partitions found.') location = '/{0:s}'.format(partition_identifier) sub_scan_node = scan_node.GetSubNodeByLocation(location) self._ScanVolume(scan_context, sub_scan_node, base_path_specs) if not base_path_specs: raise errors.SourceScannerError( 'No supported file system found in source.') self._source_path_specs = base_path_specs return scan_context
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: SourceScannerError: if the format of or within the source is not supported, the scan node is invalid or there are no credentials defined for the format. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError('Invalid or missing scan node.') credentials = credentials_manager.CredentialsManager.GetCredentials( scan_node.path_spec) if not credentials: raise errors.SourceScannerError( 'Missing credentials for scan node.') credentials_dict = dict(self._credentials) is_unlocked = False for credential_type in sorted(credentials.CREDENTIALS): credential_data = credentials_dict.get(credential_type, None) if not credential_data: continue is_unlocked = self._source_scanner.Unlock(scan_context, scan_node.path_spec, credential_type, credential_data) if is_unlocked: self._AddCredentialConfiguration(scan_node.path_spec, credential_type, credential_data) break if not is_unlocked and self._unattended_mode: is_unlocked, credential_type, credential_data = ( self._mediator.PromptUserForEncryptedVolumeCredential( self._source_scanner, scan_context, scan_node, credentials)) if is_unlocked: self._AddCredentialConfiguration(scan_node.path_spec, credential_type, credential_data) self._source_scanner.Scan(scan_context, scan_path_spec=scan_node.path_spec)
def _ScanVolumeSystemRoot(self, scan_context, scan_node, 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. base_path_specs (list[PathSpec]): file system base path specifications. Raises: SourceScannerError: 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.SourceScannerError('Invalid scan node.') if scan_node.type_indicator == ( dfvfs_definitions.TYPE_INDICATOR_APFS_CONTAINER): volume_identifiers = self._GetAPFSVolumeIdentifiers(scan_node) elif scan_node.type_indicator == dfvfs_definitions.TYPE_INDICATOR_LVM: volume_identifiers = self._GetLVMVolumeIdentifiers(scan_node) elif scan_node.type_indicator == dfvfs_definitions.TYPE_INDICATOR_VSHADOW: if not self._process_vss: volume_identifiers = [] else: volume_identifiers = self._GetVSSStoreIdentifiers(scan_node) # Process VSS stores (snapshots) starting with the most recent one. volume_identifiers.reverse() if (not self._vss_only and not self._unattended_mode and volume_identifiers): self._vss_only = not self._mediator.PromptUserForVSSCurrentVolume( ) else: raise errors.SourceScannerError( '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.SourceScannerError( 'Scan node missing for volume identifier: {0:s}.'.format( volume_identifier)) self._ScanVolume(scan_context, sub_scan_node, base_path_specs)
def _GetVSSStoreIdentifiers(self, scan_node, vss_stores=None): """Determines the VSS store identifiers. Args: scan_node: the scan node (instance of dfvfs.ScanNode). vss_stores: Optional list of preferred VSS store identifiers. The default is None. Returns: A list of VSS store identifiers. Raises: SourceScannerError: if the format of or within the source is not supported or the the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError(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 volume_identifiers: return [] try: selected_store_identifiers = self._PromptUserForVSSStoreIdentifiers( volume_system, volume_identifiers, vss_stores=vss_stores) except KeyboardInterrupt: raise errors.UserAbort(u'File system scan aborted.') return selected_store_identifiers
def _PrintTSKPartitionIdentifiersOverview(self, volume_system, volume_identifiers): """Prints an overview of TSK partition identifiers. Args: volume_system (dfvfs.TSKVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: SourceScannerError: 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 = views.CLITabularTableView(column_names=column_names) for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( '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) self._output_writer.Write('\n')
def _PromptUserForPartitionIdentifier( self, volume_system, volume_identifiers): """Prompts the user to provide a partition identifier. Args: volume_system (dfvfs.TSKVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Returns: str: partition identifier or 'all'. Raises: SourceScannerError: if the source cannot be processed. """ self._output_writer.Write( u'The following partitions were found:\n' u'Identifier\tOffset (in bytes)\tSize (in bytes)\n') for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( u'Volume missing for identifier: {0:s}.'.format(volume_identifier)) volume_extent = volume.extents[0] self._output_writer.Write( u'{0:s}\t\t{1:d} (0x{1:08x})\t{2:s}\n'.format( volume.identifier, volume_extent.offset, self._FormatHumanReadableSize(volume_extent.size))) self._output_writer.Write(u'\n') while True: self._output_writer.Write( u'Please specify the identifier of the partition that should be ' u'processed.\nAll partitions can be defined as: "all". Note that you ' u'can abort with Ctrl^C.\n') selected_volume_identifier = self._input_reader.Read() 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' or selected_volume_identifier in volume_identifiers): break self._output_writer.Write( u'\n' u'Unsupported partition identifier, please try again or abort ' u'with Ctrl^C.\n' u'\n') self._output_writer.Write(u'\n') return selected_volume_identifier
def _ScanVolume(self, scan_context, volume_scan_node): """Scans the volume scan node for volume and file systems. Args: scan_context (dfvfs.SourceScannerContext): source scanner context. volume_scan_node (dfvfs.SourceScanNode): volume scan node. Raises: SourceScannerError: 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.SourceScannerError( 'Invalid or missing volume scan node.') selected_vss_stores = [] if not volume_scan_node.sub_nodes: self._ScanVolumeScanNode(scan_context, volume_scan_node, selected_vss_stores) 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, selected_vss_stores)
def _PrintAPFSVolumeIdentifiersOverview(self, volume_system, volume_identifiers): """Prints an overview of APFS volume identifiers. Args: volume_system (dfvfs.APFSVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: SourceScannerError: 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 = views.CLITabularTableView(column_names=column_names) for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( '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) self._output_writer.Write('\n')
def _PrintLVMVolumeIdentifiersOverview( self, volume_system, volume_identifiers): """Prints an overview of LVM volume identifiers. Args: volume_system (dfvfs.LVMVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: SourceScannerError: 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 = views.CLITabularTableView(column_names=column_names) for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( '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) self._output_writer.Write('\n')
def _GetNormalizedVShadowVolumeIdentifiers(self, volume_system, volume_identifiers): """Retrieves the normalized VShadow volume identifiers. Args: volume_system (dfvfs.VShadowVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Returns: list[int]: normalized volume identifiers. """ normalized_volume_identifiers = [] for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( '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 return normalized_volume_identifiers
def _GetVolumeVssStoreIdentifiers(self, scan_context, vss_stores=None): """Determines the VSS store identifiers. Args: scan_context: the scan context (instance of dfvfs.ScanContext). vss_stores: Optional list of preferred VSS stored identifiers. The default is None. Raises: SourceScannerError: if the format of or within the source is not supported or the the scan context is invalid. """ if (not scan_context or not scan_context.last_scan_node or not scan_context.last_scan_node.path_spec): raise errors.SourceScannerError(u'Invalid scan context.') volume_system = vshadow_volume_system.VShadowVolumeSystem() volume_system.Open(scan_context.last_scan_node.path_spec) volume_identifiers = self._source_scanner.GetVolumeIdentifiers( volume_system) if not volume_identifiers: return try: self.vss_stores = self._GetVssStoreIdentifiersFromUser( volume_system, volume_identifiers, vss_stores=vss_stores) except KeyboardInterrupt: raise errors.UserAbort(u'File system scan aborted.') return
def _ScanVolume(self, scan_context, scan_node, 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. base_path_specs (list[PathSpec]): file system base path specifications. Raises: SourceScannerError: 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.SourceScannerError('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(): self._ScanVolumeSystemRoot(scan_context, scan_node, base_path_specs) elif scan_node.IsFileSystem(): self._ScanFileSystem(scan_node, base_path_specs) elif scan_node.type_indicator == dfvfs_definitions.TYPE_INDICATOR_VSHADOW: if self._process_vss: # 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. if dfvfs_definitions.PREFERRED_NTFS_BACK_END == ( dfvfs_definitions.TYPE_INDICATOR_TSK): location = '/' else: location = '\\' path_spec = path_spec_factory.Factory.NewPathSpec( dfvfs_definitions.PREFERRED_NTFS_BACK_END, location=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, base_path_specs)
def _GetVSSStoreIdentifiers(self, scan_node): """Determines the VSS store identifiers. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: VSS store identifiers. Raises: SourceScannerError: if the scan node is invalid. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError('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 [] # TODO: refactor to use scan options. if self._vss_stores: if self._vss_stores == 'all': vss_stores = volume_system.volume_identifiers else: vss_stores = self._mediator.ParseVolumeIdentifiersString( self._vss_stores, prefix='vss') selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, vss_stores, prefix='vss') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers if self._unattended_mode: return [] 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 _GetVSSStoreIdentifiers(self, scan_node): """Determines the VSS store identifiers. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: VSS store identifiers. Raises: SourceScannerError: if the format of or within the source is not supported or the scan node is invalid. UserAbort: if the user requested to abort. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError('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 [] # TODO: refactor to use scan options. if self._vss_stores: if self._vss_stores == 'all': vss_stores = range(1, volume_system.number_of_volumes + 1) else: vss_stores = self._vss_stores selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, vss_stores, prefix='vss') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers try: volume_identifiers = self._PromptUserForVSSStoreIdentifiers( 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: SourceScannerError: if the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError( 'Invalid or missing file system scan node.') base_path_specs.append(scan_node.path_spec)
def _GetTSKPartitionIdentifiers(self, scan_node): """Determines the TSK partition identifiers. This method first checks for the preferred partition number, then falls back to prompt the user if no usable preferences were specified. Args: scan_node (dfvfs.SourceScanNode): scan node. Returns: list[str]: TSK partition identifiers. Raises: RuntimeError: if the volume for a specific identifier cannot be retrieved. SourceScannerError: 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.SourceScannerError('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 [] # TODO: refactor self._partitions to use scan options. if self._partitions: partitions = range(1, volume_system.number_of_volumes + 1) selected_volume_identifiers = self._NormalizedVolumeIdentifiers( volume_system, partitions, prefix='p') if not set(selected_volume_identifiers).difference( volume_identifiers): return selected_volume_identifiers if len(volume_identifiers) == 1: return volume_identifiers return self._NormalizedVolumeIdentifiers(volume_system, volume_identifiers, prefix='p')
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: SourceScannerError: 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.SourceScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) normalized_volume_identifiers.append(volume_identifier) return normalized_volume_identifiers
def _ScanVolumeScanNode(self, scan_context, volume_scan_node, selected_vss_stores): """Scans an individual volume scan node for volume and file systems. Args: scan_context (dfvfs.SourceScannerContext): source scanner context. volume_scan_node (dfvfs.SourceScanNode): volume scan node. selected_vss_stores (list[str]): selected VSS store identifiers. Raises: SourceScannerError: 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.SourceScannerError( '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 == dfvfs_definitions.TYPE_INDICATOR_VSHADOW: location = getattr(scan_node.path_spec, 'location', None) if location == '/': 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 ( dfvfs_definitions.ENCRYPTED_VOLUME_TYPE_INDICATORS): self._ScanVolumeScanNodeEncrypted(scan_context, scan_node) elif scan_node.type_indicator == dfvfs_definitions.TYPE_INDICATOR_VSHADOW: self._ScanVolumeScanNodeVSS(scan_node, selected_vss_stores) elif scan_node.type_indicator in ( dfvfs_definitions.FILE_SYSTEM_TYPE_INDICATORS): if (not self._vss_only or not selected_vss_stores or self._PromptUserForVSSCurrentVolume()): self._source_path_specs.append(scan_node.path_spec)
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: SourceScannerError: if the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError( 'Invalid or missing file system scan node.') if self._vss_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 _PrintVSSStoreIdentifiersOverview(self, volume_system, volume_identifiers): """Prints an overview of VSS store identifiers. Args: volume_system (dfvfs.VShadowVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Raises: SourceScannerError: 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 = views.CLITabularTableView(column_names=column_names) for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( '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) self._output_writer.Write('\n')
def _ScanVolume(self, scan_context, volume_scan_node): """Scans the 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). Raises: SourceScannerError: 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.SourceScannerError( u'Invalid or missing volume scan node.') if len(volume_scan_node.sub_nodes) == 0: self._ScanVolumeScanNode(scan_context, volume_scan_node) 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)
def _PromptUserForVSSStoreIdentifiers(self, volume_system, volume_identifiers, vss_stores=None): """Prompts the user to provide the VSS store identifiers. This method first checks for the preferred VSS stores and falls back to prompt the user if no usable preferences were specified. Args: volume_system (dfvfs.VShadowVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. vss_stores (Optional[list[str]]): preferred VSS store identifiers. Returns: list[str]: selected VSS store identifiers. Raises: SourceScannerError: if the source cannot be processed. """ normalized_volume_identifiers = self._GetNormalizedVShadowVolumeIdentifiers( volume_system, volume_identifiers) # TODO: refactor this to _GetVSSStoreIdentifiers. if vss_stores: if vss_stores == ['all']: # We need to set the stores to cover all vss stores. vss_stores = range(1, volume_system.number_of_volumes + 1) if not set(vss_stores).difference(normalized_volume_identifiers): return vss_stores print_header = True while True: if print_header: self._output_writer.Write( 'The following Volume Shadow Snapshots (VSS) were found:\n' ) table_view = views.CLITabularTableView( column_names=['Identifier', 'Creation Time']) for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier( volume_identifier) if not volume: raise errors.SourceScannerError( 'Volume missing for identifier: {0:s}.'.format( volume_identifier)) vss_creation_time = volume.GetAttribute('creation_time') filetime = dfdatetime_filetime.Filetime( timestamp=vss_creation_time.value) vss_creation_time = filetime.GetPlasoTimestamp() vss_creation_time = timelib.Timestamp.CopyToIsoFormat( vss_creation_time) if volume.HasExternalData(): vss_creation_time = ( '{0:s}\tWARNING: data stored outside volume' ).format(vss_creation_time) table_view.AddRow([volume.identifier, vss_creation_time]) self._output_writer.Write('\n') table_view.Write(self._output_writer) self._output_writer.Write('\n') print_header = False self._output_writer.Write( 'Please specify the identifier(s) of the VSS that should be ' 'processed:\nNote that a range of stores can be defined as: 3..5. ' 'Multiple stores can\nbe defined as: 1,3,5 (a list of comma ' 'separated values). Ranges and lists can\nalso be combined ' 'as: 1,3..5. The first store is 1. All stores can be defined\n' 'as "all". If no stores are specified none will be processed. You\n' 'can abort with Ctrl^C.\n') selected_vss_stores = self._input_reader.Read() selected_vss_stores = selected_vss_stores.strip() if not selected_vss_stores: return [] try: selected_vss_stores = self._ParseVSSStoresString( selected_vss_stores) except errors.BadConfigOption: 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 self._output_writer.Write( '\n' 'Unsupported VSS identifier(s), please try again or abort with ' 'Ctrl^C.\n' '\n') self._output_writer.Write('\n') return selected_vss_stores
def _PromptUserForPartitionIdentifier(self, volume_system, volume_identifiers): """Prompts the user to provide a partition identifier. Args: volume_system (dfvfs.TSKVolumeSystem): volume system. volume_identifiers (list[str]): allowed volume identifiers. Returns: str: partition identifier or "all". Raises: SourceScannerError: if the source cannot be processed. """ self._output_writer.Write('The following partitions were found:\n') table_view = views.CLITabularTableView(column_names=[ 'Identifier', 'Offset (in bytes)', 'Size (in bytes)' ]) for volume_identifier in sorted(volume_identifiers): volume = volume_system.GetVolumeByIdentifier(volume_identifier) if not volume: raise errors.SourceScannerError( 'Volume 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) self._output_writer.Write('\n') while True: self._output_writer.Write( 'Please specify the identifier of the partition that should be ' 'processed.\nAll partitions can be defined as: "all". Note that you ' 'can abort with Ctrl^C.\n') selected_volume_identifier = self._input_reader.Read() 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 if (selected_volume_identifier == 'all' or selected_volume_identifier in volume_identifiers): break self._output_writer.Write( '\n' 'Unsupported partition identifier, please try again or abort ' 'with Ctrl^C.\n' '\n') self._output_writer.Write('\n') return selected_volume_identifier
def ScanSource(self, source_path): """Scans the source path for volume and file systems. This function sets the internal source path specification and source type values. Args: source_path (str): path to the source. Returns: dfvfs.SourceScannerContext: source scanner context. Raises: SourceScannerError: if the format of or within the source is not supported. """ # Symbolic links are resolved here and not earlier to preserve the user # specified source path in storage and reporting. if os.path.islink(source_path): source_path = os.path.realpath(source_path) if (not source_path.startswith('\\\\.\\') and not os.path.exists(source_path)): raise errors.SourceScannerError( '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, dfvfs_errors.BackEndError) as exception: raise errors.SourceScannerError( 'Unable to scan source with error: {0!s}.'.format(exception)) if scan_context.source_type not in ( scan_context.SOURCE_TYPE_STORAGE_MEDIA_DEVICE, scan_context.SOURCE_TYPE_STORAGE_MEDIA_IMAGE): scan_node = scan_context.GetRootScanNode() self._source_path_specs.append(scan_node.path_spec) return scan_context # 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 != ( dfvfs_definitions.TYPE_INDICATOR_TSK_PARTITION): self._ScanVolume(scan_context, scan_node, base_path_specs) else: # Determine which partition needs to be processed. partition_identifiers = self._GetTSKPartitionIdentifiers(scan_node) if not partition_identifiers: raise errors.SourceScannerError('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, base_path_specs) if not base_path_specs: raise errors.SourceScannerError( 'No supported file system found in source.') self._source_path_specs = base_path_specs return scan_context
def ScanSource(self): """Scans the source path for volume and file systems. This function sets the internal source path specification and source type values. Returns: The scan context (instance of dfvfs.ScanContext). Raises: SourceScannerError: if the format of or within the source is not supported. """ if (not self._source_path.startswith(u'\\\\.\\') and not os.path.exists(self._source_path)): raise errors.SourceScannerError( u'No such device, file or directory: {0:s}.'.format( self._source_path)) scan_context = source_scanner.SourceScannerContext() scan_context.OpenSourcePath(self._source_path) try: self._source_scanner.Scan(scan_context) except (dfvfs_errors.BackEndError, ValueError) as exception: raise errors.SourceScannerError( u'Unable to scan source with error: {0:s}.'.format(exception)) if scan_context.source_type not in [ scan_context.SOURCE_TYPE_STORAGE_MEDIA_DEVICE, scan_context.SOURCE_TYPE_STORAGE_MEDIA_IMAGE ]: scan_node = scan_context.GetRootScanNode() self._source_path_specs.append(scan_node.path_spec) return scan_context # 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] # The source scanner found a partition table and we need to determine # which partition needs to be processed. if scan_node.type_indicator not in [ dfvfs_definitions.TYPE_INDICATOR_TSK_PARTITION ]: partition_identifiers = None else: partition_identifiers = self._GetTSKPartitionIdentifiers( scan_node, partition_string=self._partition_string, partition_offset=self._partition_offset) if not partition_identifiers: self._ScanVolume(scan_context, scan_node) else: for partition_identifier in partition_identifiers: location = u'/{0:s}'.format(partition_identifier) sub_scan_node = scan_node.GetSubNodeByLocation(location) self._ScanVolume(scan_context, sub_scan_node) if not self._source_path_specs: raise errors.SourceScannerError( u'No supported file system found in source.') return scan_context
def _PromptUserForVSSStoreIdentifiers(self, volume_system, volume_identifiers, vss_stores=None): """Prompts the user to provide the VSS store identifiers. This method first checks for the preferred VSS stores and falls back to prompt the user if no usable preferences were specified. Args: volume_system: The volume system (instance of dfvfs.VShadowVolumeSystem). volume_identifiers: List of allowed volume identifiers. vss_stores: Optional list of preferred VSS store identifiers. The default is None. Returns: The list of selected VSS store identifiers. Raises: SourceScannerError: 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.SourceScannerError( u'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 # TODO: refactor this to _GetVSSStoreIdentifiers. if vss_stores: if vss_stores == [u'all']: # We need to set the stores to cover all vss stores. vss_stores = range(1, volume_system.number_of_volumes + 1) if not set(vss_stores).difference(normalized_volume_identifiers): return vss_stores print_header = True while True: if print_header: self._output_writer.Write( u'The following Volume Shadow Snapshots (VSS) were found:\n' u'Identifier\t\tCreation Time\n') for volume_identifier in volume_identifiers: volume = volume_system.GetVolumeByIdentifier( volume_identifier) if not volume: raise errors.SourceScannerError( u'Volume missing for identifier: {0:s}.'.format( volume_identifier)) vss_creation_time = volume.GetAttribute(u'creation_time') vss_creation_time = timelib.Timestamp.FromFiletime( vss_creation_time.value) vss_creation_time = timelib.Timestamp.CopyToIsoFormat( vss_creation_time) if volume.HasExternalData(): external_data = u'\tWARNING: data stored outside volume' else: external_data = u'' self._output_writer.Write( u'{0:s}\t\t\t{1:s}{2:s}\n'.format( volume.identifier, vss_creation_time, external_data)) self._output_writer.Write(u'\n') print_header = False self._output_writer.Write( u'Please specify the identifier(s) of the VSS that should be ' u'processed:\nNote that a range of stores can be defined as: 3..5. ' u'Multiple stores can\nbe defined as: 1,3,5 (a list of comma ' u'separated values). Ranges and lists can\nalso be combined ' u'as: 1,3..5. The first store is 1. All stores can be defined\n' u'as "all". If no stores are specified none will be processed. You\n' u'can abort with Ctrl^C.\n') selected_vss_stores = self._input_reader.Read() selected_vss_stores = selected_vss_stores.strip() if not selected_vss_stores: return [] try: selected_vss_stores = self._ParseVSSStoresString( selected_vss_stores) except errors.BadConfigOption: selected_vss_stores = [] if selected_vss_stores == [u'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 self._output_writer.Write( u'\n' u'Unsupported VSS identifier(s), please try again or abort with ' u'Ctrl^C.\n' u'\n') return selected_vss_stores
def _GetTSKPartitionIdentifiers(self, scan_node, partition_string=None, partition_offset=None): """Determines the TSK partition identifiers. This method first checks for the preferred partition number, then for the preferred partition offset and falls back to prompt the user if no usable preferences were specified. Args: scan_node: the scan node (instance of dfvfs.ScanNode). partition_string: Optional preferred partition number string. The default is None. partition_offset: Optional preferred partition byte offset. The default is None. Returns: A list of partition identifiers. Raises: RuntimeError: if the volume for a specific identifier cannot be retrieved. SourceScannerError: if the format of or within the source is not supported or the the scan node is invalid. """ if not scan_node or not scan_node.path_spec: raise errors.SourceScannerError(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: self._output_writer.Write(u'[WARNING] No partitions found.\n') return if partition_string == u'all': return volume_identifiers if partition_string is not None and not partition_string.startswith( u'p'): return volume_identifiers partition_number = None if partition_string: try: partition_number = int(partition_string[1:], 10) except ValueError: pass if partition_number is not None and partition_number > 0: # Plaso uses partition numbers starting with 1 while dfvfs expects # the volume index to start with 0. volume = volume_system.GetVolumeByIndex(partition_number - 1) if volume: return [u'p{0:d}'.format(partition_number)] self._output_writer.Write( u'[WARNING] No such partition: {0:d}.\n'.format( partition_number)) if partition_offset is not None: for volume in volume_system.volumes: volume_extent = volume.extents[0] if volume_extent.offset == partition_offset: return [volume.identifier] self._output_writer.Write( (u'[WARNING] No such partition with offset: {0:d} ' u'(0x{0:08x}).\n').format(partition_offset)) if len(volume_identifiers) == 1: return volume_identifiers try: selected_volume_identifier = self._PromptUserForPartitionIdentifier( volume_system, volume_identifiers) except KeyboardInterrupt: raise errors.UserAbort(u'File system scan aborted.') if selected_volume_identifier == u'all': return volume_identifiers return [selected_volume_identifier]