diff --git a/test_rig.py b/test_rig.py index b15f2c102..e8608c529 100644 --- a/test_rig.py +++ b/test_rig.py @@ -4,14 +4,22 @@ Created on 10 Mar 2013 @author: mike ''' -from volatility.framework import xp_sp2_x86_vtypes, symbols +from volatility.framework import xp_sp2_x86_vtypes, context +from volatility.framework.symbols import vtypes, native if __name__ == '__main__': - vtypes = xp_sp2_x86_vtypes.ntkrnlmp_types + virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types + + ntkrnlmp = vtypes.VTypeSymbolList('ntkrnlmp', virtual_types) + native = native.x86NativeSymbolList + + ctx = context.Context() + ctx.add_symbol_list(native) + ctx.add_symbol_list(ntkrnlmp) + print("Symbols,", native.symbols) - ntkrnlmp = symbols.VTypeSymbolList('ntkrnlmp', vtypes) - sspace = symbols.SymbolSpace() - sspace.append(ntkrnlmp) for i in ntkrnlmp.symbols: - print(sspace.resolve('ntkrnlmp!' + i)) + symbol = ctx.resolve('ntkrnlmp!' + i) + print(symbol) + # objthing = symbol(context, layer_name = '', offset = 0) diff --git a/volatility/framework/context.py b/volatility/framework/context.py index afeed17b1..02f65545f 100644 --- a/volatility/framework/context.py +++ b/volatility/framework/context.py @@ -5,7 +5,6 @@ Created on 12 Feb 2013 ''' import volatility.framework.symbols as symbols -import volatility.framework.exceptions as exceptions class Context(object): """Maintains the context within which to construct objects""" @@ -18,14 +17,15 @@ class Context(object): def add_symbol_list(self, symbol_list): """Adds a symbol list to the symbol space used by the context""" - if symbol_list.name in self._symbol_space: - raise exceptions.SymbolSpaceError("Symbol list " + symbol_list.name + " already exists in this space.") - self._symbol_space[symbol_list.name] = symbol_list + self._symbol_space.append(symbol_list) def remove_symbol_list(self, symbol_list_name): - if not symbol_list_name in self._symbol_space: - raise exceptions.SymbolSpaceError("No symbol list named " + symbol_list_name + " present in the symbol space.") - del self._symbol_space[symbol_list_name] + """Removes a symbol list from the symbol space used by the context""" + self._symbol_space.remove(symbol_list_name) + + def resolve(self, symbol_name): + """Resolves a symbol name from the various symbol lists in the symbol space""" + return self._symbol_space.resolve(symbol_name) ### Address Space Functions @@ -35,7 +35,7 @@ class Context(object): ### Object Factory Functions - def object(self, symbol, offset, layername = None): + 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, @@ -44,5 +44,5 @@ class Context(object): Returns a fully constructed object """ object_template = self._symbol_space.resolve(symbol) - return object_template(v) + return object_template(self, layer_name = layer_name, offset = offset) diff --git a/volatility/framework/interfaces.py b/volatility/framework/interfaces.py new file mode 100644 index 000000000..3975011bc --- /dev/null +++ b/volatility/framework/interfaces.py @@ -0,0 +1,18 @@ +''' +Created on 12 Apr 2013 + +@author: mike +''' + +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() + return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset) diff --git a/volatility/framework/obj.py b/volatility/framework/obj.py index 96a329903..554b83623 100644 --- a/volatility/framework/obj.py +++ b/volatility/framework/obj.py @@ -6,18 +6,10 @@ Created on 17 Feb 2013 import struct import collections +import volatility.framework.interfaces as interfaces import volatility.framework.templates as templates -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 - -class PrimitiveObject(ObjectInterface): +class PrimitiveObject(interfaces.ObjectInterface): """PrimitiveObject is an interface for any objects that should simulate a Python primitive""" @classmethod @@ -32,7 +24,7 @@ class Integer(PrimitiveObject, int): """Primitive Object that handles standard numeric types""" 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_format = 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) @@ -40,7 +32,7 @@ class Float(PrimitiveObject, float): """Primitive Object that handles double or floating point numbers""" def __new__(cls, context, layer_name, offset, symbol_name, size, **kwargs): - struct_format, struct_size = context.get_primitive_struct_type("float", symbol_name) + struct_format = 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) @@ -60,7 +52,41 @@ class String(PrimitiveObject, str): 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): +class Pointer(Integer): + """Pointer which points to another object""" + def __init__(self, context, layer_name, offset, symbol_name, size = None, target = None): + if not isinstance(target, interfaces.ObjectInterface): + raise TypeError("Pointer targets must be an ObjectInterface") + super(Pointer, self).__init__(context, layer_name, offset, symbol_name, size) + self._target = target + + def derefenence(self): + """Dereferences the pointer""" + # Cache the target + if isinstance(self._target, templates.ObjectTemplate): + self._target = self.target(context = self._context, layer_name = self._layer_name, offset = self, self = self._target.size, parent = self) + return self._target + + def __getattr__(self, attr): + """Convenience function to access unknown attributes by getting them from the target object""" + return getattr(self.dereference(), attr) + +class BitField(Integer): + """""" + def __new__(self, context, layer_name, offset, symbol_name, size = None, start_bit = 0, end_bit = 0): + # TODO: Determine endianness of the bitfield + struct_size = (end_bit + 7) // 8 + if struct_size == 1: + struct_format = "c" + elif struct_size <= 2: + struct_format = "H" + elif struct_size <= 4: + struct_format = "I" + else: + struct_format = "Q" + return super(BitField, self).__new__(context, layer_name, offset, symbol_name, size = None, struct_format = struct_format) + +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): @@ -79,6 +105,12 @@ class Struct(ObjectInterface): self._members = members def __getattr__(self, attr): + """Method for accessing members of the structure""" if attr in self._members: - return self._members[attr] + member = self._members[attr] + # Cache the constructed object + if isinstance(member, templates.ObjectTemplate): + member = member(context = self.context, layer_name = self.layer_name, offset = self.offset + member.relative_offset, parent = self) + self._members[attr] = member + return member raise AttributeError("'" + self._symbol_name + "' Struct has no attribute '" + attr + "'") diff --git a/volatility/framework/symbols.py b/volatility/framework/symbols.py deleted file mode 100644 index 53fbfbe21..000000000 --- a/volatility/framework/symbols.py +++ /dev/null @@ -1,137 +0,0 @@ -''' -Created on 7 Feb 2013 - -@author: mike -''' - -import volatility.framework.exceptions as exceptions -from volatility.framework import templates -from volatility.framework import obj - -class SymbolSpace(list): - """Handles a collection of SymbolLists""" - - def resolve(self, symbol): - symarr = symbol.split("!") - if len(symarr) == 2: - listname = symarr[0] - symname = symarr[1] - for symlist in self: - if symlist.name == listname: - if symname in symlist: - return symlist.resolve(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: - for slist in self: - if symbol in slist: - return slist.resolve(symbol) - else: - raise exceptions.SymbolNotFoundException("Symbol " + symbol + " could not be found in any symbol list") - else: - raise exceptions.SymbolNotFoundException("Malformed symbol name") - - # Consider maintaining a list of symbollist names - # A list of potentially conflicting symbols (for when no listname is provided) - -class SymbolListInterface(object): - """Handles a list of symbols""" - - def __init__(self, name, *args, **kwargs): - super(SymbolListInterface, self).__init__(*args, **kwargs) - if not isinstance(name, str) or not name: - raise exceptions.SymbolSpaceError("Symbol lists cannot be nameless") - self.name = name - - ### Required Symbol List functions - - def resolve(self, symbol): - """Resolves a symbol name into an object template""" - - @property - def symbols(self): - """Returns an iterator of the symbol names""" - - def set_object_class(self, symbol, clazz): - """Overrides the object class for a specific symbol""" - - def get_object_class(self, symbol): - """Returns the class associated with a 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""" - 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 VTypeSymbolList(SymbolListInterface): - """Symbol List that handles""" - - def __init__(self, name, vtype_dictionary): - super(VTypeSymbolList, self).__init__(name) - self._vtypedict = vtype_dictionary - self._classdict = {} - - def _vtypedict_to_template(self, dictionary): - """Converts a vtypedict into an object template""" - if not dictionary: - raise exceptions.SymbolSpaceError("Invalid vtype dictionary: " + repr(dictionary)) - # print(repr(dictionary)) - - symbol_name = dictionary[0] - result = {"objclass": self.get_object_class(symbol_name), "symbol_name": symbol_name, "size" : 0} - - # Handle specific vtypes - if symbol_name == 'array': - result["length"] = dictionary[1] - result["target"] = templates.ObjectTemplate(**self._vtypedict_to_template(dictionary[2])) - result["size"] = dictionary[1] * result["target"].size - elif symbol_name == 'pointer': - result["target"] = templates.ObjectTemplate(**self._vtypedict_to_template(dictionary[1])) - elif symbol_name == 'Enumeration': - result.update(dictionary[1]) - result["target"] = templates.ObjectTemplate(**self._vtypedict_to_template([result["target"]])) - elif symbol_name == 'BitField': - result["fields"] = dictionary[1] - elif len(dictionary) > 1: - raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) - - # print(result) - return result - - def set_object_class(self, key, value): - """Overrides a symbol's class from a Struct to the value""" - self._classdict[key] = value - - def get_object_class(self, key): - return self._classdict.get(key, obj.Struct) - - @property - def symbols(self): - """Returns an iterator of the symbol names""" - return set(self._classdict.keys()).union(set(self._vtypedict.keys())) - - def resolve(self, symbolname): - if symbolname not in self._vtypedict: - raise exceptions.SymbolNotFoundException - size, curdict = self._vtypedict[symbolname] - members = [] - for item in curdict: - relative_offset, vtypedict = curdict[item] - member = templates.MemberTemplate(relative_offset = relative_offset, **self._vtypedict_to_template(vtypedict)) - members.append(member) - return templates.ObjectTemplate(self.get_object_class(symbolname), symbol_name = symbolname, size = size, members = members) diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py new file mode 100644 index 000000000..559c9f208 --- /dev/null +++ b/volatility/framework/symbols/__init__.py @@ -0,0 +1,141 @@ +''' +Created on 7 Feb 2013 + +@author: mike +''' + +import collections +import volatility.framework.exceptions as exceptions +from volatility.framework import obj, templates, interfaces +from volatility.framework.exceptions import SymbolNotFoundException + +class SymbolSpace(collections.Mapping): + """Handles an ordered collection of SymbolLists + + This collection is ordered so that resolution of symbols can + proceed down through the ranks if a namespace isn't specified. + """ + + def __init__(self): + self._dict = collections.OrderedDict() + + def __len__(self): + return len(self._dict) + + def __getitem__(self, i): + return self._dict[i] + + def __iter__(self): + return self._dict.__iter__(self) + + def append(self, value): + """Adds a symbol_list to the end of the space""" + if not isinstance(value, SymbolListInterface): + raise TypeError(value) + if value.name in self._dict: + del self._dict[value.name] + self._dict[value.name] = value + + def remove(self, key): + """Removes a named symbol_list from the space""" + del self._dict[key] + + def resolve(self, symbol, start_from = None): + """Takes a symbol name and resolves it""" + symarr = symbol.split("!") + if len(symarr) == 2: + listname = symarr[0] + symname = symarr[1] + for symlistname in reversed(self._dict): + if symlistname == listname: + if symname in self[symlistname]: + return self[symlistname].resolve(symname, self) + 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: + # Establish skipping all elements before the symbol list to start from + skip = (start_from is not None) + for slist in reversed(self._dict): + if skip: + skip = (slist.name != start_from) + else: + if symbol in self[slist]: + return self[slist].resolve(symbol, self) + else: + if skip: + raise exceptions.SymbolSpaceError("Symbol search for \"" + symbol + "\" failed because symbol list \"" + start_from.name + "\" is not in the search space") + raise exceptions.SymbolNotFoundException("Symbol \"" + symbol + "\" could not be found in any symbol list") + else: + raise exceptions.SymbolNotFoundException("Malformed symbol name") + raise RuntimeError("Symbol Space Resolution hit an unexpected branch!") + +class SymbolListInterface(object): + """Handles a list of symbols""" + + def __init__(self, name, symbol_space = None, *args, **kwargs): + super(SymbolListInterface, self).__init__(*args, **kwargs) + if not isinstance(name, str) or not name: + raise exceptions.SymbolSpaceError("Symbol lists cannot be nameless") + self.name = name + self._overrides = {} + + ### Required Symbol List functions + + def resolve(self, symbol, symbol_space = None): + """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 + """ + + @property + def symbols(self): + """Returns an iterator of the symbol names""" + + ### Functions for overriding classes + + def set_symbol_class(self, symbol, clazz): + """Overrides the object class for a specific symbol + + We can only override existing classes, otherwise determining the complete symbol list + will become complicated for each subclass. + """ + if not symbol in self.symbols: + raise exceptions.SymbolNotFoundException("Cannot override \"" + symbol + "\" in \"" + self.name + "\", symbol not already present") + if not issubclass(clazz, interfaces.ObjectInterface): + raise exceptions.SymbolSpaceError("Attempting to add an object that does not inherit from ObjectInterface as a symbol class override") + self._overrides[symbol] = clazz + + def has_symbol_class(self, symbol): + """Returns whether the symbol's class has bee overridden or not""" + return symbol in self._overrides + + def get_symbol_class(self, symbol): + """Returns the class associated with a symbol or None if there is no associated symbol + """ + return self._overrides[symbol] + + def del_symbol_class(self, symbol): + """Removes the associated class override for a specific symbol""" + del self._overrides[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""" + 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 + + diff --git a/volatility/framework/symbols/native.py b/volatility/framework/symbols/native.py new file mode 100644 index 000000000..fc5726b01 --- /dev/null +++ b/volatility/framework/symbols/native.py @@ -0,0 +1,58 @@ +''' +Created on 10 Apr 2013 + +@author: mike +''' +import copy +import struct +import volatility.framework.obj as obj +import volatility.framework.symbols as symbols +import volatility.framework.templates as templates +import volatility.framework.exceptions as exceptions + +class NativeSymbolList(symbols.SymbolListInterface): + """Symbol List that handles Native types""" + + def __init__(self, name, native_dictionary): + super(NativeSymbolList, self).__init__(name) + self._native_dictionary = native_dictionary + for item in self._native_dictionary: + self._overrides[item] = obj.Integer + + @property + def symbols(self): + return self._native_dictionary.keys() + + def resolve(self, symbol, symbol_space = None): + """Resolve the native symbols by looking up their struct types in a dictionary""" + if symbol in self._native_dictionary: + fmt = self._native_dictionary[symbol] + return templates.ObjectTemplate(self.get_symbol_class(symbol), symbol_name = symbol, size = struct.calcsize(fmt), format_string = fmt) + if isinstance(symbol_space, symbols.SymbolSpace): + return symbol_space.resolve(symbol, self.name) + raise exceptions.SymbolNotFoundException() + +x86_native_types = {'int' : 'H', + 'short' : ' 1: + raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) + elif isinstance(symbol_space, symbols.SymbolSpace): + # Resolve the object class for this + return symbol_space.resolve(symbol_name) + else: + raise exceptions.SymbolNotFoundException("Unable to resolve \"" + symbol_name + "\" in \"" + self.name + "\", no symbol space to rescurse through") + elif self.has_symbol_class(symbol_name): + result["object_class"] = self.get_symbol_class(symbol_name) + + print(result) + return templates.ObjectTemplate(**result) #pylint: disable-msg=W0142 + + @property + def symbols(self): + """Returns an iterator of the symbol names""" + return self._vtypedict.keys() + + def resolve(self, symbol_name, symbol_space = None): + """Resolves an individual symbol""" + if symbol_name not in self._vtypedict: + raise exceptions.SymbolNotFoundException + size, curdict = self._vtypedict[symbol_name] + members = {} + for member_name in curdict: + relative_offset, vtypedict = curdict[member_name] + member = templates.member_from_object_template(relative_offset = relative_offset, object_template = self._vtypedict_to_template(vtypedict, symbol_space)) + members[member_name] = member + object_class = self._default_object_class + if self.has_symbol_class(symbol_name): + object_class = self.get_symbol_class(symbol_name) + return templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members) diff --git a/volatility/framework/templates.py b/volatility/framework/templates.py index dcce0dd96..794470959 100644 --- a/volatility/framework/templates.py +++ b/volatility/framework/templates.py @@ -4,6 +4,9 @@ Created on 1 Mar 2013 @author: mike ''' +import copy +import volatility.framework.interfaces as interfaces + class ObjectTemplate(object): """Factory class that produces objects that adhere to the Object interface on demand @@ -13,13 +16,19 @@ class ObjectTemplate(object): * Members, etc etc. """ - def __init__(self, objclass, symbol_name = None, size = None, **kwargs): - self._objclass = objclass + def __init__(self, object_class = None, symbol_name = None, size = None, **kwargs): + if not issubclass(object_class, interfaces.ObjectInterface): + raise TypeError("ObjectTemplate object_class must be a class, not " + str(type(object_class))) 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 + self._object_class = object_class + + @property + def object_class(self): + return self._object_class @property def size(self): @@ -30,12 +39,26 @@ class ObjectTemplate(object): """Returns the name of the symbol if one was provided""" return self._symbol_name - def __call__(self, context, offset, layer_name, parent = None): + @property + def kwargs(self): + return copy.deepcopy(self._kwargs) + + def __call__(self, context, layer_name, offset, parent = None): """Constructs the object Returns: an object adhereing to the Object interface """ - return self._objclass(context, layer_name, offset, self.symbol_name, self.size, parent, **self._kwargs) + return self._object_class(context = context, layer_name = layer_name, offset = offset, symbol_name = self.symbol_name, size = self.size, parent = parent, **self._kwargs) + +def member_from_object_template(relative_offset, object_template): + """Returns a MemberTemplate based upon an existing ObjectTemplate""" + if not isinstance(object_template, ObjectTemplate): + raise TypeError("object_template must be an ObjectTemplate, not " + str(type(object_template))) + return MemberTemplate(relative_offset = relative_offset, + object_class = object_template.object_class, + symbol_name = object_template.symbol_name, + size = object_template.size, + **object_template.kwargs) class MemberTemplate(ObjectTemplate): """Factory class that produces members of Structs @@ -43,13 +66,12 @@ class MemberTemplate(ObjectTemplate): This is just like a normal ObjectTemplate, but contains the relative offset fron the parent object. """ - def __init__(self, objclass, symbol_name = None, size = None, relative_offset = None, *args, **kwargs): + def __init__(self, relative_offset, **kwargs): + super(MemberTemplate, self).__init__(**kwargs) if not isinstance(relative_offset, int): raise TypeError("MemberTemplate relative_offset must be numeric, not " + str(type(relative_offset))) - self._reloffset = relative_offset - ObjectTemplate.__init__(self, objclass, symbol_name, size, *args, **kwargs) + self._relative_offset = relative_offset @property def relative_offset(self): - return self._reloffset - + return self._relative_offset diff --git a/volatility/framework/xp_sp2_x86_vtypes.py b/volatility/framework/xp_sp2_x86_vtypes.py index d5a4cb0a4..cc5bf0f69 100644 --- a/volatility/framework/xp_sp2_x86_vtypes.py +++ b/volatility/framework/xp_sp2_x86_vtypes.py @@ -4261,7 +4261,6 @@ ntkrnlmp_types = { 'DeviceObject' : [ 0x4, ['pointer', ['_DEVICE_OBJECT']]], } ], '__unnamed_17fb' : [ 0x4, { - 'Srb' : [ 0x0, ['pointer', ['_SCSI_REQUEST_BLOCK']]], } ], '__unnamed_17ff' : [ 0x10, { 'Length' : [ 0x0, ['unsigned long']], @@ -5014,7 +5013,6 @@ ntkrnlmp_types = { 'OpenRoutine' : [ 0x2c, ['pointer', ['void']]], 'WriteRoutine' : [ 0x30, ['pointer', ['void']]], 'FinishRoutine' : [ 0x34, ['pointer', ['void']]], - 'AdapterObject' : [ 0x38, ['pointer', ['_ADAPTER_OBJECT']]], 'MappedRegisterBase' : [ 0x3c, ['pointer', ['void']]], 'PortConfiguration' : [ 0x40, ['pointer', ['void']]], 'CrashDump' : [ 0x44, ['unsigned char']],