From 3dc161f9b452c459ea4c64c3a3cc424143302b69 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 22 Aug 2016 02:20:10 +0100 Subject: [PATCH] Rework Translation Layers to tie more closely to configurations This change is quite signficant, and requires that TranslationLayers get all additional parameters that they need through their requirements. These are now automatically enumerated and populated on object construction based on the requirements, so should not require lots of repetitive filling out of fields. It does come with the downside that TranslationLayers can only be contructed with a context (and appropiate config), but TLs in particular always require a context (to contain the base layer) and blank configs can be constructed relatively easily (convenience functions can be added if necessary). This allows configuration trees to be built up, and their configs spliced into an existing config (as if it were being loaded from a file). Not all ConstructableRequirements use this method, since SymbolTables (for example) do not have access to the context or config_path in order to get to any parameters stored in the context's config. They therefore are still passed their requirement values as __init__ parameters instead. --- volatility/framework/automagic/stacker.py | 17 +++++++++-------- volatility/framework/automagic/windows.py | 19 +++++++++++-------- .../framework/interfaces/configuration.py | 15 ++++++++++----- volatility/framework/layers/intel.py | 6 +++--- volatility/framework/layers/physical.py | 8 ++++---- 5 files changed, 37 insertions(+), 28 deletions(-) diff --git a/volatility/framework/automagic/stacker.py b/volatility/framework/automagic/stacker.py index 6f3398889..bcd544dc3 100644 --- a/volatility/framework/automagic/stacker.py +++ b/volatility/framework/automagic/stacker.py @@ -24,11 +24,11 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): self.local_store = self.location.path new_context = context.clone() - current_layer = context.memory.free_layer_name() - # This must be specific to get us started - new_context.add_layer( - physical.FileLayer(new_context, interfaces.configuration.path_join("automagic_general", current_layer), - current_layer, self.local_store)) + current_layer_name = context.memory.free_layer_name() + current_config_path = interfaces.configuration.path_join("automagic_general", 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, "filename")] = self.local_store + new_context.add_layer(physical.FileLayer(new_context, current_config_path, current_layer_name)) # Repeatedly apply "determine what this is" code and build as much up as possible stacked = True @@ -42,15 +42,16 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): for stacker_cls in stack_set: stacker = stacker_cls() try: - new_layer = stacker.stack(new_context, current_layer) + new_layer = stacker.stack(new_context, current_layer_name) + new_context.memory.add_layer(new_layer) break except Exception as excp: pass else: stacked = False if new_layer and stacker_cls: - stacked_layers = [new_layer] + stacked_layers - current_layer = new_layer + stacked_layers = [new_layer.name] + stacked_layers + current_layer_name = new_layer.name stacked = True stack_set.remove(stacker_cls) diff --git a/volatility/framework/automagic/windows.py b/volatility/framework/automagic/windows.py index 6a6b770bd..e84e0b4e6 100644 --- a/volatility/framework/automagic/windows.py +++ b/volatility/framework/automagic/windows.py @@ -183,15 +183,17 @@ class IntelHelper(interfaces.automagic.AutomagicInterface, interfaces.automagic. def stack(cls, context, layer_name): """Attempts to determine and stack an intel layer on a physical layer where possible""" hits = context.memory[layer_name].scan(context, PageMapScanner(cls.tests)) - new_layer = None + layer = None for test, dtb in hits: - new_layer = context.memory.free_layer_name("IntelLayer") + new_layer_name = context.memory.free_layer_name("IntelLayer") + config_path = interfaces.configuration.path_join("IntelHelper", new_layer_name) + context.config[interfaces.configuration.path_join(config_path, "memory_layer")] = layer_name + context.config[interfaces.configuration.path_join(config_path, "page_map_offset")] = dtb layer = test.layer_type(context, - config_path = interfaces.configuration.path_join("IntelHelper", new_layer), - name = new_layer, - page_map_offset = dtb) + config_path = config_path, + name = new_layer_name) break - return new_layer + return layer if __name__ == '__main__': @@ -215,10 +217,11 @@ if __name__ == '__main__': nativelst = native.x86NativeTable ctx = contexts.Context(nativelst) for filename in args.filenames: + ctx.config[ + interfaces.configuration.path_join('config' + str(args.filenames.index(filename)), "filename")] = filename data = layers.physical.FileLayer(ctx, 'config' + str(args.filenames.index(filename)), - 'data' + str(args.filenames.index(filename)), - filename = filename) + 'data' + str(args.filenames.index(filename))) ctx.memory.add_layer(data) tests = [] diff --git a/volatility/framework/interfaces/configuration.py b/volatility/framework/interfaces/configuration.py index 9ce843b25..053517ce3 100644 --- a/volatility/framework/interfaces/configuration.py +++ b/volatility/framework/interfaces/configuration.py @@ -163,11 +163,6 @@ class ConstructableRequirementInterface(RequirementInterface): if requirement_dict is None: requirement_dict = {} - node_config = context.config.branch(config_path) - # Construct the class - for req in cls.get_requirements(): - if req.name in node_config.data and req.name != "class": - requirement_dict[req.name] = node_config.data[req.name] # Fulfillment must happen, exceptions happening here mean the requirements aren't correct # and these need to be raised and fixed, rather than caught and ignored obj = cls(**requirement_dict) @@ -184,6 +179,12 @@ class ConfigurableInterface(validity.ValidityRoutines, metaclass = ABCMeta): self._context = self._check_type(context, ContextInterface) self._config_path = self._check_type(config_path, str) + # Store these programmatically, so we don't keep repreating the requirements + # This also allows constructed objects to populate a configuration without too much trouble + for requirement in self.get_requirements(): + # Create the (private) properties using the config as backend storage + setattr(self, "_" + requirement.name, self.config.get(requirement.name, requirement.default)) + @property def context(self): return self._context @@ -192,6 +193,10 @@ class ConfigurableInterface(validity.ValidityRoutines, metaclass = ABCMeta): def config_path(self): return self._config_path + @config_path.setter + def config_path(self, value): + self._config_path = self._check_type(value, str) + @property def config(self): return self._context.config.branch(self._config_path) diff --git a/volatility/framework/layers/intel.py b/volatility/framework/layers/intel.py index 944412161..025ffaf88 100644 --- a/volatility/framework/layers/intel.py +++ b/volatility/framework/layers/intel.py @@ -19,10 +19,10 @@ class Intel(interfaces.layers.TranslationLayerInterface): "architecture": "ia32" } - def __init__(self, context, config_path, name, page_map_offset, memory_layer, swap_layer = None): + def __init__(self, context, config_path, name): super().__init__(context, config_path, name) - self._base_layer = self._check_type(memory_layer, str) - self._page_map_offset = self._check_type(page_map_offset, int) + self._base_layer = self._check_type(self.config["memory_layer"], str) + self._page_map_offset = self._check_type(self.config["page_map_offset"], int) # All Intel address spaces work on 4096 byte pages self._page_size_in_bits = 12 diff --git a/volatility/framework/layers/physical.py b/volatility/framework/layers/physical.py index f117afba8..7bac74b5c 100644 --- a/volatility/framework/layers/physical.py +++ b/volatility/framework/layers/physical.py @@ -63,16 +63,16 @@ class FileLayer(interfaces.layers.DataLayerInterface): provides = {"type": "physical"} priority = 20 - def __init__(self, context, config_path, name, filename): + def __init__(self, context, config_path, name): super().__init__(context, config_path, name) - self._filename = filename + self._filename = self.config["filename"] self._file_ = None - self._size = os.path.getsize(filename) + self._size = os.path.getsize(self._filename) @property def _file(self): - """Property to prevent the intializer storing an unserializable open file (for context cloning)""" + """Property to prevent the initializer storing an unserializable open file (for context cloning)""" # FIXME: Add "+" to the mode once we've determined whether write mode is enabled mode = "rb" if not self._file_: