mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 09:47:38 +02:00
Merge pull request #1330 from Abyss-W4tcher/modxview_plugin
New linux plugin: modxview
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# We use the SemVer 2.0.0 versioning scheme
|
||||
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
|
||||
VERSION_MINOR = 16 # Number of changes that only add to the interface
|
||||
VERSION_MINOR = 17 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 0 # Number of changes that do not change the interface
|
||||
VERSION_SUFFIX = ""
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Linux-specific values that aren't found in debug symbols
|
||||
"""
|
||||
from enum import IntEnum, Flag
|
||||
from dataclasses import dataclass
|
||||
|
||||
KERNEL_NAME = "__kernel__"
|
||||
|
||||
@@ -352,3 +353,57 @@ NSEC_PER_SEC = 1e9
|
||||
MODULE_MAXIMUM_CORE_SIZE = 20000000
|
||||
MODULE_MAXIMUM_CORE_TEXT_SIZE = 20000000
|
||||
MODULE_MINIMUM_SIZE = 4096
|
||||
|
||||
|
||||
@dataclass
|
||||
class TaintFlag:
|
||||
shift: int
|
||||
desc: str
|
||||
when_present: bool
|
||||
module: bool
|
||||
|
||||
|
||||
TAINT_FLAGS = {
|
||||
"P": TaintFlag(
|
||||
shift=1 << 0, desc="PROPRIETARY_MODULE", when_present=True, module=True
|
||||
),
|
||||
"G": TaintFlag(
|
||||
shift=1 << 0, desc="PROPRIETARY_MODULE", when_present=False, module=True
|
||||
),
|
||||
"F": TaintFlag(shift=1 << 1, desc="FORCED_MODULE", when_present=True, module=False),
|
||||
"S": TaintFlag(
|
||||
shift=1 << 2, desc="CPU_OUT_OF_SPEC", when_present=True, module=False
|
||||
),
|
||||
"R": TaintFlag(shift=1 << 3, desc="FORCED_RMMOD", when_present=True, module=False),
|
||||
"M": TaintFlag(shift=1 << 4, desc="MACHINE_CHECK", when_present=True, module=False),
|
||||
"B": TaintFlag(shift=1 << 5, desc="BAD_PAGE", when_present=True, module=False),
|
||||
"U": TaintFlag(shift=1 << 6, desc="USER", when_present=True, module=False),
|
||||
"D": TaintFlag(shift=1 << 7, desc="DIE", when_present=True, module=False),
|
||||
"A": TaintFlag(
|
||||
shift=1 << 8, desc="OVERRIDDEN_ACPI_TABLE", when_present=True, module=False
|
||||
),
|
||||
"W": TaintFlag(shift=1 << 9, desc="WARN", when_present=True, module=False),
|
||||
"C": TaintFlag(shift=1 << 10, desc="CRAP", when_present=True, module=True),
|
||||
"I": TaintFlag(
|
||||
shift=1 << 11, desc="FIRMWARE_WORKAROUND", when_present=True, module=False
|
||||
),
|
||||
"O": TaintFlag(shift=1 << 12, desc="OOT_MODULE", when_present=True, module=True),
|
||||
"E": TaintFlag(
|
||||
shift=1 << 13, desc="UNSIGNED_MODULE", when_present=True, module=True
|
||||
),
|
||||
"L": TaintFlag(shift=1 << 14, desc="SOFTLOCKUP", when_present=True, module=False),
|
||||
"K": TaintFlag(shift=1 << 15, desc="LIVEPATCH", when_present=True, module=True),
|
||||
"X": TaintFlag(shift=1 << 16, desc="AUX", when_present=True, module=True),
|
||||
"T": TaintFlag(shift=1 << 17, desc="RANDSTRUCT", when_present=True, module=True),
|
||||
"N": TaintFlag(shift=1 << 18, desc="TEST", when_present=True, module=True),
|
||||
}
|
||||
"""Flags used to taint kernel and modules, for debugging purposes.
|
||||
|
||||
Map based on 6.12-rc5.
|
||||
|
||||
Documentation :
|
||||
- https://www.kernel.org/doc/Documentation/admin-guide/sysctl/kernel.rst#:~:text=guide/sysrq.rst.-,tainted,-%3D%3D%3D%3D%3D%3D%3D%0A%0ANon%2Dzero%20if
|
||||
- https://www.kernel.org/doc/Documentation/admin-guide/tainted-kernels.rst#:~:text=More%20detailed%20explanation%20for%20tainting
|
||||
- taint_flag kernel struct
|
||||
- taint_flags kernel constant
|
||||
"""
|
||||
|
||||
@@ -18,6 +18,7 @@ vollog = logging.getLogger(__name__)
|
||||
class Check_modules(plugins.PluginInterface):
|
||||
"""Compares module list to sysfs info, if available"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# 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, Dict, Iterator
|
||||
from volatility3.plugins.linux import lsmod, check_modules, hidden_modules
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints, TreeGrid, NotAvailableValue
|
||||
from volatility3.framework.symbols.linux import extensions
|
||||
from volatility3.framework.constants import architectures
|
||||
from volatility3.framework.symbols.linux.utilities import tainting
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Modxview(interfaces.plugins.PluginInterface):
|
||||
"""Centralize lsmod, check_modules and hidden_modules results to efficiently
|
||||
spot modules presence and taints."""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 17, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Linux kernel",
|
||||
architectures=architectures.LINUX_ARCHS,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="linux-tainting", component=tainting.Tainting, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="lsmod", plugin=lsmod.Lsmod, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="check_modules",
|
||||
plugin=check_modules.Check_modules,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hidden_modules",
|
||||
plugin=hidden_modules.Hidden_modules,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="plain_taints",
|
||||
description="Display the plain taints string for each module.",
|
||||
optional=True,
|
||||
default=False,
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def flatten_run_modules_results(
|
||||
cls, run_results: Dict[str, List[extensions.module]], deduplicate: bool = True
|
||||
) -> Iterator[extensions.module]:
|
||||
"""Flatten a dictionary mapping plugin names and modules list, to a single merged list.
|
||||
This is useful to get a generic lookup list of all the detected modules.
|
||||
|
||||
Args:
|
||||
run_results: dictionary of plugin names mapping a list of detected modules
|
||||
deduplicate: remove duplicate modules, based on their offsets
|
||||
|
||||
Returns:
|
||||
Iterator of modules objects
|
||||
"""
|
||||
seen_addresses = set()
|
||||
for modules in run_results.values():
|
||||
for module in modules:
|
||||
if deduplicate and module.vol.offset in seen_addresses:
|
||||
continue
|
||||
seen_addresses.add(module.vol.offset)
|
||||
yield module
|
||||
|
||||
@classmethod
|
||||
def run_modules_scanners(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_name: str,
|
||||
run_hidden_modules: bool = True,
|
||||
) -> Dict[str, List[extensions.module]]:
|
||||
"""Run module scanning plugins and aggregate the results. It is designed
|
||||
to not operate any inter-plugin results triage.
|
||||
|
||||
Args:
|
||||
run_hidden_modules: specify if the hidden_modules plugin should be run
|
||||
Returns:
|
||||
Dictionary mapping each plugin to its corresponding result
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_name]
|
||||
run_results = {}
|
||||
# lsmod
|
||||
run_results["lsmod"] = list(lsmod.Lsmod.list_modules(context, kernel_name))
|
||||
# check_modules
|
||||
sysfs_modules: dict = check_modules.Check_modules.get_kset_modules(
|
||||
context, kernel_name
|
||||
)
|
||||
## Convert get_kset_modules() offsets back to module objects
|
||||
run_results["check_modules"] = [
|
||||
kernel.object(object_type="module", offset=m_offset, absolute=True)
|
||||
for m_offset in sysfs_modules.values()
|
||||
]
|
||||
# hidden_modules
|
||||
if run_hidden_modules:
|
||||
known_modules_addresses = set(
|
||||
context.layers[kernel.layer_name].canonicalize(module.vol.offset)
|
||||
for module in run_results["lsmod"] + run_results["check_modules"]
|
||||
)
|
||||
modules_memory_boundaries = (
|
||||
hidden_modules.Hidden_modules.get_modules_memory_boundaries(
|
||||
context, kernel_name
|
||||
)
|
||||
)
|
||||
run_results["hidden_modules"] = list(
|
||||
hidden_modules.Hidden_modules.get_hidden_modules(
|
||||
context,
|
||||
kernel_name,
|
||||
known_modules_addresses,
|
||||
modules_memory_boundaries,
|
||||
)
|
||||
)
|
||||
|
||||
return run_results
|
||||
|
||||
def _generator(self):
|
||||
kernel_name = self.config["kernel"]
|
||||
run_results = self.run_modules_scanners(self.context, kernel_name)
|
||||
aggregated_modules = {}
|
||||
# We want to be explicit on the plugins results we are interested in
|
||||
for plugin_name in ["lsmod", "check_modules", "hidden_modules"]:
|
||||
# Iterate over each recovered module
|
||||
for module in run_results[plugin_name]:
|
||||
# Use offsets as unique keys, whether a module
|
||||
# appears in many plugin runs or not
|
||||
if aggregated_modules.get(module.vol.offset, None) is not None:
|
||||
# Append the plugin to the list of originating plugins
|
||||
aggregated_modules[module.vol.offset][1].append(plugin_name)
|
||||
else:
|
||||
aggregated_modules[module.vol.offset] = (module, [plugin_name])
|
||||
|
||||
for module_offset, (module, originating_plugins) in aggregated_modules.items():
|
||||
# Tainting parsing capabilities applied to the module
|
||||
if self.config.get("plain_taints"):
|
||||
taints = tainting.Tainting.get_taints_as_plain_string(
|
||||
self.context,
|
||||
kernel_name,
|
||||
module.taints,
|
||||
True,
|
||||
)
|
||||
else:
|
||||
taints = ",".join(
|
||||
tainting.Tainting.get_taints_parsed(
|
||||
self.context,
|
||||
kernel_name,
|
||||
module.taints,
|
||||
True,
|
||||
)
|
||||
)
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
module.get_name() or NotAvailableValue(),
|
||||
format_hints.Hex(module_offset),
|
||||
"lsmod" in originating_plugins,
|
||||
"check_modules" in originating_plugins,
|
||||
"hidden_modules" in originating_plugins,
|
||||
taints or NotAvailableValue(),
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
columns = [
|
||||
("Name", str),
|
||||
("Address", format_hints.Hex),
|
||||
("In procfs", bool),
|
||||
("In sysfs", bool),
|
||||
("Hidden", bool),
|
||||
("Taints", str),
|
||||
]
|
||||
|
||||
return TreeGrid(
|
||||
columns,
|
||||
self._generator(),
|
||||
)
|
||||
@@ -20,7 +20,6 @@ from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import generic, linux, intermed
|
||||
from volatility3.framework.symbols.linux.extensions import elf
|
||||
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
# Keep these in a basic module, to prevent import cycles when symbol providers require them
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import functools
|
||||
|
||||
from volatility3 import framework
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework.constants import linux as linux_constants
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class Tainting(interfaces.configuration.VersionableInterface):
|
||||
"""Tainted kernel and modules parsing capabilities.
|
||||
|
||||
Relevant Linux kernel functions:
|
||||
- modules: module_flags_taint
|
||||
- kernel: print_tainted
|
||||
"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
framework.require_interface_version(*_required_framework_version)
|
||||
|
||||
@classmethod
|
||||
@functools.lru_cache
|
||||
def _get_kernel_taint_flags_list(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
) -> Optional[List[interfaces.objects.ObjectInterface]]:
|
||||
"""Determine whether the kernel embeds taint flags definition
|
||||
in-memory or not.
|
||||
|
||||
Returns:
|
||||
A list of "taint_flag" kernel objects if taint_flags symbol exists
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
if kernel.has_symbol("taint_flags"):
|
||||
return list(kernel.object_from_symbol("taint_flags"))
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _module_flags_taint_pre_4_10_rc1(
|
||||
cls,
|
||||
taints: int,
|
||||
is_module: bool = False,
|
||||
) -> str:
|
||||
"""Convert the module's taints value to a 1-1 character mapping.
|
||||
Relies on statically defined taints mappings in the framework.
|
||||
|
||||
Args:
|
||||
taints: The taints value, represented by an integer
|
||||
is_module: Indicates if the taints value is associated with a built-in/LKM module
|
||||
|
||||
Returns:
|
||||
The raw taints string.
|
||||
"""
|
||||
taints_string = ""
|
||||
for char, taint_flag in linux_constants.TAINT_FLAGS.items():
|
||||
if is_module and not taint_flag.module:
|
||||
continue
|
||||
|
||||
if taints & taint_flag.shift:
|
||||
taints_string += char
|
||||
|
||||
return taints_string
|
||||
|
||||
@classmethod
|
||||
def _module_flags_taint_post_4_10_rc1(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
taints: int,
|
||||
is_module: bool = False,
|
||||
) -> str:
|
||||
"""Convert the module's taints value to a 1-1 character mapping.
|
||||
Relies on kernel symbol embedded taints definitions.
|
||||
|
||||
struct taint_flag {
|
||||
char c_true; /* character printed when tainted */
|
||||
char c_false; /* character printed when not tainted */
|
||||
bool module; /* also show as a per-module taint flag */
|
||||
};
|
||||
|
||||
Args:
|
||||
taints: The taints value, represented by an integer
|
||||
is_module: Indicates if the taints value is associated with a built-in/LKM module
|
||||
|
||||
Returns:
|
||||
The raw taints string.
|
||||
"""
|
||||
taints_string = ""
|
||||
for taint_bit, taint_flag in enumerate(
|
||||
cls._get_kernel_taint_flags_list(context, kernel_module_name)
|
||||
):
|
||||
if is_module and not taint_flag.module:
|
||||
continue
|
||||
c_true = chr(taint_flag.c_true)
|
||||
c_false = chr(taint_flag.c_false)
|
||||
if taints & (1 << taint_bit):
|
||||
taints_string += c_true
|
||||
elif c_false != " ":
|
||||
taints_string += c_false
|
||||
|
||||
return taints_string
|
||||
|
||||
@classmethod
|
||||
def get_taints_as_plain_string(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
taints: int,
|
||||
is_module: bool = False,
|
||||
) -> str:
|
||||
"""Convert the taints value to a 1-1 character mapping.
|
||||
|
||||
Args:
|
||||
taints: The taints value, represented by an integer
|
||||
is_module: Indicates if the taints value is associated with a built-in/LKM module
|
||||
Returns:
|
||||
The raw taints string.
|
||||
|
||||
Documentation:
|
||||
- module_flags_taint kernel function
|
||||
"""
|
||||
|
||||
if cls._get_kernel_taint_flags_list(context, kernel_module_name):
|
||||
return cls._module_flags_taint_post_4_10_rc1(
|
||||
context, kernel_module_name, taints, is_module
|
||||
)
|
||||
return cls._module_flags_taint_pre_4_10_rc1(taints, is_module)
|
||||
|
||||
@classmethod
|
||||
def get_taints_parsed(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
taints: int,
|
||||
is_module: bool = False,
|
||||
) -> List[str]:
|
||||
"""Convert the taints string to a 1-1 descriptor mapping.
|
||||
|
||||
Args:
|
||||
taints: The taints value, represented by an integer
|
||||
is_module: Indicates if the taints value is associated with a built-in/LKM module
|
||||
|
||||
Returns:
|
||||
A comprehensive (user-friendly) taint descriptor list.
|
||||
|
||||
Documentation:
|
||||
- module_flags_taint kernel function
|
||||
"""
|
||||
comprehensive_taints = []
|
||||
for character in cls.get_taints_as_plain_string(
|
||||
context, kernel_module_name, taints, is_module
|
||||
):
|
||||
taint_flag = linux_constants.TAINT_FLAGS.get(character)
|
||||
if not taint_flag:
|
||||
comprehensive_taints.append(f"<UNKNOWN_TAINT_CHAR_{character}>")
|
||||
elif taint_flag.when_present:
|
||||
comprehensive_taints.append(taint_flag.desc)
|
||||
|
||||
return comprehensive_taints
|
||||
Reference in New Issue
Block a user