From 338fcdd0ab7f59491017ed63d8e780e744c0fe88 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Wed, 2 Feb 2022 15:48:41 +0000 Subject: [PATCH 1/3] 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()) + From b43d61ca036926047a13343eb401ad920cd5e62b Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Thu, 28 Apr 2022 15:42:10 +0000 Subject: [PATCH 2/3] Address feedback from ikelos --- volatility3/framework/plugins/linux/psaux.py | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py index 31777f458..089bb61c7 100644 --- a/volatility3/framework/plugins/linux/psaux.py +++ b/volatility3/framework/plugins/linux/psaux.py @@ -4,6 +4,7 @@ from typing import Optional +from volatility3.framework.configuration import requirements from volatility3.framework import symbols, exceptions, renderers, interfaces from volatility3.framework.objects import utility from volatility3.plugins.linux import pslist @@ -11,6 +12,19 @@ from volatility3.plugins.linux import pslist class PsAux(pslist.PsList): """ Lists processes with their command line arguments """ + @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]: """ @@ -41,7 +55,7 @@ class PsAux(pslist.PsList): # 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: + if not (0 < size_to_read <= 4096): return renderers.UnreadableValue() # attempt to read it all as partial values are invalid and misleading @@ -65,13 +79,11 @@ class PsAux(pslist.PsList): return args - def _generator(self): + def _generator(self, tasks): """ 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): + for task in tasks: pid = task.pid try: @@ -86,5 +98,11 @@ class PsAux(pslist.PsList): yield (0, (pid, ppid, name, args)) def run(self): - return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], self._generator()) + 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))) From 3175e25420095f237fcc987c1efd970b8cfc3305 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Thu, 28 Apr 2022 16:11:51 +0000 Subject: [PATCH 3/3] Remove the inheritance from pslist --- volatility3/framework/plugins/linux/psaux.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py index 089bb61c7..c62712907 100644 --- a/volatility3/framework/plugins/linux/psaux.py +++ b/volatility3/framework/plugins/linux/psaux.py @@ -8,10 +8,13 @@ from volatility3.framework.configuration import requirements from volatility3.framework import symbols, exceptions, renderers, interfaces from volatility3.framework.objects import utility from volatility3.plugins.linux import pslist +from volatility3.framework.interfaces import plugins -class PsAux(pslist.PsList): +class PsAux(plugins.PluginInterface): """ Lists processes with their command line arguments """ + _required_framework_version = (2, 0, 0) + @classmethod def get_requirements(cls): # Since we're calling the plugin, make sure we have the plugin's requirements