Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 43 additions & 22 deletions volatility3/framework/automagic/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,58 @@

import logging
import struct
from typing import Generator, Iterable, List, Optional, Tuple, Type
from typing import Generator, Iterable, List, Optional, Tuple, Type, Union

from volatility3.framework import constants, interfaces, layers
from volatility3.framework import constants, exceptions, interfaces, layers
from volatility3.framework.configuration import requirements
from volatility3.framework.layers import intel

vollog = logging.getLogger(__name__)


class Intel32LayerCheck:
KUSER_USER_SPACE_ADDR = 0x7FFE0000

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document where these values come from (and why they won't change/shift between versions)?

KUSER_KERNEL_SPACE_ADDR = 0xFFDF0000

@classmethod
def check(cls, layer: Union[intel.Intel, intel.IntelPAE]):
"""Generates a single response of True or False depending on whether the space is a valid Windows AS"""
# This constraint verifies that _KUSER_SHARED_DATA is shared
# between user and kernel address spaces.
try:
uaddr = layer._translate(cls.KUSER_USER_SPACE_ADDR)[0]
kaddr = layer._translate(cls.KUSER_KERNEL_SPACE_ADDR)[0]
if kaddr != 0 and kaddr == uaddr:
return True
except (
exceptions.PagedInvalidAddressException,
exceptions.InvalidAddressException,
):

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
pass

return False


class IntelLayerCheck(Intel32LayerCheck):
KUSER_USER_SPACE_ADDR = 0x7FFE0000
KUSER_KERNEL_SPACE_ADDR = 0xFFFFF78000000000


class DtbSelfReferential:
"""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],
layer_check: Union[Intel32LayerCheck.check, IntelLayerCheck.check],
ptr_struct: str,
mask: int,
valid_range: Iterable[int],
reserved_bits: int,
) -> None:
self.layer_type = layer_type
self.layer_check = layer_check
self.ptr_struct = ptr_struct
self.ptr_size = struct.calcsize(ptr_struct)
self.mask = mask
Expand Down Expand Up @@ -92,6 +122,7 @@
def __init__(self):
super().__init__(
layer_type=layers.intel.WindowsIntel,
layer_check=Intel32LayerCheck.check,
ptr_struct="I",
mask=0xFFFFF000,
valid_range=[0x300],
Expand All @@ -103,6 +134,7 @@
def __init__(self) -> None:
super().__init__(
layer_type=layers.intel.WindowsIntel32e,
layer_check=IntelLayerCheck.check,
ptr_struct="Q",
mask=0x3FFFFFFFFFF000,
valid_range=range(0x100, 0x1FF),
Expand All @@ -114,6 +146,7 @@
def __init__(self) -> None:
super().__init__(
layer_type=layers.intel.WindowsIntel32e,
layer_check=IntelLayerCheck.check,
ptr_struct="Q",
mask=0x3FFFFFFFFFF000,
valid_range=[0x1ED],
Expand All @@ -125,6 +158,7 @@
def __init__(self) -> None:
super().__init__(
layer_type=layers.intel.WindowsIntelPAE,
layer_check=Intel32LayerCheck.check,
ptr_struct="Q",
valid_range=[0x3],
mask=0x3FFFFFFFFFF000,
Expand Down Expand Up @@ -317,18 +351,6 @@
)
return max_ptr

def page_table_is_dummy(page_table, ptr_size: int):
"""Verify that a page table has at least 12 valid pointers"""
valid_pointers = 0
for _ in get_valid_page_table_pointers(page_table, ptr_size):
valid_pointers += 1
# 10 is an arbitrary constant
if valid_pointers >= 10:
# Do not consume the entire generator to enhance performance
return False
vollog.debug(f"Found {valid_pointers} valid pointers")
return True

hits = sorted(list(hits), key=sort_by_tests)

vollog.debug(f"WindowsIntelStacker hits: {hits}")
Expand All @@ -337,13 +359,6 @@
# Turn the page tables into integers and find the largest one
page_table = base_layer.read(page_map_offset, 0x1000)
ptr_size = struct.calcsize(test.ptr_struct)
# Modern windows can have a dummy page table with only about 2 entries, so sanity check
if page_table_is_dummy(page_table, ptr_size):
vollog.debug(
f"DTB {page_map_offset:x} contains less than 12 valid pointers, ignoring"
)
continue

max_pointer = get_max_pointer(page_table, test, ptr_size)

if max_pointer <= base_layer.maximum_address:
Expand All @@ -362,12 +377,18 @@
config_path, "page_map_offset"
)
] = page_map_offset
layer = test.layer_type(
tmp_layer = test.layer_type(
context,
config_path=config_path,
name=new_layer_name,
metadata={"os": "Windows"},
)
if not test.layer_check(tmp_layer):
vollog.debug(
f"DTB {page_map_offset:x} failed {test.layer_type.__name__} _KUSER_SHARED_DATA check, ignoring"
)
continue
layer = tmp_layer
break
else:
vollog.debug(
Expand Down
Loading