From 2371c1158d954b706efd877c23526fbd88b06260 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 26 Aug 2021 11:50:04 +0100 Subject: [PATCH] Windows: Ensure DTBs don't have all valid entries We were finding that PAE results were being masked by certain pages with 64bit results. The DTBs could be distinguished from good DTBs because every entry was valid (which for a 64-bit or even 32-bit DTB is extremely unlikely except on systems with *vast* quanties of memory. As such, this now tests for all entries being allocated and doesn't return the DTB if this is the case. --- volatility3/framework/automagic/windows.py | 57 +++++----------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 147602fac..38da63707 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -54,6 +54,10 @@ class DtbTest: self.ptr_reference = ptr_reference self.mask = mask self.page_size: int = layer_type.page_size + # This calculates the *wrong* value for PAE systems, + # but they can have all four entries filled, so we'd want this test off anyway + self.num_entries = self.page_size // self.ptr_size + def _unpack(self, value: bytes) -> int: return struct.unpack("<" + self.ptr_struct, value)[0] @@ -109,7 +113,8 @@ class DtbTest: # print(hex(dtb), usr_count, sup_count, usr_count + sup_count) # We sometimes find bogus DTBs at 0x16000 with a very low sup_count and 0 usr_count # I have a winxpsp2-x64 image with identical usr/sup counts at 0x16000 and 0x24c00 as well as the actual 0x3c3000 - if usr_count or sup_count > 5: + # We almost never have every single entry allocated + if usr_count or sup_count > 5 and usr_count + sup_count < self.num_entries: return dtb, None return None @@ -120,7 +125,8 @@ class DtbTest32bit(DtbTest): super().__init__(layer_type = layers.intel.WindowsIntel, ptr_struct = "I", ptr_reference = [0x300], - mask = 0xFFFFF000) + mask = 0xFFFFF000, + num_entries = 1024) class DtbTest64bit(DtbTest): @@ -128,8 +134,9 @@ class DtbTest64bit(DtbTest): def __init__(self) -> None: super().__init__(layer_type = layers.intel.WindowsIntel32e, ptr_struct = "Q", - ptr_reference = range(0x1E0, 0x1FF), - mask = 0x3FFFFFFFFFF000) + ptr_reference = range(0x1ff, 0x100, -1), + mask = 0x3FFFFFFFFFF000, + num_entries = 512) # As of Windows-10 RS1+, the ptr_reference is randomized: # https://blahcat.github.io/2020/06/15/playing_with_self_reference_pml4_entry/ @@ -172,46 +179,6 @@ class DtbTestPae(DtbTest): return None -class DtbSelfReferential(DtbTest): - """A generic DTB test which looks for a self-referential pointer at *any* - index within the page.""" - - def __init__(self, layer_type: Type[layers.intel.Intel], ptr_struct: str, ptr_reference: int, mask: int) -> None: - super().__init__(layer_type = layer_type, ptr_struct = ptr_struct, ptr_reference = ptr_reference, mask = mask) - - def __call__(self, data: bytes, data_offset: int, page_offset: int) -> Optional[Tuple[int, int]]: - page = data[page_offset:page_offset + self.page_size] - if not page: - return None - ref_pages = set() - for ref in range(0, self.page_size, self.ptr_size): - ptr_data = page[ref:ref + self.ptr_size] - if len(ptr_data) == self.ptr_size: - ptr, = struct.unpack(self.ptr_struct, ptr_data) - if ((ptr & self.mask) == (data_offset + page_offset)) and (data_offset + page_offset > 0): - ref_pages.add(ref) - # The DTB is extremely unlikely to refer back to itself. so the number of reference should always be exactly 1 - if len(ref_pages) == 1: - return (data_offset + page_offset), ref_pages.pop() - return None - - -class DtbSelfRef32bit(DtbSelfReferential): - - def __init__(self): - super().__init__(layer_type = layers.intel.WindowsIntel, - ptr_struct = "I", - ptr_reference = 0x300, - mask = 0xFFFFF000) - - -class DtbSelfRef64bit(DtbSelfReferential): - - def __init__(self) -> None: - super().__init__(layer_type = layers.intel.WindowsIntel32e, - ptr_struct = "Q", - ptr_reference = 0x1ED, - mask = 0x3FFFFFFFFFF000) class PageMapScanner(interfaces.layers.ScannerInterface): @@ -359,7 +326,7 @@ class WindowsIntelStacker(interfaces.automagic.StackerLayerInterface): vollog.debug("Self-referential pointer not in well-known location, moving to recent windows heuristic") # There is a very high chance that the DTB will live in this narrow segment, assuming we couldn't find it previously hits = context.layers[layer_name].scan(context, - PageMapScanner([DtbSelfRef64bit()]), + PageMapScanner([DtbTest64bit()]), sections = [(0x1a0000, 0x50000)], progress_callback = progress_callback) # Flatten the generator