diff --git a/test_rig.py b/test_rig.py index 4cdad67e2..9fab8e3f9 100644 --- a/test_rig.py +++ b/test_rig.py @@ -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() diff --git a/volatility/framework/__init__.py b/volatility/framework/__init__.py index d9abefacf..73462ac12 100644 --- a/volatility/framework/__init__.py +++ b/volatility/framework/__init__.py @@ -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) + diff --git a/volatility/framework/context.py b/volatility/framework/context.py deleted file mode 100644 index ed97b3bfc..000000000 --- a/volatility/framework/context.py +++ /dev/null @@ -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) - diff --git a/volatility/framework/exceptions.py b/volatility/framework/exceptions.py index 2100f9d5d..7c0f233fa 100644 --- a/volatility/framework/exceptions.py +++ b/volatility/framework/exceptions.py @@ -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""" diff --git a/volatility/framework/interfaces/__init__.py b/volatility/framework/interfaces/__init__.py index b5ae9383b..c59a0f076 100644 --- a/volatility/framework/interfaces/__init__.py +++ b/volatility/framework/interfaces/__init__.py @@ -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): diff --git a/volatility/framework/interfaces/layers.py b/volatility/framework/interfaces/layers.py index baeaf5834..5cc7f08ac 100644 --- a/volatility/framework/interfaces/layers.py +++ b/volatility/framework/interfaces/layers.py @@ -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""" diff --git a/volatility/framework/layers/__init__.py b/volatility/framework/layers/__init__.py index 339746ae2..80ea52680 100644 --- a/volatility/framework/layers/__init__.py +++ b/volatility/framework/layers/__init__.py @@ -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 diff --git a/volatility/framework/obj/__init__.py b/volatility/framework/obj/__init__.py index 550a95d5e..eb8fba2a4 100644 --- a/volatility/framework/obj/__init__.py +++ b/volatility/framework/obj/__init__.py @@ -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 = '> 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 = {} diff --git a/volatility/framework/obj/templates.py b/volatility/framework/obj/templates.py index 4c68d5d67..7f7cdf5ae 100644 --- a/volatility/framework/obj/templates.py +++ b/volatility/framework/obj/templates.py @@ -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 diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index 3a92f0973..200b5f34a 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -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): diff --git a/volatility/framework/symbols/native.py b/volatility/framework/symbols/native.py index 02be774a7..25d099862 100644 --- a/volatility/framework/symbols/native.py +++ b/volatility/framework/symbols/native.py @@ -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, '