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
This commit is contained in:
Gustavo Moreira
2024-10-04 07:48:25 +10:00
parent e8754fadbd
commit b5948d7ec0
2 changed files with 30 additions and 43 deletions
@@ -38,13 +38,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
optional=True,
default=False,
),
requirements.BooleanRequirement(
name="heuristic-mode",
description="Relaxed constraints. This may generate false positives and "
"take a bit longer. This feature is available only when using the --fast option",
optional=True,
default=False,
),
]
@staticmethod
@@ -124,7 +117,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_module_name: str,
known_module_addresses: Set[int],
modules_memory_boundaries: Tuple,
heuristic_mode: bool = False,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Enumerate hidden modules using the traditional implementation.
@@ -135,7 +127,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_module_name: The name of the kernel module on which to operate
known_module_addresses: Set with known module addresses
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
heuristic_mode: ignored for this scan method.
Yields:
module objects
@@ -247,7 +238,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_module_name: str,
known_module_addresses: Set[int],
modules_memory_boundaries: Tuple,
heuristic_mode: bool = False,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Enumerate hidden modules by taking advantage of memory address alignment patterns
@@ -268,7 +258,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_module_name: The name of the kernel module on which to operate
known_module_addresses: Set with known module addresses
modules_memory_boundaries: Minimum and maximum address boundaries for module allocation.
heuristic_mode: If True, it loosens constraints to enhance the detection of advanced threats.
Yields:
module objects
"""
@@ -276,37 +265,41 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_layer = context.layers[vmlinux.layer_name]
module_addr_min, module_addr_max = modules_memory_boundaries
module_state_values_bytes = cls._get_module_state_values_bytes(
context, vmlinux_module_name
)
module_address_alignment = cls._get_module_address_alignment(
context, vmlinux_module_name
)
mkobj_offset = vmlinux.get_type("module").relative_child_offset("mkobj")
mod_offset = vmlinux.get_type("module_kobject").relative_child_offset("mod")
offset_to_mkobj_mod = mkobj_offset + mod_offset
mod_member_template = vmlinux.get_type("module_kobject").vol.members["mod"][1]
mod_size = mod_member_template.size
mod_member_data_format = mod_member_template.data_format
for module_addr in range(
module_addr_min, module_addr_max, module_address_alignment
):
if module_addr in known_module_addresses:
continue
if not heuristic_mode:
try:
# This is just a pre-filter. Module readability and consistency are verified in module.is_valid()
module_state_bytes = vmlinux_layer.read(
module_addr, len(module_state_values_bytes[0])
)
if module_state_bytes not in module_state_values_bytes:
continue
except (
exceptions.PagedInvalidAddressException,
exceptions.InvalidAddressException,
):
try:
# This is just a pre-filter. Module readability and consistency are verified in module.is_valid()
self_referential_bytes = vmlinux_layer.read(
module_addr + offset_to_mkobj_mod, mod_size
)
self_referential = objects.convert_data_to_value(
self_referential_bytes, int, mod_member_data_format
)
if self_referential != module_addr:
continue
except (
exceptions.PagedInvalidAddressException,
exceptions.InvalidAddressException,
):
continue
module = vmlinux.object("module", offset=module_addr, absolute=True)
if module and module.is_valid(strict_states=not heuristic_mode):
if module and module.is_valid():
yield module
@staticmethod
@@ -369,7 +362,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
vmlinux_module_name,
known_module_addresses,
modules_memory_boundaries,
heuristic_mode,
)
@classmethod
@@ -410,7 +402,6 @@ class Hidden_modules(interfaces.plugins.PluginInterface):
known_module_addresses,
modules_memory_boundaries,
fast_method=self.config.get("fast"),
heuristic_mode=self.config.get("heuristic-mode"),
):
module_addr = module.vol.offset
module_name = module.get_name() or renderers.NotAvailableValue()
@@ -35,15 +35,12 @@ class module(generic.GenericIntelProcess):
super().__init__(*args, **kwargs)
self._mod_mem_type = None # Initialize _mod_mem_type to None for memoization
def is_valid(self, strict_states=True):
def is_valid(self):
layer = self._context.layers[self.vol.layer_name]
# Make sure the entire module content is readable
if not layer.is_valid(self.vol.offset, self.vol.size):
return False
if strict_states and not self.state.is_valid_choice:
return False
core_size = self.get_core_size()
if not (
1 <= core_size <= 20000000
@@ -52,14 +49,13 @@ class module(generic.GenericIntelProcess):
):
return False
if self.has_member("mkobj") and self.mkobj.has_member("mod"):
if not (
self.mkobj
and self.mkobj.mod
and self.mkobj.mod.is_readable()
and self.mkobj.mod == self.vol.offset
):
return False
if not (
self.mkobj
and self.mkobj.mod
and self.mkobj.mod.is_readable()
and self.mkobj.mod == self.vol.offset
):
return False
return True