mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-29 11:19:40 +02:00
Merge pull request #1640 from volatilityfoundation/plugin/windows_gui
Add APIs and initial plugins for GUI support and fit Windows major APIs to current design flow
This commit is contained in:
@@ -198,7 +198,6 @@ that will be output as part of the :py:class:`~volatility3.framework.interfaces.
|
||||
def run(self):
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
kernel = self.context.modules[self.config['kernel']]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -211,9 +210,8 @@ that will be output as part of the :py:class:`~volatility3.framework.interfaces.
|
||||
],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
kernel_module_name=self.config['kernel'],
|
||||
filter_func = filter_func
|
||||
)
|
||||
)
|
||||
@@ -235,7 +233,7 @@ the :py:class:`~volatility3.plugins.windows.pslist.PsList` plugin. That plugin
|
||||
so that other plugins can call it. As such, it takes all the necessary parameters rather than accessing them
|
||||
from a configuration. Since it must be portable code, it takes a context, as well as the layer name,
|
||||
symbol table and optionally a filter. In this instance we unconditionally
|
||||
pass it the values from the configuration for the layer and symbol table from the kernel module object, constructed from
|
||||
pass it the value from the configuration for the kernel module name, constructed from
|
||||
the ``kernel`` configuration requirement. This will generate a list
|
||||
of :py:class:`~volatility3.framework.symbols.windows.extensions.EPROCESS` objects, as provided by the :py:class:`~volatility.plugins.windows.pslist.PsList` plugin,
|
||||
and is not covered here but is used as an example for how to share code across plugins
|
||||
|
||||
@@ -18,7 +18,7 @@ class Volshell(generic.Volshell):
|
||||
return [
|
||||
requirements.ModuleRequirement(name="kernel", description="Windows kernel"),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="pid", description="Process ID", optional=True
|
||||
@@ -39,9 +39,7 @@ class Volshell(generic.Volshell):
|
||||
"""Returns a list of EPROCESS objects from the primary layer"""
|
||||
# We always use the main kernel memory and associated symbols
|
||||
return list(
|
||||
pslist.PsList.list_processes(
|
||||
self.context, self.current_layer, self.current_symbol_table
|
||||
)
|
||||
pslist.PsList.list_processes(self.context, self.current_kernel_name)
|
||||
)
|
||||
|
||||
def get_process(self, pid=None, virtaddr=None, physaddr=None):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# We use the SemVer 2.0.0 versioning scheme
|
||||
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
|
||||
VERSION_MINOR = 22 # Number of changes that only add to the interface
|
||||
VERSION_MINOR = 23 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 0 # Number of changes that do not change the interface
|
||||
VERSION_SUFFIX = ""
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class RegistryHive(linear.LinearlyMappedLayer):
|
||||
# Win10 17063 introduced the Registry process to map most hives. Check
|
||||
# if it exists and update RegistryHive._base_layer
|
||||
for proc in pslist.PsList.list_processes(
|
||||
self.context, self.config["base_layer"], self.config["nt_symbols"]
|
||||
context=self.context, kernel_module_name=self.config["kernel_module_name"]
|
||||
):
|
||||
proc_name = proc.ImageFileName.cast(
|
||||
"string", max_length=proc.ImageFileName.vol.count, errors="replace"
|
||||
|
||||
@@ -46,7 +46,7 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
def _generator(self, tasks):
|
||||
vmlinux = self.context.modules[self.config["kernel"]]
|
||||
is_32bit = not symbols.symbol_table_is_64bit(
|
||||
self.context, vmlinux.symbol_table_name
|
||||
context=self.context, symbol_table_name=vmlinux.symbol_table_name
|
||||
)
|
||||
if is_32bit:
|
||||
pack_format = "I"
|
||||
|
||||
@@ -64,7 +64,7 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
# determine if we're on a 32 or 64 bit kernel
|
||||
vmlinux = self.context.modules[self.config["kernel"]]
|
||||
is_32bit_arch = not symbols.symbol_table_is_64bit(
|
||||
self.context, vmlinux.symbol_table_name
|
||||
context=self.context, symbol_table_name=vmlinux.symbol_table_name
|
||||
)
|
||||
|
||||
for task in tasks:
|
||||
|
||||
@@ -84,7 +84,9 @@ class PsScan(interfaces.plugins.PluginInterface):
|
||||
vmlinux = context.modules[vmlinux_module_name]
|
||||
|
||||
# check if this image is 32bit or 64bit
|
||||
is_32bit = not symbols.symbol_table_is_64bit(context, vmlinux.symbol_table_name)
|
||||
is_32bit = not symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=vmlinux.symbol_table_name
|
||||
)
|
||||
if is_32bit:
|
||||
pack_format = "I"
|
||||
else:
|
||||
|
||||
@@ -44,7 +44,7 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
def _generator(self, tasks):
|
||||
darwin = self.context.modules[self.config["kernel"]]
|
||||
is_32bit = not symbols.symbol_table_is_64bit(
|
||||
self.context, darwin.symbol_table_name
|
||||
context=self.context, symbol_table_name=darwin.symbol_table_name
|
||||
)
|
||||
if is_32bit:
|
||||
pack_format = "I"
|
||||
|
||||
@@ -218,7 +218,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Extract information on executed applications from the AmCache."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 - changed the signature of get_amcache_hive
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -230,7 +232,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -252,7 +254,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel: interfaces.context.ModuleInterface,
|
||||
kernel_module_name: str,
|
||||
) -> Optional[registry.RegistryHive]:
|
||||
"""Retrieves the `Amcache.hve` registry hive from the kernel module, if it can be located."""
|
||||
return next(
|
||||
@@ -261,8 +263,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
base_config_path=interfaces.configuration.path_join(
|
||||
config_path, "hivelist"
|
||||
),
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_string="amcache",
|
||||
),
|
||||
None,
|
||||
@@ -523,8 +524,6 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
)
|
||||
|
||||
def _generator(self) -> Iterator[Tuple[int, _AmcacheEntry]]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
def indented(
|
||||
entry_gen: Iterable[_AmcacheEntry], indent: int = 0
|
||||
) -> Iterator[Tuple[int, _AmcacheEntry]]:
|
||||
@@ -533,7 +532,9 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
|
||||
# Building the dictionary ahead of time is much better for performance
|
||||
# vs looking up each service's DLL individually.
|
||||
amcache = self.get_amcache_hive(self.context, self.config_path, kernel)
|
||||
amcache = self.get_amcache_hive(
|
||||
self.context, self.config_path, self.config["kernel"]
|
||||
)
|
||||
if amcache is None:
|
||||
return
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class Cachedump(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="lsadump", plugin=lsadump.Lsadump, version=(1, 0, 0)
|
||||
@@ -169,13 +169,11 @@ class Cachedump(interfaces.plugins.PluginInterface):
|
||||
offset = self.config.get("offset", None)
|
||||
|
||||
syshive = sechive = None
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=None if offset is None else [offset],
|
||||
):
|
||||
if hive.get_name().split("\\")[-1].upper() == "SYSTEM":
|
||||
|
||||
@@ -39,7 +39,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="poolscanner", plugin=poolscanner.PoolScanner, version=(1, 0, 0)
|
||||
@@ -187,7 +187,9 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
The name of the constructed symbol table
|
||||
"""
|
||||
native_types = context.symbol_space[nt_symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=nt_symbol_table
|
||||
)
|
||||
table_mapping = {"nt_symbols": nt_symbol_table}
|
||||
|
||||
if is_64bit:
|
||||
@@ -691,7 +693,8 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
callback_methods = (
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from volatility3.framework import constants, exceptions, renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
@@ -28,7 +28,7 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -41,7 +41,7 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
@classmethod
|
||||
def get_cmdline(
|
||||
cls, context: interfaces.context.ContextInterface, kernel_table_name: str, proc
|
||||
):
|
||||
) -> Optional[str]:
|
||||
"""Extracts the cmdline from PEB
|
||||
|
||||
Args:
|
||||
@@ -54,15 +54,16 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
"""
|
||||
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
if not proc_layer_name:
|
||||
return None
|
||||
|
||||
peb = context.object(
|
||||
kernel_table_name + constants.BANG + "_PEB",
|
||||
layer_name=proc_layer_name,
|
||||
offset=proc.Peb,
|
||||
)
|
||||
result_text = peb.ProcessParameters.CommandLine.get_string()
|
||||
|
||||
return result_text
|
||||
return peb.ProcessParameters.CommandLine.get_string()
|
||||
|
||||
def _generator(self, procs):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
@@ -99,7 +100,6 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
yield (0, (proc.UniqueProcessId, process_name, result_text))
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
return renderers.TreeGrid(
|
||||
@@ -107,8 +107,7 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -36,10 +36,10 @@ class CmdScan(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="consoles", plugin=consoles.Consoles, version=(1, 0, 0)
|
||||
name="consoles", plugin=consoles.Consoles, version=(2, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="no_registry",
|
||||
@@ -286,12 +286,11 @@ class CmdScan(interfaces.plugins.PluginInterface):
|
||||
|
||||
if no_registry is False:
|
||||
max_history, _ = consoles.Consoles.get_console_settings_from_registry(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
max_history,
|
||||
[],
|
||||
context=self.context,
|
||||
config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
max_history=max_history,
|
||||
max_buffers=[],
|
||||
)
|
||||
|
||||
vollog.debug(f"Possible CommandHistorySize values: {max_history}")
|
||||
@@ -360,8 +359,6 @@ class CmdScan(interfaces.plugins.PluginInterface):
|
||||
return process_name != "conhost.exe"
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
@@ -374,8 +371,7 @@ class CmdScan(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=self._conhost_proc_filter,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -29,7 +29,9 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
"""Looks for Windows console buffers"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 - change the signature of `get_console_settings_from_registry`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -41,13 +43,13 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="verinfo", component=verinfo.VerInfo, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="no_registry",
|
||||
@@ -147,7 +149,9 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
The filename of the symbol table to use and the associated class types.
|
||||
"""
|
||||
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=nt_symbol_table
|
||||
)
|
||||
|
||||
if is_64bit:
|
||||
arch = "x64"
|
||||
@@ -795,8 +799,7 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_layer_name: str,
|
||||
kernel_symbol_table_name: str,
|
||||
kernel_module_name: str,
|
||||
max_history: Set[int],
|
||||
max_buffers: Set[int],
|
||||
) -> Tuple[Set[int], Set[int]]:
|
||||
@@ -825,8 +828,7 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=context,
|
||||
base_config_path=config_path,
|
||||
layer_name=kernel_layer_name,
|
||||
symbol_table=kernel_symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
hive_offsets=None,
|
||||
):
|
||||
try:
|
||||
@@ -861,8 +863,7 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
max_history, max_buffers = self.get_console_settings_from_registry(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
max_history,
|
||||
max_buffers,
|
||||
)
|
||||
@@ -933,8 +934,6 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
return process_name.lower() != "conhost.exe"
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
@@ -947,8 +946,7 @@ class Consoles(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=self._conhost_proc_filter,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -35,13 +35,13 @@ class DebugRegisters(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="threads", component=threads.Threads, version=(1, 0, 0)
|
||||
name="threads", component=threads.Threads, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(1, 0, 0)
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -118,9 +118,7 @@ class DebugRegisters(interfaces.plugins.PluginInterface):
|
||||
proc_modules = None
|
||||
|
||||
procs = pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
|
||||
for proc in procs:
|
||||
@@ -140,7 +138,7 @@ class DebugRegisters(interfaces.plugins.PluginInterface):
|
||||
# this lookup takes a while, so only perform if we need to
|
||||
if not proc_modules:
|
||||
proc_modules = pe_symbols.PESymbols.get_process_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name, None
|
||||
self.context, self.config["kernel"], None
|
||||
)
|
||||
path_and_symbol = partial(
|
||||
pe_symbols.PESymbols.path_and_symbol_for_address,
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
from typing import List, Iterable, Tuple
|
||||
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.windows import desktops, windowstations
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeskScan(desktops.Desktops):
|
||||
"""Scans for the Desktop instances of each Window Station"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.implementation = self.scan_desktops
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="desktops", plugin=desktops.Desktops, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="windowstations",
|
||||
plugin=windowstations.WindowStations,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def scan_desktops(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_module_name: str,
|
||||
) -> Iterable[Tuple[int, str, int, str, str, int]]:
|
||||
"""
|
||||
Yields the information about each desktop and desktop thread needed for analysis
|
||||
|
||||
The tuple yielded includes the:
|
||||
Virtual address of the desktop
|
||||
The window station name
|
||||
The session id
|
||||
Desktop name
|
||||
Process name
|
||||
Process ID (PID)
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
for desktop in windowstations.WindowStations.scan_gui_object(
|
||||
context, config_path, kernel_module_name, b"Desk", "tagDESKTOP"
|
||||
):
|
||||
desktop_name = desktop.get_name(kernel.symbol_table_name)
|
||||
if not desktop_name:
|
||||
continue
|
||||
|
||||
winsta = desktop.get_window_station()
|
||||
if not winsta:
|
||||
continue
|
||||
|
||||
winsta_name, session_id = winsta.get_info(kernel.symbol_table_name)
|
||||
if not winsta_name or session_id is None:
|
||||
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
|
||||
@@ -0,0 +1,91 @@
|
||||
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
from typing import List, Iterable
|
||||
|
||||
from volatility3.framework import interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.windows import windowstations
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Desktops(interfaces.plugins.PluginInterface):
|
||||
"""Enumerates the Desktop instances of each Window Station"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.implementation = self.list_desktops
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="windowstations",
|
||||
plugin=windowstations.WindowStations,
|
||||
version=(1, 0, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_desktops(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_module_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""
|
||||
Uses `scan_window_stations` to find each window station
|
||||
For each found, enumerates its desktops followed by the
|
||||
threads of each desktop.
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
for (
|
||||
winsta,
|
||||
station_name,
|
||||
session_id,
|
||||
) in windowstations.WindowStations.scan_window_stations(
|
||||
context, config_path, kernel_module_name
|
||||
):
|
||||
# for each window station, walk its list of desktops
|
||||
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
|
||||
|
||||
def _generator(self):
|
||||
kernel_name = self.config["kernel"]
|
||||
|
||||
# call the implementation for finding desktops
|
||||
# yield the information, which will include the owning window station and process
|
||||
for desktop_info in self.implementation(
|
||||
self.context, self.config_path, kernel_name
|
||||
):
|
||||
yield 0, desktop_info
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("Window Station", str),
|
||||
("Session", int),
|
||||
("Desktop", str),
|
||||
("Process", str),
|
||||
("PID", int),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -53,7 +53,9 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
"""Detects the Direct System Call technique used to bypass EDRs"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (1, 0, 1)
|
||||
|
||||
# 2.0.0 - changes signature of `get_tasks_to_scan`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
# DLLs that are expected to host system call invocations
|
||||
valid_syscall_handlers = ("ntdll.dll", "win32u.dll")
|
||||
@@ -90,7 +92,7 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="yarascanner", component=yarascan.YaraScanner, version=(2, 1, 0)
|
||||
@@ -334,8 +336,7 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
def get_tasks_to_scan(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table_name: str,
|
||||
kernel_module_name: str,
|
||||
) -> Generator[
|
||||
Tuple[interfaces.objects.ObjectInterface, str, str, str], None, None
|
||||
]:
|
||||
@@ -350,12 +351,15 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
# gather active processes
|
||||
filter_func = pslist.PsList.create_active_process_filter()
|
||||
|
||||
is_32bit_arch = not symbols.symbol_table_is_64bit(context, symbol_table_name)
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
is_32bit_arch = not symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
proc_name = utility.array_to_string(proc.ImageFileName)
|
||||
@@ -426,10 +430,8 @@ class DirectSystemCalls(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
return
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for proc, proc_name, proc_layer_name, architecture in self.get_tasks_to_scan(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
self.context, self.config["kernel"]
|
||||
):
|
||||
proc_layer = self.context.layers[proc_layer_name]
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from volatility3.framework.renderers import conversion, format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
from volatility3.plugins import timeliner
|
||||
from volatility3.plugins.windows import info, pslist, psscan, pedump
|
||||
from volatility3.plugins.windows import info, pedump, pslist, psscan
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,13 +34,13 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="psscan", component=psscan.PsScan, version=(1, 1, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
name="pedump", component=pedump.PEDump, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="info", component=info.Info, version=(1, 0, 0)
|
||||
@@ -191,12 +191,9 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
)
|
||||
|
||||
def generate_timeline(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
for row in self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
):
|
||||
_depth, row_data = row
|
||||
@@ -223,8 +220,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
else:
|
||||
procs = pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
|
||||
|
||||
@@ -59,25 +59,25 @@ class DriverIrp(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="driverscan", plugin=driverscan.DriverScan, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="modules", plugin=modules.Modules, version=(2, 1, 0)
|
||||
name="modules", plugin=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
kernel_space_start = modules.Modules.get_kernel_space_start(
|
||||
self.context, self.config["kernel"]
|
||||
context=self.context,
|
||||
module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
for driver in driverscan.DriverScan.scan_drivers(
|
||||
|
||||
@@ -26,13 +26,13 @@ class DriverModule(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="driverscan", plugin=driverscan.DriverScan, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="modules", plugin=modules.Modules, version=(2, 1, 0)
|
||||
name="modules", plugin=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -42,10 +42,9 @@ class DriverModule(interfaces.plugins.PluginInterface):
|
||||
A common rootkit technique is to register drivers from modules that are hidden,
|
||||
which allows us to detect the disconnect between a malicious driver and its hidden module.
|
||||
"""
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
kernel_space_start = modules.Modules.get_kernel_space_start(
|
||||
|
||||
@@ -68,7 +68,7 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="handles", component=handles.Handles, version=(2, 0, 0)
|
||||
@@ -352,7 +352,6 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
offsets = list()
|
||||
# a list of processes matching the pid filter. all files for these process(es) will be dumped.
|
||||
procs = list()
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
if self.config["filter"] and (
|
||||
self.config["virtaddr"] or self.config["physaddr"]
|
||||
@@ -372,9 +371,8 @@ class DumpFiles(interfaces.plugins.PluginInterface):
|
||||
[self.config.get("pid", None)]
|
||||
)
|
||||
procs = pslist.PsList.list_processes(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ class Envars(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -58,13 +58,11 @@ class Envars(interfaces.plugins.PluginInterface):
|
||||
"""
|
||||
|
||||
values = []
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=None,
|
||||
):
|
||||
## The global variables
|
||||
@@ -216,7 +214,6 @@ class Envars(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -229,8 +226,7 @@ class Envars(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -69,18 +69,16 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
# Get the system hive
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_string="machine\\system",
|
||||
hive_offsets=None,
|
||||
):
|
||||
|
||||
@@ -84,10 +84,10 @@ class GetSIDs(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -101,14 +101,12 @@ class GetSIDs(interfaces.plugins.PluginInterface):
|
||||
|
||||
key = "Microsoft\\Windows NT\\CurrentVersion\\ProfileList"
|
||||
val = "ProfileImagePath"
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
sids = {}
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_string="config\\software",
|
||||
hive_offsets=None,
|
||||
):
|
||||
@@ -217,15 +215,13 @@ class GetSIDs(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[("PID", int), ("Process", str), ("SID", str), ("Name", str)],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -36,7 +36,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="psscan", component=psscan.PsScan, version=(1, 1, 0)
|
||||
@@ -78,7 +78,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
except AttributeError:
|
||||
# starting with windows 8
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
self.context, kernel.symbol_table_name
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
if is_64bit:
|
||||
@@ -223,24 +223,17 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
virtual = kernel.layer_name
|
||||
kvo = kernel.offset
|
||||
|
||||
ntkrnlmp = self.context.module(
|
||||
kernel.symbol_table_name, layer_name=virtual, offset=kvo
|
||||
)
|
||||
|
||||
if level > 0:
|
||||
subtype = ntkrnlmp.get_type("pointer")
|
||||
subtype = kernel.get_type("pointer")
|
||||
count = 0x1000 / subtype.size
|
||||
else:
|
||||
subtype = ntkrnlmp.get_type("_HANDLE_TABLE_ENTRY")
|
||||
subtype = kernel.get_type("_HANDLE_TABLE_ENTRY")
|
||||
count = 0x1000 / subtype.size
|
||||
|
||||
if not self.context.layers[virtual].is_valid(offset):
|
||||
if not self.context.layers[kernel.layer_name].is_valid(offset):
|
||||
return None
|
||||
|
||||
table = ntkrnlmp.object(
|
||||
table = kernel.object(
|
||||
object_type="array",
|
||||
offset=offset,
|
||||
subtype=subtype,
|
||||
@@ -248,7 +241,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
absolute=True,
|
||||
)
|
||||
|
||||
layer_object = self.context.layers[virtual]
|
||||
layer_object = self.context.layers[kernel.layer_name]
|
||||
masked_offset = offset & layer_object.maximum_address
|
||||
|
||||
for i in range(len(table)):
|
||||
@@ -262,7 +255,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
# The code above this calls `is_valid` on the `offset`
|
||||
# It is sent but then does not validate `entry` before
|
||||
# sending it to `_get_item`
|
||||
if not self.context.layers[virtual].is_valid(entry.vol.offset):
|
||||
if not self.context.layers[kernel.layer_name].is_valid(entry.vol.offset):
|
||||
continue
|
||||
|
||||
if level > 0:
|
||||
@@ -394,8 +387,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
procs = pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class Hashdump(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -593,12 +593,10 @@ class Hashdump(interfaces.plugins.PluginInterface):
|
||||
offset = self.config.get("offset", None)
|
||||
syshive = None
|
||||
samhive = None
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=None if offset is None else [offset],
|
||||
):
|
||||
if hive.get_name().split("\\")[-1].upper() == "SYSTEM":
|
||||
|
||||
@@ -48,7 +48,7 @@ class HollowProcesses(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -205,7 +205,6 @@ class HollowProcesses(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -216,8 +215,7 @@ class HollowProcesses(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -28,7 +28,7 @@ class IAT(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -126,8 +126,6 @@ class IAT(interfaces.plugins.PluginInterface):
|
||||
continue
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
@@ -140,8 +138,7 @@ class IAT(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=pslist.PsList.create_pid_filter(
|
||||
self.config.get("pid", None)
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import List, Optional
|
||||
from volatility3.framework import interfaces, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.plugins import yarascan
|
||||
from volatility3.plugins.windows import pslist, direct_system_calls
|
||||
from volatility3.plugins.windows import direct_system_calls
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -43,9 +43,6 @@ class IndirectSystemCalls(direct_system_calls.DirectSystemCalls):
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="yarascanner", component=yarascan.YaraScanner, version=(2, 1, 0)
|
||||
),
|
||||
|
||||
@@ -207,7 +207,14 @@ class Info(plugins.PluginInterface):
|
||||
yield (0, ("Symbols", table.config["isf_url"]))
|
||||
yield (
|
||||
0,
|
||||
("Is64Bit", str(symbols.symbol_table_is_64bit(self.context, symbol_table))),
|
||||
(
|
||||
"Is64Bit",
|
||||
str(
|
||||
symbols.symbol_table_is_64bit(
|
||||
context=self.context, symbol_table_name=symbol_table
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
yield (
|
||||
0,
|
||||
|
||||
@@ -36,16 +36,18 @@ class JobLinks(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self) -> Iterator[Tuple]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
memory = self.context.layers[kernel.layer_name]
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
):
|
||||
try:
|
||||
if not self.config["physical"]:
|
||||
|
||||
@@ -29,7 +29,7 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -107,7 +107,6 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -122,8 +121,7 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -36,7 +36,7 @@ class Lsadump(interfaces.plugins.PluginInterface):
|
||||
name="hashdump", component=hashdump.Hashdump, version=(1, 1, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="hivelist", component=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", component=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -209,13 +209,11 @@ class Lsadump(interfaces.plugins.PluginInterface):
|
||||
def run(self):
|
||||
offset = self.config.get("offset", None)
|
||||
syshive = sechive = None
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=None if offset is None else [offset],
|
||||
):
|
||||
if hive.get_name().split("\\")[-1].upper() == "SYSTEM":
|
||||
|
||||
@@ -41,7 +41,7 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -172,7 +172,7 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
}
|
||||
|
||||
is_32bit_arch = not symbols.symbol_table_is_64bit(
|
||||
self.context, kernel.symbol_table_name
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
for proc in procs:
|
||||
@@ -238,7 +238,6 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -258,8 +257,7 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -53,7 +53,9 @@ class MBRScan(interfaces.plugins.PluginInterface):
|
||||
layer = self.context.layers[physical_layer_name]
|
||||
architecture = (
|
||||
"intel"
|
||||
if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name)
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
else "intel64"
|
||||
)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class Memmap(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="pid",
|
||||
@@ -97,7 +97,6 @@ class Memmap(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter([self.config.get("pid", None)])
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -110,8 +109,7 @@ class Memmap(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -15,7 +15,9 @@ class ModScan(modules.Modules):
|
||||
"""Scans for modules present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
# 3.0.0 changed the signature of enumeration methods (scan_modules)
|
||||
_version = (3, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -33,7 +35,7 @@ class ModScan(modules.Modules):
|
||||
name="poolscanner", component=poolscanner.PoolScanner, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
@@ -53,7 +55,7 @@ class ModScan(modules.Modules):
|
||||
default=None,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
name="pedump", component=pedump.PEDump, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -61,26 +63,25 @@ class ModScan(modules.Modules):
|
||||
def scan_modules(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Scans for modules using the poolscanner module and constraints.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
|
||||
kernel_module_name: Name of the module for the kernel
|
||||
Returns:
|
||||
A list of Driver objects as found from the `layer_name` layer based on Driver pool signatures
|
||||
A list of kernel module objects as found from the primary (kernel) layer based on module pool signatures
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
constraints = poolscanner.PoolScanner.builtin_constraints(
|
||||
symbol_table, [b"MmLd"]
|
||||
kernel.symbol_table_name, [b"MmLd"]
|
||||
)
|
||||
|
||||
for result in poolscanner.PoolScanner.generate_pool_scan(
|
||||
context, layer_name, symbol_table, constraints
|
||||
context, kernel.layer_name, kernel.symbol_table_name, constraints
|
||||
):
|
||||
_constraint, mem_object, _header = result
|
||||
yield mem_object
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
from typing import Generator, Iterable, List, Optional
|
||||
from typing import Generator, Iterable, List, Optional, Dict, Tuple
|
||||
|
||||
from volatility3.framework import symbols, constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
@@ -18,7 +18,9 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
"""Lists the loaded kernel modules."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 1, 0)
|
||||
|
||||
# 3.0.0 - changed signature of get_session_layers, added get_session_layers_map
|
||||
_version = (3, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -33,7 +35,10 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(2, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
@@ -52,9 +57,6 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
default=None,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pedump", component=pedump.PEDump, version=(1, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def dump_module(self, session_layers, pe_table_name, mod):
|
||||
@@ -76,8 +78,6 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
return file_output
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
pe_table_name = None
|
||||
session_layers = None
|
||||
|
||||
@@ -92,12 +92,13 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
|
||||
session_layers = list(
|
||||
self.get_session_layers(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
)
|
||||
|
||||
for mod in self._enumeration_method(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
self.context, kernel_module_name=self.config["kernel"]
|
||||
):
|
||||
if self.config["base"] and self.config["base"] != mod.DllBase:
|
||||
continue
|
||||
@@ -139,7 +140,9 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
module = context.modules[module_name]
|
||||
|
||||
# default is used if/when MmSystemRangeStart is paged out
|
||||
if symbols.symbol_table_is_64bit(context, module.symbol_table_name):
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=module.symbol_table_name
|
||||
):
|
||||
object_type = "unsigned long long"
|
||||
default_start = 0xFFFF800000000000
|
||||
else:
|
||||
@@ -163,33 +166,32 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
return kernel_space_start & layer.address_mask
|
||||
|
||||
@classmethod
|
||||
def get_session_layers(
|
||||
def _do_get_session_layers(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
pids: Optional[List[int]] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
) -> Generator[Tuple[int, str], None, None]:
|
||||
"""Build a cache of possible virtual layers, in priority starting with
|
||||
the primary/kernel layer. Then keep one layer per session by cycling
|
||||
through the process list.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
pids: A list of process identifiers to include exclusively or None for no filter
|
||||
|
||||
Returns:
|
||||
A list of session layer names
|
||||
A generator of session layer names
|
||||
"""
|
||||
seen_ids: List[interfaces.objects.ObjectInterface] = []
|
||||
filter_func = pslist.PsList.create_pid_filter(pids or [])
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
proc_id = "Unknown"
|
||||
@@ -201,8 +203,8 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
# not all processes have a valid session pointer.
|
||||
try:
|
||||
session_space = context.object(
|
||||
symbol_table + constants.BANG + "_MM_SESSION_SPACE",
|
||||
layer_name=layer_name,
|
||||
kernel.symbol_table_name + constants.BANG + "_MM_SESSION_SPACE",
|
||||
layer_name=kernel.layer_name,
|
||||
offset=proc.Session,
|
||||
)
|
||||
session_id = session_space.SessionId
|
||||
@@ -218,8 +220,10 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
# create an unsigned long at that offset and use that
|
||||
# instead.
|
||||
session_id = context.object(
|
||||
layer_name=layer_name,
|
||||
object_type=symbol_table + constants.BANG + "unsigned long",
|
||||
layer_name=kernel.layer_name,
|
||||
object_type=kernel.symbol_table_name
|
||||
+ constants.BANG
|
||||
+ "unsigned long",
|
||||
offset=proc.Session + 8,
|
||||
)
|
||||
|
||||
@@ -235,8 +239,46 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
|
||||
# save the layer if we haven't seen the session yet
|
||||
seen_ids.append(session_id)
|
||||
yield session_id, proc_layer_name
|
||||
|
||||
@classmethod
|
||||
def get_session_layers(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
pids: Optional[List[int]] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
"""
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
pids: A list of process identifiers to include exclusively or None for no filter
|
||||
|
||||
Yields the names of the unique memory layers that map sessions
|
||||
"""
|
||||
for _session_id, proc_layer_name in cls._do_get_session_layers(
|
||||
context, kernel_module_name, pids
|
||||
):
|
||||
yield proc_layer_name
|
||||
|
||||
@classmethod
|
||||
def get_session_layers_map(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
pids: Optional[List[int]] = None,
|
||||
) -> Dict[int, str]:
|
||||
"""
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
pids: A list of process identifiers to include exclusively or None for no filter
|
||||
|
||||
Wraps `_do_get_session_layers` to produce a dictionary where each key is a session_id
|
||||
and the value is the name of the layer for that session
|
||||
"""
|
||||
return dict(cls._do_get_session_layers(context, kernel_module_name, pids))
|
||||
|
||||
@classmethod
|
||||
def find_session_layer(
|
||||
cls,
|
||||
@@ -268,39 +310,35 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
def list_modules(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the modules in the primary layer.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
Returns:
|
||||
A list of Modules as retrieved from PsLoadedModuleList
|
||||
"""
|
||||
|
||||
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
|
||||
if not kvo:
|
||||
kernel = context.modules[kernel_module_name]
|
||||
if not kernel.offset:
|
||||
raise ValueError(
|
||||
"Intel layer does not have an associated kernel virtual offset, failing"
|
||||
)
|
||||
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
|
||||
|
||||
try:
|
||||
# use this type if its available (starting with windows 10)
|
||||
ldr_entry_type = ntkrnlmp.get_type("_KLDR_DATA_TABLE_ENTRY")
|
||||
ldr_entry_type = kernel.get_type("_KLDR_DATA_TABLE_ENTRY")
|
||||
except exceptions.SymbolError:
|
||||
ldr_entry_type = ntkrnlmp.get_type("_LDR_DATA_TABLE_ENTRY")
|
||||
ldr_entry_type = kernel.get_type("_LDR_DATA_TABLE_ENTRY")
|
||||
|
||||
type_name = ldr_entry_type.type_name.split(constants.BANG)[1]
|
||||
|
||||
list_head = ntkrnlmp.get_symbol("PsLoadedModuleList").address
|
||||
list_entry = ntkrnlmp.object(object_type="_LIST_ENTRY", offset=list_head)
|
||||
list_head = kernel.get_symbol("PsLoadedModuleList").address
|
||||
list_entry = kernel.object(object_type="_LIST_ENTRY", offset=list_head)
|
||||
reloff = ldr_entry_type.relative_child_offset("InLoadOrderLinks")
|
||||
module = ntkrnlmp.object(
|
||||
module = kernel.object(
|
||||
object_type=type_name, offset=list_entry.vol.offset - reloff, absolute=True
|
||||
)
|
||||
|
||||
|
||||
@@ -137,7 +137,9 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
# therefore we determine the version based on the kernel version as testing
|
||||
# with several windows versions has showed this to work out correctly.
|
||||
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=nt_symbol_table
|
||||
)
|
||||
|
||||
is_18363_or_later = versions.is_win10_18363_or_later(
|
||||
context=context, symbol_table=nt_symbol_table
|
||||
|
||||
@@ -21,7 +21,9 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Traverses network tracking structures present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 changed the signature of `get_tcpip_module`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
@@ -35,7 +37,7 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
name="netscan", component=netscan.NetScan, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pdbutil", component=pdbutil.PDBUtility, version=(1, 0, 0)
|
||||
@@ -234,20 +236,18 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
def get_tcpip_module(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
nt_symbols: str,
|
||||
kernel_module_name: str,
|
||||
) -> Optional[interfaces.objects.ObjectInterface]:
|
||||
"""Uses `windows.modules` to find tcpip.sys in memory.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
nt_symbols: The name of the table containing the kernel symbols
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
|
||||
Returns:
|
||||
The constructed tcpip.sys module object.
|
||||
"""
|
||||
for mod in modules.Modules.list_modules(context, layer_name, nt_symbols):
|
||||
for mod in modules.Modules.list_modules(context, kernel_module_name):
|
||||
if mod.BaseDllName.get_string() == "tcpip.sys":
|
||||
vollog.debug(f"Found tcpip.sys image base @ 0x{mod.DllBase:x}")
|
||||
return mod
|
||||
@@ -319,7 +319,9 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
Returns:
|
||||
The list of TCP endpoint objects from the `layer_name` layer's `PartitionTable`
|
||||
"""
|
||||
if symbols.symbol_table_is_64bit(context, net_symbol_table):
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=net_symbol_table
|
||||
):
|
||||
alignment = 0x10
|
||||
else:
|
||||
alignment = 8
|
||||
@@ -630,9 +632,7 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name, self.config_path
|
||||
)
|
||||
|
||||
tcpip_module = self.get_tcpip_module(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
tcpip_module = self.get_tcpip_module(self.context, self.config["kernel"])
|
||||
if not tcpip_module:
|
||||
vollog.error("Unable to locate symbols for the memory image's tcpip module")
|
||||
|
||||
@@ -647,6 +647,11 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
)
|
||||
except exceptions.VolatilityException:
|
||||
vollog.error("Unable to locate symbols for the memory image's tcpip module")
|
||||
return
|
||||
|
||||
if not tcpip_symbol_table:
|
||||
vollog.error("Unable to reconstruct symbol table for tcpip.sys")
|
||||
return
|
||||
|
||||
for netw_obj in self.list_sockets(
|
||||
self.context,
|
||||
|
||||
@@ -16,7 +16,9 @@ class Threads(thrdscan.ThrdScan):
|
||||
"""Lists process threads"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 - changed the signature of `list_orphan_kernel_threads`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -35,10 +37,10 @@ class Threads(thrdscan.ThrdScan):
|
||||
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 1, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="modules", plugin=modules.Modules, version=(2, 1, 0)
|
||||
name="modules", plugin=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -46,7 +48,7 @@ class Threads(thrdscan.ThrdScan):
|
||||
def list_orphan_kernel_threads(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
module_name: str,
|
||||
kernel_module_name: str,
|
||||
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
|
||||
"""Yields thread objects of kernel threads that do not map to a module
|
||||
|
||||
@@ -57,19 +59,16 @@ class Threads(thrdscan.ThrdScan):
|
||||
Returns:
|
||||
A generator of thread objects of orphaned threads
|
||||
"""
|
||||
module = context.modules[module_name]
|
||||
layer_name = module.layer_name
|
||||
symbol_table_name = module.symbol_table_name
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
context, layer_name, symbol_table_name
|
||||
context=context,
|
||||
kernel_module_name=kernel_module_name,
|
||||
)
|
||||
|
||||
kernel_space_start = modules.Modules.get_kernel_space_start(
|
||||
context, module_name
|
||||
context, kernel_module_name
|
||||
)
|
||||
|
||||
for thread in thrdscan.ThrdScan.scan_threads(context, module_name):
|
||||
for thread in thrdscan.ThrdScan.scan_threads(context, kernel_module_name):
|
||||
# We don't want smeared or terminated threads
|
||||
# So we access the owning process (which could also be terminated or smeared)
|
||||
# Plus check the start address holding page
|
||||
|
||||
@@ -244,7 +244,8 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
|
||||
_required_framework_version = (2, 7, 0)
|
||||
|
||||
_version = (1, 1, 0)
|
||||
# 2.0.0 - changed signature of get_kernel_modules, get_all_vads_with_file_paths, addresses_for_process_symbols, get_process_modules
|
||||
_version = (2, 0, 0)
|
||||
|
||||
# used for special handling of the kernel PDB file. See later notes
|
||||
os_module_name = "ntoskrnl.exe"
|
||||
@@ -259,10 +260,10 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pdbutil", component=pdbutil.PDBUtility, version=(1, 0, 0)
|
||||
@@ -297,7 +298,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
layer_name: str,
|
||||
process_layer_name: str,
|
||||
base_address: int,
|
||||
) -> Optional[pefile.PE]:
|
||||
"""
|
||||
@@ -305,7 +306,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
|
||||
Args:
|
||||
pe_table_name: name of the pe types table
|
||||
layer_name: name of the process layer
|
||||
process_layer_name: name of the process layer
|
||||
base_address: base address of the module
|
||||
|
||||
Returns:
|
||||
@@ -317,7 +318,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
dos_header = context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset=base_address,
|
||||
layer_name=layer_name,
|
||||
layer_name=process_layer_name,
|
||||
)
|
||||
|
||||
for offset, data in dos_header.reconstruct():
|
||||
@@ -388,8 +389,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
layer_name: str,
|
||||
symbol_table_name: str,
|
||||
kernel_module_name: str,
|
||||
symbols: filter_modules_type,
|
||||
) -> found_symbols_type:
|
||||
"""
|
||||
@@ -405,7 +405,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
found_symbols_type: The dictionary of symbols that were resolved
|
||||
"""
|
||||
collected_modules = PESymbols.get_process_modules(
|
||||
context, layer_name, symbol_table_name, symbols
|
||||
context, kernel_module_name, symbols
|
||||
)
|
||||
|
||||
found_symbols, missing_symbols = PESymbols.find_symbols(
|
||||
@@ -483,12 +483,12 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
instance for it
|
||||
"""
|
||||
|
||||
layer_name = module_info[0]
|
||||
process_layer_name = module_info[0]
|
||||
module_start = module_info[1]
|
||||
|
||||
# we need a valid PE with an export table
|
||||
pe_module = PESymbols.get_pefile_obj(
|
||||
context, pe_table_name, layer_name, module_start
|
||||
context, pe_table_name, process_layer_name, module_start
|
||||
)
|
||||
if not pe_module:
|
||||
return None
|
||||
@@ -500,7 +500,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
return None
|
||||
|
||||
return ExportSymbolFinder(
|
||||
layer_name,
|
||||
process_layer_name,
|
||||
mod_name.lower(),
|
||||
module_start,
|
||||
pe_module.DIRECTORY_ENTRY_EXPORT.symbols,
|
||||
@@ -783,8 +783,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
def get_kernel_modules(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
filter_modules: Optional[filter_modules_type],
|
||||
) -> collected_modules_type:
|
||||
"""
|
||||
@@ -804,7 +803,9 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
filter_modules_check = None
|
||||
|
||||
session_layers = list(
|
||||
modules.Modules.get_session_layers(context, layer_name, symbol_table)
|
||||
modules.Modules.get_session_layers(
|
||||
context=context, kernel_module_name=kernel_module_name
|
||||
)
|
||||
)
|
||||
|
||||
# special handling for the kernel
|
||||
@@ -813,7 +814,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
for index, mod in enumerate(
|
||||
modules.Modules.list_modules(context, layer_name, symbol_table)
|
||||
modules.Modules.list_modules(context, kernel_module_name)
|
||||
):
|
||||
try:
|
||||
mod_name = str(mod.BaseDllName.get_string().lower())
|
||||
@@ -906,8 +907,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
def get_all_vads_with_file_paths(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table_name: str,
|
||||
kernel_module_name: str,
|
||||
) -> Generator[
|
||||
Tuple[interfaces.objects.ObjectInterface, str, ranges_type],
|
||||
None,
|
||||
@@ -920,9 +920,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
Generator[Tuple[interfaces.objects.ObjectInterface, str, ranges_type]]: Yields tuple of process objects, layers, and VADs mapping files
|
||||
"""
|
||||
procs = pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table_name,
|
||||
context=context, kernel_module_name=kernel_module_name
|
||||
)
|
||||
|
||||
for proc in procs:
|
||||
@@ -939,8 +937,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
def get_process_modules(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
filter_modules: Optional[filter_modules_type],
|
||||
) -> collected_modules_type:
|
||||
"""
|
||||
@@ -960,7 +957,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
filter_modules_check = None
|
||||
|
||||
for _proc, proc_layer_name, vads in PESymbols.get_all_vads_with_file_paths(
|
||||
context, layer_name, symbol_table
|
||||
context, kernel_module_name
|
||||
):
|
||||
for vad_start, vad_size, filepath in vads:
|
||||
filename = PESymbols.filename_for_path(filepath)
|
||||
@@ -977,8 +974,6 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
return proc_modules
|
||||
|
||||
def _generator(self) -> Generator[Tuple[int, Tuple[str, str, int]], None, None]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
if self.config["symbols"]:
|
||||
filter_module = {
|
||||
self.config["module"].lower(): {
|
||||
@@ -1003,7 +998,7 @@ class PESymbols(interfaces.plugins.PluginInterface):
|
||||
module_resolver = self.get_process_modules
|
||||
|
||||
collected_modules = module_resolver(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name, filter_module
|
||||
self.context, self.config["kernel"], filter_module
|
||||
)
|
||||
|
||||
found_symbols, _missing_symbols = PESymbols.find_symbols(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
import logging
|
||||
import ntpath
|
||||
from typing import List, Type, Optional
|
||||
from typing import List, Type, Optional, Iterator, Tuple
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
@@ -18,7 +18,9 @@ class PEDump(interfaces.plugins.PluginInterface):
|
||||
"""Allows extracting PE Files from a specific address in a specific address space"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 - changed the signature of `dump_kernel_pe_at_base`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -30,7 +32,10 @@ class PEDump(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -145,9 +150,19 @@ class PEDump(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def dump_kernel_pe_at_base(cls, context, kernel, pe_table_name, open_method, base):
|
||||
def dump_kernel_pe_at_base(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
pe_table_name: str,
|
||||
open_method: Type[interfaces.plugins.FileHandlerInterface],
|
||||
base: int,
|
||||
) -> Iterator[Tuple[int, str, str]]:
|
||||
"""
|
||||
Extracts a PE file from kernel memory at the given base address
|
||||
"""
|
||||
session_layers = modules.Modules.get_session_layers(
|
||||
context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=context, kernel_module_name=kernel_module_name
|
||||
)
|
||||
|
||||
session_layer_name = modules.Modules.find_session_layer(
|
||||
@@ -182,8 +197,7 @@ class PEDump(interfaces.plugins.PluginInterface):
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=kernel.name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
pid = proc.UniqueProcessId
|
||||
@@ -224,7 +238,11 @@ class PEDump(interfaces.plugins.PluginInterface):
|
||||
|
||||
if self.config["kernel_module"]:
|
||||
pe_files = self.dump_kernel_pe_at_base(
|
||||
self.context, kernel, pe_table_name, self.open, self.config["base"]
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
pe_table_name=pe_table_name,
|
||||
open_method=self.open,
|
||||
base=self.config["base"],
|
||||
)
|
||||
else:
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
@@ -79,6 +79,7 @@ class PoolHeaderScanner(interfaces.layers.ScannerInterface):
|
||||
offset=offset - self._header_offset,
|
||||
absolute=True,
|
||||
)
|
||||
|
||||
constraint = self._constraint_lookup[pattern]
|
||||
try:
|
||||
# Size check
|
||||
@@ -128,7 +129,7 @@ class PoolScanner(plugins.PluginInterface):
|
||||
"""A generic pool scanner plugin."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 1)
|
||||
_version = (1, 1, 1)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -181,6 +182,36 @@ class PoolScanner(plugins.PluginInterface):
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def gui_poolscanner_constraints(
|
||||
gui_table: str, tags_filter: Optional[List[bytes]] = None
|
||||
) -> List[PoolConstraint]:
|
||||
"""
|
||||
Constraints for objects managed by the GUI subsystem (win32k*.sys)
|
||||
"""
|
||||
builtins = [
|
||||
PoolConstraint(
|
||||
b"Wind",
|
||||
type_name=gui_table + constants.BANG + "tagWINDOWSTATION",
|
||||
size=(0x90, None),
|
||||
page_type=PoolType.PAGED,
|
||||
object_type="WindowStation",
|
||||
skip_type_test=True,
|
||||
),
|
||||
PoolConstraint(
|
||||
b"Desk",
|
||||
type_name=gui_table + constants.BANG + "tagDESKTOP",
|
||||
page_type=PoolType.PAGED,
|
||||
object_type="Desktop",
|
||||
skip_type_test=True,
|
||||
),
|
||||
]
|
||||
|
||||
if not tags_filter:
|
||||
return builtins
|
||||
|
||||
return [constraint for constraint in builtins if constraint.tag in tags_filter]
|
||||
|
||||
@classmethod
|
||||
def builtin_constraints(
|
||||
cls, symbol_table: str, tags_filter: Optional[List[bytes]] = None
|
||||
@@ -331,11 +362,12 @@ class PoolScanner(plugins.PluginInterface):
|
||||
return [constraint for constraint in builtins if constraint.tag in tags_filter]
|
||||
|
||||
@classmethod
|
||||
def generate_pool_scan(
|
||||
def generate_pool_scan_extended(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_layer_name: str,
|
||||
kernel_symbol_table_name: str,
|
||||
object_symbol_table_name: str,
|
||||
constraints: List[PoolConstraint],
|
||||
) -> Generator[
|
||||
Tuple[
|
||||
@@ -347,49 +379,66 @@ class PoolScanner(plugins.PluginInterface):
|
||||
None,
|
||||
]:
|
||||
"""
|
||||
The extended version of `generate_pool_scan` to support pool scanning for objects outside of the kernel (ntoskrnl).
|
||||
This requires the symbol table of the object being scanned for.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
kernel_layer_name: The name of the base kernel layer
|
||||
kernel_symbol_table_name: The name of the table containing the kernel symbols
|
||||
object_symbol_table_name: The name of the symbol table for the object being scanned for
|
||||
constraints: List of pool constraints used to limit the scan results
|
||||
|
||||
Returns:
|
||||
Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object
|
||||
"""
|
||||
|
||||
# get the object type map
|
||||
type_map = handles.Handles.get_type_map(
|
||||
context=context, layer_name=layer_name, symbol_table=symbol_table
|
||||
context=context,
|
||||
layer_name=kernel_layer_name,
|
||||
symbol_table=kernel_symbol_table_name,
|
||||
)
|
||||
|
||||
cookie = handles.Handles.find_cookie(
|
||||
context=context, layer_name=layer_name, symbol_table=symbol_table
|
||||
context=context,
|
||||
layer_name=kernel_layer_name,
|
||||
symbol_table=kernel_symbol_table_name,
|
||||
)
|
||||
|
||||
is_windows_10 = versions.is_windows_10(context, symbol_table)
|
||||
is_windows_8_or_later = versions.is_windows_8_or_later(context, symbol_table)
|
||||
is_windows_10 = versions.is_windows_10(context, kernel_symbol_table_name)
|
||||
is_windows_8_or_later = versions.is_windows_8_or_later(
|
||||
context, kernel_symbol_table_name
|
||||
)
|
||||
|
||||
# start off with the primary virtual layer
|
||||
scan_layer = layer_name
|
||||
scan_layer = kernel_layer_name
|
||||
|
||||
# switch to a non-virtual layer if necessary
|
||||
if not is_windows_10:
|
||||
scan_layer = context.layers[scan_layer].config["memory_layer"]
|
||||
|
||||
if symbols.symbol_table_is_64bit(context, symbol_table):
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel_symbol_table_name
|
||||
):
|
||||
alignment = 0x10
|
||||
else:
|
||||
alignment = 8
|
||||
|
||||
# scan in the main kernel layer for the object(s)
|
||||
for constraint, header in cls.pool_scan(
|
||||
context, scan_layer, symbol_table, constraints, alignment=alignment
|
||||
context,
|
||||
scan_layer,
|
||||
object_symbol_table_name,
|
||||
constraints,
|
||||
alignment=alignment,
|
||||
):
|
||||
|
||||
# construct the object in its own layer, using its own types
|
||||
mem_objects = header.get_object(
|
||||
constraint=constraint,
|
||||
use_top_down=is_windows_8_or_later,
|
||||
native_layer_name=layer_name,
|
||||
kernel_symbol_table=symbol_table,
|
||||
native_layer_name=kernel_layer_name,
|
||||
kernel_symbol_table=kernel_symbol_table_name,
|
||||
)
|
||||
|
||||
for mem_object in mem_objects:
|
||||
@@ -398,6 +447,7 @@ class PoolScanner(plugins.PluginInterface):
|
||||
constants.LOGLEVEL_VVV,
|
||||
f"Cannot create an instance of {constraint.type_name}",
|
||||
)
|
||||
|
||||
continue
|
||||
|
||||
if constraint.object_type is not None and not constraint.skip_type_test:
|
||||
@@ -418,6 +468,40 @@ class PoolScanner(plugins.PluginInterface):
|
||||
|
||||
yield constraint, mem_object, header
|
||||
|
||||
@classmethod
|
||||
def generate_pool_scan(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
constraints: List[PoolConstraint],
|
||||
) -> Generator[
|
||||
Tuple[
|
||||
PoolConstraint,
|
||||
interfaces.objects.ObjectInterface,
|
||||
interfaces.objects.ObjectInterface,
|
||||
],
|
||||
None,
|
||||
None,
|
||||
]:
|
||||
"""
|
||||
The original version of `generate_pool_scan` which is sufficient for objects in the kernel (ntoskrnl),
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
constraints: List of pool constraints used to limit the scan results
|
||||
|
||||
Returns:
|
||||
Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object
|
||||
"""
|
||||
|
||||
# repeat the symbol table to match the original `generate_pool_scan` behaviour
|
||||
yield from cls.generate_pool_scan_extended(
|
||||
context, layer_name, symbol_table, symbol_table, constraints
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def pool_scan(
|
||||
cls,
|
||||
@@ -483,7 +567,9 @@ class PoolScanner(plugins.PluginInterface):
|
||||
except exceptions.SymbolError:
|
||||
# We have to manually load a symbol table
|
||||
|
||||
if symbols.symbol_table_is_64bit(context, symbol_table):
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table
|
||||
):
|
||||
is_win_7 = versions.is_windows_7(context, symbol_table)
|
||||
if is_win_7:
|
||||
pool_header_json_filename = "poolheader-x64-win7"
|
||||
|
||||
@@ -61,7 +61,7 @@ class Privs(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -107,7 +107,6 @@ class Privs(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -121,8 +120,7 @@ class Privs(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -29,7 +29,7 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -83,7 +83,6 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_active_process_filter()
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
@@ -96,8 +95,7 @@ class ProcessGhosting(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -22,7 +22,9 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Lists the processes present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 1)
|
||||
|
||||
# 3.0.0 - changed signature for `list_processes`
|
||||
_version = (3, 0, 0)
|
||||
PHYSICAL_DEFAULT = False
|
||||
|
||||
@classmethod
|
||||
@@ -206,35 +208,33 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
def list_processes(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
filter_func: Callable[
|
||||
[interfaces.objects.ObjectInterface], bool
|
||||
] = lambda _: False,
|
||||
) -> Iterator["extensions.EPROCESS"]:
|
||||
"""Lists all the processes in the primary layer that are in the pid
|
||||
"""Lists all the processes in the given layer that are in the pid
|
||||
config option.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
layer_iname: The name of the layer on which to operate
|
||||
symbol_table_name: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes an EPROCESS object and returns True if the process should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of EPROCESS objects from the `layer_name` layer's PsActiveProcessHead list after filtering
|
||||
"""
|
||||
|
||||
# We only use the object factory to demonstrate how to use one
|
||||
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
|
||||
if not kvo:
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
if not kernel.offset:
|
||||
raise ValueError(
|
||||
"Intel layer does not have an associated kernel virtual offset, failing"
|
||||
)
|
||||
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
|
||||
|
||||
ps_aph_offset = ntkrnlmp.get_symbol("PsActiveProcessHead").address
|
||||
list_entry = ntkrnlmp.object(object_type="_LIST_ENTRY", offset=ps_aph_offset)
|
||||
ps_aph_offset = kernel.get_symbol("PsActiveProcessHead").address
|
||||
list_entry = kernel.object(object_type="_LIST_ENTRY", offset=ps_aph_offset)
|
||||
|
||||
# This is example code to demonstrate how to use symbol_space directly, rather than through a module:
|
||||
#
|
||||
@@ -247,10 +247,10 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
# Note: "nt_symbols!_EPROCESS" could have been used, but would rely on the "nt_symbols" symbol table not already
|
||||
# having been present. Strictly, the value of the requirement should be joined with the BANG character
|
||||
# defined in the constants file
|
||||
reloff = ntkrnlmp.get_type("_EPROCESS").relative_child_offset(
|
||||
reloff = kernel.get_type("_EPROCESS").relative_child_offset(
|
||||
"ActiveProcessLinks"
|
||||
)
|
||||
eproc = ntkrnlmp.object(
|
||||
eproc = kernel.object(
|
||||
object_type="_EPROCESS",
|
||||
offset=list_entry.vol.offset - reloff,
|
||||
absolute=True,
|
||||
@@ -273,8 +273,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
|
||||
for proc in self.list_processes(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
filter_func=self.create_pid_filter(self.config.get("pid", None)),
|
||||
):
|
||||
if not self.config.get("physical", self.PHYSICAL_DEFAULT):
|
||||
|
||||
@@ -34,7 +34,7 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="info", component=info.Info, version=(1, 0, 0)
|
||||
|
||||
@@ -40,7 +40,7 @@ class PsTree(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -85,7 +85,7 @@ class PsTree(interfaces.plugins.PluginInterface):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
):
|
||||
if not self.config.get("physical", pslist.PsList.PHYSICAL_DEFAULT):
|
||||
offset = proc.vol.offset
|
||||
|
||||
@@ -53,7 +53,7 @@ We recommend using -r pretty if you are looking at this plugin's output in a ter
|
||||
name="info", component=info.Info, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="psscan", component=psscan.PsScan, version=(1, 0, 0)
|
||||
@@ -181,12 +181,9 @@ We recommend using -r pretty if you are looking at this plugin's output in a ter
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
layer_name = kernel.layer_name
|
||||
symbol_table = kernel.symbol_table_name
|
||||
|
||||
kdbg_list_processes = list(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context, layer_name=layer_name, symbol_table=symbol_table
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -194,10 +191,12 @@ We recommend using -r pretty if you are looking at this plugin's output in a ter
|
||||
processes: Dict[str, Dict[int, extensions.EPROCESS]] = {}
|
||||
|
||||
processes["pslist"] = self._check_pslist(kdbg_list_processes)
|
||||
processes["psscan"] = self._check_psscan(layer_name, symbol_table)
|
||||
processes["psscan"] = self._check_psscan(
|
||||
kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
processes["thrdscan"] = self._check_thrdscan()
|
||||
processes["csrss"] = self._check_csrss_handles(
|
||||
kdbg_list_processes, layer_name, symbol_table
|
||||
kdbg_list_processes, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
|
||||
# Unique set of all offsets from all sources
|
||||
|
||||
@@ -28,18 +28,16 @@ class GetCellRoutine(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
|
||||
# walk each hive and validate that the GetCellRoutine handler
|
||||
@@ -47,8 +45,7 @@ class GetCellRoutine(interfaces.plugins.PluginInterface):
|
||||
for hive_object in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
):
|
||||
hive = hive_object.hive
|
||||
|
||||
|
||||
@@ -41,9 +41,11 @@ class HiveGenerator:
|
||||
class HiveList(interfaces.plugins.PluginInterface):
|
||||
"""Lists the registry hives present in a particular memory image."""
|
||||
|
||||
_version = (1, 0, 1)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
# 2.0.0 - changed the signature of list_hives
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
@@ -93,10 +95,9 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
# Construct the hive
|
||||
hive = next(
|
||||
self.list_hives(
|
||||
self.context,
|
||||
self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=[hive_object.vol.offset],
|
||||
)
|
||||
)
|
||||
@@ -137,8 +138,7 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
base_config_path: str,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
filter_string: Optional[str] = None,
|
||||
hive_offsets: Optional[List[int]] = None,
|
||||
) -> Iterator[registry.RegistryHive]:
|
||||
@@ -148,20 +148,24 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
base_config_path: The configuration path for any settings required by the new table
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
kernel_module_name: The name of the module for the kernel
|
||||
filter_string: An optional string which must be present in the hive name if specified
|
||||
offset: An optional offset to specify a specific hive to iterate over (takes precedence over filter_string)
|
||||
|
||||
Yields:
|
||||
A registry hive layer name
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
if hive_offsets is None:
|
||||
try:
|
||||
hive_offsets = [
|
||||
hive.vol.offset
|
||||
for hive in cls.list_hive_objects(
|
||||
context, layer_name, symbol_table, filter_string
|
||||
context=context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_string=filter_string,
|
||||
)
|
||||
]
|
||||
except ImportError:
|
||||
@@ -178,8 +182,9 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
context=context,
|
||||
base_config_path=base_config_path,
|
||||
hive_offset=hive_offset,
|
||||
base_layer=layer_name,
|
||||
nt_symbols=symbol_table,
|
||||
base_layer=kernel.layer_name,
|
||||
nt_symbols=kernel.symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -50,7 +50,9 @@ class HiveScan(interfaces.plugins.PluginInterface):
|
||||
|
||||
kernel = context.modules[kernel_name]
|
||||
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, kernel.symbol_table_name)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
is_windows_8_1_or_later = versions.is_windows_8_1_or_later(
|
||||
context=context, symbol_table=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ class PrintKey(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="offset", description="Hive Offset", default=None, optional=True
|
||||
@@ -240,17 +240,14 @@ class PrintKey(interfaces.plugins.PluginInterface):
|
||||
|
||||
def _registry_walker(
|
||||
self,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
hive_offsets: Optional[List[int]] = None,
|
||||
key: Optional[str] = None,
|
||||
recurse: bool = False,
|
||||
):
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
self.context,
|
||||
self.config_path,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
hive_offsets=hive_offsets,
|
||||
):
|
||||
try:
|
||||
@@ -292,7 +289,6 @@ class PrintKey(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
offset = self.config.get("offset", None)
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return TreeGrid(
|
||||
columns=[
|
||||
@@ -305,8 +301,6 @@ class PrintKey(interfaces.plugins.PluginInterface):
|
||||
("Volatile", bool),
|
||||
],
|
||||
generator=self._registry_walker(
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
hive_offsets=None if offset is None else [offset],
|
||||
key=self.config.get("key", None),
|
||||
recurse=self.config.get("recurse", None),
|
||||
|
||||
@@ -54,7 +54,7 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac
|
||||
name="offset", description="Hive Offset", default=None, optional=True
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -295,7 +295,6 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac
|
||||
hive_offsets = None
|
||||
if self.config.get("offset", None) is not None:
|
||||
hive_offsets = [self.config.get("offset", None)]
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
self._reg_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self._config_path, "windows", "registry"
|
||||
@@ -305,8 +304,7 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_string="ntuser.dat",
|
||||
hive_offsets=hive_offsets,
|
||||
):
|
||||
|
||||
@@ -1103,7 +1103,7 @@ class ScheduledTasks(interfaces.plugins.PluginInterface, timeliner.TimeLinerInte
|
||||
information about triggers, actions, run times, and creation times."""
|
||||
|
||||
_required_framework_version = (2, 11, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -1115,7 +1115,7 @@ information about triggers, actions, run times, and creation times."""
|
||||
architectures=["Intel33", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1135,7 +1135,7 @@ information about triggers, actions, run times, and creation times."""
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel: interfaces.context.ModuleInterface,
|
||||
kernel_module_name: str,
|
||||
) -> Optional[registry.RegistryHive]:
|
||||
"""Retrieves the `Amcache.hve` registry hive from the kernel module, if it can be located."""
|
||||
return next(
|
||||
@@ -1144,8 +1144,7 @@ information about triggers, actions, run times, and creation times."""
|
||||
base_config_path=interfaces.configuration.path_join(
|
||||
config_path, "hivelist"
|
||||
),
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_string="SOFTWARE",
|
||||
),
|
||||
None,
|
||||
@@ -1348,7 +1347,7 @@ information about triggers, actions, run times, and creation times."""
|
||||
args,
|
||||
(
|
||||
action_set.context
|
||||
if action_set is not None
|
||||
if (action_set is not None and action_set.context is not None)
|
||||
else renderers.NotAvailableValue()
|
||||
),
|
||||
working_directory,
|
||||
@@ -1356,11 +1355,11 @@ information about triggers, actions, run times, and creation times."""
|
||||
)
|
||||
|
||||
def _generator(self) -> Iterator[Tuple[int, _ScheduledTaskEntry]]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
# Building the dictionary ahead of time is much better for performance
|
||||
# vs looking up each service's DLL individually.
|
||||
software_hive = self.get_software_hive(self.context, self.config_path, kernel)
|
||||
software_hive = self.get_software_hive(
|
||||
self.context, self.config_path, self.config["kernel"]
|
||||
)
|
||||
if software_hive is None:
|
||||
vollog.warning("Failed to get SOFTWARE hive")
|
||||
return
|
||||
|
||||
@@ -28,7 +28,7 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -39,16 +39,14 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
# Collect all the values as we will want to group them later
|
||||
sessions = {}
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
):
|
||||
session_id = proc.get_session_id()
|
||||
|
||||
@@ -65,13 +65,13 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -79,7 +79,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
def create_shimcache_table(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
symbol_table_name: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a shimcache symbol table
|
||||
@@ -92,16 +92,18 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
Returns:
|
||||
The name of the constructed shimcache table
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
native_types = context.symbol_space[symbol_table_name].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table_name
|
||||
)
|
||||
table_mapping = {"nt_symbols": symbol_table_name}
|
||||
|
||||
try:
|
||||
symbol_filename = next(
|
||||
filename
|
||||
for version_check, for_64bit, filename in ShimcacheMem._win_version_file_map
|
||||
if is_64bit == for_64bit
|
||||
and version_check(context=context, symbol_table=symbol_table)
|
||||
and version_check(context=context, symbol_table=symbol_table_name)
|
||||
)
|
||||
except StopIteration:
|
||||
raise NotImplementedError("This version of Windows is not supported!")
|
||||
@@ -122,8 +124,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
def find_shimcache_win_xp(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
kernel_symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Attempts to find the shimcache in a Windows XP memory image
|
||||
@@ -142,9 +143,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
|
||||
seen = set()
|
||||
|
||||
for process in pslist.PsList.list_processes(
|
||||
context, layer_name, kernel_symbol_table
|
||||
):
|
||||
for process in pslist.PsList.list_processes(context, kernel_module_name):
|
||||
pid = process.UniqueProcessId
|
||||
vollog.debug("checking process %d", pid)
|
||||
for vad in vadinfo.VadInfo.list_vads(
|
||||
@@ -219,8 +218,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_layer_name: str,
|
||||
nt_symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Implements the algorithm to search for the shim cache on Windows 2000
|
||||
@@ -239,31 +237,37 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
data_sec = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
kernel_module_name,
|
||||
cls.NT_KRNL_MODS,
|
||||
".data",
|
||||
)
|
||||
mod_page = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
kernel_module_name,
|
||||
cls.NT_KRNL_MODS,
|
||||
"PAGE",
|
||||
)
|
||||
|
||||
# We require both in order to accurately handle AVL table
|
||||
if not (data_sec and mod_page):
|
||||
return None
|
||||
return
|
||||
|
||||
data_sec_offset, data_sec_size = data_sec
|
||||
mod_page_offset, mod_page_size = mod_page
|
||||
|
||||
addr_size = 8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4
|
||||
addr_size = (
|
||||
8
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
else 4
|
||||
)
|
||||
|
||||
shim_head = None
|
||||
for offset in range(
|
||||
@@ -272,8 +276,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
shim_head = cls.try_get_shim_head_at_offset(
|
||||
context,
|
||||
shimcache_symbol_table,
|
||||
nt_symbol_table,
|
||||
kernel_layer_name,
|
||||
kernel_module_name,
|
||||
mod_page_offset,
|
||||
mod_page_offset + mod_page_size,
|
||||
offset,
|
||||
@@ -293,9 +296,8 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
def try_get_shim_head_at_offset(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
kernel_symbol_table: str,
|
||||
layer_name: str,
|
||||
shimcache_symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
mod_page_start: int,
|
||||
mod_page_end: int,
|
||||
offset: int,
|
||||
@@ -307,9 +309,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
If a number of validity checks are passed, this method will return the `SHIM_CACHE_HEAD`
|
||||
object. Otherwise, `None` is returned.
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
# Check RTL_AVL_TABLE at offset
|
||||
rtl_avl_table = context.object(
|
||||
symbol_table + constants.BANG + "_RTL_AVL_TABLE", layer_name, offset
|
||||
shimcache_symbol_table + constants.BANG + "_RTL_AVL_TABLE",
|
||||
kernel.layer_name,
|
||||
offset,
|
||||
)
|
||||
if not rtl_avl_table.is_valid(mod_page_start, mod_page_end):
|
||||
return None
|
||||
@@ -317,11 +324,13 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
vollog.debug(f"Candidate RTL_AVL_TABLE found at offset {offset:#x}")
|
||||
|
||||
ersrc_size = context.symbol_space.get_type(
|
||||
kernel_symbol_table + constants.BANG + "_ERESOURCE"
|
||||
kernel.symbol_table_name + constants.BANG + "_ERESOURCE"
|
||||
).size
|
||||
ersrc_alignment = (
|
||||
0x20
|
||||
if symbols.symbol_table_is_64bit(context, kernel_symbol_table)
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
else 0x10
|
||||
# 0x20 if context.symbol_space.get_type("pointer").size == 8 else 0x10
|
||||
)
|
||||
@@ -334,8 +343,8 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
|
||||
vollog.debug(f"Constructing ERESOURCE at {eresource_offset:#x}")
|
||||
eresource = context.object(
|
||||
kernel_symbol_table + constants.BANG + "_ERESOURCE",
|
||||
layer_name,
|
||||
kernel.symbol_table_name + constants.BANG + "_ERESOURCE",
|
||||
kernel.layer_name,
|
||||
eresource_offset,
|
||||
)
|
||||
if not eresource.is_valid():
|
||||
@@ -344,12 +353,12 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
|
||||
shim_head_offset = offset + rtl_avl_table.vol.size
|
||||
|
||||
if not context.layers[layer_name].is_valid(shim_head_offset):
|
||||
if not context.layers[kernel.layer_name].is_valid(shim_head_offset):
|
||||
return None
|
||||
|
||||
shim_head = context.object(
|
||||
symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
|
||||
layer_name,
|
||||
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
|
||||
kernel.layer_name,
|
||||
shim_head_offset,
|
||||
)
|
||||
|
||||
@@ -365,8 +374,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_layer_name: str,
|
||||
nt_symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
shimcache_symbol_table: str,
|
||||
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
|
||||
"""Attempts to locate and yield shimcache entries from a Windows 8 or later memory image.
|
||||
@@ -376,10 +384,11 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
:param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
is_8_1_or_later = versions.is_windows_8_1_or_later(
|
||||
context, nt_symbol_table
|
||||
) or versions.is_win10(context, nt_symbol_table)
|
||||
context, kernel.symbol_table_name
|
||||
) or versions.is_win10(context, kernel.symbol_table_name)
|
||||
|
||||
module_names = ["ahcache.sys"] if is_8_1_or_later else cls.NT_KRNL_MODS
|
||||
vollog.debug(f"Searching for modules {module_names}")
|
||||
@@ -387,16 +396,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
data_sec = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
kernel_module_name,
|
||||
module_names,
|
||||
".data",
|
||||
)
|
||||
mod_page = cls.get_module_section_range(
|
||||
context,
|
||||
config_path,
|
||||
kernel_layer_name,
|
||||
nt_symbol_table,
|
||||
kernel_module_name,
|
||||
module_names,
|
||||
"PAGE",
|
||||
)
|
||||
@@ -419,12 +426,18 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
for offset in range(
|
||||
data_sec_offset,
|
||||
data_sec_offset + data_sec_size,
|
||||
8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4,
|
||||
(
|
||||
8
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
else 4
|
||||
),
|
||||
):
|
||||
vollog.debug(f"Building shim handle pointer at {offset:#x}")
|
||||
shim_handle = context.object(
|
||||
object_type=shimcache_symbol_table + constants.BANG + "pointer",
|
||||
layer_name=kernel_layer_name,
|
||||
layer_name=kernel.layer_name,
|
||||
subtype=handle_type,
|
||||
offset=offset,
|
||||
)
|
||||
@@ -445,7 +458,9 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
# On Windows 8 x64, the first cache contains the shim cache.
|
||||
# On Windows 8 x86, 8.1 x86/x64, and 10, the second cache contains the shim cache.
|
||||
if (
|
||||
not symbols.symbol_table_is_64bit(context, nt_symbol_table)
|
||||
not symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
and not is_8_1_or_later
|
||||
):
|
||||
valid_head = shim_heads[1]
|
||||
@@ -474,8 +489,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
entries = self.find_shimcache_win_8_or_later(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
shimcache_table_name,
|
||||
)
|
||||
|
||||
@@ -488,8 +502,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
entries = self.find_shimcache_win_2k3_to_7(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
shimcache_table_name,
|
||||
)
|
||||
|
||||
@@ -499,8 +512,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
vollog.info("Finding shimcache entries for WinXP")
|
||||
entries = self.find_shimcache_win_xp(
|
||||
self._context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
shimcache_table_name,
|
||||
)
|
||||
else:
|
||||
@@ -547,8 +559,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
module_list: List[str],
|
||||
section_name: str,
|
||||
) -> Optional[Tuple[int, int]]:
|
||||
@@ -566,14 +577,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
try:
|
||||
krnl_mod = next(
|
||||
module
|
||||
for module in modules.Modules.list_modules(
|
||||
context, layer_name, symbol_table
|
||||
)
|
||||
for module in modules.Modules.list_modules(context, kernel_module_name)
|
||||
if module.BaseDllName.String in module_list
|
||||
)
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
interfaces.configuration.path_join(config_path, "pe"),
|
||||
@@ -585,7 +596,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
|
||||
# code taken from Win32KBase._section_chunks (win32_core.py)
|
||||
dos_header = context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
layer_name,
|
||||
kernel.layer_name,
|
||||
offset=krnl_mod.DllBase,
|
||||
)
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -61,7 +61,7 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
name="pdbutil", component=pdbutil.PDBUtility, version=(1, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(1, 1, 0)
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -568,7 +568,9 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
"""
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name):
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
):
|
||||
vollog.info("This plugin only supports 64bit Windows memory samples")
|
||||
return None
|
||||
|
||||
@@ -660,8 +662,6 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
return process_name != "lsass.exe"
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
@@ -673,8 +673,7 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=self._lsass_proc_filter,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -19,7 +19,9 @@ class SSDT(plugins.PluginInterface):
|
||||
"""Lists the system call table."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 1)
|
||||
|
||||
# 2.0.0 - changed the signature of `build_module_collection`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -30,7 +32,7 @@ class SSDT(plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="modules", plugin=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", plugin=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -38,23 +40,23 @@ class SSDT(plugins.PluginInterface):
|
||||
def build_module_collection(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
) -> contexts.ModuleCollection:
|
||||
"""Builds a collection of modules.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
kernel_module_name: Name of the module for the kernel
|
||||
|
||||
Returns:
|
||||
A Module collection of available modules based on `Modules.list_modules`
|
||||
"""
|
||||
|
||||
mods = modules.Modules.list_modules(context, layer_name, symbol_table)
|
||||
mods = modules.Modules.list_modules(context, kernel_module_name)
|
||||
context_modules = []
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
for mod in mods:
|
||||
try:
|
||||
module_name_with_ext = mod.BaseDllName.get_string()
|
||||
@@ -64,17 +66,13 @@ class SSDT(plugins.PluginInterface):
|
||||
|
||||
module_name = os.path.splitext(module_name_with_ext)[0]
|
||||
|
||||
symbol_table_name = None
|
||||
if module_name in constants.windows.KERNEL_MODULE_NAMES:
|
||||
symbol_table_name = symbol_table
|
||||
|
||||
context_module = contexts.SizedModule.create(
|
||||
context=context,
|
||||
module_name=module_name,
|
||||
layer_name=layer_name,
|
||||
layer_name=kernel.layer_name,
|
||||
offset=mod.DllBase,
|
||||
size=mod.SizeOfImage,
|
||||
symbol_table_name=symbol_table_name,
|
||||
symbol_table_name=kernel.symbol_table_name,
|
||||
)
|
||||
|
||||
context_modules.append(context_module)
|
||||
@@ -84,9 +82,9 @@ class SSDT(plugins.PluginInterface):
|
||||
def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
layer_name = kernel.layer_name
|
||||
collection = self.build_module_collection(
|
||||
self.context, layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
ntkrnlmp = kernel
|
||||
@@ -103,7 +101,7 @@ class SSDT(plugins.PluginInterface):
|
||||
# on 64-bit systems the indexes are also 32-bits but they're offsets from the
|
||||
# base address of the table and can be negative, so we need a signed data type
|
||||
is_kernel_64 = symbols.symbol_table_is_64bit(
|
||||
self.context, kernel.symbol_table_name
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
if is_kernel_64:
|
||||
array_subtype = "long"
|
||||
|
||||
@@ -18,8 +18,11 @@ vollog = logging.getLogger(__name__)
|
||||
class Strings(interfaces.plugins.PluginInterface):
|
||||
"""Reads output from the strings command and indicates which process(es) each string belongs to."""
|
||||
|
||||
_version = (1, 2, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
# 2.0.0 - change signature of `generate_mapping`
|
||||
_version = (2, 0, 0)
|
||||
|
||||
strings_pattern = re.compile(rb"^(?:\W*)([0-9]+)(?:\W*)(\w[\w\W]+)\n?")
|
||||
|
||||
@classmethod
|
||||
@@ -31,7 +34,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -68,12 +71,10 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
except ValueError:
|
||||
vollog.error(f"Line in unrecognized format: line {count}")
|
||||
line = strings_fp.readline()
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
revmap = self.generate_mapping(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
progress_callback=self._progress_callback,
|
||||
pid_list=self.config["pid"],
|
||||
)
|
||||
@@ -122,8 +123,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
def generate_mapping(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
progress_callback: constants.ProgressCallback = None,
|
||||
pid_list: Optional[List[int]] = None,
|
||||
) -> Dict[int, Set[Tuple[str, int]]]:
|
||||
@@ -132,8 +132,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
|
||||
Args:
|
||||
context: the context for the method to run against
|
||||
layer_name: the layer to map against the string lines
|
||||
symbol_table: the name of the symbol table for the provided layer
|
||||
kernel_module_name: the name of the module forthe kernel
|
||||
progress_callback: an optional callable to display progress
|
||||
pid_list: a lit of process IDs to consider when generating the reverse map
|
||||
|
||||
@@ -142,7 +141,9 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
"""
|
||||
filter = pslist.PsList.create_pid_filter(pid_list)
|
||||
|
||||
layer = context.layers[layer_name]
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
layer = context.layers[kernel.layer_name]
|
||||
reverse_map: Dict[int, Set[Tuple[str, int]]] = dict()
|
||||
if isinstance(layer, intel.Intel):
|
||||
# We don't care about errors, we just wanted chunks that map correctly
|
||||
@@ -161,7 +162,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
# TODO: Include kernel modules
|
||||
|
||||
for process in pslist.PsList.list_processes(
|
||||
context, layer_name, symbol_table
|
||||
context=context, kernel_module_name=kernel_module_name
|
||||
):
|
||||
if not filter(process):
|
||||
proc_id = "Unknown"
|
||||
@@ -179,7 +180,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
for mapval in proc_layer.mapping(
|
||||
0x0, proc_layer.maximum_address, ignore_errors=True
|
||||
):
|
||||
mapped_offset, _, offset, mapped_size, maplayer = mapval
|
||||
mapped_offset, _, offset, mapped_size, _maplayer = mapval
|
||||
for val in range(
|
||||
mapped_offset, mapped_offset + mapped_size, 0x1000
|
||||
):
|
||||
|
||||
@@ -30,13 +30,13 @@ class SuspendedThreads(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(1, 0, 0)
|
||||
name="pe_symbols", component=pe_symbols.PESymbols, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="threads", component=threads.Threads, version=(1, 0, 0)
|
||||
name="threads", component=threads.Threads, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -62,9 +62,7 @@ class SuspendedThreads(interfaces.plugins.PluginInterface):
|
||||
|
||||
# walk the threads of each process checking for suspended threads
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
):
|
||||
for thread in threads.Threads.list_threads(kernel, proc):
|
||||
try:
|
||||
@@ -96,7 +94,9 @@ class SuspendedThreads(interfaces.plugins.PluginInterface):
|
||||
# will not have suspended threads
|
||||
if not proc_modules:
|
||||
proc_modules = pe_symbols.PESymbols.get_process_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name, None
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_modules=None,
|
||||
)
|
||||
|
||||
path_and_symbol = functools.partial(
|
||||
|
||||
@@ -34,8 +34,14 @@ class SuspiciousThreads(interfaces.plugins.PluginInterface):
|
||||
element_type=int,
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="threads", plugin=threads.Threads, version=(1, 0, 0)
|
||||
requirements.VersionRequirement(
|
||||
name="thrdscan", component=thrdscan.ThrdScan, version=(1, 1, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="threads", component=threads.Threads, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -133,8 +139,7 @@ class SuspiciousThreads(interfaces.plugins.PluginInterface):
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
):
|
||||
ranges = self._get_ranges(kernel, all_ranges, proc)
|
||||
|
||||
@@ -26,6 +26,8 @@ class SvcDiff(svcscan.SvcScan):
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enumeration_method = self.service_diff
|
||||
@@ -40,10 +42,10 @@ class SvcDiff(svcscan.SvcScan):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="svclist", component=svclist.SvcList, version=(1, 0, 0)
|
||||
name="svclist", component=svclist.SvcList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="svcscan", component=svcscan.SvcScan, version=(3, 0, 0)
|
||||
name="svcscan", component=svcscan.SvcScan, version=(4, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -51,8 +53,7 @@ class SvcDiff(svcscan.SvcScan):
|
||||
def service_diff(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
@@ -61,10 +62,12 @@ class SvcDiff(svcscan.SvcScan):
|
||||
On Windows 10 version 15063+ 64bit Windows memory samples, walk the services list
|
||||
and scan for services then report differences
|
||||
"""
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context, symbol_table
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
) or not versions.is_win10_15063_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
context=context, symbol_table=kernel.symbol_table_name
|
||||
):
|
||||
vollog.warning(
|
||||
"This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples"
|
||||
@@ -78,8 +81,7 @@ class SvcDiff(svcscan.SvcScan):
|
||||
# collect unique service names from scanning
|
||||
for service in svcscan.SvcScan.service_scan(
|
||||
context,
|
||||
layer_name,
|
||||
symbol_table,
|
||||
kernel_module_name,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
@@ -90,8 +92,7 @@ class SvcDiff(svcscan.SvcScan):
|
||||
# collect services from listing walking
|
||||
for service in svclist.SvcList.service_list(
|
||||
context,
|
||||
layer_name,
|
||||
symbol_table,
|
||||
kernel_module_name,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
|
||||
@@ -19,7 +19,9 @@ class SvcList(svcscan.SvcScan):
|
||||
"""Lists services contained with the services.exe doubly linked list of services"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# 2.0.0 - service_list signature changed
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -30,7 +32,7 @@ class SvcList(svcscan.SvcScan):
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.PluginRequirement(
|
||||
name="svcscan", plugin=svcscan.SvcScan, version=(3, 0, 0)
|
||||
name="svcscan", plugin=svcscan.SvcScan, version=(4, 0, 0)
|
||||
),
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
@@ -60,16 +62,17 @@ class SvcList(svcscan.SvcScan):
|
||||
def service_list(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context, symbol_table
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
) or not versions.is_win10_15063_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
context=context, symbol_table=kernel.symbol_table_name
|
||||
):
|
||||
vollog.warning(
|
||||
"This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples"
|
||||
@@ -78,19 +81,18 @@ class SvcList(svcscan.SvcScan):
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
try:
|
||||
layer_name = proc.add_process_layer()
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.warning(
|
||||
f"Unable to access memory of services.exe running with PID: {proc.UniqueProcessId}"
|
||||
)
|
||||
continue
|
||||
|
||||
layer = context.layers[layer_name]
|
||||
proc_layer = context.layers[proc_layer_name]
|
||||
|
||||
exe_range = cls._get_exe_range(proc)
|
||||
if not exe_range:
|
||||
@@ -99,7 +101,7 @@ class SvcList(svcscan.SvcScan):
|
||||
)
|
||||
continue
|
||||
|
||||
for offset in layer.scan(
|
||||
for offset in proc_layer.scan(
|
||||
context=context,
|
||||
scanner=scanners.BytesScanner(needle=b"Sc27"),
|
||||
sections=exe_range,
|
||||
@@ -108,6 +110,6 @@ class SvcList(svcscan.SvcScan):
|
||||
context,
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
layer_name,
|
||||
proc_layer_name,
|
||||
offset,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict, List, NamedTuple, Optional, Tuple, Union, cast
|
||||
from typing import Dict, List, NamedTuple, Optional, Tuple, Union, cast, Callable
|
||||
|
||||
from volatility3.framework import (
|
||||
constants,
|
||||
@@ -35,7 +35,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
"""Scans for windows services."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (3, 0, 2)
|
||||
_version = (4, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -51,13 +51,13 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="poolscanner", plugin=poolscanner.PoolScanner, version=(1, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -106,7 +106,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
@staticmethod
|
||||
def _create_service_table(
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
symbol_table_name: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Constructs a symbol table containing the symbols for services
|
||||
@@ -120,15 +120,17 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
Returns:
|
||||
A symbol table containing the symbols necessary for services
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
native_types = context.symbol_space[symbol_table_name].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table_name
|
||||
)
|
||||
|
||||
try:
|
||||
symbol_filename = next(
|
||||
filename
|
||||
for version_check, for_64bit, filename in SvcScan._win_version_file_map
|
||||
if is_64bit == for_64bit
|
||||
and version_check(context=context, symbol_table=symbol_table)
|
||||
and version_check(context=context, symbol_table=symbol_table_name)
|
||||
)
|
||||
except StopIteration:
|
||||
raise NotImplementedError("This version of Windows is not supported!")
|
||||
@@ -144,15 +146,15 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
|
||||
@staticmethod
|
||||
def _get_service_key(
|
||||
context, config_path: str, layer_name: str, symbol_table: str
|
||||
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(
|
||||
config_path, "hivelist"
|
||||
),
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_string="machine\\system",
|
||||
):
|
||||
# Get ControlSet\Services.
|
||||
@@ -278,18 +280,19 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
def service_scan(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
symbol_table: str,
|
||||
kernel_module_name: str,
|
||||
service_table_name: str,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
):
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
relative_tag_offset = context.symbol_space.get_type(
|
||||
service_table_name + constants.BANG + "_SERVICE_RECORD"
|
||||
).relative_child_offset("Tag")
|
||||
|
||||
is_vista_or_later = versions.is_vista_or_later(
|
||||
context=context, symbol_table=symbol_table
|
||||
context=context, symbol_table=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
if is_vista_or_later:
|
||||
@@ -300,9 +303,8 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
seen = []
|
||||
|
||||
for task in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table,
|
||||
context,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
proc_id = "Unknown"
|
||||
@@ -315,7 +317,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
continue
|
||||
|
||||
layer = context.layers[proc_layer_name]
|
||||
process_layer = context.layers[proc_layer_name]
|
||||
|
||||
# get process sections for scanning
|
||||
sections = []
|
||||
@@ -324,7 +326,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
if vad.get_size():
|
||||
sections.append((base, vad.get_size()))
|
||||
|
||||
for offset in layer.scan(
|
||||
for offset in process_layer.scan(
|
||||
context=context,
|
||||
scanner=scanners.BytesScanner(needle=service_tag),
|
||||
sections=sections,
|
||||
@@ -360,18 +362,20 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
yield service_record
|
||||
|
||||
@classmethod
|
||||
def get_prereq_info(cls, context, config_path, layer_name: str, symbol_table: str):
|
||||
def get_prereq_info(
|
||||
cls, context, config_path: str, kernel_module_name: str
|
||||
) -> Tuple[str, Dict, Callable]:
|
||||
"""
|
||||
Data structures and information needed to analyze service information
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
service_table_name = cls._create_service_table(
|
||||
context, symbol_table, config_path
|
||||
context, kernel.symbol_table_name, config_path
|
||||
)
|
||||
|
||||
services_key = cls._get_service_key(
|
||||
context, config_path, layer_name, symbol_table
|
||||
)
|
||||
services_key = cls._get_service_key(context, config_path, kernel_module_name)
|
||||
|
||||
service_binary_dll_map = (
|
||||
cls._get_service_binary_map(services_key)
|
||||
@@ -384,16 +388,13 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
return service_table_name, service_binary_dll_map, filter_func
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info(
|
||||
self.context, self.config_path, kernel.layer_name, kernel.symbol_table_name
|
||||
self.context, self.config_path, self.config["kernel"]
|
||||
)
|
||||
|
||||
for record in self._enumeration_method(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
self.config["kernel"],
|
||||
service_table_name,
|
||||
service_binary_dll_map,
|
||||
filter_func,
|
||||
|
||||
@@ -16,7 +16,7 @@ class Threads(thrdscan.ThrdScan):
|
||||
"""Lists process threads"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (1, 0, 1)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -59,16 +59,18 @@ class Threads(thrdscan.ThrdScan):
|
||||
|
||||
@classmethod
|
||||
def list_process_threads(
|
||||
cls, context: interfaces.context.ContextInterface, module_name: str
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Runs through all processes and lists threads for each process"""
|
||||
module = context.modules[module_name]
|
||||
layer_name = module.layer_name
|
||||
symbol_table_name = module.symbol_table_name
|
||||
module = context.modules[kernel_module_name]
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(context.config.get("pid", None))
|
||||
|
||||
for proc in pslist.PsList.list_processes(
|
||||
context=context,
|
||||
layer_name=layer_name,
|
||||
symbol_table=symbol_table_name,
|
||||
kernel_module_name=kernel_module_name,
|
||||
filter_func=filter_func,
|
||||
):
|
||||
yield from cls.list_threads(module, proc)
|
||||
|
||||
@@ -35,7 +35,7 @@ class Timers(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0)
|
||||
name="ssdt", plugin=ssdt.SSDT, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="kpcrs", plugin=kpcrs.KPCRs, version=(1, 0, 0)
|
||||
@@ -122,15 +122,18 @@ class Timers(interfaces.plugins.PluginInterface):
|
||||
|
||||
def _generator(self) -> Iterator[Tuple]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
layer_name = kernel.layer_name
|
||||
symbol_table = kernel.symbol_table_name
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
)
|
||||
|
||||
# FIXME - the list_timers API is gross. Fix after GUI merge
|
||||
for timer in self.list_timers(
|
||||
self.context, self.config["kernel"], layer_name, symbol_table
|
||||
self.context,
|
||||
self.config["kernel"],
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
):
|
||||
if not timer.valid_type():
|
||||
continue
|
||||
|
||||
@@ -33,7 +33,7 @@ class Passphrase(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", component=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="min-length",
|
||||
@@ -121,7 +121,7 @@ class Passphrase(interfaces.plugins.PluginInterface):
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
mods: Iterable[ObjectInterface] = modules.Modules.list_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
self.context, self.config["kernel"]
|
||||
)
|
||||
truecrypt_module_base = next(
|
||||
mod.DllBase
|
||||
|
||||
@@ -22,6 +22,7 @@ class unhooked_system_calls(interfaces.plugins.PluginInterface):
|
||||
"""Looks for signs of Skeleton Key malware"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
system_calls = {
|
||||
"ntdll.dll": {
|
||||
@@ -94,16 +95,16 @@ class unhooked_system_calls(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", component=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pe_symbols", plugin=pe_symbols.PESymbols, version=(1, 0, 0)
|
||||
name="pe_symbols", plugin=pe_symbols.PESymbols, version=(2, 0, 0)
|
||||
),
|
||||
]
|
||||
|
||||
def _gather_code_bytes(
|
||||
self,
|
||||
kernel: interfaces.context.ModuleInterface,
|
||||
kernel_module_name: str,
|
||||
found_symbols: pe_symbols.found_symbols_type,
|
||||
) -> _code_bytes_type:
|
||||
"""
|
||||
@@ -115,11 +116,7 @@ class unhooked_system_calls(interfaces.plugins.PluginInterface):
|
||||
"""
|
||||
code_bytes: unhooked_system_calls._code_bytes_type = {}
|
||||
|
||||
procs = pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
)
|
||||
procs = pslist.PsList.list_processes(self.context, kernel_module_name)
|
||||
|
||||
for proc in procs:
|
||||
try:
|
||||
@@ -153,18 +150,15 @@ class unhooked_system_calls(interfaces.plugins.PluginInterface):
|
||||
return code_bytes
|
||||
|
||||
def _generator(self) -> Generator[Tuple[int, Tuple[str, str, int]], None, None]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
found_symbols = pe_symbols.PESymbols.addresses_for_process_symbols(
|
||||
self.context,
|
||||
self.config_path,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
unhooked_system_calls.system_calls,
|
||||
context=self.context,
|
||||
config_path=self.config_path,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
symbols=unhooked_system_calls.system_calls,
|
||||
)
|
||||
|
||||
# code_bytes[dll_name][func_name][func_bytes]
|
||||
code_bytes = self._gather_code_bytes(kernel, found_symbols)
|
||||
code_bytes = self._gather_code_bytes(self.config["kernel"], found_symbols)
|
||||
|
||||
# walk the functions that were evaluated
|
||||
for functions in code_bytes.values():
|
||||
|
||||
@@ -52,7 +52,9 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
|
||||
The name of the constructed unloaded modules table
|
||||
"""
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table
|
||||
)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
|
||||
if is_64bit:
|
||||
@@ -100,7 +102,9 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
|
||||
offset=unloadedmodules_offset,
|
||||
subtype="array",
|
||||
)
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table
|
||||
)
|
||||
|
||||
if is_64bit:
|
||||
unloaded_count_type = "unsigned long long"
|
||||
|
||||
@@ -64,7 +64,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
optional=True,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="dump",
|
||||
@@ -273,8 +273,6 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
return renderers.TreeGrid(
|
||||
@@ -295,8 +293,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -33,7 +33,7 @@ class VadRegExScan(plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
@@ -111,11 +111,9 @@ class VadRegExScan(plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
procs = pslist.PsList.list_processes(
|
||||
self.context,
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
context=self.context,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
return renderers.TreeGrid(
|
||||
|
||||
@@ -29,7 +29,7 @@ class VadWalk(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="vadinfo", plugin=vadinfo.VadInfo, version=(2, 0, 0)
|
||||
@@ -67,7 +67,6 @@ class VadWalk(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
|
||||
return renderers.TreeGrid(
|
||||
@@ -85,8 +84,7 @@ class VadWalk(interfaces.plugins.PluginInterface):
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
)
|
||||
),
|
||||
|
||||
@@ -30,7 +30,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0)
|
||||
@@ -53,8 +53,6 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
return yarascan_requirements + vadyarascan_requirements
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
rules = yarascan.YaraScan.process_yara_options(dict(self.config))
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
|
||||
@@ -63,8 +61,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
|
||||
for task in pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
filter_func=filter_func,
|
||||
):
|
||||
layer_name = task.add_process_layer()
|
||||
|
||||
@@ -43,10 +43,10 @@ class VerInfo(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="modules", plugin=modules.Modules, version=(2, 0, 0)
|
||||
name="modules", plugin=modules.Modules, version=(3, 0, 0)
|
||||
),
|
||||
requirements.BooleanRequirement(
|
||||
name="extensive",
|
||||
@@ -253,19 +253,15 @@ class VerInfo(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
procs = pslist.PsList.list_processes(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
|
||||
mods = modules.Modules.list_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
mods = modules.Modules.list_modules(self.context, self.config["kernel"])
|
||||
|
||||
# populate the session layers for kernel modules
|
||||
session_layers = modules.Modules.get_session_layers(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
context=self.context, kernel_module_name=self.config["kernel"]
|
||||
)
|
||||
|
||||
return renderers.TreeGrid(
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
from typing import List, Tuple, Iterator, Generator, Dict
|
||||
|
||||
from volatility3.framework import interfaces, renderers, symbols, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
from volatility3.framework.symbols.windows.extensions import gui
|
||||
from volatility3.plugins.windows import poolscanner, modules
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WindowStations(interfaces.plugins.PluginInterface):
|
||||
"""Scans for top level Windows Stations"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
# These checks must be completed from newest -> oldest OS version.
|
||||
_win_version_file_map: List[Tuple[versions.OsDistinguisher, str]] = [
|
||||
(versions.is_win10_19577_or_later, "gui-win10-19577-x64"),
|
||||
(versions.is_win10_19041_or_later, "gui-win10-19041-x64"),
|
||||
(versions.is_win10_18362_or_later, "gui-win10-18362-x64"),
|
||||
(versions.is_win10_17763_or_later, "gui-win10-17763-x64"),
|
||||
(versions.is_win10_17134_or_later, "gui-win10-17134-x64"),
|
||||
(versions.is_win10_16299_or_later, "gui-win10-16299-x64"),
|
||||
(versions.is_win10_15063_or_later, "gui-win10-15063-x64"),
|
||||
(versions.is_win10_10586_or_later, "gui-win10-10586-x64"),
|
||||
(versions.is_windows_8_or_later, "gui-win8-x64"),
|
||||
(versions.is_windows_7_sp1, "gui-win7sp1-x64"),
|
||||
(versions.is_windows_7_sp0, "gui-win7sp0-x64"),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
# Since we're calling the plugin, make sure we have the plugin's requirements
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_gui_table(
|
||||
context: interfaces.context.ContextInterface,
|
||||
symbol_table: str,
|
||||
config_path: str,
|
||||
) -> str:
|
||||
"""Creates a symbol table for windows GUI types
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
symbol_table: The name of an existing symbol table containing the kernel symbols
|
||||
config_path: The configuration path within the context of the symbol table to create
|
||||
|
||||
Returns:
|
||||
The name of the constructed GUI table
|
||||
"""
|
||||
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context=context, symbol_table_name=symbol_table
|
||||
):
|
||||
raise NotImplementedError(
|
||||
"This plugin only supports x64 versions of Windows"
|
||||
)
|
||||
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
|
||||
try:
|
||||
symbol_filename = next(
|
||||
filename
|
||||
for version_check, filename in WindowStations._win_version_file_map
|
||||
if version_check(context=context, symbol_table=symbol_table)
|
||||
)
|
||||
except StopIteration:
|
||||
raise NotImplementedError("This version of Windows is not supported!")
|
||||
|
||||
vollog.debug(f"Using GUI table {symbol_filename}")
|
||||
|
||||
return intermed.IntermediateSymbolTable.create(
|
||||
context=context,
|
||||
config_path=config_path,
|
||||
sub_path=os.path.join("windows", "gui"),
|
||||
filename=symbol_filename,
|
||||
class_types=gui.class_types,
|
||||
table_mapping=table_mapping,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_session_map(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
module_name: str,
|
||||
gui_table_name: str,
|
||||
) -> Dict[int, interfaces.context.ModuleInterface]:
|
||||
"""
|
||||
Walks each session layer and returns a dictionary that
|
||||
maps session identifiers to a module in the session's layer
|
||||
"""
|
||||
session_map = modules.Modules.get_session_layers_map(context, module_name)
|
||||
|
||||
for session_id, session_layer in session_map.items():
|
||||
session_module = context.module(
|
||||
gui_table_name, layer_name=session_layer, offset=0
|
||||
)
|
||||
session_map[session_id] = session_module
|
||||
|
||||
return session_map
|
||||
|
||||
@classmethod
|
||||
def scan_gui_object(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_module_name: str,
|
||||
object_tag: bytes,
|
||||
object_type: str,
|
||||
) -> Generator[interfaces.objects.ObjectInterface, None, None]:
|
||||
"""
|
||||
An API that generically scans for GUI (win32*.sys) objects allocated in the pools (which is nearly all of them)
|
||||
|
||||
This function scans within the kernel space for the tags and then uses `get_session_map` to instantiate objects
|
||||
in their correct session address space.
|
||||
|
||||
Args:
|
||||
context:
|
||||
config_path:
|
||||
kernel_module_name:
|
||||
object_tag: The 4 byte pool header tag to search for
|
||||
object_type: The data structure of the GUI object within the pool
|
||||
"""
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
gui_table_name = cls.create_gui_table(
|
||||
context, kernel.symbol_table_name, config_path
|
||||
)
|
||||
|
||||
constraints = poolscanner.PoolScanner.gui_poolscanner_constraints(
|
||||
gui_table_name, [object_tag]
|
||||
)
|
||||
|
||||
session_map = cls.get_session_map(context, kernel_module_name, gui_table_name)
|
||||
|
||||
for result in poolscanner.PoolScanner.generate_pool_scan_extended(
|
||||
context=context,
|
||||
kernel_layer_name=kernel.layer_name,
|
||||
kernel_symbol_table_name=kernel.symbol_table_name,
|
||||
object_symbol_table_name=gui_table_name,
|
||||
constraints=constraints,
|
||||
):
|
||||
_constraint, mem_object, _header = result
|
||||
|
||||
# enforce that objects are in a valid session
|
||||
# this prevents smear and also ensures future pointer
|
||||
# dereferences are performed in the correct address space (layer)
|
||||
try:
|
||||
session_id = mem_object.get_session_id()
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if session_id is not None:
|
||||
session_module = session_map.get(session_id, None)
|
||||
if session_module:
|
||||
# create the object its own address space (per-session)
|
||||
yield session_module.object(
|
||||
object_type=object_type, offset=mem_object.vol.offset
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def scan_window_stations(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
config_path: str,
|
||||
kernel_module_name: str,
|
||||
) -> Iterator[Tuple["gui.tagWINDOWSTATION", str, int]]:
|
||||
"""
|
||||
Scans for window stations through `scan_gui_object`
|
||||
Yields each window station along with its name and session_id
|
||||
"""
|
||||
|
||||
seen = set()
|
||||
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
for scanned_winsta in cls.scan_gui_object(
|
||||
context, config_path, kernel_module_name, b"Wind", "tagWINDOWSTATION"
|
||||
):
|
||||
# walk the list of each station found through scanning
|
||||
for winsta in scanned_winsta.traverse():
|
||||
if winsta.vol.offset in seen:
|
||||
continue
|
||||
seen.add(winsta.vol.offset)
|
||||
|
||||
# stations need to have a name and be in a session
|
||||
name, session_id = winsta.get_info(kernel.symbol_table_name)
|
||||
if name and session_id is not None:
|
||||
yield winsta, name, session_id
|
||||
|
||||
def _generator(self):
|
||||
"""
|
||||
A wrapper around `scan_window_stations`
|
||||
"""
|
||||
for winsta, name, session_id in self.scan_window_stations(
|
||||
self.context, self.config_path, self.config["kernel"]
|
||||
):
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
format_hints.Hex(winsta.vol.offset),
|
||||
name,
|
||||
session_id,
|
||||
),
|
||||
)
|
||||
|
||||
# Volatility 2 reported whether the station is interactive or not, but I could not determine if its algorithm
|
||||
# is currently valid. I also did not see where the old code paths still checked the same bit mask
|
||||
def run(self):
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("Name", str),
|
||||
("SessionId", int),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -237,7 +237,9 @@ class module(generic.GenericIntelProcess):
|
||||
elf_table_name = self.get_elf_table_name()
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
|
||||
is_64bit = symbols.symbol_table_is_64bit(self._context, symbol_table_name)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
)
|
||||
sym_name = "Elf64_Sym" if is_64bit else "Elf32_Sym"
|
||||
sym_type = self._context.symbol_space.get_type(
|
||||
elf_table_name + constants.BANG + sym_name
|
||||
@@ -280,7 +282,9 @@ class module(generic.GenericIntelProcess):
|
||||
elf_table_name = self.get_elf_table_name()
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
|
||||
is_64bit = symbols.symbol_table_is_64bit(self._context, symbol_table_name)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
)
|
||||
sym_name = "Elf64_Sym" if is_64bit else "Elf32_Sym"
|
||||
sym_type = self._context.symbol_space.get_type(
|
||||
elf_table_name + constants.BANG + sym_name
|
||||
|
||||
@@ -53,7 +53,9 @@ class MMVAD_SHORT(objects.StructType):
|
||||
|
||||
# the offset is different on 32 and 64 bits
|
||||
symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
if not symbols.symbol_table_is_64bit(self._context, symbol_table_name):
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
):
|
||||
vad_address -= 4
|
||||
else:
|
||||
vad_address -= 12
|
||||
@@ -389,7 +391,9 @@ class EX_FAST_REF(objects.StructType):
|
||||
|
||||
# the mask value is different on 32 and 64 bits
|
||||
symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
if not symbols.symbol_table_is_64bit(self._context, symbol_table_name):
|
||||
if not symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
):
|
||||
max_fast_ref = 7
|
||||
else:
|
||||
max_fast_ref = 15
|
||||
@@ -874,6 +878,12 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
|
||||
|
||||
for peb in pebs:
|
||||
sym_table = self.get_symbol_table_name()
|
||||
# Fixes #1636
|
||||
try:
|
||||
peb.Ldr
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if peb.Ldr.vol.type_name.split(constants.BANG)[-1] == ("unsigned long"):
|
||||
sym_table = self.set_types(peb)
|
||||
|
||||
@@ -1400,7 +1410,9 @@ class CONTROL_AREA(objects.StructType):
|
||||
)
|
||||
mmpte_size = mmpte_type.size
|
||||
subsection = self.get_subsection()
|
||||
is_64bit = symbols.symbol_table_is_64bit(self._context, symbol_table_name)
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
)
|
||||
is_pae = self._context.layers[self.vol.layer_name].metadata.get("pae", False)
|
||||
|
||||
# the sector_size is used as a multiplier to the StartingSector
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# This file is Copyright 2025 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
from typing import Optional, Tuple, Iterator
|
||||
|
||||
from volatility3.framework import exceptions, constants, interfaces
|
||||
from volatility3.framework import objects
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols.windows.extensions import pool
|
||||
|
||||
|
||||
class tagWINDOWSTATION(objects.StructType, pool.ExecutiveObject):
|
||||
def is_valid(self) -> bool:
|
||||
sid = self.get_session_id()
|
||||
return sid is not None and 0 <= sid < 256
|
||||
|
||||
def get_session_id(self) -> Optional[int]:
|
||||
try:
|
||||
return self.dwSessionId
|
||||
except exceptions.InvalidAddressException:
|
||||
return None
|
||||
|
||||
def traverse(self, max_stations: int = 15):
|
||||
"""
|
||||
Traverses the window stations referenced in the list of stations
|
||||
"""
|
||||
seen = set()
|
||||
|
||||
# include the first window station
|
||||
yield self
|
||||
|
||||
while len(seen) < max_stations:
|
||||
try:
|
||||
winsta = self.rpwinstaNext.dereference()
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
if winsta.vol.offset in seen:
|
||||
break
|
||||
|
||||
yield winsta
|
||||
|
||||
seen.add(winsta.vol.offset)
|
||||
|
||||
def get_info(self, kernel_symbol_table_name) -> Optional[Tuple[str, int]]:
|
||||
try:
|
||||
name = self.get_name(kernel_symbol_table_name)
|
||||
session_id = self.get_session_id()
|
||||
except exceptions.InvalidAddressException:
|
||||
return None, None
|
||||
|
||||
# attempt to avoid smear
|
||||
if session_id is not None and session_id < 256 and name and len(name) > 1:
|
||||
return name, session_id
|
||||
|
||||
return None, None
|
||||
|
||||
def desktops(self, symbol_table_name, max_desktops: int = 12):
|
||||
seen = set()
|
||||
|
||||
while len(seen) < max_desktops:
|
||||
try:
|
||||
desktop = self.rpdeskList.dereference()
|
||||
name = desktop.get_name(symbol_table_name)
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
if desktop.vol.offset in seen:
|
||||
break
|
||||
|
||||
yield desktop, name
|
||||
|
||||
seen.add(desktop.vol.offset)
|
||||
|
||||
|
||||
class tagDESKTOP(objects.StructType, pool.ExecutiveObject):
|
||||
def is_valid(self) -> bool:
|
||||
"""
|
||||
Enforce a valid sid + owning window station
|
||||
"""
|
||||
sid = self.get_session_id()
|
||||
|
||||
valid_sid = sid is not None and 0 <= sid < 256
|
||||
|
||||
if valid_sid:
|
||||
return self.get_window_station() is not None
|
||||
|
||||
return False
|
||||
|
||||
def get_window_station(self) -> Optional["tagWINDOWSTATION"]:
|
||||
try:
|
||||
return self.rpwinstaParent.dereference()
|
||||
except exceptions.InvalidAddressException:
|
||||
return None
|
||||
|
||||
def get_session_id(self) -> Optional[int]:
|
||||
winsta = self.get_window_station()
|
||||
if winsta:
|
||||
return winsta.get_session_id()
|
||||
|
||||
return None
|
||||
|
||||
def get_threads(
|
||||
self,
|
||||
) -> Iterator[Tuple[interfaces.objects.ObjectInterface, str, int]]:
|
||||
"""
|
||||
Returns the threads of each desktop along with owning process information
|
||||
"""
|
||||
symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
|
||||
for thread in self.PtiList.to_list(
|
||||
symbol_table_name + constants.BANG + "tagTHREADINFO", "PtiLink"
|
||||
):
|
||||
try:
|
||||
process_name = utility.array_to_string(thread.ppi.Process.ImageFileName)
|
||||
process_pid = thread.ppi.Process.UniqueProcessId
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
yield thread, process_name, process_pid
|
||||
|
||||
|
||||
class_types = {
|
||||
"tagWINDOWSTATION": tagWINDOWSTATION,
|
||||
"tagDESKTOP": tagDESKTOP,
|
||||
}
|
||||
@@ -78,7 +78,9 @@ class POOL_HEADER(objects.StructType):
|
||||
|
||||
# otherwise we have an executive object in the pool
|
||||
else:
|
||||
if symbols.symbol_table_is_64bit(self._context, symbol_table_name):
|
||||
if symbols.symbol_table_is_64bit(
|
||||
context=self._context, symbol_table_name=symbol_table_name
|
||||
):
|
||||
alignment = 16
|
||||
else:
|
||||
alignment = 8
|
||||
@@ -95,7 +97,7 @@ class POOL_HEADER(objects.StructType):
|
||||
optional_headers,
|
||||
lengths_of_optional_headers,
|
||||
) = self._calculate_optional_header_lengths(
|
||||
self._context, symbol_table_name
|
||||
self._context, kernel_symbol_table
|
||||
)
|
||||
padding_available = (
|
||||
None
|
||||
@@ -326,12 +328,18 @@ class ExecutiveObject(interfaces.objects.ObjectInterface):
|
||||
"""This is used as a "mixin" that provides all kernel executive objects
|
||||
with a means of finding their own object header."""
|
||||
|
||||
def get_object_header(self) -> "OBJECT_HEADER":
|
||||
def get_object_header(
|
||||
self, symbol_table_name: Optional[str] = None
|
||||
) -> "OBJECT_HEADER":
|
||||
if constants.BANG not in self.vol.type_name:
|
||||
raise ValueError(
|
||||
f"Invalid symbol table name syntax (no {constants.BANG} found)"
|
||||
)
|
||||
symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
|
||||
# caller provided symbol table allows for scanning for objects from any module
|
||||
if not symbol_table_name:
|
||||
symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
|
||||
body_offset = self._context.symbol_space.get_type(
|
||||
symbol_table_name + constants.BANG + "_OBJECT_HEADER"
|
||||
).relative_child_offset("Body")
|
||||
@@ -342,6 +350,12 @@ class ExecutiveObject(interfaces.objects.ObjectInterface):
|
||||
native_layer_name=self.vol.native_layer_name,
|
||||
)
|
||||
|
||||
def get_name(self, symbol_table_name: Optional[str] = None) -> Optional[str]:
|
||||
try:
|
||||
return self.get_object_header(symbol_table_name).get_name()
|
||||
except exceptions.InvalidAddressException:
|
||||
return None
|
||||
|
||||
|
||||
class OBJECT_HEADER(objects.StructType):
|
||||
"""A class for the headers for executive kernel objects, which contains
|
||||
@@ -450,3 +464,22 @@ class OBJECT_HEADER(objects.StructType):
|
||||
absolute=True,
|
||||
)
|
||||
return header
|
||||
|
||||
def get_name(self) -> Optional[str]:
|
||||
"""
|
||||
Attempts to get the name of the object
|
||||
Sanity checks size members to avoid FPs
|
||||
Returns None if any issues detected
|
||||
"""
|
||||
try:
|
||||
name_info = self.NameInfo.Name
|
||||
if (
|
||||
name_info.Length == 0
|
||||
or name_info.MaximumLength == 0
|
||||
or name_info.Length > name_info.MaximumLength
|
||||
):
|
||||
return None
|
||||
|
||||
return name_info.String
|
||||
except (ValueError, exceptions.InvalidAddressException):
|
||||
return None
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import logging
|
||||
from typing import Callable, Tuple, List, Optional
|
||||
from typing import Callable, List, Optional, Tuple
|
||||
|
||||
from volatility3.framework import interfaces, constants, exceptions
|
||||
from volatility3.framework import constants, exceptions, interfaces
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -88,24 +88,6 @@ class OsDistinguisher:
|
||||
return True
|
||||
|
||||
|
||||
is_windows_8_1_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (6, 3),
|
||||
fallback_checks=[("_KPRCB", "PendingTickFlags", True)],
|
||||
)
|
||||
|
||||
is_vista_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (6, 0),
|
||||
fallback_checks=[("KdCopyDataBlock", None, True)],
|
||||
)
|
||||
|
||||
is_win10 = OsDistinguisher(
|
||||
version_check=lambda x: (10, 0) <= x,
|
||||
fallback_checks=[
|
||||
("ObHeaderCookie", None, True),
|
||||
("_HANDLE_TABLE", "HandleCount", False),
|
||||
],
|
||||
)
|
||||
|
||||
is_windows_xp = OsDistinguisher(
|
||||
version_check=lambda x: (5, 1) <= x < (5, 2),
|
||||
fallback_checks=[
|
||||
@@ -149,6 +131,32 @@ is_2003 = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_vista_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (6, 0),
|
||||
fallback_checks=[("KdCopyDataBlock", None, True)],
|
||||
)
|
||||
|
||||
is_windows_8_1_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (6, 3),
|
||||
fallback_checks=[("_KPRCB", "PendingTickFlags", True)],
|
||||
)
|
||||
|
||||
is_win10 = OsDistinguisher(
|
||||
version_check=lambda x: (10, 0) <= x,
|
||||
fallback_checks=[
|
||||
("ObHeaderCookie", None, True),
|
||||
("_HANDLE_TABLE", "HandleCount", False),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_10586_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 10586),
|
||||
fallback_checks=[
|
||||
("_UNLOADED_DRIVERS", None, False),
|
||||
("ObHeaderCookie", None, True),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_up_to_15063 = OsDistinguisher(
|
||||
version_check=lambda x: (10, 0) <= x < (10, 0, 15063),
|
||||
fallback_checks=[
|
||||
@@ -187,6 +195,22 @@ is_win10_16299_or_later = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_17134_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 17134),
|
||||
fallback_checks=[
|
||||
("_EPROCESS", "ProcessFirstResume", True),
|
||||
("_EPROCESS", "HighMemoryPriority", True),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_17735_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 17735),
|
||||
fallback_checks=[
|
||||
("_EPROCESS", "VmProcessorHost", True),
|
||||
("_EPROCESS", "VdmObjects", False),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_17763_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 17763),
|
||||
fallback_checks=[
|
||||
@@ -218,6 +242,14 @@ is_win10_19041_or_later = OsDistinguisher(
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_19577_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 19577),
|
||||
fallback_checks=[
|
||||
("_EPROCESS", "PaeTop", False),
|
||||
("_EPROCESS", "IdealProcessorAssignmentBlock", True),
|
||||
],
|
||||
)
|
||||
|
||||
is_win10_25398_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (10, 0, 25398),
|
||||
fallback_checks=[
|
||||
@@ -235,6 +267,31 @@ is_windows_8_or_later = OsDistinguisher(
|
||||
version_check=lambda x: x >= (6, 2),
|
||||
fallback_checks=[("_HANDLE_TABLE", "HandleCount", False)],
|
||||
)
|
||||
|
||||
is_windows_7_sp0 = OsDistinguisher(
|
||||
version_check=lambda x: x == (6, 1, 7600),
|
||||
fallback_checks=[
|
||||
("_EPROCESS", "VdmObjects", True),
|
||||
("_EPROCESS", "UmsScheduledThreads", False),
|
||||
# Dropped after vista
|
||||
("_EPROCESS", "QuotaUsage", False),
|
||||
# Added win8
|
||||
("_EPROCESS", "WnfContext", False),
|
||||
],
|
||||
)
|
||||
|
||||
is_windows_7_sp1 = OsDistinguisher(
|
||||
version_check=lambda x: x == (6, 1, 7601),
|
||||
fallback_checks=[
|
||||
("_EPROCESS", "VdmObjects", False),
|
||||
("_EPROCESS", "UmsScheduledThreads", True),
|
||||
# Dropped after vista
|
||||
("_EPROCESS", "QuotaUsage", False),
|
||||
# Added win8
|
||||
("_EPROCESS", "WnfContext", False),
|
||||
],
|
||||
)
|
||||
|
||||
# Technically, this is win7 or less
|
||||
is_windows_7 = OsDistinguisher(
|
||||
version_check=lambda x: x == (6, 1),
|
||||
|
||||
@@ -25,7 +25,7 @@ class Certificates(interfaces.plugins.PluginInterface):
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
|
||||
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="printkey", plugin=printkey.PrintKey, version=(1, 0, 0)
|
||||
@@ -69,13 +69,10 @@ class Certificates(interfaces.plugins.PluginInterface):
|
||||
return None
|
||||
|
||||
def _generator(self) -> Iterator[Tuple[int, Tuple[str, str, str, str]]]:
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for hive in hivelist.HiveList.list_hives(
|
||||
self.context,
|
||||
context=self.context,
|
||||
base_config_path=self.config_path,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
kernel_module_name=self.config["kernel"],
|
||||
):
|
||||
for top_key in [
|
||||
"Microsoft\\SystemCertificates",
|
||||
|
||||
Reference in New Issue
Block a user