mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-23 10:04:52 +02:00
Merge pull request #1743 from volatilityfoundation/requirement_checks_github_action
Testing: Verify `VersionRequirement`s
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
name: Check Volatility3 Version Requirements
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install .[test]
|
||||
|
||||
- name: Testing...
|
||||
run: |
|
||||
# Verify completeness of ConfigurableInterface requirements
|
||||
python ./test/check_configurable_requirements.py
|
||||
@@ -0,0 +1,370 @@
|
||||
"""
|
||||
This script performs syntax analysis on the volatility3 source tree through a combination of AST analysis and import-time introspection of classes.
|
||||
|
||||
The current checks it implements are:
|
||||
1. Ensure that classes derived from `ConfigurableInterface` properly
|
||||
declare all `VersionableInterface` classes that they make use of in their
|
||||
`get_requirements()` classmethod.
|
||||
|
||||
:WARNING: a notable exception to this are classes defined within factory
|
||||
functions. Because these classes are not created until the factory function
|
||||
is called, they therefore do no exist at import time and cannot be checked
|
||||
by this script. It is important to keep in mind during code review that
|
||||
this is a best-effort check and does not make guarantees about the
|
||||
completeness of declared requirements.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import argparse
|
||||
import ast
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
import pkgutil
|
||||
import sys
|
||||
import types
|
||||
from typing import Any, Iterator, List, Optional, Tuple, Type, Union
|
||||
|
||||
from volatility3.framework import configuration, interfaces
|
||||
from volatility3.framework.deprecation import PluginRenameClass
|
||||
|
||||
logging.basicConfig(format="%(levelname)s: %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NodeVisitor:
|
||||
def visit(self, node):
|
||||
"""Visit a node."""
|
||||
method = "visit_" + node.__class__.__name__
|
||||
visitor = getattr(self, method, self.generic_visit)
|
||||
self.enter(node)
|
||||
result = visitor(node)
|
||||
self.leave(node)
|
||||
return result
|
||||
|
||||
def enter(self, node):
|
||||
"""Called when entering a node."""
|
||||
method = "enter_" + node.__class__.__name__
|
||||
visitor = getattr(self, method, self.generic_enter)
|
||||
return visitor(node)
|
||||
|
||||
def leave(self, node):
|
||||
"""Called when leaving a node."""
|
||||
method = "leave_" + node.__class__.__name__
|
||||
visitor = getattr(self, method, self.generic_leave)
|
||||
return visitor(node)
|
||||
|
||||
def generic_visit(self, node):
|
||||
"""Called if no explicit visitor function exists for a node."""
|
||||
for _, value in ast.iter_fields(node):
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, ast.AST):
|
||||
self.visit(item)
|
||||
elif isinstance(value, ast.AST):
|
||||
self.visit(value)
|
||||
|
||||
def generic_enter(self, node):
|
||||
"""Default enter behavior."""
|
||||
|
||||
def generic_leave(self, node):
|
||||
"""Default leave behavior."""
|
||||
|
||||
|
||||
class CodeViolation(metaclass=abc.ABCMeta):
|
||||
def __init__(self, module: types.ModuleType, node: ast.AST) -> None:
|
||||
self.module = module
|
||||
self.node = node
|
||||
|
||||
def __str__(self):
|
||||
return f"Issue in module {self.module.__name__}: line {self.node.lineno}, col {self.node.col_offset}"
|
||||
|
||||
|
||||
class UnrequiredVersionableUsage(CodeViolation):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module: types.ModuleType,
|
||||
node: ast.AST,
|
||||
consuming_class: str,
|
||||
versionable_item_class: str,
|
||||
) -> None:
|
||||
super().__init__(module, node)
|
||||
self.consuming_class = consuming_class
|
||||
self.versionable_item_class = versionable_item_class
|
||||
|
||||
def __str__(self) -> str:
|
||||
return (
|
||||
super().__str__()
|
||||
+ ": "
|
||||
+ (
|
||||
f"Found usage of {self.versionable_item_class} "
|
||||
f"in class {self.consuming_class} that is not declared "
|
||||
f"in {self.consuming_class}'s `get_requirements()` classmethod"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def is_versionable(var):
|
||||
try:
|
||||
return (
|
||||
issubclass(var, interfaces.configuration.VersionableInterface)
|
||||
and var is not interfaces.configuration.VersionableInterface
|
||||
and not inspect.isabstract(var)
|
||||
and not (hasattr(var, "hidden") and getattr(var, "hidden") is True)
|
||||
)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
|
||||
def is_configurable(var):
|
||||
try:
|
||||
return issubclass(var, interfaces.configuration.ConfigurableInterface)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
|
||||
class ModuleVisitor(NodeVisitor):
|
||||
def __init__(self, module: types.ModuleType) -> None:
|
||||
self._module = module
|
||||
self._scopes = []
|
||||
self._violations = []
|
||||
|
||||
@property
|
||||
def violations(self):
|
||||
return self._violations
|
||||
|
||||
def enter_ClassDef(self, node: ast.ClassDef) -> Any:
|
||||
logger.debug("Entering class %s", node.name)
|
||||
clazz = None
|
||||
try:
|
||||
clazz = vars(self._module)[str(node.name)]
|
||||
except KeyError:
|
||||
logger.debug(
|
||||
"Failed to get %s from module scope: (%s)",
|
||||
node.name,
|
||||
self._module.__name__,
|
||||
)
|
||||
if self._scopes:
|
||||
try:
|
||||
logger.debug(
|
||||
"Attempting to get class %s from scope of %s",
|
||||
node.name,
|
||||
self._scopes[-1].__name__,
|
||||
)
|
||||
clazz = getattr(self._scopes[-1], node.name)
|
||||
except AttributeError:
|
||||
logger.debug(
|
||||
"Class not found in scope of %s", self._scopes[-1].__name__
|
||||
)
|
||||
if clazz:
|
||||
self._scopes.append(clazz)
|
||||
|
||||
if clazz and is_configurable(clazz):
|
||||
logger.info("Checking configurable class %s", clazz.__name__)
|
||||
visitor = ConfigurableClassVisitor(self._module, clazz)
|
||||
visitor.visit(node)
|
||||
self._violations += visitor.violations
|
||||
|
||||
self.generic_visit(node)
|
||||
|
||||
def leave_ClassDef(self, node: ast.ClassDef):
|
||||
logger.debug("Leaving class %s", node.name)
|
||||
try:
|
||||
scoped_class = next(
|
||||
scope for scope in self._scopes if scope.__name__ == node.name
|
||||
)
|
||||
self._scopes.remove(scoped_class)
|
||||
except StopIteration:
|
||||
logger.debug("%s not found in scope list", node.name)
|
||||
|
||||
|
||||
class ConfigurableClassVisitor(NodeVisitor):
|
||||
def __init__(
|
||||
self,
|
||||
module: types.ModuleType,
|
||||
clazz: Optional[Type[interfaces.configuration.ConfigurableInterface]],
|
||||
) -> None:
|
||||
self._module = module
|
||||
self._current_object = None
|
||||
self._clazz = clazz
|
||||
self._seen = set()
|
||||
self._violations: List[CodeViolation] = []
|
||||
|
||||
@property
|
||||
def versioned_classes(self):
|
||||
return (
|
||||
[
|
||||
req._component
|
||||
for req in self._clazz.get_requirements()
|
||||
if isinstance(req, configuration.requirements.VersionRequirement)
|
||||
]
|
||||
if self._clazz is not None
|
||||
else []
|
||||
)
|
||||
|
||||
def check_item(self, item: Type, node: Union[ast.Name, ast.Attribute]):
|
||||
if (
|
||||
is_versionable(item)
|
||||
and self._clazz is not None
|
||||
and item not in self.versioned_classes
|
||||
and item is not self._clazz
|
||||
and not issubclass(self._clazz, PluginRenameClass)
|
||||
):
|
||||
logger.info(
|
||||
"Found versionable item %s, checking against %s",
|
||||
str(item),
|
||||
str(self.versioned_classes),
|
||||
)
|
||||
result = UnrequiredVersionableUsage(
|
||||
self._module, node, self._clazz.__name__, item.__name__
|
||||
)
|
||||
self._violations.append(result)
|
||||
|
||||
@property
|
||||
def violations(self):
|
||||
return self._violations
|
||||
|
||||
def visit_Name(self, node: ast.Name):
|
||||
try:
|
||||
logger.debug(
|
||||
"Checking module %s for name %s", self._module.__name__, node.id
|
||||
)
|
||||
item = vars(self._module)[str(node.id)]
|
||||
logger.debug("Found %s in %s namespace", node.id, self._module.__name__)
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
self.check_item(item, node)
|
||||
|
||||
def visit_Attribute(
|
||||
self, node: ast.Attribute
|
||||
) -> Optional[UnrequiredVersionableUsage]:
|
||||
if self._clazz is None:
|
||||
self.generic_visit(node)
|
||||
return
|
||||
|
||||
if (node.lineno, node.col_offset) in self._seen:
|
||||
return
|
||||
|
||||
self._seen.add((node.lineno, node.col_offset))
|
||||
|
||||
stack = []
|
||||
root = node
|
||||
while True:
|
||||
stack.append(node.attr)
|
||||
if isinstance(node.value, ast.Attribute):
|
||||
node = node.value
|
||||
elif isinstance(node.value, ast.Name):
|
||||
stack.append(node.value.id)
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
current = None
|
||||
logger.debug("Checking %s", ".".join(stack[::-1]))
|
||||
for item in stack[::-1]:
|
||||
try:
|
||||
current = (
|
||||
vars(self._module)[item]
|
||||
if current is None
|
||||
else getattr(current, item)
|
||||
)
|
||||
except (KeyError, AttributeError) as exc:
|
||||
logger.debug(
|
||||
"Failed to get attribute %s (%s)%s",
|
||||
item,
|
||||
exc.__class__.__name__,
|
||||
(" on" + str(current)) if current is not None else "",
|
||||
)
|
||||
break
|
||||
|
||||
self.check_item(current, root)
|
||||
|
||||
|
||||
def report_missing_requirements() -> Iterator[Tuple[str, UnrequiredVersionableUsage]]:
|
||||
vol3 = importlib.import_module("volatility3")
|
||||
|
||||
for _, module_name, _ in pkgutil.walk_packages(
|
||||
vol3.__path__, vol3.__name__ + ".", onerror=lambda _: None
|
||||
):
|
||||
modname = module_name.replace(
|
||||
"volatility3.framework.plugins", "volatility3.plugins"
|
||||
)
|
||||
try:
|
||||
# import the module that we want to check
|
||||
plugin_module = importlib.import_module(modname)
|
||||
|
||||
except ImportError as exc:
|
||||
logger.warning("Failed to import %s: %s", modname, str(exc))
|
||||
continue
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"An unexpected exception occurred while importing %s: %s",
|
||||
modname,
|
||||
str(exc),
|
||||
)
|
||||
continue
|
||||
|
||||
logger.info("Checking module %s", plugin_module.__name__)
|
||||
if plugin_module.__file__ is None:
|
||||
logger.warning("Plugin module %s has no source file", modname)
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(plugin_module.__file__, "rb") as f:
|
||||
source = f.read()
|
||||
except OSError:
|
||||
logger.warning(
|
||||
"Failed to read file contents for %s", plugin_module.__file__
|
||||
)
|
||||
continue
|
||||
|
||||
try:
|
||||
module_ast_root = ast.parse(source)
|
||||
except (SyntaxError, ValueError) as exc:
|
||||
logger.warning(
|
||||
"Failed to parse source for %s: %s", plugin_module.__file__, str(exc)
|
||||
)
|
||||
raise
|
||||
|
||||
mod_visitor = ModuleVisitor(plugin_module)
|
||||
mod_visitor.visit(module_ast_root)
|
||||
|
||||
if mod_visitor.violations:
|
||||
yield from (
|
||||
(plugin_module.__name__, res) for res in iter(mod_visitor.violations)
|
||||
)
|
||||
|
||||
|
||||
def perform_review():
|
||||
found = 0
|
||||
for mod, usage in report_missing_requirements():
|
||||
found += 1
|
||||
print(str(usage))
|
||||
|
||||
if found:
|
||||
print(
|
||||
f"Found {found} issues"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
print("All configurable classes passed validation!")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-v", "--verbose", action="count", dest="verbosity", default=0)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
if args.verbosity == 0:
|
||||
logger.setLevel(logging.WARNING)
|
||||
elif args.verbosity == 1:
|
||||
logger.setLevel(logging.INFO)
|
||||
elif args.verbosity > 1:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
perform_review()
|
||||
@@ -38,6 +38,8 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
DEFAULT_NUM_DISPLAY_BYTES = 128
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -52,9 +54,18 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
reqs: List[interfaces.configuration.RequirementInterface] = []
|
||||
reqs: List[interfaces.configuration.RequirementInterface] = [
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
if cls == Volshell:
|
||||
reqs = [
|
||||
reqs += [
|
||||
requirements.TranslationLayerRequirement(
|
||||
name="primary", description="Memory layer for the kernel"
|
||||
),
|
||||
requirements.URIRequirement(
|
||||
name="script",
|
||||
description="File to load and execute at start",
|
||||
@@ -68,11 +79,8 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
return reqs + [
|
||||
requirements.TranslationLayerRequirement(
|
||||
name="primary", description="Memory layer for the kernel"
|
||||
),
|
||||
]
|
||||
|
||||
return reqs
|
||||
|
||||
def run(
|
||||
self, additional_locals: Dict[str, Any] = {}
|
||||
|
||||
@@ -36,7 +36,12 @@ class Volshell(generic.Volshell):
|
||||
requirements.IntRequirement(
|
||||
name="pid", description="Process ID", optional=True
|
||||
),
|
||||
]
|
||||
requirements.VersionRequirement(
|
||||
name="generic_volshell",
|
||||
component=generic.Volshell,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
] + super().get_requirements()
|
||||
|
||||
def change_task(self, pid=None):
|
||||
"""Change the current process and layer, based on a process ID"""
|
||||
|
||||
@@ -25,7 +25,12 @@ class Volshell(generic.Volshell):
|
||||
requirements.IntRequirement(
|
||||
name="pid", description="Process ID", optional=True
|
||||
),
|
||||
]
|
||||
requirements.VersionRequirement(
|
||||
name="generic_volshell",
|
||||
component=generic.Volshell,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
] + super().get_requirements()
|
||||
|
||||
def change_task(self, pid=None):
|
||||
"""Change the current process and layer, based on a process ID"""
|
||||
|
||||
@@ -23,7 +23,12 @@ class Volshell(generic.Volshell):
|
||||
requirements.IntRequirement(
|
||||
name="pid", description="Process ID", optional=True
|
||||
),
|
||||
]
|
||||
requirements.VersionRequirement(
|
||||
name="generic_volshell",
|
||||
component=generic.Volshell,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
] + super().get_requirements()
|
||||
|
||||
def change_process(self, pid=None):
|
||||
"""Change the current process and layer, based on a process ID"""
|
||||
|
||||
@@ -17,7 +17,7 @@ from volatility3.framework import constants, exceptions, interfaces, layers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.layers import intel, scanners
|
||||
from volatility3.framework.symbols import native
|
||||
from volatility3.framework.symbols.windows.pdbutil import PDBUtility
|
||||
from volatility3.framework.symbols.windows import pdbutil
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
@@ -50,6 +50,21 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface):
|
||||
max_pdb_size = 0x400000
|
||||
exclusion_list = ["linux", "mac"]
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.VersionRequirement(
|
||||
name="pdb_utility",
|
||||
component=pdbutil.PDBUtility,
|
||||
version=(1, 0, 1),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def find_virtual_layers_from_req(
|
||||
self,
|
||||
context: interfaces.context.ContextInterface,
|
||||
@@ -120,7 +135,7 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface):
|
||||
):
|
||||
raise TypeError("PDB name or GUID not a string value")
|
||||
|
||||
PDBUtility.load_windows_symbol_table(
|
||||
pdbutil.PDBUtility.load_windows_symbol_table(
|
||||
context=context,
|
||||
guid=kernel["GUID"],
|
||||
age=kernel["age"],
|
||||
@@ -259,7 +274,7 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface):
|
||||
bytes(name + ".pdb", "utf-8")
|
||||
for name in constants.windows.KERNEL_MODULE_NAMES
|
||||
]
|
||||
kernels = PDBUtility.pdbname_scan(
|
||||
kernels = pdbutil.PDBUtility.pdbname_scan(
|
||||
ctx=context,
|
||||
layer_name=layer_to_scan,
|
||||
start=start_scan_address,
|
||||
@@ -362,7 +377,7 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface):
|
||||
with contextlib.suppress(exceptions.InvalidAddressException):
|
||||
if vlayer.read(address, 0x2) == b"MZ":
|
||||
res = list(
|
||||
PDBUtility.pdbname_scan(
|
||||
pdbutil.PDBUtility.pdbname_scan(
|
||||
ctx=context,
|
||||
layer_name=vlayer.name,
|
||||
page_size=vlayer.page_size,
|
||||
|
||||
@@ -40,7 +40,12 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface):
|
||||
name="SQLiteCache",
|
||||
component=symbol_cache.SqliteCache,
|
||||
version=(1, 0, 0),
|
||||
)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="multi_string_scanner",
|
||||
component=scanners.MultiStringScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@property
|
||||
|
||||
@@ -9,6 +9,7 @@ import struct
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.layers import scanners, segmented
|
||||
from volatility3.framework.symbols import intermed
|
||||
|
||||
@@ -99,6 +100,16 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
context=context, config_path=config_path, name=name, metadata=metadata
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _check_header(
|
||||
cls, base_layer: interfaces.layers.DataLayerInterface, name: str = ""
|
||||
|
||||
@@ -11,6 +11,8 @@ from volatility3.framework.layers.scanners import multiregexp as multiregexp
|
||||
class BytesScanner(layers.ScannerInterface):
|
||||
thread_safe = True
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, needle: bytes) -> None:
|
||||
@@ -38,6 +40,8 @@ class RegExScanner(layers.ScannerInterface):
|
||||
|
||||
thread_safe = True
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, pattern: bytes, flags: int = re.DOTALL) -> None:
|
||||
@@ -57,6 +61,7 @@ class RegExScanner(layers.ScannerInterface):
|
||||
class MultiStringScanner(layers.ScannerInterface):
|
||||
thread_safe = True
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, patterns: List[bytes]) -> None:
|
||||
|
||||
@@ -22,7 +22,12 @@ class Banners(interfaces.plugins.PluginInterface):
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(
|
||||
name="primary", description="Memory layer to scan"
|
||||
)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
|
||||
@@ -40,6 +40,16 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
component=timeliner.TimeLinerInterface,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="multi_string_scanner",
|
||||
component=scanners.MultiStringScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
element_type=int,
|
||||
|
||||
@@ -46,6 +46,11 @@ class Check_modules(plugins.PluginInterface):
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.VersionRequirement(
|
||||
name="modules",
|
||||
component=linux_utilities_modules.Modules,
|
||||
version=(3, 0, 1),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="linux_utilities_modules_module_display_plugin",
|
||||
component=linux_utilities_modules.ModuleDisplayPlugin,
|
||||
|
||||
@@ -93,6 +93,11 @@ class Hidden_modules(plugins.PluginInterface):
|
||||
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
|
||||
|
||||
@@ -36,6 +36,11 @@ class ModuleExtract(interfaces.plugins.PluginInterface):
|
||||
description="Base virtual address to reconstruct an ELF file",
|
||||
optional=False,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="linux_utilities_module_extract",
|
||||
version=(1, 0, 0),
|
||||
component=linux_utilities_module_extract.ModuleExtract,
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
|
||||
@@ -41,6 +41,11 @@ class PsScan(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(4, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="multi_string_scanner",
|
||||
component=scanners.MultiStringScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
|
||||
@@ -46,6 +46,11 @@ class VmaRegExScan(plugins.PluginInterface):
|
||||
requirements.StringRequirement(
|
||||
name="pattern", description="RegEx pattern", optional=False
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="maxsize",
|
||||
description="Maximum size in bytes for displayed context",
|
||||
|
||||
@@ -38,6 +38,16 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
component=timeliner.TimeLinerInterface,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="multi_string_scanner",
|
||||
component=scanners.MultiStringScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
description="Filter on specific process IDs",
|
||||
|
||||
@@ -31,6 +31,11 @@ class List_Files(plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="mount", component=mount.Mount, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="mac_utilities",
|
||||
component=mac.MacUtilities,
|
||||
version=(1, 3, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -39,6 +39,11 @@ class RegExScan(plugins.PluginInterface):
|
||||
default=cls.MAXSIZE_DEFAULT,
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self, regex_pattern):
|
||||
|
||||
@@ -26,6 +26,8 @@ class VMCSTest(enum.IntFlag):
|
||||
|
||||
|
||||
class PageStartScanner(interfaces.layers.ScannerInterface):
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(self, signatures: List[bytes], page_size: int = 0x1000):
|
||||
super().__init__()
|
||||
if not len(signatures):
|
||||
@@ -69,6 +71,11 @@ class Vmscan(plugins.PluginInterface):
|
||||
requirements.TranslationLayerRequirement(
|
||||
name="primary", description="Physical base memory layer"
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="page_start_scanner",
|
||||
component=PageStartScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="log-threshold",
|
||||
description="Number of criteria failed to log to debug output",
|
||||
|
||||
@@ -41,6 +41,11 @@ class CmdScan(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="consoles", component=consoles.Consoles, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="no_registry",
|
||||
description="Don't search the registry for possible values of CommandHistorySize",
|
||||
|
||||
@@ -54,6 +54,11 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="hivelist", component=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="no_registry",
|
||||
description="Don't search the registry for possible values of CommandHistorySize and HistoryBufferMax",
|
||||
|
||||
@@ -37,6 +37,11 @@ class MBRScan(interfaces.plugins.PluginInterface):
|
||||
default=False,
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="multi_string_scanner",
|
||||
component=scanners.MultiStringScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -55,6 +55,8 @@ class PoolConstraint:
|
||||
|
||||
|
||||
class PoolHeaderScanner(interfaces.layers.ScannerInterface):
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module: interfaces.context.ModuleInterface,
|
||||
@@ -142,6 +144,11 @@ class PoolScanner(plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="handles", component=handles.Handles, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pool_header_scanner",
|
||||
component=PoolHeaderScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
|
||||
@@ -63,6 +63,11 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
def _check_for_skeleton_key_vad(
|
||||
|
||||
@@ -42,6 +42,11 @@ class SvcList(svcscan.SvcScan):
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -56,6 +56,11 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="hivelist", component=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="bytes_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -41,6 +41,11 @@ class VadRegExScan(plugins.PluginInterface):
|
||||
element_type=int,
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="regex_scanner",
|
||||
component=scanners.RegExScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.StringRequirement(
|
||||
name="pattern", description="RegEx pattern", optional=False
|
||||
),
|
||||
|
||||
@@ -48,6 +48,11 @@ class VerInfo(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="page_start_scanner",
|
||||
component=scanners.BytesScanner,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="extensive",
|
||||
description="Search physical layer for version information",
|
||||
|
||||
@@ -118,7 +118,12 @@ class YaraScan(plugins.PluginInterface):
|
||||
name="primary",
|
||||
description="Memory layer for the kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="yarascanner",
|
||||
component=YaraScanner,
|
||||
version=(2, 1, 1),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user