Merge pull request #569 from volatilityfoundation/feature/coalese-module-objects

Core: Change Module to ConfigurableInterface
This commit is contained in:
ikelos
2021-10-06 21:04:29 +01:00
committed by GitHub
6 changed files with 83 additions and 56 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ class KernelModule(interfaces.automagic.AutomagicInterface):
# The requirement is unfulfilled and is a ModuleRequirement
context.config[interfaces.configuration.path_join(
new_config_path, 'class')] = 'volatility3.framework.contexts.ConfigurableModule'
new_config_path, 'class')] = 'volatility3.framework.contexts.Module'
for req in requirement.requirements:
if requirement.requirements[req].unsatisfied(context, new_config_path) and req != 'offset':
+47 -28
View File
@@ -144,17 +144,17 @@ class Context(interfaces.context.ContextInterface):
size: The size, in bytes, that the module occupys from offset location within the layer named layer_name
"""
if size:
return SizedModule(self,
module_name = module_name,
layer_name = layer_name,
offset = offset,
size = size,
native_layer_name = native_layer_name)
return Module(self,
module_name = module_name,
layer_name = layer_name,
offset = offset,
native_layer_name = native_layer_name)
return SizedModule.create(self,
module_name = module_name,
layer_name = layer_name,
offset = offset,
size = size,
native_layer_name = native_layer_name)
return Module.create(self,
module_name = module_name,
layer_name = layer_name,
offset = offset,
native_layer_name = native_layer_name)
def get_module_wrapper(method: str) -> Callable:
@@ -179,6 +179,33 @@ def get_module_wrapper(method: str) -> Callable:
class Module(interfaces.context.ModuleInterface):
@classmethod
def create(cls,
context: interfaces.context.ContextInterface,
module_name: str,
layer_name: str,
offset: int,
**kwargs) -> 'Module':
pathjoin = interfaces.configuration.path_join
# Check if config_path is None
config_path = kwargs.get('config_path', None)
if config_path is None:
config_path = pathjoin('temporary', 'modules')
# Populate the configuration
context.config[pathjoin(config_path, 'layer_name')] = layer_name
context.config[pathjoin(config_path, 'offset')] = offset
# This is important, since the module_name may be changed in case it is already in use
if 'symbol_table_name' not in kwargs:
kwargs['symbol_table_name'] = module_name
for arg in kwargs:
context.config[pathjoin(config_path, arg)] = kwargs.get(arg, None)
# Construct the object
return_val = cls(context, config_path, context.modules.free_module_name(module_name))
context.add_module(return_val)
context.config[config_path] = return_val.name
# Add the module to the context modules collection
return return_val
def object(self,
object_type: str,
offset: int = None,
@@ -280,26 +307,11 @@ class Module(interfaces.context.ModuleInterface):
class SizedModule(Module):
def __init__(self,
context: interfaces.context.ContextInterface,
module_name: str,
layer_name: str,
offset: int,
size: int,
symbol_table_name: Optional[str] = None,
native_layer_name: Optional[str] = None) -> None:
super().__init__(context,
module_name = module_name,
layer_name = layer_name,
offset = offset,
native_layer_name = native_layer_name,
symbol_table_name = symbol_table_name)
self._size = size
@property
def size(self) -> int:
"""Returns the size of the module (0 for unknown size)"""
return self._size
size = self.config.get('size', 0)
return size or 0
@property # type: ignore # FIXME: mypy #5107
@functools.lru_cache()
@@ -346,6 +358,13 @@ class ModuleCollection(interfaces.context.ModuleContainer):
seen.add(mod.hash) # type: ignore # FIXME: mypy #5107
return ModuleCollection(new_modules)
def free_module_name(self, prefix: str = "module") -> str:
"""Returns an unused module name"""
count = 1
while prefix + str(count) in self:
count += 1
return prefix + str(count)
@property
def modules(self) -> 'ModuleCollection':
"""A name indexed dictionary of modules using that name in this
@@ -192,7 +192,7 @@ class HierarchicalDict(collections.abc.Mapping):
elif value is None:
return None
else:
raise TypeError("Invalid type stored in configuration")
raise TypeError(f"Invalid type stored in configuration: {type(value)}")
def __delitem__(self, key: str) -> None:
"""Deletes an item from the hierarchical dict."""
+27 -19
View File
@@ -136,7 +136,7 @@ class ContextInterface(metaclass = ABCMeta):
"""
class ModuleInterface(metaclass = ABCMeta):
class ModuleInterface(interfaces.configuration.ConfigurableInterface):
"""Maintains state concerning a particular loaded module in memory.
This object is OS-independent.
@@ -144,31 +144,35 @@ class ModuleInterface(metaclass = ABCMeta):
def __init__(self,
context: ContextInterface,
module_name: str,
layer_name: str,
offset: int,
symbol_table_name: Optional[str] = None,
native_layer_name: Optional[str] = None) -> None:
config_path: str,
name: str) -> None:
"""Constructs a new os-independent module.
Args:
context: The context within which this module will exist
config_path: The path within the context's configuration tree
name: The name of the module
layer_name: The layer within the context in which the module exists
offset: The offset at which the module exists in the layer
symbol_table_name: The name of an associated symbol table
native_layer_name: The default native layer for objects constructed by the module
"""
self._context = context
self._module_name = module_name
self._layer_name = layer_name
self._offset = offset
# TODO: Figure out about storing/requesting the native_layer_name for a module in the configuration
# The current module requirement does not ask for nor act upon this information
self._native_layer_name = native_layer_name or layer_name
self._symbol_table_name = symbol_table_name or self._module_name
super().__init__(context, config_path)
self._module_name = name
def build_configuration(self) -> 'configuration.HierarchicalDict':
@property
def _layer_name(self) -> str:
return self.config['layer_name']
@property
def _offset(self) -> int:
return self.config['offset']
@property
def _native_layer_name(self) -> str:
return self.config.get('native_layer_name', self._layer_name)
@property
def _symbol_table_name(self) -> str:
return self.config.get('symbol_table_name', self._module_name)
def build_configuration(self) -> 'interfaces.configuration.HierarchicalDict':
"""Builds the configuration dictionary for this specific Module"""
config = super().build_configuration()
@@ -319,6 +323,10 @@ class ModuleContainer(collections.abc.Mapping):
def __iter__(self):
return iter(self._modules)
def free_module_name(self, prefix: str = "module") -> str:
"""Returns an unused table name to ensure no collision occurs when
inserting a symbol table."""
def get_modules_by_symbol_tables(self, symbol_table: str) -> Iterable[str]:
"""Returns the modules which use the specified symbol table name"""
for module_name in self._modules:
+1 -1
View File
@@ -60,7 +60,7 @@ class ABCKmsg(ABC):
vmlinux = context.modules[self._config['kernel']]
self.layer_name = vmlinux.layer_name # type: ignore
symbol_table_name = vmlinux.symbol_table_name # type: ignore
self.vmlinux = contexts.Module(context, symbol_table_name, self.layer_name, 0) # type: ignore
self.vmlinux = contexts.Module.create(context, symbol_table_name, self.layer_name, 0) # type: ignore
self.long_unsigned_int_size = self.vmlinux.get_type('long unsigned int').size
@classmethod
@@ -60,12 +60,12 @@ class SSDT(plugins.PluginInterface):
if module_name in constants.windows.KERNEL_MODULE_NAMES:
symbol_table_name = symbol_table
context_module = contexts.SizedModule(context,
module_name,
layer_name,
mod.DllBase,
mod.SizeOfImage,
symbol_table_name = symbol_table_name)
context_module = contexts.SizedModule.create(context = context,
module_name = module_name,
layer_name = layer_name,
offset = mod.DllBase,
size = mod.SizeOfImage,
symbol_table_name = symbol_table_name)
context_modules.append(context_module)