mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-10-01 14:04:54 +02:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user