Merge branch 'master' of github.com:volatilityfoundation/volatility3

This commit is contained in:
Mike Auty
2016-07-02 12:34:19 +01:00
17 changed files with 394 additions and 179 deletions
+8 -8
View File
@@ -26,8 +26,8 @@ def utils_load_as():
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, ctx.symbol_space.natives)
ntkrnlmp.set_structure_class('_ETHREAD', volatility.framework.symbols.windows.extensions._ETHREAD)
ntkrnlmp.set_structure_class('_LIST_ENTRY', volatility.framework.symbols.windows.extensions._LIST_ENTRY)
ntkrnlmp.set_type_class('_ETHREAD', volatility.framework.symbols.windows.extensions._ETHREAD)
ntkrnlmp.set_type_class('_LIST_ENTRY', volatility.framework.symbols.windows.extensions._LIST_ENTRY)
ctx.symbol_space.append(ntkrnlmp)
# contexts.windows.WindowsContextModifier(ctx.config).modify_context(ctx)
@@ -36,7 +36,7 @@ def utils_load_as():
def test_symbols():
ctx = utils_load_as()
print("Symbols,", ctx.symbol_space.natives.structures)
print("Symbols,", ctx.symbol_space.natives.types)
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
virtual_types['TEST_POINTER'] = [0x4, {'point1': [0x0, ['pointer', ['TEST_SYMBOL']]]}]
@@ -45,11 +45,11 @@ def test_symbols():
ctx.symbol_space.append(ntkrnlmp)
for i in list(ctx.symbol_space['ntkrnlmp'].structures):
symbol = ctx.symbol_space.get_structure('ntkrnlmp!' + i)
print(symbol.vol.structure_name, symbol, symbol.vol.size)
for i in list(ctx.symbol_space['ntkrnlmp'].types):
symbol = ctx.symbol_space.get_type('ntkrnlmp!' + i)
print(symbol.vol.type_name, symbol, symbol.vol.size)
_ = symbol(ctx, objects.ObjectInformation(layer_name = '', offset = 0))
symbol = ctx.symbol_space.get_structure('ntkrnlmp!_EPROCESS')
symbol = ctx.symbol_space.get_type('ntkrnlmp!_EPROCESS')
return symbol
@@ -128,7 +128,7 @@ def test_translation():
a, b = intel._translate(val)
print(hex(val), hex(a), hex(b))
# print(bin(0x39000), bin(0xffab8020))
# print(hex(intel.translate(0xffab8020)))
# print(hex(intel.mapping(0xffab8020, 0)))
def test_plugin():
+1
View File
@@ -6,3 +6,4 @@ This includes default scanning block sizes, etc."""
import os.path
PLUGINS_PATH = [os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "plugins"))]
BANG = "!"
+1 -1
View File
@@ -73,7 +73,7 @@ class Context(interfaces.context.ContextInterface):
:return: A fully constructed object
:rtype: :py:class:`volatility.framework.interfaces.objects.ObjectInterface`
"""
object_template = self._symbol_space.get_structure(symbol)
object_template = self._symbol_space.get_type(symbol)
object_template.update_vol(**arguments)
return object_template(context = self,
object_info = interfaces.objects.ObjectInformation(layer_name = layer_name,
@@ -93,10 +93,6 @@ class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta):
# Unfortunately class attributes can't easily be inheritted from parent classes
provides = {"type": "interface"}
@abstractmethod
def translate(self, offset):
"""Returns a tuple of (offset, layer) indicating the translation of input domain to the output range"""
@abstractmethod
def mapping(self, offset, length):
"""Returns a sorted list of (offset, mapped_offset, length, layer) mappings
+8 -8
View File
@@ -53,7 +53,7 @@ class ObjectInformation(ReadOnlyMapping):
class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta):
""" A base object required to be the ancestor of every object used in volatility """
def __init__(self, context, structure_name, object_info, **kwargs):
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
@@ -64,9 +64,9 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta):
#
# NOTE:
# This allows objects to MASSIVELY MESS with their own internal representation!!!
# Changes to offset, structure_name, etc should NEVER be done
# Changes to offset, type_name, etc should NEVER be done
#
self._vol = collections.ChainMap({}, object_info, {'structure_name': structure_name}, kwargs)
self._vol = collections.ChainMap({}, object_info, {'type_name': type_name}, kwargs)
self._context = context
@property
@@ -79,10 +79,10 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta):
def write(self, value):
"""Writes the new value into the format at the offset the object currently resides at"""
def cast(self, new_structure_name, **additional):
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_structure(new_structure_name)
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,
@@ -112,7 +112,7 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta):
@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.structure_name) + " does not contain any children.")
raise KeyError(repr(template.vol.type_name) + " does not contain any children.")
class Template(validity.ValidityRoutines):
@@ -121,10 +121,10 @@ class Template(validity.ValidityRoutines):
This is effectively a class for currying object calls
"""
def __init__(self, structure_name, **arguments):
def __init__(self, type_name, **arguments):
"""Stores the keyword arguments for later use"""
# Allow the updating of template arguments whilst still in template form
self._vol = collections.ChainMap(arguments, {'structure_name': structure_name})
self._vol = collections.ChainMap(arguments, {'type_name': type_name})
@property
def vol(self):
+87 -35
View File
@@ -3,92 +3,144 @@ Created on 4 May 2013
@author: mike
"""
import bisect
from volatility.framework import validity, exceptions
from volatility.framework import validity, exceptions, constants
from volatility.framework.interfaces import configuration
class Symbol(validity.ValidityRoutines):
def __init__(self, name, offset, type_name = None):
self._name = self._check_type(name, str)
if constants.BANG in self._name:
raise ValueError("Symbol names cannot contain the symbol differentiator (" + constants.BANG + ")")
self._location = None
self._offset = self._check_type(offset, int)
if type_name is None:
type_name = name
self._type_name = self._check_type(type_name, str)
# Scope and location can be added at a later date
@property
def name(self):
"""Returns the name of the symbol"""
return self._name
@property
def type_name(self):
"""Returns the name of the type that the symbol represents"""
return self._type_name
@property
def offset(self):
"""Returns the relative offset of the symbol within the compilation unit"""
return self._offset
class SymbolTableInterface(validity.ValidityRoutines):
"""Handles a table of symbols"""
def __init__(self, name, native_structures = None):
self._check_type(native_structures, NativeTableInterface)
def __init__(self, name, native_types = None):
self._check_type(native_types, NativeTableInterface)
if name:
self._check_type(name, str)
self.name = name or None
self._native_structures = native_structures
self._native_types = native_types
# ## Required Constant symbol functions
# ## Required Symbol functions
def get_constant(self, name):
"""Resolves a symbol name into a constant
def get_symbol(self, name):
"""Resolves a symbol name into a symbol object
If the symbol isn't found, it raises a SymbolError exception
"""
raise NotImplementedError("Abstract property get_constant not implemented by subclass.")
raise NotImplementedError("Abstract property get_symbol not implemented by subclass.")
@property
def constants(self):
"""Returns an iterator of the constant symbols"""
raise NotImplementedError("Abstract property constants not implemented by subclass.")
def symbols(self):
"""Returns an iterator of the Symbols"""
raise NotImplementedError("Abstract property symbols not implemented by subclass.")
# ## Required Structure symbol functions
# ## Required Symbol type functions
def get_structure(self, name):
def get_type(self, name):
"""Resolves a symbol name into an object template
If the symbol isn't found it raises a SymbolError exception
"""
raise NotImplementedError("Abstract method get_structure not implemented by subclass.")
raise NotImplementedError("Abstract method get_type not implemented by subclass.")
@property
def structures(self):
"""Returns an iterator of the structure symbols"""
raise NotImplementedError("Abstract property structures not implemented by subclass.")
def types(self):
"""Returns an iterator of the Symbol types"""
raise NotImplementedError("Abstract property types not implemented by subclass.")
# ## Native Type Handler
@property
def natives(self):
"""Returns None or a symbol_space for handling space specific native types"""
return self._native_structures
"""Returns None or a NativeTable for handling space specific native types"""
return self._native_types
@natives.setter
def natives(self, value):
"""Checks the natives value and then applies it internally
WARNING: This allows changing the underlying size of all the other structures referenced in the symbolspace
WARNING: This allows changing the underlying size of all the other types referenced in the SymbolTable
"""
self._check_type(value, NativeTableInterface)
self._native_structures = value
self._native_types = value
# ## Functions for overriding classes
def set_structure_class(self, name, clazz):
"""Overrides the object class for a specific structure symbol
def set_type_class(self, name, clazz):
"""Overrides the object class for a specific Symbol type
Name *must* be present in self.structures
Name *must* be present in self.types
"""
raise NotImplementedError("Abstract method set_structure_class not implemented yet.")
raise NotImplementedError("Abstract method set_type_class not implemented yet.")
def get_structure_class(self, name):
"""Returns the class associated with a structure symbol"""
raise NotImplementedError("Abstract method get_structure_class not implemented yet.")
def get_type_class(self, name):
"""Returns the class associated with a Symbol type"""
raise NotImplementedError("Abstract method get_type_class not implemented yet.")
def del_structure_class(self, name):
"""Removes the associated class override for a specific structure symbol"""
raise NotImplementedError("Abstract method del_structure_class not implemented yet.")
def del_type_class(self, name):
"""Removes the associated class override for a specific Symbol type"""
raise NotImplementedError("Abstract method del_type_class not implemented yet.")
# ## Convenience functions for location symbols
def get_symbol_type(self, name):
"""Resolves a symbol name into a symbol and then resolves the symbol's type"""
return self.get_type(self.get_symbol(name).type_name)
def get_symbols_by_type(self, type_name):
"""Returns the name of all symbols in this table that have type matching type_name"""
for symbol in self.symbols:
# This allows for searching with and without the table name (in case multiple tables contain
# the same symbol name and we've not specifically been told which one)
if symbol.type_name == type_name or (symbol.type_name.endswith(constants.BANG + type_name)):
yield symbol.name
def get_symbols_by_location(self, offset):
"""Returns the name of all symbols in this table that have type matching type_name"""
sort_symbols = [(s.offset, s) for s in sorted(self.symbols, key = lambda x: x.offset)]
result = bisect.bisect_left(sort_symbols, offset)
if result == len(sort_symbols):
raise StopIteration
closest_symbol = sort_symbols[result][1]
if closest_symbol.offset == offset:
yield closest_symbol.name
class NativeTableInterface(SymbolTableInterface):
"""Class to distinguish NativeSymbolLists from other symbol lists"""
@staticmethod
def constant():
raise exceptions.SymbolError("NativeTables never hold constants")
def get_symbol(self, name):
raise exceptions.SymbolError("NativeTables never hold symbols")
@property
def constants(self):
def symbols(self):
return []
+13 -12
View File
@@ -21,8 +21,8 @@ class Intel(interfaces.layers.TranslationLayerInterface):
def __init__(self, context, config_path, name, page_map_offset, memory_layer, swap_layer = None):
interfaces.layers.TranslationLayerInterface.__init__(self, context, config_path, name)
self._base_layer = memory_layer
self._page_map_offset = page_map_offset
self._base_layer = self._check_type(memory_layer, str)
self._page_map_offset = self._check_type(page_map_offset, int)
# All Intel address spaces work on 4096 byte pages
self._page_size_in_bits = 12
@@ -91,7 +91,7 @@ class Intel(interfaces.layers.TranslationLayerInterface):
entry, = struct.unpack(self._entry_format, self._context.memory.read(self._base_layer, table_offset,
struct.calcsize(self._entry_format)))
# Now we're do
# Now we're done
if not self._page_is_valid(entry):
raise exceptions.InvalidAddressException("Page Fault at entry " + hex(entry) + " in page entry")
page = self._mask(entry, self._maxphyaddr - 1, position + 1) | self._mask(offset, position, 0)
@@ -105,16 +105,14 @@ class Intel(interfaces.layers.TranslationLayerInterface):
except exceptions.InvalidAddressException:
return False
def translate(self, offset):
"""Translates a specific offset based on the paging tables"""
result, _ = self._translate(offset)
return result
def mapping(self, offset, length):
"""Returns a sorted list of (offset, mapped_offset, length, layer) mappings
This allows translation layers to provide maps of contiguous regions in one layer
"""
if length == 0:
mapped_offset, _ = self._translate(offset)
return [(offset, mapped_offset, length, self._base_layer)]
result = []
while length > 0:
chunk_offset, page_size = self._translate(offset)
@@ -126,19 +124,22 @@ class Intel(interfaces.layers.TranslationLayerInterface):
@property
def dependencies(self):
"""Returns a list of the lower layers that this layer is dependent upon"""
"""Returns a list of the lower layer names that this layer is dependent upon"""
# TODO: Add in the whole buffalo
return [self._base_layer]
@classmethod
def get_schema(cls):
return [volatility.framework.configuration.requirements.TranslationLayerRequirement(name = 'memory_layer',
constraints = {"type": "physical"},
constraints = {
"type": "physical"},
optional = False),
volatility.framework.configuration.requirements.TranslationLayerRequirement(name = 'swap_layer',
constraints = {"type": "physical"},
constraints = {
"type": "physical"},
optional = True),
volatility.framework.configuration.requirements.IntRequirement(name = 'page_map_offset', optional = False)]
volatility.framework.configuration.requirements.IntRequirement(name = 'page_map_offset',
optional = False)]
class IntelPAE(Intel):
+132
View File
@@ -0,0 +1,132 @@
"""
Created on 6 Apr 2016
@author: npetroni@volexity.com
"""
import struct
from volatility.framework import interfaces, exceptions
from volatility.framework.configuration import requirements
class LimeFormatException(exceptions.LayerException):
"""Thrown when an error occurs with the underlying Lime file format"""
class LimeLayer(interfaces.layers.TranslationLayerInterface):
"""A Lime format TranslationLayer. Lime is generally used to store
physical memory images where there are large holes in the physical
address space"""
provides = {"type": "physical"}
priority = 21
MAGIC = 0x4c694d45
VERSION = 1
# Magic[4], Version[4], Start[8], End[8], Reserved[8]
# XXX move this to a custom SymbolSpace?
_header_struct = struct.Struct('<IIQQQ')
def __init__(self, context, config_path, name, base_layer):
interfaces.layers.TranslationLayerInterface.__init__(self, context, config_path, name)
self._base_layer = base_layer
# list of tuples (logical start, base start, size)
# loaded by _load_segments() on first access
self._segments = []
self._minaddr = 0
self._maxaddr = 0
@property
def minimum_address(self):
return self._minaddr
@property
def maximum_address(self):
return self._maxaddr
def _load_segments(self):
base_layer = self._context.memory[self._base_layer]
base_maxaddr = base_layer.maximum_address
maxaddr = 0
offset = 0
header_size = self._header_struct.size
segments = []
while offset < base_maxaddr:
header_data = base_layer.read(offset, header_size)
(magic, version, start, end, reserved) = self._header_struct.unpack(header_data)
if magic != self.MAGIC:
raise LimeFormatException("bad magic 0x%x at file offset 0x%x" % (magic, offset))
if version != self.VERSION:
raise LimeFormatException("unexpected version %d at file offset 0x%x" % (version, offset))
if start < maxaddr or end < start:
raise LimeFormatException("bad start/end 0x%x/0x%x at file offset 0x%x" % (start, end, offset))
segment_length = end - start + 1
segments.append((start, offset + header_size, segment_length))
maxaddr = end
offset = offset + header_size + segment_length
if len(segments) == 0:
raise LimeFormatException("No LiME segments defined in " + self._base_layer)
self._segments = segments
self._minaddr = segments[0][0]
self._maxaddr = maxaddr
def _find_segment(self, offset):
"""Finds the segment containing a given offset
Returns the segment tuple
"""
if not self._segments:
self._load_segments()
for logical_start, base_start, size in self._segments:
if offset >= logical_start and offset < (logical_start + size):
return (logical_start, base_start, size)
raise exceptions.InvalidAddressException("Lime fault at address " + hex(offset))
def is_valid(self, offset, length = 1):
"""Returns whether the address offset can be translated to a valid address"""
try:
return all([self._context.memory[self._base_layer].is_valid(mapped_offset) for _, mapped_offset, _, _ in
self.mapping(offset, length)])
except exceptions.InvalidAddressException:
return False
def mapping(self, offset, length):
"""Returns a sorted list of (offset, mapped_offset, length, layer) mappings"""
if length == 0:
logical_start, base_start, size = self._find_segment(offset)
mapped_offset = offset - logical_start + base_start
return [(offset, mapped_offset, 0, self._base_layer)]
result = []
while length > 0:
logical_start, base_start, size = self._find_segment(offset)
chunk_offset = offset - logical_start + base_start
chunk_size = min(size - (offset - logical_start), length)
result.append((offset, chunk_offset, chunk_size, self._base_layer))
length -= chunk_size
offset += chunk_size
return result
@property
def dependencies(self):
"""Returns a list of the lower layers that this layer is dependent upon"""
return [self._base_layer]
@classmethod
def get_schema(cls):
return [requirements.TranslationLayerRequirement(name = 'base_layer',
constraints = {"type": "physical"},
optional = False)]
+5 -3
View File
@@ -62,7 +62,9 @@ class FileLayer(interfaces.layers.DataLayerInterface):
def __init__(self, context, config_path, name, filename):
interfaces.layers.DataLayerInterface.__init__(self, context, config_path, name)
self._file = open(filename, "r+b")
# FIXME: Add "+" to the mode once we've determined whether write mode is enabled
mode = "rb"
self._file = open(filename, mode)
self._size = os.path.getsize(filename)
@property
@@ -78,6 +80,8 @@ class FileLayer(interfaces.layers.DataLayerInterface):
def is_valid(self, offset, length = 1):
"""Returns whether the offset is valid or not"""
if length <= 0:
raise TypeError("Length must be positive")
return (self.minimum_address <= offset <= self.maximum_address and
self.minimum_address <= offset + length - 1 <= self.maximum_address)
@@ -85,8 +89,6 @@ class FileLayer(interfaces.layers.DataLayerInterface):
"""Reads from the file at offset for length"""
if not self.is_valid(offset, length):
raise exceptions.InvalidAddressException("Offset outside of the " + self.name + " file boundaries")
if length < 0:
raise TypeError("Length must be positive")
self._file.seek(offset)
data = self._file.read(length)
if len(data) < length:
+36 -23
View File
@@ -26,19 +26,23 @@ class Void(interfaces.objects.ObjectInterface):
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, structure_name, object_info, struct_format):
def __init__(self, context, type_name, object_info, struct_format):
interfaces.objects.ObjectInterface.__init__(self,
context = context,
structure_name = structure_name,
type_name = type_name,
object_info = object_info,
struct_format = struct_format)
self._struct_format = struct_format
def __new__(cls, context, structure_name, object_info, struct_format, **kwargs):
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__
@@ -84,15 +88,15 @@ class Bytes(PrimitiveObject, bytes):
"""Primitive Object that handles specific series of bytes"""
_struct_type = bytes
def __init__(self, context, structure_name, object_info, length = 1):
def __init__(self, context, type_name, object_info, length = 1):
interfaces.objects.ObjectInterface.__init__(self,
context = context,
structure_name = structure_name,
type_name = type_name,
object_info = object_info,
struct_format = str(length) + "s")
self._vol['length'] = length
def __new__(cls, context, structure_name, object_info, length = 1, **kwargs):
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__
@@ -112,17 +116,17 @@ class String(PrimitiveObject, str):
"""
_struct_type = str
def __init__(self, context, structure_name, object_info, max_length = 1, encoding = "utf-8", errors = None):
def __init__(self, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = None):
PrimitiveObject.__init__(self,
context = context,
structure_name = structure_name,
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, structure_name, object_info, max_length = 1, encoding = "utf-8", errors = None, **kwargs):
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__
@@ -145,12 +149,12 @@ class String(PrimitiveObject, str):
class Pointer(Integer):
"""Pointer which points to another object"""
def __init__(self, context, structure_name, object_info, struct_format, target = None):
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,
structure_name = structure_name,
type_name = type_name,
struct_format = struct_format)
self._vol['target'] = target
@@ -195,16 +199,16 @@ class Pointer(Integer):
class BitField(PrimitiveObject, int):
"""Object containing a field which is made up of bits rather than whole bytes"""
def __new__(cls, context, structure_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0):
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,
structure_name = structure_name,
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, structure_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0):
PrimitiveObject.__init__(self, context, structure_name, object_info, struct_format)
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
@@ -232,11 +236,11 @@ class Enumeration(interfaces.objects.ObjectInterface):
class Array(interfaces.objects.ObjectInterface, collections.Sequence):
"""Object which can contain a fixed number of an object type"""
def __init__(self, context, structure_name, object_info, count = 0, target = None):
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,
structure_name = structure_name,
type_name = type_name,
object_info = object_info)
self._vol['count'] = self._check_type(count, int)
self._vol['target'] = target
@@ -293,10 +297,10 @@ class Struct(interfaces.objects.ObjectInterface):
Keep the number of methods in this class low or very specific, since each one could overload a valid member.
"""
def __init__(self, context, structure_name, object_info, size, members):
def __init__(self, context, type_name, object_info, size, members):
interfaces.objects.ObjectInterface.__init__(self,
context = context,
structure_name = structure_name,
type_name = type_name,
object_info = object_info,
size = size,
members = members)
@@ -306,7 +310,7 @@ class Struct(interfaces.objects.ObjectInterface):
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
@classmethod
def size(cls, template):
"""Method to return the size of this structure"""
"""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
@@ -339,7 +343,7 @@ class Struct(interfaces.objects.ObjectInterface):
@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 structure_name
# 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()]):
@@ -350,7 +354,7 @@ class Struct(interfaces.objects.ObjectInterface):
return self.__getattr__(attr)
def __getattr__(self, attr):
"""Method for accessing members of the structure"""
"""Method for accessing members of the type"""
if attr in self._concrete_members:
return self._concrete_members[attr]
elif attr in self.vol.members:
@@ -362,7 +366,16 @@ class Struct(interfaces.objects.ObjectInterface):
parent = self))
self._concrete_members[attr] = member
return member
raise AttributeError("'" + self.vol.structure_name + "' Struct has no attribute '" + attr + "'")
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__))
+4 -4
View File
@@ -12,14 +12,14 @@ class ObjectTemplate(interfaces.objects.Template, validity.ValidityRoutines):
This is effectively a method of currying, but adds more structure to avoid abuse.
It also allows inspection of information that should already be known:
* Structure size
* Type size
* Members, etc
etc.
"""
def __init__(self, object_class = None, structure_name = None, **arguments):
def __init__(self, object_class = None, type_name = None, **arguments):
interfaces.objects.Template.__init__(self,
structure_name = structure_name,
type_name = type_name,
**arguments)
self._check_class(object_class, interfaces.objects.ObjectInterface)
self.update_vol(object_class = object_class)
@@ -71,5 +71,5 @@ class ReferenceTemplate(interfaces.objects.Template):
"""
def __call__(self, context, object_info):
template = context.symbol_space.get_structure(self.vol.structure_name)
template = context.symbol_space.get_type(self.vol.type_name)
return template(context = context, object_info = object_info)
+48 -30
View File
@@ -8,14 +8,14 @@ import collections
import collections.abc
import warnings
from volatility.framework import objects, interfaces, exceptions
from volatility.framework import objects, interfaces, exceptions, constants
from volatility.framework.symbols import native, vtypes, windows
class SymbolType(object):
# Suitably random values until we make this an Enum and require python >= 3.4
STRUCTURE = 143534545
CONSTANT = 28293045
TYPE = 143534545
SYMBOL = 28293045
class SymbolSpace(collections.abc.Mapping):
@@ -25,25 +25,43 @@ class SymbolSpace(collections.abc.Mapping):
proceed down through the ranks if a namespace isn't specified.
"""
def __init__(self, native_structures = None):
if not isinstance(native_structures, interfaces.symbols.NativeTableInterface):
raise TypeError("SymbolSpace native_structures must be NativeSymbolInterface")
def __init__(self, native_types = None):
if not isinstance(native_types, interfaces.symbols.NativeTableInterface):
raise TypeError("SymbolSpace native_types must be NativeSymbolInterface")
self._dict = collections.OrderedDict()
self._native_structures = native_structures
self._native_types = native_types
# Permanently cache all resolved symbols
self._resolved = {}
def get_symbols_by_type(self, type_name):
"""Returns all symbols based """
for table in self._dict.keys():
for symbol_name in self._dict[table].get_symbols_by_type(type_name):
yield table + constants.BANG + symbol_name
def get_symbols_by_location(self, offset, table_name = None):
"""Returns all symbols that exist at a specific relative offset"""
table_list = self._dict.values()
if table_name is not None:
if table_name in self._dict:
table_list = [self._dict[table_name]]
else:
table_list = []
for table in table_list:
for symbol_name in self._dict[table].get_symbols_by_location(offset = offset):
yield table + constants.BANG + symbol_name
@property
def natives(self):
"""Returns the native_types for this symbol space"""
return self._native_structures
return self._native_types
@natives.setter
def natives(self, native_structures):
if native_structures is not None:
def natives(self, native_types):
if native_types is not None:
warnings.warn(
"Resetting the native type can cause have drastic effects on memory analysis using this space")
self._native_structures = native_structures
self._native_types = native_types
def __len__(self):
"""Returns the number of tables within the space"""
@@ -73,32 +91,32 @@ class SymbolSpace(collections.abc.Mapping):
def _weak_resolve(self, resolve_type, name):
"""Takes a symbol name and resolves it with ReferentialTemplates"""
if resolve_type == SymbolType.STRUCTURE:
get_function = 'get_structure'
elif resolve_type == SymbolType.CONSTANT:
get_function = 'get_constant'
if resolve_type == SymbolType.TYPE:
get_function = 'get_type'
elif resolve_type == SymbolType.SYMBOL:
get_function = 'get_symbol'
else:
raise ValueError("Weak_resolve called without a proper SymbolType.")
name_array = name.split("!")
name_array = name.split(constants.BANG)
if len(name_array) == 2:
table_name = name_array[0]
component_name = name_array[1]
return getattr(self._dict[table_name], get_function)(component_name)
elif name in self.natives.structures:
elif name in self.natives.types:
return getattr(self.natives, get_function)(name)
raise exceptions.SymbolError("Malformed symbol name")
def get_structure(self, structure_name):
def get_type(self, type_name):
"""Takes a symbol name and resolves it
This method ensures that all referenced templates (including self-referential templates)
are satisfied as ObjectTemplates
"""
# Traverse down any resolutions
if structure_name not in self._resolved:
self._resolved[structure_name] = self._weak_resolve(SymbolType.STRUCTURE, structure_name)
traverse_list = [structure_name]
if type_name not in self._resolved:
self._resolved[type_name] = self._weak_resolve(SymbolType.TYPE, type_name)
traverse_list = [type_name]
replacements = set()
# Whole Symbols that still need traversing
while traverse_list:
@@ -110,18 +128,18 @@ class SymbolSpace(collections.abc.Mapping):
if isinstance(child, objects.templates.ReferenceTemplate):
# If we haven't seen it before, subresolve it and also add it
# to the "symbols that still need traversing" list
if child.vol.structure_name not in self._resolved:
traverse_list.append(child.vol.structure_name)
self._resolved[child.vol.structure_name] = self._weak_resolve(SymbolType.STRUCTURE,
child.vol.structure_name)
if child.vol.type_name not in self._resolved:
traverse_list.append(child.vol.type_name)
self._resolved[child.vol.type_name] = self._weak_resolve(SymbolType.TYPE,
child.vol.type_name)
# Stash the replacement
replacements.add((traverser, child))
elif child.children:
template_traverse_list.append(child)
for (parent, child) in replacements:
parent.replace_child(child, self._resolved[child.vol.structure_name])
return self._resolved[structure_name]
parent.replace_child(child, self._resolved[child.vol.type_name])
return self._resolved[type_name]
def get_constant(self, constant_name):
"""Look-up a constant name across all the contained symbol spaces"""
return self._weak_resolve(SymbolType.CONSTANT, constant_name)
def get_symbol(self, symbol_name):
"""Look-up a symbol name across all the contained symbol spaces"""
return self._weak_resolve(SymbolType.SYMBOL, symbol_name)
+20 -20
View File
@@ -19,19 +19,19 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
native_class, _native_struct = self._native_dictionary[native_type]
self._overrides[native_type] = native_class
# Create this once early, because it may get used a lot
self._structures = set(self._native_dictionary).union(
self._types = set(self._native_dictionary).union(
{'Enumeration', 'array', 'BitField', 'void', 'pointer', 'String', 'Bytes'})
def get_structure_class(self, name):
def get_type_class(self, name):
ntype, fmt = native_types.get(name, (objects.Integer, ''))
return ntype
@property
def structures(self):
"""Returns an iterator of the structure symbol names"""
return self._structures
def types(self):
"""Returns an iterator of the symbol type names"""
return self._types
def get_structure(self, structure_name):
def get_type(self, type_name):
"""Resolves a symbol name into an object template
symbol_space is used to resolve any target symbols if they don't exist in this list
@@ -39,31 +39,31 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
# NOTE: These need updating whenever the object init signatures change
additional = {}
obj = None
if structure_name == 'void':
if type_name == 'void':
obj = objects.Void
elif structure_name == 'array':
elif type_name == 'array':
obj = objects.Array
additional = {"count": 0, "target": self.get_structure('void')}
elif structure_name == 'Enumeration':
additional = {"count": 0, "target": self.get_type('void')}
elif type_name == 'Enumeration':
obj = objects.Enumeration
additional = {"target": self.get_structure('void'), "choices": {}}
elif structure_name == 'BitField':
additional = {"target": self.get_type('void'), "choices": {}}
elif type_name == 'BitField':
obj = objects.BitField
additional = {"start_bit": 0, "end_bit": 0}
elif structure_name == 'String':
elif type_name == 'String':
obj = objects.String
additional = {"max_length": 0}
elif structure_name == 'Bytes':
elif type_name == 'Bytes':
obj = objects.Bytes
additional = {"length": 0}
if obj is not None:
return objects.templates.ObjectTemplate(obj, structure_name = structure_name, **additional)
return objects.templates.ObjectTemplate(obj, type_name = type_name, **additional)
_native_type, native_format = self._native_dictionary[structure_name]
if structure_name == 'pointer':
additional = {'target': self.get_structure('void')}
return objects.templates.ObjectTemplate(self.get_structure_class(structure_name), # pylint: disable=W0142
structure_name = structure_name,
_native_type, native_format = self._native_dictionary[type_name]
if type_name == 'pointer':
additional = {'target': self.get_type('void')}
return objects.templates.ObjectTemplate(self.get_type_class(type_name), # pylint: disable=W0142
type_name = type_name,
struct_format = native_format,
**additional)
+23 -23
View File
@@ -6,7 +6,7 @@ Created on 10 Apr 2013
import copy
from volatility.framework import exceptions, objects, interfaces
from volatility.framework import exceptions, objects, interfaces, constants
# ## TODO
@@ -36,22 +36,22 @@ from volatility.framework import exceptions, objects, interfaces
# vtype list and a struct dictionary
class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface):
"""Symbol Table that handles vtype datastructures"""
"""Symbol Table that handles vtype datatypes"""
def __init__(self, name, vtype_dictionary, native_structures = None):
interfaces.symbols.SymbolTableInterface.__init__(self, name, native_structures)
def __init__(self, name, vtype_dictionary, native_types = None):
interfaces.symbols.SymbolTableInterface.__init__(self, name, native_types)
self._vtypedict = vtype_dictionary
self._overrides = {}
def get_structure_class(self, name):
def get_type_class(self, name):
return self._overrides.get(name, objects.Struct)
def set_structure_class(self, name, clazz):
if name not in self.structures:
raise ValueError("Symbol " + name + " not in " + self.name + " SymbolTable")
def set_type_class(self, name, clazz):
if name not in self.types:
raise ValueError("Symbol type " + name + " not in " + self.name + " SymbolTable")
self._overrides[name] = clazz
def del_structure_class(self, name):
def del_type_class(self, name):
if name in self._overrides:
del self._overrides[name]
@@ -60,23 +60,23 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface):
if not dictionary:
raise exceptions.SymbolSpaceError("Invalid vtype dictionary: " + repr(dictionary))
structure_name = dictionary[0]
type_name = dictionary[0]
if structure_name in self.natives.structures:
if type_name in self.natives.types:
# The symbol is a native type
native_template = self.natives.get_structure(structure_name)
native_template = self.natives.get_type(type_name)
# Add specific additional parameters, etc
update = {}
if structure_name == 'array':
if type_name == 'array':
update['count'] = dictionary[1]
update['target'] = self._vtypedict_to_template(dictionary[2])
elif structure_name == 'pointer':
elif type_name == 'pointer':
update["target"] = self._vtypedict_to_template(dictionary[1])
elif structure_name == 'Enumeration':
elif type_name == 'Enumeration':
update = copy.deepcopy(dictionary[1])
update["target"] = self._vtypedict_to_template([update['target']])
elif structure_name == 'BitField':
elif type_name == 'BitField':
update = dictionary[1]
update['target'] = self._vtypedict_to_template([update['native_type']])
native_template.update_vol(**update) # pylint: disable=W0142
@@ -86,25 +86,25 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface):
if len(dictionary) > 1:
raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary))
return objects.templates.ReferenceTemplate(structure_name = self.name + "!" + structure_name)
return objects.templates.ReferenceTemplate(type_name = self.name + constants.BANG + type_name)
@property
def structures(self):
def types(self):
"""Returns an iterator of the symbol names"""
return self._vtypedict.keys()
def get_structure(self, structure_name):
def get_type(self, type_name):
"""Resolves an individual symbol"""
if structure_name not in self._vtypedict:
if type_name not in self._vtypedict:
raise exceptions.SymbolError
size, curdict = self._vtypedict[structure_name]
size, curdict = self._vtypedict[type_name]
members = {}
for member_name in curdict:
relative_offset, vtypedict = curdict[member_name]
member = (relative_offset, self._vtypedict_to_template(vtypedict))
members[member_name] = member
object_class = self.get_structure_class(structure_name)
return objects.templates.ObjectTemplate(structure_name = self.name + "!" + structure_name,
object_class = self.get_type_class(type_name)
return objects.templates.ObjectTemplate(type_name = self.name + constants.BANG + type_name,
object_class = object_class,
size = size,
members = members)
@@ -12,13 +12,13 @@ class _ETHREAD(objects.Struct):
class _LIST_ENTRY(objects.Struct, collections.abc.Iterable):
def to_list(self, structure, member, forward = True, sentinel = True, layer = None):
def to_list(self, symbol_type, member, forward = True, sentinel = True, layer = None):
"""Returns an iterator of the entries in the list"""
if layer is None:
layer = self.vol.layer_name
relative_offset = self._context.symbol_space.get_structure(structure).relative_child_offset(member)
relative_offset = self._context.symbol_space.get_type(symbol_type).relative_child_offset(member)
direction = 'Blink'
if forward:
@@ -26,16 +26,16 @@ class _LIST_ENTRY(objects.Struct, collections.abc.Iterable):
link = getattr(self, direction).dereference()
if not sentinel:
yield self._context.object(structure, layer, offset = self.vol.offset - relative_offset)
yield self._context.object(symbol_type, layer, offset = self.vol.offset - relative_offset)
seen = {self.vol.offset}
while link.vol.offset not in seen:
obj = self._context.object(structure, layer, offset = link.vol.offset - relative_offset)
obj = self._context.object(symbol_type, layer, offset = link.vol.offset - relative_offset)
yield obj
seen.add(link.vol.offset)
link = getattr(link, direction).dereference()
def __iter__(self):
return self.to_list(self.vol.parent.vol.structure_name, self.vol.member_name)
return self.to_list(self.vol.parent.vol.type_name, self.vol.member_name)
@@ -38,8 +38,8 @@ class WindowsKernelSymbolProvider(interfaces.symbols.SymbolTableProviderInterfac
vtype_table = vtypes.VTypeSymbolTable(cls.space_name, virtual_types, context.symbol_space.natives)
# Set-up windows specific types
vtype_table.set_structure_class('_ETHREAD', extensions._ETHREAD)
vtype_table.set_structure_class('_LIST_ENTRY', extensions._LIST_ENTRY)
vtype_table.set_type_class('_ETHREAD', extensions._ETHREAD)
vtype_table.set_type_class('_LIST_ENTRY', extensions._LIST_ENTRY)
context.symbol_space.append(vtype_table)
context.config[config_path] = cls.space_name
+1 -1
View File
@@ -27,7 +27,7 @@ class PsList(plugins.PluginInterface):
# Get the process in the physical space
flateproc = ctx.object("ntkrnlmp!_EPROCESS", physical_layer, offset = offset)
# Determine the relative offset from the Thread head to the ThreadListEntry
reloff = ctx.symbol_space.get_structure("ntkrnlmp!_ETHREAD").relative_child_offset("ThreadListEntry")
reloff = ctx.symbol_space.get_type("ntkrnlmp!_ETHREAD").relative_child_offset("ThreadListEntry")
# Get the thread object in kernel space from the
ethread = ctx.object("ntkrnlmp!_ETHREAD", kernel_layer, offset = flateproc.ThreadListHead.Flink - reloff)
# Get the process from the thread object in kernel space