From 5fe078e48743fd57d738ef9d39d16a7e51bdc824 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 29 Dec 2016 15:21:39 +0000 Subject: [PATCH] Adapt Automagic to have requirements, and convert the Stacker's single_location parameter to make use of it. --- volatility/cli/__init__.py | 4 ++-- volatility/framework/automagic/__init__.py | 5 +++-- volatility/framework/automagic/pdbscan.py | 4 ++-- volatility/framework/automagic/stacker.py | 18 +++++++++++++++--- volatility/framework/automagic/windows.py | 6 ++++-- .../framework/configuration/requirements.py | 14 +++++++------- volatility/framework/interfaces/automagic.py | 13 ++++++++++++- 7 files changed, 45 insertions(+), 19 deletions(-) diff --git a/volatility/cli/__init__.py b/volatility/cli/__init__.py index ce00dc4e1..78298517b 100644 --- a/volatility/cli/__init__.py +++ b/volatility/cli/__init__.py @@ -95,14 +95,14 @@ class CommandLine(object): if not args.file or not os.path.exists(args.file): raise RuntimeError("Please provide a valid filename") else: - ctx.config["automagic.general.single_location"] = "file://" + os.path.abspath(args.file) + ctx.config["automagic.LayerStacker.single_location"] = "file://" + os.path.abspath(args.file) pass ### # BACK TO THE FRAMEWORK ### # Clever magic figures out how to fulfill each requirement that might not be fulfilled - automagics = automagic.available() + automagics = automagic.available(ctx) automagic.run(automagics, ctx, plugin, "plugins", progress_callback = progress_callback) # Check all the requirements and/or go back to the automagic step diff --git a/volatility/framework/automagic/__init__.py b/volatility/framework/automagic/__init__.py index 1c4e21385..5936f6aaa 100644 --- a/volatility/framework/automagic/__init__.py +++ b/volatility/framework/automagic/__init__.py @@ -17,14 +17,15 @@ from volatility.framework.configuration import requirements vollog = logging.getLogger(__name__) -def available(): +def available(context, config_path = 'automagic'): """Returns an ordered list of all subclasses of :class:`~volatility.framework.interfaces.automagic.AutomagicInterface`. The order is based on the priority attributes of the subclasses, in order to ensure the automagics are listed in an appropriate order. """ import_files(sys.modules[__name__]) - return sorted([clazz() for clazz in class_subclasses(interfaces.automagic.AutomagicInterface)], + 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/pdbscan.py b/volatility/framework/automagic/pdbscan.py index 46cc713a2..596e31647 100644 --- a/volatility/framework/automagic/pdbscan.py +++ b/volatility/framework/automagic/pdbscan.py @@ -126,8 +126,8 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface): suffixes = ['.json', '.json.xz'] """Provides a list of supported suffixes for Intermediate Format data files""" - def __init__(self): - super().__init__() + def __init__(self, context, config_path): + super().__init__(context, config_path) self.valid_kernels = [] def recurse_pdb_finder(self, context, config_path, requirement, progress_callback = None): diff --git a/volatility/framework/automagic/stacker.py b/volatility/framework/automagic/stacker.py index 304a4d3ba..1d890b0c2 100644 --- a/volatility/framework/automagic/stacker.py +++ b/volatility/framework/automagic/stacker.py @@ -11,8 +11,10 @@ import logging from urllib import parse import volatility +from volatility.framework import configuration from volatility.framework import interfaces from volatility.framework.automagic import construct_layers +from volatility.framework.configuration import requirements from volatility.framework.layers import physical vollog = logging.getLogger(__name__) @@ -42,9 +44,9 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): return # Bow out quickly if the UI hasn't provided a single_location - if "automagic.general.single_location" not in context.config: + if not self.validate(self.context, self.config_path): return - location = context.config["automagic.general.single_location"] + location = self.config["single_location"] self._check_type(location, str) self._check_type(requirement, interfaces.configuration.RequirementInterface) self.location = parse.urlparse(location) @@ -98,7 +100,9 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): # splice in the new configuration into the original context context.config.splice(path, new_context.memory[layer].build_configuration()) # Call the construction magic now we may have new things to construct - constructor = construct_layers.ConstructionMagic() + constructor = construct_layers.ConstructionMagic(context, + interfaces.configuration.path_join(self.config_path, + "ConstructionMagic")) constructor(context, config_path, requirement) def find_suitable_requirements(self, stacked_layers, requirement, context, config_path): @@ -125,3 +129,11 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): result = self.find_suitable_requirements(stacked_layers, req, context, child_config_path) if result: return result + + @classmethod + def get_requirements(cls): + # This is not optional for the stacker to run, so optional must be marked as False + return [requirements.StringRequirement("single_location", + description = "Specifies a base location on which to stack", + default = "", + optional = False)] diff --git a/volatility/framework/automagic/windows.py b/volatility/framework/automagic/windows.py index b8c5b484e..1861661fb 100644 --- a/volatility/framework/automagic/windows.py +++ b/volatility/framework/automagic/windows.py @@ -208,7 +208,7 @@ class PageMapScanner(interfaces.layers.ScannerInterface): yield (test, result) -class WintelHelper(interfaces.automagic.AutomagicInterface, interfaces.automagic.StackerLayerInterface): +class WintelHelper(interfaces.automagic.AutomagicInterface): """This class if both an :class:`~volatility.framework.interfaces.automagic.AutomagicInterface` and a :class:`~volatility.framework.interfaces.automagic.StackerLayerInterface` class. @@ -245,6 +245,8 @@ class WintelHelper(interfaces.automagic.AutomagicInterface, interfaces.automagic for subreq in requirement.requirements.values(): self(context, sub_config_path, subreq) + +class WintelStacker(interfaces.automagic.StackerLayerInterface): @classmethod def stack(cls, context, layer_name, progress_callback = None): """Attempts to determine and stack an intel layer on a physical layer where possible @@ -255,7 +257,7 @@ class WintelHelper(interfaces.automagic.AutomagicInterface, interfaces.automagic that range, and ignore any that contain multiple self-references (since the DTB is very unlikely to point to itself more than once). """ - hits = context.memory[layer_name].scan(context, PageMapScanner(cls.tests)) + hits = context.memory[layer_name].scan(context, PageMapScanner(WintelHelper.tests)) layer = None for test, dtb in hits: new_layer_name = context.memory.free_layer_name("IntelLayer") diff --git a/volatility/framework/configuration/requirements.py b/volatility/framework/configuration/requirements.py index e5dc3a1de..c846f4312 100644 --- a/volatility/framework/configuration/requirements.py +++ b/volatility/framework/configuration/requirements.py @@ -8,17 +8,17 @@ etc) as well as indicating what they expect to be in the context (such as partic import logging from volatility.framework import constants -from volatility.framework import interfaces +from volatility.framework.interfaces import configuration as interfaces_configuration vollog = logging.getLogger(__name__) # Allow these two to be imported directly from requirements # This helps prevent import loops since other interfaces need to be able to check instances of this -TranslationLayerRequirement = interfaces.configuration.TranslationLayerRequirement -SymbolRequirement = interfaces.configuration.SymbolRequirement +TranslationLayerRequirement = interfaces_configuration.TranslationLayerRequirement +SymbolRequirement = interfaces_configuration.SymbolRequirement -class MultiRequirement(interfaces.configuration.RequirementInterface): +class MultiRequirement(interfaces_configuration.RequirementInterface): """Class to hold multiple requirements. Technically the Interface could handle this, but it's an interface, so this is a concrete implementation. @@ -28,7 +28,7 @@ class MultiRequirement(interfaces.configuration.RequirementInterface): return self.validate_children(context, config_path) -class InstanceRequirement(interfaces.configuration.RequirementInterface): +class InstanceRequirement(interfaces_configuration.RequirementInterface): """Class to represent a single simple type (such as a boolean, a string, an integer or a series of bytes)""" instance_type = bool @@ -68,7 +68,7 @@ class BytesRequirement(InstanceRequirement): instance_type = bytes -class ChoiceRequirement(interfaces.configuration.RequirementInterface): +class ChoiceRequirement(interfaces_configuration.RequirementInterface): """Allows one from a choice of strings""" def __init__(self, choices, *args, **kwargs): @@ -91,7 +91,7 @@ class ChoiceRequirement(interfaces.configuration.RequirementInterface): return True -class ListRequirement(interfaces.configuration.RequirementInterface): +class ListRequirement(interfaces_configuration.RequirementInterface): """Allows for a list of a specific type of requirement (all of which must be met for this requirement to be met) to be specified This roughly correlates to allowing a number of arguments to follow a command line parameter, diff --git a/volatility/framework/interfaces/automagic.py b/volatility/framework/interfaces/automagic.py index 0bff650f3..2be2b12d4 100644 --- a/volatility/framework/interfaces/automagic.py +++ b/volatility/framework/interfaces/automagic.py @@ -3,13 +3,24 @@ from abc import ABCMeta, abstractmethod from volatility.framework import validity +from volatility.framework.configuration import requirements +from volatility.framework.interfaces import configuration as interfaces_configuration -class AutomagicInterface(validity.ValidityRoutines, metaclass = ABCMeta): +class AutomagicInterface(interfaces_configuration.ConfigurableInterface, metaclass = ABCMeta): """Class that defines an automagic component that can help fulfill a Requirement""" priority = 10 + def __init__(self, context, config_path, *args, **kwargs): + super().__init__(context, config_path) + for requirement in self.get_requirements(): + if not isinstance(requirement, (requirements.InstanceRequirement, + requirements.ChoiceRequirement, + requirements.ListRequirement)): + raise ValueError( + "Automagic requirements must be an InstanceRequirement, ChoiceRequirement or ListRequirement") + @abstractmethod def __call__(self, context, config_path, configurable, progress_callback = None): """Runs the automagic over the configurable"""