From 33d1c696e5f2353ba54cc8e82c76749e57404d76 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 1 Jul 2020 02:28:37 +0100 Subject: [PATCH] Automagic: Allow stackers to be configurable --- volatility/cli/__init__.py | 2 + volatility/framework/automagic/linux.py | 2 +- volatility/framework/automagic/mac.py | 2 +- volatility/framework/automagic/stacker.py | 74 ++++++++++++++++++++--- volatility/framework/automagic/windows.py | 2 +- 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/volatility/cli/__init__.py b/volatility/cli/__init__.py index 98097f7f0..d06622cf0 100644 --- a/volatility/cli/__init__.py +++ b/volatility/cli/__init__.py @@ -26,6 +26,7 @@ import volatility.symbols from volatility import framework from volatility.cli import text_renderer, volargparse from volatility.framework import automagic, constants, contexts, exceptions, interfaces, plugins, configuration +from volatility.framework.automagic import stacker from volatility.framework.configuration import requirements # Make sure we log everything @@ -270,6 +271,7 @@ class CommandLine(interfaces.plugins.FileConsumerInterface): # It should be up to the UI to determine which automagics to run, so this is before BACK TO THE FRAMEWORK automagics = automagic.choose_automagic(automagics, plugin) + ctx.config['automagic.LayerStacker.stackers'] = stacker.choose_stackers(plugin) self.output_dir = args.output_dir ### diff --git a/volatility/framework/automagic/linux.py b/volatility/framework/automagic/linux.py index fa6795354..4ec720445 100644 --- a/volatility/framework/automagic/linux.py +++ b/volatility/framework/automagic/linux.py @@ -14,7 +14,7 @@ from volatility.framework.symbols import linux vollog = logging.getLogger(__name__) -class LintelStacker(interfaces.automagic.StackerLayerInterface): +class LinuxIntelStacker(interfaces.automagic.StackerLayerInterface): stack_order = 45 @classmethod diff --git a/volatility/framework/automagic/mac.py b/volatility/framework/automagic/mac.py index 361f7c623..6e6e61cb5 100644 --- a/volatility/framework/automagic/mac.py +++ b/volatility/framework/automagic/mac.py @@ -14,7 +14,7 @@ from volatility.framework.symbols import mac vollog = logging.getLogger(__name__) -class MacintelStacker(interfaces.automagic.StackerLayerInterface): +class MacIntelStacker(interfaces.automagic.StackerLayerInterface): stack_order = 45 @classmethod diff --git a/volatility/framework/automagic/stacker.py b/volatility/framework/automagic/stacker.py index e1d09e6e6..12d312717 100644 --- a/volatility/framework/automagic/stacker.py +++ b/volatility/framework/automagic/stacker.py @@ -13,7 +13,7 @@ once a layer successfully stacks on top of the existing layers, it is removed fr import logging import sys import traceback -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Type from volatility import framework from volatility.framework import interfaces, constants @@ -45,7 +45,7 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): def __call__(self, context: interfaces.context.ContextInterface, config_path: str, - requirement: interfaces.configuration.RequirementInterface, + requirement: interfaces.configuration.RequirementInterface = None, progress_callback: constants.ProgressCallback = None) -> Optional[List[str]]: """Runs the automagic over the configurable.""" @@ -103,7 +103,8 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): physical_layer = physical.FileLayer(new_context, current_config_path, current_layer_name) new_context.add_layer(physical_layer) - stacked_layers = self.stack_layer(new_context, current_layer_name, progress_callback) + stacked_layers = self.stack_layer(new_context, current_layer_name, self.create_stackers_list(), + progress_callback) if stacked_layers is not None: # Applies the stacked_layers to each requirement in the requirements list @@ -123,8 +124,11 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): vollog.debug("Stacked layers: {}".format(stacked_layers)) @classmethod - def stack_layer(cls, context: interfaces.context.ContextInterface, initial_layer: str, - progress_callback: constants.ProgressCallback): + def stack_layer(cls, + context: interfaces.context.ContextInterface, + initial_layer: str, + stack_set: List[Type[interfaces.automagic.StackerLayerInterface]] = None, + progress_callback: constants.ProgressCallback = None): """Stacks as many possible layers on top of the initial layer as can be done. WARNING: This modifies the context provided and may pollute it with unnecessary layers @@ -138,6 +142,7 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): Args: context: The context on which to operate initial_layer: The name of the initial layer within the context + stack_set: A list of StackerLayerInterface objects in the order they should be stacked progress_callback: A function to report progress during the process Returns: @@ -146,8 +151,14 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): # Repeatedly apply "determine what this is" code and build as much up as possible stacked = True stacked_layers = [initial_layer] - stack_set = sorted(framework.class_subclasses(interfaces.automagic.StackerLayerInterface), - key = lambda x: x.stack_order) + if not stack_set or not len(stack_set): + stack_set = sorted(framework.class_subclasses(interfaces.automagic.StackerLayerInterface), + key = lambda x: x.stack_order) + + for stacker in stack_set: + if not issubclass(stacker, interfaces.automagic.StackerLayerInterface): + raise TypeError("Stacker {} is not a descendent of StackerLayerInterface".format(stacker.__name__)) + while stacked: stacked = False new_layer = None @@ -176,6 +187,19 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): stack_set.remove(stacker_cls) return stacked_layers + def create_stackers_list(self): + """Creates the list of stackers to use based on the config option""" + stack_set = sorted(framework.class_subclasses(interfaces.automagic.StackerLayerInterface), + key = lambda x: x.stack_order) + stacker_list = self.config.get('stackers', []) + if len(stacker_list): + result = [] + for stacker in stack_set: + if stacker.__name__ in stacker_list: + result.append(stacker) + stack_set = result + return stack_set + @classmethod def find_suitable_requirements(cls, context: interfaces.context.ContextInterface, config_path: str, requirement: interfaces.configuration.RequirementInterface, @@ -214,7 +238,39 @@ class LayerStacker(interfaces.automagic.AutomagicInterface): def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: # This is not optional for the stacker to run, so optional must be marked as False return [ - requirements.URIRequirement("single_location", + requirements.URIRequirement(name = "single_location", description = "Specifies a base location on which to stack", - optional = True) + optional = True), + requirements.ListRequirement(name = "stackers", description = "List of stackers", optional = True) ] + + +def choose_stackers(plugin): + """Chooses the available stackers based on the plugin""" + plugin_module_components = plugin.__module__.split('.') + oses = ['windows', 'linux', 'mac'] + + operating_system = 'unknown' + lowest_index = len(plugin_module_components) + + for os in oses: + try: + if plugin_module_components.index(os) < lowest_index: + lowest_index = plugin_module_components.index(os) + operating_system = os + except ValueError: + # The value wasn't found, try the next one + pass + + result = [] + for stacker in sorted(framework.class_subclasses(interfaces.automagic.StackerLayerInterface), + key = lambda x: x.stack_order): + stacker_name = stacker.__name__.lower() + append = True + if 'intel' in stacker_name: + if operating_system in oses: + if not stacker_name.startswith(operating_system): + append = False + if append: + result.append(stacker.__name__) + return result diff --git a/volatility/framework/automagic/windows.py b/volatility/framework/automagic/windows.py index 89fc80b00..ec07e6114 100644 --- a/volatility/framework/automagic/windows.py +++ b/volatility/framework/automagic/windows.py @@ -286,7 +286,7 @@ class WintelHelper(interfaces.automagic.AutomagicInterface): self(context, sub_config_path, subreq) -class WintelStacker(interfaces.automagic.StackerLayerInterface): +class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): stack_order = 40 @classmethod