From 16cb449a77555fa7514b968352771f882589f37c Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 17 Dec 2021 14:23:14 +1100 Subject: [PATCH 01/27] Added changes to the Linux pslist and pstree plugins to be able to show user threads. --- .../framework/constants/linux/__init__.py | 2 + volatility3/framework/plugins/linux/pslist.py | 125 +++++++++++++++--- volatility3/framework/plugins/linux/pstree.py | 79 +++++++---- .../framework/symbols/linux/__init__.py | 9 ++ 4 files changed, 171 insertions(+), 44 deletions(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index c25ea0e2f..276c6015f 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -11,3 +11,5 @@ KERNEL_NAME = "__kernel__" # arch/x86/include/asm/page_types.h PAGE_SHIFT = 12 """The value hard coded from the Linux Kernel (hence not extracted from the layer itself)""" + +PF_KTHREAD = 0x00200000 # I'm a kernel thread diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index 5672bb56e..516c74305 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -1,11 +1,12 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 +# This file is Copyright 2021 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 Callable, Iterable, List, Any +from typing import Callable, Iterable, List, Any, Tuple -from volatility3.framework import renderers, interfaces +from volatility3.framework import renderers, interfaces, constants from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility +from volatility3.framework.symbols import linux class PsList(interfaces.plugins.PluginInterface): @@ -13,7 +14,7 @@ class PsList(interfaces.plugins.PluginInterface): _required_framework_version = (2, 0, 0) - _version = (2, 0, 0) + _version = (2, 1, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -23,7 +24,15 @@ class PsList(interfaces.plugins.PluginInterface): requirements.ListRequirement(name = 'pid', description = 'Filter on specific process IDs', element_type = int, - optional = True) + optional = True), + requirements.BooleanRequirement(name="threads", + description="Include user threads", + optional=True, + default=False), + requirements.BooleanRequirement(name="decorate_comm", + description="Show `user threads` comm in curly brackets, and `kernel threads` comm in square brackets", + optional=True, + default=False), ] @classmethod @@ -48,31 +57,88 @@ class PsList(interfaces.plugins.PluginInterface): else: return lambda _: False - def _generator(self): + @staticmethod + def task_is_kernel_thread(task: interfaces.objects.ObjectInterface) -> bool: + return (task.flags & constants.PF_KTHREAD) != 0 + + @staticmethod + def task_is_thread_group_leader(task: interfaces.objects.ObjectInterface) -> bool: + return task.tgid == task.pid + + @staticmethod + def task_is_user_thread(task: interfaces.objects.ObjectInterface) -> bool: + return task.tgid != task.pid + + def _get_task_fields( + self, + task: interfaces.objects.ObjectInterface, + decorate_comm: bool = False) -> Tuple[int, int, int, str]: + """Extract the fields needed for the final output + + Args: + task: A task object from where to get the fields. + decorate_comm: If True, it decorates the comm string of + - User threads: in curly brackets, + - Kernel threads: in square brackets + Defaults to False. + Returns: + A tuple with the fields to show in the plugin output. + """ + pid = task.tgid + tid = task.pid + ppid = task.parent.tgid if task.parent else 0 + name = utility.array_to_string(task.comm) + if decorate_comm: + if self.task_is_kernel_thread(task): + name = f"[{name}]" + elif self.task_is_user_thread(task): + name = f"{{{name}}}" + + task_fields = (pid, tid, ppid, name) + return task_fields + + def _generator( + self, + pid_filter: Callable[[Any], bool], + include_threads: bool = False, + decorate_comm: bool = False): + """Generates the tasks list. + + Args: + pid_filter: A function which takes a process object and returns True if the process should be ignored/filtered + include_threads: If True, the output will also show the user threads + If False, only the thread group leaders will be shown + Defaults to False. + decorate_comm: If True, it decorates the comm string of + - User threads: in curly brackets, + - Kernel threads: in square brackets + Defaults to False. + Yields: + Each rows + """ for task in self.list_tasks(self.context, self.config['kernel'], - filter_func = self.create_pid_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)) + pid_filter, + include_threads): + row = self._get_task_fields(task, decorate_comm) + yield (0, row) @classmethod def list_tasks( cls, context: interfaces.context.ContextInterface, vmlinux_module_name: str, - filter_func: Callable[[int], bool] = lambda _: False) -> Iterable[interfaces.objects.ObjectInterface]: + filter_func: Callable[[int], bool] = lambda _: False, + include_threads: bool = 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 vmlinux_module_name: The name of the kernel module on which to operate - + filter_func: A function which takes a process object and returns True if the process should be ignored/filtered + include_threads: If True, it will also return user threads. Yields: - Process objects + Task objects """ vmlinux = context.modules[vmlinux_module_name] @@ -80,8 +146,29 @@ class PsList(interfaces.plugins.PluginInterface): # Note that the init_task itself is not yielded, since "ps" also never shows it. for task in init_task.tasks: - if not filter_func(task): - yield task + if filter_func(task): + continue + + task_threads = [] + current_task = None + next_task = task.thread_group.next + while current_task is None or current_task.vol.offset != task.vol.offset: + current_task = linux.LinuxUtilities.container_of(next_task, "task_struct", "thread_group", vmlinux) + if cls.task_is_thread_group_leader(current_task): + # Making sure the first task yielded is the Task Group Leader + yield current_task + elif include_threads: + task_threads.append(current_task) + next_task = current_task.thread_group.next + + # yield the other task threads + yield from task_threads def run(self): - return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator()) + pids = self.config.get('pid') + include_threads = self.config.get('threads') + decorate_comm = self.config.get('decorate_comm') + filter_func = self.create_pid_filter(pids) + + columns = [("PID", int), ("TID", int), ("PPID", int), ("COMM", str)] + return renderers.TreeGrid(columns, self._generator(filter_func, include_threads, decorate_comm)) diff --git a/volatility3/framework/plugins/linux/pstree.py b/volatility3/framework/plugins/linux/pstree.py index 3b95a344c..174e94ec4 100644 --- a/volatility3/framework/plugins/linux/pstree.py +++ b/volatility3/framework/plugins/linux/pstree.py @@ -1,8 +1,7 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 +# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -from volatility3.framework.objects import utility from volatility3.plugins.linux import pslist @@ -12,44 +11,74 @@ class PsTree(pslist.PsList): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._processes = {} + self._tasks = {} self._levels = {} self._children = {} - def find_level(self, pid): - """Finds how deep the pid is in the processes list.""" - seen = set([]) - seen.add(pid) - level = 0 - proc = self._processes.get(pid, None) - while proc is not None and proc.parent != 0 and proc.parent.pid not in seen: - ppid = int(proc.parent.pid) + def find_level(self, pid: int) -> None: + """Finds how deep the PID is in the tasks hierarchy. - child_list = self._children.get(ppid, set([])) + Args: + pid: PID to find the level in the hierachy + """ + seen = set([pid]) + level = 0 + proc = self._tasks.get(pid) + while proc and proc.parent and proc.parent.pid not in seen: + if self.task_is_thread_group_leader(proc): + parent_pid = proc.parent.pid + else: + parent_pid = proc.tgid + + child_list = self._children.setdefault(parent_pid, set()) child_list.add(proc.pid) - self._children[ppid] = child_list - proc = self._processes.get(ppid, None) + + proc = self._tasks.get(parent_pid) level += 1 + self._levels[pid] = level - def _generator(self): - """Generates the.""" + def _generator( + self, + pid_filter, + include_threads: bool = False, + decorate_com: bool = False): + """Generates the tasks hierarchy tree. + + Args: + pid_filter: A function which takes a process object and returns True if the process should be ignored/filtered + include_threads: If True, the output will also show the user threads + If False, only the thread group leaders will be shown + Defaults to False. + decorate_comm: If True, it decorates the comm string of + - User threads: in curly brackets, + - Kernel threads: in square brackets + Defaults to False. + Yields: + Each rows + """ vmlinux = self.context.modules[self.config['kernel']] - for proc in self.list_tasks(self.context, vmlinux.name): - self._processes[proc.pid] = proc + for proc in self.list_tasks(self.context, + vmlinux.name, + filter_func=pid_filter, + include_threads=include_threads): + self._tasks[proc.pid] = proc # Build the child/level maps - for pid in self._processes: + for pid in self._tasks: self.find_level(pid) def yield_processes(pid): - proc = self._processes[pid] - row = (proc.pid, proc.parent.pid, utility.array_to_string(proc.comm)) + task = self._tasks[pid] - yield (self._levels[pid] - 1, row) - for child_pid in self._children.get(pid, []): + row = self._get_task_fields(task, decorate_com) + + tid = task.pid + yield (self._levels[tid] - 1, row) + + for child_pid in sorted(self._children.get(tid, [])): yield from yield_processes(child_pid) - for pid in self._levels: - if self._levels[pid] == 1: + for pid, level in self._levels.items(): + if level == 1: yield from yield_processes(pid) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 36e23a35d..4df97954d 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -267,3 +267,12 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset) yield list_struct list_start = getattr(list_struct, list_member) + + @classmethod + def container_of(cls, addr, type_name, member_name, vmlinux): + if not addr: + return + type_dec = vmlinux.get_type(type_name) + member_offset = type_dec.relative_child_offset(member_name) + container_addr = addr - member_offset + return vmlinux.object(object_type=type_name, offset=container_addr, absolute=True) \ No newline at end of file From 86d3785bea15f4337555c39b7008510d66032943 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 17 Dec 2021 14:42:23 +1100 Subject: [PATCH 02/27] Fixing constant module and reference comment --- volatility3/framework/constants/linux/__init__.py | 1 + volatility3/framework/plugins/linux/pslist.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index 276c6015f..c0f85593f 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -12,4 +12,5 @@ KERNEL_NAME = "__kernel__" PAGE_SHIFT = 12 """The value hard coded from the Linux Kernel (hence not extracted from the layer itself)""" +# include/linux/sched.h PF_KTHREAD = 0x00200000 # I'm a kernel thread diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index 516c74305..b42453d3c 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -59,7 +59,7 @@ class PsList(interfaces.plugins.PluginInterface): @staticmethod def task_is_kernel_thread(task: interfaces.objects.ObjectInterface) -> bool: - return (task.flags & constants.PF_KTHREAD) != 0 + return (task.flags & constants.linux.PF_KTHREAD) != 0 @staticmethod def task_is_thread_group_leader(task: interfaces.objects.ObjectInterface) -> bool: From 39c97b8e9795892f0fbf6851154f66f62bb1871b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 20 Dec 2021 15:26:45 +1100 Subject: [PATCH 03/27] Moving thread type check methods to the task object --- volatility3/framework/plugins/linux/pslist.py | 20 +++----------- .../symbols/linux/extensions/__init__.py | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index b42453d3c..4c55f853c 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -3,7 +3,7 @@ # from typing import Callable, Iterable, List, Any, Tuple -from volatility3.framework import renderers, interfaces, constants +from volatility3.framework import renderers, interfaces from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility from volatility3.framework.symbols import linux @@ -57,18 +57,6 @@ class PsList(interfaces.plugins.PluginInterface): else: return lambda _: False - @staticmethod - def task_is_kernel_thread(task: interfaces.objects.ObjectInterface) -> bool: - return (task.flags & constants.linux.PF_KTHREAD) != 0 - - @staticmethod - def task_is_thread_group_leader(task: interfaces.objects.ObjectInterface) -> bool: - return task.tgid == task.pid - - @staticmethod - def task_is_user_thread(task: interfaces.objects.ObjectInterface) -> bool: - return task.tgid != task.pid - def _get_task_fields( self, task: interfaces.objects.ObjectInterface, @@ -89,9 +77,9 @@ class PsList(interfaces.plugins.PluginInterface): ppid = task.parent.tgid if task.parent else 0 name = utility.array_to_string(task.comm) if decorate_comm: - if self.task_is_kernel_thread(task): + if task.is_kernel_thread: name = f"[{name}]" - elif self.task_is_user_thread(task): + elif task.is_user_thread: name = f"{{{name}}}" task_fields = (pid, tid, ppid, name) @@ -154,7 +142,7 @@ class PsList(interfaces.plugins.PluginInterface): next_task = task.thread_group.next while current_task is None or current_task.vol.offset != task.vol.offset: current_task = linux.LinuxUtilities.container_of(next_task, "task_struct", "thread_group", vmlinux) - if cls.task_is_thread_group_leader(current_task): + if current_task.is_thread_group_leader: # Making sure the first task yielded is the Task Group Leader yield current_task elif include_threads: diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 0edd60608..f28705617 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -201,6 +201,33 @@ class task_struct(generic.GenericIntelProcess): yield (start, end - start) + @property + def is_kernel_thread(self) -> bool: + """Checks if this task is a kernel thread. + + Returns: + bool: True, if this task is a kernel thread. Otherwise, False. + """ + return (self.flags & constants.linux.PF_KTHREAD) != 0 + + @property + def is_thread_group_leader(self) -> bool: + """Checks if this task is a thread group leader. + + Returns: + bool: True, if this task is a thread group leader. Otherwise, False. + """ + return self.tgid == self.pid + + @property + def is_user_thread(self) -> bool: + """Checks if this task is a user thread. + + Returns: + bool: True, if this task is a user thread. Otherwise, False. + """ + return not self.is_kernel_thread and self.tgid != self.pid + class fs_struct(objects.StructType): From 8bcb7b42276c98f675904bd9d6fdfb0d565641ef Mon Sep 17 00:00:00 2001 From: cpuu Date: Fri, 18 Feb 2022 13:25:46 +0900 Subject: [PATCH 04/27] Add offset information in pslist plugin for Linux --- volatility3/framework/plugins/linux/pslist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index 5672bb56e..dd1832576 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -6,6 +6,7 @@ from typing import Callable, Iterable, List, Any from volatility3.framework import renderers, interfaces from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints class PsList(interfaces.plugins.PluginInterface): @@ -57,7 +58,7 @@ class PsList(interfaces.plugins.PluginInterface): if task.parent: ppid = task.parent.pid name = utility.array_to_string(task.comm) - yield (0, (pid, ppid, name)) + yield (0, (format_hints.Hex(task.vol.offset), name, pid, ppid)) @classmethod def list_tasks( @@ -84,4 +85,4 @@ class PsList(interfaces.plugins.PluginInterface): yield task def run(self): - return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator()) + return renderers.TreeGrid([("OFFSET", format_hints.Hex), ("COMM", str), ("PID", int), ("PPID", int)], self._generator()) From 72ebf11fd36ff6d0d942181713529c25a02f55f5 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Sun, 24 Apr 2022 15:13:55 +0300 Subject: [PATCH 05/27] support CallbackListHead when CmpCallBackVector not present --- .../framework/plugins/windows/callbacks.py | 75 ++++++++++++++----- .../symbols/windows/callbacks-x64.json | 43 +++++++++++ .../symbols/windows/callbacks-x86.json | 43 +++++++++++ 3 files changed, 143 insertions(+), 18 deletions(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 352dba448..d8e5aea86 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -111,30 +111,19 @@ class Callbacks(interfaces.plugins.PluginInterface): yield symbol_name, callback.Callback, None @classmethod - def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, - callback_table_name: str) -> Iterable[Tuple[str, int, None]]: - """Lists all registry callbacks. - - Args: - context: The context to retrieve required elements (layers, symbol tables) from - layer_name: The name of the layer on which to operate - symbol_table: The name of the table containing the kernel symbols - callback_table_name: The nae of the table containing the callback symbols - - Yields: - A name, location and optional detail string + def _list_registry_callbacks_legacy(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, + callback_table_name: str) -> Iterable[Tuple[str, int, None]]: + """ + Lists all registry callbacks from the old format via the CmpCallBackVector. """ kvo = context.layers[layer_name].config['kernel_virtual_offset'] ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo) full_type_name = callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK" - try: - symbol_offset = ntkrnlmp.get_symbol("CmpCallBackVector").address - symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address - except exceptions.SymbolError: - vollog.debug("Cannot find CmpCallBackVector or CmpCallBackCount") - return + symbol_offset = ntkrnlmp.get_symbol("CmpCallBackVector").address + symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address + callback_count = ntkrnlmp.object(object_type = "unsigned int", offset = symbol_count_offset) @@ -155,6 +144,56 @@ class Callbacks(interfaces.plugins.PluginInterface): if callback.Function != 0: yield "CmRegisterCallback", callback.Function, None + @classmethod + def _list_registry_callbacks_new(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, + callback_table_name: str) -> Iterable[Tuple[str, int, None]]: + """ + Lists all registry callbacks via the CallbackListHead. + """ + + kvo = context.layers[layer_name].config['kernel_virtual_offset'] + ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo) + full_type_name = callback_table_name + constants.BANG + "_CM_CALLBACK_ENTRY" + + symbol_offset = ntkrnlmp.get_symbol("CallbackListHead").address + symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address + + callback_count = ntkrnlmp.object(object_type = "unsigned int", offset = symbol_count_offset) + + if callback_count == 0: + return + + callback_list = ntkrnlmp.object(object_type = "_LIST_ENTRY", offset = symbol_offset) + for callback in callback_list.to_list(full_type_name, "Link"): + yield "CmRegisterCallbackEx", callback.Function, f"Alltitude: {callback.Alltitude.String}" + + @classmethod + def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, + callback_table_name: str) -> Iterable[Tuple[str, int, None]]: + """Lists all registry callbacks. + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + layer_name: The name of the layer on which to operate + symbol_table: The name of the table containing the kernel symbols + callback_table_name: The nae of the table containing the callback symbols + + Yields: + A name, location and optional detail string + """ + + kvo = context.layers[layer_name].config['kernel_virtual_offset'] + ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo) + full_type_name = callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK" + + if ntkrnlmp.has_symbol("CmpCallBackVector") and ntkrnlmp.has_symbol("CmpCallBackCount"): + yield from cls._list_registry_callbacks_legacy(context, layer_name, symbol_table, callback_table_name) + elif ntkrnlmp.has_symbol("CallbackListHead") and ntkrnlmp.has_symbol("CmpCallBackCount"): + yield from cls._list_registry_callbacks_new(context, layer_name, symbol_table, callback_table_name) + else: + vollog.debug("Cannot find CmpCallBackVector or CmpCallBackCount or CallbackListHead") + return + @classmethod def list_bugcheck_reason_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, callback_table_name: str) -> Iterable[Tuple[str, int, str]]: diff --git a/volatility3/framework/symbols/windows/callbacks-x64.json b/volatility3/framework/symbols/windows/callbacks-x64.json index dbb6086df..5300d28f9 100644 --- a/volatility3/framework/symbols/windows/callbacks-x64.json +++ b/volatility3/framework/symbols/windows/callbacks-x64.json @@ -8,6 +8,12 @@ "signed": false, "endian": "little" }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, "unsigned char": { "kind": "char", "size": 1, @@ -137,6 +143,43 @@ }, "kind": "struct", "size": 64 + }, + "_CM_CALLBACK_ENTRY": { + "fields": { + "Link": { + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + }, + "offset": 0 + }, + "Cookie": { + "type": { + "kind": "base", + "name": "unsigned long long" + }, + "offset": 24 + }, + "Function": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "Alltitude": { + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + }, + "offset": 48 + } + }, + "kind": "struct", + "size": 64 } }, "metadata": { diff --git a/volatility3/framework/symbols/windows/callbacks-x86.json b/volatility3/framework/symbols/windows/callbacks-x86.json index cf0cb8b65..52baeb18f 100644 --- a/volatility3/framework/symbols/windows/callbacks-x86.json +++ b/volatility3/framework/symbols/windows/callbacks-x86.json @@ -8,6 +8,12 @@ "signed": false, "endian": "little" }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, "unsigned char": { "kind": "char", "size": 1, @@ -137,6 +143,43 @@ }, "kind": "struct", "size": 28 + }, + "_CM_CALLBACK_ENTRY": { + "fields": { + "Link": { + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + }, + "offset": 0 + }, + "Cookie": { + "type": { + "kind": "base", + "name": "unsigned long long" + }, + "offset": 16 + }, + "Function": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 28 + }, + "Alltitude": { + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + }, + "offset": 32 + } + }, + "kind": "struct", + "size": 40 } }, "metadata": { From bc04d22a1b7e2969f1ddd5ec6598439724c17bc7 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Sun, 24 Apr 2022 16:42:25 +0300 Subject: [PATCH 06/27] remove unused line --- volatility3/framework/plugins/windows/callbacks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index d8e5aea86..c10d97405 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -184,7 +184,6 @@ class Callbacks(interfaces.plugins.PluginInterface): kvo = context.layers[layer_name].config['kernel_virtual_offset'] ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo) - full_type_name = callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK" if ntkrnlmp.has_symbol("CmpCallBackVector") and ntkrnlmp.has_symbol("CmpCallBackCount"): yield from cls._list_registry_callbacks_legacy(context, layer_name, symbol_table, callback_table_name) From 0057f81269b382fdaa9f15922ec41f0cd1f31faa Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 25 Apr 2022 09:27:54 +0300 Subject: [PATCH 07/27] log which symbol does not exist --- volatility3/framework/plugins/windows/callbacks.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index c10d97405..5ee11f164 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -190,7 +190,14 @@ class Callbacks(interfaces.plugins.PluginInterface): elif ntkrnlmp.has_symbol("CallbackListHead") and ntkrnlmp.has_symbol("CmpCallBackCount"): yield from cls._list_registry_callbacks_new(context, layer_name, symbol_table, callback_table_name) else: - vollog.debug("Cannot find CmpCallBackVector or CmpCallBackCount or CallbackListHead") + symbols_to_check = ["CmpCallBackVector", "CmpCallBackCount", "CallbackListHead"] + vollog.debug("Failed to get registry callbacks!") + for symbol_name in symbols_to_check: + symbol_status = "does not exist" + if ntkrnlmp.has_symbol(symbol_name): + symbol_status = "exists" + vollog.debug(f"symbol {symbol_name} {symbol_status}.") + return @classmethod From 7308f4af0c4da19228b86c2b4aa8adb3773e952e Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 25 Apr 2022 11:21:05 +0300 Subject: [PATCH 08/27] minor improvments --- volatility3/framework/interfaces/objects.py | 19 ++++++++++--- volatility3/framework/interfaces/symbols.py | 14 ++++++++++ .../framework/symbols/windows/__init__.py | 27 +++++++++---------- .../symbols/windows/extensions/__init__.py | 8 ++---- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/volatility3/framework/interfaces/objects.py b/volatility3/framework/interfaces/objects.py index e589abd15..c1fb29bb6 100644 --- a/volatility3/framework/interfaces/objects.py +++ b/volatility3/framework/interfaces/objects.py @@ -115,7 +115,15 @@ class ObjectInterface(metaclass = abc.ABCMeta): mask = context.layers[object_info.layer_name].address_mask normalized_offset = object_info.offset & mask - self._vol = collections.ChainMap({}, {'type_name': type_name, 'offset': normalized_offset}, object_info, kwargs) + vol_info_dict = {'type_name': type_name, 'offset': normalized_offset} + if constants.BANG in type_name: + table_name, struct_name = type_name.split(constants.BANG) + vol_info_dict["table_name"] = table_name + vol_info_dict["short_name"] = struct_name + else: + vol_info_dict["short_name"] = type_name + + self._vol = collections.ChainMap({}, vol_info_dict, object_info, kwargs) self._context = context def __getattr__(self, attr: str) -> Any: @@ -142,7 +150,7 @@ class ObjectInterface(metaclass = abc.ABCMeta): """ if constants.BANG not in self.vol.type_name: raise ValueError(f"Unable to determine table for symbol: {self.vol.type_name}") - table_name = self.vol.type_name[:self.vol.type_name.index(constants.BANG)] + table_name = self.vol.table_name if table_name not in self._context.symbol_space: raise KeyError(f"Symbol table not found in context's symbol_space for symbol: {self.vol.type_name}") return table_name @@ -156,7 +164,7 @@ class ObjectInterface(metaclass = abc.ABCMeta): """ # TODO: Carefully consider the implications of casting and how it should work if constants.BANG not in new_type_name: - symbol_table = self.vol['type_name'].split(constants.BANG)[0] + symbol_table = self.get_symbol_table_name() new_type_name = symbol_table + constants.BANG + new_type_name object_template = self._context.symbol_space.get_type(new_type_name) object_template = object_template.clone() @@ -169,6 +177,11 @@ class ObjectInterface(metaclass = abc.ABCMeta): size = object_template.size) return object_template(context = self._context, object_info = object_info) + def at_layer(self, new_layer_name) -> 'ObjectInterface': + """Returns the same object casted at a different layer. + """ + return self._context.object(self.vol.type_name, offset=self.vol.offset, layer_name=new_layer_name) + def has_member(self, member_name: str) -> bool: """Returns whether the object would contain a member called member_name. diff --git a/volatility3/framework/interfaces/symbols.py b/volatility3/framework/interfaces/symbols.py index b271de412..99690054d 100644 --- a/volatility3/framework/interfaces/symbols.py +++ b/volatility3/framework/interfaces/symbols.py @@ -167,6 +167,20 @@ class BaseSymbolTableInterface: """ raise NotImplementedError("Abstract method set_type_class not implemented yet.") + def try_set_type_class(self, name: str, clazz: Type[objects.ObjectInterface]) -> bool: + """Calls the set_type_class function but does not throw an exception. + Returns whether setting the type class was successfull. + Args: + name: The name of the type to override the class for + clazz: The actual class to override for the provided type name + """ + try: + self.set_type_class(name, clazz) + + return True + except ValueError: + return False + def get_type_class(self, name: str) -> Type[objects.ObjectInterface]: """Returns the class associated with a Symbol type.""" raise NotImplementedError("Abstract method get_type_class not implemented yet.") diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index f09dadedf..468d998c4 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -4,7 +4,7 @@ from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows import extensions -from volatility3.framework.symbols.windows.extensions import registry, pool +from volatility3.framework.symbols.windows.extensions import registry, pool, pe class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): @@ -38,6 +38,11 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('_SHARED_CACHE_MAP', extensions.SHARED_CACHE_MAP) self.set_type_class('_VACB', extensions.VACB) self.set_type_class('_POOL_TRACKER_BIG_PAGES', pool.POOL_TRACKER_BIG_PAGES) + self.set_type_class('_IMAGE_DOS_HEADER', pe.IMAGE_DOS_HEADER) + self.set_type_class('_IMAGE_NT_HEADERS', pe.IMAGE_NT_HEADERS) + + # Might not exist in 32-bit operating systems. + self.try_set_type_class('_IMAGE_NT_HEADERS64', pe.IMAGE_NT_HEADERS) # This doesn't exist in very specific versions of windows try: @@ -49,19 +54,11 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): pass # these don't exist in windows XP - try: - self.set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT) - except ValueError: - pass - + self.try_set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT) + # these were introduced starting in windows 8 - try: - self.set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT) - except ValueError: - pass - + self.try_set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT) + # these were introduced starting in windows 7 - try: - self.set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT) - except ValueError: - pass + self.try_set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT) + \ No newline at end of file diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 7d083fbba..2f0f2388c 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -574,13 +574,9 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): proc_layer = self._context.layers[proc_layer_name] if not proc_layer.is_valid(self.Peb): raise exceptions.InvalidAddressException(proc_layer_name, self.Peb, - f"Invalid address at {self.Peb:0x}") + f"Invalid Peb address at {self.Peb:0x}") - sym_table = self.vol.type_name.split(constants.BANG)[0] - peb = self._context.object(f"{sym_table}{constants.BANG}_PEB", - layer_name = proc_layer_name, - offset = self.Peb) - return peb + return self.at_layer(proc_layer_name).Peb def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: """Generator for DLLs in the order that they were loaded.""" From c8ab4eb814b79afd4a2553703d97a3749a2e1fd8 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 25 Apr 2022 11:25:45 +0300 Subject: [PATCH 09/27] if not table name is present table name is an empty string --- volatility3/framework/interfaces/objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/interfaces/objects.py b/volatility3/framework/interfaces/objects.py index c1fb29bb6..8c7167a78 100644 --- a/volatility3/framework/interfaces/objects.py +++ b/volatility3/framework/interfaces/objects.py @@ -121,6 +121,7 @@ class ObjectInterface(metaclass = abc.ABCMeta): vol_info_dict["table_name"] = table_name vol_info_dict["short_name"] = struct_name else: + vol_info_dict["table_name"] = "" vol_info_dict["short_name"] = type_name self._vol = collections.ChainMap({}, vol_info_dict, object_info, kwargs) From a8d70c065738b3c3691da3ec2a82a6d4a7ca24a6 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 26 Apr 2022 10:11:12 +0300 Subject: [PATCH 10/27] fix typo --- volatility3/framework/plugins/windows/callbacks.py | 2 +- volatility3/framework/symbols/windows/callbacks-x64.json | 2 +- volatility3/framework/symbols/windows/callbacks-x86.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 5ee11f164..dca17aff7 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -165,7 +165,7 @@ class Callbacks(interfaces.plugins.PluginInterface): callback_list = ntkrnlmp.object(object_type = "_LIST_ENTRY", offset = symbol_offset) for callback in callback_list.to_list(full_type_name, "Link"): - yield "CmRegisterCallbackEx", callback.Function, f"Alltitude: {callback.Alltitude.String}" + yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {callback.Altitude.String}" @classmethod def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str, diff --git a/volatility3/framework/symbols/windows/callbacks-x64.json b/volatility3/framework/symbols/windows/callbacks-x64.json index 5300d28f9..87682cb92 100644 --- a/volatility3/framework/symbols/windows/callbacks-x64.json +++ b/volatility3/framework/symbols/windows/callbacks-x64.json @@ -170,7 +170,7 @@ }, "offset": 40 }, - "Alltitude": { + "Altitude": { "type": { "kind": "struct", "name": "nt_symbols!_UNICODE_STRING" diff --git a/volatility3/framework/symbols/windows/callbacks-x86.json b/volatility3/framework/symbols/windows/callbacks-x86.json index 52baeb18f..702b68a65 100644 --- a/volatility3/framework/symbols/windows/callbacks-x86.json +++ b/volatility3/framework/symbols/windows/callbacks-x86.json @@ -170,7 +170,7 @@ }, "offset": 28 }, - "Alltitude": { + "Altitude": { "type": { "kind": "struct", "name": "nt_symbols!_UNICODE_STRING" From 0dc0b8ca4019b8401b5478e25949ee2647caebb1 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 28 Apr 2022 00:12:41 +0100 Subject: [PATCH 11/27] Core: Bump the API correctly for an addition (to 2.1.0) --- API_CHANGES.md | 2 +- volatility3/framework/constants/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/API_CHANGES.md b/API_CHANGES.md index b3962f072..03fcc010c 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -4,7 +4,7 @@ API Changes When an addition to the existing API is made, the minor version is bumped. When an API feature or function is removed or changed, the major version is bumped. -2.0.4 +2.1.0 ===== Add in the linux `task.get_threads` method added to rhe API. diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index ddc96bf31..5060906d5 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -39,8 +39,8 @@ BANG = "!" # We use the SemVer 2.0.0 versioning scheme VERSION_MAJOR = 2 # Number of releases of the library with a breaking change -VERSION_MINOR = 0 # Number of changes that only add to the interface -VERSION_PATCH = 4 # Number of changes that do not change the interface +VERSION_MINOR = 1 # Number of changes that only add to the interface +VERSION_PATCH = 0 # Number of changes that do not change the interface VERSION_SUFFIX = "" # TODO: At version 2.0.0, remove the symbol_shift feature From a236bdd60047702dea18aed0170dcc2efc4a0cc2 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 28 Apr 2022 08:26:52 +0900 Subject: [PATCH 12/27] Fix typo for API_CHANGES.md --- API_CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API_CHANGES.md b/API_CHANGES.md index 03fcc010c..4e1820eff 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -6,7 +6,7 @@ When an API feature or function is removed or changed, the major version is bump 2.1.0 ===== -Add in the linux `task.get_threads` method added to rhe API. +Add in the linux `task.get_threads` method added to the API. 2.0.3 ===== From 0a3c6297823dd0e49d19864efc9962f3bcef6075 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 28 Apr 2022 10:14:49 +1000 Subject: [PATCH 13/27] Updated to use task's is_thread_group_leader function --- volatility3/framework/plugins/linux/pstree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/pstree.py b/volatility3/framework/plugins/linux/pstree.py index 174e94ec4..a44310147 100644 --- a/volatility3/framework/plugins/linux/pstree.py +++ b/volatility3/framework/plugins/linux/pstree.py @@ -25,7 +25,7 @@ class PsTree(pslist.PsList): level = 0 proc = self._tasks.get(pid) while proc and proc.parent and proc.parent.pid not in seen: - if self.task_is_thread_group_leader(proc): + if proc.is_thread_group_leader: parent_pid = proc.parent.pid else: parent_pid = proc.tgid From 192759b5d5c8c2622059847fbd915faf8af66e50 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 28 Apr 2022 10:17:39 +1000 Subject: [PATCH 14/27] Updated to use the new task::get_threads --- volatility3/framework/plugins/linux/pslist.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index 4c55f853c..a196810aa 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -137,20 +137,10 @@ class PsList(interfaces.plugins.PluginInterface): if filter_func(task): continue - task_threads = [] - current_task = None - next_task = task.thread_group.next - while current_task is None or current_task.vol.offset != task.vol.offset: - current_task = linux.LinuxUtilities.container_of(next_task, "task_struct", "thread_group", vmlinux) - if current_task.is_thread_group_leader: - # Making sure the first task yielded is the Task Group Leader - yield current_task - elif include_threads: - task_threads.append(current_task) - next_task = current_task.thread_group.next + yield task - # yield the other task threads - yield from task_threads + if include_threads: + yield from task.get_threads() def run(self): pids = self.config.get('pid') From 5f879649162f571eb3d5931bbbffac58b5a9e47b Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 28 Apr 2022 15:41:29 +0100 Subject: [PATCH 15/27] Linux: Make sure the pslist offset column says that it's virtual --- volatility3/framework/plugins/linux/pslist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index dd1832576..0a9090bd4 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -85,4 +85,4 @@ class PsList(interfaces.plugins.PluginInterface): yield task def run(self): - return renderers.TreeGrid([("OFFSET", format_hints.Hex), ("COMM", str), ("PID", int), ("PPID", int)], self._generator()) + return renderers.TreeGrid([("OFFSET (V)", format_hints.Hex), ("COMM", str), ("PID", int), ("PPID", int)], self._generator()) From e1b8a0b5cc99bcc3257eb698bb77004148471ab4 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 06:42:37 +1000 Subject: [PATCH 16/27] container_of() is no longer needed here --- volatility3/framework/symbols/linux/__init__.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 4df97954d..8d7f00e06 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -266,13 +266,4 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): while list_start: list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset) yield list_struct - list_start = getattr(list_struct, list_member) - - @classmethod - def container_of(cls, addr, type_name, member_name, vmlinux): - if not addr: - return - type_dec = vmlinux.get_type(type_name) - member_offset = type_dec.relative_child_offset(member_name) - container_addr = addr - member_offset - return vmlinux.object(object_type=type_name, offset=container_addr, absolute=True) \ No newline at end of file + list_start = getattr(list_struct, list_member) \ No newline at end of file From 9aba96a1970e5a469df529e17de836c0c2a9e19b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 06:46:58 +1000 Subject: [PATCH 17/27] Remove unused import module --- volatility3/framework/plugins/linux/pslist.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index a196810aa..45d364224 100644 --- a/volatility3/framework/plugins/linux/pslist.py +++ b/volatility3/framework/plugins/linux/pslist.py @@ -6,7 +6,6 @@ from typing import Callable, Iterable, List, Any, Tuple from volatility3.framework import renderers, interfaces from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility -from volatility3.framework.symbols import linux class PsList(interfaces.plugins.PluginInterface): From 818002aba49fe8b3ee852a627a6fc13277acb987 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 30 Apr 2022 15:13:34 +0100 Subject: [PATCH 18/27] Codebase: Fix LGTM issues --- volatility3/cli/text_renderer.py | 5 +---- volatility3/framework/configuration/requirements.py | 6 +++--- volatility3/framework/layers/qemu.py | 3 --- volatility3/framework/plugins/windows/ldrmodules.py | 1 - 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 1ddfcca84..08608a3d0 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -322,10 +322,7 @@ class PrettyTextRenderer(CLIRenderer): tab_width = 8 while line.find('\t') >= 0: i = line.find('\t') - if (tab_width > 0): - pad = " " * (tab_width - (i % tab_width)) - else: - pad = "" + pad = " " * (tab_width - (i % tab_width)) line = line.replace("\t", pad, 1) return line diff --git a/volatility3/framework/configuration/requirements.py b/volatility3/framework/configuration/requirements.py index 746b72226..4edc6d17c 100644 --- a/volatility3/framework/configuration/requirements.py +++ b/volatility3/framework/configuration/requirements.py @@ -470,14 +470,14 @@ class ModuleRequirement(interfaces.configuration.ConstructableRequirementInterfa if req_unsatisfied: result.update(req_unsatisfied) if not result: + vollog.log(constants.LOGLEVEL_V, f"IndexError - No configuration provided: {config_path}") result = {config_path: self} - return result ### NOTE: This validate method has side effects (the dependencies can change)!!! self._validate_class(context, interfaces.configuration.parent_path(config_path)) - vollog.log(constants.LOGLEVEL_V, f"IndexError - No configuration provided: {config_path}") - return {config_path: self} + + return result def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None: """Constructs the appropriate layer and adds it based on the class parameter.""" diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index df383a04a..f1ba1e468 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -3,7 +3,6 @@ # import functools import json -import math from typing import Optional, Dict, Any, Tuple, List, Set from volatility3.framework import interfaces, exceptions, constants @@ -131,8 +130,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): index = 8 section_info = dict() current_section_id = -1 - version_id = -1 - name = None while section_byte != self.QEVM_EOF and index <= base_layer.maximum_address: section_byte = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned char', offset = index, diff --git a/volatility3/framework/plugins/windows/ldrmodules.py b/volatility3/framework/plugins/windows/ldrmodules.py index 42eeacd4d..e7c96e946 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -1,7 +1,6 @@ from volatility3.framework import interfaces, constants from volatility3.framework import renderers, interfaces, exceptions from volatility3.framework.configuration import requirements -from volatility3.framework.objects import utility from volatility3.framework.renderers import format_hints from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows.extensions import pe From 8bd7daf28fdbaf95dcfce1cfaa4e25517d24cd71 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 2 May 2022 09:33:41 +0300 Subject: [PATCH 19/27] fix minor improvments --- volatility3/framework/interfaces/objects.py | 10 +--------- volatility3/framework/interfaces/symbols.py | 2 +- volatility3/framework/symbols/windows/__init__.py | 8 ++++---- .../framework/symbols/windows/extensions/__init__.py | 6 +++++- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/volatility3/framework/interfaces/objects.py b/volatility3/framework/interfaces/objects.py index 8c7167a78..98ceca3d0 100644 --- a/volatility3/framework/interfaces/objects.py +++ b/volatility3/framework/interfaces/objects.py @@ -116,14 +116,6 @@ class ObjectInterface(metaclass = abc.ABCMeta): normalized_offset = object_info.offset & mask vol_info_dict = {'type_name': type_name, 'offset': normalized_offset} - if constants.BANG in type_name: - table_name, struct_name = type_name.split(constants.BANG) - vol_info_dict["table_name"] = table_name - vol_info_dict["short_name"] = struct_name - else: - vol_info_dict["table_name"] = "" - vol_info_dict["short_name"] = type_name - self._vol = collections.ChainMap({}, vol_info_dict, object_info, kwargs) self._context = context @@ -151,7 +143,7 @@ class ObjectInterface(metaclass = abc.ABCMeta): """ if constants.BANG not in self.vol.type_name: raise ValueError(f"Unable to determine table for symbol: {self.vol.type_name}") - table_name = self.vol.table_name + table_name = self.vol.type_name[:self.vol.type_name.index(constants.BANG)] if table_name not in self._context.symbol_space: raise KeyError(f"Symbol table not found in context's symbol_space for symbol: {self.vol.type_name}") return table_name diff --git a/volatility3/framework/interfaces/symbols.py b/volatility3/framework/interfaces/symbols.py index 99690054d..9f2cb9fc9 100644 --- a/volatility3/framework/interfaces/symbols.py +++ b/volatility3/framework/interfaces/symbols.py @@ -167,7 +167,7 @@ class BaseSymbolTableInterface: """ raise NotImplementedError("Abstract method set_type_class not implemented yet.") - def try_set_type_class(self, name: str, clazz: Type[objects.ObjectInterface]) -> bool: + def optional_set_type_class(self, name: str, clazz: Type[objects.ObjectInterface]) -> bool: """Calls the set_type_class function but does not throw an exception. Returns whether setting the type class was successfull. Args: diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index 468d998c4..b5129bb04 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -42,7 +42,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('_IMAGE_NT_HEADERS', pe.IMAGE_NT_HEADERS) # Might not exist in 32-bit operating systems. - self.try_set_type_class('_IMAGE_NT_HEADERS64', pe.IMAGE_NT_HEADERS) + self.optional_set_type_class('_IMAGE_NT_HEADERS64', pe.IMAGE_NT_HEADERS) # This doesn't exist in very specific versions of windows try: @@ -54,11 +54,11 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): pass # these don't exist in windows XP - self.try_set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT) + self.optional_set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT) # these were introduced starting in windows 8 - self.try_set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT) + self.optional_set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT) # these were introduced starting in windows 7 - self.try_set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT) + self.optional_set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT) \ No newline at end of file diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 2f0f2388c..e7da0316d 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -576,7 +576,11 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): raise exceptions.InvalidAddressException(proc_layer_name, self.Peb, f"Invalid Peb address at {self.Peb:0x}") - return self.at_layer(proc_layer_name).Peb + sym_table = self.get_symbol_table_name() + peb = self._context.object(f"{sym_table}{constants.BANG}_PEB", + layer_name = proc_layer_name, + offset = self.Peb) + return peb def load_order_modules(self) -> Iterable[interfaces.objects.ObjectInterface]: """Generator for DLLs in the order that they were loaded.""" From bb1e41f59be2444a4efeb7f81043f3ed214211fa Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 2 May 2022 09:36:50 +0300 Subject: [PATCH 20/27] remove at_layer --- volatility3/framework/interfaces/objects.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/volatility3/framework/interfaces/objects.py b/volatility3/framework/interfaces/objects.py index 98ceca3d0..2240c58c9 100644 --- a/volatility3/framework/interfaces/objects.py +++ b/volatility3/framework/interfaces/objects.py @@ -170,11 +170,6 @@ class ObjectInterface(metaclass = abc.ABCMeta): size = object_template.size) return object_template(context = self._context, object_info = object_info) - def at_layer(self, new_layer_name) -> 'ObjectInterface': - """Returns the same object casted at a different layer. - """ - return self._context.object(self.vol.type_name, offset=self.vol.offset, layer_name=new_layer_name) - def has_member(self, member_name: str) -> bool: """Returns whether the object would contain a member called member_name. From 14c11b24f6af44f627bc276c76b2b53d852724fa Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 2 May 2022 14:33:35 +0100 Subject: [PATCH 21/27] Symbols: Make _IMAGE_NT_HEADERS optional since not all Windows versions contain it --- volatility3/framework/symbols/windows/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index b5129bb04..899b89dc2 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -39,9 +39,9 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('_VACB', extensions.VACB) self.set_type_class('_POOL_TRACKER_BIG_PAGES', pool.POOL_TRACKER_BIG_PAGES) self.set_type_class('_IMAGE_DOS_HEADER', pe.IMAGE_DOS_HEADER) - self.set_type_class('_IMAGE_NT_HEADERS', pe.IMAGE_NT_HEADERS) - # Might not exist in 32-bit operating systems. + # Might not necessarily defined in every version of windows + self.optional_set_type_class('_IMAGE_NT_HEADERS', pe.IMAGE_NT_HEADERS) self.optional_set_type_class('_IMAGE_NT_HEADERS64', pe.IMAGE_NT_HEADERS) # This doesn't exist in very specific versions of windows From 6b7d5b63fe6d3fbef6fdcf339dc5c6b9426129e8 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 6 May 2022 00:49:03 +0900 Subject: [PATCH 22/27] Add: venv environments, memory dump for .gitignore --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index c6da33754..5986612ea 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,16 @@ config*.json # Pyinstaller files build dist + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Memory dump files +*.dmp +*.vmem From 16313c593989a9d5c42afa430194bfe248766c80 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 6 May 2022 01:18:31 +0900 Subject: [PATCH 23/27] Fix: typo for dlllist --- volatility3/framework/plugins/windows/dlllist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index b24f2c0ca..2fd7deeaf 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -65,7 +65,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): try: name = dll_entry.FullDllName.get_string() except exceptions.InvalidAddressException: - name = 'UnreadbleDLLName' + name = 'UnreadableDLLName' if layer_name is None: layer_name = dll_entry.vol.layer_name From 8b6194f8f4231ce8304366f2e985ca7e47f71e4e Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sat, 7 May 2022 18:16:43 +0900 Subject: [PATCH 24/27] Remove: environment .bak on .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5986612ea..d26e17d91 100644 --- a/.gitignore +++ b/.gitignore @@ -34,8 +34,6 @@ dist env/ venv/ ENV/ -env.bak/ -venv.bak/ # Memory dump files *.dmp From 8f369180e4c1dcb8fafcc618b78450bcf36b7bb2 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sun, 8 May 2022 18:33:38 +0900 Subject: [PATCH 25/27] Fix: typo for documents --- doc/source/symbol-tables.rst | 2 +- doc/source/volshell.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/symbol-tables.rst b/doc/source/symbol-tables.rst index 245dd9c67..4dea6077d 100644 --- a/doc/source/symbol-tables.rst +++ b/doc/source/symbol-tables.rst @@ -63,7 +63,7 @@ To determine the string for a particular memory image, use the `banners` plugin. try to locate that exact kernel debugging package for the operating system. Unfortunately each distribution provides its debugging packages under different package names and there are so many that the distribution may not keep all old versions of the debugging symbols, and therefore **it may not be possible to find the right symbols to analyze a linux -memory image with volatlity**. With Macs there are far fewer kernels and only one distribution, making it easier to +memory image with volatility**. With Macs there are far fewer kernels and only one distribution, making it easier to ensure that the right symbols can be found. Once a kernel with debugging symbols/appropriate DWARF file has been located, `dwarf2json `_ will convert it into an diff --git a/doc/source/volshell.rst b/doc/source/volshell.rst index de3c4398a..5a4b21ade 100644 --- a/doc/source/volshell.rst +++ b/doc/source/volshell.rst @@ -110,7 +110,7 @@ This means that pointers do not need to be explicitly dereferenced to access und Running plugins --------------- -It's possible to run any plugin by importing it appropriately and passing it to the `display_plugin_ouptut` or `dpo` +It's possible to run any plugin by importing it appropriately and passing it to the `display_plugin_output` or `dpo` method. In the following example we'll provide no additional parameters. Volatility will show us which parameters were required: From fde05cd1f2d7a35dd1949988f667d7f97d8e9459 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 9 May 2022 11:23:13 +0900 Subject: [PATCH 26/27] Fix: typo for cli exception message --- volatility3/cli/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 488892d37..e3fb726a1 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -423,7 +423,7 @@ class CommandLine: detail = f"{excp}" caused_by = ["A required python module is not installed (install the module and re-run)"] else: - general = "Volatilty encountered an unexpected situation." + general = "Volatility encountered an unexpected situation." detail = "" caused_by = [ "Please re-run using with -vvv and file a bug with the output", f"at {constants.BUG_URL}" From f54edee36203d8537bf6716a577799bd9184bb1c Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 9 May 2022 10:56:40 +0300 Subject: [PATCH 27/27] removed svcscan import --- volatility3/framework/plugins/windows/callbacks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index dca17aff7..2195671df 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -11,7 +11,6 @@ from volatility3.framework.renderers import format_hints from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows import versions from volatility3.plugins.windows import ssdt -from volatility3.plugins.windows import svcscan vollog = logging.getLogger(__name__) @@ -28,7 +27,6 @@ class Callbacks(interfaces.plugins.PluginInterface): requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', architectures = ["Intel32", "Intel64"]), requirements.PluginRequirement(name = 'ssdt', plugin = ssdt.SSDT, version = (1, 0, 0)), - requirements.PluginRequirement(name = 'svcscan', plugin = svcscan.SvcScan, version = (1, 0, 0)) ] @staticmethod