예제 #1
0
    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(
                "Invalid intermediate dictionary: {}".format(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_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(
                "Unknown Intermediate format: {}".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)
예제 #2
0
 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("Unknown enumeration: {}".format(name))
     result = {"choices": copy.deepcopy(lookup['constants']), "base_type": self.natives.get_type(lookup['base'])}
     return result
예제 #4
0
    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[Dict[
            str, Type[interfaces.objects.ObjectInterface]]] = None
    ) -> 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
        """
        # 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 = volatility.framework.layers.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(
                "File does not pass version validation: {}".format(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)

        # Inherit
        super().__init__(context,
                         config_path,
                         name,
                         native_types or self._delegate.natives,
                         table_mapping=table_mapping,
                         class_types=class_types)
예제 #5
0
    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_shift: int = 0,
                 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_shift: An offset by which to alter all returned symbols for this table
            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 = volatility.framework.layers.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(
                "File does not pass version validation: {}".format(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("ISF version {} has been deprecated: {}".format(
                metadata.get('format', "0.0.0"), 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_shift'] = symbol_shift
        self.config['symbol_mask'] = symbol_mask
예제 #6
0
    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],
                                  'latin-1'))]  # 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")