From 6105a2e4bc86aee8caf214c1469a7cb086e4593e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 11 Mar 2013 01:36:56 +0000 Subject: [PATCH] Enhance symbol listing and templates, add in gitignore and test rig. --- .gitignore | 2 + test_rig.py | 16 +++++++ volatility/framework/__init__.py | 13 +++-- volatility/framework/obj.py | 33 ++++++++----- volatility/framework/symbols.py | 80 +++++++++++++++++++++++++------ volatility/framework/templates.py | 26 ++++++---- 6 files changed, 127 insertions(+), 43 deletions(-) create mode 100644 .gitignore create mode 100644 test_rig.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8d35cb327 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.pyc diff --git a/test_rig.py b/test_rig.py new file mode 100644 index 000000000..b538d3bb2 --- /dev/null +++ b/test_rig.py @@ -0,0 +1,16 @@ +''' +Created on 10 Mar 2013 + +@author: mike +''' + +from volatility.framework import xp_sp2_x86_vtypes, symbols + +if __name__ == '__main__': + vtypes = xp_sp2_x86_vtypes.ntkrnlmp_types + + ntkrnlmp = symbols.VTypeSymbolList('ntkrnlmp', vtypes) + sspace = symbols.SymbolSpace() + sspace.append(ntkrnlmp) + sspace.resolve('ntkrnlmp!_FXSAVE_FORMAT') + diff --git a/volatility/framework/__init__.py b/volatility/framework/__init__.py index a98fc0e40..d9abefacf 100644 --- a/volatility/framework/__init__.py +++ b/volatility/framework/__init__.py @@ -15,19 +15,18 @@ _current = 3 # Number of releases of the library with any change _revision = 0 # Number of changes that don't affect the interface _age = 0 # Number of consecutive versions of the interface the current version supports -import volatility.framework.exceptions as exceptions - -@property def version(): + """Provides the so version number of the library""" return _current - _age, _age, _revision def require_version(*args): + """Checks the required version of a plugin""" if len(args): - if args[0] != version[0]: - raise exceptions.VolatilityException("Framework version " + str(version[0]) + " is incompatible with required version " + str(args[0])) + if args[0] != version()[0]: + raise Exception("Framework version " + str(version()[0]) + " is incompatible with required version " + str(args[0])) if len(args) > 1: - if args[1] > version[1]: - raise exceptions.VolatilityException("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]])) + 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]])) diff --git a/volatility/framework/obj.py b/volatility/framework/obj.py index 1800d2646..f347cf67e 100644 --- a/volatility/framework/obj.py +++ b/volatility/framework/obj.py @@ -6,6 +6,7 @@ Created on 17 Feb 2013 import struct import collections +import volatility.framework.templates as templates class ObjectInterface(object): """ A base object required to be the ancestor of every object used in volatility """ @@ -30,23 +31,25 @@ class PrimitiveObject(ObjectInterface): class Integer(PrimitiveObject, int): """Primitive Object that handles standard numeric types""" - def __new__(cls, context, layer_name, offset, symbol_name, size, struct_format, **kwargs): - struct_format = context.get_primitive_struct_type("int", symbol_name) - return cls._struct_value(struct_format, context, layer_name, offset, symbol_name) + def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs): + struct_format, struct_size = context.get_primitive_struct_type("int", symbol_name) + struct_size = struct.calcsize(struct_format) + return cls._struct_value(struct_format, context, layer_name, offset, symbol_name, size = struct_size) class Float(PrimitiveObject, float): """Primitive Object that handles double or floating point numbers""" - def __new__(cls, context, layer_name, offset, symbol_name, **kwargs): - struct_format = context.get_primitive_struct_type("float", symbol_name) - return cls._struct_value(struct_format, context, layer_name, offset, symbol_name) + def __new__(cls, context, layer_name, offset, symbol_name, size, **kwargs): + struct_format, struct_size = context.get_primitive_struct_type("float", symbol_name) + struct_size = struct.calcsize(struct_format) + return cls._struct_value(struct_format, context, layer_name, offset, symbol_name, size = struct_size) class Bytes(PrimitiveObject, bytes): """Primitive Object that handles specific series of bytes """ - def __new__(cls, context, layer_name, offset, symbol_name, **kwargs): - return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name) + def __new__(cls, context, layer_name, offset, symbol_name, size, **kwargs): + return cls._struct_value(str(size) + "s", context, layer_name, offset, symbol_name, size) class String(PrimitiveObject, str): """Primitive Object that handles string values @@ -54,21 +57,25 @@ 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, size = None, length = 1): + return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name, size) class Struct(ObjectInterface): """Object which can contain members that are other objects""" def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None): + super(Struct).__init__(self, context, layer_name, offset, symbol_name, size) # Members should be an iterable mapping of symbol names to callable Object Templates # An object template is a callable that when called with a context, offset, layer_name and symbol_name if not isinstance(members, collections.Iterable): - raise - if struct_size is None: + raise TypeError("Struct members parameter must be iterable not " + type(members)) + if not all([isinstance(members, templates.MemberTemplate)]): + raise TypeError("Struct members must be derived from MemberTemplate objects") + if size is None: # Attempt to determine the maximum size by asking for i in members: - + pass + self._members = members def __getattr__(self, attr): diff --git a/volatility/framework/symbols.py b/volatility/framework/symbols.py index 0f2cc8f3f..af8ec8b67 100644 --- a/volatility/framework/symbols.py +++ b/volatility/framework/symbols.py @@ -5,8 +5,10 @@ Created on 7 Feb 2013 ''' import volatility.framework.exceptions as exceptions +from volatility.framework import templates +from volatility.framework import obj -class SymbolSpace(dict): +class SymbolSpace(list): """Handles a collection of SymbolLists""" def resolve(self, symbol): @@ -14,11 +16,12 @@ class SymbolSpace(dict): if len(symarr) == 2: listname = symarr[0] symname = symarr[1] - if listname in [value.name for value in self]: - if symname in self[listname]: - return self[listname].resolve(symname) - else: - raise exceptions.SymbolNotFoundException("Symbol " + symname + " could not be found in the " + listname + " list") + for symlist in self: + if symlist.name == listname: + if symname in symlist: + return symlist[symname] + else: + raise exceptions.SymbolNotFoundException("Symbol " + symname + " could not be found in the " + listname + " list") else: raise exceptions.SymbolNotFoundException("Symbol list " + listname + " was not present in the symbol space") elif len(symarr) == 1: @@ -33,20 +36,69 @@ class SymbolSpace(dict): # Consider maintaining a list of symbollist names # A list of potentially conflicting symbols (for when no listname is provided) - -class SymbolList(object): +class SymbolListInterface(object): """Handles a list of symbols""" - def __init__(self, name): + def __init__(self, name, *args, **kwargs): + super(SymbolListInterface).__init__(*args, **kwargs) if not isinstance(name, str) or not name: raise exceptions.SymbolSpaceError("Symbol lists cannot be nameless") self.name = name - def resolve(self, symbolname): + def __len__(self): + """Returns the number of items in the symbol list""" + + def __getitem__(self, key): """Resolves a symbol name into an object template""" - def __contains__(self, symbol): - return symbol in self.get_symbols() + def __setitem__(self, key, value): + # TODO: Determine whether this is an appropriate design decision + """Overrides a symbol's class from a Struct to the value""" - def get_symbols(self): - """Returns a list of all available symbols""" + def __delitem__(self, key): + # TODO: Determine whether this is an appropriate design decision + """Removes the class override back to a Struct""" + + def __iter__(self): + """Returns an iterator of the symbol names""" + + def __contains__(self, symbol): + """Determines whether a symbol exists in the list or not""" + +class VTypeSymbolList(SymbolListInterface): + """Symbol List that handles""" + + def __init__(self, name, vtype_dictionary): + super(VTypeSymbolList, self).__init__(name) + self._vtypedict = vtype_dictionary + self._classdict = {} + + def __getitem__(self, key): + """Resolves a symbol name into an object template""" + size, curdict = self._vtypedict[key] + for item in curdict: + relative_offset, vtypedict = curdict[item] + self._vtypedict_to_template(vtypedict) + templates.MemberTemplate(self._classdict.get(key, obj.Struct), symbol_name = item, size = None, relative_offset = relative_offset) + members = [self._vtypedict_to_template(curdict[item]) for item in curdict] + return templates.ObjectTemplate(self._classdict.get(key, obj.Struct), symbol_name = key, size = size, members = members) + + def _vtypedict_to_template(self, dictionary): + """Converts a vtypedict into an object template""" + print(repr(dictionary)) + + def __setitem__(self, key, value): + """Overrides a symbol's class from a Struct to the value""" + self._classdict[key] = value + + def __iter__(self): + """Returns an iterator of the symbol names""" + return set(self._classdict.keys() + self._vtypedict.keys()) + + def __contains__(self, value): + """Determines whether a symbol exists in the list or not""" + return (value in self._vtypedict or value in self._classdict) + + def resolve(self, symbolname): + if symbolname not in self._vtypedict: + raise exceptions.SymbolNotFoundException diff --git a/volatility/framework/templates.py b/volatility/framework/templates.py index c92a00b9d..dcce0dd96 100644 --- a/volatility/framework/templates.py +++ b/volatility/framework/templates.py @@ -4,8 +4,6 @@ Created on 1 Mar 2013 @author: mike ''' -import volatility.framework.obj as obj - class ObjectTemplate(object): """Factory class that produces objects that adhere to the Object interface on demand @@ -15,21 +13,29 @@ class ObjectTemplate(object): * Members, etc etc. """ - def __init__(self, objclass, **kwargs): + def __init__(self, objclass, symbol_name = None, size = None, **kwargs): self._objclass = objclass - self._size = None + if not isinstance(size, int): + raise TypeError("ObjectTemplate size must be numeric, not " + str(type(size))) + self._size = size + self._symbol_name = symbol_name self._kwargs = kwargs @property def size(self): return self._size - def __call__(self, context, symbol_name, offset, layer_name): + @property + def symbol_name(self): + """Returns the name of the symbol if one was provided""" + return self._symbol_name + + def __call__(self, context, offset, layer_name, parent = None): """Constructs the object Returns: an object adhereing to the Object interface """ - return self._objclass(context, layer_name, offset, symbol_name, self.size, **self._kwargs) + return self._objclass(context, layer_name, offset, self.symbol_name, self.size, parent, **self._kwargs) class MemberTemplate(ObjectTemplate): """Factory class that produces members of Structs @@ -37,11 +43,13 @@ class MemberTemplate(ObjectTemplate): This is just like a normal ObjectTemplate, but contains the relative offset fron the parent object. """ - def __init__(self, objclass, relative_offset, **kwargs): - self._objclass = objclass + def __init__(self, objclass, symbol_name = None, size = None, relative_offset = None, *args, **kwargs): + if not isinstance(relative_offset, int): + raise TypeError("MemberTemplate relative_offset must be numeric, not " + str(type(relative_offset))) self._reloffset = relative_offset - self._kwargs = kwargs + ObjectTemplate.__init__(self, objclass, symbol_name, size, *args, **kwargs) @property def relative_offset(self): return self._reloffset +