diff --git a/volatility/framework/automagic/__init__.py b/volatility/framework/automagic/__init__.py index 9407f3f61..8da855edb 100644 --- a/volatility/framework/automagic/__init__.py +++ b/volatility/framework/automagic/__init__.py @@ -12,7 +12,7 @@ import sys import traceback import typing -from volatility.framework import class_subclasses, import_files, interfaces, validity +from volatility.framework import class_subclasses, import_files, interfaces, validity, constants from volatility.framework.automagic import construct_layers, stacker, windows, pdbscan from volatility.framework.configuration import requirements @@ -42,7 +42,7 @@ def available(context: interfaces.context.ContextInterface) \ :type context: volatility.framework.interfaces.context.ContextInterface """ import_files(sys.modules[__name__]) - config_path = 'automagic' + config_path = constants.AUTOMAGIC_CONFIG_PATH return sorted([clazz(context, interfaces.configuration.path_join(config_path, clazz.__name__)) for clazz in class_subclasses(interfaces.automagic.AutomagicInterface)], key = lambda x: x.priority) diff --git a/volatility/framework/automagic/stacker.py b/volatility/framework/automagic/stacker.py index 7d19d3c35..9d8e19be8 100644 --- a/volatility/framework/automagic/stacker.py +++ b/volatility/framework/automagic/stacker.py @@ -34,6 +34,7 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): # Most important automagic, must happen first! priority = 10 page_map_offset = None + cache = {} def __call__(self, context: interfaces.context.ContextInterface, @@ -54,17 +55,51 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): return unsatisfied if not self.config or not self.config.get('single_location', None): raise ValueError("Unable to run LayerStacker, single_location parameter not provided") - location = self.config['single_location'] - self._check_type(location, str) self._check_type(requirement, interfaces.configuration.RequirementInterface) + # If we've already run for this requirement, stick the configuration under the new config_path + if requirement.name in self.cache: + stacked_layers, config = self.cache[requirement] + print(" CONFIG", dict(config)) + context.config.merge(config_path, config) + return stacked_layers + + # Search for suitable requirements + stacked_layers = self.stack(context, config_path, [requirement], progress_callback) + + if stacked_layers: + # Call the construction magic now we may have new things to construct + constructor = construct_layers.ConstructionMagic(context, + interfaces.configuration.path_join(self.config_path, + "ConstructionMagic")) + constructor(context, config_path, requirement) + print(dict(context.config), config_path) + self.cache[requirement.name] = context.config[config_path], stacked_layers + return None + + def stack(self, + context: interfaces.context.ContextInterface, + config_path: str, + requirements: typing.List[interfaces.configuration.RequirementInterface], + progress_callback: validity.ProgressCallback) -> typing.List[str]: + """Stacks the various layers and attaches these to a specific requirement + + :param context: Context on which to operate + :param config_path: Configuration path under which to store stacking data + :param location: File URL for the underlying physical layer + :param requirements: List of requirements, each of which has the stack built on the first suitable (sub-)requirement + :param progress_callback: Function to provide callback progress + """ + location = self.config.get('single_location', None) + self._check_type(location, str) + # Setup the local copy of the resource new_context = context.clone() current_layer_name = context.memory.free_layer_name("FileLayer") current_config_path = interfaces.configuration.path_join(config_path, "stack", current_layer_name) + # This must be specific to get us started, setup the config and run new_context.config[interfaces.configuration.path_join(current_config_path, "location")] = location - physical_layer = physical.FileLayer(new_context, current_config_path, current_layer_name) new_context.add_layer(physical_layer) @@ -100,18 +135,15 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): vollog.debug("Stacked layers: {}".format(stacked_layers)) if stacked_layers: + # Applies the stacked_layers to each requirement in the requirements list + for requirement in requirements: + result = self.find_suitable_requirements(stacked_layers, requirement, new_context, config_path) + if result: + path, layer = result + # splice in the new configuration into the original context + context.config.merge(path, new_context.memory[layer].build_configuration()) - result = self.find_suitable_requirements(stacked_layers, requirement, new_context, config_path) - if result: - path, layer = result - # splice in the new configuration into the original context - context.config.merge(path, new_context.memory[layer].build_configuration()) - # Call the construction magic now we may have new things to construct - constructor = construct_layers.ConstructionMagic(context, - interfaces.configuration.path_join(self.config_path, - "ConstructionMagic")) - constructor(context, config_path, requirement) - return None + return stacked_layers def find_suitable_requirements(self, stacked_layers: typing.List, diff --git a/volatility/framework/constants/__init__.py b/volatility/framework/constants/__init__.py index 76a8f12fb..eebf7be6d 100644 --- a/volatility/framework/constants/__init__.py +++ b/volatility/framework/constants/__init__.py @@ -2,9 +2,10 @@ Stores all the constant values that are generally fixed throughout volatility This includes default scanning block sizes, etc.""" -import os.path import sys +import os.path + import volatility.framework.constants.linux PLUGINS_PATH = [os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "plugins")), @@ -14,6 +15,7 @@ SYMBOL_BASEPATHS = [os.path.abspath(os.path.join(os.path.dirname(__file__), ".." BANG = "!" PACKAGE_VERSION = "3.0.0_alpha1" DISABLE_MULTITHREADED_SCANNING = False +AUTOMAGIC_CONFIG_PATH = 'automagic' LOGLEVEL_V = 9 LOGLEVEL_VV = 8