Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d5a0b93
add TAINT_FLAGS constant
Abyss-W4tcher Oct 31, 2024
e1b343a
add module taints parsing apis
Abyss-W4tcher Oct 31, 2024
c88ebe8
introduce modxview linux plugin
Abyss-W4tcher Oct 31, 2024
9440f53
use a dict of dataclasses for taint_flags
Abyss-W4tcher Nov 1, 2024
9d08c46
add module offset to seen_addresses
Abyss-W4tcher Nov 1, 2024
b209ea3
remove slashes in columns
Abyss-W4tcher Nov 1, 2024
485ef89
remove taints_value overload attr
Abyss-W4tcher Nov 8, 2024
dd3542b
explicit loop iterator
Abyss-W4tcher Nov 8, 2024
a388895
Merge branch 'volatilityfoundation:develop' into modxview_plugin
Abyss-W4tcher Dec 22, 2024
f3d7647
unify Tainting parsing capabilities
Abyss-W4tcher Dec 22, 2024
dda104b
move out Tainting capabilities
Abyss-W4tcher Jan 2, 2025
2a5f38e
introduce versioned Linux utilities
Abyss-W4tcher Jan 2, 2025
8bc6259
initial tainting utilities
Abyss-W4tcher Jan 2, 2025
3105a31
leverage Tainting from separated Linux utilities
Abyss-W4tcher Jan 2, 2025
6e4213e
update tainting requirements to new versioned utilities
Abyss-W4tcher Jan 2, 2025
0a35026
2.13.0 -> 2.14.0 bump
Abyss-W4tcher Jan 2, 2025
89b8da8
make self.kernel private and call parent __init__
Abyss-W4tcher Jan 2, 2025
32a9d16
Merge branch 'develop' into modxview_plugin
ikelos Jan 3, 2025
4a34b98
minor readability adjustments
Abyss-W4tcher Jan 3, 2025
38c5cc1
bump framework req to 2.16.0
Abyss-W4tcher Jan 3, 2025
a7b4e2f
version check_modules
Abyss-W4tcher Jan 5, 2025
2d262e7
cut unnecessary intermediate LinuxUtilityInterface
Abyss-W4tcher Jan 5, 2025
5c70356
version check_modules requirement
Abyss-W4tcher Jan 5, 2025
302f9fd
cut unnecessary plugin runner functions
Abyss-W4tcher Jan 5, 2025
4115c26
bump framework req to 2.18.0
Abyss-W4tcher Jan 5, 2025
bd82f4f
2.16.0 -> 2.18.0 bump
Abyss-W4tcher Jan 5, 2025
d956742
remove typing.Set import
Abyss-W4tcher Jan 5, 2025
0e4e751
stateless classmethods
Abyss-W4tcher Jan 11, 2025
b447bfa
remove module tainting proxies
Abyss-W4tcher Jan 16, 2025
94704c6
2.16.0 -> 2.17.0 bump
Abyss-W4tcher Jan 16, 2025
cd8690a
require framework version 2.17.0
Abyss-W4tcher Jan 16, 2025
ca98aa2
Merge branch 'develop' into modxview_plugin
Abyss-W4tcher Jan 18, 2025
cc9486c
pre-process module triaging to improve readability
Abyss-W4tcher Jan 18, 2025
3b679cb
explicit None check
Abyss-W4tcher Jan 18, 2025
bb6556d
correct arguments for pre_4_10_rc1
Abyss-W4tcher Jan 18, 2025
0b82f73
functools caching and doc.
Abyss-W4tcher Jan 18, 2025
8095924
typo
Abyss-W4tcher Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion volatility3/framework/constants/_version.py
Original file line number Diff line number Diff line change
@@ -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 = ""

Expand Down
55 changes: 55 additions & 0 deletions volatility3/framework/constants/linux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__"

Expand Down Expand Up @@ -352,3 +353,57 @@ def flags(self) -> str:
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
"""
1 change: 1 addition & 0 deletions volatility3/framework/plugins/linux/check_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
class Check_modules(plugins.PluginInterface):
"""Compares module list to sysfs info, if available"""

_version = (1, 0, 0)
_required_framework_version = (2, 0, 0)

@classmethod
Expand Down
189 changes: 189 additions & 0 deletions volatility3/framework/plugins/linux/modxview.py
Original file line number Diff line number Diff line change
@@ -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(),
)
1 change: 0 additions & 1 deletion volatility3/framework/symbols/linux/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
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
Expand Down
Empty file.
Loading
Loading