From 244751e9aebf30c2f592c576b3a57ddbbe24b9ed Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 6 Mar 2022 18:48:00 +0000 Subject: [PATCH] Layers: Better checks on PAE page tables This checks that the very top level table points to the next four pages, as we'd expected in general. This relies on the same assumptions as the existing PAE detection did, ie that the PAE page_map maps the next four pages immediately. Previously we didn't check that the top page was valid, once we found the self-referential pointer. This adds in an appropriate check to reduce false positives. Closes #631. --- volatility3/framework/automagic/windows.py | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 71548ca40..b93ee244c 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -28,9 +28,9 @@ The self-referential indices for older versions of windows are listed below: """ import logging import struct -from typing import Generator, List, Optional, Tuple, Type, Iterable +from typing import Generator, Iterable, List, Optional, Tuple, Type -from volatility3.framework import interfaces, layers, constants +from volatility3.framework import constants, interfaces, layers from volatility3.framework.configuration import requirements from volatility3.framework.layers import intel @@ -116,10 +116,27 @@ class DtbSelfRefPae(DtbSelfReferential): mask = 0x3FFFFFFFFFF000, reserved_bits = 0x0) - def __call__(self, *args, **kwargs): - dtb = super().__call__(*args, **kwargs) + @staticmethod + def _and_bytes(abytes, bbytes): + return bytes([a & b for a, b in zip(abytes[::-1], bbytes[::-1])][::-1]) + + def __call__(self, data: bytes, data_offset: int, page_offset: int) -> Optional[Tuple[int, int]]: + dtb = super().__call__(data, data_offset, page_offset) if dtb: - return dtb[0] - 0x4000, dtb[1] + # Find the top page + top_pae_page = dtb[0] - 0x4000 + # The top page should map to the next four pages after it + # Build what we expect the page table to be + expected_table = b''.join([struct.pack(self.ptr_struct, top_pae_page + (i * 0x1000)) for i in range(1, 5)]) + # Mask off the page bits of top level page map + page_table_mask = b"\x00\xf0\xff\xff\xff\xff\xff\xff" * 4 + page_table = data[top_pae_page - data_offset: top_pae_page - data_offset + (4 * self.ptr_size)] + # Compare them + anded_bytes = self._and_bytes(page_table, page_table_mask) + if (anded_bytes == expected_table): + return top_pae_page, dtb[1] + # Return None since the dtb value *isn't* None + return None return dtb