mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-17 20:35:40 +02:00
Merge pull request #1884 from volatilityfoundation/issues/ruff-formatter
Apply changes that ruff formatter would make which black doesn't mind
This commit is contained in:
@@ -14,7 +14,6 @@ vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BannerCacheGenerator:
|
||||
|
||||
def __init__(self, path: str, url_prefix: str):
|
||||
self._path = path
|
||||
self._url_prefix = url_prefix
|
||||
@@ -79,7 +78,6 @@ class BannerCacheGenerator:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--path", default=os.path.dirname(__file__))
|
||||
parser.add_argument(
|
||||
|
||||
@@ -208,7 +208,6 @@ class Volatility3PyPyTest(VolatilityTest):
|
||||
|
||||
|
||||
class VolatilityTester:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
images: List[VolatilityImage],
|
||||
|
||||
@@ -22,7 +22,6 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
class PDBRetreiver:
|
||||
|
||||
def retreive_pdb(self, guid: str, file_name: str) -> Optional[str]:
|
||||
logger.info("Download PDB file...")
|
||||
file_name = ".".join(file_name.split(".")[:-1] + ["pdb"])
|
||||
|
||||
@@ -13,7 +13,6 @@ DWARF2JSON = "./dwarf2json"
|
||||
|
||||
|
||||
class Downloader:
|
||||
|
||||
def __init__(self, url_lists: List[List[str]]) -> None:
|
||||
self.url_lists = url_lists
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ try:
|
||||
import pyarrow as pa
|
||||
import pyarrow.parquet as pq
|
||||
import pyarrow.compute as pc
|
||||
|
||||
HAS_PYARROW = True
|
||||
except ImportError:
|
||||
# The user doesn't have pyarrow installed, but HAS_PYARROW will be false so just continue
|
||||
@@ -41,10 +42,33 @@ class TestArrowRendererBase(ABC):
|
||||
table = self._get_table_from_output(out)
|
||||
assert table.num_rows > 10
|
||||
|
||||
assert table.filter(pc.match_substring(pc.utf8_lower(table.column('ImageFileName')), "system")).num_rows > 0
|
||||
assert table.filter(pc.match_substring(pc.utf8_lower(table.column('ImageFileName')), "csrss.exe")).num_rows > 0
|
||||
assert table.filter(pc.match_substring(pc.utf8_lower(table.column('ImageFileName')), "svchost.exe")).num_rows > 0
|
||||
assert table.filter(pc.greater(table.column('PID'), 0)).num_rows == table.num_rows
|
||||
assert (
|
||||
table.filter(
|
||||
pc.match_substring(
|
||||
pc.utf8_lower(table.column("ImageFileName")), "system"
|
||||
)
|
||||
).num_rows
|
||||
> 0
|
||||
)
|
||||
assert (
|
||||
table.filter(
|
||||
pc.match_substring(
|
||||
pc.utf8_lower(table.column("ImageFileName")), "csrss.exe"
|
||||
)
|
||||
).num_rows
|
||||
> 0
|
||||
)
|
||||
assert (
|
||||
table.filter(
|
||||
pc.match_substring(
|
||||
pc.utf8_lower(table.column("ImageFileName")), "svchost.exe"
|
||||
)
|
||||
).num_rows
|
||||
> 0
|
||||
)
|
||||
assert (
|
||||
table.filter(pc.greater(table.column("PID"), 0)).num_rows == table.num_rows
|
||||
)
|
||||
|
||||
def test_linux_generic_pslist(self, volatility, python, image):
|
||||
rc, out, _err = test_volatility.runvol_plugin(
|
||||
@@ -59,12 +83,23 @@ class TestArrowRendererBase(ABC):
|
||||
table = self._get_table_from_output(out)
|
||||
assert table.num_rows > 10
|
||||
|
||||
init_rows = table.filter(pc.match_substring(pc.utf8_lower(table.column('COMM')), "init"))
|
||||
systemd_rows = table.filter(pc.match_substring(pc.utf8_lower(table.column('COMM')), "systemd"))
|
||||
init_rows = table.filter(
|
||||
pc.match_substring(pc.utf8_lower(table.column("COMM")), "init")
|
||||
)
|
||||
systemd_rows = table.filter(
|
||||
pc.match_substring(pc.utf8_lower(table.column("COMM")), "systemd")
|
||||
)
|
||||
assert (init_rows.num_rows > 0) or (systemd_rows.num_rows > 0)
|
||||
|
||||
assert table.filter(pc.match_substring(pc.utf8_lower(table.column('COMM')), "watchdog")).num_rows > 0
|
||||
assert table.filter(pc.greater(table.column('PID'), 0)).num_rows == table.num_rows
|
||||
assert (
|
||||
table.filter(
|
||||
pc.match_substring(pc.utf8_lower(table.column("COMM")), "watchdog")
|
||||
).num_rows
|
||||
> 0
|
||||
)
|
||||
assert (
|
||||
table.filter(pc.greater(table.column("PID"), 0)).num_rows == table.num_rows
|
||||
)
|
||||
|
||||
def test_windows_generic_handles(self, volatility, python, image):
|
||||
rc, out, _err = test_volatility.runvol_plugin(
|
||||
@@ -79,7 +114,14 @@ class TestArrowRendererBase(ABC):
|
||||
|
||||
table = self._get_table_from_output(out)
|
||||
assert table.num_rows > 500
|
||||
assert table.filter(pc.match_substring(pc.utf8_lower(table.column('Name')), "machine\\system")).num_rows > 0
|
||||
assert (
|
||||
table.filter(
|
||||
pc.match_substring(
|
||||
pc.utf8_lower(table.column("Name")), "machine\\system"
|
||||
)
|
||||
).num_rows
|
||||
> 0
|
||||
)
|
||||
|
||||
def test_linux_generic_lsof(self, volatility, python, image):
|
||||
rc, out, _err = test_volatility.runvol_plugin(
|
||||
@@ -94,6 +136,7 @@ class TestArrowRendererBase(ABC):
|
||||
table = self._get_table_from_output(out)
|
||||
assert table.num_rows > 35
|
||||
|
||||
|
||||
class TestParquetRenderer(TestArrowRendererBase):
|
||||
renderer_format = "parquet"
|
||||
|
||||
@@ -106,4 +149,3 @@ class TestArrowRenderer(TestArrowRendererBase):
|
||||
|
||||
def _get_table_from_output(self, output_bytes):
|
||||
return pa.ipc.open_stream(io.BytesIO(output_bytes)).read_all()
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ class CodeViolation(metaclass=abc.ABCMeta):
|
||||
|
||||
|
||||
class UnrequiredVersionableUsage(CodeViolation):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module: types.ModuleType,
|
||||
@@ -107,7 +106,6 @@ class UnrequiredVersionableUsage(CodeViolation):
|
||||
|
||||
|
||||
class DirectVolatilityImportUsage(CodeViolation):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module: types.ModuleType,
|
||||
@@ -174,8 +172,11 @@ class ModuleVisitor(NodeVisitor):
|
||||
"""
|
||||
if (
|
||||
node.module
|
||||
and node.module.startswith("volatility3.") # Give a pass to volatility3 module
|
||||
and node.module != "volatility3.framework.constants._version" # make an exception for this
|
||||
and node.module.startswith(
|
||||
"volatility3."
|
||||
) # Give a pass to volatility3 module
|
||||
and node.module
|
||||
!= "volatility3.framework.constants._version" # make an exception for this
|
||||
):
|
||||
for name in node.names:
|
||||
try:
|
||||
@@ -204,7 +205,6 @@ class ModuleVisitor(NodeVisitor):
|
||||
def enter_ImportFrom(self, node: ast.ImportFrom):
|
||||
self._check_vol3_import_from(node)
|
||||
|
||||
|
||||
def enter_ClassDef(self, node: ast.ClassDef) -> Any:
|
||||
logger.debug("Entering class %s", node.name)
|
||||
clazz = None
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
"""Volatility 3 - An open-source memory forensics framework"""
|
||||
|
||||
import inspect
|
||||
import sys
|
||||
from importlib import abc
|
||||
|
||||
@@ -10,6 +10,7 @@ User interfaces make use of the framework to:
|
||||
* run the plugin
|
||||
* display the results
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import inspect
|
||||
import io
|
||||
@@ -458,8 +459,9 @@ class CommandLine:
|
||||
raise ValueError(
|
||||
"Invalid extension (extensions must be of the format \"conf.path.value='value'\")"
|
||||
)
|
||||
address, value = extension[: extension.find("=")], json.loads(
|
||||
extension[extension.find("=") + 1 :]
|
||||
address, value = (
|
||||
extension[: extension.find("=")],
|
||||
json.loads(extension[extension.find("=") + 1 :]),
|
||||
)
|
||||
ctx.config[address] = value
|
||||
|
||||
@@ -574,7 +576,7 @@ class CommandLine:
|
||||
delayed_logs.append(
|
||||
(
|
||||
logging.DEBUG,
|
||||
f"Loaded configuration: {json.dumps(result, indent = 2, sort_keys = True)}",
|
||||
f"Loaded configuration: {json.dumps(result, indent=2, sort_keys=True)}",
|
||||
)
|
||||
)
|
||||
return delayed_logs, result
|
||||
@@ -763,7 +765,7 @@ class CommandLine:
|
||||
constants.LOGLEVEL_VVVV,
|
||||
]
|
||||
):
|
||||
logging.addLevelName(level_value, f"DETAIL {level+1}")
|
||||
logging.addLevelName(level_value, f"DETAIL {level + 1}")
|
||||
|
||||
def file_handler_class_factory(self, direct=True):
|
||||
output_dir = self.output_dir
|
||||
|
||||
@@ -278,7 +278,6 @@ class CLIRenderer(interfaces.renderers.Renderer):
|
||||
|
||||
|
||||
class QuickTextRenderer(CLIRenderer):
|
||||
|
||||
name = "quick"
|
||||
|
||||
def get_render_options(self):
|
||||
@@ -348,7 +347,6 @@ class NoneRenderer(CLIRenderer):
|
||||
|
||||
|
||||
class CSVRenderer(CLIRenderer):
|
||||
|
||||
name = "csv"
|
||||
structured_output = True
|
||||
|
||||
|
||||
@@ -344,8 +344,9 @@ class VolShell(cli.CommandLine):
|
||||
raise ValueError(
|
||||
"Invalid extension (extensions must be of the format \"conf.path.value='value'\")"
|
||||
)
|
||||
address, value = extension[: extension.find("=")], json.loads(
|
||||
extension[extension.find("=") + 1 :]
|
||||
address, value = (
|
||||
extension[: extension.find("=")],
|
||||
json.loads(extension[extension.find("=") + 1 :]),
|
||||
)
|
||||
ctx.config[address] = value
|
||||
|
||||
|
||||
@@ -469,7 +469,7 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
and dereference_count < MAX_DEREFERENCE_COUNT
|
||||
):
|
||||
# before defreerencing the pointer, show it's information
|
||||
print(f'{" " * dereference_count}{self._display_simple_type(volobject)}')
|
||||
print(f"{' ' * dereference_count}{self._display_simple_type(volobject)}")
|
||||
|
||||
# check that we can follow the pointer before dereferencing and do not
|
||||
# attempt to follow null pointers.
|
||||
@@ -486,7 +486,7 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
|
||||
if hasattr(volobject.vol, "members"):
|
||||
# display the header for this object, if the original object was just a type string, display the type information
|
||||
struct_header = f'{" " * dereference_count}{volobject.vol.type_name} ({volobject.vol.size} bytes)'
|
||||
struct_header = f"{' ' * dereference_count}{volobject.vol.type_name} ({volobject.vol.size} bytes)"
|
||||
if isinstance(object, str) and offset is None:
|
||||
suffix = ":"
|
||||
else:
|
||||
@@ -523,7 +523,7 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
len_typename = len(member_type_name)
|
||||
if len(member_type_name) > MAX_TYPENAME_DISPLAY_LENGTH:
|
||||
len_typename = MAX_TYPENAME_DISPLAY_LENGTH
|
||||
member_type_name = f"{member_type_name[:len_typename - 3]}..."
|
||||
member_type_name = f"{member_type_name[: len_typename - 3]}..."
|
||||
|
||||
if isinstance(volobject, interfaces.objects.ObjectInterface):
|
||||
# We're an instance, so also display the data
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
"""Volatility 3 framework."""
|
||||
|
||||
# Check the python version to ensure it's suitable
|
||||
import glob
|
||||
import sys
|
||||
|
||||
@@ -7,6 +7,7 @@ from loaded PE files.
|
||||
This module contains a standalone scanner, and also a :class:`~volatility3.framework.interfaces.layers.ScannerInterface`
|
||||
based scanner for use within the framework by calling :func:`~volatility3.framework.interfaces.layers.DataLayerInterface.scan`.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import math
|
||||
|
||||
@@ -153,8 +153,9 @@ class LayerStacker(interfaces.automagic.AutomagicInterface):
|
||||
constructor(context, config_path, requirement)
|
||||
|
||||
# Stash the changed config items
|
||||
self._cached = context.config.get(path, None), context.config.branch(
|
||||
path
|
||||
self._cached = (
|
||||
context.config.get(path, None),
|
||||
context.config.branch(path),
|
||||
)
|
||||
vollog.debug(
|
||||
f"physical_layer maximum_address: {physical_layer.maximum_address}"
|
||||
|
||||
@@ -26,6 +26,7 @@ The self-referential indices for older versions of windows are listed below:
|
||||
| x64 | 0x1ED |
|
||||
+--------------+-------+
|
||||
"""
|
||||
|
||||
import logging
|
||||
import struct
|
||||
from typing import Generator, Iterable, List, Optional, Tuple, Type
|
||||
|
||||
@@ -8,6 +8,7 @@ These requirement types allow plugins to request simple information
|
||||
types (such as strings, integers, etc) as well as indicating what they
|
||||
expect to be in the context (such as particular layers or symboltables).
|
||||
"""
|
||||
|
||||
import abc
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
Linux-specific values that aren't found in debug symbols
|
||||
"""
|
||||
|
||||
import enum
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ This has been made an object to allow quick swapping and changing of
|
||||
contexts, to allow a plugin to act on multiple different contexts
|
||||
without them interfering with each other.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
@@ -79,7 +79,7 @@ def deprecated_method(
|
||||
"This is a bug, the deprecated call needs to be removed and the caller needs to update their code to use the new method.",
|
||||
)
|
||||
|
||||
deprecation_msg = f"Method \"{deprecated_func.__module__ + '.' + deprecated_func.__qualname__}\" is deprecated and will be removed in the first release after {removal_date}, use \"{replacement.__module__ + '.' + replacement.__qualname__}\" instead. {additional_information}"
|
||||
deprecation_msg = f'Method "{deprecated_func.__module__ + "." + deprecated_func.__qualname__}" is deprecated and will be removed in the first release after {removal_date}, use "{replacement.__module__ + "." + replacement.__qualname__}" instead. {additional_information}'
|
||||
warnings.warn(deprecation_msg, FutureWarning)
|
||||
# Return the wrapped function with its original arguments
|
||||
return deprecated_func(*args, **kwargs)
|
||||
|
||||
@@ -8,6 +8,7 @@ space or symbol tables, and by layers when an address is invalid. The
|
||||
:class:`PagedInvalidAddressException` contains information about the
|
||||
size of the invalid page.
|
||||
"""
|
||||
|
||||
from typing import Callable, Dict, Optional, Tuple
|
||||
|
||||
from volatility3.framework import interfaces
|
||||
@@ -161,4 +162,4 @@ class VersionMismatchException(VolatilityException):
|
||||
self.failure_reason = failure_reason
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.source_component.__module__+ '.' + self.source_component.__qualname__}: Version {self.target_version} dependency on {self.target_component.__module__+ '.' + self.target_component.__name__} {self.target_component.version} unmet."
|
||||
return f"{self.source_component.__module__ + '.' + self.source_component.__qualname__}: Version {self.target_version} dependency on {self.target_component.__module__ + '.' + self.target_component.__name__} {self.target_component.version} unmet."
|
||||
|
||||
@@ -7,6 +7,7 @@ runs.
|
||||
Automagic objects attempt to automatically fill configuration values
|
||||
that a user has not filled.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from abc import ABCMeta
|
||||
from typing import Any, List, Optional, Tuple, Type, Union
|
||||
|
||||
@@ -11,6 +11,7 @@ convenience functions, most notably the object constructor function,
|
||||
`object`, which will construct a symbol on a layer at a particular
|
||||
offset.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import copy
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
One layer may combine other layers, map data based on the data itself,
|
||||
or map a procedure (such as decryption) across another layer of data.
|
||||
"""
|
||||
|
||||
import collections.abc
|
||||
import functools
|
||||
import logging
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
"""Objects are the core of volatility, and provide pythonic access to
|
||||
interpreted values of data from a layer."""
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import collections.abc
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
"""Symbols provide structural information about a set of bytes."""
|
||||
|
||||
import bisect
|
||||
import collections.abc
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
The user of the file doesn't have to worry about the compression,
|
||||
but random access is not allowed."""
|
||||
|
||||
import ctypes
|
||||
import logging
|
||||
import struct
|
||||
|
||||
@@ -2,7 +2,4 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
"""Codecs used for encoding or decoding data should live here
|
||||
|
||||
|
||||
"""
|
||||
"""Codecs used for encoding or decoding data should live here"""
|
||||
|
||||
@@ -315,7 +315,13 @@ class Intel(linear.LinearlyMappedLayer):
|
||||
):
|
||||
# The block isn't contiguous
|
||||
if stashed_offset is not None:
|
||||
yield stashed_offset, stashed_size, stashed_mapped_offset, stashed_mapped_size, stashed_map_layer
|
||||
yield (
|
||||
stashed_offset,
|
||||
stashed_size,
|
||||
stashed_mapped_offset,
|
||||
stashed_mapped_size,
|
||||
stashed_map_layer,
|
||||
)
|
||||
# Update all the stashed values after output
|
||||
stashed_offset = offset
|
||||
stashed_mapped_offset = mapped_offset
|
||||
@@ -334,7 +340,13 @@ class Intel(linear.LinearlyMappedLayer):
|
||||
and stashed_mapped_size is not None
|
||||
and stashed_map_layer is not None
|
||||
):
|
||||
yield stashed_offset, stashed_size, stashed_mapped_offset, stashed_mapped_size, stashed_map_layer
|
||||
yield (
|
||||
stashed_offset,
|
||||
stashed_size,
|
||||
stashed_mapped_offset,
|
||||
stashed_mapped_size,
|
||||
stashed_map_layer,
|
||||
)
|
||||
|
||||
def _mapping(
|
||||
self, offset: int, length: int, ignore_errors: bool = False
|
||||
|
||||
@@ -234,9 +234,13 @@ class PdbMSFStream(linear.LinearlyMappedLayer):
|
||||
layer_name=self.name, invalid_address=offset + returned
|
||||
)
|
||||
else:
|
||||
yield offset + returned, chunk_size, (
|
||||
self._pages[page] * page_size
|
||||
) + page_position, chunk_size, self._base_layer
|
||||
yield (
|
||||
offset + returned,
|
||||
chunk_size,
|
||||
(self._pages[page] * page_size) + page_position,
|
||||
chunk_size,
|
||||
self._base_layer,
|
||||
)
|
||||
returned += chunk_size
|
||||
length -= chunk_size
|
||||
|
||||
|
||||
@@ -305,8 +305,9 @@ class JarHandler(VolatilityHandler):
|
||||
def default_open(req: urllib.request.Request) -> Optional[Any]:
|
||||
"""Handles the request if it's the jar scheme."""
|
||||
if req.type == "jar":
|
||||
subscheme, remainder = req.full_url.split(":")[1], ":".join(
|
||||
req.full_url.split(":")[2:]
|
||||
subscheme, remainder = (
|
||||
req.full_url.split(":")[1],
|
||||
":".join(req.full_url.split(":")[2:]),
|
||||
)
|
||||
if subscheme != "file":
|
||||
vollog.log(
|
||||
|
||||
@@ -129,7 +129,13 @@ class NonLinearlySegmentedLayer(
|
||||
return None
|
||||
# Crop it to the amount we need left
|
||||
chunk_size = min(size, length + offset - logical_offset)
|
||||
yield logical_offset, chunk_size, mapped_offset, mapped_size, self._base_layer
|
||||
yield (
|
||||
logical_offset,
|
||||
chunk_size,
|
||||
mapped_offset,
|
||||
mapped_size,
|
||||
self._base_layer,
|
||||
)
|
||||
current_offset += chunk_size
|
||||
# Terminate if we've gone (or reached) our required limit
|
||||
if current_offset >= offset + length:
|
||||
|
||||
@@ -65,10 +65,10 @@ class VmwareLayer(segmented.SegmentedLayer):
|
||||
data = meta_layer.read(0, header_size)
|
||||
magic, unknown, groupCount = struct.unpack(self.header_structure, data)
|
||||
if magic not in [
|
||||
b"\xD0\xBE\xD2\xBE",
|
||||
b"\xD1\xBA\xD1\xBA",
|
||||
b"\xD2\xBE\xD2\xBE",
|
||||
b"\xD3\xBE\xD3\xBE",
|
||||
b"\xd0\xbe\xd2\xbe",
|
||||
b"\xd1\xba\xd1\xba",
|
||||
b"\xd2\xbe\xd2\xbe",
|
||||
b"\xd3\xbe\xd3\xbe",
|
||||
]:
|
||||
raise VmwareFormatException(
|
||||
self.name, f"Wrong magic bytes for Vmware layer: {repr(magic)}"
|
||||
|
||||
@@ -60,8 +60,9 @@ class Banners(interfaces.plugins.PluginInterface):
|
||||
not in b" #()+,;/-.0123456789:@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
|
||||
]
|
||||
if not failed:
|
||||
yield format_hints.Hex(offset), str(
|
||||
data, encoding="latin-1", errors="?"
|
||||
yield (
|
||||
format_hints.Hex(offset),
|
||||
str(data, encoding="latin-1", errors="?"),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -72,9 +72,12 @@ class IsfInfo(plugins.PluginInterface):
|
||||
for extension in constants.ISF_EXTENSIONS:
|
||||
# By ending with an extension (and therefore, not /), we should not return any directories
|
||||
if name.endswith(extension):
|
||||
yield "jar:file:" + str(
|
||||
pathlib.Path(base_name)
|
||||
) + "!" + name
|
||||
yield (
|
||||
"jar:file:"
|
||||
+ str(pathlib.Path(base_name))
|
||||
+ "!"
|
||||
+ name
|
||||
)
|
||||
|
||||
else:
|
||||
for extension in constants.ISF_EXTENSIONS:
|
||||
|
||||
@@ -245,7 +245,6 @@ You can try using ffmpeg to decode the raw buffer. Example usage:
|
||||
return fb
|
||||
|
||||
def _generator(self):
|
||||
|
||||
if not has_pil:
|
||||
vollog.error(
|
||||
"PIL (pillow) module is required to use this plugin. Please install it manually or through pyproject.toml."
|
||||
|
||||
@@ -47,7 +47,17 @@ class Addr(plugins.PluginInterface):
|
||||
prefix_len = in_ifaddr.get_prefix_len()
|
||||
scope_type = in_ifaddr.get_scope_type()
|
||||
ip_addr = in_ifaddr.get_address()
|
||||
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip_addr, prefix_len, scope_type, operational_state
|
||||
yield (
|
||||
net_ns_id,
|
||||
iface_ifindex,
|
||||
iface_name,
|
||||
mac_addr,
|
||||
promisc,
|
||||
ip_addr,
|
||||
prefix_len,
|
||||
scope_type,
|
||||
operational_state,
|
||||
)
|
||||
|
||||
# Interface IPv6 Addresses
|
||||
inet6_dev = net_dev.ip6_ptr.dereference().cast("inet6_dev")
|
||||
@@ -55,7 +65,17 @@ class Addr(plugins.PluginInterface):
|
||||
prefix_len = inet6_ifaddr.get_prefix_len()
|
||||
scope_type = inet6_ifaddr.get_scope_type()
|
||||
ip6_addr = inet6_ifaddr.get_address()
|
||||
yield net_ns_id, iface_ifindex, iface_name, mac_addr, promisc, ip6_addr, prefix_len, scope_type, operational_state
|
||||
yield (
|
||||
net_ns_id,
|
||||
iface_ifindex,
|
||||
iface_name,
|
||||
mac_addr,
|
||||
promisc,
|
||||
ip6_addr,
|
||||
prefix_len,
|
||||
scope_type,
|
||||
operational_state,
|
||||
)
|
||||
|
||||
def _enumerate_net_namespace_list(self):
|
||||
vmlinux = self.context.modules[self.config["kernel"]]
|
||||
@@ -82,7 +102,9 @@ class Addr(plugins.PluginInterface):
|
||||
scope_type,
|
||||
operational_state,
|
||||
) in self._gather_net_dev_info(net_dev):
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
net_ns_id or renderers.NotAvailableValue(),
|
||||
iface_ifindex,
|
||||
iface_name,
|
||||
@@ -92,6 +114,7 @@ class Addr(plugins.PluginInterface):
|
||||
prefix_len,
|
||||
scope_type,
|
||||
operational_state,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -150,7 +173,16 @@ class Link(plugins.PluginInterface):
|
||||
]
|
||||
flags_str = ",".join(flags_list)
|
||||
|
||||
yield net_ns_id or renderers.NotAvailableValue(), iface_name, mac_addr, operational_state, mtu, qdisc_name or renderers.NotAvailableValue(), qlen, flags_str
|
||||
yield (
|
||||
net_ns_id or renderers.NotAvailableValue(),
|
||||
iface_name,
|
||||
mac_addr,
|
||||
operational_state,
|
||||
mtu,
|
||||
qdisc_name or renderers.NotAvailableValue(),
|
||||
qlen,
|
||||
flags_str,
|
||||
)
|
||||
|
||||
def _generator(self):
|
||||
vmlinux = self.context.modules[self.config["kernel"]]
|
||||
|
||||
@@ -551,12 +551,15 @@ class Kmsg(interfaces.plugins.PluginInterface):
|
||||
for facility, level, timestamp, caller, line in ABCKmsg.run_all(
|
||||
context=self.context, config=self.config
|
||||
):
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
facility,
|
||||
level,
|
||||
timestamp,
|
||||
caller or renderers.NotAvailableValue(),
|
||||
line,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -235,5 +235,9 @@ class Lsof(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
)
|
||||
|
||||
yield description, timeliner.TimeLinerType.CHANGED, fd_user.change_time
|
||||
yield description, timeliner.TimeLinerType.MODIFIED, fd_user.modification_time
|
||||
yield (
|
||||
description,
|
||||
timeliner.TimeLinerType.MODIFIED,
|
||||
fd_user.modification_time,
|
||||
)
|
||||
yield description, timeliner.TimeLinerType.ACCESSED, fd_user.access_time
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
"""A module containing a plugin that verifies the operation function
|
||||
pointers of network protocols."""
|
||||
|
||||
import logging
|
||||
from typing import List, Tuple, Generator
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
"""A module containing a plugin that checks the system call table for hooks."""
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
@@ -223,7 +223,16 @@ class AbstractNetfilter(ABC):
|
||||
)
|
||||
hooked = module_info is None
|
||||
|
||||
yield netns, proto_name, hook_name, priority, hook_ops_hook, module_info, symbol_name, hooked
|
||||
yield (
|
||||
netns,
|
||||
proto_name,
|
||||
hook_name,
|
||||
priority,
|
||||
hook_ops_hook,
|
||||
module_info,
|
||||
symbol_name,
|
||||
hooked,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
|
||||
@@ -100,11 +100,14 @@ class Tty_Check(plugins.PluginInterface):
|
||||
else:
|
||||
module_name = renderers.NotAvailableValue()
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
name,
|
||||
format_hints.Hex(recv_buf),
|
||||
module_name,
|
||||
symbol_name or renderers.NotAvailableValue(),
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -75,10 +75,13 @@ class ModuleExtract(interfaces.plugins.PluginInterface):
|
||||
with self.open(file_name) as file_handle:
|
||||
file_handle.write(elf_data)
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(base_address),
|
||||
len(elf_data),
|
||||
file_handle.preferred_filename,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -386,7 +386,11 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
inode_out = inode_in.to_user(vmlinux_layer)
|
||||
description = f"Cached Inode for {inode_out.path}"
|
||||
yield description, timeliner.TimeLinerType.ACCESSED, inode_out.access_time
|
||||
yield description, timeliner.TimeLinerType.MODIFIED, inode_out.modification_time
|
||||
yield (
|
||||
description,
|
||||
timeliner.TimeLinerType.MODIFIED,
|
||||
inode_out.modification_time,
|
||||
)
|
||||
yield description, timeliner.TimeLinerType.CHANGED, inode_out.change_time
|
||||
|
||||
@classmethod
|
||||
@@ -813,7 +817,6 @@ class RecoverFs(plugins.PluginInterface):
|
||||
|
||||
visited_paths = seen_prefixes = set()
|
||||
for inode_in in inodes_iter:
|
||||
|
||||
# Code is slightly duplicated here with the if-block below.
|
||||
# However this prevents unneeded tar manipulation if fifo
|
||||
# or sock inodes come through for example.
|
||||
|
||||
@@ -225,7 +225,9 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
task_euid = self._format_cred(task_fields.euid)
|
||||
task_egid = self._format_cred(task_fields.egid)
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(task_fields.offset),
|
||||
task_fields.user_pid,
|
||||
task_fields.user_tid,
|
||||
@@ -237,6 +239,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
task_egid,
|
||||
task_fields.creation_time or renderers.NotAvailableValue(),
|
||||
file_output,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -70,7 +70,6 @@ class PerfEvents(plugins.PluginInterface):
|
||||
for task in pslist.PsList.list_tasks(
|
||||
context, vmlinux_module_name, include_threads=True
|
||||
):
|
||||
|
||||
# walk the list of perf_event entries for this process
|
||||
for event in task.perf_event_list.to_list(
|
||||
vmlinux.symbol_table_name + constants.BANG + "perf_event", "owner_entry"
|
||||
|
||||
@@ -64,7 +64,6 @@ class VmaRegExScan(plugins.PluginInterface):
|
||||
vollog.debug(f"RegEx Pattern: {regex_pattern}")
|
||||
|
||||
for task in tasks:
|
||||
|
||||
if not task.mm:
|
||||
continue
|
||||
name = utility.array_to_string(task.comm)
|
||||
@@ -106,12 +105,15 @@ class VmaRegExScan(plugins.PluginInterface):
|
||||
bytes_result = result_data
|
||||
|
||||
user_pid = task.tgid
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
user_pid,
|
||||
name,
|
||||
format_hints.Hex(offset),
|
||||
text_result,
|
||||
bytes_result,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -103,12 +103,15 @@ class VmaYaraScan(interfaces.plugins.PluginInterface):
|
||||
layer_name=proc_layer.name,
|
||||
length=len(value),
|
||||
)
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(offset),
|
||||
task.tgid,
|
||||
rule_name,
|
||||
name,
|
||||
layer_data,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
found in Mac's lsmod command."""
|
||||
|
||||
from typing import Set
|
||||
|
||||
from volatility3.framework import renderers, interfaces, exceptions
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
found in Mac's mount command."""
|
||||
|
||||
from volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
"""In-memory artifacts from OSX systems."""
|
||||
|
||||
from typing import Iterator, Tuple, Any, Generator, List
|
||||
|
||||
from volatility3.framework import exceptions, renderers, interfaces
|
||||
|
||||
@@ -78,7 +78,6 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
def _create_default_scan_constraints(
|
||||
context: interfaces.context.ContextInterface, symbol_table: str
|
||||
) -> List[poolscanner.PoolConstraint]:
|
||||
|
||||
shutdown_packet_size = context.symbol_space.get_type(
|
||||
symbol_table + constants.BANG + "_SHUTDOWN_PACKET"
|
||||
).size
|
||||
@@ -590,7 +589,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
except exceptions.InvalidAddressException:
|
||||
component = renderers.UnreadableValue()
|
||||
|
||||
yield "KeBugCheckReasonCallbackListHead", callback.CallbackRoutine, component
|
||||
yield (
|
||||
"KeBugCheckReasonCallbackListHead",
|
||||
callback.CallbackRoutine,
|
||||
component,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def list_bugcheck_callbacks(
|
||||
|
||||
@@ -77,6 +77,11 @@ class DeskScan(desktops.Desktops):
|
||||
continue
|
||||
|
||||
for _thread, process_name, process_pid in desktop.get_threads():
|
||||
yield format_hints.Hex(
|
||||
desktop.vol.offset
|
||||
), winsta_name, session_id, desktop_name, process_name, process_pid
|
||||
yield (
|
||||
format_hints.Hex(desktop.vol.offset),
|
||||
winsta_name,
|
||||
session_id,
|
||||
desktop_name,
|
||||
process_name,
|
||||
process_pid,
|
||||
)
|
||||
|
||||
@@ -63,9 +63,14 @@ class Desktops(interfaces.plugins.PluginInterface):
|
||||
for desktop, desktop_name in winsta.desktops(kernel.symbol_table_name):
|
||||
# for each desktop, walk its threads
|
||||
for _thread, process_name, process_pid in desktop.get_threads():
|
||||
yield format_hints.Hex(
|
||||
desktop.vol.offset
|
||||
), station_name, session_id, desktop_name, process_name, process_pid
|
||||
yield (
|
||||
format_hints.Hex(desktop.vol.offset),
|
||||
station_name,
|
||||
session_id,
|
||||
desktop_name,
|
||||
process_name,
|
||||
process_pid,
|
||||
)
|
||||
|
||||
def _generator(self):
|
||||
kernel_name = self.config["kernel"]
|
||||
|
||||
@@ -96,7 +96,6 @@ class KPCRs(interfaces.plugins.PluginInterface):
|
||||
yield kpcr, kpcr.member(kpcr_member)
|
||||
|
||||
def _generator(self) -> Iterator[Tuple]:
|
||||
|
||||
for kpcr, current_prcb in self.list_kpcrs(self.context, self.config["kernel"]):
|
||||
yield (
|
||||
0,
|
||||
|
||||
@@ -451,12 +451,15 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
|
||||
address, disasm_bytes = syscall_info
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
proc_name,
|
||||
proc.UniqueProcessId,
|
||||
vad_path,
|
||||
format_hints.Hex(address),
|
||||
disasm_bytes,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
|
||||
@@ -198,10 +198,13 @@ class HollowProcesses(interfaces.plugins.PluginInterface):
|
||||
|
||||
for check in checks:
|
||||
for note in check(proc, vads, dlls):
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
pid,
|
||||
proc_name,
|
||||
note,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -92,8 +92,11 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
for vad, data_object in cls.list_injection_sites(
|
||||
context, kernel_layer_name, symbol_table, proc
|
||||
):
|
||||
yield vad, data_object.context.layers[data_object.layer_name].read(
|
||||
yield (
|
||||
vad,
|
||||
data_object.context.layers[data_object.layer_name].read(
|
||||
data_object.offset, data_object.length
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -167,7 +167,6 @@ class PebMasquerade(interfaces.plugins.PluginInterface):
|
||||
|
||||
if isinstance(peb_imagefilepath, str) and peb:
|
||||
try:
|
||||
|
||||
# Length values are of type USHORT
|
||||
peb_imagefilepath_length = (
|
||||
peb.ProcessParameters.ImagePathName.Length // 2
|
||||
|
||||
@@ -149,9 +149,12 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
for file_object_address, delete_pending, delete_on_close in cls._vad_checks(
|
||||
control_area, path
|
||||
):
|
||||
yield format_hints.Hex(
|
||||
file_object_address
|
||||
), delete_pending, delete_on_close, vad_base
|
||||
yield (
|
||||
format_hints.Hex(file_object_address),
|
||||
delete_pending,
|
||||
delete_on_close,
|
||||
vad_base,
|
||||
)
|
||||
|
||||
def _generator(self, procs):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
@@ -187,7 +190,9 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
path = renderers.NotAvailableValue()
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
pid,
|
||||
process_name,
|
||||
format_hints.Hex(base_address),
|
||||
@@ -195,6 +200,7 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
delete_pending or renderers.NotApplicableValue(),
|
||||
delete_on_close or renderers.NotApplicableValue(),
|
||||
path,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -648,12 +648,15 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
csystem, cryptdll_base, cryptdll_size
|
||||
)
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
lsass_proc.UniqueProcessId,
|
||||
"lsass.exe",
|
||||
skeleton_key_present,
|
||||
format_hints.Hex(csystem.Initialize),
|
||||
format_hints.Hex(csystem.Decrypt),
|
||||
),
|
||||
)
|
||||
|
||||
def _lsass_proc_filter(self, proc):
|
||||
|
||||
@@ -196,7 +196,9 @@ class SuspiciousThreads(interfaces.plugins.PluginInterface):
|
||||
for vad_path, note in self._check_thread_address(
|
||||
exe_path, ranges, address
|
||||
):
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
proc_name,
|
||||
pid,
|
||||
tid,
|
||||
@@ -204,6 +206,7 @@ class SuspiciousThreads(interfaces.plugins.PluginInterface):
|
||||
format_hints.Hex(address),
|
||||
vad_path,
|
||||
note,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -132,7 +132,9 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
# There should only be one STANDARD_INFORMATION attribute, but we
|
||||
# do this just in case.
|
||||
for std_information in mft_record.standard_information_entries():
|
||||
yield 0, cls.MFTScanResult(
|
||||
yield (
|
||||
0,
|
||||
cls.MFTScanResult(
|
||||
format_hints.Hex(std_information.vol.offset),
|
||||
str(mft_record.get_signature()),
|
||||
mft_record.RecordNumber,
|
||||
@@ -145,6 +147,7 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
conversion.wintime_to_datetime(std_information.UpdatedTime),
|
||||
conversion.wintime_to_datetime(std_information.AccessedTime),
|
||||
renderers.NotApplicableValue(),
|
||||
),
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
pass
|
||||
@@ -163,14 +166,15 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
# File Name Attribute
|
||||
try:
|
||||
for filename_info in mft_record.filename_entries():
|
||||
|
||||
# If we don't have a valid enum, coerce to hex so we can keep the record
|
||||
try:
|
||||
permissions = filename_info.Flags.lookup()
|
||||
except ValueError:
|
||||
permissions = hex(filename_info.Flags)
|
||||
|
||||
yield 1, cls.MFTScanResult(
|
||||
yield (
|
||||
1,
|
||||
cls.MFTScanResult(
|
||||
format_hints.Hex(filename_info.vol.offset),
|
||||
str(mft_record.get_signature()),
|
||||
mft_record.RecordNumber,
|
||||
@@ -183,6 +187,7 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
conversion.wintime_to_datetime(filename_info.UpdatedTime),
|
||||
conversion.wintime_to_datetime(filename_info.AccessedTime),
|
||||
filename_info.get_full_name(),
|
||||
),
|
||||
)
|
||||
except exceptions.InvalidAddressException:
|
||||
return
|
||||
@@ -214,7 +219,9 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
# but in this case memory usage is so extreme due to the number of
|
||||
# records that it becomes necessary. The rich types are still
|
||||
# exposed through classmethods.
|
||||
yield level, (
|
||||
yield (
|
||||
level,
|
||||
(
|
||||
record.offset,
|
||||
record.record_type,
|
||||
int(record.record_number),
|
||||
@@ -231,6 +238,7 @@ class MFTScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
if isinstance(record.filename, objects.String)
|
||||
else record.filename
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
def generate_timeline(self):
|
||||
@@ -344,7 +352,9 @@ class ADS(interfaces.plugins.PluginInterface):
|
||||
# but in this case memory usage is so extreme due to the number of
|
||||
# records that it becomes necessary. The rich types are still
|
||||
# exposed through classmethods.
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
record.offset,
|
||||
str(record.signature),
|
||||
int(record.record_number),
|
||||
@@ -360,6 +370,7 @@ class ADS(interfaces.plugins.PluginInterface):
|
||||
else record.stream_name
|
||||
),
|
||||
record.content,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -454,13 +465,16 @@ class ResidentData(interfaces.plugins.PluginInterface):
|
||||
# but in this case memory usage is so extreme due to the number of
|
||||
# records that it becomes necessary. The rich types are still
|
||||
# exposed through classmethods.
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
resident_data_entry.offset,
|
||||
str(resident_data_entry.signature),
|
||||
int(resident_data_entry.record_number),
|
||||
resident_data_entry.attribute_type,
|
||||
str(resident_data_entry.filename),
|
||||
resident_data_entry.content,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -119,13 +119,16 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
if self.config["dump"]:
|
||||
file_output = self.dump_module(session_layers, pe_table_name, mod)
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -436,7 +436,6 @@ class PoolScanner(plugins.PluginInterface):
|
||||
constraints,
|
||||
alignment=alignment,
|
||||
):
|
||||
|
||||
mem_objects = header.get_object(
|
||||
constraint=constraint,
|
||||
use_top_down=is_windows_8_or_later,
|
||||
|
||||
@@ -246,13 +246,29 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
) -> Iterator[Tuple[str, timeliner.TimeLinerType, datetime.datetime]]:
|
||||
for _, entry in self._generator():
|
||||
if isinstance(entry.last_modify_time, datetime.datetime):
|
||||
yield f"Amcache: {entry.entry_type} {entry.path} registry key modified", timeliner.TimeLinerType.MODIFIED, entry.last_modify_time
|
||||
yield (
|
||||
f"Amcache: {entry.entry_type} {entry.path} registry key modified",
|
||||
timeliner.TimeLinerType.MODIFIED,
|
||||
entry.last_modify_time,
|
||||
)
|
||||
if isinstance(entry.last_modify_time_2, datetime.datetime):
|
||||
yield f"Amcache: {entry.entry_type} {entry.path} STANDARD_INFORMATION create time", timeliner.TimeLinerType.CREATED, entry.last_modify_time_2
|
||||
yield (
|
||||
f"Amcache: {entry.entry_type} {entry.path} STANDARD_INFORMATION create time",
|
||||
timeliner.TimeLinerType.CREATED,
|
||||
entry.last_modify_time_2,
|
||||
)
|
||||
if isinstance(entry.install_time, datetime.datetime):
|
||||
yield f"Amcache: {entry.entry_type} {entry.path} installed", timeliner.TimeLinerType.CREATED, entry.install_time
|
||||
yield (
|
||||
f"Amcache: {entry.entry_type} {entry.path} installed",
|
||||
timeliner.TimeLinerType.CREATED,
|
||||
entry.install_time,
|
||||
)
|
||||
if isinstance(entry.compile_time, datetime.datetime):
|
||||
yield f"Amcache: {entry.entry_type} {entry.path} compiled (PE metadata)", timeliner.TimeLinerType.MODIFIED, entry.compile_time
|
||||
yield (
|
||||
f"Amcache: {entry.entry_type} {entry.path} compiled (PE metadata)",
|
||||
timeliner.TimeLinerType.MODIFIED,
|
||||
entry.compile_time,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_amcache_hive(
|
||||
@@ -319,7 +335,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
vollog.debug(f"Found sha1hash {sha1_hash}")
|
||||
product_name = _get_string_value(values, val_enum.Product.value)
|
||||
|
||||
yield program_id, _AmcacheEntry(
|
||||
yield (
|
||||
program_id,
|
||||
_AmcacheEntry(
|
||||
AmcacheEntryType.File.name,
|
||||
path=path,
|
||||
company=company,
|
||||
@@ -333,6 +351,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
else sha1_hash
|
||||
),
|
||||
product_name=product_name,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -365,7 +384,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
)
|
||||
version = _get_string_value(values, val_enum.Version.value)
|
||||
|
||||
yield program_id, _AmcacheEntry(
|
||||
yield (
|
||||
program_id,
|
||||
_AmcacheEntry(
|
||||
AmcacheEntryType.Program.name,
|
||||
company=company,
|
||||
last_modify_time=conversion.wintime_to_datetime(
|
||||
@@ -374,6 +395,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
install_time=install_time,
|
||||
product_name=product,
|
||||
product_version=version,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -411,7 +433,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
|
||||
product: str = name if isinstance(name, str) else "UNKNOWN" # type: ignore
|
||||
|
||||
yield program_id.strip().strip("\u0000"), _AmcacheEntry(
|
||||
yield (
|
||||
program_id.strip().strip("\u0000"),
|
||||
_AmcacheEntry(
|
||||
AmcacheEntryType.Program.name,
|
||||
path=path,
|
||||
last_modify_time=last_mod,
|
||||
@@ -419,6 +443,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
product_name=product,
|
||||
company=publisher,
|
||||
product_version=version,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -456,7 +481,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
prod_ver = _get_string_value(values, val_enum.ProductVersion.value)
|
||||
program_id = _get_string_value(values, val_enum.ProgramID.value)
|
||||
|
||||
yield program_id, _AmcacheEntry(
|
||||
yield (
|
||||
program_id,
|
||||
_AmcacheEntry(
|
||||
AmcacheEntryType.File.name,
|
||||
path=path,
|
||||
company=publisher,
|
||||
@@ -469,6 +496,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
),
|
||||
product_name=prod_name,
|
||||
product_version=prod_ver,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -485,7 +513,6 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
wanted_values = [key.value for key in val_enum]
|
||||
|
||||
for binary_key in driver_binary_key.get_subkeys():
|
||||
|
||||
values = {
|
||||
str(value.get_name()): value
|
||||
for value in binary_key.get_values()
|
||||
@@ -636,7 +663,6 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
yield 0, empty_program
|
||||
|
||||
def run(self):
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("EntryType", str),
|
||||
|
||||
@@ -185,7 +185,6 @@ NULL = "\u0000"
|
||||
|
||||
|
||||
class _ScheduledTasksReader(io.BytesIO):
|
||||
|
||||
def read_task_scheduler_time(self) -> Optional[datetime.datetime]:
|
||||
_ = bool(self.read_aligned_u1()) # is_localized
|
||||
filetime = self.decode_filetime()
|
||||
@@ -393,7 +392,6 @@ class TaskAction:
|
||||
|
||||
num_attachment_filenames = reader.read_u4()
|
||||
if num_attachment_filenames is not None:
|
||||
|
||||
attachment_filenames = [
|
||||
reader.read_bstring() for _ in range(num_attachment_filenames)
|
||||
]
|
||||
@@ -1138,11 +1136,23 @@ class ScheduledTasks(interfaces.plugins.PluginInterface, timeliner.TimeLinerInte
|
||||
) -> Iterator[Tuple[str, timeliner.TimeLinerType, datetime.datetime]]:
|
||||
for _, task in self._generator():
|
||||
if isinstance(task.last_run_time, datetime.datetime):
|
||||
yield f"ScheduledTasks: task action {task.action_description} with trigger {task.trigger_description} ran", timeliner.TimeLinerType.ACCESSED, task.last_run_time
|
||||
yield (
|
||||
f"ScheduledTasks: task action {task.action_description} with trigger {task.trigger_description} ran",
|
||||
timeliner.TimeLinerType.ACCESSED,
|
||||
task.last_run_time,
|
||||
)
|
||||
if isinstance(task.last_successful_run_time, datetime.datetime):
|
||||
yield f"ScheduledTasks: task action {task.action_description} with trigger {task.trigger_description} ran successfully", timeliner.TimeLinerType.ACCESSED, task.last_successful_run_time
|
||||
yield (
|
||||
f"ScheduledTasks: task action {task.action_description} with trigger {task.trigger_description} ran successfully",
|
||||
timeliner.TimeLinerType.ACCESSED,
|
||||
task.last_successful_run_time,
|
||||
)
|
||||
if isinstance(task.creation_time, datetime.datetime):
|
||||
yield f"ScheduledTasks: Creation Time for task {task.guid} with trigger {task.trigger_description or '<UNKNOWN>'}", timeliner.TimeLinerType.CREATED, task.creation_time
|
||||
yield (
|
||||
f"ScheduledTasks: Creation Time for task {task.guid} with trigger {task.trigger_description or '<UNKNOWN>'}",
|
||||
timeliner.TimeLinerType.CREATED,
|
||||
task.creation_time,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_software_hive(
|
||||
@@ -1203,7 +1213,6 @@ class ScheduledTasks(interfaces.plugins.PluginInterface, timeliner.TimeLinerInte
|
||||
def parse_dynamic_info_value(
|
||||
cls, dyn_info_value: reg_extensions.CM_KEY_VALUE
|
||||
) -> Optional[DynamicInfo]:
|
||||
|
||||
try:
|
||||
data = dyn_info_value.decode_data()
|
||||
except exceptions.InvalidAddressException:
|
||||
@@ -1318,7 +1327,6 @@ class ScheduledTasks(interfaces.plugins.PluginInterface, timeliner.TimeLinerInte
|
||||
all_actions = action_set.actions or [None] if action_set is not None else [None]
|
||||
|
||||
for action, trigger in itertools.product(all_actions, all_triggers):
|
||||
|
||||
if action is not None:
|
||||
if action.action_type in (
|
||||
ActionType.Exe,
|
||||
|
||||
@@ -95,13 +95,16 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
# Group and yield each row
|
||||
for rows in sessions.values():
|
||||
for row in rows:
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
row.get("session_id"),
|
||||
row.get("session_type"),
|
||||
row.get("process_id"),
|
||||
row.get("process_name"),
|
||||
row.get("user_name"),
|
||||
row.get("process_start"),
|
||||
),
|
||||
)
|
||||
|
||||
def generate_timeline(self):
|
||||
|
||||
@@ -51,9 +51,17 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
) -> Iterator[Tuple[str, timeliner.TimeLinerType, datetime]]:
|
||||
for _, (_, last_modified, last_update, _, _, file_path) in self._generator():
|
||||
if isinstance(last_update, datetime):
|
||||
yield f"Shimcache: File {file_path} executed", timeliner.TimeLinerType.ACCESSED, last_update
|
||||
yield (
|
||||
f"Shimcache: File {file_path} executed",
|
||||
timeliner.TimeLinerType.ACCESSED,
|
||||
last_update,
|
||||
)
|
||||
if isinstance(last_modified, datetime):
|
||||
yield f"Shimcache: File {file_path} modified", timeliner.TimeLinerType.MODIFIED, last_modified
|
||||
yield (
|
||||
f"Shimcache: File {file_path} modified",
|
||||
timeliner.TimeLinerType.MODIFIED,
|
||||
last_modified,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -161,7 +169,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
continue
|
||||
|
||||
try:
|
||||
if proc_layer.read(vad.get_start(), 4) != b"\xEF\xBE\xAD\xDE":
|
||||
if proc_layer.read(vad.get_start(), 4) != b"\xef\xbe\xad\xde":
|
||||
if pid == 624:
|
||||
vollog.debug("VAD magic bytes don't match DEADBEEF")
|
||||
continue
|
||||
|
||||
@@ -150,7 +150,6 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
def _get_service_key(
|
||||
context, config_path: str, kernel_module_name: str
|
||||
) -> Optional[objects.StructType]:
|
||||
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=context,
|
||||
base_config_path=interfaces.configuration.path_join(
|
||||
|
||||
@@ -167,7 +167,9 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
info = self.gather_thread_info(ethread, vads_cache)
|
||||
|
||||
if info:
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(info.offset),
|
||||
info.pid,
|
||||
info.tid,
|
||||
@@ -177,6 +179,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
info.win32_start_path or renderers.NotAvailableValue(),
|
||||
info.create_time,
|
||||
info.exit_time,
|
||||
),
|
||||
)
|
||||
|
||||
def generate_timeline(self):
|
||||
|
||||
@@ -62,7 +62,6 @@ class VadRegExScan(plugins.PluginInterface):
|
||||
vollog.debug(f"RegEx Pattern: {regex_pattern}")
|
||||
|
||||
for proc in procs:
|
||||
|
||||
# attempt to create a process layer for each proc
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
if not proc_layer_name:
|
||||
@@ -106,12 +105,15 @@ class VadRegExScan(plugins.PluginInterface):
|
||||
max_length=proc.ImageFileName.vol.count,
|
||||
errors="replace",
|
||||
)
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
proc_id,
|
||||
process_name,
|
||||
format_hints.Hex(offset),
|
||||
text_result,
|
||||
bytes_result,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -100,7 +100,9 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
layer_name=layer.name,
|
||||
length=len(value),
|
||||
)
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(offset),
|
||||
task.UniqueProcessId,
|
||||
task.get_create_time(),
|
||||
@@ -115,6 +117,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
rule_name,
|
||||
name,
|
||||
layer_data,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -112,7 +112,9 @@ class Windows(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
continue
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(window.vol.offset),
|
||||
station_name,
|
||||
sess_id,
|
||||
@@ -121,6 +123,7 @@ class Windows(interfaces.plugins.PluginInterface):
|
||||
window_proc,
|
||||
process_name,
|
||||
process_pid,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Renderers display the unified output format in some manner (be it text
|
||||
or file or graphical output
|
||||
"""
|
||||
|
||||
import collections
|
||||
import collections.abc
|
||||
import dataclasses
|
||||
|
||||
@@ -8,6 +8,7 @@ These hints allow a plugin to indicate how they would like data from a particula
|
||||
|
||||
Text renderers should attempt to honour all hints provided in this module where possible
|
||||
"""
|
||||
|
||||
from typing import Type, Union
|
||||
|
||||
from volatility3.framework import interfaces
|
||||
|
||||
@@ -210,9 +210,10 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface):
|
||||
replacements = set()
|
||||
# Whole Symbols that still need traversing
|
||||
while traverse_list:
|
||||
template_traverse_list, traverse_list = [
|
||||
self._resolved[traverse_list[0]]
|
||||
], traverse_list[1:]
|
||||
template_traverse_list, traverse_list = (
|
||||
[self._resolved[traverse_list[0]]],
|
||||
traverse_list[1:],
|
||||
)
|
||||
# Traverse a single symbol looking for any ReferenceTemplate objects
|
||||
while template_traverse_list:
|
||||
traverser, template_traverse_list = (
|
||||
|
||||
@@ -246,9 +246,12 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface):
|
||||
if name.endswith(zip_match + extension) or (
|
||||
zip_match == "*" and name.endswith(extension)
|
||||
):
|
||||
yield "jar:file:" + str(
|
||||
pathlib.Path(zip_path)
|
||||
) + "!" + name
|
||||
yield (
|
||||
"jar:file:"
|
||||
+ str(pathlib.Path(zip_path))
|
||||
+ "!"
|
||||
+ name
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
|
||||
@@ -36,7 +36,6 @@ vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class module(generic.GenericIntelProcess):
|
||||
|
||||
def is_valid(self):
|
||||
"""Determine whether it is a valid module object by verifying the self-referential
|
||||
in module_kobject. This also confirms that the module is actively allocated and
|
||||
@@ -991,7 +990,6 @@ class maple_tree(objects.StructType):
|
||||
|
||||
|
||||
class mm_struct(objects.StructType):
|
||||
|
||||
# TODO: As of version 3.0.0 this method should be removed
|
||||
def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""
|
||||
@@ -3084,7 +3082,6 @@ class latch_tree_root(objects.StructType):
|
||||
|
||||
|
||||
class kernel_symbol(objects.StructType):
|
||||
|
||||
def _offset_to_ptr(self, off) -> int:
|
||||
layer = self._context.layers[self.vol.layer_name]
|
||||
long_mask = (1 << layer.bits_per_register) - 1
|
||||
|
||||
@@ -297,7 +297,6 @@ class Modules(interfaces.configuration.VersionableInterface):
|
||||
|
||||
# process each module coming from back the current source
|
||||
for module in gatherer.gather_modules(context, kernel_module_name):
|
||||
|
||||
# the kernel sends back a ModuleInfo directly
|
||||
if isinstance(module, ModuleInfo):
|
||||
modinfo = module
|
||||
@@ -998,13 +997,16 @@ class ModuleDisplayPlugin(interfaces.configuration.VersionableInterface):
|
||||
with self.open(file_name) as file_handle:
|
||||
file_handle.write(elf_data)
|
||||
|
||||
yield 0, (
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(module.vol.offset),
|
||||
name,
|
||||
format_hints.Hex(code_size),
|
||||
taints,
|
||||
parameters,
|
||||
file_name,
|
||||
),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -48,7 +48,6 @@ class _SHUTDOWN_PACKET(objects.StructType, pool.ExecutiveObject):
|
||||
return False
|
||||
|
||||
try:
|
||||
|
||||
device = self.DeviceObject
|
||||
if not device or not (device.DriverObject.DriverStart % 0x1000 == 0):
|
||||
vollog.debug(
|
||||
|
||||
@@ -220,7 +220,6 @@ class GUIExtensions(interfaces.configuration.VersionableInterface):
|
||||
break
|
||||
|
||||
class tagWND(objects.StructType, pool.ExecutiveObject):
|
||||
|
||||
def is_valid(self) -> bool:
|
||||
"""
|
||||
Enforce a valid sid
|
||||
|
||||
@@ -54,7 +54,6 @@ class MFTEntry(objects.StructType):
|
||||
return max(names, key=lambda x: len(str(x)))
|
||||
|
||||
def _attributes(self) -> Iterator["MFTAttribute"]:
|
||||
|
||||
# We will update this on each pass in the next loop and use it as the new offset.
|
||||
attr_base_offset = self.FirstAttrOffset
|
||||
attribute_object_type_name = (
|
||||
|
||||
@@ -183,7 +183,6 @@ class SHIM_CACHE_ENTRY(objects.StructType):
|
||||
== self.ListEntry.Flink.Blink.dereference().vol.offset
|
||||
)
|
||||
):
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -489,7 +489,7 @@ class PdbReader:
|
||||
"""Strips unnecessary components from the start of a symbol name."""
|
||||
new_name = name
|
||||
|
||||
if new_name[:1] in ["_", "@", "\u007F"]:
|
||||
if new_name[:1] in ["_", "@", "\u007f"]:
|
||||
new_name = new_name[1:]
|
||||
|
||||
name_array = new_name.split("@")
|
||||
|
||||
@@ -12,6 +12,7 @@ are dependent upon, please DO NOT alter or remove this file unless you know the
|
||||
The framework is configured this way to allow plugin developers/users to override any plugin functionality whether
|
||||
existing or new.
|
||||
"""
|
||||
|
||||
from volatility3.framework import constants
|
||||
|
||||
__path__ = constants.PLUGINS_PATH
|
||||
|
||||
@@ -11,6 +11,7 @@ existing or new.
|
||||
|
||||
When overriding the plugins directory, you must include a file like this in any subdirectories that may be necessary.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ existing or new.
|
||||
|
||||
When overriding the plugins directory, you must include a file like this in any subdirectories that may be necessary.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ existing or new.
|
||||
|
||||
When overriding the plugins directory, you must include a file like this in any subdirectories that may be necessary.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ existing or new.
|
||||
|
||||
When overriding the plugins directory, you must include a file like this in any subdirectories that may be necessary.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
This is the namespace for all volatility symbols, and determines the
|
||||
path for loading symbol ISF files
|
||||
"""
|
||||
|
||||
from volatility3.framework import constants
|
||||
|
||||
__path__ = constants.SYMBOL_BASEPATHS
|
||||
|
||||
Reference in New Issue
Block a user