Skip to content

Commit

Permalink
Merge pull request #1640 from volatilityfoundation/plugin/windows_gui
Browse files Browse the repository at this point in the history
Add APIs and initial plugins for GUI support and fit Windows major APIs to current design flow
  • Loading branch information
ikelos authored Mar 6, 2025
2 parents caa52c0 + 7e00f2c commit e935254
Show file tree
Hide file tree
Showing 96 changed files with 226,824 additions and 641 deletions.
8 changes: 3 additions & 5 deletions doc/source/simple-plugin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand All @@ -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
)
)
Expand All @@ -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
Expand Down
6 changes: 2 additions & 4 deletions volatility3/cli/volshell/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def get_requirements(cls):
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
Expand All @@ -39,9 +39,7 @@ def list_processes(self):
"""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):
Expand Down
2 changes: 1 addition & 1 deletion volatility3/framework/constants/_version.py
Original file line number Diff line number Diff line change
@@ -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 = ""

Expand Down
2 changes: 1 addition & 1 deletion volatility3/framework/layers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __init__(
# 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"
Expand Down
2 changes: 1 addition & 1 deletion volatility3/framework/plugins/linux/bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
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"
Expand Down
2 changes: 1 addition & 1 deletion volatility3/framework/plugins/linux/malfind.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def _generator(self, tasks):
# 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:
Expand Down
4 changes: 3 additions & 1 deletion volatility3/framework/plugins/linux/psscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ def scan_tasks(
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:
Expand Down
2 changes: 1 addition & 1 deletion volatility3/framework/plugins/mac/bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def get_requirements(cls):
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"
Expand Down
17 changes: 9 additions & 8 deletions volatility3/framework/plugins/windows/amcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand All @@ -230,7 +232,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="hivelist", plugin=hivelist.HiveList, version=(1, 0, 0)
name="hivelist", plugin=hivelist.HiveList, version=(2, 0, 0)
),
]

Expand All @@ -252,7 +254,7 @@ def get_amcache_hive(
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(
Expand All @@ -261,8 +263,7 @@ def get_amcache_hive(
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,
Expand Down Expand Up @@ -523,8 +524,6 @@ def parse_driver_binary_key(
)

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]]:
Expand All @@ -533,7 +532,9 @@ def indented(

# 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

Expand Down
10 changes: 4 additions & 6 deletions volatility3/framework/plugins/windows/cachedump.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_requirements(cls):
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)
Expand Down Expand Up @@ -169,13 +169,11 @@ 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":
Expand Down
9 changes: 6 additions & 3 deletions volatility3/framework/plugins/windows/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
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)
Expand Down Expand Up @@ -187,7 +187,9 @@ def create_callback_symbol_table(
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:
Expand Down Expand Up @@ -691,7 +693,8 @@ def _generator(self):
)

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 = (
Expand Down
15 changes: 7 additions & 8 deletions volatility3/framework/plugins/windows/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,7 +28,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
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",
Expand All @@ -41,7 +41,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
@classmethod
def get_cmdline(
cls, context: interfaces.context.ContextInterface, kernel_table_name: str, proc
):
) -> Optional[str]:
"""Extracts the cmdline from PEB
Args:
Expand All @@ -54,15 +54,16 @@ def get_cmdline(
"""

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"]]
Expand Down Expand Up @@ -99,16 +100,14 @@ def _generator(self, procs):
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(
[("PID", int), ("Process", str), ("Args", 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,
)
),
Expand Down
20 changes: 8 additions & 12 deletions volatility3/framework/plugins/windows/cmdscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ def get_requirements(cls):
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",
Expand Down Expand Up @@ -286,12 +286,11 @@ def _generator(

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}")
Expand Down Expand Up @@ -360,8 +359,6 @@ def _conhost_proc_filter(self, proc: interfaces.objects.ObjectInterface):
return process_name != "conhost.exe"

def run(self):
kernel = self.context.modules[self.config["kernel"]]

return renderers.TreeGrid(
[
("PID", int),
Expand All @@ -374,8 +371,7 @@ def run(self):
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,
)
),
Expand Down
Loading

0 comments on commit e935254

Please sign in to comment.