Adapt Automagic to have requirements, and convert the Stacker's single_location parameter to make use of it.

This commit is contained in:
Mike Auty
2016-12-29 15:21:39 +00:00
parent 7f0fb89da5
commit 5fe078e487
7 changed files with 45 additions and 19 deletions
+2 -2
View File
@@ -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
+3 -2
View File
@@ -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)
+2 -2
View File
@@ -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):
+15 -3
View File
@@ -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)]
+4 -2
View File
@@ -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")
@@ -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,
+12 -1
View File
@@ -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"""