diff --git a/volatility/framework/layers/msf.py b/volatility/framework/layers/msf.py index 3106db217..4dbf63ba9 100644 --- a/volatility/framework/layers/msf.py +++ b/volatility/framework/layers/msf.py @@ -1,3 +1,4 @@ +import math from typing import Optional, Dict, Any, List, Iterable, Tuple from volatility.framework import interfaces, constants @@ -25,6 +26,62 @@ class PdbMSF(interfaces.layers.TranslationLayerInterface): if response is None: raise ValueError("Could not find a suitable header") self._version, self._header = response + self._streams = {} # type: Dict[int, Optional[PdbMSFStream]] + + def read_streams(self): + # Recover the root table, by recovering the root table index table... + module = self.context.module(self._pdb_table_name, self._base_layer, offset = 0) + entry_size = module.get_type("unsigned long").size + + root_table_num_pages = math.ceil(self._header.StreamInfo.StreamInfoSize / self._header.PageSize) + root_index_size = math.ceil((root_table_num_pages * entry_size) / self._header.PageSize) + root_index = module.object( + type_name = "array", + layer_name = self._base_layer, + offset = self._header.vol.size, + count = root_index_size, + subtype = module.get_type("unsigned long")) + root_index_layer_name = self.create_stream_from_pages("root_index", [x for x in root_index]) + + module = self.context.module(self._pdb_table_name, root_index_layer_name, offset = 0) + root_pages = self.context.object( + symbol = self._pdb_table_name + constants.BANG + "array", + layer_name = root_index_layer_name, + offset = 0, + count = root_table_num_pages, + subtype = module.get_type("unsigned long")) + root_layer_name = self.create_stream_from_pages("root", [x for x in root_pages]) + + module = self.context.module(self._pdb_table_name, root_layer_name, offset = 0) + num_streams = module.object(type_name = "unsigned long", offset = 0) + stream_sizes = module.object( + type_name = "array", offset = entry_size, count = num_streams, subtype = module.get_type("unsigned long")) + + current_offset = (num_streams + 1) * entry_size + + for stream in range(num_streams): + list_size = math.ceil(stream_sizes[stream] / self.page_size) + if list_size: + stream_page_list = module.object( + type_name = "array", + offset = current_offset, + count = list_size, + subtype = module.get_type("unsigned long")) + current_offset += (list_size * entry_size) + self._streams[stream] = self.create_stream_from_pages("stream", [x for x in stream_page_list]) + else: + self._streams[stream] = None + + def create_stream_from_pages(self, stream_name: str, pages: List[int]) -> str: + # Construct a root layer based on a number of pages + layer_name = self.context.memory.free_layer_name(self.name + "_" + stream_name) + path_join = interfaces.configuration.path_join + config_path = path_join(self.config_path, stream_name) + self.context.config[path_join(config_path, 'base_layer')] = self.name + self.context.config[path_join(config_path, 'pages')] = pages + layer = PdbMSFStream(self.context, config_path, layer_name) + self.context.memory.add_layer(layer) + return layer_name def _check_header(self) -> Optional[Tuple[str, interfaces.objects.ObjectInterface]]: """Verifies the header of the PDB file and returns the version of the file""" @@ -32,9 +89,14 @@ class PdbMSF(interfaces.layers.TranslationLayerInterface): 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, current_header + if not (current_header.PageSize < 0x100 or current_header.PageSize > (128 * 0x10000)): + return header, current_header return None + @property + def page_size(self): + return self._header.PageSize + @property def dependencies(self) -> List[str]: """Returns a list of the lower layers that this layer is dependent upon""" @@ -60,4 +122,49 @@ class PdbMSF(interfaces.layers.TranslationLayerInterface): class PdbMSFStream(interfaces.layers.TranslationLayerInterface): - pass + + 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._pages = self.config.get("pages", None) + if not self._pages: + raise ValueError("Invalid/no pages specified") + if not isinstance(self._pdb_layer, PdbMSF): + raise TypeError("Base Layer must be a PdbMSF layer") + + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [requirements.ListRequirement(name = 'pages', element_type = int, min_elements = 1)] + + def mapping(self, offset: int, length: int, ignore_errors: bool = False) -> Iterable[Tuple[int, int, int, str]]: + returned = 0 + page_size = self._pdb_layer.page_size + while length > 0: + page = math.floor((offset + returned) / page_size) + page_position = ((offset + returned) % page_size) + chunk_size = min(page_size - page_position, length) + yield (offset + returned, (self._pages[page] * page_size) + page_position, chunk_size, self._base_layer) + returned += chunk_size + length -= chunk_size + + @property + def dependencies(self) -> List[str]: + return [self._base_layer] + + def is_valid(self, offset: int, length: int = 1) -> bool: + return self.minimum_address <= offset and offset + length < self.maximum_address + + @property + def minimum_address(self) -> int: + return 0 + + @property + def maximum_address(self) -> int: + return len(self._pages) * self._pdb_layer.page_size + + @property + def _pdb_layer(self) -> PdbMSF: + return self._context.memory.get(self._base_layer, None) diff --git a/volatility/framework/symbols/windows/mspdb.py b/volatility/framework/symbols/windows/mspdb.py index f0dadfa29..756b14373 100644 --- a/volatility/framework/symbols/windows/mspdb.py +++ b/volatility/framework/symbols/windows/mspdb.py @@ -39,6 +39,8 @@ class PdbReader: msf_layer = msf.PdbMSF(new_context, msf_config_path, msf_layer_name) new_context.add_layer(msf_layer) + msf_layer.read_streams() + return msf_layer_name, new_context diff --git a/volatility/framework/symbols/windows/pdb.json b/volatility/framework/symbols/windows/pdb.json index 149175ce1..e8a0b26f4 100644 --- a/volatility/framework/symbols/windows/pdb.json +++ b/volatility/framework/symbols/windows/pdb.json @@ -56,7 +56,7 @@ "name": "unsigned short" } }, - "StreamTableStreamInfo": { + "StreamInfo": { "offset": 52, "type": { "kind": "struct", @@ -101,7 +101,7 @@ "name": "unsigned long" } }, - "StreamTableStreamInfo": { + "StreamInfo": { "offset": 44, "type": { "kind": "struct", @@ -110,7 +110,7 @@ } }, "kind": "struct", - "size": 60 + "size": 52 } }, "enums": {