From 7a62e74996d356a55865688c3ff42cb9618ddfaf Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 10 Apr 2016 11:54:32 +0100 Subject: [PATCH 01/14] Refactor the translation method out of the TranslationLayer, since it's not always applicable/useful. Contemplate a rename of the TranslationLayer now it doesn't actually translate. 5;) --- test_rig.py | 2 +- volatility/framework/interfaces/layers.py | 4 ---- volatility/framework/layers/intel.py | 10 ++++------ 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/test_rig.py b/test_rig.py index 3a22b75e3..c09aef923 100644 --- a/test_rig.py +++ b/test_rig.py @@ -128,7 +128,7 @@ def test_translation(): a, b = intel._translate(val) print(hex(val), hex(a), hex(b)) # print(bin(0x39000), bin(0xffab8020)) - # print(hex(intel.translate(0xffab8020))) + # print(hex(intel.mapping(0xffab8020, 0))) def test_plugin(): diff --git a/volatility/framework/interfaces/layers.py b/volatility/framework/interfaces/layers.py index 4217fbac5..cfe88423c 100644 --- a/volatility/framework/interfaces/layers.py +++ b/volatility/framework/interfaces/layers.py @@ -93,10 +93,6 @@ class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta): # Unfortunately class attributes can't easily be inheritted from parent classes provides = {"type": "interface"} - @abstractmethod - def translate(self, offset): - """Returns a tuple of (offset, layer) indicating the translation of input domain to the output range""" - @abstractmethod def mapping(self, offset, length): """Returns a sorted list of (offset, mapped_offset, length, layer) mappings diff --git a/volatility/framework/layers/intel.py b/volatility/framework/layers/intel.py index d7166fbf2..d8e046323 100644 --- a/volatility/framework/layers/intel.py +++ b/volatility/framework/layers/intel.py @@ -91,7 +91,7 @@ class Intel(interfaces.layers.TranslationLayerInterface): entry, = struct.unpack(self._entry_format, self._context.memory.read(self._base_layer, table_offset, struct.calcsize(self._entry_format))) - # Now we're do + # Now we're done if not self._page_is_valid(entry): raise exceptions.InvalidAddressException("Page Fault at entry " + hex(entry) + " in page entry") page = self._mask(entry, self._maxphyaddr - 1, position + 1) | self._mask(offset, position, 0) @@ -105,16 +105,14 @@ class Intel(interfaces.layers.TranslationLayerInterface): except exceptions.InvalidAddressException: return False - def translate(self, offset): - """Translates a specific offset based on the paging tables""" - result, _ = self._translate(offset) - return result - def mapping(self, offset, length): """Returns a sorted list of (offset, mapped_offset, length, layer) mappings This allows translation layers to provide maps of contiguous regions in one layer """ + if length == 0: + mapped_offset, _ = self._translate(offset) + return [(offset, mapped_offset, length, self._base_layer)] result = [] while length > 0: chunk_offset, page_size = self._translate(offset) From 2d23c93def642e3094db21c49af71334d3f1694d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 11:41:54 +0100 Subject: [PATCH 02/14] Completely refactor constants and structures to symbols and types. --- test_rig.py | 14 ++-- volatility/framework/contexts/__init__.py | 2 +- volatility/framework/interfaces/objects.py | 16 ++--- volatility/framework/interfaces/symbols.py | 71 ++++++++++--------- volatility/framework/objects/__init__.py | 50 +++++++------ volatility/framework/objects/templates.py | 8 +-- volatility/framework/symbols/__init__.py | 56 +++++++-------- volatility/framework/symbols/native.py | 40 +++++------ volatility/framework/symbols/vtypes.py | 44 ++++++------ .../framework/symbols/windows/extensions.py | 10 +-- .../framework/symbols/windows/xp_sp2.py | 4 +- volatility/plugins/windows/pslist.py | 2 +- 12 files changed, 162 insertions(+), 155 deletions(-) diff --git a/test_rig.py b/test_rig.py index c09aef923..9153e7374 100644 --- a/test_rig.py +++ b/test_rig.py @@ -26,8 +26,8 @@ def utils_load_as(): virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, ctx.symbol_space.natives) - ntkrnlmp.set_structure_class('_ETHREAD', volatility.framework.symbols.windows.extensions._ETHREAD) - ntkrnlmp.set_structure_class('_LIST_ENTRY', volatility.framework.symbols.windows.extensions._LIST_ENTRY) + ntkrnlmp.set_type_class('_ETHREAD', volatility.framework.symbols.windows.extensions._ETHREAD) + ntkrnlmp.set_type_class('_LIST_ENTRY', volatility.framework.symbols.windows.extensions._LIST_ENTRY) ctx.symbol_space.append(ntkrnlmp) # contexts.windows.WindowsContextModifier(ctx.config).modify_context(ctx) @@ -36,7 +36,7 @@ def utils_load_as(): def test_symbols(): ctx = utils_load_as() - print("Symbols,", ctx.symbol_space.natives.structures) + print("Symbols,", ctx.symbol_space.natives.types) virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types virtual_types['TEST_POINTER'] = [0x4, {'point1': [0x0, ['pointer', ['TEST_SYMBOL']]]}] @@ -45,11 +45,11 @@ def test_symbols(): ctx.symbol_space.append(ntkrnlmp) - for i in list(ctx.symbol_space['ntkrnlmp'].structures): - symbol = ctx.symbol_space.get_structure('ntkrnlmp!' + i) - print(symbol.vol.structure_name, symbol, symbol.vol.size) + for i in list(ctx.symbol_space['ntkrnlmp'].types): + symbol = ctx.symbol_space.get_type('ntkrnlmp!' + i) + print(symbol.vol.type_name, symbol, symbol.vol.size) _ = symbol(ctx, objects.ObjectInformation(layer_name = '', offset = 0)) - symbol = ctx.symbol_space.get_structure('ntkrnlmp!_EPROCESS') + symbol = ctx.symbol_space.get_type('ntkrnlmp!_EPROCESS') return symbol diff --git a/volatility/framework/contexts/__init__.py b/volatility/framework/contexts/__init__.py index 7c54e0174..042a1f1b7 100644 --- a/volatility/framework/contexts/__init__.py +++ b/volatility/framework/contexts/__init__.py @@ -73,7 +73,7 @@ class Context(interfaces.context.ContextInterface): :return: A fully constructed object :rtype: :py:class:`volatility.framework.interfaces.objects.ObjectInterface` """ - object_template = self._symbol_space.get_structure(symbol) + object_template = self._symbol_space.get_type(symbol) object_template.update_vol(**arguments) return object_template(context = self, object_info = interfaces.objects.ObjectInformation(layer_name = layer_name, diff --git a/volatility/framework/interfaces/objects.py b/volatility/framework/interfaces/objects.py index b4e9a6831..a7d3763b5 100644 --- a/volatility/framework/interfaces/objects.py +++ b/volatility/framework/interfaces/objects.py @@ -53,7 +53,7 @@ class ObjectInformation(ReadOnlyMapping): class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta): """ A base object required to be the ancestor of every object used in volatility """ - def __init__(self, context, structure_name, object_info, **kwargs): + def __init__(self, context, type_name, object_info, **kwargs): # Since objects are likely to be instantiated often, # we're only checking that context, offset and parent # Everything else may be wrong, but that will get caught later on @@ -64,9 +64,9 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta): # # NOTE: # This allows objects to MASSIVELY MESS with their own internal representation!!! - # Changes to offset, structure_name, etc should NEVER be done + # Changes to offset, type_name, etc should NEVER be done # - self._vol = collections.ChainMap({}, object_info, {'structure_name': structure_name}, kwargs) + self._vol = collections.ChainMap({}, object_info, {'type_name': type_name}, kwargs) self._context = context @property @@ -79,10 +79,10 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta): def write(self, value): """Writes the new value into the format at the offset the object currently resides at""" - def cast(self, new_structure_name, **additional): + def cast(self, new_type_name, **additional): """Returns a new object at the offset and from the layer that the current object inhabits""" # TODO: Carefully consider the implications of casting and how it should work - object_template = self._context.symbol_space.get_structure(new_structure_name) + object_template = self._context.symbol_space.get_type(new_type_name) object_template.update_vol(**additional) object_info = ObjectInformation(layer_name = self.vol.layer_name, offset = self.vol.offset, @@ -112,7 +112,7 @@ class ObjectInterface(validity.ValidityRoutines, metaclass = ABCMeta): @classmethod def relative_child_offset(cls, template, child): """Returns the relative offset from the head of the parent data to the child member""" - raise KeyError(repr(template.vol.structure_name) + " does not contain any children.") + raise KeyError(repr(template.vol.type_name) + " does not contain any children.") class Template(validity.ValidityRoutines): @@ -121,10 +121,10 @@ class Template(validity.ValidityRoutines): This is effectively a class for currying object calls """ - def __init__(self, structure_name, **arguments): + def __init__(self, type_name, **arguments): """Stores the keyword arguments for later use""" # Allow the updating of template arguments whilst still in template form - self._vol = collections.ChainMap(arguments, {'structure_name': structure_name}) + self._vol = collections.ChainMap(arguments, {'type_name': type_name}) @property def vol(self): diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py index 9347064b1..4e443702c 100644 --- a/volatility/framework/interfaces/symbols.py +++ b/volatility/framework/interfaces/symbols.py @@ -11,84 +11,87 @@ from volatility.framework.interfaces import configuration class SymbolTableInterface(validity.ValidityRoutines): """Handles a table of symbols""" - def __init__(self, name, native_structures = None): - self._check_type(native_structures, NativeTableInterface) + def __init__(self, name, native_types = None): + self._check_type(native_types, NativeTableInterface) if name: self._check_type(name, str) self.name = name or None - self._native_structures = native_structures + self._native_types = native_types - # ## Required Constant symbol functions + # ## Required Symbol functions - def get_constant(self, name): - """Resolves a symbol name into a constant + def get_symbol(self, name): + """Resolves a symbol name into a symbol object If the symbol isn't found, it raises a SymbolError exception """ - raise NotImplementedError("Abstract property get_constant not implemented by subclass.") + raise NotImplementedError("Abstract property get_symbol not implemented by subclass.") + + def get_symbol_type(self, name): + """Resolves a symbol name into a symbol and then resolves the symbol's type""" + return self.get_type(self.get_symbol(name).type_name) @property - def constants(self): - """Returns an iterator of the constant symbols""" - raise NotImplementedError("Abstract property constants not implemented by subclass.") + def symbols(self): + """Returns an iterator of the Symbols""" + raise NotImplementedError("Abstract property symbols not implemented by subclass.") - # ## Required Structure symbol functions + # ## Required Symbol type functions - def get_structure(self, name): + def get_type(self, name): """Resolves a symbol name into an object template If the symbol isn't found it raises a SymbolError exception """ - raise NotImplementedError("Abstract method get_structure not implemented by subclass.") + raise NotImplementedError("Abstract method get_type not implemented by subclass.") @property - def structures(self): - """Returns an iterator of the structure symbols""" - raise NotImplementedError("Abstract property structures not implemented by subclass.") + def types(self): + """Returns an iterator of the Symbol types""" + raise NotImplementedError("Abstract property types not implemented by subclass.") # ## Native Type Handler @property def natives(self): - """Returns None or a symbol_space for handling space specific native types""" - return self._native_structures + """Returns None or a NativeTable for handling space specific native types""" + return self._native_types @natives.setter def natives(self, value): """Checks the natives value and then applies it internally - WARNING: This allows changing the underlying size of all the other structures referenced in the symbolspace + WARNING: This allows changing the underlying size of all the other types referenced in the SymbolTable """ self._check_type(value, NativeTableInterface) - self._native_structures = value + self._native_types = value # ## Functions for overriding classes - def set_structure_class(self, name, clazz): - """Overrides the object class for a specific structure symbol + def set_type_class(self, name, clazz): + """Overrides the object class for a specific Symbol type - Name *must* be present in self.structures + Name *must* be present in self.types """ - raise NotImplementedError("Abstract method set_structure_class not implemented yet.") + raise NotImplementedError("Abstract method set_type_class not implemented yet.") - def get_structure_class(self, name): - """Returns the class associated with a structure symbol""" - raise NotImplementedError("Abstract method get_structure_class not implemented yet.") + def get_type_class(self, name): + """Returns the class associated with a Symbol type""" + raise NotImplementedError("Abstract method get_type_class not implemented yet.") - def del_structure_class(self, name): - """Removes the associated class override for a specific structure symbol""" - raise NotImplementedError("Abstract method del_structure_class not implemented yet.") + def del_type_class(self, name): + """Removes the associated class override for a specific Symbol type""" + raise NotImplementedError("Abstract method del_type_class not implemented yet.") class NativeTableInterface(SymbolTableInterface): """Class to distinguish NativeSymbolLists from other symbol lists""" - @staticmethod - def constant(): - raise exceptions.SymbolError("NativeTables never hold constants") + def get_symbol(self, name): + raise exceptions.SymbolError("NativeTables never hold symbols") @property - def constants(self): + def symbols(self): return [] diff --git a/volatility/framework/objects/__init__.py b/volatility/framework/objects/__init__.py index 48ad5f734..d53cad858 100644 --- a/volatility/framework/objects/__init__.py +++ b/volatility/framework/objects/__init__.py @@ -26,19 +26,23 @@ class Void(interfaces.objects.ObjectInterface): raise TypeError("Cannot write data to a void, recast as another object") +class Function(interfaces.objects.ObjectInterface): + """""" + + class PrimitiveObject(interfaces.objects.ObjectInterface): """PrimitiveObject is an interface for any objects that should simulate a Python primitive""" _struct_type = int - def __init__(self, context, structure_name, object_info, struct_format): + def __init__(self, context, type_name, object_info, struct_format): interfaces.objects.ObjectInterface.__init__(self, context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info, struct_format = struct_format) self._struct_format = struct_format - def __new__(cls, context, structure_name, object_info, struct_format, **kwargs): + def __new__(cls, context, type_name, object_info, struct_format, **kwargs): """Creates the appropriate class and returns it so that the native type is inherritted The only reason the **kwargs is added, is so that the inherriting types can override __init__ @@ -84,15 +88,15 @@ class Bytes(PrimitiveObject, bytes): """Primitive Object that handles specific series of bytes""" _struct_type = bytes - def __init__(self, context, structure_name, object_info, length = 1): + def __init__(self, context, type_name, object_info, length = 1): interfaces.objects.ObjectInterface.__init__(self, context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info, struct_format = str(length) + "s") self._vol['length'] = length - def __new__(cls, context, structure_name, object_info, length = 1, **kwargs): + def __new__(cls, context, type_name, object_info, length = 1, **kwargs): """Creates the appropriate class and returns it so that the native type is inherritted The only reason the **kwargs is added, is so that the inherriting types can override __init__ @@ -112,17 +116,17 @@ class String(PrimitiveObject, str): """ _struct_type = str - def __init__(self, context, structure_name, object_info, max_length = 1, encoding = "utf-8", errors = None): + def __init__(self, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = None): PrimitiveObject.__init__(self, context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info, struct_format = str(max_length) + 's') self._vol["max_length"] = max_length self._vol['encoding'] = encoding self._vol['errors'] = errors - def __new__(cls, context, structure_name, object_info, max_length = 1, encoding = "utf-8", errors = None, **kwargs): + def __new__(cls, context, type_name, object_info, max_length = 1, encoding = "utf-8", errors = None, **kwargs): """Creates the appropriate class and returns it so that the native type is inherited The only reason the **kwargs is added, is so that the inherriting types can override __init__ @@ -145,12 +149,12 @@ class String(PrimitiveObject, str): class Pointer(Integer): """Pointer which points to another object""" - def __init__(self, context, structure_name, object_info, struct_format, target = None): + def __init__(self, context, type_name, object_info, struct_format, target = None): self._check_type(target, templates.ObjectTemplate) Integer.__init__(self, context = context, object_info = object_info, - structure_name = structure_name, + type_name = type_name, struct_format = struct_format) self._vol['target'] = target @@ -195,16 +199,16 @@ class Pointer(Integer): class BitField(PrimitiveObject, int): """Object containing a field which is made up of bits rather than whole bytes""" - def __new__(cls, context, structure_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0): + def __new__(cls, context, type_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0): cls._check_type(target, Integer) value = target(context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info, struct_format = struct_format) return cls._struct_type.__new__(cls, (value >> start_bit) & ((1 << end_bit) - 1)) - def __init__(self, context, structure_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0): - PrimitiveObject.__init__(self, context, structure_name, object_info, struct_format) + def __init__(self, context, type_name, object_info, struct_format, target = None, start_bit = 0, end_bit = 0): + PrimitiveObject.__init__(self, context, type_name, object_info, struct_format) self._vol['target'] = target self._vol['start_bit'] = start_bit self._vol['end_bit'] = end_bit @@ -232,11 +236,11 @@ class Enumeration(interfaces.objects.ObjectInterface): class Array(interfaces.objects.ObjectInterface, collections.Sequence): """Object which can contain a fixed number of an object type""" - def __init__(self, context, structure_name, object_info, count = 0, target = None): + def __init__(self, context, type_name, object_info, count = 0, target = None): self._check_type(target, templates.ObjectTemplate) interfaces.objects.ObjectInterface.__init__(self, context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info) self._vol['count'] = self._check_type(count, int) self._vol['target'] = target @@ -293,10 +297,10 @@ class Struct(interfaces.objects.ObjectInterface): Keep the number of methods in this class low or very specific, since each one could overload a valid member. """ - def __init__(self, context, structure_name, object_info, size, members): + def __init__(self, context, type_name, object_info, size, members): interfaces.objects.ObjectInterface.__init__(self, context = context, - structure_name = structure_name, + type_name = type_name, object_info = object_info, size = size, members = members) @@ -306,7 +310,7 @@ class Struct(interfaces.objects.ObjectInterface): class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy): @classmethod def size(cls, template): - """Method to return the size of this structure""" + """Method to return the size of this type""" if template.vol.get('size', None) is None: raise TypeError("Struct ObjectTemplate not provided with a size") return template.vol.size @@ -339,7 +343,7 @@ class Struct(interfaces.objects.ObjectInterface): @classmethod def _check_members(cls, members): # Members should be an iterable mapping of symbol names to tuples of (relative_offset, ObjectTemplate) - # An object template is a callable that when called with a context, offset, layer_name and structure_name + # An object template is a callable that when called with a context, offset, layer_name and type_name if not isinstance(members, collections.Mapping): raise TypeError("Struct members parameter must be a mapping not " + type(members)) if not all([(isinstance(member, tuple) and len(member) == 2) for member in members.values()]): @@ -350,7 +354,7 @@ class Struct(interfaces.objects.ObjectInterface): return self.__getattr__(attr) def __getattr__(self, attr): - """Method for accessing members of the structure""" + """Method for accessing members of the type""" if attr in self._concrete_members: return self._concrete_members[attr] elif attr in self.vol.members: @@ -362,7 +366,7 @@ class Struct(interfaces.objects.ObjectInterface): parent = self)) self._concrete_members[attr] = member return member - raise AttributeError("'" + self.vol.structure_name + "' Struct has no attribute '" + attr + "'") + raise AttributeError("'" + self.vol.type_name + "' Struct has no attribute '" + attr + "'") def write(self, value): raise TypeError("Structs cannot be written to directly, individual members must be written instead") diff --git a/volatility/framework/objects/templates.py b/volatility/framework/objects/templates.py index d90f5779f..404adb58a 100644 --- a/volatility/framework/objects/templates.py +++ b/volatility/framework/objects/templates.py @@ -12,14 +12,14 @@ class ObjectTemplate(interfaces.objects.Template, validity.ValidityRoutines): This is effectively a method of currying, but adds more structure to avoid abuse. It also allows inspection of information that should already be known: - * Structure size + * Type size * Members, etc etc. """ - def __init__(self, object_class = None, structure_name = None, **arguments): + def __init__(self, object_class = None, type_name = None, **arguments): interfaces.objects.Template.__init__(self, - structure_name = structure_name, + type_name = type_name, **arguments) self._check_class(object_class, interfaces.objects.ObjectInterface) self.update_vol(object_class = object_class) @@ -71,5 +71,5 @@ class ReferenceTemplate(interfaces.objects.Template): """ def __call__(self, context, object_info): - template = context.symbol_space.get_structure(self.vol.structure_name) + template = context.symbol_space.get_type(self.vol.type_name) return template(context = context, object_info = object_info) diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index 442abb699..c05852fb4 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -14,8 +14,8 @@ from volatility.framework.symbols import native, vtypes, windows class SymbolType(object): # Suitably random values until we make this an Enum and require python >= 3.4 - STRUCTURE = 143534545 - CONSTANT = 28293045 + TYPE = 143534545 + SYMBOL = 28293045 class SymbolSpace(collections.abc.Mapping): @@ -25,25 +25,25 @@ class SymbolSpace(collections.abc.Mapping): proceed down through the ranks if a namespace isn't specified. """ - def __init__(self, native_structures = None): - if not isinstance(native_structures, interfaces.symbols.NativeTableInterface): - raise TypeError("SymbolSpace native_structures must be NativeSymbolInterface") + def __init__(self, native_types = None): + if not isinstance(native_types, interfaces.symbols.NativeTableInterface): + raise TypeError("SymbolSpace native_types must be NativeSymbolInterface") self._dict = collections.OrderedDict() - self._native_structures = native_structures + self._native_types = native_types # Permanently cache all resolved symbols self._resolved = {} @property def natives(self): """Returns the native_types for this symbol space""" - return self._native_structures + return self._native_types @natives.setter - def natives(self, native_structures): - if native_structures is not None: + def natives(self, native_types): + if native_types is not None: warnings.warn( "Resetting the native type can cause have drastic effects on memory analysis using this space") - self._native_structures = native_structures + self._native_types = native_types def __len__(self): """Returns the number of tables within the space""" @@ -73,10 +73,10 @@ class SymbolSpace(collections.abc.Mapping): def _weak_resolve(self, resolve_type, name): """Takes a symbol name and resolves it with ReferentialTemplates""" - if resolve_type == SymbolType.STRUCTURE: - get_function = 'get_structure' - elif resolve_type == SymbolType.CONSTANT: - get_function = 'get_constant' + if resolve_type == SymbolType.TYPE: + get_function = 'get_type' + elif resolve_type == SymbolType.SYMBOL: + get_function = 'get_symbol' else: raise ValueError("Weak_resolve called without a proper SymbolType.") @@ -85,20 +85,20 @@ class SymbolSpace(collections.abc.Mapping): table_name = name_array[0] component_name = name_array[1] return getattr(self._dict[table_name], get_function)(component_name) - elif name in self.natives.structures: + elif name in self.natives.types: return getattr(self.natives, get_function)(name) raise exceptions.SymbolError("Malformed symbol name") - def get_structure(self, structure_name): + def get_type(self, type_name): """Takes a symbol name and resolves it This method ensures that all referenced templates (including self-referential templates) are satisfied as ObjectTemplates """ # Traverse down any resolutions - if structure_name not in self._resolved: - self._resolved[structure_name] = self._weak_resolve(SymbolType.STRUCTURE, structure_name) - traverse_list = [structure_name] + if type_name not in self._resolved: + self._resolved[type_name] = self._weak_resolve(SymbolType.TYPE, type_name) + traverse_list = [type_name] replacements = set() # Whole Symbols that still need traversing while traverse_list: @@ -110,18 +110,18 @@ class SymbolSpace(collections.abc.Mapping): 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.vol.structure_name not in self._resolved: - traverse_list.append(child.vol.structure_name) - self._resolved[child.vol.structure_name] = self._weak_resolve(SymbolType.STRUCTURE, - child.vol.structure_name) + if child.vol.type_name not in self._resolved: + traverse_list.append(child.vol.type_name) + self._resolved[child.vol.type_name] = self._weak_resolve(SymbolType.TYPE, + child.vol.type_name) # Stash the replacement replacements.add((traverser, child)) elif child.children: template_traverse_list.append(child) for (parent, child) in replacements: - parent.replace_child(child, self._resolved[child.vol.structure_name]) - return self._resolved[structure_name] + parent.replace_child(child, self._resolved[child.vol.type_name]) + return self._resolved[type_name] - def get_constant(self, constant_name): - """Look-up a constant name across all the contained symbol spaces""" - return self._weak_resolve(SymbolType.CONSTANT, constant_name) + def get_symbol(self, symbol_name): + """Look-up a symbol name across all the contained symbol spaces""" + return self._weak_resolve(SymbolType.SYMBOL, symbol_name) diff --git a/volatility/framework/symbols/native.py b/volatility/framework/symbols/native.py index 3da7b33a0..c74e70015 100644 --- a/volatility/framework/symbols/native.py +++ b/volatility/framework/symbols/native.py @@ -19,19 +19,19 @@ class NativeTable(interfaces.symbols.NativeTableInterface): native_class, _native_struct = self._native_dictionary[native_type] self._overrides[native_type] = native_class # Create this once early, because it may get used a lot - self._structures = set(self._native_dictionary).union( + self._types = set(self._native_dictionary).union( {'Enumeration', 'array', 'BitField', 'void', 'pointer', 'String', 'Bytes'}) - def get_structure_class(self, name): + def get_type_class(self, name): ntype, fmt = native_types.get(name, (objects.Integer, '')) return ntype @property - def structures(self): - """Returns an iterator of the structure symbol names""" - return self._structures + def types(self): + """Returns an iterator of the symbol type names""" + return self._types - def get_structure(self, structure_name): + def get_type(self, type_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 @@ -39,31 +39,31 @@ class NativeTable(interfaces.symbols.NativeTableInterface): # NOTE: These need updating whenever the object init signatures change additional = {} obj = None - if structure_name == 'void': + if type_name == 'void': obj = objects.Void - elif structure_name == 'array': + elif type_name == 'array': obj = objects.Array - additional = {"count": 0, "target": self.get_structure('void')} - elif structure_name == 'Enumeration': + additional = {"count": 0, "target": self.get_type('void')} + elif type_name == 'Enumeration': obj = objects.Enumeration - additional = {"target": self.get_structure('void'), "choices": {}} - elif structure_name == 'BitField': + additional = {"target": self.get_type('void'), "choices": {}} + elif type_name == 'BitField': obj = objects.BitField additional = {"start_bit": 0, "end_bit": 0} - elif structure_name == 'String': + elif type_name == 'String': obj = objects.String additional = {"max_length": 0} - elif structure_name == 'Bytes': + elif type_name == 'Bytes': obj = objects.Bytes additional = {"length": 0} if obj is not None: - return objects.templates.ObjectTemplate(obj, structure_name = structure_name, **additional) + return objects.templates.ObjectTemplate(obj, type_name = type_name, **additional) - _native_type, native_format = self._native_dictionary[structure_name] - if structure_name == 'pointer': - additional = {'target': self.get_structure('void')} - return objects.templates.ObjectTemplate(self.get_structure_class(structure_name), # pylint: disable=W0142 - structure_name = structure_name, + _native_type, native_format = self._native_dictionary[type_name] + if type_name == 'pointer': + additional = {'target': self.get_type('void')} + return objects.templates.ObjectTemplate(self.get_type_class(type_name), # pylint: disable=W0142 + type_name = type_name, struct_format = native_format, **additional) diff --git a/volatility/framework/symbols/vtypes.py b/volatility/framework/symbols/vtypes.py index ebd566cd6..8a115998c 100644 --- a/volatility/framework/symbols/vtypes.py +++ b/volatility/framework/symbols/vtypes.py @@ -36,22 +36,22 @@ from volatility.framework import exceptions, objects, interfaces # vtype list and a struct dictionary class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface): - """Symbol Table that handles vtype datastructures""" + """Symbol Table that handles vtype datatypes""" - def __init__(self, name, vtype_dictionary, native_structures = None): - interfaces.symbols.SymbolTableInterface.__init__(self, name, native_structures) + def __init__(self, name, vtype_dictionary, native_types = None): + interfaces.symbols.SymbolTableInterface.__init__(self, name, native_types) self._vtypedict = vtype_dictionary self._overrides = {} - def get_structure_class(self, name): + def get_type_class(self, name): return self._overrides.get(name, objects.Struct) - def set_structure_class(self, name, clazz): - if name not in self.structures: - raise ValueError("Symbol " + name + " not in " + self.name + " SymbolTable") + def set_type_class(self, name, clazz): + if name not in self.types: + raise ValueError("Symbol type " + name + " not in " + self.name + " SymbolTable") self._overrides[name] = clazz - def del_structure_class(self, name): + def del_type_class(self, name): if name in self._overrides: del self._overrides[name] @@ -60,23 +60,23 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface): if not dictionary: raise exceptions.SymbolSpaceError("Invalid vtype dictionary: " + repr(dictionary)) - structure_name = dictionary[0] + type_name = dictionary[0] - if structure_name in self.natives.structures: + if type_name in self.natives.types: # The symbol is a native type - native_template = self.natives.get_structure(structure_name) + native_template = self.natives.get_type(type_name) # Add specific additional parameters, etc update = {} - if structure_name == 'array': + if type_name == 'array': update['count'] = dictionary[1] update['target'] = self._vtypedict_to_template(dictionary[2]) - elif structure_name == 'pointer': + elif type_name == 'pointer': update["target"] = self._vtypedict_to_template(dictionary[1]) - elif structure_name == 'Enumeration': + elif type_name == 'Enumeration': update = copy.deepcopy(dictionary[1]) update["target"] = self._vtypedict_to_template([update['target']]) - elif structure_name == 'BitField': + elif type_name == 'BitField': update = dictionary[1] update['target'] = self._vtypedict_to_template([update['native_type']]) native_template.update_vol(**update) # pylint: disable=W0142 @@ -86,25 +86,25 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface): if len(dictionary) > 1: raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) - return objects.templates.ReferenceTemplate(structure_name = self.name + "!" + structure_name) + return objects.templates.ReferenceTemplate(type_name = self.name + "!" + type_name) @property - def structures(self): + def types(self): """Returns an iterator of the symbol names""" return self._vtypedict.keys() - def get_structure(self, structure_name): + def get_type(self, type_name): """Resolves an individual symbol""" - if structure_name not in self._vtypedict: + if type_name not in self._vtypedict: raise exceptions.SymbolError - size, curdict = self._vtypedict[structure_name] + size, curdict = self._vtypedict[type_name] members = {} for member_name in curdict: relative_offset, vtypedict = curdict[member_name] member = (relative_offset, self._vtypedict_to_template(vtypedict)) members[member_name] = member - object_class = self.get_structure_class(structure_name) - return objects.templates.ObjectTemplate(structure_name = self.name + "!" + structure_name, + object_class = self.get_type_class(type_name) + return objects.templates.ObjectTemplate(type_name = self.name + "!" + type_name, object_class = object_class, size = size, members = members) diff --git a/volatility/framework/symbols/windows/extensions.py b/volatility/framework/symbols/windows/extensions.py index ccb0b4936..8833d502c 100644 --- a/volatility/framework/symbols/windows/extensions.py +++ b/volatility/framework/symbols/windows/extensions.py @@ -12,13 +12,13 @@ class _ETHREAD(objects.Struct): class _LIST_ENTRY(objects.Struct, collections.abc.Iterable): - def to_list(self, structure, member, forward = True, sentinel = True, layer = None): + def to_list(self, symbol_type, member, forward = True, sentinel = True, layer = None): """Returns an iterator of the entries in the list""" if layer is None: layer = self.vol.layer_name - relative_offset = self._context.symbol_space.get_structure(structure).relative_child_offset(member) + relative_offset = self._context.symbol_space.get_type(symbol_type).relative_child_offset(member) direction = 'Blink' if forward: @@ -26,16 +26,16 @@ class _LIST_ENTRY(objects.Struct, collections.abc.Iterable): link = getattr(self, direction).dereference() if not sentinel: - yield self._context.object(structure, layer, offset = self.vol.offset - relative_offset) + yield self._context.object(symbol_type, layer, offset = self.vol.offset - relative_offset) seen = {self.vol.offset} while link.vol.offset not in seen: - obj = self._context.object(structure, layer, offset = link.vol.offset - relative_offset) + obj = self._context.object(symbol_type, layer, offset = link.vol.offset - relative_offset) yield obj seen.add(link.vol.offset) link = getattr(link, direction).dereference() def __iter__(self): - return self.to_list(self.vol.parent.vol.structure_name, self.vol.member_name) + return self.to_list(self.vol.parent.vol.type_name, self.vol.member_name) diff --git a/volatility/framework/symbols/windows/xp_sp2.py b/volatility/framework/symbols/windows/xp_sp2.py index 85c79f2e8..6e9119bd5 100644 --- a/volatility/framework/symbols/windows/xp_sp2.py +++ b/volatility/framework/symbols/windows/xp_sp2.py @@ -38,8 +38,8 @@ class WindowsKernelSymbolProvider(interfaces.symbols.SymbolTableProviderInterfac vtype_table = vtypes.VTypeSymbolTable(cls.space_name, virtual_types, context.symbol_space.natives) # Set-up windows specific types - vtype_table.set_structure_class('_ETHREAD', extensions._ETHREAD) - vtype_table.set_structure_class('_LIST_ENTRY', extensions._LIST_ENTRY) + vtype_table.set_type_class('_ETHREAD', extensions._ETHREAD) + vtype_table.set_type_class('_LIST_ENTRY', extensions._LIST_ENTRY) context.symbol_space.append(vtype_table) context.config[config_path] = cls.space_name diff --git a/volatility/plugins/windows/pslist.py b/volatility/plugins/windows/pslist.py index d190a2149..23ae6cf32 100644 --- a/volatility/plugins/windows/pslist.py +++ b/volatility/plugins/windows/pslist.py @@ -27,7 +27,7 @@ class PsList(plugins.PluginInterface): # Get the process in the physical space flateproc = ctx.object("ntkrnlmp!_EPROCESS", physical_layer, offset = offset) # Determine the relative offset from the Thread head to the ThreadListEntry - reloff = ctx.symbol_space.get_structure("ntkrnlmp!_ETHREAD").relative_child_offset("ThreadListEntry") + reloff = ctx.symbol_space.get_type("ntkrnlmp!_ETHREAD").relative_child_offset("ThreadListEntry") # Get the thread object in kernel space from the ethread = ctx.object("ntkrnlmp!_ETHREAD", kernel_layer, offset = flateproc.ThreadListHead.Flink - reloff) # Get the process from the thread object in kernel space From 659bfc1670277b9aab220caed7707442d51d45ae Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 11:54:44 +0100 Subject: [PATCH 03/14] Add in Symbol class to properly represent programming symbols. --- volatility/framework/interfaces/symbols.py | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py index 4e443702c..76dfb5f6b 100644 --- a/volatility/framework/interfaces/symbols.py +++ b/volatility/framework/interfaces/symbols.py @@ -8,6 +8,32 @@ from volatility.framework import validity, exceptions from volatility.framework.interfaces import configuration +class Symbol(validity.ValidityRoutines): + def __init__(self, name, offset, type_name = None): + self._name = self._check_type(name, str) + self._location = None + self._offset = self._check_type(offset, int) + if type_name is None: + type_name = name + self._type_name = self._check_type(type_name, str) + # Scope and location can be added at a later date + + @property + def name(self): + """Returns the name of the symbol""" + return self._name + + @property + def type_name(self): + """Returns the name of the type that the symbol represents""" + return self._type_name + + @property + def offset(self): + """Returns the relative offset of the symbol within the compilation unit""" + return self._offset + + class SymbolTableInterface(validity.ValidityRoutines): """Handles a table of symbols""" From 34b81a5265a79a88fbba69f723af2ce247114021 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 12:03:46 +0100 Subject: [PATCH 04/14] Add in Unions Since a Union is identical to a Struct (and at the moment a struct doesn't enforce non-overlapping members), these are identical and a Union is a descendent of Struct. If this ever becomes a problem there is a filthy way to fix it, but it's really bad and will likely cause more subtle and difficult to diagnose problems. Stick with inheritance. --- volatility/framework/objects/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/volatility/framework/objects/__init__.py b/volatility/framework/objects/__init__.py index d53cad858..9d88c02f1 100644 --- a/volatility/framework/objects/__init__.py +++ b/volatility/framework/objects/__init__.py @@ -370,3 +370,12 @@ class Struct(interfaces.objects.ObjectInterface): def write(self, value): raise TypeError("Structs cannot be written to directly, individual members must be written instead") + + +# Nice way of duplicating the class, but *could* causes problems with isintance +class Union(Struct): + pass + +# Really nasty way of duplicating the class +# WILL cause problems with any mutable class/static variables +# Union = type('Union', Struct.__bases__, dict(Struct.__dict__)) From fea6e0820a97f78d88acd99a21b7b762f2cb300c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 15:26:31 +0100 Subject: [PATCH 05/14] Convert to using a BANG variable for the symbol name delimiter. Whilst it's easy to do now (because we don't yet use it many places) convert the ! delimiter into a variable. --- volatility/framework/constants.py | 1 + volatility/framework/interfaces/symbols.py | 4 +++- volatility/framework/symbols/__init__.py | 4 ++-- volatility/framework/symbols/vtypes.py | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/volatility/framework/constants.py b/volatility/framework/constants.py index f79a773b5..1656c225d 100644 --- a/volatility/framework/constants.py +++ b/volatility/framework/constants.py @@ -6,3 +6,4 @@ This includes default scanning block sizes, etc.""" import os.path PLUGINS_PATH = [os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "plugins"))] +BANG = "!" diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py index 76dfb5f6b..9c0d562d0 100644 --- a/volatility/framework/interfaces/symbols.py +++ b/volatility/framework/interfaces/symbols.py @@ -4,13 +4,15 @@ Created on 4 May 2013 @author: mike """ -from volatility.framework import validity, exceptions +from volatility.framework import validity, exceptions, constants from volatility.framework.interfaces import configuration class Symbol(validity.ValidityRoutines): def __init__(self, name, offset, type_name = None): self._name = self._check_type(name, str) + if constants.BANG in self._name: + raise ValueError("Symbol names cannot contain the symbol differentiator (" + constants.BANG + ")") self._location = None self._offset = self._check_type(offset, int) if type_name is None: diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index c05852fb4..3e21bc61c 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -8,7 +8,7 @@ import collections import collections.abc import warnings -from volatility.framework import objects, interfaces, exceptions +from volatility.framework import objects, interfaces, exceptions, constants from volatility.framework.symbols import native, vtypes, windows @@ -80,7 +80,7 @@ class SymbolSpace(collections.abc.Mapping): else: raise ValueError("Weak_resolve called without a proper SymbolType.") - name_array = name.split("!") + name_array = name.split(constants.BANG) if len(name_array) == 2: table_name = name_array[0] component_name = name_array[1] diff --git a/volatility/framework/symbols/vtypes.py b/volatility/framework/symbols/vtypes.py index 8a115998c..19affc3eb 100644 --- a/volatility/framework/symbols/vtypes.py +++ b/volatility/framework/symbols/vtypes.py @@ -6,7 +6,7 @@ Created on 10 Apr 2013 import copy -from volatility.framework import exceptions, objects, interfaces +from volatility.framework import exceptions, objects, interfaces, constants # ## TODO @@ -86,7 +86,7 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface): if len(dictionary) > 1: raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary)) - return objects.templates.ReferenceTemplate(type_name = self.name + "!" + type_name) + return objects.templates.ReferenceTemplate(type_name = self.name + constants.BANG + type_name) @property def types(self): @@ -104,7 +104,7 @@ class VTypeSymbolTable(interfaces.symbols.SymbolTableInterface): member = (relative_offset, self._vtypedict_to_template(vtypedict)) members[member_name] = member object_class = self.get_type_class(type_name) - return objects.templates.ObjectTemplate(type_name = self.name + "!" + type_name, + return objects.templates.ObjectTemplate(type_name = self.name + constants.BANG + type_name, object_class = object_class, size = size, members = members) From e0b7f0b5f0be2e6705c4ecee5109959d7b6f2348 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 15:28:12 +0100 Subject: [PATCH 06/14] Add in helper functions for symbolspaces/symboltables to locate specific symbols. --- volatility/framework/interfaces/symbols.py | 29 +++++++++++++++++++--- volatility/framework/symbols/__init__.py | 12 +++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/volatility/framework/interfaces/symbols.py b/volatility/framework/interfaces/symbols.py index 9c0d562d0..e8ab029cf 100644 --- a/volatility/framework/interfaces/symbols.py +++ b/volatility/framework/interfaces/symbols.py @@ -3,6 +3,7 @@ Created on 4 May 2013 @author: mike """ +import bisect from volatility.framework import validity, exceptions, constants from volatility.framework.interfaces import configuration @@ -55,10 +56,6 @@ class SymbolTableInterface(validity.ValidityRoutines): """ raise NotImplementedError("Abstract property get_symbol not implemented by subclass.") - def get_symbol_type(self, name): - """Resolves a symbol name into a symbol and then resolves the symbol's type""" - return self.get_type(self.get_symbol(name).type_name) - @property def symbols(self): """Returns an iterator of the Symbols""" @@ -111,6 +108,30 @@ class SymbolTableInterface(validity.ValidityRoutines): """Removes the associated class override for a specific Symbol type""" raise NotImplementedError("Abstract method del_type_class not implemented yet.") + # ## Convenience functions for location symbols + + def get_symbol_type(self, name): + """Resolves a symbol name into a symbol and then resolves the symbol's type""" + return self.get_type(self.get_symbol(name).type_name) + + def get_symbols_by_type(self, type_name): + """Returns the name of all symbols in this table that have type matching type_name""" + for symbol in self.symbols: + # This allows for searching with and without the table name (in case multiple tables contain + # the same symbol name and we've not specifically been told which one) + if symbol.type_name == type_name or (symbol.type_name.endswith(constants.BANG + type_name)): + yield symbol.name + + def get_symbols_by_location(self, offset): + """Returns the name of all symbols in this table that have type matching type_name""" + sort_symbols = [(s.offset, s) for s in sorted(self.symbols, key = lambda x: x.offset)] + result = bisect.bisect_left(sort_symbols, offset) + if result == len(sort_symbols): + raise StopIteration + closest_symbol = sort_symbols[result][1] + if closest_symbol.offset == offset: + yield closest_symbol.name + class NativeTableInterface(SymbolTableInterface): """Class to distinguish NativeSymbolLists from other symbol lists""" diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index 3e21bc61c..2b4c6f0f3 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -33,6 +33,18 @@ class SymbolSpace(collections.abc.Mapping): # Permanently cache all resolved symbols self._resolved = {} + def get_symbols_by_type(self, type_name): + """Returns all symbols based """ + for table in self._dict.keys(): + for symbol_name in self._dict[table].get_symbols_by_type(type_name): + yield table + constants.BANG + symbol_name + + def get_symbols_by_location(self, offset): + """Returns all symbols that exist at a specific relative offset""" + for table in self._dict.values(): + for symbol_name in self._dict[table].get_symbols_by_location(offset = offset): + yield table + constants.BANG + symbol_name + @property def natives(self): """Returns the native_types for this symbol space""" From 16bed2aa94b99827e8bc2e4a33eaeb08d1dcfaf0 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 16:02:30 +0100 Subject: [PATCH 07/14] Allow specifying a table_name for get_symbols_by_location. --- volatility/framework/symbols/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/volatility/framework/symbols/__init__.py b/volatility/framework/symbols/__init__.py index 2b4c6f0f3..962f4a96d 100644 --- a/volatility/framework/symbols/__init__.py +++ b/volatility/framework/symbols/__init__.py @@ -39,9 +39,15 @@ class SymbolSpace(collections.abc.Mapping): for symbol_name in self._dict[table].get_symbols_by_type(type_name): yield table + constants.BANG + symbol_name - def get_symbols_by_location(self, offset): + def get_symbols_by_location(self, offset, table_name = None): """Returns all symbols that exist at a specific relative offset""" - for table in self._dict.values(): + table_list = self._dict.values() + if table_name is not None: + if table_name in self._dict: + table_list = [self._dict[table_name]] + else: + table_list = [] + for table in table_list: for symbol_name in self._dict[table].get_symbols_by_location(offset = offset): yield table + constants.BANG + symbol_name From 9faa13820ace5340d7b591074767af443fa01b94 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 19:21:24 +0100 Subject: [PATCH 08/14] Since we're a forensics program, make sure we only read unless we know the user REALLY wants to write. --- volatility/framework/layers/physical.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility/framework/layers/physical.py b/volatility/framework/layers/physical.py index d200ffd54..64709c436 100644 --- a/volatility/framework/layers/physical.py +++ b/volatility/framework/layers/physical.py @@ -62,7 +62,9 @@ class FileLayer(interfaces.layers.DataLayerInterface): def __init__(self, context, config_path, name, filename): interfaces.layers.DataLayerInterface.__init__(self, context, config_path, name) - self._file = open(filename, "r+b") + # FIXME: Add "+" to the mode once we've determined whether write mode is enabled + mode = "rb" + self._file = open(filename, mode) self._size = os.path.getsize(filename) @property From d882f93fee2a36498e6f56461b9ce36fbb4c2df1 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2016 19:23:15 +0100 Subject: [PATCH 09/14] Move the length check, and require a non-zero length to protect is_valid. --- volatility/framework/layers/physical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility/framework/layers/physical.py b/volatility/framework/layers/physical.py index 64709c436..2cb600f14 100644 --- a/volatility/framework/layers/physical.py +++ b/volatility/framework/layers/physical.py @@ -80,6 +80,8 @@ class FileLayer(interfaces.layers.DataLayerInterface): def is_valid(self, offset, length = 1): """Returns whether the offset is valid or not""" + if length <= 0: + raise TypeError("Length must be positive") return (self.minimum_address <= offset <= self.maximum_address and self.minimum_address <= offset + length - 1 <= self.maximum_address) @@ -87,8 +89,6 @@ class FileLayer(interfaces.layers.DataLayerInterface): """Reads from the file at offset for length""" if not self.is_valid(offset, length): raise exceptions.InvalidAddressException("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: From 472132e5742860bfc2c1a33f2a9e722e1f3e9c65 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 26 May 2016 09:14:09 +0100 Subject: [PATCH 10/14] Clarify and verify that the memory_layer parameter is the name of the memory layer, not the layer itself. --- volatility/framework/layers/intel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility/framework/layers/intel.py b/volatility/framework/layers/intel.py index d8e046323..45758fc8d 100644 --- a/volatility/framework/layers/intel.py +++ b/volatility/framework/layers/intel.py @@ -21,8 +21,8 @@ class Intel(interfaces.layers.TranslationLayerInterface): def __init__(self, context, config_path, name, page_map_offset, memory_layer, swap_layer = None): interfaces.layers.TranslationLayerInterface.__init__(self, context, config_path, name) - self._base_layer = memory_layer - self._page_map_offset = page_map_offset + self._base_layer = self._check_type(memory_layer, str) + self._page_map_offset = self._check_type(page_map_offset, int) # All Intel address spaces work on 4096 byte pages self._page_size_in_bits = 12 @@ -124,7 +124,7 @@ class Intel(interfaces.layers.TranslationLayerInterface): @property def dependencies(self): - """Returns a list of the lower layers that this layer is dependent upon""" + """Returns a list of the lower layer names that this layer is dependent upon""" # TODO: Add in the whole buffalo return [self._base_layer] From 633225edb2a843394386a5a437004284cd32d4b5 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 26 May 2016 09:14:37 +0100 Subject: [PATCH 11/14] Minor reformatting by pycharm for line length. --- volatility/framework/layers/intel.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/volatility/framework/layers/intel.py b/volatility/framework/layers/intel.py index 45758fc8d..2e9a73979 100644 --- a/volatility/framework/layers/intel.py +++ b/volatility/framework/layers/intel.py @@ -131,12 +131,15 @@ class Intel(interfaces.layers.TranslationLayerInterface): @classmethod def get_schema(cls): return [volatility.framework.configuration.requirements.TranslationLayerRequirement(name = 'memory_layer', - constraints = {"type": "physical"}, + constraints = { + "type": "physical"}, optional = False), volatility.framework.configuration.requirements.TranslationLayerRequirement(name = 'swap_layer', - constraints = {"type": "physical"}, + constraints = { + "type": "physical"}, optional = True), - volatility.framework.configuration.requirements.IntRequirement(name = 'page_map_offset', optional = False)] + volatility.framework.configuration.requirements.IntRequirement(name = 'page_map_offset', + optional = False)] class IntelPAE(Intel): From f9a950bb3930459c752290f18e3c968c520da56b Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Wed, 25 May 2016 20:52:46 -0400 Subject: [PATCH 12/14] added LIME translation layer --- volatility/framework/layers/lime.py | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 volatility/framework/layers/lime.py diff --git a/volatility/framework/layers/lime.py b/volatility/framework/layers/lime.py new file mode 100644 index 000000000..bdbac4a9d --- /dev/null +++ b/volatility/framework/layers/lime.py @@ -0,0 +1,118 @@ +""" +Created on 6 Apr 2016 + +@author: npetroni@volexity.com +""" + +import struct + +from volatility.framework import interfaces, exceptions +from volatility.framework.configuration import requirements + +class LimeFormatException(exceptions.LayerException): + """Thrown when an error occurs with the underlying Lime file format""" + +class LimeLayer(interfaces.layers.TranslationLayerInterface): + """A Lime format TranslationLayer. Lime is generally used to store + physical memory images where there are large holes in the physical + address space""" + + provides = {"type": "physical"} + priority = 21 + + MAGIC = 0x4c694d45 + VERSION = 1 + + # Magic[4], Version[4], Start[8], End[8], Reserved[8] + # XXX move this to a custom SymbolSpace? + _header_struct = struct.Struct('= logical_start and offset < (logical_start + size): + return (logical_start, base_start, size) + + raise exceptions.InvalidAddressException("Lime fault at address " + hex(offset)) + + + def is_valid(self, offset, length = 1): + """Returns whether the address offset can be translated to a valid address""" + try: + return all([self._context.memory[self._base_layer].is_valid(mapped_offset) for _, mapped_offset, _, _ in + self.mapping(offset, length)]) + except exceptions.InvalidAddressException: + return False + + def mapping(self, offset, length): + """Returns a sorted list of (offset, mapped_offset, length, layer) mappings""" + if length == 0: + logical_start, base_start, size = self._find_segment(offset) + mapped_offset = offset - logical_start + base_start + return [(offset, mapped_offset, 0, self._base_layer)] + result = [] + while length > 0: + logical_start, base_start, size = self._find_segment(offset) + chunk_offset = offset - logical_start + base_start + chunk_size = size - (offset - logical_start) + result.append((offset, chunk_offset, chunk_size, self._base_layer)) + length -= chunk_size + offset += chunk_size + return result + + @property + def dependencies(self): + """Returns a list of the lower layers that this layer is dependent upon""" + return [self._base_layer] + + @classmethod + def get_schema(cls): + return [requirements.TranslationLayerRequirement(name = 'base_layer', + constraints = {"type": "physical"}, + optional = False)] From 2686487e6f5df0601fae1edae565ad8ad38df6fd Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Thu, 26 May 2016 06:44:09 -0400 Subject: [PATCH 13/14] lime.py: first working version - moved segment loading out of __init__(), now happens on first use - fixed mapping() to return the correct chunk_size - self._base_layer is a string, not a layer --- volatility/framework/layers/lime.py | 36 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/volatility/framework/layers/lime.py b/volatility/framework/layers/lime.py index bdbac4a9d..f022e5083 100644 --- a/volatility/framework/layers/lime.py +++ b/volatility/framework/layers/lime.py @@ -33,13 +33,27 @@ class LimeLayer(interfaces.layers.TranslationLayerInterface): self._base_layer = base_layer # list of tuples (logical start, base start, size) - segments = [] + # loaded by _load_segments() on first access + self._segments = [] + self._minaddr = 0 + self._maxaddr = 0 - # logical end of physical memory + @property + def minimum_address(self): + return self._minaddr + + @property + def maximum_address(self): + return self._maxaddr + + def _load_segments(self): + base_layer = self._context.memory[self._base_layer] base_maxaddr = base_layer.maximum_address maxaddr = 0 offset = 0 header_size = self._header_struct.size + segments = [] + while offset < base_maxaddr: header_data = base_layer.read(offset, header_size) @@ -54,20 +68,17 @@ class LimeLayer(interfaces.layers.TranslationLayerInterface): raise LimeFormatException("bad start/end 0x%x/0x%x at file offset 0x%x" % (start, end, offset)) segment_length = end - start + 1 - segments.append((start, offset, segment_length)) + segments.append((start, offset + header_size, segment_length)) maxaddr = end offset = offset + header_size + segment_length + if len(segments) == 0: + raise LimeFormatException("No LiME segments defined in " + self._base_layer) + self._segments = segments + self._minaddr = segments[0][0] self._maxaddr = maxaddr - @property - def minimum_address(self): - return self._segments[0][0] - - @property - def maximum_address(self): - return self._maxaddr def _find_segment(self, offset): """Finds the segment containing a given offset @@ -75,6 +86,9 @@ class LimeLayer(interfaces.layers.TranslationLayerInterface): Returns the segment tuple """ + if self._segments == []: + self._load_segments() + for logical_start, base_start, size in self._segments: if offset >= logical_start and offset < (logical_start + size): return (logical_start, base_start, size) @@ -100,7 +114,7 @@ class LimeLayer(interfaces.layers.TranslationLayerInterface): while length > 0: logical_start, base_start, size = self._find_segment(offset) chunk_offset = offset - logical_start + base_start - chunk_size = size - (offset - logical_start) + chunk_size = min(size - (offset - logical_start), length) result.append((offset, chunk_offset, chunk_size, self._base_layer)) length -= chunk_size offset += chunk_size From 02f26e78294094afc604afa9b1279625584c4619 Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Thu, 26 May 2016 09:24:36 -0400 Subject: [PATCH 14/14] lime.py: improve check -- empty lists evaluate to False --- volatility/framework/layers/lime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility/framework/layers/lime.py b/volatility/framework/layers/lime.py index f022e5083..c12539041 100644 --- a/volatility/framework/layers/lime.py +++ b/volatility/framework/layers/lime.py @@ -86,7 +86,7 @@ class LimeLayer(interfaces.layers.TranslationLayerInterface): Returns the segment tuple """ - if self._segments == []: + if not self._segments: self._load_segments() for logical_start, base_start, size in self._segments: