Refactor code locations, tidy up object initializers and add in File Layer.

This commit is contained in:
Mike Auty
2013-05-06 17:45:15 +01:00
parent 86c57972b7
commit 9c2e486356
14 changed files with 256 additions and 170 deletions
+10 -2
View File
@@ -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
+1 -1
View File
@@ -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):
+4 -82
View File
@@ -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
@@ -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
"""
+5 -3
View File
@@ -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.
@@ -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
"""
+1 -2
View File
@@ -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)
+9 -33
View File
@@ -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?
+86
View File
@@ -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)
@@ -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 = '<I', **kwargs):
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = '<I'):
super(PrimitiveObject, self).__init__(context = context,
layer_name = layer_name,
offset = offset,
@@ -87,7 +87,7 @@ class String(PrimitiveObject, str):
class Pointer(Integer):
"""Pointer which points to another object"""
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = None, target = None, **kwargs):
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, struct_format = None, target = None):
if not isinstance(target, templates.ObjectTemplate):
raise TypeError("Pointer targets must be an ObjectTemplate")
super(Pointer, self).__init__(context,
@@ -145,16 +145,16 @@ class BitField(PrimitiveObject, int):
return [arguments['target']]
return []
class Enumeration(interfaces.ObjectInterface):
class Enumeration(interfaces.objects.ObjectInterface):
"""Returns an object made up of choices"""
# FIXME: Add in body for the enumeration object
@classmethod
def template_children(cls, arguments):
return []
class Array(interfaces.ObjectInterface, collections.Sequence):
class Array(interfaces.objects.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, parent = None, count = 0, target = None, **kwargs):
def __init__(self, context, layer_name, offset, symbol_name, size = None, parent = None, count = 0, target = None):
if not isinstance(target, templates.ObjectTemplate):
raise TypeError("Array target must be an ObjectTemplate")
super(Array, self).__init__(context = context,
@@ -195,10 +195,10 @@ class Array(interfaces.ObjectInterface, collections.Sequence):
"""Returns the length of the array"""
return self._count
class Struct(interfaces.ObjectInterface):
class Struct(interfaces.objects.ObjectInterface):
"""Object which can contain members that are other objects"""
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None, parent = None, **kwargs):
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None, parent = None):
super(Struct, self).__init__(context = context,
layer_name = layer_name,
offset = offset,
@@ -6,7 +6,7 @@ Created on 1 Mar 2013
from volatility.framework import interfaces, validity
class ObjectTemplate(interfaces.Template, validity.ValidityRoutines):
class ObjectTemplate(interfaces.objects.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,7 +17,7 @@ class ObjectTemplate(interfaces.Template, validity.ValidityRoutines):
"""
def __init__(self, object_class = None, symbol_name = None, **kwargs):
super(ObjectTemplate, self).__init__(symbol_name = symbol_name, **kwargs)
self.object_class = self.class_check(object_class, interfaces.ObjectInterface)
self.object_class = self.class_check(object_class, interfaces.objects.ObjectInterface)
@property
def size(self):
@@ -50,7 +50,7 @@ class ObjectTemplate(interfaces.Template, validity.ValidityRoutines):
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):
class ReferenceTemplate(interfaces.objects.Template):
"""Factory class that produces objects based on a delayed reference type
It should not return any attributes
+2 -2
View File
@@ -5,7 +5,7 @@ Created on 7 Feb 2013
'''
import collections
from volatility.framework import obj, interfaces, exceptions
from volatility.framework import objects, interfaces, exceptions
class SymbolSpace(collections.Mapping):
"""Handles an ordered collection of SymbolTables
@@ -76,7 +76,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, obj.templates.ReferenceTemplate):
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.symbol_name not in resolved:
+25 -26
View File
@@ -4,8 +4,7 @@ Created on 10 Apr 2013
@author: mike
'''
import copy
import struct
from volatility.framework import obj, interfaces
from volatility.framework import objects, interfaces
class NativeTable(interfaces.symbols.NativeTableInterface):
"""Symbol List that handles Native types"""
@@ -19,7 +18,7 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
self._overrides[native_type] = native_class
def get_symbol_class(self, symbol):
ntype, fmt = native_types.get(symbol, (obj.Integer, ''))
ntype, fmt = native_types.get(symbol, (objects.Integer, ''))
return ntype
@property
@@ -27,45 +26,45 @@ class NativeTable(interfaces.symbols.NativeTableInterface):
"""Returns an iterator of the symbol names"""
return set(self._native_dictionary.keys()).union(set(['Enumeration', 'array', 'BitField', 'void', 'pointer']))
def resolve(self, symbol_name, **kwargs):
def resolve(self, symbol_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
"""
additional = {}
if symbol_name == 'void':
return obj.templates.ObjectTemplate(obj.Void, symbol_name = symbol_name)
return objects.templates.ObjectTemplate(objects.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'))
return objects.templates.ObjectTemplate(objects.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 = {})
return objects.templates.ObjectTemplate(objects.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)
return objects.templates.ObjectTemplate(objects.BitField, symbol_name = symbol_name, start_bit = 0, end_bit = 0)
_native_type, native_format = self._native_dictionary[symbol_name]
if symbol_name == 'pointer':
additional = {'target': self.resolve('void')}
return obj.templates.ObjectTemplate(self.get_symbol_class(symbol_name), #pylint: disable-msg=W0142
return objects.templates.ObjectTemplate(self.get_symbol_class(symbol_name), #pylint: disable-msg=W0142
symbol_name = symbol_name,
struct_format = native_format,
**additional)
native_types = {'int' : (obj.Integer, '<i'),
'long': (obj.Integer, '<i'),
'unsigned long' : (obj.Integer, '<I'),
'unsigned int' : (obj.Integer, '<I'),
'pointer' : (obj.Pointer, '<I'),
'char' : (obj.Integer, '<b'),
'byte' : (obj.Bytes, '<c'),
'unsigned char' : (obj.Integer, '<B'),
'unsigned short int' : (obj.Integer, '<H'),
'unsigned short' : (obj.Integer, '<H'),
'unsigned be short' : (obj.Integer, '>H'),
'short' : (obj.Integer, '<h'),
'long long' : (obj.Integer, '<q'),
'unsigned long long' : (obj.Integer, '<Q'),
'float': (obj.Float, "<d"),
'double': (obj.Float, "<d")}
native_types = {'int' : (objects.Integer, '<i'),
'long': (objects.Integer, '<i'),
'unsigned long' : (objects.Integer, '<I'),
'unsigned int' : (objects.Integer, '<I'),
'pointer' : (objects.Pointer, '<I'),
'char' : (objects.Integer, '<b'),
'byte' : (objects.Bytes, '<c'),
'unsigned char' : (objects.Integer, '<B'),
'unsigned short int' : (objects.Integer, '<H'),
'unsigned short' : (objects.Integer, '<H'),
'unsigned be short' : (objects.Integer, '>H'),
'short' : (objects.Integer, '<h'),
'long long' : (objects.Integer, '<q'),
'unsigned long long' : (objects.Integer, '<Q'),
'float': (objects.Float, "<d"),
'double': (objects.Float, "<d")}
x86NativeTable = NativeTable("native", native_types)
native_types['pointer'] = (obj.Pointer, '<Q')
native_types['pointer'] = (objects.Pointer, '<Q')
x64NativeTable = NativeTable("native", native_types)
+5 -6
View File
@@ -5,8 +5,7 @@ Created on 10 Apr 2013
'''
import copy
from volatility.framework import exceptions, obj
from volatility.framework.interfaces import symbols
from volatility.framework import exceptions, objects, interfaces
### TODO
#
@@ -34,7 +33,7 @@ from volatility.framework.interfaces import symbols
# Need to figure out how to tell the difference for vtypes between
# vtype list and a struct dictionary
class VTypeSymbolTable(symbols.SymbolTableInterface):
class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface):
"""Symbol Table that handles vtype datastructures"""
def __init__(self, name, vtype_dictionary, native_symbols = None):
@@ -43,7 +42,7 @@ class VTypeSymbolTable(symbols.SymbolTableInterface):
self._overrides = {}
def get_symbol_class(self, symbol):
return self._overrides.get(symbol, obj.Struct)
return self._overrides.get(symbol, objects.Struct)
def set_symbol_class(self, symbol, clazz):
if symbol not in self.symbols:
@@ -85,7 +84,7 @@ class VTypeSymbolTable(symbols.SymbolTableInterface):
if len(dictionary) > 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)