From 7673dd8d2d7e6b44672b071c2a3c3ec42375220e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 29 Jun 2020 22:32:45 +0100 Subject: [PATCH] Mac: Consolidate methods of listing processes This also updates all other plugins that rely on process listing and theoretically allows them to choose their preferred method of process listing. At the moment, the default (first in the method list) is chosen. An optional pslist_method StringRequirement can be added to each plugin, but using the list in the requirements could break if the pslist plugin is too old (ie, using the list would happen before the PluginRequirement gets checked). If this is a feature we want, it should be easy to add to all but netstat, which does not parameterize the list of processes. --- volatility/framework/plugins/mac/bash.py | 26 ++- volatility/framework/plugins/mac/lsof.py | 15 +- volatility/framework/plugins/mac/malfind.py | 15 +- volatility/framework/plugins/mac/netstat.py | 10 +- volatility/framework/plugins/mac/proc_maps.py | 15 +- volatility/framework/plugins/mac/psaux.py | 15 +- volatility/framework/plugins/mac/pslist.py | 173 ++++++++++++++++-- .../plugins/mac/pslist_process_groups.py | 89 --------- volatility/framework/plugins/mac/pstree.py | 10 +- volatility/framework/plugins/mac/tasks.py | 56 ------ 10 files changed, 214 insertions(+), 210 deletions(-) delete mode 100644 volatility/framework/plugins/mac/pslist_process_groups.py delete mode 100644 volatility/framework/plugins/mac/tasks.py diff --git a/volatility/framework/plugins/mac/bash.py b/volatility/framework/plugins/mac/bash.py index eeda5eb0d..947194632 100644 --- a/volatility/framework/plugins/mac/bash.py +++ b/volatility/framework/plugins/mac/bash.py @@ -12,10 +12,9 @@ from volatility.framework.configuration import requirements from volatility.framework.interfaces import plugins from volatility.framework.layers import scanners from volatility.framework.objects import utility -from volatility.plugins import timeliner -from volatility.plugins.mac import tasks - from volatility.framework.symbols.linux.bash import BashIntermedSymbols +from volatility.plugins import timeliner +from volatility.plugins.mac import pslist class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface): @@ -28,7 +27,7 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] def _generator(self, tasks): @@ -84,24 +83,23 @@ class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface): yield (0, (int(task.p_pid), task_name, hist.get_time_object(), hist.get_command())) def run(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('method', 'tasks')) return renderers.TreeGrid([("PID", int), ("Process", str), ("CommandTime", datetime.datetime), ("Command", str)], self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func))) + list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = filter_func))) def generate_timeline(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) for row in self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func)): + list_tasks(self.context, self.config['primary'], self.config['darwin'], filter_func = filter_func)): _depth, row_data = row description = "{} ({}): \"{}\"".format(row_data[0], row_data[1], row_data[3]) yield (description, timeliner.TimeLinerType.CREATED, row_data[2]) diff --git a/volatility/framework/plugins/mac/lsof.py b/volatility/framework/plugins/mac/lsof.py index 8aaf6c43b..6c2176f97 100644 --- a/volatility/framework/plugins/mac/lsof.py +++ b/volatility/framework/plugins/mac/lsof.py @@ -8,7 +8,7 @@ from volatility.framework import renderers from volatility.framework.automagic import mac from volatility.framework.configuration import requirements from volatility.framework.interfaces import plugins -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist vollog = logging.getLogger(__name__) @@ -23,7 +23,7 @@ class Lsof(plugins.PluginInterface): description = 'Kernel Address Space', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] def _generator(self, tasks): @@ -36,11 +36,12 @@ class Lsof(plugins.PluginInterface): yield (0, (pid, fd, filepath)) def run(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) return renderers.TreeGrid([("PID", int), ("File Descriptor", int), ("File Path", str)], self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func))) + list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = filter_func))) diff --git a/volatility/framework/plugins/mac/malfind.py b/volatility/framework/plugins/mac/malfind.py index c80fd9799..44370d313 100644 --- a/volatility/framework/plugins/mac/malfind.py +++ b/volatility/framework/plugins/mac/malfind.py @@ -8,7 +8,7 @@ from volatility.framework import renderers from volatility.framework.configuration import requirements from volatility.framework.objects import utility from volatility.framework.renderers import format_hints -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist class Malfind(interfaces.plugins.PluginInterface): @@ -21,7 +21,7 @@ class Malfind(interfaces.plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Linux kernel symbols"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)), + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)), ] def _list_injections(self, task): @@ -61,13 +61,14 @@ class Malfind(interfaces.plugins.PluginInterface): vma.get_perms(), format_hints.HexBytes(data), disasm)) def run(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex), ("End", format_hints.Hex), ("Protection", str), ("Hexdump", format_hints.HexBytes), ("Disasm", interfaces.renderers.Disassembly)], self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func))) + list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = filter_func))) diff --git a/volatility/framework/plugins/mac/netstat.py b/volatility/framework/plugins/mac/netstat.py index d653d1da6..885ac7d19 100644 --- a/volatility/framework/plugins/mac/netstat.py +++ b/volatility/framework/plugins/mac/netstat.py @@ -11,7 +11,7 @@ from volatility.framework.configuration import requirements from volatility.framework.interfaces import plugins from volatility.framework.objects import utility from volatility.framework.renderers import format_hints -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist vollog = logging.getLogger(__name__) @@ -26,7 +26,7 @@ class Netstat(plugins.PluginInterface): description = 'Kernel Address Space', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] @classmethod @@ -45,7 +45,9 @@ class Netstat(plugins.PluginInterface): 2) The process ID of the processed that opened the socket 3) The address of the associated socket structure """ - for task in tasks.Tasks.list_tasks(context, layer_name, darwin_symbols, filter_func): + # This is hardcoded, since a change in method + list_tasks = pslist.PsList.get_list_tasks(pslist.PsList.pslist_methods[0]) + for task in list_tasks(context, layer_name, darwin_symbols, filter_func): task_name = utility.array_to_string(task.p_comm) pid = task.p_pid @@ -67,7 +69,7 @@ class Netstat(plugins.PluginInterface): yield task_name, pid, socket def _generator(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) for task_name, pid, socket in self.list_sockets(self.context, self.config['primary'], diff --git a/volatility/framework/plugins/mac/proc_maps.py b/volatility/framework/plugins/mac/proc_maps.py index 2160b0695..faeb70c15 100644 --- a/volatility/framework/plugins/mac/proc_maps.py +++ b/volatility/framework/plugins/mac/proc_maps.py @@ -6,7 +6,7 @@ from volatility.framework import renderers, interfaces from volatility.framework.configuration import requirements from volatility.framework.objects import utility from volatility.framework.renderers import format_hints -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist class Maps(interfaces.plugins.PluginInterface): @@ -19,7 +19,7 @@ class Maps(interfaces.plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Linux kernel symbols"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] def _generator(self, tasks): @@ -36,12 +36,13 @@ class Maps(interfaces.plugins.PluginInterface): format_hints.Hex(vma.links.end), vma.get_perms(), path)) def run(self): - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex), ("End", format_hints.Hex), ("Protection", str), ("Map Name", str)], self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func))) + list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = filter_func))) diff --git a/volatility/framework/plugins/mac/psaux.py b/volatility/framework/plugins/mac/psaux.py index 208954acc..cc984b9e5 100644 --- a/volatility/framework/plugins/mac/psaux.py +++ b/volatility/framework/plugins/mac/psaux.py @@ -8,7 +8,7 @@ from volatility.framework import exceptions, renderers, interfaces from volatility.framework.configuration import requirements from volatility.framework.interfaces import plugins from volatility.framework.objects import utility -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist class Psaux(plugins.PluginInterface): @@ -21,7 +21,7 @@ class Psaux(plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] def _generator(self, tasks: Iterator[Any]) -> Generator[Tuple[int, Tuple[int, str, int, str]], None, None]: @@ -86,11 +86,12 @@ class Psaux(plugins.PluginInterface): yield (0, (task.p_pid, task_name, task.p_argc, args_str)) def run(self) -> renderers.TreeGrid: - filter_func = tasks.Tasks.create_pid_filter([self.config.get('pid', None)]) + filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) return renderers.TreeGrid([("PID", int), ("Process", str), ("Argc", int), ("Arguments", str)], self._generator( - tasks.Tasks.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = filter_func))) + list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = filter_func))) diff --git a/volatility/framework/plugins/mac/pslist.py b/volatility/framework/plugins/mac/pslist.py index e13ef624a..c32ca5c22 100644 --- a/volatility/framework/plugins/mac/pslist.py +++ b/volatility/framework/plugins/mac/pslist.py @@ -6,7 +6,6 @@ import logging from typing import Callable, Iterable, List, Dict from volatility.framework import renderers, interfaces, contexts, exceptions -from volatility.framework.automagic import mac from volatility.framework.configuration import requirements from volatility.framework.objects import utility @@ -16,7 +15,8 @@ vollog = logging.getLogger(__name__) class PsList(interfaces.plugins.PluginInterface): """Lists the processes present in a particular mac memory image.""" - _version = (1, 0, 0) + _version = (2, 0, 0) + pslist_methods = ['tasks', 'allproc', 'process_group'] @classmethod def get_requirements(cls): @@ -24,9 +24,43 @@ class PsList(interfaces.plugins.PluginInterface): requirements.TranslationLayerRequirement(name = 'primary', description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), - requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols") + requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"), + requirements.ChoiceRequirement(name = 'pslist_method', + description = 'Method to determine for processes', + choices = cls.pslist_methods, + default = cls.pslist_methods[0], + optional = True) ] + @classmethod + def get_list_tasks( + cls, method: str + ) -> Callable[[interfaces.context.ContextInterface, str, str, Callable[[int], bool]], + Iterable[interfaces.objects.ObjectInterface]]: + """Returns the list_tasks method based on the selector + + Args: + method: Must be one fo the available methods in get_task_choices + + Returns: + list_tasks method for listing tasks + """ + # Ensure method is one of the suitable choices + if method not in cls.pslist_methods: + method = cls.pslist_methods[0] + + if method == 'allproc': + list_tasks = cls.list_tasks_allproc + elif method == 'tasks': + list_tasks = cls.list_tasks_tasks + elif method == 'process_group': + list_tasks = cls.list_tasks_process_group + else: + raise ValueError("Impossible method choice chosen") + vollog.debug("Using method {}".format(method)) + + return list_tasks + @classmethod def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]: @@ -43,23 +77,25 @@ class PsList(interfaces.plugins.PluginInterface): return filter_func def _generator(self): - for task in self.list_tasks(self.context, - self.config['primary'], - self.config['darwin'], - filter_func = self.create_pid_filter([self.config.get('pid', None)])): + list_tasks = self.get_list_tasks(self.config.get('method', self.pslist_methods[0])) + + for task in list_tasks(self.context, + self.config['primary'], + self.config['darwin'], + filter_func = self.create_pid_filter([self.config.get('pid', None)])): pid = task.p_pid ppid = task.p_ppid name = utility.array_to_string(task.p_comm) yield (0, (pid, ppid, name)) @classmethod - def list_tasks(cls, - context: interfaces.context.ContextInterface, - layer_name: str, - darwin_symbols: str, - filter_func: Callable[[int], bool] = lambda _: False) -> \ + def list_tasks_allproc(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + darwin_symbols: str, + filter_func: Callable[[int], bool] = lambda _: False) -> \ Iterable[interfaces.objects.ObjectInterface]: - """Lists all the processes in the primary layer. + """Lists all the processes in the primary layer based on the allproc method Args: context: The context to retrieve required elements (layers, symbol tables) from @@ -73,7 +109,7 @@ class PsList(interfaces.plugins.PluginInterface): kernel = contexts.Module(context, darwin_symbols, layer_name, 0) - kernel_as = context.layers[layer_name] + kernel_layer = context.layers[layer_name] proc = kernel.object_from_symbol(symbol_name = "allproc").lh_first @@ -85,7 +121,7 @@ class PsList(interfaces.plugins.PluginInterface): else: seen[proc.vol.offset] = 1 - if not filter_func(proc) and kernel_as.is_valid(proc.vol.offset, proc.vol.size): + if not filter_func(proc) and kernel_layer.is_valid(proc.vol.offset, proc.vol.size): yield proc try: @@ -93,5 +129,112 @@ class PsList(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: break + @classmethod + def list_tasks_tasks(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + darwin_symbols: str, + filter_func: Callable[[int], bool] = lambda _: False) -> \ + Iterable[interfaces.objects.ObjectInterface]: + """Lists all the tasks in the primary layer based on the tasks queue + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + layer_name: The name of the layer on which to operate + darwin_symbols: The name of the table containing the kernel symbols + filter_func: A function which takes a task object and returns True if the task should be ignored/filtered + + Returns: + The list of task objects from the `layer_name` layer's `tasks` list after filtering + """ + + kernel = contexts.Module(context, darwin_symbols, layer_name, 0) + + kernel_layer = context.layers[layer_name] + + queue_entry = kernel.object_from_symbol(symbol_name = "tasks") + + seen = {} # type: Dict[int, int] + for task in queue_entry.walk_list(queue_entry, "tasks", "task"): + if task.vol.offset in seen: + vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).") + break + else: + seen[task.vol.offset] = 1 + + try: + proc = task.bsd_info.dereference().cast("proc") + except exceptions.PagedInvalidAddressException: + continue + + if kernel_layer.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc): + yield proc + + @classmethod + def list_tasks_process_group(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + darwin_symbols: str, + filter_func: Callable[[int], bool] = lambda _: False) -> \ + Iterable[interfaces.objects.ObjectInterface]: + """Lists all the tasks in the primary layer using process groups + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + layer_name: The name of the layer on which to operate + darwin_symbols: The name of the table containing the kernel symbols + filter_func: A function which takes a task object and returns True if the task should be ignored/filtered + + Returns: + The list of task objects from the `layer_name` layer's `tasks` list after filtering + """ + + kernel = contexts.Module(context, darwin_symbols, layer_name, 0) + + table_size = kernel.object_from_symbol(symbol_name = "pgrphash") + + pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl") + + proc_array = kernel.object(object_type = "array", + offset = pgrphashtbl, + count = table_size + 1, + subtype = kernel.get_type("pgrphashhead")) + + for proc_list in proc_array: + # test the validity of the current element + # it is expected that many won't be initialized + try: + pgrp = proc_list.lh_first + except exceptions.PagedInvalidAddressException: + continue + + seen_pgrps = set() + + # this walks the particular process group + while pgrp and pgrp.vol.offset not in seen_pgrps: + seen_pgrps.add(pgrp.vol.offset) + + # nothing can be done if this list pointer is invalid, so move on + try: + p = pgrp.pg_members.lh_first + except exceptions.PagedInvalidAddressException: + break + + seen_pg = set() + while p and p.vol.offset not in seen_pg: + seen_pg.add(p.vol.offset) + + if p.is_readable() and not filter_func(p): + yield p + + try: + p = p.p_pglist.le_next + except exceptions.PagedInvalidAddressException: + break + try: + pgrp = pgrp.pg_hash.le_next + except exceptions.PagedInvalidAddressException: + break + def run(self): return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator()) diff --git a/volatility/framework/plugins/mac/pslist_process_groups.py b/volatility/framework/plugins/mac/pslist_process_groups.py deleted file mode 100644 index e145aba14..000000000 --- a/volatility/framework/plugins/mac/pslist_process_groups.py +++ /dev/null @@ -1,89 +0,0 @@ -# This file is Copyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0 -# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 -# - -import logging -from typing import Callable, Dict, Iterable - -from volatility.framework import constants, interfaces, objects, contexts, exceptions -from volatility.plugins.mac import pslist - -vollog = logging.getLogger(__name__) - -class Pslist_Process_Groups(pslist.PsList): - """Lists the processes present in a particular mac memory image by enumerating the process group hash table.""" - - @classmethod - def list_tasks(cls, - context: interfaces.context.ContextInterface, - layer_name: str, - darwin_symbols: str, - filter_func: Callable[[int], bool] = lambda _: False) -> \ - Iterable[interfaces.objects.ObjectInterface]: - """Lists all the tasks in the primary layer. - - Args: - context: The context to retrieve required elements (layers, symbol tables) from - layer_name: The name of the layer on which to operate - darwin_symbols: The name of the table containing the kernel symbols - filter_func: A function which takes a task object and returns True if the task should be ignored/filtered - - Returns: - The list of task objects from the `layer_name` layer's `tasks` list after filtering - """ - - kernel = contexts.Module(context, darwin_symbols, layer_name, 0) - - kernel_as = context.layers[layer_name] - - table_size = kernel.object_from_symbol(symbol_name = "pgrphash") - - pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl") - - proc_array = kernel.object(object_type = "array", - offset = pgrphashtbl, - count = table_size + 1, - subtype = kernel.get_type("pgrphashhead")) - - for proc_list in proc_array: - # test the validity of the current element - # it is expected that many won't be initialized - try: - pgrp = proc_list.lh_first - except exceptions.PagedInvalidAddressException: - continue - - seen_pgrps = set() - - # this walks the particular process group - while pgrp and pgrp.vol.offset not in seen_pgrps: - seen_pgrps.add(pgrp.vol.offset) - - # nothing can be done if this list pointer is invalid, so move on - try: - p = pgrp.pg_members.lh_first - except exceptions.PagedInvalidAddressException: - break - - seen_pg = set() - while p and p.vol.offset not in seen_pg: - seen_pg.add(p.vol.offset) - - if p.is_readable(): - yield p - - try: - p = p.p_pglist.le_next - except exceptions.PagedInvalidAddressException: - break - try: - pgrp = pgrp.pg_hash.le_next - except exceptions.PagedInvalidAddressException: - break - - - - - - - diff --git a/volatility/framework/plugins/mac/pstree.py b/volatility/framework/plugins/mac/pstree.py index 75f0a2a66..0f033175c 100644 --- a/volatility/framework/plugins/mac/pstree.py +++ b/volatility/framework/plugins/mac/pstree.py @@ -6,7 +6,7 @@ from volatility.framework import renderers from volatility.framework.configuration import requirements from volatility.framework.interfaces import plugins from volatility.framework.objects import utility -from volatility.plugins.mac import tasks +from volatility.plugins.mac import pslist class PsTree(plugins.PluginInterface): @@ -26,7 +26,7 @@ class PsTree(plugins.PluginInterface): description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"), - requirements.PluginRequirement(name = 'tasks', plugin = tasks.Tasks, version = (1, 0, 0)) + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)) ] def _find_level(self, pid): @@ -45,8 +45,10 @@ class PsTree(plugins.PluginInterface): self._levels[pid] = level def _generator(self): - """Generates the.""" - for proc in tasks.Tasks.list_tasks(self.context, self.config['primary'], self.config['darwin']): + """Generates the tree list of processes""" + list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0])) + + for proc in list_tasks(self.context, self.config['primary'], self.config['darwin']): self._processes[proc.p_pid] = proc # Build the child/level maps diff --git a/volatility/framework/plugins/mac/tasks.py b/volatility/framework/plugins/mac/tasks.py deleted file mode 100644 index d7f472161..000000000 --- a/volatility/framework/plugins/mac/tasks.py +++ /dev/null @@ -1,56 +0,0 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 -# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 -# - -import logging -from typing import Callable, Dict, Iterable - -from volatility.framework import interfaces, contexts, exceptions -from volatility.plugins.mac import pslist - -vollog = logging.getLogger(__name__) - - -class Tasks(pslist.PsList): - """Lists the processes present in a particular mac memory image.""" - - @classmethod - def list_tasks(cls, - context: interfaces.context.ContextInterface, - layer_name: str, - darwin_symbols: str, - filter_func: Callable[[int], bool] = lambda _: False) -> \ - Iterable[interfaces.objects.ObjectInterface]: - """Lists all the tasks in the primary layer. - - Args: - context: The context to retrieve required elements (layers, symbol tables) from - layer_name: The name of the layer on which to operate - darwin_symbols: The name of the table containing the kernel symbols - filter_func: A function which takes a task object and returns True if the task should be ignored/filtered - - Returns: - The list of task objects from the `layer_name` layer's `tasks` list after filtering - """ - - kernel = contexts.Module(context, darwin_symbols, layer_name, 0) - - kernel_as = context.layers[layer_name] - - queue_entry = kernel.object_from_symbol(symbol_name = "tasks") - - seen = {} # type: Dict[int, int] - for task in queue_entry.walk_list(queue_entry, "tasks", "task"): - if task.vol.offset in seen: - vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).") - break - else: - seen[task.vol.offset] = 1 - - try: - proc = task.bsd_info.dereference().cast("proc") - except exceptions.PagedInvalidAddressException: - continue - - if kernel_as.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc): - yield proc