From a85bcb9e5813f3cb38ce6cfa5b68ebc181cb4b85 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 9 Apr 2015 15:56:57 -0500 Subject: [PATCH] Rework config system yet again, and start filling in some of the requirements. --- volatility/cli/__init__.py | 3 +- volatility/framework/__init__.py | 13 +++- volatility/framework/config.py | 20 +----- volatility/framework/contexts/intel.py | 30 ++++++-- volatility/framework/contexts/physical.py | 14 ++-- volatility/framework/interfaces/config.py | 84 +++++++++++++++------- volatility/framework/interfaces/context.py | 10 +++ volatility/plugins/windows/pslist.py | 8 ++- 8 files changed, 123 insertions(+), 59 deletions(-) diff --git a/volatility/cli/__init__.py b/volatility/cli/__init__.py index 4682b87bf..5fbb1ce70 100644 --- a/volatility/cli/__init__.py +++ b/volatility/cli/__init__.py @@ -38,7 +38,8 @@ class CommandLine(): reqs = plugin.requirements() for req in reqs: if isinstance(req, config.TranslationLayerRequirement): - pass + # The name given to the root config doesn't matter, so go with volatility + context_config = config.ConfigGroup('volatility') def main(): diff --git a/volatility/framework/__init__.py b/volatility/framework/__init__.py index 6e81a31e6..0974156fe 100644 --- a/volatility/framework/__init__.py +++ b/volatility/framework/__init__.py @@ -59,10 +59,21 @@ class Context(interfaces.context.ContextInterface): interfaces.context.ContextInterface.__init__(self) self._symbol_space = symbols.SymbolSpace(natives) self._memory = layers.Memory() - self.config = config.Config() + self._config = config.ConfigGroup(name = 'volatility') # ## Symbol Space Functions + @property + def config(self): + """Returns the configuration object for this context""" + return self._config + + @config.setter + def config(self, value): + if not isinstance(value, config.ConfigGroup): + raise TypeError("Configuration must of type ConfigGroup") + self._config = value + @property def symbol_space(self): """The space of all symbols that can be accessed within this context. diff --git a/volatility/framework/config.py b/volatility/framework/config.py index 785fc7b83..e3860842a 100644 --- a/volatility/framework/config.py +++ b/volatility/framework/config.py @@ -5,25 +5,7 @@ Created on 7 May 2013 """ import re -from volatility.framework.interfaces.config import GenericRequirement, ConfigInterface - -class Config(ConfigInterface): - """Class to hold and provide a namespace for plugins and core options""" - def __init__(self): - self._namespace = {'core': {}} - - def add_item(self, namespace, item): - self._type_check(namespace, str) - self._type_check(item, GenericRequirement) - subconfig = self._namespace.get(namespace,{}) - subconfig[item.name] = item - self._namespace[namespace] = subconfig - - def __contains__(self, item): - return (item in self._namespace) - - def __len__(self): - return len(self._namespace) +from volatility.framework.interfaces.config import GenericRequirement, ConfigGroup class InstanceRequirement(GenericRequirement): instance_type = bool diff --git a/volatility/framework/contexts/intel.py b/volatility/framework/contexts/intel.py index a9e61470d..9e1179e54 100644 --- a/volatility/framework/contexts/intel.py +++ b/volatility/framework/contexts/intel.py @@ -4,19 +4,37 @@ __author__ = 'mike' class IntelContextModifier(interfaces.context.ContextModifierInterface): - def __init__(self, config): - pass - @classmethod def requirements(cls): return [config.ChoiceRequirement(name = "architecture", choices = ["auto", "pae", "32", "64"], description = "Determines the memory image", default = "auto"), - config.IntRequirement(name = "pagemapoffset", - description = "Offset to the directory table base")] + config.IntRequirement(name = "page_map_offset", + description = "Offset to the directory table base"), + config.StringRequirement(name = 'layer_name', + description = 'Name of the layer to be added to the memory space', + default = 'intel'), + config.StringRequirement(name = 'physical_layer', + description = "Layer name for the physical layer"), + config.StringRequirement(name = 'swap_layer', + description = "Layer name for the swap layer", + optional = True)] def __call__(self, context): # TODO: Attempt to determine whether the image is 32, PAE or x64 (although the context must already know whether it is x64) - intel = layers.intel.IntelPAE(context, 'kernel', 'physical', page_map_offset = 0x319000) + config = self.config_get(context) + + layer = None + if config.get('architecture') == 'pae': + layer = layers.intel.IntelPAE + elif config.get('architecture') == '32': + layer = layers.intel.Intel + elif config.get('architecture') == '64': + layer = layers.intel.Intel32e + else: + #TODO: Add automagic here + layer = layers.intel.IntelPAE + + intel = layer(context, config.get('layer_name'), config.get('physical_layer'), page_map_offset = config.get('pagemapoffset')) context.add_layer(intel) diff --git a/volatility/framework/contexts/physical.py b/volatility/framework/contexts/physical.py index facf9438b..9fe372d55 100644 --- a/volatility/framework/contexts/physical.py +++ b/volatility/framework/contexts/physical.py @@ -1,18 +1,20 @@ -from volatility.framework import interfaces, layers +from volatility.framework import interfaces, layers, config __author__ = 'mike' class PhysicalContextModifier(interfaces.context.ContextModifierInterface): - def __init__(self, filename): - self.filename = '/home/mike/memory/private/jon-fres.dmp' - @classmethod def requirements(cls): - pass + return [config.StringRequirement(name = 'location', + description = 'URL to the physical address space'), + config.StringRequirement(name = 'layer_name', + description = 'Layer name for the physical space', + default = 'physical')] def __call__(self, context): # Ideally allow for the plugin to specify the layering, but if not then guess at the best one - base = layers.physical.FileLayer(context, 'physical', filename = self.filename) + config = self.config_get(context) + base = layers.physical.FileLayer(context, config.get('layer_name'), filename = config.get('location')) context.add_layer(base) diff --git a/volatility/framework/interfaces/config.py b/volatility/framework/interfaces/config.py index 82e6ca02a..6ee8b869a 100644 --- a/volatility/framework/interfaces/config.py +++ b/volatility/framework/interfaces/config.py @@ -1,4 +1,5 @@ from abc import ABCMeta, abstractmethod +import collections.abc from volatility.framework import validity @@ -6,15 +7,69 @@ from volatility.framework import validity __author__ = 'mike' -class GenericRequirement(validity.ValidityRoutines, metaclass = ABCMeta): +class ConfigurationItem(validity.ValidityRoutines): + """Class to distinguish configuration elements from everything else""" + namespace_divider = "." + + def __init__(self, name): + validity.ValidityRoutines.__init__(self) + self._type_check(name, str) + if self.namespace_divider in name: + raise ValueError("Name cannot contain the namespace divider (" + self.namespace_divider + ")") + self._name = name + + @property + def name(self): + """The name of the Option.""" + return self._name + + +class ConfigGroup(ConfigurationItem, collections.abc.Mapping): + """Class to hold and provide a namespace for plugins and core options""" + def __init__(self, name): + ConfigurationItem.__init__(self, name) + self._namespace = {} + + def add_item(self, item, namespace = None): + if not isinstance(item, ConfigurationItem): + raise TypeError("Only ConfigurationItem objects can be added to a ConfigGroup") + if namespace: + ns_split = namespace.split(self.namespace_divider) + if ns_split[0] not in self: + self._namespace[ns_split[0]] == ConfigGroup(ns_split[0]) + return self._namespace[ns_split[0]].add_item(self, item, self.namespace_divider.join(ns_split[1:])) + self._namespace[item.name] = item + + def __iter__(self): + return iter(self._namespace) + + def __getitem__(self, item): + self._type_check(item, str) + item_split = item.split(self.namespace_divider) + if len(item_split) > 1: + return self._namespace[item_split[0]][self.namespace_divider.join(item_split[1:])] + # Let namespace produce the index error if necessary + return self._namespace[item_split[0]] + + def __contains__(self, item): + item_split = item.split(self.namespace_divider) + if len(item_split) > 1: + if item_split[0] in self._namespace: + return self.namespace_divider.join(item_split[1:]) in self._namespace[item_split[0]] + else: + return False + return item in self._namespace + + def __len__(self): + return len(self._namespace) + +class GenericRequirement(ConfigurationItem, metaclass = ABCMeta): """Class to handle a single specific configuration option""" def __init__(self, name, description = None, default = None, optional = False): """Creates a new option""" - validity.ValidityRoutines.__init__(self) + ConfigurationItem.__init__(self, name) self._default = default - self._type_check(name, str) - self._name = name self._description = description self._value = None self._optional = optional @@ -36,11 +91,6 @@ class GenericRequirement(validity.ValidityRoutines, metaclass = ABCMeta): """Whether the option is required for or not""" return self._optional - @property - def name(self): - """The name of the Option.""" - return self._name - @property def description(self): """A short description of what the Option is designed to affect or achieve.""" @@ -55,18 +105,4 @@ class GenericRequirement(validity.ValidityRoutines, metaclass = ABCMeta): def validate(self, context): """Validates the currently set value""" - return self.validate_input(self.value, context) - -class ConfigInterface(validity.ValidityRoutines): - """Class to hold and provide a namespace for plugins and core options""" - @abstractmethod - def add_item(self, namespace, item): - pass - - @abstractmethod - def __contains__(self, item): - pass - - @abstractmethod - def __len__(self): - pass \ No newline at end of file + return self.validate_input(self.value, context) \ No newline at end of file diff --git a/volatility/framework/interfaces/context.py b/volatility/framework/interfaces/context.py index 402aed994..d8d0f1f47 100644 --- a/volatility/framework/interfaces/context.py +++ b/volatility/framework/interfaces/context.py @@ -17,6 +17,10 @@ class ContextInterface(object, metaclass = ABCMeta): # ## Symbol Space Functions + @abstractproperty + def config(self): + """Returns the configuration object for this context""" + @abstractproperty def symbol_space(self): """Returns the symbol_space for the context""" @@ -46,6 +50,12 @@ class ContextInterface(object, metaclass = ABCMeta): class ContextModifierInterface(object, metaclass = ABCMeta): + def __init__(self, namespace): + self.namespace = namespace + + def config_get(self, context): + return context.config[self.namespace] + @classmethod @abstractmethod def requirements(cls): diff --git a/volatility/plugins/windows/pslist.py b/volatility/plugins/windows/pslist.py index c103ac1f7..5193ae199 100644 --- a/volatility/plugins/windows/pslist.py +++ b/volatility/plugins/windows/pslist.py @@ -1,4 +1,5 @@ import inspect +from volatility.framework import config import volatility.framework.interfaces.plugins as plugins @@ -6,8 +7,11 @@ import volatility.framework.interfaces.plugins as plugins class PsList(plugins.PluginInterface): @classmethod def requirements(cls): - print(inspect.getfullargspec(cls.__call__)) - return {"primary": "Intel"} + return [config.TranslationLayerRequirement(name = 'primary', + description = 'Kernel Address Space'), + config.IntRequirement(name = 'pid', + description = "Process ID", + optional = True)] @staticmethod def kernel_process_from_physical_process(ctx, physical_layer, kernel_layer, offset):