diff --git a/test_rig.py b/test_rig.py index 2c37e6a30..12aaf29f9 100644 --- a/test_rig.py +++ b/test_rig.py @@ -42,10 +42,18 @@ def test_memory(): ctx = framework.Context(nativelst) ctx.symbol_space.append(ntkrnlmp) - base = layers.BufferDataLayer(ctx, 'data', buffer = b"\x04\x00\x00\x00\x01\x00\x00\x00\x02\x00") + base = layers.physical.BufferDataLayer(ctx, 'data', buffer = b"\x04\x00\x00\x00\x01\x02\x03\x04\x02\x00") ctx.memory.add_layer(base) val = ctx.object('ntkrnlmp!TEST_POINTER', 'data', 0) - print(hex(val.point1.test1), val.point1.test1.size) + print(hex(val.point1.test1), val.point1.test2) + +# TODO: +# +# Plugins - Tree/List input/output +# Architectures +# Scanning Framework +# GUI/UI +# if __name__ == '__main__': # import timeit diff --git a/volatility/framework/__init__.py b/volatility/framework/__init__.py index 73462ac12..76427cb6e 100644 --- a/volatility/framework/__init__.py +++ b/volatility/framework/__init__.py @@ -30,7 +30,7 @@ def require_version(*args): from volatility.framework import interfaces, symbols, layers -class Context(interfaces.ContextInterface): +class Context(interfaces.context.ContextInterface): """Maintains the context within which to construct objects""" def __init__(self, natives): diff --git a/volatility/framework/interfaces/__init__.py b/volatility/framework/interfaces/__init__.py index c59a0f076..789142ef3 100644 --- a/volatility/framework/interfaces/__init__.py +++ b/volatility/framework/interfaces/__init__.py @@ -4,85 +4,7 @@ Created on 12 Apr 2013 @author: mike ''' -import copy -from volatility.framework import validity - -class ContextInterface(object): - """Class for providing the interface for the Context object""" - - def __init__(self): - """Intializes the context with a symbol_space""" - - ### Symbol Space Functions - - @property - def symbol_space(self): - """Returns the symbol_space for the context""" - - ### Memory Functions - - @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 - - 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 - """ - -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, 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._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.symbol_space.resolve(new_symbol_name) - return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset) - -class Template(object): - """Class for all Factories that take offsets, and data layers and produce objects - - This is effectively a class for currying object calls - """ - def __init__(self, symbol_name = None, **kwargs): - """Stores the keyword arguments for later use""" - self._kwargs = kwargs - self._symbol_name = symbol_name - - @property - def symbol_name(self): - """Returns the name of the particular symbol""" - return self._symbol_name - - @property - def arguments(self): - """Returns the keyword arguments stored earlier""" - return copy.deepcopy(self._kwargs) - - def update_arguments(self, **newargs): - """Updates the keyword arguments""" - self._kwargs.update(newargs) - - def __call__(self, context, layer_name, offset, parent = None): - """Constructs the object - - Returns: an object adhereing to the Object interface - """ +# Import the submodules we want people to be able to use without importing them themselves +# This will also avoid namespace issues, because people can use interfaces.layers to +# avoid clashing with the layers package +from volatility.framework.interfaces import layers, symbols, context, objects diff --git a/volatility/framework/interfaces/context.py b/volatility/framework/interfaces/context.py new file mode 100644 index 000000000..d9f988e73 --- /dev/null +++ b/volatility/framework/interfaces/context.py @@ -0,0 +1,39 @@ +''' +Created on 6 May 2013 + +@author: mike +''' + +class ContextInterface(object): + """Class for providing the interface for the Context object""" + + def __init__(self): + """Intializes the context with a symbol_space""" + + ### Symbol Space Functions + + @property + def symbol_space(self): + """Returns the symbol_space for the context""" + + ### Memory Functions + + @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 + + 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 + """ + diff --git a/volatility/framework/interfaces/layers.py b/volatility/framework/interfaces/layers.py index 5cc7f08ac..16fa70bb9 100644 --- a/volatility/framework/interfaces/layers.py +++ b/volatility/framework/interfaces/layers.py @@ -4,14 +4,16 @@ Created on 4 May 2013 @author: mike ''' -from volatility.framework import validity, interfaces +from volatility.framework import validity +# We can't just import interfaces because we'd have a cycle going +from volatility.framework.interfaces import context as context_module class DataLayerInterface(validity.ValidityRoutines): """A Layer that directly holds data (and does not translate it""" def __init__(self, context, name): self._name = self.type_check(name, str) - self._context = self.type_check(context, interfaces.ContextInterface) + self._context = self.type_check(context, context_module.ContextInterface) @property def name(self): @@ -30,7 +32,7 @@ class DataLayerInterface(validity.ValidityRoutines): """Returns a boolean based on whether the offset is valid or not""" def read(self, offset, length, pad = False): - """Read takes an offset and a size and returns a bytestring of length size + """Read takes an offset and a size and returns 'bytes' (not 'str') of length size If there is a fault of any kind (such as a pagefault), an exception will be thrown unless pad is set, in which case the read errors will be replaced by null characters. diff --git a/volatility/framework/interfaces/objects.py b/volatility/framework/interfaces/objects.py new file mode 100644 index 000000000..f829a042e --- /dev/null +++ b/volatility/framework/interfaces/objects.py @@ -0,0 +1,56 @@ +''' +Created on 6 May 2013 + +@author: mike +''' + +import copy +from volatility.framework import validity +from volatility.framework.interfaces import context as context_module + +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, parent = None): + # 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._context = self.type_check(context, context_module.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.symbol_space.resolve(new_symbol_name) + return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset) + +class Template(object): + """Class for all Factories that take offsets, and data layers and produce objects + + This is effectively a class for currying object calls + """ + def __init__(self, symbol_name = None, **kwargs): + """Stores the keyword arguments for later use""" + self._kwargs = kwargs + self._symbol_name = symbol_name + + @property + def symbol_name(self): + """Returns the name of the particular symbol""" + return self._symbol_name + + @property + def arguments(self): + """Returns the keyword arguments stored earlier""" + return copy.deepcopy(self._kwargs) + + def update_arguments(self, **newargs): + """Updates the keyword arguments""" + self._kwargs.update(newargs) + + def __call__(self, context, layer_name, offset, parent = None): + """Constructs the object + + Returns: an object adhereing to the Object interface + """ diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py index 889a26872..93d779e1e 100644 --- a/volatility/framework/interfaces/symbols.py +++ b/volatility/framework/interfaces/symbols.py @@ -9,8 +9,7 @@ from volatility.framework import validity class SymbolTableInterface(validity.ValidityRoutines): """Handles a table of symbols""" - def __init__(self, name, native_symbols = None, *args, **kwargs): - super(SymbolTableInterface, self).__init__(*args, **kwargs) + def __init__(self, name, native_symbols = None): self.name = self.type_check(name or None, str) self._native_symbols = self.type_check(native_symbols, NativeTableInterface) diff --git a/volatility/framework/layers/__init__.py b/volatility/framework/layers/__init__.py index 80ea52680..7522ecea7 100644 --- a/volatility/framework/layers/__init__.py +++ b/volatility/framework/layers/__init__.py @@ -4,8 +4,8 @@ Created on 4 May 2013 @author: mike ''' -from volatility.framework import validity, exceptions -from volatility.framework.interfaces import layers +from volatility.framework import validity, interfaces, exceptions +from volatility.framework.layers import physical class Memory(validity.ValidityRoutines): """Container for multiple layers of data""" @@ -14,7 +14,10 @@ class Memory(validity.ValidityRoutines): self._layers = {} def read(self, layer, offset, length, pad = False): - """Reads from a particular layer at offset for length bytes""" + """Reads from a particular layer at offset for length bytes + + Returns 'bytes' not 'str' + """ return self[layer].read(offset, length, pad) def write(self, layer, offset, data): @@ -26,8 +29,8 @@ class Memory(validity.ValidityRoutines): This will throw an exception if the required dependencies are not met """ - self.type_check(layer, layers.DataLayerInterface) - if isinstance(layer, layers.TranslationLayerInterface): + self.type_check(layer, interfaces.layers.DataLayerInterface) + if isinstance(layer, interfaces.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] @@ -52,31 +55,4 @@ class Memory(validity.ValidityRoutines): 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, context, name, buffer): - super(BufferDataLayer, self).__init__(context, name) - self._buffer = self.type_check(buffer, bytes) - - @property - def maximum_address(self): - """Returns the largest available address in the space""" - return len(self._buffer) - 1 - - @property - def minimum_address(self): - return 0 - - def is_valid(self, offset): - return (offset >= self.minimum_address and offset <= self.maximum_address) - - def read(self, address, length, pad = False): - """Reads the data from the buffer""" - return self._buffer[address:address + length] - - def write(self, address, data): - """Writes the data from to the buffer""" - self.type_check(data, bytes) - self._buffer = self._buffer[:address] + data + self._buffer[address + len(data):] + # TODO: Is having a cycle check necessary? diff --git a/volatility/framework/layers/physical.py b/volatility/framework/layers/physical.py new file mode 100644 index 000000000..5b60fe2a4 --- /dev/null +++ b/volatility/framework/layers/physical.py @@ -0,0 +1,86 @@ +''' +Created on 6 May 2013 + +@author: mike +''' + +import os.path +from volatility.framework import interfaces, exceptions + +class BufferDataLayer(interfaces.layers.DataLayerInterface): + """A DataLayer class backed by a buffer in memory, designed for testing and swift data access""" + + def __init__(self, context, name, buffer): + super(BufferDataLayer, self).__init__(context, name) + self._buffer = self.type_check(buffer, bytes) + + @property + def maximum_address(self): + """Returns the largest available address in the space""" + return len(self._buffer) - 1 + + @property + def minimum_address(self): + """Returns the smallest available address in the space""" + return 0 + + def is_valid(self, offset): + """Returns whether the offset is valid or not""" + return (offset >= self.minimum_address and offset <= self.maximum_address) + + def read(self, address, length, pad = False): + """Reads the data from the buffer""" + return self._buffer[address:address + length] + + def write(self, address, data): + """Writes the data from to the buffer""" + self.type_check(data, bytes) + self._buffer = self._buffer[:address] + data + self._buffer[address + len(data):] + +class FileLayer(interfaces.layers.DataLayerInterface): + """a DataLayer backed by a file on the filesystem""" + + def __init__(self, context, name, filename): + super(FileLayer, self).__init__(context, name) + + self._file = open(filename, "r+b") + self._size = os.path.getsize(filename) + + @property + def maximum_address(self): + """Returns the largest available address in the space""" + # Zero based, so we return the size of the file minus 1 + return self._size - 1 + + @property + def minimum_address(self): + """Returns the smallest available address in the space""" + return 0 + + def is_valid(self, offset): + """Returns whether the offset is valid or not""" + return (offset >= self.minimum_address and offset <= self.maximum_address) + + def read(self, offset, length, pad = False): + """Reads from the file at offset for length""" + if not self.is_valid(offset): + raise exceptions.InvalidAddressException("Offset outside of the " + self.name + " file boundaries") + if not self.is_valid(offset + length): + raise exceptions.InvalidAddressException("Final 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: + if pad: + data += (b"\x00" * (length - len(data))) + else: + raise exceptions.InvalidAddressException("Could not read sufficient bytes from the " + self.name + " file") + return data + + def write(self, offset, data): + """Writes to the file""" + if not self.is_valid(offset): + raise exceptions.InvalidAddressException("Offset outside of the " + self.name + " file boundaries") + self._file.seek(offset) + self._file.write(data) diff --git a/volatility/framework/obj/__init__.py b/volatility/framework/objects/__init__.py similarity index 95% rename from volatility/framework/obj/__init__.py rename to volatility/framework/objects/__init__.py index c280299f1..4c6d9d416 100644 --- a/volatility/framework/obj/__init__.py +++ b/volatility/framework/objects/__init__.py @@ -7,9 +7,9 @@ Created on 17 Feb 2013 import struct import collections from volatility.framework import interfaces -from volatility.framework.obj import templates +from volatility.framework.objects import templates -class Void(interfaces.ObjectInterface): +class Void(interfaces.objects.ObjectInterface): """Returns an object to represent void/unknown types""" @classmethod def template_size(cls, arguments): @@ -25,10 +25,10 @@ class Void(interfaces.ObjectInterface): def template_replace_child(cls, old_child, new_child, arguments): """Dummy method that does nothing for Void objects""" -class PrimitiveObject(interfaces.ObjectInterface): +class PrimitiveObject(interfaces.objects.ObjectInterface): """PrimitiveObject is an interface for any objects that should simulate a Python primitive""" - def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = 'H'), - 'short' : (obj.Integer, 'H'), + 'short' : (objects.Integer, ' 1: raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) - return obj.templates.ReferenceTemplate(symbol_name = self.name + "!" + symbol_name) + return objects.templates.ReferenceTemplate(symbol_name = self.name + "!" + symbol_name) @property def symbols(self): @@ -103,4 +102,4 @@ class VTypeSymbolTable(symbols.SymbolTableInterface): member = (relative_offset, self._vtypedict_to_template(vtypedict)) members[member_name] = member object_class = self.get_symbol_class(symbol_name) - return obj.templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members) + return objects.templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members)