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:
ikelos
2025-10-16 20:15:17 +01:00
committed by GitHub
92 changed files with 2425 additions and 588 deletions
-2
View File
@@ -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(
-1
View File
@@ -208,7 +208,6 @@ class Volatility3PyPyTest(VolatilityTest):
class VolatilityTester:
def __init__(
self,
images: List[VolatilityImage],
-1
View File
@@ -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"])
-1
View File
@@ -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
+53 -11
View File
@@ -8,9 +8,10 @@ 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
# The user doesn't have pyarrow installed, but HAS_PYARROW will be false so just continue
pass
@@ -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()
+5 -5
View File
@@ -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
+1
View File
@@ -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
+6 -4
View File
@@ -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
-2
View File
@@ -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
+3 -2
View File
@@ -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
+3 -3
View File
@@ -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
+1
View File
@@ -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
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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)
+2 -1
View File
@@ -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
+1
View File
@@ -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"""
+14 -2
View File
@@ -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
+7 -3
View File
@@ -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
+3 -2
View File
@@ -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(
+7 -1
View File
@@ -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:
+4 -4
View File
@@ -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)}"
+3 -2
View File
@@ -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):
+6 -3
View File
@@ -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."
+45 -13
View File
@@ -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,16 +102,19 @@ class Addr(plugins.PluginInterface):
scope_type,
operational_state,
) in self._gather_net_dev_info(net_dev):
yield 0, (
net_ns_id or renderers.NotAvailableValue(),
iface_ifindex,
iface_name,
mac_addr,
promisc,
ip6_addr,
prefix_len,
scope_type,
operational_state,
yield (
0,
(
net_ns_id or renderers.NotAvailableValue(),
iface_ifindex,
iface_name,
mac_addr,
promisc,
ip6_addr,
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"]]
+9 -6
View File
@@ -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, (
facility,
level,
timestamp,
caller or renderers.NotAvailableValue(),
line,
yield (
0,
(
facility,
level,
timestamp,
caller or renderers.NotAvailableValue(),
line,
),
)
def run(self):
+5 -1
View File
@@ -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, (
name,
format_hints.Hex(recv_buf),
module_name,
symbol_name or renderers.NotAvailableValue(),
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, (
format_hints.Hex(base_address),
len(elf_data),
file_handle.preferred_filename,
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.
+15 -12
View File
@@ -225,18 +225,21 @@ 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, (
format_hints.Hex(task_fields.offset),
task_fields.user_pid,
task_fields.user_tid,
task_fields.user_ppid,
task_fields.name,
task_uid,
task_gid,
task_euid,
task_egid,
task_fields.creation_time or renderers.NotAvailableValue(),
file_output,
yield (
0,
(
format_hints.Hex(task_fields.offset),
task_fields.user_pid,
task_fields.user_tid,
task_fields.user_ppid,
task_fields.name,
task_uid,
task_gid,
task_euid,
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, (
user_pid,
name,
format_hints.Hex(offset),
text_result,
bytes_result,
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, (
format_hints.Hex(offset),
task.tgid,
rule_name,
name,
layer_data,
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, (
proc_name,
proc.UniqueProcessId,
vad_path,
format_hints.Hex(address),
disasm_bytes,
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, (
pid,
proc_name,
note,
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(
data_object.offset, data_object.length
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,14 +190,17 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
else:
path = renderers.NotAvailableValue()
yield 0, (
pid,
process_name,
format_hints.Hex(base_address),
format_hints.Hex(file_object_address),
delete_pending or renderers.NotApplicableValue(),
delete_on_close or renderers.NotApplicableValue(),
path,
yield (
0,
(
pid,
process_name,
format_hints.Hex(base_address),
format_hints.Hex(file_object_address),
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, (
lsass_proc.UniqueProcessId,
"lsass.exe",
skeleton_key_present,
format_hints.Hex(csystem.Initialize),
format_hints.Hex(csystem.Decrypt),
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,14 +196,17 @@ class SuspiciousThreads(interfaces.plugins.PluginInterface):
for vad_path, note in self._check_thread_address(
exe_path, ranges, address
):
yield 0, (
proc_name,
pid,
tid,
context,
format_hints.Hex(address),
vad_path,
note,
yield (
0,
(
proc_name,
pid,
tid,
context,
format_hints.Hex(address),
vad_path,
note,
),
)
def run(self):
@@ -132,19 +132,22 @@ 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(
format_hints.Hex(std_information.vol.offset),
str(mft_record.get_signature()),
mft_record.RecordNumber,
mft_record.LinkCount,
mft_flag,
renderers.NotApplicableValue(),
"STANDARD_INFORMATION",
conversion.wintime_to_datetime(std_information.CreationTime),
conversion.wintime_to_datetime(std_information.ModifiedTime),
conversion.wintime_to_datetime(std_information.UpdatedTime),
conversion.wintime_to_datetime(std_information.AccessedTime),
renderers.NotApplicableValue(),
yield (
0,
cls.MFTScanResult(
format_hints.Hex(std_information.vol.offset),
str(mft_record.get_signature()),
mft_record.RecordNumber,
mft_record.LinkCount,
mft_flag,
renderers.NotApplicableValue(),
"STANDARD_INFORMATION",
conversion.wintime_to_datetime(std_information.CreationTime),
conversion.wintime_to_datetime(std_information.ModifiedTime),
conversion.wintime_to_datetime(std_information.UpdatedTime),
conversion.wintime_to_datetime(std_information.AccessedTime),
renderers.NotApplicableValue(),
),
)
except exceptions.InvalidAddressException:
pass
@@ -163,26 +166,28 @@ 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(
format_hints.Hex(filename_info.vol.offset),
str(mft_record.get_signature()),
mft_record.RecordNumber,
mft_record.LinkCount,
mft_flag,
permissions,
"FILE_NAME",
conversion.wintime_to_datetime(filename_info.CreationTime),
conversion.wintime_to_datetime(filename_info.ModifiedTime),
conversion.wintime_to_datetime(filename_info.UpdatedTime),
conversion.wintime_to_datetime(filename_info.AccessedTime),
filename_info.get_full_name(),
yield (
1,
cls.MFTScanResult(
format_hints.Hex(filename_info.vol.offset),
str(mft_record.get_signature()),
mft_record.RecordNumber,
mft_record.LinkCount,
mft_flag,
permissions,
"FILE_NAME",
conversion.wintime_to_datetime(filename_info.CreationTime),
conversion.wintime_to_datetime(filename_info.ModifiedTime),
conversion.wintime_to_datetime(filename_info.UpdatedTime),
conversion.wintime_to_datetime(filename_info.AccessedTime),
filename_info.get_full_name(),
),
)
except exceptions.InvalidAddressException:
return
@@ -214,22 +219,25 @@ 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, (
record.offset,
record.record_type,
int(record.record_number),
int(record.link_count),
record.mft_type,
record.permissions,
record.attribute_type,
record.created,
record.modified,
record.updated,
record.accessed,
yield (
level,
(
str(record.filename)
if isinstance(record.filename, objects.String)
else record.filename
record.offset,
record.record_type,
int(record.record_number),
int(record.link_count),
record.mft_type,
record.permissions,
record.attribute_type,
record.created,
record.modified,
record.updated,
record.accessed,
(
str(record.filename)
if isinstance(record.filename, objects.String)
else record.filename
),
),
)
@@ -344,22 +352,25 @@ 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, (
record.offset,
str(record.signature),
int(record.record_number),
record.attribute_type,
yield (
0,
(
str(record.filename)
if isinstance(record.filename, objects.String)
else record.filename
record.offset,
str(record.signature),
int(record.record_number),
record.attribute_type,
(
str(record.filename)
if isinstance(record.filename, objects.String)
else record.filename
),
(
str(record.stream_name)
if isinstance(record.stream_name, objects.String)
else record.stream_name
),
record.content,
),
(
str(record.stream_name)
if isinstance(record.stream_name, objects.String)
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, (
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,
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, (
format_hints.Hex(mod.vol.offset),
format_hints.Hex(mod.DllBase),
format_hints.Hex(mod.SizeOfImage),
BaseDllName,
FullDllName,
file_output,
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,20 +335,23 @@ 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(
AmcacheEntryType.File.name,
path=path,
company=company,
last_modify_time=last_mod_time,
last_modify_time_2=last_mod_time_2,
install_time=install_time,
compile_time=compile_time,
sha1_hash=(
sha1_hash.lstrip("0000")
if isinstance(sha1_hash, str)
else sha1_hash
yield (
program_id,
_AmcacheEntry(
AmcacheEntryType.File.name,
path=path,
company=company,
last_modify_time=last_mod_time,
last_modify_time_2=last_mod_time_2,
install_time=install_time,
compile_time=compile_time,
sha1_hash=(
sha1_hash.lstrip("0000")
if isinstance(sha1_hash, str)
else sha1_hash
),
product_name=product_name,
),
product_name=product_name,
)
@classmethod
@@ -365,15 +384,18 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
)
version = _get_string_value(values, val_enum.Version.value)
yield program_id, _AmcacheEntry(
AmcacheEntryType.Program.name,
company=company,
last_modify_time=conversion.wintime_to_datetime(
program_key.LastWriteTime.QuadPart
yield (
program_id,
_AmcacheEntry(
AmcacheEntryType.Program.name,
company=company,
last_modify_time=conversion.wintime_to_datetime(
program_key.LastWriteTime.QuadPart
),
install_time=install_time,
product_name=product,
product_version=version,
),
install_time=install_time,
product_name=product,
product_version=version,
)
@classmethod
@@ -411,14 +433,17 @@ 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(
AmcacheEntryType.Program.name,
path=path,
last_modify_time=last_mod,
install_time=install_date,
product_name=product,
company=publisher,
product_version=version,
yield (
program_id.strip().strip("\u0000"),
_AmcacheEntry(
AmcacheEntryType.Program.name,
path=path,
last_modify_time=last_mod,
install_time=install_date,
product_name=product,
company=publisher,
product_version=version,
),
)
@classmethod
@@ -456,19 +481,22 @@ 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(
AmcacheEntryType.File.name,
path=path,
company=publisher,
last_modify_time=last_mod,
compile_time=linkdate,
sha1_hash=(
sha1_hash.lstrip("0000")
if isinstance(sha1_hash, str)
else sha1_hash
yield (
program_id,
_AmcacheEntry(
AmcacheEntryType.File.name,
path=path,
company=publisher,
last_modify_time=last_mod,
compile_time=linkdate,
sha1_hash=(
sha1_hash.lstrip("0000")
if isinstance(sha1_hash, str)
else sha1_hash
),
product_name=prod_name,
product_version=prod_ver,
),
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, (
row.get("session_id"),
row.get("session_type"),
row.get("process_id"),
row.get("process_name"),
row.get("user_name"),
row.get("process_start"),
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,16 +167,19 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
info = self.gather_thread_info(ethread, vads_cache)
if info:
yield 0, (
format_hints.Hex(info.offset),
info.pid,
info.tid,
format_hints.Hex(info.start_addr),
info.start_path or renderers.NotAvailableValue(),
format_hints.Hex(info.win32_start_addr),
info.win32_start_path or renderers.NotAvailableValue(),
info.create_time,
info.exit_time,
yield (
0,
(
format_hints.Hex(info.offset),
info.pid,
info.tid,
format_hints.Hex(info.start_addr),
info.start_path or renderers.NotAvailableValue(),
format_hints.Hex(info.win32_start_addr),
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, (
proc_id,
process_name,
format_hints.Hex(offset),
text_result,
bytes_result,
yield (
0,
(
proc_id,
process_name,
format_hints.Hex(offset),
text_result,
bytes_result,
),
)
def run(self):
@@ -100,21 +100,24 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
layer_name=layer.name,
length=len(value),
)
yield 0, (
format_hints.Hex(offset),
task.UniqueProcessId,
task.get_create_time(),
task.InheritedFromUniqueProcessId,
task.ImageFileName.cast(
"string",
max_length=task.ImageFileName.vol.count,
errors="replace",
yield (
0,
(
format_hints.Hex(offset),
task.UniqueProcessId,
task.get_create_time(),
task.InheritedFromUniqueProcessId,
task.ImageFileName.cast(
"string",
max_length=task.ImageFileName.vol.count,
errors="replace",
),
task.get_session_id(),
task.ActiveThreads,
rule_name,
name,
layer_data,
),
task.get_session_id(),
task.ActiveThreads,
rule_name,
name,
layer_data,
)
@classmethod
@@ -112,15 +112,18 @@ class Windows(interfaces.plugins.PluginInterface):
)
continue
yield 0, (
format_hints.Hex(window.vol.offset),
station_name,
sess_id,
desktop_name,
window_name or renderers.NotAvailableValue(),
window_proc,
process_name,
process_pid,
yield (
0,
(
format_hints.Hex(window.vol.offset),
station_name,
sess_id,
desktop_name,
window_name or renderers.NotAvailableValue(),
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
+4 -3
View File
@@ -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 = (
+6 -3
View File
@@ -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, (
format_hints.Hex(module.vol.offset),
name,
format_hints.Hex(code_size),
taints,
parameters,
file_name,
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("@")
+1
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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