mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-23 06:42:21 +02:00
This template type allows objects that have not been able to be resolved to exist within the symbol system. It emits a debug message on creation so that intermediate format developers can identify potential issues, but does not raise an exception so as to allow partial tables to be used. If the UnresolvedTemplate is called (to create an object) before the symbol has been added to the symbolspace, it will fail with a SymbolError (as thrown by the individual SymbolTable). For this reason, the class has been made private to the SymbolSpace class to prevent unexpected use.
173 lines
6.2 KiB
Python
173 lines
6.2 KiB
Python
"""
|
|
Created on 6 May 2013
|
|
|
|
@author: mike
|
|
"""
|
|
|
|
import collections
|
|
import collections.abc
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
from volatility.framework import validity
|
|
from volatility.framework.interfaces import context as context_module
|
|
|
|
|
|
class ReadOnlyMapping(validity.ValidityRoutines, collections.abc.Mapping):
|
|
"""A read-only mapping of various values that offer attribute access as well"""
|
|
|
|
def __init__(self, dictionary):
|
|
self._dict = dictionary
|
|
|
|
def __getattr__(self, attr):
|
|
"""Returns the item as an attribute"""
|
|
if attr in self._dict:
|
|
return self._dict[attr]
|
|
raise AttributeError("'" + self.__class__.__name__ + "' object has no attribute '" + attr + '"')
|
|
|
|
def __getitem__(self, name):
|
|
"""Returns the item requested"""
|
|
return self._dict[name]
|
|
|
|
def __iter__(self):
|
|
"""Returns an iterator of the dictionary items"""
|
|
return self._dict.__iter__()
|
|
|
|
def __len__(self):
|
|
"""Returns the length of the internal dictionary"""
|
|
return len(self._dict)
|
|
|
|
|
|
class ObjectInformation(ReadOnlyMapping):
|
|
"""Contains information useful/pertinent only to an individual object (like an instance)"""
|
|
|
|
def __init__(self, layer_name, offset, member_name = None, parent = None):
|
|
self._check_type(offset, int)
|
|
if parent:
|
|
self._check_type(parent, ObjectInterface)
|
|
super().__init__({'layer_name': layer_name,
|
|
'offset': offset,
|
|
'member_name': member_name,
|
|
'parent': parent})
|
|
|
|
|
|
class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta):
|
|
""" A base object required to be the ancestor of every object used in volatility """
|
|
|
|
def __init__(self, context, type_name, object_info, **kwargs):
|
|
# Since objects are likely to be instantiated often,
|
|
# we're only checking that context, offset and parent
|
|
# Everything else may be wrong, but that will get caught later on
|
|
self._check_type(context, context_module.ContextInterface)
|
|
self._check_type(object_info, ObjectInformation)
|
|
|
|
# Add an empty dictionary at the start to allow objects to add their own data to the vol object
|
|
#
|
|
# NOTE:
|
|
# This allows objects to MASSIVELY MESS with their own internal representation!!!
|
|
# Changes to offset, type_name, etc should NEVER be done
|
|
#
|
|
self._vol = collections.ChainMap({}, object_info, {'type_name': type_name}, kwargs)
|
|
self._context = context
|
|
|
|
@property
|
|
def vol(self):
|
|
"""Returns the volatility specific object information"""
|
|
# Wrap the outgoing vol in a read-only proxy
|
|
return ReadOnlyMapping(self._vol)
|
|
|
|
@abstractmethod
|
|
def write(self, value):
|
|
"""Writes the new value into the format at the offset the object currently resides at"""
|
|
|
|
def cast(self, new_type_name, **additional):
|
|
"""Returns a new object at the offset and from the layer that the current object inhabits"""
|
|
# TODO: Carefully consider the implications of casting and how it should work
|
|
object_template = self._context.symbol_space.get_type(new_type_name)
|
|
object_template.update_vol(**additional)
|
|
object_info = ObjectInformation(layer_name = self.vol.layer_name,
|
|
offset = self.vol.offset,
|
|
member_name = self.vol.member_name,
|
|
parent = self.vol.parent)
|
|
return object_template(context = self._context,
|
|
object_info = object_info)
|
|
|
|
class VolTemplateProxy(object):
|
|
"""A container for proxied methods that the ObjectTemplate of this object will call.
|
|
|
|
They are class methods rather than static methods, to allow for code reuse."""
|
|
|
|
@classmethod
|
|
def size(cls, template):
|
|
"""Returns the size of the template object"""
|
|
|
|
@classmethod
|
|
def children(cls, template):
|
|
"""Returns the children of the template"""
|
|
return []
|
|
|
|
@classmethod
|
|
def replace_child(cls, template, old_child, new_child):
|
|
"""Substitutes the old_child for the new_child"""
|
|
|
|
@classmethod
|
|
def relative_child_offset(cls, template, child):
|
|
"""Returns the relative offset from the head of the parent data to the child member"""
|
|
raise KeyError(repr(template.vol.type_name) + " does not contain any children.")
|
|
|
|
|
|
class Template(validity.ValidityRoutines):
|
|
"""Class for all Factories that take offsets, and data layers and produce objects
|
|
|
|
This is effectively a class for currying object calls
|
|
"""
|
|
|
|
def __init__(self, type_name, **arguments):
|
|
"""Stores the keyword arguments for later use"""
|
|
# Allow the updating of template arguments whilst still in template form
|
|
super().__init__()
|
|
self._vol = collections.ChainMap(arguments, {'type_name': type_name})
|
|
|
|
@property
|
|
def vol(self):
|
|
"""Returns a volatility information object, much like the ObjectInterface provides"""
|
|
return ReadOnlyMapping(self._vol)
|
|
|
|
@property
|
|
def children(self):
|
|
"""A function that returns a list of child templates of a template
|
|
|
|
This is used to traverse the template tree
|
|
"""
|
|
return []
|
|
|
|
@property
|
|
@abstractmethod
|
|
def size(self):
|
|
"""Returns the size of the template"""
|
|
|
|
@abstractmethod
|
|
def relative_child_offset(self, child):
|
|
"""A function that returns the relative offset of a child from its parent offset
|
|
|
|
This may throw exceptions including ChildNotFoundException and NotImplementedError
|
|
"""
|
|
|
|
@abstractmethod
|
|
def replace_child(self, old_child, new_child):
|
|
"""A function for replacing one child with another"""
|
|
|
|
def update_vol(self, **new_arguments):
|
|
"""Updates the keyword arguments"""
|
|
self._vol.update(new_arguments)
|
|
|
|
def __call__(self, context, object_info):
|
|
"""Constructs the object
|
|
|
|
:type context: framework.interfaces.context.ContextInterface
|
|
:type object_info: ObjectInformation
|
|
:param context:
|
|
:param object_info:
|
|
|
|
:return O Returns: an object adhering to the Object interface
|
|
"""
|