mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-22 06:22:23 +02:00
Since a Union is identical to a Struct (and at the moment a struct doesn't enforce non-overlapping members), these are identical and a Union is a descendent of Struct. If this ever becomes a problem there is a filthy way to fix it, but it's really bad and will likely cause more subtle and difficult to diagnose problems. Stick with inheritance.
382 lines
17 KiB
Python
382 lines
17 KiB
Python
"""
|
|
Created on 17 Feb 2013
|
|
|
|
@author: mike
|
|
"""
|
|
|
|
import collections
|
|
import struct
|
|
|
|
from volatility.framework import interfaces
|
|
from volatility.framework.interfaces.objects import ObjectInformation
|
|
from volatility.framework.objects import templates
|
|
|
|
|
|
class Void(interfaces.objects.ObjectInterface):
|
|
"""Returns an object to represent void/unknown types"""
|
|
|
|
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
|
@classmethod
|
|
def size(cls, template):
|
|
"""Dummy size for Void objects"""
|
|
raise TypeError("Void types are incomplete, cannot contain data and do not have a size.")
|
|
|
|
def write(self, value):
|
|
"""Dummy method that does nothing for Void objects"""
|
|
raise TypeError("Cannot write data to a void, recast as another object")
|
|
|
|
|
|
class Function(interfaces.objects.ObjectInterface):
|
|
""""""
|
|
|
|
|
|
class PrimitiveObject(interfaces.objects.ObjectInterface):
|
|
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
|
|
_struct_type = int
|
|
|
|
def __init__(self, context, type_name, object_info, struct_format):
|
|
interfaces.objects.ObjectInterface.__init__(self,
|
|
context = context,
|
|
type_name = type_name,
|
|
object_info = object_info,
|
|
struct_format = struct_format)
|
|
self._struct_format = struct_format
|
|
|
|
def __new__(cls, context, type_name, object_info, struct_format, **kwargs):
|
|
"""Creates the appropriate class and returns it so that the native type is inherritted
|
|
|
|
The only reason the **kwargs is added, is so that the inherriting types can override __init__
|
|
without needing to override __new__"""
|
|
return cls._struct_type.__new__(cls,
|
|
cls._struct_value(context,
|
|
struct_format,
|
|
object_info.layer_name,
|
|
object_info.offset))
|
|
|
|
@classmethod
|
|
def _struct_value(cls, context, struct_format, layer_name, offset):
|
|
length = struct.calcsize(struct_format)
|
|
data = context.memory.read(layer_name, offset, length)
|
|
(value,) = struct.unpack(struct_format, data)
|
|
return value
|
|
|
|
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
|
@classmethod
|
|
def size(cls, template):
|
|
"""Returns the size of the templated object"""
|
|
return struct.calcsize(template.vol.struct_format)
|
|
|
|
def write(self, value):
|
|
"""Writes the object into the layer of the context at the current offset"""
|
|
if isinstance(value, self._struct_type):
|
|
data = struct.pack(self.vol.struct_format, value)
|
|
return self._context.memory.write(self.vol.layer_name, self.vol.offset, data)
|
|
raise TypeError(
|
|
repr(self.__class__.__name__) + " objects require a " + repr(type(self._struct_type)) + " to be written")
|
|
|
|
|
|
class Integer(PrimitiveObject, int):
|
|
"""Primitive Object that handles standard numeric types"""
|
|
|
|
|
|
class Float(PrimitiveObject, float):
|
|
"""Primitive Object that handles double or floating point numbers"""
|
|
_struct_type = float
|
|
|
|
|
|
class Bytes(PrimitiveObject, bytes):
|
|
"""Primitive Object that handles specific series of bytes"""
|
|
_struct_type = bytes
|
|
|
|
def __init__(self, context, type_name, object_info, length = 1):
|
|
interfaces.objects.ObjectInterface.__init__(self,
|
|
context = context,
|
|
type_name = type_name,
|
|
object_info = object_info,
|
|
struct_format = str(length) + "s")
|
|
self._vol['length'] = length
|
|
|
|
def __new__(cls, context, type_name, object_info, length = 1, **kwargs):
|
|
"""Creates the appropriate class and returns it so that the native type is inherritted
|
|
|
|
The only reason the **kwargs is added, is so that the inherriting types can override __init__
|
|
without needing to override __new__"""
|
|
return cls._struct_type.__new__(cls,
|
|
cls._struct_value(context,
|
|
struct_format = str(length) + "s",
|
|
layer_name = object_info.layer_name,
|
|
offset = object_info.offset))
|
|
|
|
|
|
# TODO: Fix up strings unpacking to include an encoding
|
|
class String(PrimitiveObject, str):
|
|
"""Primitive Object that handles string values
|
|
|
|
length: specifies the maximum possible length that the string could hold in memory
|
|
"""
|
|
_struct_type = str
|
|
|
|
def __init__(self, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = None):
|
|
PrimitiveObject.__init__(self,
|
|
context = context,
|
|
type_name = type_name,
|
|
object_info = object_info,
|
|
struct_format = str(max_length) + 's')
|
|
self._vol["max_length"] = max_length
|
|
self._vol['encoding'] = encoding
|
|
self._vol['errors'] = errors
|
|
|
|
def __new__(cls, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = None, **kwargs):
|
|
"""Creates the appropriate class and returns it so that the native type is inherited
|
|
|
|
The only reason the **kwargs is added, is so that the inherriting types can override __init__
|
|
without needing to override __new__"""
|
|
params = {}
|
|
if encoding:
|
|
params['encoding'] = encoding
|
|
if errors:
|
|
params['errors'] = errors
|
|
value = cls._struct_type.__new__(cls,
|
|
cls._struct_value(context,
|
|
struct_format = str(max_length) + "s",
|
|
layer_name = object_info.layer_name,
|
|
offset = object_info.offset),
|
|
**params)
|
|
# We don't truncate on "\x00" because the string decoder does that for us
|
|
return value
|
|
|
|
|
|
class Pointer(Integer):
|
|
"""Pointer which points to another object"""
|
|
|
|
def __init__(self, context, type_name, object_info, struct_format, target = None):
|
|
self._check_type(target, templates.ObjectTemplate)
|
|
Integer.__init__(self,
|
|
context = context,
|
|
object_info = object_info,
|
|
type_name = type_name,
|
|
struct_format = struct_format)
|
|
self._vol['target'] = target
|
|
|
|
def dereference(self, layer_name = None):
|
|
"""Dereferences the pointer
|
|
|
|
Layer_name is identifies the appropriate layer within the context that the pointer points to.
|
|
If layer_name is None, it defaults to the same layer that the pointer is currently instantiated in.
|
|
"""
|
|
if layer_name is None:
|
|
layer_name = self.vol.layer_name
|
|
return self.vol.target(context = self._context,
|
|
object_info = interfaces.objects.ObjectInformation(
|
|
layer_name = layer_name,
|
|
offset = self,
|
|
parent = self))
|
|
|
|
def __getattr__(self, attr):
|
|
"""Convenience function to access unknown attributes by getting them from the target object"""
|
|
return getattr(self.dereference(), attr)
|
|
|
|
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
|
@classmethod
|
|
def size(cls, template):
|
|
return Integer.VolTemplateProxy.size(cls, template)
|
|
|
|
@classmethod
|
|
def children(cls, template):
|
|
"""Returns the children of the template"""
|
|
if 'target' in template.vol:
|
|
return [template.vol.target]
|
|
return []
|
|
|
|
@classmethod
|
|
def replace_child(cls, template, old_child, new_child):
|
|
"""Substitutes the old_child for the new_child"""
|
|
if 'target' in template.vol:
|
|
if template.vol.target == old_child:
|
|
template.update_vol(target = new_child)
|
|
|
|
|
|
class BitField(PrimitiveObject, int):
|
|
"""Object containing a field which is made up of bits rather than whole bytes"""
|
|
|
|
def __new__(cls, context, type_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0):
|
|
cls._check_type(target, Integer)
|
|
value = target(context = context,
|
|
type_name = type_name,
|
|
object_info = object_info,
|
|
struct_format = struct_format)
|
|
return cls._struct_type.__new__(cls, (value >> start_bit) & ((1 << end_bit) - 1))
|
|
|
|
def __init__(self, context, type_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0):
|
|
PrimitiveObject.__init__(self, context, type_name, object_info, struct_format)
|
|
self._vol['target'] = target
|
|
self._vol['start_bit'] = start_bit
|
|
self._vol['end_bit'] = end_bit
|
|
|
|
@classmethod
|
|
def _template_children(cls, template):
|
|
"""Returns the target type"""
|
|
if 'target' in template.vol:
|
|
return [template.vol.target]
|
|
return []
|
|
|
|
def write(self, value):
|
|
raise NotImplementedError("Writing to BitFields is not yet implemented")
|
|
|
|
|
|
class Enumeration(interfaces.objects.ObjectInterface):
|
|
"""Returns an object made up of choices"""
|
|
|
|
# FIXME: Add in body for the enumeration object
|
|
|
|
def write(self, value):
|
|
raise NotImplementedError("Writing to Enumerations is not yet implemented")
|
|
|
|
|
|
class Array(interfaces.objects.ObjectInterface, collections.Sequence):
|
|
"""Object which can contain a fixed number of an object type"""
|
|
|
|
def __init__(self, context, type_name, object_info, count = 0, target = None):
|
|
self._check_type(target, templates.ObjectTemplate)
|
|
interfaces.objects.ObjectInterface.__init__(self,
|
|
context = context,
|
|
type_name = type_name,
|
|
object_info = object_info)
|
|
self._vol['count'] = self._check_type(count, int)
|
|
self._vol['target'] = target
|
|
|
|
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
|
@classmethod
|
|
def size(cls, template):
|
|
"""Returns the size of the array, based on the count and the target"""
|
|
if 'target' not in template.vol and 'count' not in template.vol:
|
|
raise TypeError("Array ObjectTemplate must be provided a count and target")
|
|
return template.vol.get('target', None).size * template.vol.get('count', 0)
|
|
|
|
@classmethod
|
|
def children(cls, template):
|
|
"""Returns the children of the template"""
|
|
if 'target' in template.vol:
|
|
return [template.vol.target]
|
|
return []
|
|
|
|
@classmethod
|
|
def replace_child(cls, template, old_child, new_child):
|
|
"""Substitutes the old_child for the new_child"""
|
|
if 'target' in template.vol:
|
|
if template.vol['target'] == old_child:
|
|
template.update_vol(target = 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"""
|
|
if 'target' in template and child == 'target':
|
|
return 0
|
|
raise IndexError("Member " + child + " not present in array template")
|
|
|
|
def __getitem__(self, i):
|
|
"""Returns the i-th item from the array"""
|
|
if i >= self.vol.count or 0 > i:
|
|
raise IndexError
|
|
object_info = ObjectInformation(layer_name = self.vol.layer_name,
|
|
offset = self.vol.offset + (self.vol.target.size * i),
|
|
parent = self)
|
|
return self.vol.target(context = self._context, object_info = object_info)
|
|
|
|
def __len__(self):
|
|
"""Returns the length of the array"""
|
|
return self.vol.count
|
|
|
|
def write(self, value):
|
|
raise NotImplementedError("Writing to Arrays is not yet implemented")
|
|
|
|
|
|
class Struct(interfaces.objects.ObjectInterface):
|
|
"""Object which can contain members that are other objects
|
|
|
|
Keep the number of methods in this class low or very specific, since each one could overload a valid member.
|
|
"""
|
|
|
|
def __init__(self, context, type_name, object_info, size, members):
|
|
interfaces.objects.ObjectInterface.__init__(self,
|
|
context = context,
|
|
type_name = type_name,
|
|
object_info = object_info,
|
|
size = size,
|
|
members = members)
|
|
self._check_members(members)
|
|
self._concrete_members = {}
|
|
|
|
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
|
|
@classmethod
|
|
def size(cls, template):
|
|
"""Method to return the size of this type"""
|
|
if template.vol.get('size', None) is None:
|
|
raise TypeError("Struct ObjectTemplate not provided with a size")
|
|
return template.vol.size
|
|
|
|
@classmethod
|
|
def children(cls, template):
|
|
"""Method to list children of a template"""
|
|
return [member for _, member in template.vol.members.values()]
|
|
|
|
@classmethod
|
|
def replace_child(cls, template, old_child, new_child):
|
|
"""Replace a child elements within the arguments handed to the template"""
|
|
for member in template.vol.members.get('members', {}):
|
|
relative_offset, member_template = template.vol.members[member]
|
|
if member_template == old_child:
|
|
# Members will give access to the mutable members list,
|
|
# but in case that ever changes, do the update correctly
|
|
tmp_list = template.vol.members
|
|
tmp_list[member] = (relative_offset, new_child)
|
|
template.update_vol(members = tmp_list)
|
|
|
|
@classmethod
|
|
def relative_child_offset(cls, template, child):
|
|
"""Returns the relative offset of a child to its parent"""
|
|
retlist = template.vol.members.get(child, None)
|
|
if retlist is None:
|
|
raise IndexError("Member " + child + " not present in template")
|
|
return retlist[0]
|
|
|
|
@classmethod
|
|
def _check_members(cls, members):
|
|
# Members should be an iterable mapping of symbol names to tuples of (relative_offset, ObjectTemplate)
|
|
# An object template is a callable that when called with a context, offset, layer_name and type_name
|
|
if not isinstance(members, collections.Mapping):
|
|
raise TypeError("Struct members parameter must be a mapping not " + type(members))
|
|
if not all([(isinstance(member, tuple) and len(member) == 2) for member in members.values()]):
|
|
raise TypeError("Struct members must be a tuple of relative_offsets and templates")
|
|
|
|
def member(self, attr = 'member'):
|
|
"""Specifically named method for retrieving members."""
|
|
return self.__getattr__(attr)
|
|
|
|
def __getattr__(self, attr):
|
|
"""Method for accessing members of the type"""
|
|
if attr in self._concrete_members:
|
|
return self._concrete_members[attr]
|
|
elif attr in self.vol.members:
|
|
relative_offset, member = self.vol.members[attr]
|
|
member = member(context = self._context,
|
|
object_info = interfaces.objects.ObjectInformation(layer_name = self.vol.layer_name,
|
|
offset = self.vol.offset + relative_offset,
|
|
member_name = attr,
|
|
parent = self))
|
|
self._concrete_members[attr] = member
|
|
return member
|
|
raise AttributeError("'" + self.vol.type_name + "' Struct has no attribute '" + attr + "'")
|
|
|
|
def write(self, value):
|
|
raise TypeError("Structs cannot be written to directly, individual members must be written instead")
|
|
|
|
|
|
# Nice way of duplicating the class, but *could* causes problems with isintance
|
|
class Union(Struct):
|
|
pass
|
|
|
|
# Really nasty way of duplicating the class
|
|
# WILL cause problems with any mutable class/static variables
|
|
# Union = type('Union', Struct.__bases__, dict(Struct.__dict__))
|