Skip to content

Commit 590aa9c

Browse files
committed
Make it callable from other plugins.
Additionally, classmethod helpers were added, and docstrings were enhanced for improved usability and clarity.
1 parent d5e6e7c commit 590aa9c

File tree

1 file changed

+116
-38
lines changed

1 file changed

+116
-38
lines changed

volatility3/framework/plugins/linux/hidden_modules.py

Lines changed: 116 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33
#
44
import re
5-
import functools
65
import logging
76
import contextlib
8-
from typing import List, Iterable
7+
from typing import List, Set, Tuple, Iterable
98
from volatility3.framework import renderers, interfaces, exceptions, objects
109
from volatility3.framework.constants.architectures import LINUX_ARCHS
1110
from volatility3.framework.renderers import format_hints
@@ -41,7 +40,21 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
4140
),
4241
]
4342

44-
def _get_modules_memory_boundaries(self, vmlinux):
43+
@staticmethod
44+
def get_modules_memory_boundaries(
45+
context: interfaces.context.ContextInterface,
46+
vmlinux_module_name: str,
47+
) -> Tuple[int]:
48+
"""Determine the boundaries of the module allocation area
49+
50+
Args:
51+
context: The context to retrieve required elements (layers, symbol tables) from
52+
vmlinux_module_name: The name of the kernel module on which to operate
53+
54+
Returns:
55+
A tuple containing the minimum and maximum addresses for the module allocation area.
56+
"""
57+
vmlinux = context.modules[vmlinux_module_name]
4558
if vmlinux.has_symbol("mod_tree"):
4659
mod_tree = vmlinux.object_from_symbol("mod_tree")
4760
modules_addr_min = mod_tree.addr_min
@@ -73,8 +86,8 @@ def _get_modules_memory_boundaries(self, vmlinux):
7386

7487
return modules_addr_min, modules_addr_max
7588

89+
@staticmethod
7690
def _get_module_state_values_bytes(
77-
self,
7891
context: interfaces.context.ContextInterface,
7992
vmlinux_module_name: str,
8093
) -> List[bytes]:
@@ -97,12 +110,13 @@ def _get_module_state_values_bytes(
97110
]
98111
return values_bytes
99112

100-
def get_hidden_modules_vol2(
101-
self,
113+
@classmethod
114+
def _get_hidden_modules_vol2(
115+
cls,
102116
context: interfaces.context.ContextInterface,
103117
vmlinux_module_name: str,
104-
known_module_addresses,
105-
modules_memory_boundaries: tuple,
118+
known_module_addresses: Set[int],
119+
modules_memory_boundaries: Tuple,
106120
) -> Iterable[interfaces.objects.ObjectInterface]:
107121
"""Enumerate hidden modules using the traditional implementation.
108122
@@ -111,6 +125,9 @@ def get_hidden_modules_vol2(
111125
Args:
112126
context: The context to retrieve required elements (layers, symbol tables) from
113127
vmlinux_module_name: The name of the kernel module on which to operate
128+
known_module_addresses: Set with known module addresses
129+
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
130+
114131
Yields:
115132
module objects
116133
"""
@@ -175,7 +192,7 @@ def get_hidden_modules_vol2(
175192
scan_buf = b"".join(scan_list)
176193
del scan_list
177194

178-
module_state_values_bytes = self._get_module_state_values_bytes(
195+
module_state_values_bytes = cls._get_module_state_values_bytes(
179196
context, vmlinux_module_name
180197
)
181198
values_bytes_pattern = b"|".join(module_state_values_bytes)
@@ -190,27 +207,37 @@ def get_hidden_modules_vol2(
190207
if module and module.is_valid():
191208
yield module
192209

193-
@functools.cached_property
194-
def module_address_alignment(self) -> int:
210+
@classmethod
211+
def _get_module_address_alignment(
212+
cls,
213+
context: interfaces.context.ContextInterface,
214+
vmlinux_module_name: str,
215+
) -> int:
195216
"""Obtain the module memory address alignment. This is only used with the fast scan method.
196217
197218
struct module is aligned to the L1 cache line, which is typically 64 bytes for most
198219
common i386/AMD64/ARM64 configurations. In some cases, it can be 128 bytes, but this
199220
will still work.
200221
222+
Args:
223+
context: The context to retrieve required elements (layers, symbol tables) from
224+
vmlinux_module_name: The name of the kernel module on which to operate
225+
201226
Returns:
202227
The struct module alignment
203228
"""
204229
# FIXME: When dwarf2json/ISF supports type alignments. Read it directly from the type metadata
205-
# The cached_property won't provide any benefits until then
230+
# Also, 'context' and 'vmlinux_module_name' are not used yet, but they will be needed to obtain
231+
# the type metadata
206232
return 64
207233

208-
def get_hidden_modules_fast(
209-
self,
234+
@classmethod
235+
def _get_hidden_modules_fast(
236+
cls,
210237
context: interfaces.context.ContextInterface,
211238
vmlinux_module_name: str,
212-
known_module_addresses,
213-
modules_memory_boundaries: tuple,
239+
known_module_addresses: Set[int],
240+
modules_memory_boundaries: Tuple,
214241
) -> Iterable[interfaces.objects.ObjectInterface]:
215242
"""Enumerate hidden modules by taking advantage of memory address alignment patterns
216243
@@ -229,6 +256,9 @@ def get_hidden_modules_fast(
229256
Args:
230257
context: The context to retrieve required elements (layers, symbol tables) from
231258
vmlinux_module_name: The name of the kernel module on which to operate
259+
known_module_addresses: Set with known module addresses
260+
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
261+
232262
Yields:
233263
module objects
234264
"""
@@ -237,12 +267,16 @@ def get_hidden_modules_fast(
237267

238268
module_addr_min, module_addr_max = modules_memory_boundaries
239269

240-
module_state_values_bytes = self._get_module_state_values_bytes(
270+
module_state_values_bytes = cls._get_module_state_values_bytes(
271+
context, vmlinux_module_name
272+
)
273+
274+
module_address_alignment = cls._get_module_address_alignment(
241275
context, vmlinux_module_name
242276
)
243277

244278
for module_addr in range(
245-
module_addr_min, module_addr_max, self.module_address_alignment
279+
module_addr_min, module_addr_max, module_address_alignment
246280
):
247281
if module_addr in known_module_addresses:
248282
continue
@@ -264,51 +298,59 @@ def get_hidden_modules_fast(
264298
if module and module.is_valid():
265299
yield module
266300

267-
def _validate_alignment_patterns(self, addresses: Iterable[int]) -> bool:
301+
@staticmethod
302+
def _validate_alignment_patterns(
303+
addresses: Iterable[int],
304+
address_alignment: int,
305+
) -> bool:
268306
"""Check if the memory addresses meet our alignments patterns
269307
270308
Args:
271309
addresses: Iterable with the address values
310+
address_alignment: Number of bytes for alignment validation
272311
273312
Returns:
274313
True if all the addresses meet the alignment
275314
"""
276-
return all(addr % self.module_address_alignment == 0 for addr in addresses)
315+
return all(addr % address_alignment == 0 for addr in addresses)
277316

317+
@classmethod
278318
def get_hidden_modules(
279-
self,
319+
cls,
280320
context: interfaces.context.ContextInterface,
281321
vmlinux_module_name: str,
322+
known_module_addresses: Set[int],
323+
modules_memory_boundaries: Tuple,
324+
fast_method: bool = False,
282325
) -> Iterable[interfaces.objects.ObjectInterface]:
283326
"""Enumerate hidden modules
284327
285328
Args:
286329
context: The context to retrieve required elements (layers, symbol tables) from
287330
vmlinux_module_name: The name of the kernel module on which to operate
331+
known_module_addresses: Set with known module addresses
332+
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
333+
fast_method: If True, it uses the fast method. Otherwise, it uses the traditional one.
334+
288335
Yields:
289336
module objects
290337
"""
291-
vmlinux = context.modules[vmlinux_module_name]
292-
vmlinux_layer = context.layers[vmlinux.layer_name]
293-
294-
known_module_addresses = {
295-
vmlinux_layer.canonicalize(module.vol.offset)
296-
for module in lsmod.Lsmod.list_modules(context, vmlinux_module_name)
297-
}
298-
299-
modules_memory_boundaries = self._get_modules_memory_boundaries(vmlinux)
300-
301-
if self.config.get("fast"):
302-
if self._validate_alignment_patterns(known_module_addresses):
303-
scan_method = self.get_hidden_modules_fast
338+
if fast_method:
339+
module_address_alignment = cls._get_module_address_alignment(
340+
context, vmlinux_module_name
341+
)
342+
if cls._validate_alignment_patterns(
343+
known_module_addresses, module_address_alignment
344+
):
345+
scan_method = cls._get_hidden_modules_fast
304346
else:
305347
vollog.warning(
306-
f"Module addresses aren't aligned to {self.module_address_alignment} bytes. "
348+
f"Module addresses aren't aligned to {module_address_alignment} bytes. "
307349
"Switching to the traditional scan method."
308350
)
309-
scan_method = self.get_hidden_modules_vol2
351+
scan_method = cls._get_hidden_modules_vol2
310352
else:
311-
scan_method = self.get_hidden_modules_vol2
353+
scan_method = cls._get_hidden_modules_vol2
312354

313355
yield from scan_method(
314356
context,
@@ -317,9 +359,45 @@ def get_hidden_modules(
317359
modules_memory_boundaries,
318360
)
319361

362+
@classmethod
363+
def get_lsmod_module_addresses(
364+
cls,
365+
context: interfaces.context.ContextInterface,
366+
vmlinux_module_name: str,
367+
) -> Set[int]:
368+
"""Obtain a set the known module addresses from linux.lsmod plugin
369+
370+
Args:
371+
context: The context to retrieve required elements (layers, symbol tables) from
372+
vmlinux_module_name: The name of the kernel module on which to operate
373+
374+
Returns:
375+
A set containing known kernel module addresses
376+
"""
377+
vmlinux = context.modules[vmlinux_module_name]
378+
vmlinux_layer = context.layers[vmlinux.layer_name]
379+
380+
known_module_addresses = {
381+
vmlinux_layer.canonicalize(module.vol.offset)
382+
for module in lsmod.Lsmod.list_modules(context, vmlinux_module_name)
383+
}
384+
return known_module_addresses
385+
320386
def _generator(self):
321387
vmlinux_module_name = self.config["kernel"]
322-
for module in self.get_hidden_modules(self.context, vmlinux_module_name):
388+
known_module_addresses = self.get_lsmod_module_addresses(
389+
self.context, vmlinux_module_name
390+
)
391+
modules_memory_boundaries = self.get_modules_memory_boundaries(
392+
self.context, vmlinux_module_name
393+
)
394+
for module in self.get_hidden_modules(
395+
self.context,
396+
vmlinux_module_name,
397+
known_module_addresses,
398+
modules_memory_boundaries,
399+
fast_method=self.config.get("fast"),
400+
):
323401
module_addr = module.vol.offset
324402
module_name = module.get_name() or renderers.NotAvailableValue()
325403
fields = (format_hints.Hex(module_addr), module_name)

0 commit comments

Comments
 (0)