diff --git a/volatility/framework/context.py b/volatility/framework/context.py index 7c6e4f051..ed97b3bfc 100644 --- a/volatility/framework/context.py +++ b/volatility/framework/context.py @@ -4,7 +4,7 @@ Created on 12 Feb 2013 @author: mike ''' -import volatility.framework.interfaces as interfaces +from volatility.framework import interfaces class Context(interfaces.ContextInterface): """Maintains the context within which to construct objects""" diff --git a/volatility/framework/interfaces.py b/volatility/framework/interfaces/__init__.py similarity index 51% rename from volatility/framework/interfaces.py rename to volatility/framework/interfaces/__init__.py index 3d51b990d..b5ae9383b 100644 --- a/volatility/framework/interfaces.py +++ b/volatility/framework/interfaces/__init__.py @@ -5,19 +5,7 @@ Created on 12 Apr 2013 ''' import copy - -class ObjectInterface(object): - """ 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): - self._context = context - 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) - return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset) +from volatility.framework import validity class ContextInterface(object): """Class for providing the interface for the Context object""" @@ -47,6 +35,23 @@ class ContextInterface(object): 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, **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._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) + 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 @@ -76,72 +81,3 @@ class Template(object): Returns: an object adhereing to the Object interface """ - -class SymbolTableInterface(object): - """Handles a table of symbols""" - - def __init__(self, name, native_symbols = None, *args, **kwargs): - super(SymbolTableInterface, self).__init__(*args, **kwargs) - if not isinstance(name, str) or not name: - raise TypeError("Symbol Table name must be a string") - if not isinstance(native_symbols, NativeTableInterface): - raise TypeError("Symbol Table native_symbols must be a NativeTable, not " + str(type(native_symbols))) - self.name = name - self._native_symbols = native_symbols - - ### Required Symbol List functions - - def resolve(self, symbol): - """Resolves a symbol name into an object template - - If the symbol isn't found it raises a SymbolNotFound exception - """ - - @property - def symbols(self): - """Returns an iterator of the symbol names""" - - ### Native Type Handler - - @property - def natives(self): - """Returns None or a symbol_space for handling space specific native types""" - return self._native_symbols - - ### Functions for overriding classes - - def set_symbol_class(self, symbol, clazz): - """Overrides the object class for a specific symbol - - Symbol *must* be present in self.symbols - """ - - def get_symbol_class(self, symbol): - """Returns the class associated with a symbol""" - - def del_symbol_class(self, symbol): - """Removes the associated class override for a specific symbol""" - - ### Helper functions that can be overridden - - def __len__(self): - """Returns the number of items in the symbol list""" - return len(self.symbols) - - def __getitem__(self, key): - """Resolves a symbol name into an object template - - Note, this method cannot sub-resolve throughout a whole symbol space - """ - return self.resolve(key) - - def __iter__(self): - """Returns an iterator of the available keys""" - return self.symbols - - def __contains__(self, symbol): - """Determines whether a symbol exists in the list or not""" - return symbol in self.symbols - -class NativeTableInterface(SymbolTableInterface): - """Class to distinguish NativeSymbolLists from other symbol lists""" diff --git a/volatility/framework/interfaces/layers.py b/volatility/framework/interfaces/layers.py new file mode 100644 index 000000000..baeaf5834 --- /dev/null +++ b/volatility/framework/interfaces/layers.py @@ -0,0 +1,52 @@ +''' +Created on 4 May 2013 + +@author: mike +''' + +from volatility.framework import validity, interfaces + +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) + + @property + def name(self): + """Returns the layer name""" + return self._name + + @property + def maximum_address(self): + """Returns the maximum valid address of the space""" + + @property + def minimum_address(self): + """Returns the minimum valid address of the space""" + + def is_valid(self, offset): + """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 + + 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. + """ + + def write(self, offset, data): + """Writes a chunk of data at offset. + + Any unavailable sections in the underlying bases will cause an exception to be thrown. + Note: Writes are not atomic, therefore some data can be written, even if an exception is thrown. + """ + +class TranslationLayerInterface(DataLayerInterface): + + def translate(self, offset): + """Returns a tuple of (offset, layer) indicating the translation of input domain to the output range""" + + def mapping(self, offset, length): + """Returns a list of (offset, length, layer) mappings""" diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py new file mode 100644 index 000000000..889a26872 --- /dev/null +++ b/volatility/framework/interfaces/symbols.py @@ -0,0 +1,72 @@ +''' +Created on 4 May 2013 + +@author: mike +''' + +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) + self.name = self.type_check(name or None, str) + self._native_symbols = self.type_check(native_symbols, NativeTableInterface) + + ### Required Symbol List functions + + def resolve(self, symbol): + """Resolves a symbol name into an object template + + If the symbol isn't found it raises a SymbolNotFound exception + """ + + @property + def symbols(self): + """Returns an iterator of the symbol names""" + + ### Native Type Handler + + @property + def natives(self): + """Returns None or a symbol_space for handling space specific native types""" + return self._native_symbols + + ### Functions for overriding classes + + def set_symbol_class(self, symbol, clazz): + """Overrides the object class for a specific symbol + + Symbol *must* be present in self.symbols + """ + + def get_symbol_class(self, symbol): + """Returns the class associated with a symbol""" + + def del_symbol_class(self, symbol): + """Removes the associated class override for a specific symbol""" + + ### Helper functions that can be overridden + + def __len__(self): + """Returns the number of items in the symbol list""" + return len(self.symbols) + + def __getitem__(self, key): + """Resolves a symbol name into an object template + + Note, this method cannot sub-resolve throughout a whole symbol space + """ + return self.resolve(key) + + def __iter__(self): + """Returns an iterator of the available keys""" + return self.symbols + + def __contains__(self, symbol): + """Determines whether a symbol exists in the list or not""" + return symbol in self.symbols + +class NativeTableInterface(SymbolTableInterface): + """Class to distinguish NativeSymbolLists from other symbol lists""" diff --git a/volatility/framework/layers/__init__.py b/volatility/framework/layers/__init__.py new file mode 100644 index 000000000..339746ae2 --- /dev/null +++ b/volatility/framework/layers/__init__.py @@ -0,0 +1,35 @@ +''' +Created on 4 May 2013 + +@author: mike +''' + +from volatility.framework.interfaces import layers + +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) + 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):] diff --git a/volatility/framework/obj.py b/volatility/framework/obj/__init__.py similarity index 98% rename from volatility/framework/obj.py rename to volatility/framework/obj/__init__.py index 25b358537..550a95d5e 100644 --- a/volatility/framework/obj.py +++ b/volatility/framework/obj/__init__.py @@ -6,8 +6,8 @@ Created on 17 Feb 2013 import struct import collections -import volatility.framework.interfaces as interfaces -import volatility.framework.templates as templates +from volatility.framework import interfaces +from volatility.framework.obj import templates class Void(interfaces.ObjectInterface): """Returns an object to represent void/unknown types""" diff --git a/volatility/framework/templates.py b/volatility/framework/obj/templates.py similarity index 97% rename from volatility/framework/templates.py rename to volatility/framework/obj/templates.py index b89e495f6..4c68d5d67 100644 --- a/volatility/framework/templates.py +++ b/volatility/framework/obj/templates.py @@ -4,7 +4,7 @@ Created on 1 Mar 2013 @author: mike ''' -import volatility.framework.interfaces as interfaces +from volatility.framework import interfaces class ObjectTemplate(interfaces.Template): """Factory class that produces objects that adhere to the Object interface on demand diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index f2e8d0f85..3a92f0973 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -5,9 +5,7 @@ Created on 7 Feb 2013 ''' import collections -from volatility.framework import obj, templates, interfaces -from volatility.framework.exceptions import SymbolNotFoundException -import volatility.framework.exceptions as exceptions +from volatility.framework import obj, interfaces, exceptions class SymbolSpace(collections.Mapping): """Handles an ordered collection of SymbolTables @@ -17,7 +15,7 @@ class SymbolSpace(collections.Mapping): """ def __init__(self, native_symbols): - if not isinstance(native_symbols, interfaces.NativeTableInterface): + if not isinstance(native_symbols, interfaces.symbols.NativeTableInterface): raise TypeError("SymbolSpace native_symbols must be NativeSymbolInterface") self._dict = collections.OrderedDict() self._native_symbols = native_symbols @@ -38,7 +36,7 @@ class SymbolSpace(collections.Mapping): def append(self, value): """Adds a symbol_list to the end of the space""" - if not isinstance(value, interfaces.SymbolTableInterface): + if not isinstance(value, interfaces.symbols.SymbolTableInterface): raise TypeError(value) if value.name in self._dict: del self._dict[value.name] @@ -76,7 +74,7 @@ class SymbolSpace(collections.Mapping): while template_traverse_list: traverser, template_traverse_list = template_traverse_list[0], template_traverse_list[1:] for child in traverser.children: - if isinstance(child, templates.ReferenceTemplate): + if isinstance(child, obj.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.symbol_name not in resolved: diff --git a/volatility/framework/symbols/native.py b/volatility/framework/symbols/native.py index 20ca42656..02be774a7 100644 --- a/volatility/framework/symbols/native.py +++ b/volatility/framework/symbols/native.py @@ -5,11 +5,9 @@ Created on 10 Apr 2013 ''' import copy import struct -import volatility.framework.obj as obj -import volatility.framework.templates as templates -import volatility.framework.interfaces as interfaces +from volatility.framework import obj, interfaces -class NativeTable(interfaces.NativeTableInterface): +class NativeTable(interfaces.symbols.NativeTableInterface): """Symbol List that handles Native types""" def __init__(self, name, native_dictionary): @@ -35,19 +33,19 @@ class NativeTable(interfaces.NativeTableInterface): symbol_space is used to resolve any target symbols if they don't exist in this list """ if symbol_name == 'void': - return templates.ObjectTemplate(obj.Void, symbol_name = symbol_name, size = 0) + return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name, size = 0) elif symbol_name == 'array': - return 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'), size = 0) elif symbol_name == 'Enumeration': - return 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 = {}, size = 0) elif symbol_name == 'BitField': - return 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, size = 0) _native_type, native_format = self._native_dictionary[symbol_name] native_size = struct.calcsize(native_format) if symbol_name == 'pointer': - return templates.ObjectTemplate(obj.Pointer, symbol_name = symbol_name, target = self.resolve('void'), size = native_size) - return 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'), size = native_size) + return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format, size = native_size) native_types = {'int' : (obj.Integer, ' 1: raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) - return templates.ReferenceTemplate(symbol_name = self.name + "!" + symbol_name) + return obj.templates.ReferenceTemplate(symbol_name = self.name + "!" + symbol_name) @property def symbols(self): @@ -105,4 +103,4 @@ class VTypeSymbolTable(interfaces.SymbolTableInterface): member = (relative_offset, self._vtypedict_to_template(vtypedict)) members[member_name] = member object_class = self.get_symbol_class(symbol_name) - return templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members) + return obj.templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members) diff --git a/volatility/framework/validity.py b/volatility/framework/validity.py new file mode 100644 index 000000000..e96d3331b --- /dev/null +++ b/volatility/framework/validity.py @@ -0,0 +1,21 @@ +''' +Created on 4 May 2013 + +@author: mike +''' + +class ValidityRoutines(object): + """Class to hold all validation routines, such as type checking""" + + 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)) + return value + + def confirm(self, assertion, error): + """Acts like an assertion, but will not be disabled when __debug__ is disabled""" + if not assertion: + if error is None: + error = "An unspecified Assertion was not met" + raise AssertionError(error)