diff --git a/.gitignore b/.gitignore index c6da33754..d26e17d91 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,14 @@ config*.json # Pyinstaller files build dist + +# Environments +.env +.venv +env/ +venv/ +ENV/ + +# Memory dump files +*.dmp +*.vmem diff --git a/API_CHANGES.md b/API_CHANGES.md index b3962f072..4e1820eff 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -4,9 +4,9 @@ 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. +Add in the linux `task.get_threads` method added to the API. 2.0.3 ===== 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: 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}" 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/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index c25ea0e2f..c0f85593f 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -11,3 +11,6 @@ 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)""" + +# include/linux/sched.h +PF_KTHREAD = 0x00200000 # I'm a kernel thread diff --git a/volatility3/framework/interfaces/objects.py b/volatility3/framework/interfaces/objects.py index e589abd15..2240c58c9 100644 --- a/volatility3/framework/interfaces/objects.py +++ b/volatility3/framework/interfaces/objects.py @@ -115,7 +115,8 @@ 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} + self._vol = collections.ChainMap({}, vol_info_dict, object_info, kwargs) self._context = context def __getattr__(self, attr: str) -> Any: @@ -156,7 +157,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() diff --git a/volatility3/framework/interfaces/symbols.py b/volatility3/framework/interfaces/symbols.py index b271de412..9f2cb9fc9 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 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: + 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/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/linux/pslist.py b/volatility3/framework/plugins/linux/pslist.py index 5672bb56e..f9a1fe2a0 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.configuration import requirements from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints 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,76 @@ class PsList(interfaces.plugins.PluginInterface): else: return lambda _: False - def _generator(self): + 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 task.is_kernel_thread: + name = f"[{name}]" + elif task.is_user_thread: + name = f"{{{name}}}" + + task_fields = (format_hints.Hex(task.vol.offset), 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 +134,19 @@ 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 + + yield task + + if include_threads: + yield from task.get_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 = [("OFFSET (V)", format_hints.Hex), ("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..a44310147 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 proc.is_thread_group_leader: + 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/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 352dba448..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 @@ -111,30 +109,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 +142,62 @@ 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"Altitude: {callback.Altitude.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) + + 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: + 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 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/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 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 diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 5b0bfcf40..0c5ce395c 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -267,4 +267,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) + list_start = getattr(list_struct, list_member) \ No newline at end of file diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 89c20fc0c..6792ab19c 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 + def get_threads(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns a list of the task_struct based on the list_head thread_node structure.""" diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index f09dadedf..899b89dc2 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) + + # 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 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.optional_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.optional_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.optional_set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT) + \ No newline at end of file diff --git a/volatility3/framework/symbols/windows/callbacks-x64.json b/volatility3/framework/symbols/windows/callbacks-x64.json index dbb6086df..87682cb92 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 + }, + "Altitude": { + "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..702b68a65 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 + }, + "Altitude": { + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + }, + "offset": 32 + } + }, + "kind": "struct", + "size": 40 } }, "metadata": { diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 7d083fbba..e7da0316d 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -574,9 +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] + 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)