Add in MSF and Stream layer support.

This commit is contained in:
Mike Auty
2019-07-30 22:14:19 +01:00
committed by ikelos
parent 3da5f63393
commit 80c693a91a
3 changed files with 114 additions and 5 deletions
+109 -2
View File
@@ -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)
@@ -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
@@ -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": {