Windows PsList: Add method for listing procs from kernel

This adds a new classmethod, `list_processes_from_kernel`, updates the
`list_processes` method signature to use only the kernel module name and
the context instead of splitting information about the kernel between
the layer_name and symbol_table_name paramters, and does a major version
number increase on the plugin.

Also updates the documentation to reflect pslist method signature
change.

Co-authored-by: Andrew Case <andrew@dfir.org>
This commit is contained in:
David McDonald
2025-03-05 17:59:38 -06:00
co-authored by Andrew Case
parent 59919dc1da
commit 5cbc07887c
2 changed files with 19 additions and 15 deletions
+3 -5
View File
@@ -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
+16 -10
View File
@@ -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,32 +208,37 @@ 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
"""
kernel = context.modules[kernel_module_name]
# We only use the object factory to demonstrate how to use one
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
kvo = context.layers[kernel.layer_name].config.get(
"kernel_virtual_offset", None
)
if not kvo:
raise ValueError(
"Intel layer does not have an associated kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
ntkrnlmp = context.module(
kernel.symbol_table_name, layer_name=kernel.layer_name, offset=kvo
)
ps_aph_offset = ntkrnlmp.get_symbol("PsActiveProcessHead").address
list_entry = ntkrnlmp.object(object_type="_LIST_ENTRY", offset=ps_aph_offset)
@@ -273,8 +280,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):