From 5cbc07887c2a26fd58b3866fb76adfa72019ab7b Mon Sep 17 00:00:00 2001 From: David McDonald Date: Thu, 27 Feb 2025 14:33:29 -0600 Subject: [PATCH] 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 --- doc/source/simple-plugin.rst | 8 +++--- .../framework/plugins/windows/pslist.py | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/doc/source/simple-plugin.rst b/doc/source/simple-plugin.rst index 07d9e1467..a6916a027 100644 --- a/doc/source/simple-plugin.rst +++ b/doc/source/simple-plugin.rst @@ -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 diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 3d3f12869..7909945a1 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -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):