Skip to content

Commit 73e422f

Browse files
committed
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.
1 parent f66acd6 commit 73e422f

File tree

3 files changed

+162
-148
lines changed

3 files changed

+162
-148
lines changed

volatility3/framework/constants/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# We use the SemVer 2.0.0 versioning scheme
22
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
3-
VERSION_MINOR = 23 # Number of changes that only add to the interface
3+
VERSION_MINOR = 24 # Number of changes that only add to the interface
44
VERSION_PATCH = 0 # Number of changes that do not change the interface
55
VERSION_SUFFIX = ""
66

volatility3/framework/plugins/windows/devicetree.py

+57-138
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,9 @@
88
from volatility3.framework import constants, exceptions, interfaces, renderers
99
from volatility3.framework.configuration import requirements
1010
from volatility3.framework.renderers import format_hints
11-
from volatility3.framework.symbols.windows.extensions import DEVICE_OBJECT
11+
from volatility3.framework.symbols.windows import extensions
1212
from volatility3.plugins.windows import driverscan
1313

14-
DEVICE_CODES = {
15-
0x00000027: "FILE_DEVICE_8042_PORT",
16-
0x00000032: "FILE_DEVICE_ACPI",
17-
0x00000029: "FILE_DEVICE_BATTERY",
18-
0x00000001: "FILE_DEVICE_BEEP",
19-
0x0000002A: "FILE_DEVICE_BUS_EXTENDER",
20-
0x00000002: "FILE_DEVICE_CD_ROM",
21-
0x00000003: "FILE_DEVICE_CD_ROM_FILE_SYSTEM",
22-
0x00000030: "FILE_DEVICE_CHANGER",
23-
0x00000004: "FILE_DEVICE_CONTROLLER",
24-
0x00000005: "FILE_DEVICE_DATALINK",
25-
0x00000006: "FILE_DEVICE_DFS",
26-
0x00000035: "FILE_DEVICE_DFS_FILE_SYSTEM",
27-
0x00000036: "FILE_DEVICE_DFS_VOLUME",
28-
0x00000007: "FILE_DEVICE_DISK",
29-
0x00000008: "FILE_DEVICE_DISK_FILE_SYSTEM",
30-
0x00000033: "FILE_DEVICE_DVD",
31-
0x00000009: "FILE_DEVICE_FILE_SYSTEM",
32-
0x0000003A: "FILE_DEVICE_FIPS",
33-
0x00000034: "FILE_DEVICE_FULLSCREEN_VIDEO",
34-
0x0000000A: "FILE_DEVICE_INPORT_PORT",
35-
0x0000000B: "FILE_DEVICE_KEYBOARD",
36-
0x0000002F: "FILE_DEVICE_KS",
37-
0x00000039: "FILE_DEVICE_KSEC",
38-
0x0000000C: "FILE_DEVICE_MAILSLOT",
39-
0x0000002D: "FILE_DEVICE_MASS_STORAGE",
40-
0x0000000D: "FILE_DEVICE_MIDI_IN",
41-
0x0000000E: "FILE_DEVICE_MIDI_OUT",
42-
0x0000002B: "FILE_DEVICE_MODEM",
43-
0x0000000F: "FILE_DEVICE_MOUSE",
44-
0x00000010: "FILE_DEVICE_MULTI_UNC_PROVIDER",
45-
0x00000011: "FILE_DEVICE_NAMED_PIPE",
46-
0x00000012: "FILE_DEVICE_NETWORK",
47-
0x00000013: "FILE_DEVICE_NETWORK_BROWSER",
48-
0x00000014: "FILE_DEVICE_NETWORK_FILE_SYSTEM",
49-
0x00000028: "FILE_DEVICE_NETWORK_REDIRECTOR",
50-
0x00000015: "FILE_DEVICE_NULL",
51-
0x00000016: "FILE_DEVICE_PARALLEL_PORT",
52-
0x00000017: "FILE_DEVICE_PHYSICAL_NETCARD",
53-
0x00000018: "FILE_DEVICE_PRINTER",
54-
0x00000019: "FILE_DEVICE_SCANNER",
55-
0x0000001C: "FILE_DEVICE_SCREEN",
56-
0x00000037: "FILE_DEVICE_SERENUM",
57-
0x0000001A: "FILE_DEVICE_SERIAL_MOUSE_PORT",
58-
0x0000001B: "FILE_DEVICE_SERIAL_PORT",
59-
0x00000031: "FILE_DEVICE_SMARTCARD",
60-
0x0000002E: "FILE_DEVICE_SMB",
61-
0x0000001D: "FILE_DEVICE_SOUND",
62-
0x0000001E: "FILE_DEVICE_STREAMS",
63-
0x0000001F: "FILE_DEVICE_TAPE",
64-
0x00000020: "FILE_DEVICE_TAPE_FILE_SYSTEM",
65-
0x00000038: "FILE_DEVICE_TERMSRV",
66-
0x00000021: "FILE_DEVICE_TRANSPORT",
67-
0x00000022: "FILE_DEVICE_UNKNOWN",
68-
0x0000002C: "FILE_DEVICE_VDM",
69-
0x00000023: "FILE_DEVICE_VIDEO",
70-
0x00000024: "FILE_DEVICE_VIRTUAL_DISK",
71-
0x00000025: "FILE_DEVICE_WAVE_IN",
72-
0x00000026: "FILE_DEVICE_WAVE_OUT",
73-
}
74-
7514
vollog = logging.getLogger(__name__)
7615

7716

@@ -101,97 +40,77 @@ def _generator(self) -> Iterator[Tuple]:
10140
self.config["kernel"],
10241
):
10342
try:
104-
try:
105-
driver_name = driver.DriverName.get_string()
106-
except (ValueError, exceptions.InvalidAddressException):
107-
vollog.log(
108-
constants.LOGLEVEL_VVVV,
109-
f"Failed to get Driver name : {driver.vol.offset:x}",
110-
)
111-
driver_name = renderers.UnparsableValue()
112-
113-
yield (
114-
0,
115-
(
116-
format_hints.Hex(driver.vol.offset),
117-
"DRV",
118-
driver_name,
119-
renderers.NotApplicableValue(),
120-
renderers.NotApplicableValue(),
121-
renderers.NotApplicableValue(),
122-
),
123-
)
124-
125-
# Scan to get the device information of driver.
126-
for device in driver.get_devices():
127-
for level, (
128-
offset,
129-
drv_name,
130-
dev_name,
131-
att_drv_name,
132-
dev_typ,
133-
) in self._traverse_device_stack(device, driver_name, 1):
134-
yield level, (
135-
offset,
136-
"DEV" if level == 1 else "ATT",
137-
drv_name,
138-
dev_name,
139-
att_drv_name,
140-
dev_typ,
141-
)
142-
143-
except exceptions.InvalidAddressException:
144-
vollog.log(
145-
constants.LOGLEVEL_VVVV,
146-
f"Invalid address identified in drivers and devices: {driver.vol.offset:x}",
147-
)
148-
continue
149-
150-
@staticmethod
151-
def _traverse_device_stack(
152-
device: DEVICE_OBJECT, driver_name: str, level: int, seen: Set[int] = set()
153-
) -> Iterator[Tuple]:
154-
while device and device.vol.offset not in seen:
155-
seen.add(device.vol.offset)
156-
try:
157-
device_name = device.get_device_name()
43+
driver_name = driver.DriverName.get_string()
15844
except (ValueError, exceptions.InvalidAddressException):
15945
vollog.log(
16046
constants.LOGLEVEL_VVVV,
161-
f"Failed to get Device name : {device.vol.offset:x}",
47+
f"Failed to get Driver name : {driver.vol.offset:x}",
16248
)
163-
device_name = renderers.UnparsableValue()
164-
165-
device_type = DEVICE_CODES.get(device.DeviceType, "UNKNOWN")
166-
167-
att_drv_name = device.DriverObject.DriverName.get_string()
49+
driver_name = renderers.UnparsableValue()
16850

16951
yield (
170-
level,
52+
0,
17153
(
172-
format_hints.Hex(device.vol.offset),
54+
format_hints.Hex(driver.vol.offset),
55+
"DRV",
17356
driver_name,
174-
device_name,
175-
att_drv_name,
176-
device_type,
57+
renderers.NotApplicableValue(),
58+
renderers.NotApplicableValue(),
59+
renderers.NotApplicableValue(),
17760
),
17861
)
179-
try:
180-
attached = device.AttachedDevice.dereference()
181-
yield from DeviceTree._traverse_device_stack(
182-
attached, driver_name, level + 1, seen
183-
)
184-
except exceptions.InvalidAddressException:
185-
vollog.debug(
186-
f"Failed to dereference attached device for device at {device.vol.offset:#x}, "
187-
"devnode may not have drivers associated with it"
188-
)
62+
63+
# Scan to get the device information of driver.
64+
for device in driver.get_devices():
65+
for level, device_entry in self._traverse_device_tree(device, 1):
66+
try:
67+
device_name = device.get_device_name()
68+
except (ValueError, exceptions.InvalidAddressException):
69+
device_name = renderers.UnparsableValue()
70+
71+
try:
72+
attached_driver_name = device.get_attached_driver_name()
73+
except exceptions.InvalidAddressException:
74+
attached_driver_name = renderers.UnparsableValue()
75+
76+
try:
77+
device_type = device.get_device_type()
78+
except exceptions.InvalidAddressException:
79+
device_type = renderers.UnparsableValue()
80+
81+
yield level, (
82+
format_hints.Hex(device_entry.vol.offset),
83+
"DEV" if level == 1 else "ATT",
84+
driver_name,
85+
device_name,
86+
attached_driver_name,
87+
device_type,
88+
)
89+
90+
@classmethod
91+
def _traverse_device_tree(
92+
cls, device: extensions.DEVICE_OBJECT, level: int, seen: Set[int] = set()
93+
) -> Iterator[Tuple[int, extensions.DEVICE_OBJECT]]:
94+
vollog.debug(f"Traversing device tree for device at {device.vol.offset:#x}")
95+
while device and device.vol.offset not in seen:
96+
seen.add(device.vol.offset)
97+
98+
# Yield the first device and its level
99+
yield (
100+
level,
101+
device,
102+
)
103+
104+
for attached in device.get_attached_devices():
105+
# Go depth-first through all of this device's child devices
106+
yield from cls._traverse_device_tree(attached, level + 1, seen)
189107

190108
try:
109+
# Then move sideways to the next device in the current linked list
191110
device = device.NextDevice.dereference()
192111
except exceptions.InvalidAddressException:
193112
vollog.debug(
194-
f"Failed to dereference next driver in linked list at {int(device.NextDevice)}, "
113+
"Failed to dereference next driver in linked list, "
195114
"may have reached end of list"
196115
)
197116

volatility3/framework/symbols/windows/extensions/__init__.py

+104-9
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
from volatility3.framework.layers import intel
2323
from volatility3.framework.objects import utility
2424
from volatility3.framework.renderers import conversion
25-
from volatility3.framework.symbols import generic
25+
from volatility3.framework.symbols import generic, windows
2626
from volatility3.framework.symbols.windows.extensions import pool
27-
from volatility3.framework.symbols import windows
2827

2928
vollog = logging.getLogger(__name__)
3029

@@ -406,33 +405,129 @@ def dereference(self) -> interfaces.objects.ObjectInterface:
406405
)
407406

408407

408+
DEVICE_CODES = {
409+
0x00000027: "FILE_DEVICE_8042_PORT",
410+
0x00000032: "FILE_DEVICE_ACPI",
411+
0x00000029: "FILE_DEVICE_BATTERY",
412+
0x00000001: "FILE_DEVICE_BEEP",
413+
0x0000002A: "FILE_DEVICE_BUS_EXTENDER",
414+
0x00000002: "FILE_DEVICE_CD_ROM",
415+
0x00000003: "FILE_DEVICE_CD_ROM_FILE_SYSTEM",
416+
0x00000030: "FILE_DEVICE_CHANGER",
417+
0x00000004: "FILE_DEVICE_CONTROLLER",
418+
0x00000005: "FILE_DEVICE_DATALINK",
419+
0x00000006: "FILE_DEVICE_DFS",
420+
0x00000035: "FILE_DEVICE_DFS_FILE_SYSTEM",
421+
0x00000036: "FILE_DEVICE_DFS_VOLUME",
422+
0x00000007: "FILE_DEVICE_DISK",
423+
0x00000008: "FILE_DEVICE_DISK_FILE_SYSTEM",
424+
0x00000033: "FILE_DEVICE_DVD",
425+
0x00000009: "FILE_DEVICE_FILE_SYSTEM",
426+
0x0000003A: "FILE_DEVICE_FIPS",
427+
0x00000034: "FILE_DEVICE_FULLSCREEN_VIDEO",
428+
0x0000000A: "FILE_DEVICE_INPORT_PORT",
429+
0x0000000B: "FILE_DEVICE_KEYBOARD",
430+
0x0000002F: "FILE_DEVICE_KS",
431+
0x00000039: "FILE_DEVICE_KSEC",
432+
0x0000000C: "FILE_DEVICE_MAILSLOT",
433+
0x0000002D: "FILE_DEVICE_MASS_STORAGE",
434+
0x0000000D: "FILE_DEVICE_MIDI_IN",
435+
0x0000000E: "FILE_DEVICE_MIDI_OUT",
436+
0x0000002B: "FILE_DEVICE_MODEM",
437+
0x0000000F: "FILE_DEVICE_MOUSE",
438+
0x00000010: "FILE_DEVICE_MULTI_UNC_PROVIDER",
439+
0x00000011: "FILE_DEVICE_NAMED_PIPE",
440+
0x00000012: "FILE_DEVICE_NETWORK",
441+
0x00000013: "FILE_DEVICE_NETWORK_BROWSER",
442+
0x00000014: "FILE_DEVICE_NETWORK_FILE_SYSTEM",
443+
0x00000028: "FILE_DEVICE_NETWORK_REDIRECTOR",
444+
0x00000015: "FILE_DEVICE_NULL",
445+
0x00000016: "FILE_DEVICE_PARALLEL_PORT",
446+
0x00000017: "FILE_DEVICE_PHYSICAL_NETCARD",
447+
0x00000018: "FILE_DEVICE_PRINTER",
448+
0x00000019: "FILE_DEVICE_SCANNER",
449+
0x0000001C: "FILE_DEVICE_SCREEN",
450+
0x00000037: "FILE_DEVICE_SERENUM",
451+
0x0000001A: "FILE_DEVICE_SERIAL_MOUSE_PORT",
452+
0x0000001B: "FILE_DEVICE_SERIAL_PORT",
453+
0x00000031: "FILE_DEVICE_SMARTCARD",
454+
0x0000002E: "FILE_DEVICE_SMB",
455+
0x0000001D: "FILE_DEVICE_SOUND",
456+
0x0000001E: "FILE_DEVICE_STREAMS",
457+
0x0000001F: "FILE_DEVICE_TAPE",
458+
0x00000020: "FILE_DEVICE_TAPE_FILE_SYSTEM",
459+
0x00000038: "FILE_DEVICE_TERMSRV",
460+
0x00000021: "FILE_DEVICE_TRANSPORT",
461+
0x00000022: "FILE_DEVICE_UNKNOWN",
462+
0x0000002C: "FILE_DEVICE_VDM",
463+
0x00000023: "FILE_DEVICE_VIDEO",
464+
0x00000024: "FILE_DEVICE_VIRTUAL_DISK",
465+
0x00000025: "FILE_DEVICE_WAVE_IN",
466+
0x00000026: "FILE_DEVICE_WAVE_OUT",
467+
}
468+
469+
409470
class DEVICE_OBJECT(objects.StructType, pool.ExecutiveObject):
410471
"""A class for kernel device objects."""
411472

412473
def get_device_name(self) -> str:
413-
"""Get device's name from the object header."""
474+
"""Get device's name from the object header.
475+
476+
Raises:
477+
ValueError if the device has no _OBJECT_HEADER_NAME_INFO member
478+
InvalidAddressException if the name cannot be dereferenced due to smear/swapped pages
479+
480+
"""
414481
header = self.get_object_header()
415482
return header.NameInfo.Name.String # type: ignore
416483

417-
def get_attached_devices(self) -> Generator[ObjectInterface, None, None]:
418-
"""Enumerate the attached device's objects"""
484+
def get_attached_driver_name(self) -> str:
485+
"""Gets the name of the driver that is attached to this device.
486+
487+
Raises:
488+
InvalidAddressException if the name cannot be dereferenced due to smear/swapped pages
489+
"""
490+
return self.DriverObject.DriverName.get_string()
491+
492+
def get_device_type(self) -> str:
493+
"""Gets the device type as a readable string.
494+
495+
Raises:
496+
InvalidAddressException if the DeviceType member cannot be read.
497+
"""
498+
return DEVICE_CODES.get(self.DeviceType, "UNKNOWN")
499+
500+
def get_attached_devices(self) -> Iterator["DEVICE_OBJECT"]:
501+
"""Enumerate the attached device's objects.
502+
503+
This only yields devices that are direct children of this devnode. In order to visit
504+
nodes farther down the tree, this method must be called on each of those children
505+
(and their children, and so on) to fully enumerate the device subtree. Child enumeration
506+
will stop if a device is seen twice or at the first InvalidAddressException, but the
507+
InvalidAddressException is not raised to the calling function.
508+
"""
419509
seen = set()
420510

421511
try:
422512
device = self.AttachedDevice.dereference()
423513
except exceptions.InvalidAddressException:
514+
vollog.debug(
515+
f"No attached device dereferenced for DEVICE_OBJECT at {self.vol.offset:#x}"
516+
)
424517
return
425518

426-
while device:
427-
if device.vol.offset in seen:
428-
break
519+
while device and device.vol.offset not in seen:
429520
seen.add(device.vol.offset)
430521

431522
yield device
432523

433524
try:
434-
device = device.AttachedDevice.dereference()
525+
device = device.NextDevice.dereference()
435526
except exceptions.InvalidAddressException:
527+
vollog.debug(
528+
f"Failed to dereference next device "
529+
f"for device at {device.vol.offset:#x}, may have reached list end"
530+
)
436531
return
437532

438533

0 commit comments

Comments
 (0)