Skip to content

Commit b5948d7

Browse files
committed
Linux: hidden_modules: Add @Abyss-W4tcher suggestion to optimize the fast scan method for even better performance, using the mkobj.mod self referential validation used in module.is_valid() as pre-filter
Removed the --heuristic-mode and the module.states validation, since the self referential check is enough by itself
1 parent e8754fa commit b5948d7

File tree

2 files changed

+30
-43
lines changed

2 files changed

+30
-43
lines changed

volatility3/framework/plugins/linux/hidden_modules.py

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
3838
optional=True,
3939
default=False,
4040
),
41-
requirements.BooleanRequirement(
42-
name="heuristic-mode",
43-
description="Relaxed constraints. This may generate false positives and "
44-
"take a bit longer. This feature is available only when using the --fast option",
45-
optional=True,
46-
default=False,
47-
),
4841
]
4942

5043
@staticmethod
@@ -124,7 +117,6 @@ def _get_hidden_modules_vol2(
124117
vmlinux_module_name: str,
125118
known_module_addresses: Set[int],
126119
modules_memory_boundaries: Tuple,
127-
heuristic_mode: bool = False,
128120
) -> Iterable[interfaces.objects.ObjectInterface]:
129121
"""Enumerate hidden modules using the traditional implementation.
130122
@@ -135,7 +127,6 @@ def _get_hidden_modules_vol2(
135127
vmlinux_module_name: The name of the kernel module on which to operate
136128
known_module_addresses: Set with known module addresses
137129
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
138-
heuristic_mode: ignored for this scan method.
139130
140131
Yields:
141132
module objects
@@ -247,7 +238,6 @@ def _get_hidden_modules_fast(
247238
vmlinux_module_name: str,
248239
known_module_addresses: Set[int],
249240
modules_memory_boundaries: Tuple,
250-
heuristic_mode: bool = False,
251241
) -> Iterable[interfaces.objects.ObjectInterface]:
252242
"""Enumerate hidden modules by taking advantage of memory address alignment patterns
253243
@@ -268,45 +258,48 @@ def _get_hidden_modules_fast(
268258
vmlinux_module_name: The name of the kernel module on which to operate
269259
known_module_addresses: Set with known module addresses
270260
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
271-
heuristic_mode: If True, it loosens constraints to enhance the detection of advanced threats.
272261
Yields:
273262
module objects
274263
"""
275264
vmlinux = context.modules[vmlinux_module_name]
276265
vmlinux_layer = context.layers[vmlinux.layer_name]
277266

278267
module_addr_min, module_addr_max = modules_memory_boundaries
279-
280-
module_state_values_bytes = cls._get_module_state_values_bytes(
281-
context, vmlinux_module_name
282-
)
283-
284268
module_address_alignment = cls._get_module_address_alignment(
285269
context, vmlinux_module_name
286270
)
287271

272+
mkobj_offset = vmlinux.get_type("module").relative_child_offset("mkobj")
273+
mod_offset = vmlinux.get_type("module_kobject").relative_child_offset("mod")
274+
offset_to_mkobj_mod = mkobj_offset + mod_offset
275+
mod_member_template = vmlinux.get_type("module_kobject").vol.members["mod"][1]
276+
mod_size = mod_member_template.size
277+
mod_member_data_format = mod_member_template.data_format
278+
288279
for module_addr in range(
289280
module_addr_min, module_addr_max, module_address_alignment
290281
):
291282
if module_addr in known_module_addresses:
292283
continue
293284

294-
if not heuristic_mode:
295-
try:
296-
# This is just a pre-filter. Module readability and consistency are verified in module.is_valid()
297-
module_state_bytes = vmlinux_layer.read(
298-
module_addr, len(module_state_values_bytes[0])
299-
)
300-
if module_state_bytes not in module_state_values_bytes:
301-
continue
302-
except (
303-
exceptions.PagedInvalidAddressException,
304-
exceptions.InvalidAddressException,
305-
):
285+
try:
286+
# This is just a pre-filter. Module readability and consistency are verified in module.is_valid()
287+
self_referential_bytes = vmlinux_layer.read(
288+
module_addr + offset_to_mkobj_mod, mod_size
289+
)
290+
self_referential = objects.convert_data_to_value(
291+
self_referential_bytes, int, mod_member_data_format
292+
)
293+
if self_referential != module_addr:
306294
continue
295+
except (
296+
exceptions.PagedInvalidAddressException,
297+
exceptions.InvalidAddressException,
298+
):
299+
continue
307300

308301
module = vmlinux.object("module", offset=module_addr, absolute=True)
309-
if module and module.is_valid(strict_states=not heuristic_mode):
302+
if module and module.is_valid():
310303
yield module
311304

312305
@staticmethod
@@ -369,7 +362,6 @@ def get_hidden_modules(
369362
vmlinux_module_name,
370363
known_module_addresses,
371364
modules_memory_boundaries,
372-
heuristic_mode,
373365
)
374366

375367
@classmethod
@@ -410,7 +402,6 @@ def _generator(self):
410402
known_module_addresses,
411403
modules_memory_boundaries,
412404
fast_method=self.config.get("fast"),
413-
heuristic_mode=self.config.get("heuristic-mode"),
414405
):
415406
module_addr = module.vol.offset
416407
module_name = module.get_name() or renderers.NotAvailableValue()

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,12 @@ def __init__(self, *args, **kwargs):
3535
super().__init__(*args, **kwargs)
3636
self._mod_mem_type = None # Initialize _mod_mem_type to None for memoization
3737

38-
def is_valid(self, strict_states=True):
38+
def is_valid(self):
3939
layer = self._context.layers[self.vol.layer_name]
4040
# Make sure the entire module content is readable
4141
if not layer.is_valid(self.vol.offset, self.vol.size):
4242
return False
4343

44-
if strict_states and not self.state.is_valid_choice:
45-
return False
46-
4744
core_size = self.get_core_size()
4845
if not (
4946
1 <= core_size <= 20000000
@@ -52,14 +49,13 @@ def is_valid(self, strict_states=True):
5249
):
5350
return False
5451

55-
if self.has_member("mkobj") and self.mkobj.has_member("mod"):
56-
if not (
57-
self.mkobj
58-
and self.mkobj.mod
59-
and self.mkobj.mod.is_readable()
60-
and self.mkobj.mod == self.vol.offset
61-
):
62-
return False
52+
if not (
53+
self.mkobj
54+
and self.mkobj.mod
55+
and self.mkobj.mod.is_readable()
56+
and self.mkobj.mod == self.vol.offset
57+
):
58+
return False
6359

6460
return True
6561

0 commit comments

Comments
 (0)