From cb8e105449dfe892bb8f0b60cc69d6c5696ce88f Mon Sep 17 00:00:00 2001 From: drakemp Date: Wed, 30 Jan 2019 11:09:47 -0500 Subject: [PATCH] FreeBSD Pslist draft --- volatility/plugins/freebsd/__init__.py | 35 ++++++++ volatility/plugins/freebsd/pslist.py | 107 +++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 volatility/plugins/freebsd/__init__.py create mode 100644 volatility/plugins/freebsd/pslist.py diff --git a/volatility/plugins/freebsd/__init__.py b/volatility/plugins/freebsd/__init__.py new file mode 100644 index 000000000..138a42ee8 --- /dev/null +++ b/volatility/plugins/freebsd/__init__.py @@ -0,0 +1,35 @@ +# This file was contributed to the Volatility Framework Version 3. +# Copyright (C) 2018 Volatility Foundation. +# +# THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE Volatility Contributors +# Public License V1.0("LICENSE") AS FIRST COMPLETED BY: Volatility Foundation, +# Inc. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION +# OF, OR PREPARATION OF SUBSEQUENT WORKS, DERIVATIVE WORKS OR DERIVED WORKS BASED +# ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS +# TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED +# WORK,” “RECIPIENT" AND “DISTRIBUTOR" ARE DEFINED IN THE LICENSE. A COPY OF THE +# LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.txt" ACCOMPANYING THE +# CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A +# COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE: +# https://www.volatilityfoundation.org/license/vcpl_v1.0 +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +# specific language governing rights and limitations under the License. +# +"""All FreeBSD-related plugins + + NOTE: This file is important for core plugins to run (which certain components such as the windows registry layers) + are dependent upon, please DO NOT alter or remove this file unless you know the consequences of doing so. + + The framework is configured this way to allow plugin developers/users to override any plugin functionality whether + existing or new. +""" + +import os + +from volatility.framework import constants + +# This is necessary to ensure the core plugins are available, whilst still be overridable +plugin_path_components = __name__.split('.')[2:] +__path__ = [os.path.join(x, *plugin_path_components) for x in constants.PLUGINS_PATH] diff --git a/volatility/plugins/freebsd/pslist.py b/volatility/plugins/freebsd/pslist.py new file mode 100644 index 000000000..d6ef6af99 --- /dev/null +++ b/volatility/plugins/freebsd/pslist.py @@ -0,0 +1,107 @@ +# This file was contributed to the Volatility Framework Version 2. +# Copyright (C) 2018 Volatility Foundation. +# +# THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE Volatility Contributors +# Public License V1.0("LICENSE") AS FIRST COMPLETED BY: Volatility Foundation, +# Inc. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION +# OF, OR PREPARATION OF SUBSEQUENT WORKS, DERIVATIVE WORKS OR DERIVED WORKS BASED +# ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS +# TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED +# WORK,” “RECIPIENT" AND “DISTRIBUTOR" ARE DEFINED IN THE LICENSE. A COPY OF THE +# LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.txt" ACCOMPANYING THE +# CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A +# COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE: +# https://www.volatilityfoundation.org/license/vcpl_v1.0 +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +# specific language governing rights and limitations under the License. +# + +from typing import Callable, Iterable, List + +import volatility.framework.interfaces.plugins as interfaces_plugins +from volatility.framework import renderers, interfaces, contexts +# from volatility.framework.automagic import linux #FreeBSD automagic not implemented +from volatility.framework.configuration import requirements +# from volatility.framework.objects import utility + +class PsList(interfaces_plugins.PluginInterface): + """Lists the processes present in a FreeBSD memory image""" + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + """Returns a list of requirement to satisfy executing the plugin""" + return [requirements.TranslationLayerRequirement( + name = 'primary', + description = "Memory layer for the kernel", + architectures = ['Intel32', 'Intel64']), + requirements.SymbolRequirement( + name = 'ksyms', #TODO: is this right? + description = "FreeBSD kernel Symbols") + # Would it make sense to require the 'allproc' symbol? + ] + + @classmethod + def create_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]: + # FIXME: mypy #4973 or #2608 + pid_list = pid_list or [] + filter_list = [x for x in pid_list if x is not None] + if filter_list: + def filter_func(x): + return x not in filter_list + + return filter_func + else: + return lambda _: False + + def _generator(self): + """Produces all task after filtering""" + for task in list_tasks( + self.context, + self.config['primary'], + self.config['ksyms'], + filter = self.create_filter([self.config.get('pid', None)])): + pid = task.pid + ppid = 0 + if task.parent: + ppid = task.parent.pid + name = utility.array_to_string(task.comm) + yield (0, (pid, ppid, name)) + + @classmethod + def list_tasks(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + ksyms_symbols: str, + filter: Callable[[int],bool] = lambda _:False) \ + -> Iterable[interfaces.objects.ObjectInterface]: + """List all processes (tasks) in primary layer""" + #TODO: aslr_mask_symbol_table? + + view = contexts.Module(context, + ksyms_symbols, + layer_name, + 0, + absolute_symbol_addresses=True) + + # Symbol 'allproc' must be in profile + proc = view.object(symbol_name = "allproc").lh_first + + #seen = {} + while proc is not None and proc.vol.offset != 0: + #TODO: Uncomment after testing + #if proc.vol.offset in seen: + # vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition)") + # break + #else: + # seen[proc.vol.offset] = 1 + + # Generate and advance + yield proc + proc = proc.p_list.le_next.dereference() + + + def run(self): + """Entry point for plugin""" + return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator())