def _extract_files(self, artifact: ResolvedArtifact, artifact_output: ForensicStore) -> bool: """ Extract an artifact's files and folders to the forensic store :param artifact: artifact to extract :param artifact_output: Output forensic store :type artifact_output: ForensicStore :type artifact: ResolvedArtifact :return: True on success, False if nothing was written """ artifact_name = artifact.artifact.name success = False for export_file in artifact.files: success = True file_infos = get_file_infos(export_file) if not file_infos: LOGGER.warning("Could not get file infos for \"%s\". Skipping", dfvfs_helper.reconstruct_full_path(export_file)) continue if file_infos['type'] != dfvfs_defs.FILE_ENTRY_TYPE_FILE: LOGGER.debug("Not exporting entry of wrong type: %s", dfvfs_helper.reconstruct_full_path(export_file)) continue file_contents = dfvfs_helper.get_file_handle(export_file) if not file_contents: LOGGER.warning("Could not get file contents for \"%s\"", dfvfs_helper.reconstruct_full_path(export_file)) success = False continue store_obj_id = artifact_output.add_file_element( artifact_name, file_infos['name'], created=file_infos.get('created', None), modified=file_infos.get('modified', None), accessed=file_infos.get('accessed', None), origin={ 'path': file_infos['path'], 'partition': self.partition_name }, errors=None) output_name = f"{self.partition_name}_" \ f"{dfvfs_helper.get_relative_path(export_file).replace('/', '_').strip('_')}" with artifact_output.add_file_element_export( store_obj_id, export_name=output_name) as file_export: chunk_size = 65536 data = file_contents.read(chunk_size) while data: file_export.write(data) data = file_contents.read(chunk_size) file_contents.close() return success
def __init__(self, source_paths: List[str], result_root: fs.base.FS, artifact_registry: Registry, encryption_handler: dfvfs_helper.EncryptionHandler): self.dfvfs_list = [] self.artifact_registry = artifact_registry self.tmp_dir = None new_source_path = source_paths[0] self.dfvfs_list = [ dfvfs_helper.DFVFSHelper(new_source_path, encryption_handler) ] self.encryption_handler = encryption_handler # noinspection PyTypeChecker self.store = ForensicStore(result_root)
def find_store(local_path): store = None for root, dirs, files in os.walk(local_path): if root.endswith('.forensicstore'): assert store is None # There is only one partition here store = ForensicStore(root) assert store is not None return store
def _key_to_forensicstore(store: ForensicStore, key: WinRegistryKey, values: Optional[List[str]] = None, artifact: str = '') -> None: """ Export a registry key to the forensicstore, optionally only picking certain values from the key """ last_write_tuple = key.last_written_time.CopyToStatTimeTuple() if last_write_tuple[0]: last_write_date = datetime.utcfromtimestamp(last_write_tuple[0]) last_write_date = last_write_date.replace( microsecond=(int(last_write_tuple[1] / 10))) else: last_write_date = datetime.utcfromtimestamp(0) try: key_item_id = store.add_registry_key_element( artifact=artifact, modified=last_write_date, key=key.path, errors=None) except TypeError as err: LOGGER.exception("Error adding registry key: %s", err) return for value in key.GetValues(): name = value.name if values and name not in values: continue if name is None: name = '(Default)' type_str = value.data_type_string if type_str == 'REG_DWORD_LE': type_str = 'REG_DWORD' # if value.data and (value.DataIsBinaryData() or value.data_type_string == "REG_NONE"): # data = value.data # elif value.data: # data = '{}'.format(value.GetDataAsObject()) # else: # data = b"" try: store.add_registry_value_element(key_item_id, type_str, value.data, name) except sqlite3.OperationalError: LOGGER.exception("Error updating value") continue
class ArtifactExtractor(object): """ This is the main class that manages the artifact extraction from any dfVFS-supported input """ # pylint: disable=too-few-public-methods def __init__(self, source_paths: List[str], result_root: fs.base.FS, artifact_registry: Registry, encryption_handler: dfvfs_helper.EncryptionHandler): self.dfvfs_list = [] self.artifact_registry = artifact_registry self.tmp_dir = None new_source_path = source_paths[0] self.dfvfs_list = [ dfvfs_helper.DFVFSHelper(new_source_path, encryption_handler) ] self.encryption_handler = encryption_handler # noinspection PyTypeChecker self.store = ForensicStore(result_root) def clean_up(self): """ Called when no more actions will be called in this object """ for d in self.dfvfs_list: d.clean_up() self.dfvfs_list = None self.encryption_handler = None if self.tmp_dir: self.tmp_dir.cleanup() self.tmp_dir = None def extract_artifact(self, artifact_name): # pylint: disable=invalid-name """ Extract a particular artifact from all possible locations within this dfvfs-image """ real_partitions: List[PartitionInfo] = [] # forensic images can have more than one partition, but we always only process one image at a time real_partitions = [ PartitionInfo(helper=self.dfvfs_list[0], path_spec=partition, name=chr(ord('c') + i)) for i, partition in enumerate(self.dfvfs_list[0].partitions()) if not dfvfs_utils.is_on_filesystem( partition, dfvfs_defs.TYPE_INDICATOR_VSHADOW) ] LOGGER.info("Found %d partitions", len(real_partitions)) for partinfo in real_partitions: current_os = self._guess_os(partinfo.helper, partinfo.path_spec) try: if current_os == definitions.OPERATING_SYSTEM_WINDOWS: system = WindowsSystem(partinfo.helper, partinfo.path_spec) elif current_os == definitions.OPERATING_SYSTEM_UNKNOWN: system = UnknownOS() LOGGER.warning( "Operating system not detected on partition %s. Only basic extraction possible.", dfvfs_utils.reconstruct_full_path(partinfo.path_spec)) else: LOGGER.warning( "Operating system %s is not yet supported on %s. Using basic extraction.", dfvfs_utils.reconstruct_full_path(partinfo.path_spec), current_os) system = UnknownOS() LOGGER.info("=== Starting processing of partition") resolver = ArtifactResolver(partinfo, self.artifact_registry, system) resolver.process_artifact(artifact_name, self.store) if current_os == definitions.OPERATING_SYSTEM_WINDOWS: system._reg_reader._cleanup_open_files("") # TODO except RuntimeError as err: LOGGER.exception( "Encountered exception during processing of %s: %s", dfvfs_utils.reconstruct_full_path(partinfo.path_spec), err) if 'pytest' in sys.modules: raise # we want to see what exactly is failing when tests are running self.store.close() @staticmethod def _guess_os(dfvfs, partition): """Tries to determine the underlying operating system. Adapted from plaso/engine.py Returns: str: operating system, for example "Windows". This should be one of the values in definitions.OPERATING_SYSTEMS. """ find_specs = [ '/etc', '/System/Library', '/Windows/System32', '/WINNT/System32', '/WINNT35/System32', '/WTSRV/System32', ] locations = [] for path_spec in dfvfs.find_paths(find_specs, partitions=[partition]): path = dfvfs_utils.get_relative_path(path_spec) if path: locations.append(path.lower().rstrip('/')) # We need to check for both forward and backward slashes since the path # spec will be OS dependent, as in running the tool on Windows will return # Windows paths (backward slash) vs. forward slash on *NIX systems. windows_locations = { '/windows/system32', '\\windows\\system32', '/winnt/system32', '\\winnt\\system32', '/winnt35/system32', '\\winnt35\\system32', '\\wtsrv\\system32', '/wtsrv/system32' } if windows_locations.intersection(set(locations)): return definitions.OPERATING_SYSTEM_WINDOWS if '/system/library' in locations: return definitions.OPERATING_SYSTEM_MACOSX if '/etc' in locations: return definitions.OPERATING_SYSTEM_LINUX return definitions.OPERATING_SYSTEM_UNKNOWN