diff --git a/volatility/framework/layers/__init__.py b/volatility/framework/layers/__init__.py index 71f62dd38..63a7c7595 100644 --- a/volatility/framework/layers/__init__.py +++ b/volatility/framework/layers/__init__.py @@ -18,4 +18,4 @@ # specific language governing rights and limitations under the License. # -from volatility.framework.layers import resources, intel, lime, physical, segmented, vmware, crash +from volatility.framework.layers import resources, intel, lime, physical, segmented, vmware, crash, msf diff --git a/volatility/framework/layers/msf.py b/volatility/framework/layers/msf.py new file mode 100644 index 000000000..0e5e35e44 --- /dev/null +++ b/volatility/framework/layers/msf.py @@ -0,0 +1,60 @@ +from typing import Optional, Dict, Any, List, Iterable, Tuple + +from volatility.framework import interfaces, constants +from volatility.framework.configuration import requirements +from volatility.framework.objects import utility +from volatility.framework.symbols import intermed + + +class PdbMSF(interfaces.layers.TranslationLayerInterface): + headers = { + "MSF_HDR": "Microsoft C/C++ program database 2.00\r\n\x1a\x4a\x47", + "BIG_MSF_HDR": "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53", + } + + def __init__(self, + context: 'interfaces.context.ContextInterface', + config_path: str, + name: str, + metadata: Optional[Dict[str, Any]] = None) -> None: + super().__init__(context, config_path, name, metadata) + self._base_layer = self.config["base_layer"] + + self._pdb_table_name = intermed.IntermediateSymbolTable.create(context, self._config_path, 'windows', 'pdb') + self._version = self._check_header() + + def _check_header(self) -> Optional[str]: + """Verifies the header of the PDB file and returns the version of the file""" + for header in self.headers: + header_type = self._pdb_table_name + constants.BANG + header + current_header = self.context.object(header_type, self._base_layer, 0) + if utility.array_to_string(current_header.Magic) == self.headers[header]: + return header + return None + + @property + def dependencies(self) -> List[str]: + """Returns a list of the lower layers that this layer is dependent upon""" + return [self._base_layer] + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [requirements.TranslationLayerRequirement(name = 'base_layer', optional = False)] + + @property + def maximum_address(self) -> int: + return self.context.memory[self._base_layer].maximum_address + + @property + def minimum_address(self) -> int: + return self.context.memory[self._base_layer].minimum_address + + def is_valid(self, offset: int, length: int = 1) -> bool: + return self.context.memory[self._base_layer].is_valid(offset, length) + + def mapping(self, offset: int, length: int, ignore_errors: bool = False) -> Iterable[Tuple[int, int, int, str]]: + yield (offset, offset, length, self._base_layer) + + +class PdbMSFStream(interfaces.layers.TranslationLayerInterface): + pass diff --git a/volatility/framework/symbols/windows/mspdb.py b/volatility/framework/symbols/windows/mspdb.py new file mode 100644 index 000000000..f0dadfa29 --- /dev/null +++ b/volatility/framework/symbols/windows/mspdb.py @@ -0,0 +1,64 @@ +import argparse +import os +from typing import Tuple +from urllib import request + +from volatility.framework import contexts, interfaces +from volatility.framework.layers import physical, msf + + +class PdbReader: + """Class to read Microsoft PDB files""" + + def __init__(self, context: interfaces.context.ContextInterface, layer_name: str): + self._context = context + self._layer_name = layer_name + + @classmethod + def load_pdb_layer(cls, context: interfaces.context.ContextInterface, + location: str) -> Tuple[str, interfaces.context.ContextInterface]: + """Loads a PDB file into a layer within the context and returns the name of the new layer + + Note: the context may be changed by this method + """ + physical_layer_name = context.memory.free_layer_name("FileLayer") + physical_config_path = interfaces.configuration.path_join("pdbreader", physical_layer_name) + + # Create the file layer + # This must be specific to get us started, setup the config and run + new_context = context.clone() + new_context.config[interfaces.configuration.path_join(physical_config_path, "location")] = location + + physical_layer = physical.FileLayer(new_context, physical_config_path, physical_layer_name) + new_context.add_layer(physical_layer) + + # Add on the MSF format layer + msf_layer_name = context.memory.free_layer_name("MSFLayer") + msf_config_path = interfaces.configuration.path_join("pdbreader", msf_layer_name) + new_context.config[interfaces.configuration.path_join(msf_config_path, "base_layer")] = physical_layer_name + msf_layer = msf.PdbMSF(new_context, msf_config_path, msf_layer_name) + new_context.add_layer(msf_layer) + + return msf_layer_name, new_context + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--filename", help = "Provide the name of a pdb file to read", required = True) + args = parser.parse_args() + + ctx = contexts.Context() + if not os.path.exists(args.filename): + parser.error("File {} does not exists".format(args.filename)) + location = "file:" + request.pathname2url(args.filename) + + layer_name, ctx = PdbReader.load_pdb_layer(ctx, location) + + reader = PdbReader(ctx, layer_name) + + ### TESTING + x = ctx.object('pdb1!BIG_MSF_HDR', layer_name, 0) + import pdb + + pdb.set_trace() diff --git a/volatility/framework/symbols/windows/pdb.json b/volatility/framework/symbols/windows/pdb.json new file mode 100644 index 000000000..8e5f57e67 --- /dev/null +++ b/volatility/framework/symbols/windows/pdb.json @@ -0,0 +1,170 @@ +{ + "symbols": { + }, + "user_types": { + "SI_PERSIST": { + "fields": { + "cb": { + "offset": 0, + "type": { + "kind": "base", + "name": "long" + } + }, + "mpspnpn": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "MSF_HDR": { + "fields": { + "Magic": { + "offset": 0, + "type": { + "count": 44, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + } + }, + "PageSize": { + "offset": 44, + "type": { + "kind": "base", + "name": "long" + } + }, + "FPM": { + "offset": 48, + "type": { + "kind": "base", + "name": "unsigned short" + } + }, + "Mac": { + "offset": 50, + "type": { + "kind": "base", + "name": "unsigned short" + } + }, + "siSt": { + "offset": 52, + "type": { + "kind": "struct", + "name": "SI_PERSIST" + } + } + }, + "kind": "struct", + "size": 60 + }, + "BIG_MSF_HDR": { + "fields": { + "Magic": { + "offset": 0, + "type": { + "count": 30, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + } + }, + "PageSize": { + "offset": 30, + "type": { + "kind": "base", + "name": "long" + } + }, + "FPM": { + "offset": 34, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Mac": { + "offset": 38, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "siSt": { + "offset": 42, + "type": { + "kind": "struct", + "name": "SI_PERSIST" + } + } + }, + "kind": "struct", + "size": 60 + } + }, + "enums": { + }, + "base_types": { + "unsigned char": { + "endian": "little", + "kind": "char", + "signed": false, + "size": 1 + }, + "unsigned short": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 2 + }, + "long": { + "endian": "little", + "kind": "int", + "signed": true, + "size": 4 + }, + "char": { + "endian": "little", + "kind": "char", + "signed": true, + "size": 1 + }, + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + }, + "long long": { + "endian": "little", + "kind": "int", + "signed": true, + "size": 8 + }, + "unsigned long long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 8 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "ikelos-by-hand", + "datetime": "2019-05-22T15:51:03" + }, + "format": "4.0.0" + } +}