Merge pull request #1845 from SolitudePy/categorize_linux_malware

Malware categorization: linux.check_afinfo & linux.hidden_modules
This commit is contained in:
ikelos
2025-06-10 21:05:41 +01:00
committed by GitHub
6 changed files with 435 additions and 395 deletions
@@ -36,7 +36,7 @@ For plugin requests, please create an issue with a description of the requested
$ python3 vol.py --help | grep -i linux. | head -n 5
banners.Banners Attempts to identify potential linux banners in an
linux.bash.Bash Recovers bash command history from memory.
linux.check_afinfo.Check_afinfo
linux.malware.check_afinfo.Check_afinfo
linux.check_creds.Check_creds
linux.malware.check_idt.Check_idt
+2 -2
View File
@@ -426,7 +426,7 @@ class TestLinuxPageCacheInodepages:
class TestLinuxCheckAfinfo:
def test_linux_generic_check_afinfo(self, image, volatility, python):
rc, out, _err = test_volatility.runvol_plugin(
"linux.check_afinfo.Check_afinfo", image, volatility, python
"linux.malware.check_afinfo.Check_afinfo", image, volatility, python
)
# linux-sample-1.bin has no suspicious results.
@@ -525,7 +525,7 @@ class TestLinuxHiddenModules:
# TODO: this check should be specific, against a distinct infected sample
image = LinuxSamples.LINUX_GENERIC.value.path
rc, out, _err = test_volatility.runvol_plugin(
"linux.hidden_modules.Hidden_modules", image, volatility, python
"linux.malware.hidden_modules.Hidden_modules", image, volatility, python
)
# linux-sample-1.bin has no hidden modules.
@@ -1,215 +1,20 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
"""A module containing a plugin that verifies the operation function
pointers of network protocols."""
import logging
from typing import List, Tuple, Generator
from volatility3.framework import exceptions, interfaces
from volatility3.framework import renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.renderers import format_hints
from volatility3.framework import interfaces, deprecation
from volatility3.plugins.linux.malware import check_afinfo
vollog = logging.getLogger(__name__)
class Check_afinfo(plugins.PluginInterface):
"""Verifies the operation function pointers of network protocols."""
class Check_afinfo(
interfaces.plugins.PluginInterface,
deprecation.PluginRenameClass,
replacement_class=check_afinfo.Check_afinfo,
removal_date="2026-06-07",
):
"""Verifies the operation function pointers of network protocols (deprecated)."""
_version = (1, 0, 0)
_required_framework_version = (2, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(
name="kernel",
description="Linux kernel",
architectures=["Intel32", "Intel64"],
),
]
@classmethod
def _check_members(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
var_ops: interfaces.objects.ObjectInterface,
var_name: str,
members: List[str],
) -> Generator[Tuple[str, str, int], None, None]:
"""
Yields any members that are not pointing inside the kernel
"""
vmlinux = context.modules[vmlinux_name]
for check in members:
# redhat-specific garbage
if check.startswith("__UNIQUE_ID_rh_kabi_hide"):
continue
# These structures have members like `write` and `next`, which are built in Python functions
addr = var_ops.member(attr=check)
# Unimplemented handlers are set to 0
if not addr:
continue
if len(vmlinux.get_symbols_by_absolute_location(addr)) == 0:
yield var_name, check, addr
@classmethod
def _check_pre_4_18_ops(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
var_name: str,
var: interfaces.objects.ObjectInterface,
op_members: List[str],
seq_members: List[str],
):
"""
Finds the correct way to reference `op_members`
"""
vmlinux = context.modules[vmlinux_name]
if var.has_member("seq_fops"):
yield from cls._check_members(
context, vmlinux_name, var.seq_fops, var_name, op_members
)
# newer kernels
if var.has_member("seq_ops"):
yield from cls._check_members(
context, vmlinux_name, var.seq_ops, var_name, seq_members
)
# this is the most commonly hooked member by rootkits, so a force a check on it
elif var.has_member("seq_show"):
if len(vmlinux.get_symbols_by_location(var.seq_show)) == 0:
yield var_name, "show", var.seq_show
else:
raise exceptions.VolatilityException(
"_check_afinfo_pre_4_18: Unable to find sequence operations members for checking."
)
@classmethod
def _check_afinfo_pre_4_18(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
seq_members: str,
) -> Generator[Tuple[str, str, int], None, None]:
"""
Checks the operations structures for network protocols of < 4.18 systems
"""
tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"])
udp = (
"udp_seq_afinfo",
[
"udplite6_seq_afinfo",
"udp6_seq_afinfo",
"udplite4_seq_afinfo",
"udp4_seq_afinfo",
],
)
protocols = [tcp, udp]
vmlinux = context.modules[vmlinux_name]
op_members = vmlinux.get_type("file_operations").members
# loop through all symbols
for struct_type, global_vars in protocols:
for global_var_name in global_vars:
# this will lookup fail for the IPv6 protocols on kernels without IPv6 support
try:
global_var = vmlinux.object_from_symbol(global_var_name)
except exceptions.SymbolError:
continue
yield from cls._check_pre_4_18_ops(
context,
vmlinux_name,
global_var_name,
global_var,
op_members,
seq_members,
)
@classmethod
def _check_afinfo_post_4_18(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
seq_members: str,
) -> Generator[Tuple[str, str, int], None, None]:
"""
Checks the operations structures for network protocols of >= 4.18 systems
"""
vmlinux = context.modules[vmlinux_name]
ops_structs = [
"raw_seq_ops",
"udp_seq_ops",
"arp_seq_ops",
"unix_seq_ops",
"udp6_seq_ops",
"raw6_seq_ops",
"tcp_seq_ops",
"tcp4_seq_ops",
"tcp6_seq_ops",
"packet_seq_ops",
]
for protocol_ops_var in ops_structs:
# These will fail if the particular kernel doesn't have support for a protocol like IPv6
try:
protocol_ops = vmlinux.object_from_symbol(protocol_ops_var)
except exceptions.SymbolError:
continue
yield from cls._check_members(
context, vmlinux_name, protocol_ops, protocol_ops_var, seq_members
)
@classmethod
def check_afinfo(
cls, context: interfaces.context.ContextInterface, vmlinux_name
) -> Generator[Tuple[str, str, int], None, None]:
"""
Walks the network protocol operations structures for common network protocols.
Reports any initialized operations members that do not point inside the kernel.
"""
vmlinux = context.modules[vmlinux_name]
type_check = vmlinux.get_type("tcp_seq_afinfo")
if type_check.has_member("seq_fops"):
checker = cls._check_afinfo_pre_4_18
else:
checker = cls._check_afinfo_post_4_18
seq_members = vmlinux.get_type("seq_operations").members
yield from checker(context, vmlinux_name, seq_members)
def _generator(self):
"""
A simple wrapper around `check_afino`
"""
for name, member, address in self.check_afinfo(
self.context, self.config["kernel"]
):
yield 0, (name, member, format_hints.Hex(address))
def run(self):
return renderers.TreeGrid(
[
("Symbol Name", str),
("Member", str),
("Handler Address", format_hints.Hex),
],
self._generator(),
)
@@ -1,197 +1,20 @@
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from typing import List, Set, Tuple, Iterable
from volatility3.framework.symbols.linux.utilities import (
modules as linux_utilities_modules,
)
from volatility3.framework import interfaces, exceptions, deprecation
from volatility3.framework.configuration import requirements
from volatility3.framework.symbols.linux import extensions
from volatility3.framework.interfaces import plugins
from volatility3.framework import interfaces, deprecation
from volatility3.plugins.linux.malware import hidden_modules
vollog = logging.getLogger(__name__)
class Hidden_modules(plugins.PluginInterface):
"""Carves memory to find hidden kernel modules"""
class Hidden_modules(
interfaces.plugins.PluginInterface,
deprecation.PluginRenameClass,
replacement_class=hidden_modules.Hidden_modules,
removal_date="2026-06-07",
):
"""Carves memory to find hidden kernel modules (deprecated)."""
_required_framework_version = (2, 25, 0)
_version = (3, 0, 2)
@classmethod
def find_hidden_modules(
cls, context, vmlinux_module_name: str
) -> extensions.module:
if context.symbol_space.verify_table_versions(
"dwarf2json", lambda version, _: (not version) or version < (0, 8, 0)
):
raise exceptions.SymbolSpaceError(
"Invalid symbol table, please ensure the ISF table produced by dwarf2json was created with version 0.8.0 or later"
)
known_module_addresses = cls.get_lsmod_module_addresses(
context, vmlinux_module_name
)
modules_memory_boundaries = (
linux_utilities_modules.Modules.get_modules_memory_boundaries(
context, vmlinux_module_name
)
)
yield from linux_utilities_modules.Modules.get_hidden_modules(
context,
vmlinux_module_name,
known_module_addresses,
modules_memory_boundaries,
)
@classmethod
def get_hidden_modules(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
known_module_addresses: Set[int],
modules_memory_boundaries: Tuple,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Enumerate hidden modules by taking advantage of memory address alignment patterns
This technique is much faster and uses less memory than the traditional scan method
in Volatility2, but it doesn't work with older kernels.
From kernels 4.2 struct module allocation are aligned to the L1 cache line size.
In i386/amd64/arm64 this is typically 64 bytes. However, this can be changed in
the Linux kernel configuration via CONFIG_X86_L1_CACHE_SHIFT. The alignment can
also be obtained from the DWARF info i.e. DW_AT_alignment<64>, but dwarf2json
doesn't support this feature yet.
In kernels < 4.2, alignment attributes are absent in the struct module, meaning
alignment cannot be guaranteed. Therefore, for older kernels, it's better to use
the traditional scan technique.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
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.
Yields:
module objects
"""
return linux_utilities_modules.get_hidden_modules(
vmlinux_module_name, known_module_addresses, modules_memory_boundaries
)
run = linux_utilities_modules.ModuleDisplayPlugin.run
_generator = linux_utilities_modules.ModuleDisplayPlugin.generator
implementation = find_hidden_modules
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.VersionRequirement(
name="linux_utilities_modules_module_display_plugin",
component=linux_utilities_modules.ModuleDisplayPlugin,
version=(1, 0, 0),
),
requirements.VersionRequirement(
name="linux_utilities_modules",
component=linux_utilities_modules.Modules,
version=(3, 0, 1),
),
] + linux_utilities_modules.ModuleDisplayPlugin.get_requirements()
@staticmethod
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_modules_memory_boundaries,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
def get_modules_memory_boundaries(
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> Tuple[int, int]:
return linux_utilities_modules.Modules.get_modules_memory_boundaries(
context, vmlinux_module_name
)
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_module_address_alignment,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
@classmethod
def _get_module_address_alignment(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> int:
"""Obtain the module memory address alignment.
struct module is aligned to the L1 cache line, which is typically 64 bytes for most
common i386/AMD64/ARM64 configurations. In some cases, it can be 128 bytes, but this
will still work.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
vmlinux_module_name: The name of the kernel module on which to operate
Returns:
The struct module alignment
"""
return linux_utilities_modules.get_module_address_alignment(
context, vmlinux_module_name
)
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_hidden_modules,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
@staticmethod
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.validate_alignment_patterns,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
def _validate_alignment_patterns(
addresses: Iterable[int],
address_alignment: int,
) -> bool:
"""Check if the memory addresses meet our alignments patterns
Args:
addresses: Iterable with the address values
address_alignment: Number of bytes for alignment validation
Returns:
True if all the addresses meet the alignment
"""
return linux_utilities_modules.validate_alignment_patterns(
addresses, address_alignment
)
@classmethod
def get_lsmod_module_addresses(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> Set[int]:
"""Obtain a set the known module addresses from linux.lsmod plugin
Args:
context: The context to retrieve required elements (layers, symbol tables) from
vmlinux_module_name: The name of the kernel module on which to operate
Returns:
A set containing known kernel module addresses
"""
vmlinux = context.modules[vmlinux_module_name]
vmlinux_layer = context.layers[vmlinux.layer_name]
known_module_addresses = {
vmlinux_layer.canonicalize(module.vol.offset)
for module in linux_utilities_modules.Modules.list_modules(
context, vmlinux_module_name
)
}
return known_module_addresses
@@ -0,0 +1,215 @@
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
"""A module containing a plugin that verifies the operation function
pointers of network protocols."""
import logging
from typing import List, Tuple, Generator
from volatility3.framework import exceptions, interfaces
from volatility3.framework import renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.renderers import format_hints
vollog = logging.getLogger(__name__)
class Check_afinfo(plugins.PluginInterface):
"""Verifies the operation function pointers of network protocols."""
_version = (1, 0, 0)
_required_framework_version = (2, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(
name="kernel",
description="Linux kernel",
architectures=["Intel32", "Intel64"],
),
]
@classmethod
def _check_members(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
var_ops: interfaces.objects.ObjectInterface,
var_name: str,
members: List[str],
) -> Generator[Tuple[str, str, int], None, None]:
"""
Yields any members that are not pointing inside the kernel
"""
vmlinux = context.modules[vmlinux_name]
for check in members:
# redhat-specific garbage
if check.startswith("__UNIQUE_ID_rh_kabi_hide"):
continue
# These structures have members like `write` and `next`, which are built in Python functions
addr = var_ops.member(attr=check)
# Unimplemented handlers are set to 0
if not addr:
continue
if len(vmlinux.get_symbols_by_absolute_location(addr)) == 0:
yield var_name, check, addr
@classmethod
def _check_pre_4_18_ops(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
var_name: str,
var: interfaces.objects.ObjectInterface,
op_members: List[str],
seq_members: List[str],
):
"""
Finds the correct way to reference `op_members`
"""
vmlinux = context.modules[vmlinux_name]
if var.has_member("seq_fops"):
yield from cls._check_members(
context, vmlinux_name, var.seq_fops, var_name, op_members
)
# newer kernels
if var.has_member("seq_ops"):
yield from cls._check_members(
context, vmlinux_name, var.seq_ops, var_name, seq_members
)
# this is the most commonly hooked member by rootkits, so a force a check on it
elif var.has_member("seq_show"):
if len(vmlinux.get_symbols_by_location(var.seq_show)) == 0:
yield var_name, "show", var.seq_show
else:
raise exceptions.VolatilityException(
"_check_afinfo_pre_4_18: Unable to find sequence operations members for checking."
)
@classmethod
def _check_afinfo_pre_4_18(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
seq_members: str,
) -> Generator[Tuple[str, str, int], None, None]:
"""
Checks the operations structures for network protocols of < 4.18 systems
"""
tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"])
udp = (
"udp_seq_afinfo",
[
"udplite6_seq_afinfo",
"udp6_seq_afinfo",
"udplite4_seq_afinfo",
"udp4_seq_afinfo",
],
)
protocols = [tcp, udp]
vmlinux = context.modules[vmlinux_name]
op_members = vmlinux.get_type("file_operations").members
# loop through all symbols
for struct_type, global_vars in protocols:
for global_var_name in global_vars:
# this will lookup fail for the IPv6 protocols on kernels without IPv6 support
try:
global_var = vmlinux.object_from_symbol(global_var_name)
except exceptions.SymbolError:
continue
yield from cls._check_pre_4_18_ops(
context,
vmlinux_name,
global_var_name,
global_var,
op_members,
seq_members,
)
@classmethod
def _check_afinfo_post_4_18(
cls,
context: interfaces.context.ContextInterface,
vmlinux_name: str,
seq_members: str,
) -> Generator[Tuple[str, str, int], None, None]:
"""
Checks the operations structures for network protocols of >= 4.18 systems
"""
vmlinux = context.modules[vmlinux_name]
ops_structs = [
"raw_seq_ops",
"udp_seq_ops",
"arp_seq_ops",
"unix_seq_ops",
"udp6_seq_ops",
"raw6_seq_ops",
"tcp_seq_ops",
"tcp4_seq_ops",
"tcp6_seq_ops",
"packet_seq_ops",
]
for protocol_ops_var in ops_structs:
# These will fail if the particular kernel doesn't have support for a protocol like IPv6
try:
protocol_ops = vmlinux.object_from_symbol(protocol_ops_var)
except exceptions.SymbolError:
continue
yield from cls._check_members(
context, vmlinux_name, protocol_ops, protocol_ops_var, seq_members
)
@classmethod
def check_afinfo(
cls, context: interfaces.context.ContextInterface, vmlinux_name
) -> Generator[Tuple[str, str, int], None, None]:
"""
Walks the network protocol operations structures for common network protocols.
Reports any initialized operations members that do not point inside the kernel.
"""
vmlinux = context.modules[vmlinux_name]
type_check = vmlinux.get_type("tcp_seq_afinfo")
if type_check.has_member("seq_fops"):
checker = cls._check_afinfo_pre_4_18
else:
checker = cls._check_afinfo_post_4_18
seq_members = vmlinux.get_type("seq_operations").members
yield from checker(context, vmlinux_name, seq_members)
def _generator(self):
"""
A simple wrapper around `check_afino`
"""
for name, member, address in self.check_afinfo(
self.context, self.config["kernel"]
):
yield 0, (name, member, format_hints.Hex(address))
def run(self):
return renderers.TreeGrid(
[
("Symbol Name", str),
("Member", str),
("Handler Address", format_hints.Hex),
],
self._generator(),
)
@@ -0,0 +1,197 @@
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import logging
from typing import List, Set, Tuple, Iterable
from volatility3.framework.symbols.linux.utilities import (
modules as linux_utilities_modules,
)
from volatility3.framework import interfaces, exceptions, deprecation
from volatility3.framework.configuration import requirements
from volatility3.framework.symbols.linux import extensions
from volatility3.framework.interfaces import plugins
vollog = logging.getLogger(__name__)
class Hidden_modules(plugins.PluginInterface):
"""Carves memory to find hidden kernel modules"""
_required_framework_version = (2, 25, 0)
_version = (3, 0, 2)
@classmethod
def find_hidden_modules(
cls, context, vmlinux_module_name: str
) -> extensions.module:
if context.symbol_space.verify_table_versions(
"dwarf2json", lambda version, _: (not version) or version < (0, 8, 0)
):
raise exceptions.SymbolSpaceError(
"Invalid symbol table, please ensure the ISF table produced by dwarf2json was created with version 0.8.0 or later"
)
known_module_addresses = cls.get_lsmod_module_addresses(
context, vmlinux_module_name
)
modules_memory_boundaries = (
linux_utilities_modules.Modules.get_modules_memory_boundaries(
context, vmlinux_module_name
)
)
yield from linux_utilities_modules.Modules.get_hidden_modules(
context,
vmlinux_module_name,
known_module_addresses,
modules_memory_boundaries,
)
@classmethod
def get_hidden_modules(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
known_module_addresses: Set[int],
modules_memory_boundaries: Tuple,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Enumerate hidden modules by taking advantage of memory address alignment patterns
This technique is much faster and uses less memory than the traditional scan method
in Volatility2, but it doesn't work with older kernels.
From kernels 4.2 struct module allocation are aligned to the L1 cache line size.
In i386/amd64/arm64 this is typically 64 bytes. However, this can be changed in
the Linux kernel configuration via CONFIG_X86_L1_CACHE_SHIFT. The alignment can
also be obtained from the DWARF info i.e. DW_AT_alignment<64>, but dwarf2json
doesn't support this feature yet.
In kernels < 4.2, alignment attributes are absent in the struct module, meaning
alignment cannot be guaranteed. Therefore, for older kernels, it's better to use
the traditional scan technique.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
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.
Yields:
module objects
"""
return linux_utilities_modules.get_hidden_modules(
vmlinux_module_name, known_module_addresses, modules_memory_boundaries
)
run = linux_utilities_modules.ModuleDisplayPlugin.run
_generator = linux_utilities_modules.ModuleDisplayPlugin.generator
implementation = find_hidden_modules
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.VersionRequirement(
name="linux_utilities_modules_module_display_plugin",
component=linux_utilities_modules.ModuleDisplayPlugin,
version=(1, 0, 0),
),
requirements.VersionRequirement(
name="linux_utilities_modules",
component=linux_utilities_modules.Modules,
version=(3, 0, 1),
),
] + linux_utilities_modules.ModuleDisplayPlugin.get_requirements()
@staticmethod
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_modules_memory_boundaries,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
def get_modules_memory_boundaries(
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> Tuple[int, int]:
return linux_utilities_modules.Modules.get_modules_memory_boundaries(
context, vmlinux_module_name
)
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_module_address_alignment,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
@classmethod
def _get_module_address_alignment(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> int:
"""Obtain the module memory address alignment.
struct module is aligned to the L1 cache line, which is typically 64 bytes for most
common i386/AMD64/ARM64 configurations. In some cases, it can be 128 bytes, but this
will still work.
Args:
context: The context to retrieve required elements (layers, symbol tables) from
vmlinux_module_name: The name of the kernel module on which to operate
Returns:
The struct module alignment
"""
return linux_utilities_modules.get_module_address_alignment(
context, vmlinux_module_name
)
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.get_hidden_modules,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
@staticmethod
@deprecation.deprecated_method(
replacement=linux_utilities_modules.Modules.validate_alignment_patterns,
removal_date="2025-09-25",
replacement_version=(3, 0, 0),
)
def _validate_alignment_patterns(
addresses: Iterable[int],
address_alignment: int,
) -> bool:
"""Check if the memory addresses meet our alignments patterns
Args:
addresses: Iterable with the address values
address_alignment: Number of bytes for alignment validation
Returns:
True if all the addresses meet the alignment
"""
return linux_utilities_modules.validate_alignment_patterns(
addresses, address_alignment
)
@classmethod
def get_lsmod_module_addresses(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> Set[int]:
"""Obtain a set the known module addresses from linux.lsmod plugin
Args:
context: The context to retrieve required elements (layers, symbol tables) from
vmlinux_module_name: The name of the kernel module on which to operate
Returns:
A set containing known kernel module addresses
"""
vmlinux = context.modules[vmlinux_module_name]
vmlinux_layer = context.layers[vmlinux.layer_name]
known_module_addresses = {
vmlinux_layer.canonicalize(module.vol.offset)
for module in linux_utilities_modules.Modules.list_modules(
context, vmlinux_module_name
)
}
return known_module_addresses