Source code for volatility3.plugins.linux.psaux

# This file is Copyright 2022 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

from volatility3.framework import exceptions, interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.framework.objects import utility
from volatility3.plugins.linux import pslist


[docs]class PsAux(plugins.PluginInterface): """Lists processes with their command line arguments""" _required_framework_version = (2, 0, 0)
[docs] @classmethod def get_requirements(cls): # Since we're calling the plugin, make sure we have the plugin's requirements return [ requirements.ModuleRequirement( name="kernel", description="Linux kernel", architectures=["Intel32", "Intel64"], ), requirements.PluginRequirement( name="pslist", plugin=pslist.PsList, version=(2, 0, 0) ), requirements.ListRequirement( name="pid", description="Filter on specific process IDs", element_type=int, optional=True, ), ]
def _get_command_line_args( self, task: interfaces.objects.ObjectInterface, name: str ) -> Optional[str]: """ Reads the command line arguments of a process These are stored on the userland stack Kernel threads re-use the process data structure, but do not have a valid 'mm' pointer Parameters: task: task_struct object of the process name: string name of the process (from task.comm) """ # kernel threads never have an mm as they do not have userland mappings try: mm = task.mm except exceptions.InvalidAddressException: mm = None if mm: proc_layer_name = task.add_process_layer() if proc_layer_name is None: return renderers.UnreadableValue() proc_layer = self.context.layers[proc_layer_name] # read argv from userland start = task.mm.arg_start # get the size of the arguments with sanity checking size_to_read = task.mm.arg_end - task.mm.arg_start if not (0 < size_to_read <= 4096): return renderers.UnreadableValue() # attempt to read it all as partial values are invalid and misleading try: argv = proc_layer.read(start, size_to_read) except exceptions.InvalidAddressException: return renderers.UnreadableValue() # the arguments are null byte terminated, replace the nulls with spaces s = argv.decode().split("\x00") args = " ".join(s) else: # kernel thread # [ ] mimics ps on a live system # also helps identify malware masquerading as a kernel thread, which is fairly common args = "[" + name + "]" # remove trailing space, if present if len(args) > 1 and args[-1] == " ": args = args[:-1] return args def _generator(self, tasks): """Generates a listing of processes along with command line arguments""" # walk the process list and report the arguments for task in tasks: pid = task.pid try: ppid = task.parent.pid except exceptions.InvalidAddressException: ppid = 0 name = utility.array_to_string(task.comm) args = self._get_command_line_args(task, name) yield (0, (pid, ppid, name, args))
[docs] def run(self): filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) return renderers.TreeGrid( [("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], self._generator( pslist.PsList.list_tasks( self.context, self.config["kernel"], filter_func=filter_func ) ), )