Move context, tidy up object model and add memory to test_rig.

This commit is contained in:
Mike Auty
2013-05-05 22:06:38 +01:00
parent dc65c356fc
commit e351958413
12 changed files with 196 additions and 92 deletions
+27 -10
View File
@@ -5,34 +5,51 @@ Created on 10 Mar 2013
'''
import pdb
from volatility.framework import xp_sp2_x86_vtypes, context, symbols
from volatility import framework
from volatility.framework import xp_sp2_x86_vtypes, layers
from volatility.framework.symbols import vtypes, native
def main():
def test_symbols():
nativelst = native.x86NativeTable
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, nativelst)
ctx = context.Context(symbols.SymbolSpace(nativelst))
ctx.symbol_space.append(nativelst)
ctx = framework.Context(nativelst)
# ctx.symbol_space.append(nativelst)
ctx.symbol_space.append(ntkrnlmp)
print("Symbols,", nativelst.symbols)
for _ in []: # 1, 2]:
for i in list(ntkrnlmp.symbols):
symbol = ctx.symbol_space.resolve('ntkrnlmp!' + i)
print(symbol.symbol_name, symbol, symbol.size)
# objthing = symbol(context, layer_name = '', offset = 0)
for i in list(ntkrnlmp.symbols):
symbol = ctx.symbol_space.resolve('ntkrnlmp!' + i)
print(symbol.symbol_name, symbol, symbol.size)
_objthing = symbol(ctx, layer_name = '', offset = 0)
symbol = ctx.symbol_space.resolve('ntkrnlmp!_EPROCESS')
def test_memory():
nativelst = native.x86NativeTable
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
virtual_types['TEST_SYMBOL'] = [0x6,
{'test1': [0x0, ['unsigned int']],
'test2': [0x4, ['unsigned short']]
}]
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, nativelst)
ctx = framework.Context(nativelst)
ctx.symbol_space.append(ntkrnlmp)
base = layers.BufferDataLayer(ctx, 'data', buffer = b"\x01\x00\x00\x00\x02\x00")
ctx.memory.add_layer(base)
val = ctx.object('ntkrnlmp!TEST_SYMBOL', 'data', 0)
print(hex(val.test1), val.test1.size)
if __name__ == '__main__':
# import timeit
# print(timeit.Timer(main).timeit(10))
try:
main()
# test_symbols()
test_memory()
except Exception as e:
print(repr(e))
pdb.post_mortem()
+39
View File
@@ -28,5 +28,44 @@ def require_version(*args):
if args[1] > version()[1]:
raise Exception("Framework version " + ".".join([str(x) for x in version()[0:1]]) + " is an older revision than the required version " + ".".join([str(x) for x in args[0:2]]))
from volatility.framework import interfaces, symbols, layers
class Context(interfaces.ContextInterface):
"""Maintains the context within which to construct objects"""
def __init__(self, natives):
super(Context, self).__init__()
self._symbol_space = symbols.SymbolSpace(natives)
self._memory = layers.Memory()
### Symbol Space Functions
@property
def symbol_space(self):
return self._symbol_space
@property
def memory(self):
return self._memory
### Address Space Functions
def add_translation_layer(self, layer):
"""Adds a named translation layer to the context"""
self._memory.add_layer(layer)
### Object Factory Functions
def object(self, symbol, layer_name, offset):
"""Object factory, takes a context, symbol, offset and optional layername
Looks up the layername in the context, finds the object template based on the symbol,
and constructs an object using the object template on the layer at the offset.
Returns a fully constructed object
"""
object_template = self._symbol_space.resolve(symbol)
return object_template(self, layer_name = layer_name, offset = offset)
-41
View File
@@ -1,41 +0,0 @@
'''
Created on 12 Feb 2013
@author: mike
'''
from volatility.framework import interfaces
class Context(interfaces.ContextInterface):
"""Maintains the context within which to construct objects"""
def __init__(self, symbol_space):
super(Context, self).__init__()
self._symbol_space = symbol_space
self._layers = {}
### Symbol Space Functions
@property
def symbol_space(self):
return self._symbol_space
### Address Space Functions
def add_translation_layer(self, layer, name = None):
"""Adds a named translation layer to the context"""
self._layers[name] = layer
### Object Factory Functions
def object(self, symbol, layer_name, offset):
"""Object factory, takes a context, symbol, offset and optional layername
Looks up the layername in the context, finds the object template based on the symbol,
and constructs an object using the object template on the layer at the offset.
Returns a fully constructed object
"""
object_template = self._symbol_space.resolve(symbol)
return object_template(self, layer_name = layer_name, offset = offset)
+3
View File
@@ -15,3 +15,6 @@ class InvalidAddressException(VolatilityException):
class SymbolSpaceError(VolatilityException):
"""Thrown when an error occurs dealing with Symbols and Symbolspaces"""
class LayerException(VolatilityException):
"""Thrown when an error occurs dealing with memory and layers"""
+12 -7
View File
@@ -19,10 +19,15 @@ class ContextInterface(object):
def symbol_space(self):
"""Returns the symbol_space for the context"""
### Address Space Functions
### Memory Functions
def add_translation_layer(self, layer, name = None):
"""Adds a named translation layer to the context"""
@property
def memory(self):
"""Returns the memory object for the context"""
def add_layer(self, layer):
"""Adds a named translation layer to the context memory"""
self.memory.add_layer(layer)
### Object Factory Functions
@@ -37,19 +42,19 @@ class ContextInterface(object):
class ObjectInterface(validity.ValidityRoutines):
""" A base object required to be the ancestor of every object used in volatility """
def __init__(self, context, layer_name, offset, symbol_name, size, **kwargs):
def __init__(self, context, layer_name, offset, symbol_name, size, parent = None, **kwargs):
# Since objects are likely to be instantiated often,
# we're only checking that a context is a context
# Everything else may be wrong, but that will get caught later on
self.type_check(context, ContextInterface)
self._context = context
self._context = self.type_check(context, ContextInterface)
self._parent = None if not parent else self.type_check(parent, ObjectInterface)
self._offset = offset
self._layer_name = layer_name
self._symbol_name = symbol_name
self._size = size
def cast(self, new_symbol_name):
object_template = self._context.resolve(new_symbol_name)
object_template = self._context.symbol_space.resolve(new_symbol_name)
return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset)
class Template(object):
@@ -50,3 +50,6 @@ class TranslationLayerInterface(DataLayerInterface):
def mapping(self, offset, length):
"""Returns a list of (offset, length, layer) mappings"""
def dependencies(self):
"""Returns a list of layer names that this layer translates onto"""
+49 -2
View File
@@ -4,13 +4,60 @@ Created on 4 May 2013
@author: mike
'''
from volatility.framework import validity, exceptions
from volatility.framework.interfaces import layers
class Memory(validity.ValidityRoutines):
"""Container for multiple layers of data"""
def __init__(self):
self._layers = {}
def read(self, layer, offset, length, pad = False):
"""Reads from a particular layer at offset for length bytes"""
return self[layer].read(offset, length, pad)
def write(self, layer, offset, data):
"""Writes to a particular layer at offset for length bytes"""
self[layer].write(offset, data)
def add_layer(self, layer):
"""Adds a layer to memory model
This will throw an exception if the required dependencies are not met
"""
self.type_check(layer, layers.DataLayerInterface)
if isinstance(layer, layers.TranslationLayerInterface):
if layer.name in self._layers:
raise exceptions.LayerException("")
missing_list = [sublayer for sublayer in layer.dependencies if sublayer not in self._layers]
if missing_list:
raise exceptions.LayerException("Layer " + layer.name + " has unmet dependencies of " + ", ".join(missing_list))
self._layers[layer.name] = layer
def del_layer(self, name):
"""Removes the layer called name
This will throw an exception if other layers depend upon this layer
"""
for layer in self._layers:
depend_list = [superlayer for superlayer in self._layers if name in superlayer.dependencies]
if depend_list:
raise exceptions.LayerException("Layer " + layer.name + " is depended upon by " + ", ".join(depend_list))
del self._layers[name]
def __getitem__(self, name):
"""Returns the layer of specified name"""
return self._layers[name]
def check_cycles(self):
"""Runs through the available layers and identifies if there are cycles in the DAG"""
class BufferDataLayer(layers.DataLayerInterface):
"""A DataLayer class backed by a buffer in memory, designed for testing and swift data access"""
def __init__(self, name = None, buffer = None):
super(BufferDataLayer, self).__init__(name)
def __init__(self, context, name, buffer):
super(BufferDataLayer, self).__init__(context, name)
self._buffer = self.type_check(buffer, bytes)
@property
+39 -18
View File
@@ -28,15 +28,19 @@ class Void(interfaces.ObjectInterface):
class PrimitiveObject(interfaces.ObjectInterface):
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
def __init__(self, context, layer_name, offset, symbol_name, struct_format = '<I', **kwargs):
super(PrimitiveObject, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = '<I', **kwargs):
super(PrimitiveObject, self).__init__(context = context,
layer_name = layer_name,
offset = offset,
symbol_name = symbol_name,
size = size,
parent = parent)
self._struct_format = struct_format
@classmethod
def _struct_value(cls, struct_format, context, layer_name, offset, symbol_name):
aspace = context.get_address_space(layer_name)
length = struct.calcsize(struct_format)
data = aspace.read(offset, length)
data = context.memory.read(layer_name, offset, length)
(value,) = struct.unpack(struct_format, data)
return value
@@ -58,19 +62,19 @@ class Integer(PrimitiveObject, int):
"""Primitive Object that handles standard numeric types"""
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
return int.__new__(cls, cls._struct_value(struct_format, context, layer_name, offset, symbol_name))
class Float(PrimitiveObject, float):
"""Primitive Object that handles double or floating point numbers"""
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
return float.__new__(cls, cls._struct_value(struct_format, context, layer_name, offset, symbol_name))
class Bytes(PrimitiveObject, bytes):
"""Primitive Object that handles specific series of bytes"""
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
def __new__(cls, context, layer_name, offset, symbol_name, length = 1, **kwargs):
return bytes.__new__(cls, cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name))
class String(PrimitiveObject, str):
"""Primitive Object that handles string values
@@ -78,18 +82,20 @@ class String(PrimitiveObject, str):
length: specifies the maximum possible length that the string could hold in memory
"""
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
def __new__(cls, context, layer_name, offset, symbol_name, length = 1, **kwargs):
return str.__new__(cls, cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name))
class Pointer(Integer):
"""Pointer which points to another object"""
def __init__(self, context, layer_name, offset, symbol_name, struct_format = None, target = None):
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = None, target = None, **kwargs):
if not isinstance(target, templates.ObjectTemplate):
raise TypeError("Pointer targets must be an ObjectTemplate")
super(Pointer, self).__init__(context,
layer_name = layer_name,
offset = offset,
symbol_name = symbol_name,
size = size,
parent = parent,
struct_format = struct_format)
self._target = target
@@ -97,7 +103,11 @@ class Pointer(Integer):
"""Dereferences the pointer"""
# Cache the target
if isinstance(self._target, templates.ObjectTemplate):
self._target = self.target(context = self._context, layer_name = self._layer_name, offset = self, self = self._target.size, parent = self)
self._target = self.target(context = self._context,
layer_name = self._layer_name,
offset = self,
self = self._target.size,
parent = self)
return self._target
def __getattr__(self, attr):
@@ -120,8 +130,13 @@ 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, layer_name, offset, symbol_name, target = None, start_bit = 0, end_bit = 0):
value = target(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
def __new__(cls, context, layer_name, offset, symbol_name, size = None, parent = None, target = None, start_bit = 0, end_bit = 0, **kwargs):
value = target(context = context,
layer_name = layer_name,
offset = offset,
symbol_name = symbol_name,
size = size,
parent = parent)
return (value >> start_bit) & ((1 << end_bit) - 1)
@classmethod
@@ -140,10 +155,15 @@ class Enumeration(interfaces.ObjectInterface):
class Array(interfaces.ObjectInterface, collections.Sequence):
"""Object which can contain a fixed number of an object type"""
def __init__(self, context, layer_name, offset, symbol_name, size = None, count = 0, target = None):
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, count = 0, target = None, **kwargs):
if not isinstance(target, templates.ObjectTemplate):
raise TypeError("Array target must be an ObjectTemplate")
super(Array, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name, size = size)
super(Array, self).__init__(context = context,
layer_name = layer_name,
offset = offset,
symbol_name = symbol_name,
size = size,
parent = parent)
self._count = count
self._target = target
@@ -179,12 +199,13 @@ class Array(interfaces.ObjectInterface, collections.Sequence):
class Struct(interfaces.ObjectInterface):
"""Object which can contain members that are other objects"""
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None):
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None, parent = None, **kwargs):
super(Struct, self).__init__(context = context,
layer_name = layer_name,
offset = offset,
symbol_name = symbol_name,
size = size)
size = size,
parent = parent)
self.check_members(members)
self._members = members
self._concrete_members = {}
+8 -6
View File
@@ -4,9 +4,9 @@ Created on 1 Mar 2013
@author: mike
'''
from volatility.framework import interfaces
from volatility.framework import interfaces, validity
class ObjectTemplate(interfaces.Template):
class ObjectTemplate(interfaces.Template, validity.ValidityRoutines):
"""Factory class that produces objects that adhere to the Object interface on demand
This is effectively a method of currying, but adds more structure to avoid abuse.
@@ -17,9 +17,7 @@ class ObjectTemplate(interfaces.Template):
"""
def __init__(self, object_class = None, symbol_name = None, **kwargs):
super(ObjectTemplate, self).__init__(symbol_name = symbol_name, **kwargs)
if not issubclass(object_class, interfaces.ObjectInterface):
raise TypeError("ObjectTemplate object_class must inherit from ObjectInterface")
self.object_class = object_class
self.object_class = self.class_check(object_class, interfaces.ObjectInterface)
@property
def size(self):
@@ -46,7 +44,11 @@ class ObjectTemplate(interfaces.Template):
Returns: an object adhereing to the Object interface
"""
return self.object_class(context = context, layer_name = layer_name, offset = offset, symbol_name = self.symbol_name, size = self.size, parent = parent, **self._kwargs)
# We always use the template size (as calculated by the object class)
# over the one passed in by an argument
self._kwargs['size'] = self.size
self._kwargs['symbol_name'] = self.symbol_name
return self.object_class(context = context, layer_name = layer_name, offset = offset, parent = parent, **self._kwargs)
class ReferenceTemplate(interfaces.Template):
"""Factory class that produces objects based on a delayed reference type
+2
View File
@@ -53,6 +53,8 @@ class SymbolSpace(collections.Mapping):
table_name = symarr[0]
symbol_name = symarr[1]
return self._dict[table_name].resolve(symbol_name)
elif symbol in self.natives:
return self.natives.resolve(symbol)
raise exceptions.SymbolNotFoundException("Malformed symbol name")
def resolve(self, symbol):
+6 -7
View File
@@ -33,19 +33,18 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
symbol_space is used to resolve any target symbols if they don't exist in this list
"""
if symbol_name == 'void':
return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name, size = 0)
return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name)
elif symbol_name == 'array':
return obj.templates.ObjectTemplate(obj.Array, symbol_name = symbol_name, count = 0, target = self.resolve('void'), size = 0)
return obj.templates.ObjectTemplate(obj.Array, symbol_name = symbol_name, count = 0, target = self.resolve('void'))
elif symbol_name == 'Enumeration':
return obj.templates.ObjectTemplate(obj.Enumeration, symbol_name = symbol_name, target = self.resolve('void'), choices = {}, size = 0)
return obj.templates.ObjectTemplate(obj.Enumeration, symbol_name = symbol_name, target = self.resolve('void'), choices = {})
elif symbol_name == 'BitField':
return obj.templates.ObjectTemplate(obj.BitField, symbol_name = symbol_name, start_bit = 0, end_bit = 0, size = 0)
return obj.templates.ObjectTemplate(obj.BitField, symbol_name = symbol_name, start_bit = 0, end_bit = 0)
_native_type, native_format = self._native_dictionary[symbol_name]
native_size = struct.calcsize(native_format)
if symbol_name == 'pointer':
return obj.templates.ObjectTemplate(obj.Pointer, symbol_name = symbol_name, target = self.resolve('void'), size = native_size)
return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format, size = native_size)
return obj.templates.ObjectTemplate(obj.Pointer, symbol_name = symbol_name, target = self.resolve('void'))
return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format)
native_types = {'int' : (obj.Integer, '<i'),
'long': (obj.Integer, '<i'),
+8 -1
View File
@@ -10,9 +10,16 @@ class ValidityRoutines(object):
def type_check(self, value, valid_type):
"""Checks that value is an instance of valid_type, and returns value if it is, or throws a TypeError otherwise"""
if not isinstance(value, valid_type):
raise TypeError(self.__class__.__name__ + " expected " + valid_type.__class__.__name__ + ", not " + type(value))
print(repr(valid_type), repr(type(value).__name__))
raise TypeError(self.__class__.__name__ + " expected " + valid_type.__name__ + ", not " + type(value).__name__)
return value
def class_check(self, klass, valid_class):
"""Checks that value is an instance of valid_type, and returns value if it is, or throws a TypeError otherwise"""
if not issubclass(klass, valid_class):
raise TypeError(self.__class__.__name__ + " expected " + valid_class.__name__ + ", not " + klass.__name__)
return klass
def confirm(self, assertion, error):
"""Acts like an assertion, but will not be disabled when __debug__ is disabled"""
if not assertion: