def _interdict_to_template(self, dictionary: Dict[str, Any]) -> interfaces.objects.Template: """Converts an intermediate format dict into an object template.""" if not dictionary: raise exceptions.SymbolSpaceError(f"Invalid intermediate dictionary: {dictionary}") type_name = dictionary['kind'] if type_name == 'base': type_name = dictionary['name'] if type_name in self.natives.types: # The symbol is a native type native_template = self.natives.get_type(self.name + constants.BANG + type_name) # Add specific additional parameters, etc update = {} if type_name == 'array': update['count'] = dictionary['count'] update['subtype'] = self._interdict_to_template(dictionary['subtype']) elif type_name == 'pointer': if dictionary.get('base', None): base_type = self.natives.get_type(self.name + constants.BANG + dictionary['base']) update['data_format'] = base_type.vol['data_format'] update['subtype'] = self._interdict_to_template(dictionary['subtype']) elif type_name == 'enum': update = self._lookup_enum(dictionary['name']) elif type_name == 'bitfield': update = { 'start_bit': dictionary['bit_position'], 'end_bit': dictionary['bit_position'] + dictionary['bit_length'] } update['base_type'] = self._interdict_to_template(dictionary['type']) # We do *not* call native_template.clone(), since it slows everything down a lot # We require that the native.get_type method always returns a newly constructed python object native_template.update_vol(**update) return native_template # Otherwise if dictionary['kind'] not in objects.AggregateTypes.values(): raise exceptions.SymbolSpaceError(f"Unknown Intermediate format: {dictionary}") reference_name = dictionary['name'] if constants.BANG not in reference_name: reference_name = self.name + constants.BANG + reference_name else: reference_parts = reference_name.split(constants.BANG) reference_name = (self.table_mapping.get(reference_parts[0], reference_parts[0]) + constants.BANG + constants.BANG.join(reference_parts[1:])) return objects.templates.ReferenceTemplate(type_name = reference_name)
def _validate_json(self) -> None: if ('user_types' not in self._json_object or 'base_types' not in self._json_object or 'metadata' not in self._json_object or 'symbols' not in self._json_object or 'enums' not in self._json_object): raise exceptions.SymbolSpaceError("Malformed JSON file provided")
def _lookup_enum(self, name: str) -> Dict[str, Any]: """Looks up an enumeration and returns a dictionary of __init__ parameters for an Enum.""" lookup = self._json_object['enums'].get(name, None) if not lookup: raise exceptions.SymbolSpaceError(f"Unknown enumeration: {name}") result = {"choices": copy.deepcopy(lookup['constants']), "base_type": self.natives.get_type(lookup['base'])} return result
def __init__(self, context: interfaces.context.ContextInterface, config_path: str, name: str, isf_url: str, native_types: interfaces.symbols.NativeTableInterface = None, table_mapping: Optional[Dict[str, str]] = None, validate: bool = True, class_types: Optional[Mapping[ str, Type[interfaces.objects.ObjectInterface]]] = None, symbol_mask: int = 0) -> None: """Instantiates a SymbolTable based on an IntermediateSymbolFormat JSON file. This is validated against the appropriate schema. The validation can be disabled by passing validate = False, but this should almost never be done. Args: context: The volatility context for the symbol table config_path: The configuration path for the symbol table name: The name for the symbol table (this is used in symbols e.g. table!symbol ) isf_url: The URL pointing to the ISF file location native_types: The NativeSymbolTable that contains the native types for this symbol table table_mapping: A dictionary linking names referenced in the file with symbol tables in the context validate: Determines whether the ISF file will be validated against the appropriate schema class_types: A dictionary of type names and classes that override StructType when they are instantiated symbol_mask: An address mask used for all returned symbol offsets from this table (a mask of 0 disables masking) """ # Check there are no obvious errors # Open the file and test the version self._versions = dict([(x.version, x) for x in class_subclasses(ISFormatTable)]) fp = resources.ResourceAccessor().open(isf_url) reader = codecs.getreader("utf-8") json_object = json.load(reader(fp)) # type: ignore fp.close() # Validation is expensive, but we cache to store the hashes of successfully validated json objects if validate and not schemas.validate(json_object): raise exceptions.SymbolSpaceError( f"File does not pass version validation: {isf_url}") metadata = json_object.get('metadata', None) # Determine the delegate or throw an exception self._delegate = self._closest_version(metadata.get( 'format', "0.0.0"), self._versions)(context, config_path, name, json_object, native_types, table_mapping) if self._delegate.version < constants.ISF_MINIMUM_SUPPORTED: raise RuntimeError( "ISF version {} is no longer supported: {}".format( metadata.get('format', "0.0.0"), isf_url)) elif self._delegate.version < constants.ISF_MINIMUM_DEPRECATED: vollog.warning( f"ISF version {metadata.get('format', '0.0.0')} has been deprecated: {isf_url}" ) # Inherit super().__init__(context, config_path, name, native_types or self._delegate.natives, table_mapping=table_mapping, class_types=class_types) # Since we've been created with parameters, ensure our config is populated likewise self.config['isf_url'] = isf_url self.config['symbol_mask'] = symbol_mask
def _banner_scan( self, context: interfaces.context.ContextInterface, config_path: str, requirement: interfaces.configuration. ConstructableRequirementInterface, layer_name: str, progress_callback: constants.ProgressCallback = None) -> None: """Accepts a context, config_path and SymbolTableRequirement, with a constructed layer_name and scans the layer for banners.""" # Bomb out early if there's no banners if not self.banners: return mss = scanners.MultiStringScanner( [x for x in self.banners if x is not None]) layer = context.layers[layer_name] # Check if the Stacker has already found what we're looking for if layer.config.get(self.banner_config_key, None): banner_list = [(0, bytes(layer.config[self.banner_config_key], 'raw_unicode_escape')) ] # type: Iterable[Any] else: # Swap to the physical layer for scanning # TODO: Fix this so it works for layers other than just Intel layer = context.layers[layer.config['memory_layer']] banner_list = layer.scan(context=context, scanner=mss, progress_callback=progress_callback) for _, banner in banner_list: vollog.debug("Identified banner: {}".format(repr(banner))) symbol_files = self.banners.get(banner, None) if symbol_files: isf_path = symbol_files[0] vollog.debug("Using symbol library: {}".format( symbol_files[0])) clazz = self.symbol_class # Set the discovered options path_join = interfaces.configuration.path_join context.config[path_join(config_path, requirement.name, "class")] = clazz context.config[path_join(config_path, requirement.name, "isf_url")] = isf_path context.config[path_join(config_path, requirement.name, "symbol_mask")] = layer.address_mask # Set a default symbol_shift when attempt to determine it, # so we can create the symbols which are used in finding the aslr_shift anyway if not context.config.get( path_join(config_path, requirement.name, "symbol_shift"), None): # Don't overwrite it if it's already been set, it will be manually refound if not present prefound_kaslr_value = context.layers[ layer_name].metadata.get('kaslr_value', 0) context.config[path_join( config_path, requirement.name, "symbol_shift")] = prefound_kaslr_value # Construct the appropriate symbol table requirement.construct(context, config_path) # Apply the ASLR masking (only if we're not already shifted) if self.find_aslr and not context.config.get( path_join(config_path, requirement.name, "symbol_shift"), None): unmasked_symbol_table_name = context.config.get( path_join(config_path, requirement.name), None) if not unmasked_symbol_table_name: raise exceptions.SymbolSpaceError( "Symbol table could not be constructed") if not isinstance(layer, layers.intel.Intel): raise TypeError("Layer name {} is not an intel space") aslr_shift = self.find_aslr(context, unmasked_symbol_table_name, layer.config['memory_layer']) context.config[path_join(config_path, requirement.name, "symbol_shift")] = aslr_shift context.symbol_space.clear_symbol_cache( unmasked_symbol_table_name) break else: if symbol_files: vollog.debug("Symbol library path not found: {}".format( symbol_files[0])) # print("Kernel", banner, hex(banner_offset)) else: vollog.debug("No existing banners found")