From 338fcdd0ab7f59491017ed63d8e780e744c0fe88 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Wed, 2 Feb 2022 15:48:41 +0000 Subject: [PATCH] Add the psaux plugin for Linux command line argument listing --- volatility3/framework/plugins/linux/psaux.py | 90 ++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 volatility3/framework/plugins/linux/psaux.py diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py new file mode 100644 index 000000000..31777f458 --- /dev/null +++ b/volatility3/framework/plugins/linux/psaux.py @@ -0,0 +1,90 @@ +# 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 symbols, exceptions, renderers, interfaces +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +class PsAux(pslist.PsList): + """ Lists processes with their command line arguments """ + + 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 theads 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 size_to_read < 1 or 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): + """ Generates a listing of processes along with command line arguments """ + + vmlinux = self.context.modules[self.config['kernel']] + + # walk the process list and report the arguments + for task in self.list_tasks(self.context, vmlinux.name): + 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)) + + def run(self): + return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], self._generator()) +