From d57733d5302c8e18e900a1a72a8eb69a7ab47211 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Tue, 1 Nov 2016 01:33:44 +0000 Subject: [PATCH] Add in support for format versioning. --- volatility/framework/symbols/intermed.py | 87 +++++++++++++++++++----- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/volatility/framework/symbols/intermed.py b/volatility/framework/symbols/intermed.py index 4e686747f..964643b44 100644 --- a/volatility/framework/symbols/intermed.py +++ b/volatility/framework/symbols/intermed.py @@ -3,22 +3,74 @@ import json import logging import urllib.parse -from volatility.framework import constants, exceptions, interfaces, objects +from volatility.framework import constants, exceptions, interfaces, objects, class_subclasses vollog = logging.getLogger(__name__) class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): - """Class for storing intermediate debugging data as objects and classes""" - def __init__(self, name, idd_filepath, native_types = None): - super().__init__(name, native_types) + # Check there are no obvious errors url = urllib.parse.urlparse(idd_filepath) if url.scheme != 'file': raise NotImplementedError( "This scheme is not yet implement for the Intermediate Symbol Format: {}".format(url.scheme)) + # Inherit + super().__init__(name, native_types) + + # Open the file and test the version + self._versions = dict([(x.version, x) for x in class_subclasses(ISFormatTable)]) with open(url.path, "r") as fp: - self._json = json.load(fp) + json_object = json.load(fp) + metadata = json_object.get('metadata', None) + + # Determine the delegate or throw an exception + self._delegate = self._closest_version(metadata.get('version', "0.0.0"), self._versions)(name, json_object, + native_types) + + def _closest_version(self, version, versions): + """Determines the highest suitable handler for specified version format""" + """Finds the highest suitable format version to read the data""" + supported, age, revision = [int(x) for x in version.split(".")] + supported_versions = [x for x in versions.keys() if x[0] == supported and x[1] >= age] + if not supported_versions: + raise ValueError("No Intermediate Format versions support file version: {}".format(version)) + return versions[max(supported_versions)] + + def _construct_delegate_function(name, is_property = False): + def _delegate_function(self, *args, **kwargs): + if is_property: + return getattr(self._delegate, name) + return getattr(self._delegate, name)(*args, **kwargs) + + if is_property: + return property(_delegate_function) + return _delegate_function + + symbols = _construct_delegate_function('symbols', True) + types = _construct_delegate_function('types', True) + get_type = _construct_delegate_function('get_type') + get_symbol = _construct_delegate_function('get_symbol') + get_type_class = _construct_delegate_function('get_type_class') + set_type_class = _construct_delegate_function('set_type_class') + del_type_class = _construct_delegate_function('del_type_class') + + +class ISFormatTable(interfaces.symbols.SymbolTableInterface): + """Provide a base class to identify all subclasses""" + pass + + +class Version1Format(ISFormatTable): + """Class for storing intermediate debugging data as objects and classes""" + current = 0 + revision = 0 + age = 0 + version = (current - age, age, revision) + + def __init__(self, name, json_object, native_types = None): + super().__init__(name, native_types) + self._json_object = json_object self._validate_json() self._overrides = {} self._symbol_cache = None @@ -26,16 +78,16 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): # TODO: Check the format and make use of the other metadata def _validate_json(self): - if (not 'user_types' in self._json or - not 'base_types' in self._json or - not 'metadata' in self._json or - not 'symbols' in self._json or - not 'enums' in self._json): + if (not 'user_types' in self._json_object or + not 'base_types' in self._json_object or + not 'metadata' in self._json_object or + not 'symbols' in self._json_object or + not 'enums' in self._json_object): raise exceptions.SymbolSpaceError("Malformed JSON file provided") def get_symbol(self, name): """Returns the location offset given by the symbol name""" - symbol = self._json['symbols'].get(name, None) + symbol = self._json_object['symbols'].get(name, None) if not symbol: raise KeyError("Unknown symbol: {}".format(name)) return interfaces.symbols.Symbol(name = name, address = symbol['address']) @@ -43,8 +95,9 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): @property def symbols(self): if not self._symbol_cache: - self._symbol_cache = [interfaces.symbols.Symbol(name = x, address = self._json['symbols'][x]['address']) for - x in self._json['symbols']] + self._symbol_cache = [ + interfaces.symbols.Symbol(name = x, address = self._json_object['symbols'][x]['address']) for + x in self._json_object['symbols']] return self._symbol_cache # TODO: Add the ability to add/remove/change symbols after creation, note that this should invalidate the cache @@ -64,7 +117,7 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): @property def types(self): """Returns an iterator of the symbol names""" - return self._json.get('user_types', {}) + return self._json_object.get('user_types', {}) def _interdict_to_template(self, dictionary): """Converts an intermediate format dict into an object template""" @@ -102,7 +155,7 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): def _lookup_enum(self, name): """Looks up an enumeration and returns a dictionary of __init__ parameters for an Enum""" - lookup = self._json['enums'].get(name, None) + lookup = self._json_object['enums'].get(name, None) if not lookup: raise exceptions.SymbolSpaceError("Unknown enumeration found: {}".format(name)) result = {"choices": copy.deepcopy(lookup['constants']), @@ -111,9 +164,9 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): def get_type(self, type_name): """Resolves an individual symbol""" - if type_name not in self._json['user_types']: + if type_name not in self._json_object['user_types']: raise exceptions.SymbolError("Unknown symbol: {}".format(type_name)) - curdict = self._json['user_types'][type_name] + curdict = self._json_object['user_types'][type_name] members = {} for member_name in curdict['fields']: interdict = curdict['fields'][member_name]