Skip to content

Commit 0b6203c

Browse files
authored
Merge pull request #1678 from volatilityfoundation/fix_unloaded_modules
Fix unloaded modules bugs. Change API to fit current formats
2 parents 0e236fd + 45fe8f6 commit 0b6203c

File tree

1 file changed

+58
-29
lines changed

1 file changed

+58
-29
lines changed

volatility3/framework/plugins/windows/unloadedmodules.py

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import logging
66
import datetime
7-
from typing import List, Iterable
7+
from typing import List, Generator, Tuple
88

99
from volatility3.framework import constants
1010
from volatility3.framework import interfaces, symbols, exceptions
@@ -14,6 +14,7 @@
1414
from volatility3.framework.renderers import format_hints, conversion
1515
from volatility3.framework.symbols import intermed
1616
from volatility3.plugins import timeliner
17+
from volatility3.plugins.windows import modules
1718

1819
vollog = logging.getLogger(__name__)
1920

@@ -22,7 +23,7 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
2223
"""Lists the unloaded kernel modules."""
2324

2425
_required_framework_version = (2, 0, 0)
25-
_version = (1, 0, 2)
26+
_version = (2, 0, 0)
2627

2728
@classmethod
2829
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -75,10 +76,9 @@ def create_unloadedmodules_table(
7576
def list_unloadedmodules(
7677
cls,
7778
context: interfaces.context.ContextInterface,
78-
layer_name: str,
79-
symbol_table: str,
79+
kernel_module_name: str,
8080
unloadedmodule_table_name: str,
81-
) -> Iterable[interfaces.objects.ObjectInterface]:
81+
) -> Generator[Tuple[str, int, int, datetime.datetime], None, None]:
8282
"""Lists all the unloaded modules in the primary layer.
8383
8484
Args:
@@ -90,20 +90,16 @@ def list_unloadedmodules(
9090
A list of Unloaded Modules as retrieved from MmUnloadedDrivers
9191
"""
9292

93-
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
94-
if not kvo:
95-
raise ValueError(
96-
"Intel layer does not have an associated kernel virtual offset, failing"
97-
)
98-
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
93+
ntkrnlmp = context.modules[kernel_module_name]
94+
9995
unloadedmodules_offset = ntkrnlmp.get_symbol("MmUnloadedDrivers").address
10096
unloadedmodules = ntkrnlmp.object(
10197
object_type="pointer",
10298
offset=unloadedmodules_offset,
10399
subtype="array",
104100
)
105101
is_64bit = symbols.symbol_table_is_64bit(
106-
context=context, symbol_table_name=symbol_table
102+
context=context, symbol_table_name=ntkrnlmp.symbol_table_name
107103
)
108104

109105
if is_64bit:
@@ -116,53 +112,86 @@ def list_unloadedmodules(
116112
object_type=unloaded_count_type, offset=last_unloadedmodule_offset
117113
)
118114

115+
# Bring down to default when smear present. Some samples had this completely broken
116+
if unloaded_count > 1024:
117+
vollog.warning(
118+
f"Smeared array count found {unloaded_count}. Defaulting to 1024 elements."
119+
)
120+
unloaded_count = 1024
121+
119122
unloadedmodules_array = context.object(
120123
object_type=unloadedmodule_table_name
121124
+ constants.BANG
122125
+ "_UNLOADED_DRIVERS",
123-
layer_name=layer_name,
126+
layer_name=ntkrnlmp.layer_name,
124127
offset=unloadedmodules,
125128
)
126129
unloadedmodules_array.UnloadedDrivers.count = unloaded_count
127130

131+
kernel_space_start = modules.Modules.get_kernel_space_start(
132+
context, kernel_module_name
133+
)
134+
135+
address_mask = context.layers[ntkrnlmp.layer_name].address_mask
136+
128137
for driver in unloadedmodules_array.UnloadedDrivers:
129138
# Mass testing led to dozens of samples backtracing on this plugin when
130139
# accessing members of modules coming out this list
131140
# Given how often temporary drivers load and unload on Win10+, I
132141
# assume the chance for smear is very high
133142
try:
134-
driver.StartAddress
135-
driver.EndAddress
136-
driver.CurrentTime
137-
yield driver
143+
start_address = driver.StartAddress & address_mask
144+
end_address = driver.EndAddress & address_mask
145+
current_time = driver.CurrentTime
146+
driver_name = driver.Name.String
138147
except exceptions.InvalidAddressException:
139148
continue
140149

150+
if (
151+
current_time > 1024
152+
and start_address > kernel_space_start
153+
and start_address & 0xFFF == 0x0
154+
and end_address & 0xFFF == 0x0
155+
and end_address > kernel_space_start
156+
):
157+
yield driver_name, start_address, end_address, current_time
158+
141159
def _generator(self):
142160
kernel = self.context.modules[self.config["kernel"]]
143161

162+
if not kernel.has_symbol("MmUnloadedDrivers"):
163+
vollog.error(
164+
"The symbol table for this sample is missing the `MmUnloadedDrivers` symbol. Cannot proceed."
165+
)
166+
return
167+
168+
if not kernel.has_symbol("MmLastUnloadedDriver"):
169+
vollog.error(
170+
"The symbol table for this sample is missing the `MmLastUnloadededDriver` symbol. Cannot proceed."
171+
)
172+
return
173+
144174
unloadedmodule_table_name = self.create_unloadedmodules_table(
145175
self.context, kernel.symbol_table_name, self.config_path
146176
)
147177

148-
for mod in self.list_unloadedmodules(
178+
for (
179+
driver_name,
180+
start_address,
181+
end_address,
182+
current_time,
183+
) in self.list_unloadedmodules(
149184
self.context,
150-
kernel.layer_name,
151-
kernel.symbol_table_name,
185+
self.config["kernel"],
152186
unloadedmodule_table_name,
153187
):
154-
try:
155-
name = mod.Name.String
156-
except exceptions.InvalidAddressException:
157-
name = renderers.UnreadableValue()
158-
159188
yield (
160189
0,
161190
(
162-
name,
163-
format_hints.Hex(mod.StartAddress),
164-
format_hints.Hex(mod.EndAddress),
165-
conversion.wintime_to_datetime(mod.CurrentTime),
191+
driver_name,
192+
format_hints.Hex(start_address),
193+
format_hints.Hex(end_address),
194+
conversion.wintime_to_datetime(current_time),
166195
),
167196
)
168197

0 commit comments

Comments
 (0)