Windows: Cleanup devicetree + extension class

Moves a lot of the code that was parsing information about device
objects out of the `DeviceTree` plugin logic and into the
`DEVICE_OBJECT` extension class. Also moved the dict mapping integer
values to device types into the extension module.

Fixes a bug within the extension class' `get_attached_devices` method -
now, immediate children of the device in the tree are yielded, instead
of the faulty deep traversal that was occuring before. Because of this
bugfix in an extension class as well as the addition of new methods in
the extension class, this also bumps the framework minor version number.
This commit is contained in:
David McDonald
2025-03-08 02:04:40 -06:00
parent f66acd6720
commit 73e422fc97
3 changed files with 162 additions and 148 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# We use the SemVer 2.0.0 versioning scheme
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
VERSION_MINOR = 23 # Number of changes that only add to the interface
VERSION_MINOR = 24 # Number of changes that only add to the interface
VERSION_PATCH = 0 # Number of changes that do not change the interface
VERSION_SUFFIX = ""
@@ -8,70 +8,9 @@ from typing import Iterator, List, Set, Tuple
from volatility3.framework import constants, exceptions, interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.renderers import format_hints
from volatility3.framework.symbols.windows.extensions import DEVICE_OBJECT
from volatility3.framework.symbols.windows import extensions
from volatility3.plugins.windows import driverscan
DEVICE_CODES = {
0x00000027: "FILE_DEVICE_8042_PORT",
0x00000032: "FILE_DEVICE_ACPI",
0x00000029: "FILE_DEVICE_BATTERY",
0x00000001: "FILE_DEVICE_BEEP",
0x0000002A: "FILE_DEVICE_BUS_EXTENDER",
0x00000002: "FILE_DEVICE_CD_ROM",
0x00000003: "FILE_DEVICE_CD_ROM_FILE_SYSTEM",
0x00000030: "FILE_DEVICE_CHANGER",
0x00000004: "FILE_DEVICE_CONTROLLER",
0x00000005: "FILE_DEVICE_DATALINK",
0x00000006: "FILE_DEVICE_DFS",
0x00000035: "FILE_DEVICE_DFS_FILE_SYSTEM",
0x00000036: "FILE_DEVICE_DFS_VOLUME",
0x00000007: "FILE_DEVICE_DISK",
0x00000008: "FILE_DEVICE_DISK_FILE_SYSTEM",
0x00000033: "FILE_DEVICE_DVD",
0x00000009: "FILE_DEVICE_FILE_SYSTEM",
0x0000003A: "FILE_DEVICE_FIPS",
0x00000034: "FILE_DEVICE_FULLSCREEN_VIDEO",
0x0000000A: "FILE_DEVICE_INPORT_PORT",
0x0000000B: "FILE_DEVICE_KEYBOARD",
0x0000002F: "FILE_DEVICE_KS",
0x00000039: "FILE_DEVICE_KSEC",
0x0000000C: "FILE_DEVICE_MAILSLOT",
0x0000002D: "FILE_DEVICE_MASS_STORAGE",
0x0000000D: "FILE_DEVICE_MIDI_IN",
0x0000000E: "FILE_DEVICE_MIDI_OUT",
0x0000002B: "FILE_DEVICE_MODEM",
0x0000000F: "FILE_DEVICE_MOUSE",
0x00000010: "FILE_DEVICE_MULTI_UNC_PROVIDER",
0x00000011: "FILE_DEVICE_NAMED_PIPE",
0x00000012: "FILE_DEVICE_NETWORK",
0x00000013: "FILE_DEVICE_NETWORK_BROWSER",
0x00000014: "FILE_DEVICE_NETWORK_FILE_SYSTEM",
0x00000028: "FILE_DEVICE_NETWORK_REDIRECTOR",
0x00000015: "FILE_DEVICE_NULL",
0x00000016: "FILE_DEVICE_PARALLEL_PORT",
0x00000017: "FILE_DEVICE_PHYSICAL_NETCARD",
0x00000018: "FILE_DEVICE_PRINTER",
0x00000019: "FILE_DEVICE_SCANNER",
0x0000001C: "FILE_DEVICE_SCREEN",
0x00000037: "FILE_DEVICE_SERENUM",
0x0000001A: "FILE_DEVICE_SERIAL_MOUSE_PORT",
0x0000001B: "FILE_DEVICE_SERIAL_PORT",
0x00000031: "FILE_DEVICE_SMARTCARD",
0x0000002E: "FILE_DEVICE_SMB",
0x0000001D: "FILE_DEVICE_SOUND",
0x0000001E: "FILE_DEVICE_STREAMS",
0x0000001F: "FILE_DEVICE_TAPE",
0x00000020: "FILE_DEVICE_TAPE_FILE_SYSTEM",
0x00000038: "FILE_DEVICE_TERMSRV",
0x00000021: "FILE_DEVICE_TRANSPORT",
0x00000022: "FILE_DEVICE_UNKNOWN",
0x0000002C: "FILE_DEVICE_VDM",
0x00000023: "FILE_DEVICE_VIDEO",
0x00000024: "FILE_DEVICE_VIRTUAL_DISK",
0x00000025: "FILE_DEVICE_WAVE_IN",
0x00000026: "FILE_DEVICE_WAVE_OUT",
}
vollog = logging.getLogger(__name__)
@@ -101,97 +40,77 @@ class DeviceTree(interfaces.plugins.PluginInterface):
self.config["kernel"],
):
try:
try:
driver_name = driver.DriverName.get_string()
except (ValueError, exceptions.InvalidAddressException):
vollog.log(
constants.LOGLEVEL_VVVV,
f"Failed to get Driver name : {driver.vol.offset:x}",
)
driver_name = renderers.UnparsableValue()
yield (
0,
(
format_hints.Hex(driver.vol.offset),
"DRV",
driver_name,
renderers.NotApplicableValue(),
renderers.NotApplicableValue(),
renderers.NotApplicableValue(),
),
)
# Scan to get the device information of driver.
for device in driver.get_devices():
for level, (
offset,
drv_name,
dev_name,
att_drv_name,
dev_typ,
) in self._traverse_device_stack(device, driver_name, 1):
yield level, (
offset,
"DEV" if level == 1 else "ATT",
drv_name,
dev_name,
att_drv_name,
dev_typ,
)
except exceptions.InvalidAddressException:
vollog.log(
constants.LOGLEVEL_VVVV,
f"Invalid address identified in drivers and devices: {driver.vol.offset:x}",
)
continue
@staticmethod
def _traverse_device_stack(
device: DEVICE_OBJECT, driver_name: str, level: int, seen: Set[int] = set()
) -> Iterator[Tuple]:
while device and device.vol.offset not in seen:
seen.add(device.vol.offset)
try:
device_name = device.get_device_name()
driver_name = driver.DriverName.get_string()
except (ValueError, exceptions.InvalidAddressException):
vollog.log(
constants.LOGLEVEL_VVVV,
f"Failed to get Device name : {device.vol.offset:x}",
f"Failed to get Driver name : {driver.vol.offset:x}",
)
device_name = renderers.UnparsableValue()
device_type = DEVICE_CODES.get(device.DeviceType, "UNKNOWN")
att_drv_name = device.DriverObject.DriverName.get_string()
driver_name = renderers.UnparsableValue()
yield (
level,
0,
(
format_hints.Hex(device.vol.offset),
format_hints.Hex(driver.vol.offset),
"DRV",
driver_name,
device_name,
att_drv_name,
device_type,
renderers.NotApplicableValue(),
renderers.NotApplicableValue(),
renderers.NotApplicableValue(),
),
)
try:
attached = device.AttachedDevice.dereference()
yield from DeviceTree._traverse_device_stack(
attached, driver_name, level + 1, seen
)
except exceptions.InvalidAddressException:
vollog.debug(
f"Failed to dereference attached device for device at {device.vol.offset:#x}, "
"devnode may not have drivers associated with it"
)
# Scan to get the device information of driver.
for device in driver.get_devices():
for level, device_entry in self._traverse_device_tree(device, 1):
try:
device_name = device.get_device_name()
except (ValueError, exceptions.InvalidAddressException):
device_name = renderers.UnparsableValue()
try:
attached_driver_name = device.get_attached_driver_name()
except exceptions.InvalidAddressException:
attached_driver_name = renderers.UnparsableValue()
try:
device_type = device.get_device_type()
except exceptions.InvalidAddressException:
device_type = renderers.UnparsableValue()
yield level, (
format_hints.Hex(device_entry.vol.offset),
"DEV" if level == 1 else "ATT",
driver_name,
device_name,
attached_driver_name,
device_type,
)
@classmethod
def _traverse_device_tree(
cls, device: extensions.DEVICE_OBJECT, level: int, seen: Set[int] = set()
) -> Iterator[Tuple[int, extensions.DEVICE_OBJECT]]:
vollog.debug(f"Traversing device tree for device at {device.vol.offset:#x}")
while device and device.vol.offset not in seen:
seen.add(device.vol.offset)
# Yield the first device and its level
yield (
level,
device,
)
for attached in device.get_attached_devices():
# Go depth-first through all of this device's child devices
yield from cls._traverse_device_tree(attached, level + 1, seen)
try:
# Then move sideways to the next device in the current linked list
device = device.NextDevice.dereference()
except exceptions.InvalidAddressException:
vollog.debug(
f"Failed to dereference next driver in linked list at {int(device.NextDevice)}, "
"Failed to dereference next driver in linked list, "
"may have reached end of list"
)
@@ -22,9 +22,8 @@ from volatility3.framework.interfaces.objects import ObjectInterface
from volatility3.framework.layers import intel
from volatility3.framework.objects import utility
from volatility3.framework.renderers import conversion
from volatility3.framework.symbols import generic
from volatility3.framework.symbols import generic, windows
from volatility3.framework.symbols.windows.extensions import pool
from volatility3.framework.symbols import windows
vollog = logging.getLogger(__name__)
@@ -406,33 +405,129 @@ class EX_FAST_REF(objects.StructType):
)
DEVICE_CODES = {
0x00000027: "FILE_DEVICE_8042_PORT",
0x00000032: "FILE_DEVICE_ACPI",
0x00000029: "FILE_DEVICE_BATTERY",
0x00000001: "FILE_DEVICE_BEEP",
0x0000002A: "FILE_DEVICE_BUS_EXTENDER",
0x00000002: "FILE_DEVICE_CD_ROM",
0x00000003: "FILE_DEVICE_CD_ROM_FILE_SYSTEM",
0x00000030: "FILE_DEVICE_CHANGER",
0x00000004: "FILE_DEVICE_CONTROLLER",
0x00000005: "FILE_DEVICE_DATALINK",
0x00000006: "FILE_DEVICE_DFS",
0x00000035: "FILE_DEVICE_DFS_FILE_SYSTEM",
0x00000036: "FILE_DEVICE_DFS_VOLUME",
0x00000007: "FILE_DEVICE_DISK",
0x00000008: "FILE_DEVICE_DISK_FILE_SYSTEM",
0x00000033: "FILE_DEVICE_DVD",
0x00000009: "FILE_DEVICE_FILE_SYSTEM",
0x0000003A: "FILE_DEVICE_FIPS",
0x00000034: "FILE_DEVICE_FULLSCREEN_VIDEO",
0x0000000A: "FILE_DEVICE_INPORT_PORT",
0x0000000B: "FILE_DEVICE_KEYBOARD",
0x0000002F: "FILE_DEVICE_KS",
0x00000039: "FILE_DEVICE_KSEC",
0x0000000C: "FILE_DEVICE_MAILSLOT",
0x0000002D: "FILE_DEVICE_MASS_STORAGE",
0x0000000D: "FILE_DEVICE_MIDI_IN",
0x0000000E: "FILE_DEVICE_MIDI_OUT",
0x0000002B: "FILE_DEVICE_MODEM",
0x0000000F: "FILE_DEVICE_MOUSE",
0x00000010: "FILE_DEVICE_MULTI_UNC_PROVIDER",
0x00000011: "FILE_DEVICE_NAMED_PIPE",
0x00000012: "FILE_DEVICE_NETWORK",
0x00000013: "FILE_DEVICE_NETWORK_BROWSER",
0x00000014: "FILE_DEVICE_NETWORK_FILE_SYSTEM",
0x00000028: "FILE_DEVICE_NETWORK_REDIRECTOR",
0x00000015: "FILE_DEVICE_NULL",
0x00000016: "FILE_DEVICE_PARALLEL_PORT",
0x00000017: "FILE_DEVICE_PHYSICAL_NETCARD",
0x00000018: "FILE_DEVICE_PRINTER",
0x00000019: "FILE_DEVICE_SCANNER",
0x0000001C: "FILE_DEVICE_SCREEN",
0x00000037: "FILE_DEVICE_SERENUM",
0x0000001A: "FILE_DEVICE_SERIAL_MOUSE_PORT",
0x0000001B: "FILE_DEVICE_SERIAL_PORT",
0x00000031: "FILE_DEVICE_SMARTCARD",
0x0000002E: "FILE_DEVICE_SMB",
0x0000001D: "FILE_DEVICE_SOUND",
0x0000001E: "FILE_DEVICE_STREAMS",
0x0000001F: "FILE_DEVICE_TAPE",
0x00000020: "FILE_DEVICE_TAPE_FILE_SYSTEM",
0x00000038: "FILE_DEVICE_TERMSRV",
0x00000021: "FILE_DEVICE_TRANSPORT",
0x00000022: "FILE_DEVICE_UNKNOWN",
0x0000002C: "FILE_DEVICE_VDM",
0x00000023: "FILE_DEVICE_VIDEO",
0x00000024: "FILE_DEVICE_VIRTUAL_DISK",
0x00000025: "FILE_DEVICE_WAVE_IN",
0x00000026: "FILE_DEVICE_WAVE_OUT",
}
class DEVICE_OBJECT(objects.StructType, pool.ExecutiveObject):
"""A class for kernel device objects."""
def get_device_name(self) -> str:
"""Get device's name from the object header."""
"""Get device's name from the object header.
Raises:
ValueError if the device has no _OBJECT_HEADER_NAME_INFO member
InvalidAddressException if the name cannot be dereferenced due to smear/swapped pages
"""
header = self.get_object_header()
return header.NameInfo.Name.String # type: ignore
def get_attached_devices(self) -> Generator[ObjectInterface, None, None]:
"""Enumerate the attached device's objects"""
def get_attached_driver_name(self) -> str:
"""Gets the name of the driver that is attached to this device.
Raises:
InvalidAddressException if the name cannot be dereferenced due to smear/swapped pages
"""
return self.DriverObject.DriverName.get_string()
def get_device_type(self) -> str:
"""Gets the device type as a readable string.
Raises:
InvalidAddressException if the DeviceType member cannot be read.
"""
return DEVICE_CODES.get(self.DeviceType, "UNKNOWN")
def get_attached_devices(self) -> Iterator["DEVICE_OBJECT"]:
"""Enumerate the attached device's objects.
This only yields devices that are direct children of this devnode. In order to visit
nodes farther down the tree, this method must be called on each of those children
(and their children, and so on) to fully enumerate the device subtree. Child enumeration
will stop if a device is seen twice or at the first InvalidAddressException, but the
InvalidAddressException is not raised to the calling function.
"""
seen = set()
try:
device = self.AttachedDevice.dereference()
except exceptions.InvalidAddressException:
vollog.debug(
f"No attached device dereferenced for DEVICE_OBJECT at {self.vol.offset:#x}"
)
return
while device:
if device.vol.offset in seen:
break
while device and device.vol.offset not in seen:
seen.add(device.vol.offset)
yield device
try:
device = device.AttachedDevice.dereference()
device = device.NextDevice.dereference()
except exceptions.InvalidAddressException:
vollog.debug(
f"Failed to dereference next device "
f"for device at {device.vol.offset:#x}, may have reached list end"
)
return