mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-11 12:17:38 +02:00
Adapt Automagic to have requirements, and convert the Stacker's single_location parameter to make use of it.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"""
|
||||
|
||||
Reference in New Issue
Block a user