diff --git a/volatility3/framework/constants/_version.py b/volatility3/framework/constants/_version.py index aa8e8936fe..1ea59c0689 100644 --- a/volatility3/framework/constants/_version.py +++ b/volatility3/framework/constants/_version.py @@ -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 = "" diff --git a/volatility3/framework/plugins/windows/devicetree.py b/volatility3/framework/plugins/windows/devicetree.py index 012a8750d8..648a402441 100644 --- a/volatility3/framework/plugins/windows/devicetree.py +++ b/volatility3/framework/plugins/windows/devicetree.py @@ -3,75 +3,14 @@ # import logging +from typing import Iterator, List, Set, Tuple -from typing import Iterator, List, Tuple - -from volatility3.framework import constants, renderers, exceptions, interfaces +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 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,90 +40,79 @@ def _generator(self) -> Iterator[Tuple]: self.config["kernel"], ): try: - try: - driver_name = driver.get_driver_name() - 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(), - ), + 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}", ) - - # Scan to get the device information of driver. - for device in driver.get_devices(): + 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, device_entry in self._traverse_device_tree(device, 1): try: device_name = device.get_device_name() except (ValueError, exceptions.InvalidAddressException): - vollog.log( - constants.LOGLEVEL_VVVV, - f"Failed to get Device name : {device.vol.offset:x}", - ) device_name = renderers.UnparsableValue() - device_type = DEVICE_CODES.get(device.DeviceType, "UNKNOWN") - - yield ( - 1, - ( - format_hints.Hex(driver.vol.offset), - "DEV", - driver_name, - device_name, - renderers.NotApplicableValue(), - device_type, - ), + 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, ) - # Scan to get the attached devices information of device. - for level, attached_device in enumerate( - device.get_attached_devices(), start=2 - ): - try: - device_name = attached_device.get_device_name() - except (ValueError, exceptions.InvalidAddressException): - vollog.log( - constants.LOGLEVEL_VVVV, - f"Failed to get Attached Device Name: {attached_device.vol.offset:x}", - ) - device_name = renderers.UnparsableValue() - - attached_device_driver_name = ( - attached_device.DriverObject.DriverName.get_string() - ) - attached_device_type = DEVICE_CODES.get( - attached_device.DeviceType, "UNKNOWN" - ) - - yield ( - level, - ( - format_hints.Hex(driver.vol.offset), - "ATT", - driver_name, - device_name, - attached_device_driver_name, - attached_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.log( - constants.LOGLEVEL_VVVV, - f"Invalid address identified in drivers and devices: {driver.vol.offset:x}", + vollog.debug( + "Failed to dereference next driver in linked list, " + "may have reached end of list" ) - continue def run(self) -> renderers.TreeGrid: return renderers.TreeGrid( diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 933178c914..9b2962ac06 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -22,9 +22,8 @@ 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 @@ def dereference(self) -> interfaces.objects.ObjectInterface: ) +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