mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-12 04:37:38 +02:00
Refactor requirements to support a list of swap layers.
This commit is contained in:
@@ -15,6 +15,7 @@ import logging
|
||||
import sys
|
||||
|
||||
import volatility.framework
|
||||
import volatility.framework.configuration.requirements
|
||||
import volatility.plugins
|
||||
from volatility.framework import automagic, constants, contexts, interfaces
|
||||
from volatility.framework.configuration import requirements
|
||||
@@ -226,7 +227,7 @@ class CommandLine(object):
|
||||
additional["action"] = "store_true"
|
||||
if "type" in additional:
|
||||
del additional["type"]
|
||||
elif isinstance(requirement, interfaces.configuration.ListRequirement):
|
||||
elif isinstance(requirement, volatility.framework.configuration.requirements.ListRequirement):
|
||||
if requirement.min_elements != requirement.max_elements:
|
||||
if requirement.min_elements > 0:
|
||||
additional["nargs"] = "+"
|
||||
@@ -236,7 +237,7 @@ class CommandLine(object):
|
||||
else:
|
||||
additional["nargs"] = requirement.max_elements
|
||||
additional["type"] = requirement.element_type.instance_type
|
||||
elif isinstance(requirement, interfaces.configuration.ChoiceRequirement):
|
||||
elif isinstance(requirement, volatility.framework.configuration.requirements.ChoiceRequirement):
|
||||
additional["type"] = str
|
||||
additional["choices"] = requirement.choices
|
||||
else:
|
||||
|
||||
@@ -8,7 +8,9 @@ etc) as well as indicating what they expect to be in the context (such as partic
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from volatility.framework import interfaces, constants
|
||||
from volatility.framework.interfaces import configuration as interfaces_configuration
|
||||
from volatility.framework.interfaces.configuration import RequirementInterface, InstanceRequirement, vollog, path_join
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,3 +51,77 @@ class StringRequirement(interfaces_configuration.InstanceRequirement):
|
||||
class BytesRequirement(interfaces_configuration.InstanceRequirement):
|
||||
"""A requirement type that contains a byte string"""
|
||||
instance_type = bytes # type: typing.ClassVar[typing.Type]
|
||||
|
||||
|
||||
class ListRequirement(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,
|
||||
such as a list of integers or a list of strings.
|
||||
|
||||
It is distinct from a multi-requirement which stores the subrequirements in a dictionary, not a list,
|
||||
and does not allow for a dynamic number of values.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
element_type: InstanceRequirement,
|
||||
max_elements: int,
|
||||
min_elements: int, *args, **kwargs) -> None:
|
||||
"""Constructs the object
|
||||
|
||||
:param element_type: The (requirement) type of each element within the list
|
||||
:type element_type: InstanceRequirement
|
||||
:param max_elements; The maximum number of acceptable elements this list can contain
|
||||
:type max_elements: int
|
||||
:param min_elements: The minimum number of acceptable elements this list can contain
|
||||
:type min_elements: int
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(element_type, InstanceRequirement):
|
||||
raise TypeError("ListRequirements can only contain simple InstanceRequirements")
|
||||
self.element_type = element_type
|
||||
self.min_elements = min_elements # type: int
|
||||
self.max_elements = max_elements # type: int
|
||||
|
||||
def unsatisfied(self, context: interfaces.context.ContextInterface, config_path: str) -> typing.List[str]:
|
||||
"""Check the types on each of the returned values and their number and then call the element type's check for each one"""
|
||||
value = self.config_value(context, config_path)
|
||||
if not isinstance(value, list):
|
||||
# TODO: Check this is the correct response for an error
|
||||
raise ValueError("")
|
||||
if not (self.min_elements <= len(value) <= self.max_elements):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - List option provided more or less elements than allowed.")
|
||||
return [path_join(config_path, self.name)]
|
||||
if not all([self._check_type(element, self.element_type.instance_type) for element in value]):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - At least one element in the list is not of the correct type.")
|
||||
return [path_join(config_path, self.name)]
|
||||
result = []
|
||||
for element in value:
|
||||
if isinstance(element, str):
|
||||
subresult = self.element_type.unsatisfied(context, element)
|
||||
for subvalue in subresult:
|
||||
result.append(subvalue)
|
||||
return result
|
||||
|
||||
|
||||
class ChoiceRequirement(RequirementInterface):
|
||||
"""Allows one from a choice of strings"""
|
||||
|
||||
def __init__(self, choices: typing.List[str], *args, **kwargs) -> None:
|
||||
"""Constructs the object
|
||||
|
||||
:param choices: A list of possible string options that can be chosen from
|
||||
:type choices: list of str
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(choices, list) or any([not isinstance(choice, str) for choice in choices]):
|
||||
raise TypeError("ChoiceRequirement takes a list of strings as choices")
|
||||
self.choices = choices
|
||||
|
||||
def unsatisfied(self, context: interfaces.context.ContextInterface, config_path: str) -> typing.List[str]:
|
||||
"""Validates the provided value to ensure it is one of the available choices"""
|
||||
value = self.config_value(context, config_path)
|
||||
if value not in self.choices:
|
||||
vollog.log(constants.LOGLEVEL_V, "ValueError - Value is not within the set of available choices")
|
||||
return [path_join(config_path, self.name)]
|
||||
return []
|
||||
|
||||
@@ -5,6 +5,7 @@ Automagic objects attempt to automatically fill configuration values that a user
|
||||
import typing
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import volatility.framework.configuration.requirements
|
||||
from volatility.framework import validity, interfaces
|
||||
from volatility.framework.interfaces import configuration as interfaces_configuration
|
||||
|
||||
@@ -41,8 +42,8 @@ class AutomagicInterface(interfaces_configuration.ConfigurableInterface, metacla
|
||||
super().__init__(context, config_path)
|
||||
for requirement in self.get_requirements():
|
||||
if not isinstance(requirement, (interfaces_configuration.InstanceRequirement,
|
||||
interfaces_configuration.ChoiceRequirement,
|
||||
interfaces_configuration.ListRequirement)):
|
||||
volatility.framework.configuration.requirements.ChoiceRequirement,
|
||||
volatility.framework.configuration.requirements.ListRequirement)):
|
||||
raise ValueError(
|
||||
"Automagic requirements must be an InstanceRequirement, ChoiceRequirement or ListRequirement")
|
||||
|
||||
|
||||
@@ -664,77 +664,3 @@ class SymbolRequirement(ConstructableRequirementInterface):
|
||||
if obj is not None:
|
||||
context.symbol_space.append(obj)
|
||||
return None
|
||||
|
||||
|
||||
class ChoiceRequirement(RequirementInterface):
|
||||
"""Allows one from a choice of strings"""
|
||||
|
||||
def __init__(self, choices: typing.List[str], *args, **kwargs) -> None:
|
||||
"""Constructs the object
|
||||
|
||||
:param choices: A list of possible string options that can be chosen from
|
||||
:type choices: list of str
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(choices, list) or any([not isinstance(choice, str) for choice in choices]):
|
||||
raise TypeError("ChoiceRequirement takes a list of strings as choices")
|
||||
self.choices = choices
|
||||
|
||||
def unsatisfied(self, context: interfaces.context.ContextInterface, config_path: str) -> typing.List[str]:
|
||||
"""Validates the provided value to ensure it is one of the available choices"""
|
||||
value = self.config_value(context, config_path)
|
||||
if value not in self.choices:
|
||||
vollog.log(constants.LOGLEVEL_V, "ValueError - Value is not within the set of available choices")
|
||||
return [path_join(config_path, self.name)]
|
||||
return []
|
||||
|
||||
|
||||
class ListRequirement(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,
|
||||
such as a list of integers or a list of strings.
|
||||
|
||||
It is distinct from a multi-requirement which stores the subrequirements in a dictionary, not a list,
|
||||
and does not allow for a dynamic number of values.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
element_type: InstanceRequirement,
|
||||
max_elements: int,
|
||||
min_elements: int, *args, **kwargs) -> None:
|
||||
"""Constructs the object
|
||||
|
||||
:param element_type: The (requirement) type of each element within the list
|
||||
:type element_type: InstanceRequirement
|
||||
:param max_elements; The maximum number of acceptable elements this list can contain
|
||||
:type max_elements: int
|
||||
:param min_elements: The minimum number of acceptable elements this list can contain
|
||||
:type min_elements: int
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not isinstance(element_type, InstanceRequirement):
|
||||
raise TypeError("ListRequirements can only contain simple InstanceRequirements")
|
||||
self.element_type = element_type
|
||||
self.min_elements = min_elements # type: int
|
||||
self.max_elements = max_elements # type: int
|
||||
|
||||
def unsatisfied(self, context: interfaces.context.ContextInterface, config_path: str) -> typing.List[str]:
|
||||
"""Check the types on each of the returned values and their number and then call the element type's check for each one"""
|
||||
value = self.config_value(context, config_path)
|
||||
if not isinstance(value, list):
|
||||
# TODO: Check this is the correct response for an error
|
||||
raise ValueError("")
|
||||
if not (self.min_elements <= len(value) <= self.max_elements):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - List option provided more or less elements than allowed.")
|
||||
return [path_join(config_path, self.name)]
|
||||
if not all([self._check_type(element, self.element_type.instance_type) for element in value]):
|
||||
vollog.log(constants.LOGLEVEL_V, "TypeError - At least one element in the list is not of the correct type.")
|
||||
return [path_join(config_path, self.name)]
|
||||
result = []
|
||||
for element in value:
|
||||
if isinstance(element, str):
|
||||
subresult = self.element_type.unsatisfied(context, element)
|
||||
for subvalue in subresult:
|
||||
result.append(subvalue)
|
||||
return result
|
||||
|
||||
@@ -43,6 +43,12 @@ class Intel(interfaces.layers.TranslationLayerInterface):
|
||||
name: str) -> None:
|
||||
super().__init__(context, config_path, name)
|
||||
self._base_layer = self._check_type(self.config["memory_layer"], str)
|
||||
self._swap_layers = []
|
||||
self._check_type(self.config.get("swap_layers", []), list)
|
||||
for layer_name in self.config.get("swap_layers", []):
|
||||
self._check_type(layer_name, str)
|
||||
if layer_name in context.memory:
|
||||
self._swap_layers.append(layer_name)
|
||||
self._page_map_offset = self._check_type(self.config["page_map_offset"], int)
|
||||
self._optimize_scan = False
|
||||
|
||||
@@ -169,14 +175,20 @@ class Intel(interfaces.layers.TranslationLayerInterface):
|
||||
def dependencies(self) -> typing.List[str]:
|
||||
"""Returns a list of the lower layer names that this layer is dependent upon"""
|
||||
# TODO: Add in the whole buffalo
|
||||
return [self._base_layer]
|
||||
return [self._base_layer] + self._swap_layers
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> typing.List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'memory_layer',
|
||||
optional = False),
|
||||
requirements.TranslationLayerRequirement(name = 'swap_layer',
|
||||
optional = True),
|
||||
requirements.ListRequirement(name = 'swap_layers',
|
||||
element_type = requirements.StringRequirement(
|
||||
name = 'layer_name',
|
||||
optional = False
|
||||
),
|
||||
min_elements = 0,
|
||||
max_elements = 100,
|
||||
optional = True),
|
||||
requirements.IntRequirement(name = 'page_map_offset',
|
||||
optional = False),
|
||||
requirements.IntRequirement(name = 'kernel_virtual_offset',
|
||||
|
||||
Reference in New Issue
Block a user