-
Notifications
You must be signed in to change notification settings - Fork 534
New linux plugin: modxview #1330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ikelos
merged 37 commits into
volatilityfoundation:develop
from
Abyss-W4tcher:modxview_plugin
Jan 18, 2025
Merged
Changes from 10 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
d5a0b93
add TAINT_FLAGS constant
Abyss-W4tcher e1b343a
add module taints parsing apis
Abyss-W4tcher c88ebe8
introduce modxview linux plugin
Abyss-W4tcher 9440f53
use a dict of dataclasses for taint_flags
Abyss-W4tcher 9d08c46
add module offset to seen_addresses
Abyss-W4tcher b209ea3
remove slashes in columns
Abyss-W4tcher 485ef89
remove taints_value overload attr
Abyss-W4tcher dd3542b
explicit loop iterator
Abyss-W4tcher a388895
Merge branch 'volatilityfoundation:develop' into modxview_plugin
Abyss-W4tcher f3d7647
unify Tainting parsing capabilities
Abyss-W4tcher dda104b
move out Tainting capabilities
Abyss-W4tcher 2a5f38e
introduce versioned Linux utilities
Abyss-W4tcher 8bc6259
initial tainting utilities
Abyss-W4tcher 3105a31
leverage Tainting from separated Linux utilities
Abyss-W4tcher 6e4213e
update tainting requirements to new versioned utilities
Abyss-W4tcher 0a35026
2.13.0 -> 2.14.0 bump
Abyss-W4tcher 89b8da8
make self.kernel private and call parent __init__
Abyss-W4tcher 32a9d16
Merge branch 'develop' into modxview_plugin
ikelos 4a34b98
minor readability adjustments
Abyss-W4tcher 38c5cc1
bump framework req to 2.16.0
Abyss-W4tcher a7b4e2f
version check_modules
Abyss-W4tcher 2d262e7
cut unnecessary intermediate LinuxUtilityInterface
Abyss-W4tcher 5c70356
version check_modules requirement
Abyss-W4tcher 302f9fd
cut unnecessary plugin runner functions
Abyss-W4tcher 4115c26
bump framework req to 2.18.0
Abyss-W4tcher bd82f4f
2.16.0 -> 2.18.0 bump
Abyss-W4tcher d956742
remove typing.Set import
Abyss-W4tcher 0e4e751
stateless classmethods
Abyss-W4tcher b447bfa
remove module tainting proxies
Abyss-W4tcher 94704c6
2.16.0 -> 2.17.0 bump
Abyss-W4tcher cd8690a
require framework version 2.17.0
Abyss-W4tcher ca98aa2
Merge branch 'develop' into modxview_plugin
Abyss-W4tcher cc9486c
pre-process module triaging to improve readability
Abyss-W4tcher 3b679cb
explicit None check
Abyss-W4tcher bb6556d
correct arguments for pre_4_10_rc1
Abyss-W4tcher 0b82f73
functools caching and doc.
Abyss-W4tcher 8095924
typo
Abyss-W4tcher File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
# 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, Set, 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 | ||
|
||
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, 11, 0) | ||
|
||
@classmethod | ||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: | ||
return [ | ||
requirements.ModuleRequirement( | ||
name="kernel", | ||
description="Linux kernel", | ||
architectures=architectures.LINUX_ARCHS, | ||
), | ||
requirements.PluginRequirement( | ||
name="lsmod", plugin=lsmod.Lsmod, version=(2, 0, 0) | ||
), | ||
requirements.PluginRequirement( | ||
name="check_modules", | ||
plugin=check_modules.Check_modules, | ||
version=(0, 0, 0), | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
), | ||
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 run_lsmod( | ||
cls, context: interfaces.context.ContextInterface, kernel_name: str | ||
) -> List[extensions.module]: | ||
"""Wrapper for the lsmod plugin.""" | ||
return list(lsmod.Lsmod.list_modules(context, kernel_name)) | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@classmethod | ||
def run_check_modules( | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cls, | ||
context: interfaces.context.ContextInterface, | ||
kernel_name: str, | ||
) -> List[extensions.module]: | ||
"""Wrapper for the check_modules plugin. | ||
Here, we extract the /sys/module/ list.""" | ||
kernel = context.modules[kernel_name] | ||
sysfs_modules: dict = check_modules.Check_modules.get_kset_modules( | ||
context, kernel_name | ||
) | ||
|
||
# Convert get_kset_modules() offsets back to module objects | ||
return [ | ||
kernel.object(object_type="module", offset=m_offset, absolute=True) | ||
for m_offset in sysfs_modules.values() | ||
] | ||
|
||
@classmethod | ||
def run_hidden_modules( | ||
cls, | ||
context: interfaces.context.ContextInterface, | ||
kernel_name: str, | ||
known_modules_addresses: Set[int], | ||
) -> List[extensions.module]: | ||
"""Wrapper for the hidden_modules plugin.""" | ||
modules_memory_boundaries = ( | ||
hidden_modules.Hidden_modules.get_modules_memory_boundaries( | ||
context, kernel_name | ||
) | ||
) | ||
return list( | ||
hidden_modules.Hidden_modules.get_hidden_modules( | ||
context, | ||
kernel_name, | ||
known_modules_addresses, | ||
modules_memory_boundaries, | ||
) | ||
) | ||
|
||
@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 | ||
gcmoreira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@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. | ||
|
||
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 = {} | ||
run_results["lsmod"] = cls.run_lsmod(context, kernel_name) | ||
run_results["check_modules"] = cls.run_check_modules(context, kernel_name) | ||
if run_hidden_modules: | ||
known_module_addresses = set( | ||
context.layers[kernel.layer_name].canonicalize(module.vol.offset) | ||
for module in run_results["lsmod"] + run_results["check_modules"] | ||
) | ||
run_results["hidden_modules"] = cls.run_hidden_modules( | ||
context, kernel_name, known_module_addresses | ||
) | ||
|
||
return run_results | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def _generator(self): | ||
kernel_name = self.config["kernel"] | ||
run_results = self.run_modules_scanners(self.context, kernel_name) | ||
modules_offsets = {} | ||
for key in ["lsmod", "check_modules", "hidden_modules"]: | ||
modules_offsets[key] = set(module.vol.offset for module in run_results[key]) | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
seen_addresses = set() | ||
for modules_list in run_results.values(): | ||
Abyss-W4tcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for module in modules_list: | ||
if module.vol.offset in seen_addresses: | ||
continue | ||
seen_addresses.add(module.vol.offset) | ||
|
||
if self.config.get("plain_taints"): | ||
taints = module.get_taints_as_plain_string() | ||
else: | ||
taints = ",".join(module.get_taints_parsed()) | ||
|
||
yield ( | ||
0, | ||
( | ||
module.get_name() or NotAvailableValue(), | ||
format_hints.Hex(module.vol.offset), | ||
module.vol.offset in modules_offsets["lsmod"], | ||
module.vol.offset in modules_offsets["check_modules"], | ||
module.vol.offset in modules_offsets["hidden_modules"], | ||
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(), | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.