From 92c7b3e5500b03fa68d8893204b31f50cef7b4dc Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 9 Dec 2022 15:37:20 +0000 Subject: [PATCH 01/39] add linux envars --- volatility3/framework/plugins/linux/envars.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 volatility3/framework/plugins/linux/envars.py diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py new file mode 100644 index 000000000..b62400c45 --- /dev/null +++ b/volatility3/framework/plugins/linux/envars.py @@ -0,0 +1,110 @@ +# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import logging + +from volatility3.framework import exceptions, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.interfaces import plugins +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +vollog = logging.getLogger(__name__) + +class Envars(plugins.PluginInterface): + """Lists processes with their environment variables""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.PluginRequirement( + name="pslist", plugin=pslist.PsList, version=(2, 0, 0) + ), + requirements.ListRequirement( + name="pid", + description="Filter on specific process IDs", + element_type=int, + optional=True, + ), + ] + + def _generator(self, tasks): + """Generates a listing of processes along with environment variables""" + + # walk the process list and return the envars + for task in tasks: + pid = task.pid + + # get process name as string + name = utility.array_to_string(task.comm) + + # try and get task parent + try: + ppid = task.parent.pid + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read parent pid for task {pid} {name}, setting ppid to 0.") + ppid = 0 + + # kernel threads never have an mm as they do not have userland mappings + try: + mm = task.mm + except exceptions.InvalidAddressException: + # no mm so cannot get envars + vollog.debug(f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars.") + mm = None + continue + + # if mm exists attempt to get envars + if mm: + + # get process layer to read envars from + proc_layer_name = task.add_process_layer() + if proc_layer_name is None: + vollog.debug(f"Unable to construct process layer for task {pid} {name}, will not extract any envars.") + continue + proc_layer = self.context.layers[proc_layer_name] + + + # get the size of the envars with sanity checking + envars_size = task.mm.env_end - task.mm.env_start + if not (0 < envars_size <= 8192): + vollog.debug(f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars.") + continue + + # attempt to read all envars data + try: + envar_data = proc_layer.read(task.mm.env_start, envars_size) + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars.") + continue + + # parse envar data, envars are null terminated, keys and values are separated by '=' + envar_data = envar_data.rstrip(b'\x00') + for envar_pair in envar_data.split(b'\x00'): + try: + key, value = envar_pair.decode().split('=', 1) + except ValueError: + vollog.debug(f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated") + continue + yield (0, (pid, ppid, name, key, value)) + + def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + + return renderers.TreeGrid( + [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], + self._generator( + pslist.PsList.list_tasks( + self.context, self.config["kernel"], filter_func=filter_func + ) + ), + ) From a553a69efde143183c0580910dcc089cf0e060ed Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 21 Dec 2022 06:42:02 +0000 Subject: [PATCH 02/39] fix linting issues --- volatility3/framework/plugins/linux/envars.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index b62400c45..028eb2a57 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -12,6 +12,7 @@ from volatility3.plugins.linux import pslist vollog = logging.getLogger(__name__) + class Envars(plugins.PluginInterface): """Lists processes with their environment variables""" @@ -51,7 +52,9 @@ class Envars(plugins.PluginInterface): try: ppid = task.parent.pid except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read parent pid for task {pid} {name}, setting ppid to 0.") + vollog.debug( + f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." + ) ppid = 0 # kernel threads never have an mm as they do not have userland mappings @@ -59,7 +62,9 @@ class Envars(plugins.PluginInterface): mm = task.mm except exceptions.InvalidAddressException: # no mm so cannot get envars - vollog.debug(f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars.") + vollog.debug( + f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." + ) mm = None continue @@ -69,31 +74,38 @@ class Envars(plugins.PluginInterface): # get process layer to read envars from proc_layer_name = task.add_process_layer() if proc_layer_name is None: - vollog.debug(f"Unable to construct process layer for task {pid} {name}, will not extract any envars.") + vollog.debug( + f"Unable to construct process layer for task {pid} {name}, will not extract any envars." + ) continue proc_layer = self.context.layers[proc_layer_name] - # get the size of the envars with sanity checking envars_size = task.mm.env_end - task.mm.env_start if not (0 < envars_size <= 8192): - vollog.debug(f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars.") + vollog.debug( + f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." + ) continue # attempt to read all envars data try: envar_data = proc_layer.read(task.mm.env_start, envars_size) except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars.") + vollog.debug( + f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." + ) continue # parse envar data, envars are null terminated, keys and values are separated by '=' - envar_data = envar_data.rstrip(b'\x00') - for envar_pair in envar_data.split(b'\x00'): + envar_data = envar_data.rstrip(b"\x00") + for envar_pair in envar_data.split(b"\x00"): try: - key, value = envar_pair.decode().split('=', 1) + key, value = envar_pair.decode().split("=", 1) except ValueError: - vollog.debug(f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated") + vollog.debug( + f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" + ) continue yield (0, (pid, ppid, name, key, value)) From c1e425217bf8ea0e4b62a56ddaff5db29d87b4f0 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 5 Jan 2023 10:20:19 +0000 Subject: [PATCH 03/39] Initial canonical helper addition The intel 64-bit 4-page paging mechanism allows for 48-bit virtual addresses. They introduced a convention that the higher bits must be set a particular way to avoid operating system developers abusing those bits and creating problems that would be difficult to resolve in the future. Volatility requires that addresses for mapping or translation fit within the available bounds of the virtual address space. This unfortunately means that addresses that have the protections against abuse in place can may live outside this range. This provides two function (canonicalize and decanonicalize) which will either set the appropriate sign extension or remove it. The decanonicalize function will return an adress outside of the address range if the original value was not canonical. --- volatility3/framework/layers/intel.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/volatility3/framework/layers/intel.py b/volatility3/framework/layers/intel.py index dce207fd5..ecfb6bf11 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -56,6 +56,11 @@ class Intel(linear.LinearlyMappedLayer): ) self._entry_size = struct.calcsize(self._entry_format) self._entry_number = self.page_size // self._entry_size + self._canonical_prefix = self._mask( + (1 << self._bits_per_register) - 1, + self._bits_per_register, + self._maxvirtaddr, + ) # These can vary depending on the type of space self._index_shift = int( @@ -106,6 +111,23 @@ class Intel(linear.LinearlyMappedLayer): """Returns whether a particular page is valid based on its entry.""" return bool(entry & 1) + def canonicalize(self, addr: int) -> int: + """Canonicalizes an address by performing an appropiate sign extension on the higher addresses""" + if self._bits_per_register <= self._maxvirtaddr: + return addr & self.address_mask + elif addr < (1 << self._maxvirtaddr - 1): + return addr + return self._mask(addr, self._maxvirtaddr, 0) + self._canonical_prefix + + def decanonicalize(self, addr: int) -> int: + """Removes canonicalization to ensure an adress fits within the correct range if it has been canonicalized + + This will produce an address outside the range if the canonicalization is incorrect + """ + if addr < (1 << self._maxvirtaddr - 1): + return addr + return addr ^ self._canonical_prefix + def _translate(self, offset: int) -> Tuple[int, int, str]: """Translates a specific offset based on paging tables. From 0163f0b9e67258d2a433766d0027ffc25d0b6d07 Mon Sep 17 00:00:00 2001 From: Eve Date: Thu, 5 Jan 2023 12:17:36 +0000 Subject: [PATCH 04/39] add linux.iomem plugin based on vol2 plugin by atcuno --- volatility3/framework/plugins/linux/iomem.py | 139 +++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 volatility3/framework/plugins/linux/iomem.py diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py new file mode 100644 index 000000000..6b0469d60 --- /dev/null +++ b/volatility3/framework/plugins/linux/iomem.py @@ -0,0 +1,139 @@ +# This file is Copyright 2023 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +import logging +from typing import List + +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 + +vollog = logging.getLogger(__name__) + + +class IOMem(interfaces.plugins.PluginInterface): + """Generates an output similar to /proc/iomem on a running system.""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ) + ] + + @classmethod + def parse_resource( + cls, + context: interfaces.context.ContextInterface, + vmlinux_module_name: str, + resource_offset: int, + seen: set = set(), + depth: int = 0, + ): + """Recursively parse from a root resource to find details about all related resources. + + 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 + resource_offset: The offset to the resouce to be parsed + seen: The set of resource offsets that have already been parsed + depth: How deep into the resource structure we are + + Yields: + Each row of output + """ + # create the resource object + vmlinux = context.modules[vmlinux_module_name] + resource = vmlinux.object("resource", resource_offset) + + # extract the information required for this resource + name = utility.pointer_to_string(resource.name, 128) + start = format_hints.Hex(resource.start) + end = format_hints.Hex(resource.end) + + # mark this resource as seen in the seen set. Normally this should not be needed but will protect + # against possible infinite loops. Warn the user if an infinite loop would have happened. + if resource_offset in seen: + vollog.warning( + f"The resource object at {resource_offset:#x} '{name}' has already been processed, " + "this should not normally occur. No further results from related resources will be " + "displayed to protect against infinite loops." + ) + return None + else: + seen.add(resource_offset) + + # yield information on this resource + yield depth, (name, start, end) + + # process child resource if this exists + if resource.child != 0: + yield from cls.parse_resource( + context, + vmlinux_module_name, + resource.child, + seen, + depth + 1, + ) + + # process sibling resource if this exists + if resource.sibling != 0: + yield from cls.parse_resource( + context, + vmlinux_module_name, + resource.sibling, + seen, + depth, + ) + + def _generator(self): + """Generates an output similar to /proc/iomem on a running system + + Args: + None + + Yields: + Each row of output using the parse_resource function + """ + + # get the kernel module from the current context + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + + # check that the iomem_resource symbol exists + # normally exported in /kernel/resource.c + try: + iomem_root_offset = vmlinux.get_absolute_symbol_address("iomem_resource") + except exceptions.SymbolError: + iomem_root_offset = None + + # error if 'iomem_resource' is not found + if not iomem_root_offset: + raise TypeError( + "This plugin requires the iomem_resource structure. This structure is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + ) + + # error if type 'resource' is not found + if not vmlinux.has_type("resource"): + raise TypeError( + "This plugin requires the resource type. This type is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + ) + + # recursively parse the resources starting from the root resource at 'iomem_resource' + yield from self.parse_resource( + self.context, vmlinux_module_name, iomem_root_offset + ) + + def run(self): + columns = [ + ("NAME", str), + ("START", format_hints.Hex), + ("END", format_hints.Hex), + ] + return renderers.TreeGrid(columns, self._generator()) From bd291c43d5bc31409cb6ac920f6135c29b63c58f Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 10:15:36 +0200 Subject: [PATCH 05/39] added debug message --- volatility3/framework/symbols/windows/pdbutil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 1c3260fed..74fd0e4e8 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -54,6 +54,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface): """ result = cls.get_guid_from_mz(context, layer_name, offset) if result is None: + vollog.debug(f"Could not get GUID for {hex(offset)}") return None guid, age, pdb_name = result if config_path is None: From 5aca49388eb83d20316d24add538dc79d8e34dee Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 12:01:45 +0200 Subject: [PATCH 06/39] fix smearing --- volatility3/framework/plugins/windows/callbacks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 3bde95cf3..98e09d925 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,10 @@ 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"Altitude: {callback.Altitude.String}" + altitude = "-" + with contextlib.suppress(exceptions.InvalidAddressException): + altitude = callback.Altitude.String + yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {altitude}" @classmethod def list_registry_callbacks( From 5e33a98481e019083f1f4a41d8a145e2e31742e0 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 12:39:08 +0200 Subject: [PATCH 07/39] change default value to None --- volatility3/framework/plugins/windows/callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 98e09d925..a1283d394 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,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"): - altitude = "-" + altitude = None with contextlib.suppress(exceptions.InvalidAddressException): altitude = callback.Altitude.String yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {altitude}" From 4cafc982f4972a8dace64589a0adcd7a02518d83 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 16 Jan 2023 10:47:09 +0000 Subject: [PATCH 08/39] Windows: Fix up callbacks typos and typing info --- volatility3/framework/plugins/windows/callbacks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index a1283d394..6898935fc 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -82,7 +82,7 @@ class Callbacks(interfaces.plugins.PluginInterface): 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 + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -182,7 +182,7 @@ class Callbacks(interfaces.plugins.PluginInterface): layer_name: str, symbol_table: str, callback_table_name: str, - ) -> Iterable[Tuple[str, int, None]]: + ) -> Iterable[Tuple[str, int, Optional[str]]]: """ Lists all registry callbacks via the CallbackListHead. """ @@ -215,14 +215,14 @@ class Callbacks(interfaces.plugins.PluginInterface): layer_name: str, symbol_table: str, callback_table_name: str, - ) -> Iterable[Tuple[str, int, None]]: + ) -> Iterable[Tuple[str, int, Optional[str]]]: """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 + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -272,7 +272,7 @@ class Callbacks(interfaces.plugins.PluginInterface): 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 + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -330,7 +330,7 @@ class Callbacks(interfaces.plugins.PluginInterface): 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 + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string From f036acdeb8181a0cec6f4a73611080c8e14b5349 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 13:02:20 +0200 Subject: [PATCH 09/39] missing import --- volatility3/framework/plugins/windows/callbacks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 6898935fc..56609a73a 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -3,6 +3,7 @@ # import logging +import contextlib from typing import List, Iterable, Tuple, Optional, Union from volatility3.framework import constants, exceptions, renderers, interfaces, symbols From e16887414e97cb9b2ef414a9ce15071fa9ac18f4 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 17 Jan 2023 10:15:03 +0200 Subject: [PATCH 10/39] add escapechar --- volatility3/cli/text_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 5df378d08..bb0f41ca3 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,7 @@ class CSVRenderer(CLIRenderer): # Ignore the type because namedtuples don't realize they have accessible attributes header_list.append(f"{column.name}") - writer = csv.DictWriter(outfd, header_list, lineterminator="\n") + writer = csv.DictWriter(outfd, header_list, lineterminator="\n", escapechar='\\') writer.writeheader() def visitor(node: interfaces.renderers.TreeNode, accumulator): From 728e8b608ab59e4fa43353f4f2dea9f378b0e06a Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 17 Jan 2023 16:11:08 +0200 Subject: [PATCH 11/39] black reformat --- volatility3/cli/text_renderer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index bb0f41ca3..7e2167b6b 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,9 @@ class CSVRenderer(CLIRenderer): # Ignore the type because namedtuples don't realize they have accessible attributes header_list.append(f"{column.name}") - writer = csv.DictWriter(outfd, header_list, lineterminator="\n", escapechar='\\') + writer = csv.DictWriter( + outfd, header_list, lineterminator="\n", escapechar="\\" + ) writer.writeheader() def visitor(node: interfaces.renderers.TreeNode, accumulator): From 1d4aa72c85c07f4bdad05de8abe1ecc8c053e760 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 27 Jan 2023 11:07:05 +0000 Subject: [PATCH 12/39] update linux.iomem with basic smear protection --- volatility3/framework/plugins/linux/iomem.py | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 6b0469d60..08a61bb46 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -48,15 +48,32 @@ class IOMem(interfaces.plugins.PluginInterface): Yields: Each row of output """ - # create the resource object vmlinux = context.modules[vmlinux_module_name] - resource = vmlinux.object("resource", resource_offset) + + # create the resource object with protection against memory smear + try: + resource = vmlinux.object("resource", resource_offset) + except exceptions.InvalidAddressException: + vollog.warning( + f"Unable to create resource object at {resource_offset:#x}. This resource, " + "its sibling, and any of it's childern and will be missing from the output." + ) + return None # extract the information required for this resource - name = utility.pointer_to_string(resource.name, 128) start = format_hints.Hex(resource.start) end = format_hints.Hex(resource.end) + # get name with protection against smear as following a pointer + try: + name = utility.pointer_to_string(resource.name, 128) + except exceptions.InvalidAddressException: + vollog.warning( + "Unable to follow pointer to name for resource object at {resource_offset:#x}, " + "replaced with UnreadableValue" + ) + name = renderers.UnreadableValue() + # mark this resource as seen in the seen set. Normally this should not be needed but will protect # against possible infinite loops. Warn the user if an infinite loop would have happened. if resource_offset in seen: From c2a1afb0ba8305b34bed11defdd21b9d83b76deb Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 29 Jan 2023 12:42:17 +0000 Subject: [PATCH 13/39] Core: Update codeql action to only run once a week --- .github/workflows/codeql.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 078af2abf..b9300251a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,11 +12,6 @@ name: "CodeQL" on: - push: - branches: [ "develop" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "develop" ] schedule: - cron: '16 8 * * 0' From 3297ba02e7cd2d24dfabac35c24996f36ca86891 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 29 Jan 2023 12:46:06 +0000 Subject: [PATCH 14/39] Core: Rather than scheduling it daily, only do it on commits --- .github/workflows/codeql.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b9300251a..fa9bd7ef6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,8 +12,13 @@ name: "CodeQL" on: - schedule: - - cron: '16 8 * * 0' + push: + branches: [ "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "develop" ] +# schedule: +# - cron: '16 8 * * 0' jobs: analyze: From b375c6f71d0a90d8b3d632edbde9d25f9c72c266 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 1 Feb 2023 09:43:02 +0000 Subject: [PATCH 15/39] Update linux.iomem --- volatility3/framework/plugins/linux/iomem.py | 44 +++++++++++--------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 08a61bb46..fddea4668 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -16,6 +16,7 @@ class IOMem(interfaces.plugins.PluginInterface): """Generates an output similar to /proc/iomem on a running system.""" _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -60,10 +61,6 @@ class IOMem(interfaces.plugins.PluginInterface): ) return None - # extract the information required for this resource - start = format_hints.Hex(resource.start) - end = format_hints.Hex(resource.end) - # get name with protection against smear as following a pointer try: name = utility.pointer_to_string(resource.name, 128) @@ -87,7 +84,7 @@ class IOMem(interfaces.plugins.PluginInterface): seen.add(resource_offset) # yield information on this resource - yield depth, (name, start, end) + yield depth, (name, resource.start, resource.end) # process child resource if this exists if resource.child != 0: @@ -123,17 +120,32 @@ class IOMem(interfaces.plugins.PluginInterface): vmlinux_module_name = self.config["kernel"] vmlinux = self.context.modules[vmlinux_module_name] - # check that the iomem_resource symbol exists - # normally exported in /kernel/resource.c + # get the address for the iomem_resource try: iomem_root_offset = vmlinux.get_absolute_symbol_address("iomem_resource") except exceptions.SymbolError: iomem_root_offset = None - # error if 'iomem_resource' is not found - if not iomem_root_offset: + # only continue if iomem_root address was located + if iomem_root_offset is not None: + + # recursively parse the resources starting from the root resource at 'iomem_resource' + for depth, (name, start, end) in self.parse_resource( + self.context, vmlinux_module_name, iomem_root_offset + ): + # use format_hints to format start and end addresses for the renderers + yield depth, (name, format_hints.Hex(start), format_hints.Hex(end)) + + def run(self): + # get the kernel module from the current context + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + + # check that the iomem_resource symbol exists + # normally exported in /kernel/resource.c + if not vmlinux.has_symbol("iomem_resource"): raise TypeError( - "This plugin requires the iomem_resource structure. This structure is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + "This plugin requires the iomem_resource symbol. This symbol is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." ) # error if type 'resource' is not found @@ -142,15 +154,9 @@ class IOMem(interfaces.plugins.PluginInterface): "This plugin requires the resource type. This type is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." ) - # recursively parse the resources starting from the root resource at 'iomem_resource' - yield from self.parse_resource( - self.context, vmlinux_module_name, iomem_root_offset - ) - - def run(self): columns = [ - ("NAME", str), - ("START", format_hints.Hex), - ("END", format_hints.Hex), + ("Name", str), + ("Start", format_hints.Hex), + ("End", format_hints.Hex), ] return renderers.TreeGrid(columns, self._generator()) From 43a17384c32da91d711f9f186a3036bab43a3954 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 3 Feb 2023 00:35:49 +0000 Subject: [PATCH 16/39] Core: Update to black 23.1.0 which removes many blank lines and parentheses --- volatility3/cli/__init__.py | 2 +- volatility3/cli/text_renderer.py | 2 +- volatility3/cli/volargparse.py | 1 - volatility3/framework/automagic/construct_layers.py | 1 - volatility3/framework/automagic/mac.py | 1 - volatility3/framework/automagic/symbol_cache.py | 3 ++- volatility3/framework/automagic/symbol_finder.py | 4 ++-- volatility3/framework/interfaces/layers.py | 5 ++--- volatility3/framework/layers/avml.py | 2 +- volatility3/framework/layers/crash.py | 1 - volatility3/framework/layers/intel.py | 2 +- volatility3/framework/layers/leechcore.py | 1 - volatility3/framework/layers/linear.py | 4 ++-- volatility3/framework/layers/registry.py | 1 - volatility3/framework/plugins/linux/check_afinfo.py | 4 +--- volatility3/framework/plugins/linux/check_creds.py | 3 +-- volatility3/framework/plugins/linux/check_modules.py | 2 -- volatility3/framework/plugins/linux/check_syscall.py | 7 +++---- volatility3/framework/plugins/linux/lsmod.py | 1 - volatility3/framework/plugins/linux/lsof.py | 1 - volatility3/framework/plugins/linux/mountinfo.py | 1 - volatility3/framework/plugins/linux/tty_check.py | 2 -- volatility3/framework/plugins/mac/check_syscall.py | 2 +- volatility3/framework/plugins/mac/kauth_listeners.py | 1 - volatility3/framework/plugins/mac/kauth_scopes.py | 1 - volatility3/framework/plugins/mac/kevents.py | 1 - volatility3/framework/plugins/mac/list_files.py | 3 --- volatility3/framework/plugins/mac/lsmod.py | 2 -- volatility3/framework/plugins/mac/netstat.py | 2 -- volatility3/framework/plugins/mac/pslist.py | 1 - volatility3/framework/plugins/timeliner.py | 2 +- volatility3/framework/plugins/windows/bigpools.py | 1 - volatility3/framework/plugins/windows/cachedump.py | 1 - volatility3/framework/plugins/windows/callbacks.py | 5 ----- volatility3/framework/plugins/windows/devicetree.py | 2 +- volatility3/framework/plugins/windows/dlllist.py | 2 -- volatility3/framework/plugins/windows/driverirp.py | 2 -- volatility3/framework/plugins/windows/drivermodule.py | 1 - volatility3/framework/plugins/windows/driverscan.py | 1 - volatility3/framework/plugins/windows/dumpfiles.py | 1 - volatility3/framework/plugins/windows/envars.py | 1 - volatility3/framework/plugins/windows/filescan.py | 2 -- volatility3/framework/plugins/windows/getservicesids.py | 1 - volatility3/framework/plugins/windows/getsids.py | 3 --- volatility3/framework/plugins/windows/handles.py | 8 ++------ volatility3/framework/plugins/windows/hashdump.py | 1 - volatility3/framework/plugins/windows/info.py | 3 --- volatility3/framework/plugins/windows/joblinks.py | 2 +- volatility3/framework/plugins/windows/ldrmodules.py | 1 - volatility3/framework/plugins/windows/lsadump.py | 6 ------ volatility3/framework/plugins/windows/malfind.py | 1 - volatility3/framework/plugins/windows/mbrscan.py | 2 -- volatility3/framework/plugins/windows/modscan.py | 3 --- volatility3/framework/plugins/windows/modules.py | 1 - volatility3/framework/plugins/windows/mutantscan.py | 2 -- volatility3/framework/plugins/windows/netscan.py | 2 -- volatility3/framework/plugins/windows/netstat.py | 2 -- volatility3/framework/plugins/windows/poolscanner.py | 2 -- volatility3/framework/plugins/windows/privileges.py | 2 -- volatility3/framework/plugins/windows/pslist.py | 1 - volatility3/framework/plugins/windows/psscan.py | 2 -- .../framework/plugins/windows/registry/hivelist.py | 1 - .../framework/plugins/windows/registry/hivescan.py | 1 - .../framework/plugins/windows/registry/printkey.py | 4 +--- .../framework/plugins/windows/registry/userassist.py | 2 -- volatility3/framework/plugins/windows/sessions.py | 2 -- .../framework/plugins/windows/skeleton_key_check.py | 2 -- volatility3/framework/plugins/windows/ssdt.py | 3 --- volatility3/framework/plugins/windows/svcscan.py | 2 -- volatility3/framework/plugins/windows/symlinkscan.py | 2 -- volatility3/framework/plugins/windows/vadinfo.py | 1 - volatility3/framework/plugins/windows/verinfo.py | 1 - volatility3/framework/plugins/windows/virtmap.py | 2 +- volatility3/framework/renderers/__init__.py | 4 ++-- volatility3/framework/renderers/format_hints.py | 1 - volatility3/framework/symbols/__init__.py | 2 +- volatility3/framework/symbols/linux/__init__.py | 4 +--- .../framework/symbols/linux/extensions/__init__.py | 3 --- volatility3/framework/symbols/linux/extensions/elf.py | 1 - volatility3/framework/symbols/mac/__init__.py | 4 ---- volatility3/framework/symbols/mac/extensions/__init__.py | 4 ++-- .../framework/symbols/windows/extensions/__init__.py | 8 -------- .../framework/symbols/windows/extensions/network.py | 4 ---- volatility3/framework/symbols/windows/extensions/pe.py | 1 - volatility3/framework/symbols/windows/pdbutil.py | 4 +--- volatility3/plugins/windows/registry/certificates.py | 1 - 86 files changed, 32 insertions(+), 162 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index fb124a3c9..336902d50 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -443,7 +443,7 @@ class CommandLine: # Construct and run the plugin if constructed: renderers[args.renderer]().render(constructed.run()) - except (exceptions.VolatilityException) as excp: + except exceptions.VolatilityException as excp: self.process_exceptions(excp) @classmethod diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 5df378d08..4df04e2a3 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -346,7 +346,7 @@ class PrettyTextRenderer(CLIRenderer): column_titles = [""] + [column.name for column in grid.columns] outfd.write(format_string.format(*column_titles)) - for (depth, line) in final_output: + for depth, line in final_output: nums_line = max([len(line[column]) for column in line]) for column in line: line[column] = line[column] + ([""] * (nums_line - len(line[column]))) diff --git a/volatility3/cli/volargparse.py b/volatility3/cli/volargparse.py index dd89a64fd..3048a0885 100644 --- a/volatility3/cli/volargparse.py +++ b/volatility3/cli/volargparse.py @@ -31,7 +31,6 @@ class HelpfulSubparserAction(argparse._SubParsersAction): values: Union[str, Sequence[Any], None], option_string: Optional[str] = None, ) -> None: - parser_name = "" arg_strings = [] # type: List[str] if values is not None: diff --git a/volatility3/framework/automagic/construct_layers.py b/volatility3/framework/automagic/construct_layers.py index ceed2fe50..239f0cfb6 100644 --- a/volatility3/framework/automagic/construct_layers.py +++ b/volatility3/framework/automagic/construct_layers.py @@ -36,7 +36,6 @@ class ConstructionMagic(interfaces.automagic.AutomagicInterface): progress_callback=None, optional=False, ) -> List[str]: - # Make sure we import the layers, so they can reconstructed framework.import_files(sys.modules["volatility3.framework.layers"]) diff --git a/volatility3/framework/automagic/mac.py b/volatility3/framework/automagic/mac.py index 3ca0b4ea2..aa75fbc3d 100644 --- a/volatility3/framework/automagic/mac.py +++ b/volatility3/framework/automagic/mac.py @@ -251,7 +251,6 @@ class MacIntelStacker(interfaces.automagic.StackerLayerInterface): context=context, progress_callback=progress_callback, ): - banner = context.layers[layer_name].read(offset, 128) idx = banner.find(b"\x00") diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 44a76506c..63c6fc7fa 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -161,7 +161,8 @@ class CacheManagerInterface(interfaces.configuration.VersionableInterface): """Returns ISF statistics based on the location Returns: - A tuple of base_types, types, enums, symbols, or None is location not found""" + A tuple of base_types, types, enums, symbols, or None is location not found + """ def get_hash(self, location: str) -> Optional[str]: """Returns the hash of the JSON from within a location ISF""" diff --git a/volatility3/framework/automagic/symbol_finder.py b/volatility3/framework/automagic/symbol_finder.py index 0143e74b1..f30dff456 100644 --- a/volatility3/framework/automagic/symbol_finder.py +++ b/volatility3/framework/automagic/symbol_finder.py @@ -82,13 +82,13 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface): shortcut=False, ) - for (sub_path, requirement) in self._requirements: + for sub_path, requirement in self._requirements: parent_path = interfaces.configuration.parent_path(sub_path) if isinstance( requirement, requirements.SymbolTableRequirement ) and requirement.unsatisfied(context, parent_path): - for (tl_sub_path, tl_requirement) in self._requirements: + for tl_sub_path, tl_requirement in self._requirements: tl_parent_path = interfaces.configuration.parent_path(tl_sub_path) # Find the TranslationLayer sibling to the SymbolTableRequirement if ( diff --git a/volatility3/framework/interfaces/layers.py b/volatility3/framework/interfaces/layers.py index a3c31a953..68592f8cb 100644 --- a/volatility3/framework/interfaces/layers.py +++ b/volatility3/framework/interfaces/layers.py @@ -294,7 +294,7 @@ class DataLayerInterface( sections.""" result: List[Tuple[int, int]] = [] position = 0 - for (start, length) in sorted(sections): + for start, length in sorted(sections): if result and start <= position: initial_start, _ = result.pop() result.append((initial_start, (start + length) - initial_start)) @@ -375,7 +375,6 @@ class DataLayerInterface( def _scan_metric( self, _scanner: "ScannerInterface", sections: List[Tuple[int, int]] ) -> Callable[[int], float]: - if not sections: raise ValueError("Sections have no size, nothing to scan") last_section, last_length = sections[-1] @@ -551,7 +550,7 @@ class TranslationLayerInterface(DataLayerInterface, metaclass=ABCMeta): scanner.chunk_size + scanner.overlap DataLayers by default are assumed to have no holes """ - for (section_start, section_length) in sections: + for section_start, section_length in sections: output: List[Tuple[str, int, int]] = [] # Hold the offsets of each chunk (including how much has been filled) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index b12fdd01c..66f3f0e4f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -73,7 +73,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ) segments, consumed = self._read_snappy_frames(chunk_data, end - start) # The returned segments are accurate the chunk_data that was passed in, but needs shifting - for (thing, mapped_offset, size, mapped_size, compressed) in segments: + for thing, mapped_offset, size, mapped_size, compressed in segments: self._segments.append( ( thing + start, diff --git a/volatility3/framework/layers/crash.py b/volatility3/framework/layers/crash.py index 64166cfba..8efd4f7c7 100644 --- a/volatility3/framework/layers/crash.py +++ b/volatility3/framework/layers/crash.py @@ -39,7 +39,6 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer): def __init__( self, context: interfaces.context.ContextInterface, config_path: str, name: str ) -> None: - # Construct these so we can use self.config self._context = context self._config_path = config_path diff --git a/volatility3/framework/layers/intel.py b/volatility3/framework/layers/intel.py index ecfb6bf11..478eb168f 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -173,7 +173,7 @@ class Intel(linear.LinearlyMappedLayer): ) # Run through the offset in various chunks - for (name, size, large_page) in self._structure: + for name, size, large_page in self._structure: # Check we're valid if not self._page_is_valid(entry): raise exceptions.PagedInvalidAddressException( diff --git a/volatility3/framework/layers/leechcore.py b/volatility3/framework/layers/leechcore.py index 73700dd3c..542fd6ca2 100644 --- a/volatility3/framework/layers/leechcore.py +++ b/volatility3/framework/layers/leechcore.py @@ -91,7 +91,6 @@ if HAS_LEECHCORE: chunk_size = size output = [] for entry in self.handle.memmap: - if ( entry["base"] + entry["size"] <= chunk_start or entry["base"] >= chunk_start + chunk_size diff --git a/volatility3/framework/layers/linear.py b/volatility3/framework/layers/linear.py index 19203eb66..47170df7b 100644 --- a/volatility3/framework/layers/linear.py +++ b/volatility3/framework/layers/linear.py @@ -42,7 +42,7 @@ class LinearlyMappedLayer(interfaces.layers.TranslationLayerInterface): length size.""" current_offset = offset output: List[bytes] = [] - for (offset, _, mapped_offset, mapped_length, layer) in self.mapping( + for offset, _, mapped_offset, mapped_length, layer in self.mapping( offset, length, ignore_errors=pad ): if not pad and offset > current_offset: @@ -71,7 +71,7 @@ class LinearlyMappedLayer(interfaces.layers.TranslationLayerInterface): underlying mapping.""" current_offset = offset length = len(value) - for (offset, _, mapped_offset, length, layer) in self.mapping(offset, length): + for offset, _, mapped_offset, length, layer in self.mapping(offset, length): if offset > current_offset: raise exceptions.InvalidAddressException( self.name, diff --git a/volatility3/framework/layers/registry.py b/volatility3/framework/layers/registry.py index 660e0a299..cc8ce1f4c 100644 --- a/volatility3/framework/layers/registry.py +++ b/volatility3/framework/layers/registry.py @@ -269,7 +269,6 @@ class RegistryHive(linear.LinearlyMappedLayer): def mapping( self, offset: int, length: int, ignore_errors: bool = False ) -> Iterable[Tuple[int, int, int, int, str]]: - if length < 0: raise ValueError("Mapping length of RegistryHive must be positive or zero") diff --git a/volatility3/framework/plugins/linux/check_afinfo.py b/volatility3/framework/plugins/linux/check_afinfo.py index c177ee642..90e714eaa 100644 --- a/volatility3/framework/plugins/linux/check_afinfo.py +++ b/volatility3/framework/plugins/linux/check_afinfo.py @@ -68,7 +68,6 @@ class Check_afinfo(plugins.PluginInterface): yield var_name, "show", var.seq_show def _generator(self): - vmlinux = self.context.modules[self.config["kernel"]] op_members = vmlinux.get_type("file_operations").members @@ -86,7 +85,7 @@ class Check_afinfo(plugins.PluginInterface): ) protocols = [tcp, udp] - for (struct_type, global_vars) in protocols: + for struct_type, global_vars in protocols: for global_var_name in global_vars: # this will lookup fail for the IPv6 protocols on kernels without IPv6 support try: @@ -104,7 +103,6 @@ class Check_afinfo(plugins.PluginInterface): yield 0, (name, member, format_hints.Hex(address)) def run(self): - return renderers.TreeGrid( [ ("Symbol Name", str), diff --git a/volatility3/framework/plugins/linux/check_creds.py b/volatility3/framework/plugins/linux/check_creds.py index 6d4e2bc8a..ab6ee4935 100644 --- a/volatility3/framework/plugins/linux/check_creds.py +++ b/volatility3/framework/plugins/linux/check_creds.py @@ -46,7 +46,6 @@ class Check_creds(interfaces.plugins.PluginInterface): tasks = pslist.PsList.list_tasks(self.context, vmlinux.name) for task in tasks: - cred_addr = task.cred.dereference().vol.offset if cred_addr not in creds: @@ -54,7 +53,7 @@ class Check_creds(interfaces.plugins.PluginInterface): creds[cred_addr].append(task.pid) - for (_, pids) in creds.items(): + for _, pids in creds.items(): if len(pids) > 1: pid_str = "" for pid in pids: diff --git a/volatility3/framework/plugins/linux/check_modules.py b/volatility3/framework/plugins/linux/check_modules.py index 766858888..9b3594c5e 100644 --- a/volatility3/framework/plugins/linux/check_modules.py +++ b/volatility3/framework/plugins/linux/check_modules.py @@ -37,7 +37,6 @@ class Check_modules(plugins.PluginInterface): def get_kset_modules( cls, context: interfaces.context.ContextInterface, vmlinux_name: str ): - vmlinux = context.modules[vmlinux_name] try: @@ -57,7 +56,6 @@ class Check_modules(plugins.PluginInterface): for kobj in module_kset.list.to_list( vmlinux.symbol_table_name + constants.BANG + "kobject", "entry" ): - mod_kobj = vmlinux.object( object_type="module_kobject", offset=kobj.vol.offset - kobj_off, diff --git a/volatility3/framework/plugins/linux/check_syscall.py b/volatility3/framework/plugins/linux/check_syscall.py index 6b11038ec..b1d2919f9 100644 --- a/volatility3/framework/plugins/linux/check_syscall.py +++ b/volatility3/framework/plugins/linux/check_syscall.py @@ -110,7 +110,7 @@ class Check_syscall(plugins.PluginInterface): vmlinux = self.context.modules[self.config["kernel"]] data = self.context.layers.read(vmlinux.layer_name, func_addr, 6) - for (address, size, mnemonic, op_str) in md.disasm_lite(data, func_addr): + for address, size, mnemonic, op_str in md.disasm_lite(data, func_addr): if mnemonic == "CMP": table_size = int(op_str.split(",")[1].strip()) & 0xFFFF break @@ -161,7 +161,7 @@ class Check_syscall(plugins.PluginInterface): ia32_info = self._get_table_info(vmlinux, "ia32_sys_call_table", ptr_sz) tables.append(("32bit", ia32_info)) - for (table_name, (tableaddr, tblsz)) in tables: + for table_name, (tableaddr, tblsz) in tables: table = vmlinux.object( object_type="array", subtype=vmlinux.get_type("pointer"), @@ -169,7 +169,7 @@ class Check_syscall(plugins.PluginInterface): count=tblsz, ) - for (i, call_addr) in enumerate(table): + for i, call_addr in enumerate(table): if not call_addr: continue @@ -196,7 +196,6 @@ class Check_syscall(plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Table Address", format_hints.Hex), diff --git a/volatility3/framework/plugins/linux/lsmod.py b/volatility3/framework/plugins/linux/lsmod.py index 1c1e094c3..a65b0d00b 100644 --- a/volatility3/framework/plugins/linux/lsmod.py +++ b/volatility3/framework/plugins/linux/lsmod.py @@ -60,7 +60,6 @@ class Lsmod(plugins.PluginInterface): def _generator(self): try: for module in self.list_modules(self.context, self.config["kernel"]): - mod_size = module.get_init_size() + module.get_core_size() mod_name = utility.array_to_string(module.name) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 62bade1f1..d970ad8a9 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -52,7 +52,6 @@ class Lsof(plugins.PluginInterface): symbol_table: str, filter_func: Callable[[int], bool] = lambda _: False, ): - linuxutils_symbol_table = None # type: ignore for task in pslist.PsList.list_tasks(context, symbol_table, filter_func): if linuxutils_symbol_table is None: diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index c849d51c6..ebd6e55a0 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -203,7 +203,6 @@ class MountInfo(plugins.PluginInterface): mount_format: bool, per_namespace: bool, ) -> Iterable[Tuple[int, Tuple]]: - for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, per_namespace): if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids: continue diff --git a/volatility3/framework/plugins/linux/tty_check.py b/volatility3/framework/plugins/linux/tty_check.py index dcc9f3e06..45238ef8c 100644 --- a/volatility3/framework/plugins/linux/tty_check.py +++ b/volatility3/framework/plugins/linux/tty_check.py @@ -61,7 +61,6 @@ class tty_check(plugins.PluginInterface): for tty in tty_drivers.to_list( vmlinux.symbol_table_name + constants.BANG + "tty_driver", "tty_drivers" ): - try: ttys = utility.array_of_pointers( tty.ttys.dereference(), @@ -73,7 +72,6 @@ class tty_check(plugins.PluginInterface): continue for tty_dev in ttys: - if tty_dev == 0: continue diff --git a/volatility3/framework/plugins/mac/check_syscall.py b/volatility3/framework/plugins/mac/check_syscall.py index a7a32e9ab..5c22e6463 100644 --- a/volatility3/framework/plugins/mac/check_syscall.py +++ b/volatility3/framework/plugins/mac/check_syscall.py @@ -47,7 +47,7 @@ class Check_syscall(plugins.PluginInterface): table = kernel.object_from_symbol(symbol_name="sysent") - for (i, ent) in enumerate(table): + for i, ent in enumerate(table): try: call_addr = ent.sy_call.dereference().vol.offset except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/mac/kauth_listeners.py b/volatility3/framework/plugins/mac/kauth_listeners.py index 0b945a8fd..ed43bfb42 100644 --- a/volatility3/framework/plugins/mac/kauth_listeners.py +++ b/volatility3/framework/plugins/mac/kauth_listeners.py @@ -49,7 +49,6 @@ class Kauth_listeners(interfaces.plugins.PluginInterface): for scope in kauth_scopes.Kauth_scopes.list_kauth_scopes( self.context, self.config["kernel"] ): - scope_name = utility.pointer_to_string(scope.ks_identifier, 128) for listener in scope.get_listeners(): diff --git a/volatility3/framework/plugins/mac/kauth_scopes.py b/volatility3/framework/plugins/mac/kauth_scopes.py index bfd7216a8..afb320a07 100644 --- a/volatility3/framework/plugins/mac/kauth_scopes.py +++ b/volatility3/framework/plugins/mac/kauth_scopes.py @@ -65,7 +65,6 @@ class Kauth_scopes(interfaces.plugins.PluginInterface): ) for scope in self.list_kauth_scopes(self.context, self.config["kernel"]): - callback = scope.ks_callback if callback == 0: continue diff --git a/volatility3/framework/plugins/mac/kevents.py b/volatility3/framework/plugins/mac/kevents.py index 74c5f6037..3b996bc0a 100644 --- a/volatility3/framework/plugins/mac/kevents.py +++ b/volatility3/framework/plugins/mac/kevents.py @@ -184,7 +184,6 @@ class Kevents(interfaces.plugins.PluginInterface): for task_name, pid, kn in self.list_kernel_events( self.context, self.config["kernel"], filter_func=filter_func ): - filter_index = kn.kn_kevent.filter * -1 if filter_index in self.event_types: filter_name = self.event_types[filter_index] diff --git a/volatility3/framework/plugins/mac/list_files.py b/volatility3/framework/plugins/mac/list_files.py index ede0fa32b..c18b0b7a2 100644 --- a/volatility3/framework/plugins/mac/list_files.py +++ b/volatility3/framework/plugins/mac/list_files.py @@ -137,7 +137,6 @@ class List_Files(plugins.PluginInterface): def _walk_mounts( cls, context: interfaces.context.ContextInterface, kernel_module_name: str ) -> Iterable[interfaces.objects.ObjectInterface]: - loop_vnodes = {} # iterate each vnode source from each mount @@ -186,7 +185,6 @@ class List_Files(plugins.PluginInterface): def list_files( cls, context: interfaces.context.ContextInterface, kernel_module_name: str ) -> Iterable[interfaces.objects.ObjectInterface]: - vnodes = cls._walk_mounts(context, kernel_module_name) for voff, (vnode_name, parent_offset, vnode) in vnodes.items(): @@ -196,7 +194,6 @@ class List_Files(plugins.PluginInterface): def _generator(self): for vnode, full_path in self.list_files(self.context, self.config["kernel"]): - yield (0, (format_hints.Hex(vnode.vol.offset), full_path)) def run(self): diff --git a/volatility3/framework/plugins/mac/lsmod.py b/volatility3/framework/plugins/mac/lsmod.py index 2cdd5e3de..2979e374b 100644 --- a/volatility3/framework/plugins/mac/lsmod.py +++ b/volatility3/framework/plugins/mac/lsmod.py @@ -63,7 +63,6 @@ class Lsmod(plugins.PluginInterface): seen: Set = set() while kmod != 0 and kmod not in seen and len(seen) < 1024: - kmod_obj = kmod.dereference() if not kernel_layer.is_valid(kmod_obj.vol.offset, kmod_obj.vol.size): @@ -81,7 +80,6 @@ class Lsmod(plugins.PluginInterface): def _generator(self): for module in self.list_modules(self.context, self.config["kernel"]): - mod_name = utility.array_to_string(module.name) mod_size = module.size diff --git a/volatility3/framework/plugins/mac/netstat.py b/volatility3/framework/plugins/mac/netstat.py index 581a9c67f..76bba25f6 100644 --- a/volatility3/framework/plugins/mac/netstat.py +++ b/volatility3/framework/plugins/mac/netstat.py @@ -68,7 +68,6 @@ class Netstat(plugins.PluginInterface): # This is hardcoded, since a change in the default method would change the expected results list_tasks = pslist.PsList.get_list_tasks(pslist.PsList.pslist_methods[0]) for task in list_tasks(context, kernel_module_name, filter_func): - task_name = utility.array_to_string(task.p_comm) pid = task.p_pid @@ -101,7 +100,6 @@ class Netstat(plugins.PluginInterface): for task_name, pid, socket in self.list_sockets( self.context, self.config["kernel"], filter_func=filter_func ): - family = socket.get_family() if family == 1: diff --git a/volatility3/framework/plugins/mac/pslist.py b/volatility3/framework/plugins/mac/pslist.py index c2ae71e7e..1d97216bf 100644 --- a/volatility3/framework/plugins/mac/pslist.py +++ b/volatility3/framework/plugins/mac/pslist.py @@ -83,7 +83,6 @@ class PsList(interfaces.plugins.PluginInterface): @classmethod def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]: - filter_func = lambda _: False # FIXME: mypy #4973 or #2608 pid_list = pid_list or [] diff --git a/volatility3/framework/plugins/timeliner.py b/volatility3/framework/plugins/timeliner.py index 0776b6cc8..d1c1c0f70 100644 --- a/volatility3/framework/plugins/timeliner.py +++ b/volatility3/framework/plugins/timeliner.py @@ -136,7 +136,7 @@ class Timeliner(interfaces.plugins.PluginInterface): ) try: vollog.log(logging.INFO, f"Running {plugin_name}") - for (item, timestamp_type, timestamp) in plugin.generate_timeline(): + for item, timestamp_type, timestamp in plugin.generate_timeline(): times = self.timeline.get((plugin_name, item), {}) if times.get(timestamp_type, None) is not None: vollog.debug( diff --git a/volatility3/framework/plugins/windows/bigpools.py b/volatility3/framework/plugins/windows/bigpools.py index 1a51a0b81..393c2a417 100644 --- a/volatility3/framework/plugins/windows/bigpools.py +++ b/volatility3/framework/plugins/windows/bigpools.py @@ -141,7 +141,6 @@ class BigPools(interfaces.plugins.PluginInterface): tags=tags, show_free=self.config.get("show-free"), ): - num_bytes = big_pool.get_number_of_bytes() if not isinstance(num_bytes, interfaces.renderers.BaseAbsentValue): num_bytes = format_hints.Hex(num_bytes) diff --git a/volatility3/framework/plugins/windows/cachedump.py b/volatility3/framework/plugins/windows/cachedump.py index 7d3093ed7..a9b669add 100644 --- a/volatility3/framework/plugins/windows/cachedump.py +++ b/volatility3/framework/plugins/windows/cachedump.py @@ -173,7 +173,6 @@ class Cachedump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SECURITY": diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 56609a73a..48b2e7c62 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -104,7 +104,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ] for symbol_name, extended_list in symbol_names: - try: symbol_offset = ntkrnlmp.get_symbol(symbol_name).address except exceptions.SymbolError: @@ -354,7 +353,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ) for callback in callback_record.Entry: - if not context.layers[layer_name].is_valid(callback.CallbackRoutine, 64): continue @@ -372,7 +370,6 @@ class Callbacks(interfaces.plugins.PluginInterface): yield "KeBugCheckCallbackListHead", callback.CallbackRoutine, component def _generator(self): - kernel = self.context.modules[self.config["kernel"]] callback_table_name = self.create_callback_table( @@ -397,7 +394,6 @@ class Callbacks(interfaces.plugins.PluginInterface): kernel.symbol_table_name, callback_table_name, ): - if callback_detail is None: detail = renderers.NotApplicableValue() else: @@ -451,7 +447,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Type", str), diff --git a/volatility3/framework/plugins/windows/devicetree.py b/volatility3/framework/plugins/windows/devicetree.py index 2541629d5..6f39799c1 100644 --- a/volatility3/framework/plugins/windows/devicetree.py +++ b/volatility3/framework/plugins/windows/devicetree.py @@ -180,7 +180,7 @@ class DeviceTree(interfaces.plugins.PluginInterface): ), ) - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVVV, f"Invalid address identified in drivers and devices: {driver.vol.offset:x}", diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index c1593b836..d73cea652 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -129,12 +129,10 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): nt_major_version == 6 and nt_minor_version >= 1 ) for proc in procs: - proc_id = proc.UniqueProcessId proc_layer_name = proc.add_process_layer() for entry in proc.load_order_modules(): - BaseDllName = FullDllName = renderers.UnreadableValue() with contextlib.suppress(exceptions.InvalidAddressException): BaseDllName = entry.BaseDllName.get_string() diff --git a/volatility3/framework/plugins/windows/driverirp.py b/volatility3/framework/plugins/windows/driverirp.py index 4d2c24dea..b5cd33db7 100644 --- a/volatility3/framework/plugins/windows/driverirp.py +++ b/volatility3/framework/plugins/windows/driverirp.py @@ -71,7 +71,6 @@ class DriverIrp(interfaces.plugins.PluginInterface): for driver in driverscan.DriverScan.scan_drivers( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: driver_name = driver.get_driver_name() except (ValueError, exceptions.InvalidAddressException): @@ -113,7 +112,6 @@ class DriverIrp(interfaces.plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Offset", format_hints.Hex), diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py index cc735db30..de827602e 100644 --- a/volatility3/framework/plugins/windows/drivermodule.py +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -73,7 +73,6 @@ class DriverModule(interfaces.plugins.PluginInterface): ) def run(self) -> renderers.TreeGrid: - return renderers.TreeGrid( [ ("Offset", format_hints.Hex), diff --git a/volatility3/framework/plugins/windows/driverscan.py b/volatility3/framework/plugins/windows/driverscan.py index d8df80702..24d81c3d5 100644 --- a/volatility3/framework/plugins/windows/driverscan.py +++ b/volatility3/framework/plugins/windows/driverscan.py @@ -54,7 +54,6 @@ class DriverScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object diff --git a/volatility3/framework/plugins/windows/dumpfiles.py b/volatility3/framework/plugins/windows/dumpfiles.py index af9568897..38d55d15d 100755 --- a/volatility3/framework/plugins/windows/dumpfiles.py +++ b/volatility3/framework/plugins/windows/dumpfiles.py @@ -229,7 +229,6 @@ class DumpFiles(interfaces.plugins.PluginInterface): ) for proc in procs: - try: object_table = proc.ObjectTable except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/envars.py b/volatility3/framework/plugins/windows/envars.py index a1dbd7665..66db03c9c 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -221,7 +221,6 @@ class Envars(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/filescan.py b/volatility3/framework/plugins/windows/filescan.py index de3331e16..0f68f39d4 100644 --- a/volatility3/framework/plugins/windows/filescan.py +++ b/volatility3/framework/plugins/windows/filescan.py @@ -53,7 +53,6 @@ class FileScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -63,7 +62,6 @@ class FileScan(interfaces.plugins.PluginInterface): for fileobj in self.scan_files( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: file_name = fileobj.FileName.String except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index c4088426f..9b20ed2d0 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -73,7 +73,6 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): ] def _generator(self): - kernel = self.context.modules[self.config["kernel"]] # Get the system hive for hive in hivelist.HiveList.list_hives( diff --git a/volatility3/framework/plugins/windows/getsids.py b/volatility3/framework/plugins/windows/getsids.py index 2334a328d..3e332f85d 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -112,7 +112,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): filter_string="config\\software", hive_offsets=None, ): - try: for subkey in hive.get_key(key).get_subkeys(): sid = str(subkey.get_name()) @@ -165,7 +164,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): return sids def _generator(self, procs): - user_sids = self.lookup_user_sids() # Go all over the process list, get the token @@ -214,7 +212,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/handles.py b/volatility3/framework/plugins/windows/handles.py index 2f25a0597..dd7c90860 100644 --- a/volatility3/framework/plugins/windows/handles.py +++ b/volatility3/framework/plugins/windows/handles.py @@ -136,7 +136,6 @@ class Handles(interfaces.plugins.PluginInterface): """ if self._sar_value is None: - if not has_capstone: return None kernel = self.context.modules[self.config["kernel"]] @@ -160,7 +159,7 @@ class Handles(interfaces.plugins.PluginInterface): md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) - for (address, size, mnemonic, op_str) in md.disasm_lite( + for address, size, mnemonic, op_str in md.disasm_lite( data, kvo + func_addr ): # print("{} {} {} {}".format(address, size, mnemonic, op_str)) @@ -300,7 +299,6 @@ class Handles(interfaces.plugins.PluginInterface): masked_offset = offset & layer_object.maximum_address for entry in table: - if level > 0: for x in self._make_handle_array(entry, level - 1, depth): yield x @@ -329,7 +327,6 @@ class Handles(interfaces.plugins.PluginInterface): continue def handles(self, handle_table): - try: TableCode = handle_table.TableCode & ~self._level_mask table_levels = handle_table.TableCode & self._level_mask @@ -395,7 +392,7 @@ class Handles(interfaces.plugins.PluginInterface): except (ValueError, exceptions.InvalidAddressException): obj_name = "" - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVV, f"Cannot access _OBJECT_HEADER at {entry.vol.offset:#x}", @@ -416,7 +413,6 @@ class Handles(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index 72bea2c8b..0c98ab8ca 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -602,7 +602,6 @@ class Hashdump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SAM": diff --git a/volatility3/framework/plugins/windows/info.py b/volatility3/framework/plugins/windows/info.py index aa7837029..100a677c2 100644 --- a/volatility3/framework/plugins/windows/info.py +++ b/volatility3/framework/plugins/windows/info.py @@ -187,7 +187,6 @@ class Info(plugins.PluginInterface): return nt_header def _generator(self): - kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -215,7 +214,6 @@ class Info(plugins.PluginInterface): yield (0, (layer.name, f"{i} {layer.__class__.__name__}")) if kdbg.Header.OwnerTag == 0x4742444B: - yield (0, ("KdDebuggerDataBlock", hex(kdbg.vol.offset))) yield (0, ("NTBuildLab", kdbg.get_build_lab())) yield (0, ("CSDVersion", str(kdbg.get_csdversion()))) @@ -285,5 +283,4 @@ class Info(plugins.PluginInterface): ) def run(self): - return TreeGrid([("Variable", str), ("Value", str)], self._generator()) diff --git a/volatility3/framework/plugins/windows/joblinks.py b/volatility3/framework/plugins/windows/joblinks.py index 354ef31c9..d84c133c0 100644 --- a/volatility3/framework/plugins/windows/joblinks.py +++ b/volatility3/framework/plugins/windows/joblinks.py @@ -103,7 +103,7 @@ class JobLinks(interfaces.plugins.PluginInterface): ), ) - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: continue def run(self) -> renderers.TreeGrid: diff --git a/volatility3/framework/plugins/windows/ldrmodules.py b/volatility3/framework/plugins/windows/ldrmodules.py index a9b229048..9642810a5 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -33,7 +33,6 @@ class LdrModules(interfaces.plugins.PluginInterface): ] def _generator(self, procs): - pe_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, "windows", "pe", class_types=pe.class_types ) diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index 8cb239905..12589b07e 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -118,12 +118,10 @@ class Lsadump(interfaces.plugins.PluginInterface): if enc_secret_key: enc_secret_value = next(enc_secret_key.get_values()) if enc_secret_value: - enc_secret = sechive.read( enc_secret_value.Data + 4, enc_secret_value.DataLength ) if enc_secret: - if not is_vista_or_later: secret = cls.decrypt_secret(enc_secret[0xC:], lsakey) else: @@ -160,7 +158,6 @@ class Lsadump(interfaces.plugins.PluginInterface): def _generator( self, syshive: registry.RegistryHive, sechive: registry.RegistryHive ): - kernel = self.context.modules[self.config["kernel"]] vista_or_later = versions.is_vista_or_later( @@ -183,7 +180,6 @@ class Lsadump(interfaces.plugins.PluginInterface): return for key in secrets_key.get_subkeys(): - sec_val_key = hashdump.Hashdump.get_hive_key( sechive, "Policy\\Secrets\\" + key.get_key_path().split("\\")[3] + "\\CurrVal", @@ -208,7 +204,6 @@ class Lsadump(interfaces.plugins.PluginInterface): yield (0, (key.get_name(), secret.decode("latin1"), secret)) def run(self): - offset = self.config.get("offset", None) syshive = sechive = None kernel = self.context.modules[self.config["kernel"]] @@ -220,7 +215,6 @@ class Lsadump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SECURITY": diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index 1e7a009eb..424925955 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -151,7 +151,6 @@ class Malfind(interfaces.plugins.PluginInterface): for vad, data in self.list_injections( self.context, kernel.layer_name, kernel.symbol_table_name, proc ): - # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index ccf6eccea..e58ca8c24 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -99,7 +99,6 @@ class MBRScan(interfaces.plugins.PluginInterface): all_zeros = bootcode.count(b"\x00") == len(bootcode) if not all_zeros: - partition_entries = [ partition_table.FirstEntry, partition_table.SecondEntry, @@ -155,7 +154,6 @@ class MBRScan(interfaces.plugins.PluginInterface): for partition_index, partition_entry_object in enumerate( partition_entries, start=1 ): - if not self.config.get("full", True): yield ( 1, diff --git a/volatility3/framework/plugins/windows/modscan.py b/volatility3/framework/plugins/windows/modscan.py index bbd9a7b4a..99fadac07 100644 --- a/volatility3/framework/plugins/windows/modscan.py +++ b/volatility3/framework/plugins/windows/modscan.py @@ -70,7 +70,6 @@ class ModScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -175,7 +174,6 @@ class ModScan(interfaces.plugins.PluginInterface): for mod in self.scan_modules( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: BaseDllName = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: @@ -188,7 +186,6 @@ class ModScan(interfaces.plugins.PluginInterface): file_output = "Disabled" if self.config["dump"]: - session_layer_name = self.find_session_layer( self.context, session_layers, mod.DllBase ) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index eba6d1ce7..ff61c215c 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -53,7 +53,6 @@ class Modules(interfaces.plugins.PluginInterface): for mod in self.list_modules( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: BaseDllName = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/mutantscan.py b/volatility3/framework/plugins/windows/mutantscan.py index ad6e024d1..64d3b5470 100644 --- a/volatility3/framework/plugins/windows/mutantscan.py +++ b/volatility3/framework/plugins/windows/mutantscan.py @@ -53,7 +53,6 @@ class MutantScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -63,7 +62,6 @@ class MutantScan(interfaces.plugins.PluginInterface): for mutant in self.scan_mutants( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: name = mutant.get_name() except (ValueError, exceptions.InvalidAddressException): diff --git a/volatility3/framework/plugins/windows/netscan.py b/volatility3/framework/plugins/windows/netscan.py index 5c866bfeb..d0bbd5cbd 100644 --- a/volatility3/framework/plugins/windows/netscan.py +++ b/volatility3/framework/plugins/windows/netscan.py @@ -375,7 +375,6 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, nt_symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -394,7 +393,6 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, netscan_symbol_table, ): - vollog.debug( f"Found netw obj @ 0x{netw_obj.vol.offset:2x} of assumed type {type(netw_obj)}" ) diff --git a/volatility3/framework/plugins/windows/netstat.py b/volatility3/framework/plugins/windows/netstat.py index 1685f2a21..d3ce3fd2e 100644 --- a/volatility3/framework/plugins/windows/netstat.py +++ b/volatility3/framework/plugins/windows/netstat.py @@ -329,7 +329,6 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): alignment, net_symbol_table, ): - endpoint = context.object( obj_name, layer_name=layer_name, @@ -591,7 +590,6 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): tcpip_module.DllBase, tcpip_symbol_table, ): - # objects passed pool header constraints. check for additional constraints if strict flag is set. if not show_corrupt_results and not netw_obj.is_valid(): continue diff --git a/volatility3/framework/plugins/windows/poolscanner.py b/volatility3/framework/plugins/windows/poolscanner.py index 13c611bf8..e131c5f78 100644 --- a/volatility3/framework/plugins/windows/poolscanner.py +++ b/volatility3/framework/plugins/windows/poolscanner.py @@ -144,7 +144,6 @@ class PoolScanner(plugins.PluginInterface): ] def _generator(self): - kernel = self.context.modules[self.config["kernel"]] symbol_table = kernel.symbol_table_name @@ -367,7 +366,6 @@ class PoolScanner(plugins.PluginInterface): for constraint, header in cls.pool_scan( context, scan_layer, symbol_table, constraints, alignment=alignment ): - mem_objects = header.get_object( constraint=constraint, use_top_down=is_windows_8_or_later, diff --git a/volatility3/framework/plugins/windows/privileges.py b/volatility3/framework/plugins/windows/privileges.py index 7a7087c95..0370dfc92 100644 --- a/volatility3/framework/plugins/windows/privileges.py +++ b/volatility3/framework/plugins/windows/privileges.py @@ -66,7 +66,6 @@ class Privs(interfaces.plugins.PluginInterface): ] def _generator(self, procs): - for task in procs: try: process_token = task.Token.dereference().cast("_TOKEN") @@ -107,7 +106,6 @@ class Privs(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 7a06af36f..88697e71a 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -226,7 +226,6 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, filter_func=self.create_pid_filter(self.config.get("pid", None)), ): - if not self.config.get("physical", self.PHYSICAL_DEFAULT): offset = proc.vol.offset else: diff --git a/volatility3/framework/plugins/windows/psscan.py b/volatility3/framework/plugins/windows/psscan.py index 427814d22..3d9ae5c1e 100644 --- a/volatility3/framework/plugins/windows/psscan.py +++ b/volatility3/framework/plugins/windows/psscan.py @@ -87,7 +87,6 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result if not filter_func(mem_object): yield mem_object @@ -192,7 +191,6 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, filter_func=pslist.PsList.create_pid_filter(self.config.get("pid", None)), ): - file_output = "Disabled" if self.config["dump"]: # windows 10 objects (maybe others in the future) are already in virtual memory diff --git a/volatility3/framework/plugins/windows/registry/hivelist.py b/volatility3/framework/plugins/windows/registry/hivelist.py index 4abcd2f15..91798de40 100644 --- a/volatility3/framework/plugins/windows/registry/hivelist.py +++ b/volatility3/framework/plugins/windows/registry/hivelist.py @@ -88,7 +88,6 @@ class HiveList(interfaces.plugins.PluginInterface): symbol_table=kernel.symbol_table_name, filter_string=self.config.get("filter", None), ): - file_output = "Disabled" if self.config["dump"]: # Construct the hive diff --git a/volatility3/framework/plugins/windows/registry/hivescan.py b/volatility3/framework/plugins/windows/registry/hivescan.py index c3a52e303..7b3c0b622 100644 --- a/volatility3/framework/plugins/windows/registry/hivescan.py +++ b/volatility3/framework/plugins/windows/registry/hivescan.py @@ -86,7 +86,6 @@ class HiveScan(interfaces.plugins.PluginInterface): for hive in self.scan_hives( self.context, kernel.layer_name, kernel.symbol_table_name ): - yield (0, (format_hints.Hex(hive.vol.offset),)) def run(self): diff --git a/volatility3/framework/plugins/windows/registry/printkey.py b/volatility3/framework/plugins/windows/registry/printkey.py index 19527321e..537bfc943 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -241,7 +241,6 @@ class PrintKey(interfaces.plugins.PluginInterface): key: str = None, recurse: bool = False, ): - for hive in hivelist.HiveList.list_hives( self.context, self.config_path, @@ -249,14 +248,13 @@ class PrintKey(interfaces.plugins.PluginInterface): symbol_table=symbol_table, hive_offsets=hive_offsets, ): - try: # Walk it if key is not None: node_path = hive.get_key(key, return_list=True) else: node_path = [hive.get_node(hive.root_cell_offset)] - for (x, y) in self._printkey_iterator(hive, node_path, recurse=recurse): + for x, y in self._printkey_iterator(hive, node_path, recurse=recurse): yield (x - len(node_path), y) except ( exceptions.InvalidAddressException, diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index f64a130fa..f90724f66 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -248,7 +248,6 @@ class UserAssist(interfaces.plugins.PluginInterface): # output any values under Count for value in countkey.get_values(): - value_name = value.get_name() with contextlib.suppress(UnicodeDecodeError): value_name = codecs.encode(value_name, "rot_13") @@ -281,7 +280,6 @@ class UserAssist(interfaces.plugins.PluginInterface): yield result def _generator(self): - hive_offsets = None if self.config.get("offset", None) is not None: hive_offsets = [self.config.get("offset", None)] diff --git a/volatility3/framework/plugins/windows/sessions.py b/volatility3/framework/plugins/windows/sessions.py index 3e15878bd..d766b40ea 100644 --- a/volatility3/framework/plugins/windows/sessions.py +++ b/volatility3/framework/plugins/windows/sessions.py @@ -51,7 +51,6 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) kernel.symbol_table_name, filter_func=filter_func, ): - session_id = proc.get_session_id() # Detect RDP, Console or set default value @@ -112,7 +111,6 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) yield (description, timeliner.TimeLinerType.CREATED, row_data[5]) def run(self): - return renderers.TreeGrid( [ ("Session ID", int), diff --git a/volatility3/framework/plugins/windows/skeleton_key_check.py b/volatility3/framework/plugins/windows/skeleton_key_check.py index e7a1820e4..b697774cb 100644 --- a/volatility3/framework/plugins/windows/skeleton_key_check.py +++ b/volatility3/framework/plugins/windows/skeleton_key_check.py @@ -187,7 +187,6 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface): proc_layer_name: str, cryptdll_base: int, ) -> Tuple[interfaces.objects.ObjectInterface, int, int, int]: - """ Finds the CSystems array through use of PDB symbols @@ -574,7 +573,6 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface): scanners.BytesScanner(b"\x17\x00\x00\x00\x01\x00\x00\x00"), sections=[(cryptdll_base, cryptdll_size)], ): - # this occurs across page boundaries if not proc_layer.is_valid(address, ecrypt_size): continue diff --git a/volatility3/framework/plugins/windows/ssdt.py b/volatility3/framework/plugins/windows/ssdt.py index 184d8388c..6a47c36e9 100644 --- a/volatility3/framework/plugins/windows/ssdt.py +++ b/volatility3/framework/plugins/windows/ssdt.py @@ -56,7 +56,6 @@ class SSDT(plugins.PluginInterface): context_modules = [] for mod in mods: - try: module_name_with_ext = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: @@ -83,7 +82,6 @@ class SSDT(plugins.PluginInterface): return contexts.ModuleCollection(context_modules) def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]: - kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -132,7 +130,6 @@ class SSDT(plugins.PluginInterface): ) for idx, function_obj in enumerate(functions): - function = find_address(function_obj) module_symbols = collection.get_module_symbols_by_absolute_location( function diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index e6c1829e9..60562915e 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -180,7 +180,6 @@ class SvcScan(interfaces.plugins.PluginInterface): symbol_table=kernel.symbol_table_name, filter_func=filter_func, ): - proc_id = "Unknown" try: proc_id = task.UniqueProcessId @@ -200,7 +199,6 @@ class SvcScan(interfaces.plugins.PluginInterface): scanner=scanners.BytesScanner(needle=service_tag), sections=vadyarascan.VadYaraScan.get_vad_maps(task), ): - if not is_vista_or_later: service_record = self.context.object( service_table_name + constants.BANG + "_SERVICE_RECORD", diff --git a/volatility3/framework/plugins/windows/symlinkscan.py b/volatility3/framework/plugins/windows/symlinkscan.py index 78c2c6931..89fdf142e 100644 --- a/volatility3/framework/plugins/windows/symlinkscan.py +++ b/volatility3/framework/plugins/windows/symlinkscan.py @@ -52,7 +52,6 @@ class SymlinkScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfa for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -62,7 +61,6 @@ class SymlinkScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfa for link in self.scan_symlinks( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: from_name = link.get_link_name() except (ValueError, exceptions.InvalidAddressException): diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 3214c7134..812affe86 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -214,7 +214,6 @@ class VadInfo(interfaces.plugins.PluginInterface): process_name = utility.array_to_string(proc.ImageFileName) for vad in self.list_vads(proc, filter_func=filter_func): - file_output = "Disabled" if self.config["dump"]: file_handle = self.vad_dump( diff --git a/volatility3/framework/plugins/windows/verinfo.py b/volatility3/framework/plugins/windows/verinfo.py index fea4a0f80..1c6615804 100644 --- a/volatility3/framework/plugins/windows/verinfo.py +++ b/volatility3/framework/plugins/windows/verinfo.py @@ -222,7 +222,6 @@ class VerInfo(interfaces.plugins.PluginInterface): continue for entry in proc.load_order_modules(): - try: BaseDllName = entry.BaseDllName.get_string() except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/virtmap.py b/volatility3/framework/plugins/windows/virtmap.py index 6fbf13932..5190bec8d 100644 --- a/volatility3/framework/plugins/windows/virtmap.py +++ b/volatility3/framework/plugins/windows/virtmap.py @@ -31,7 +31,7 @@ class VirtMap(interfaces.plugins.PluginInterface): def _generator(self, map): for entry in sorted(map): - for (start, end) in map[entry]: + for start, end in map[entry]: yield (0, (entry, format_hints.Hex(start), format_hints.Hex(end))) @classmethod diff --git a/volatility3/framework/renderers/__init__.py b/volatility3/framework/renderers/__init__.py index ee87b3b85..534686022 100644 --- a/volatility3/framework/renderers/__init__.py +++ b/volatility3/framework/renderers/__init__.py @@ -181,7 +181,7 @@ class TreeGrid(interfaces.renderers.TreeGrid): converted_columns: List[interfaces.renderers.Column] = [] if len(columns) < 1: raise ValueError("Columns must be a list containing at least one column") - for (name, column_type) in columns: + for name, column_type in columns: is_simple_type = issubclass(column_type, self.base_types) if not is_simple_type: raise TypeError( @@ -238,7 +238,7 @@ class TreeGrid(interfaces.renderers.TreeGrid): if not self.populated: try: prev_nodes: List[interfaces.renderers.TreeNode] = [] - for (level, item) in self._generator: + for level, item in self._generator: parent_index = min(len(prev_nodes), level) parent = prev_nodes[parent_index - 1] if parent_index > 0 else None treenode = self._append(parent, item) diff --git a/volatility3/framework/renderers/format_hints.py b/volatility3/framework/renderers/format_hints.py index 239acbde3..6ec9ebab9 100644 --- a/volatility3/framework/renderers/format_hints.py +++ b/volatility3/framework/renderers/format_hints.py @@ -36,7 +36,6 @@ class MultiTypeData(bytes): split_nulls: bool = False, show_hex: bool = False, ) -> "MultiTypeData": - if isinstance(original, int): data = str(original).encode(encoding) else: diff --git a/volatility3/framework/symbols/__init__.py b/volatility3/framework/symbols/__init__.py index d1af56a26..10cf39cf1 100644 --- a/volatility3/framework/symbols/__init__.py +++ b/volatility3/framework/symbols/__init__.py @@ -192,7 +192,7 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface): replacements.add((traverser, child)) elif child.children: template_traverse_list.append(child) - for (parent, child) in replacements: + for parent, child in replacements: parent.replace_child(child, self._resolved[child.vol.type_name]) def get_type(self, type_name: str) -> interfaces.objects.Template: diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 0d7cbb7e4..ce07167e5 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -62,7 +62,6 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): # based on __d_path from the Linux kernel @classmethod def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str: - ret_path: List[str] = [] while dentry != rdentry or vfsmnt != rmnt: @@ -204,7 +203,6 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): symbol_table: str, task: interfaces.objects.ObjectInterface, ): - # task.files can be null if not task.files: return @@ -225,7 +223,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): fd_table, count=max_fds, subtype=file_type, context=context ) - for (fd_num, filp) in enumerate(fds): + for fd_num, filp in enumerate(fds): if filp != 0: full_path = LinuxUtilities.path_for_file(context, task, filp) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 55f139730..5ab8f1aa0 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -595,7 +595,6 @@ class list_head(objects.StructType, collections.abc.Iterable): seen = {self.vol.offset} while link.vol.offset not in seen: - obj = self._context.object( symbol_type, layer, offset=link.vol.offset - relative_offset ) @@ -630,7 +629,6 @@ class files_struct(objects.StructType): class mount(objects.StructType): - MNT_NOSUID = 0x01 MNT_NODEV = 0x02 MNT_NOEXEC = 0x04 @@ -755,7 +753,6 @@ class mount(objects.StructType): and current_mnt.has_parent() and current_mnt.vol.offset not in mnt_seen ): - current_dentry = current_mnt.mnt_mountpoint mnt_seen.add(current_mnt.vol.offset) current_mnt = current_mnt.mnt_parent diff --git a/volatility3/framework/symbols/linux/extensions/elf.py b/volatility3/framework/symbols/linux/extensions/elf.py index df6b23df8..416a7e4d2 100644 --- a/volatility3/framework/symbols/linux/extensions/elf.py +++ b/volatility3/framework/symbols/linux/extensions/elf.py @@ -22,7 +22,6 @@ class elf(objects.StructType): size: int, members: Dict[str, Tuple[int, interfaces.objects.Template]], ) -> None: - super().__init__( context=context, type_name=type_name, diff --git a/volatility3/framework/symbols/mac/__init__.py b/volatility3/framework/symbols/mac/__init__.py index 3909817ea..56ac96633 100644 --- a/volatility3/framework/symbols/mac/__init__.py +++ b/volatility3/framework/symbols/mac/__init__.py @@ -70,7 +70,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): kernel, # ikelos - how to type this?? mods_list: Iterator[Any], ): - try: start_addr = kernel.object_from_symbol("vm_kernel_stext") except exceptions.SymbolError: @@ -231,7 +230,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): next_member: str, max_elements: int = 4096, ) -> Iterable[interfaces.objects.ObjectInterface]: - for element in cls._walk_iterable( queue, "tqh_first", "tqe_next", next_member, max_elements ): @@ -244,7 +242,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): next_member: str, max_elements: int = 4096, ) -> Iterable[interfaces.objects.ObjectInterface]: - for element in cls._walk_iterable( queue, "lh_first", "le_next", next_member, max_elements ): @@ -257,7 +254,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): next_member: str, max_elements: int = 4096, ) -> Iterable[interfaces.objects.ObjectInterface]: - for element in cls._walk_iterable( queue, "slh_first", "sle_next", next_member, max_elements ): diff --git a/volatility3/framework/symbols/mac/extensions/__init__.py b/volatility3/framework/symbols/mac/extensions/__init__.py index b678304b8..c89b527e6 100644 --- a/volatility3/framework/symbols/mac/extensions/__init__.py +++ b/volatility3/framework/symbols/mac/extensions/__init__.py @@ -206,7 +206,7 @@ class vm_map_entry(objects.StructType): permask = "rwx" perms = "" - for (ctr, i) in enumerate([1, 3, 5]): + for ctr, i in enumerate([1, 3, 5]): if (self.protection & i) == i: perms = perms + permask[ctr] else: @@ -593,7 +593,7 @@ class sysctl_oid(objects.StructType): checks = [0x80000000, 0x40000000, 0x00800000] perms = ["R", "W", "L"] - for (i, c) in enumerate(checks): + for i, c in enumerate(checks): if c & self.oid_kind: ret = ret + perms[i] else: diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index d34d6a22f..ba00a4053 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -202,7 +202,6 @@ class MMVAD_SHORT(objects.StructType): # this is for windows 8 and 10 elif self.has_member("VadNode"): - if self.VadNode.has_member("u1"): return self.VadNode.u1.Parent & ~0x3 @@ -211,7 +210,6 @@ class MMVAD_SHORT(objects.StructType): # also for windows 8 and 10 elif self.has_member("Core"): - if self.Core.VadNode.has_member("u1"): return self.Core.VadNode.u1.Parent & ~0x3 @@ -224,14 +222,12 @@ class MMVAD_SHORT(objects.StructType): """Get the VAD's starting virtual address. This is the first accessible byte in the range.""" if self.has_member("StartingVpn"): - if self.has_member("StartingVpnHigh"): return (self.StartingVpn << 12) | (self.StartingVpnHigh << 44) else: return self.StartingVpn << 12 elif self.has_member("Core"): - if self.Core.has_member("StartingVpnHigh"): return (self.Core.StartingVpn << 12) | (self.Core.StartingVpnHigh << 44) else: @@ -243,7 +239,6 @@ class MMVAD_SHORT(objects.StructType): """Get the VAD's ending virtual address. This is the last accessible byte in the range.""" if self.has_member("EndingVpn"): - if self.has_member("EndingVpnHigh"): return (((self.EndingVpn + 1) << 12) | (self.EndingVpnHigh << 44)) - 1 else: @@ -376,7 +371,6 @@ class EX_FAST_REF(objects.StructType): """ def dereference(self) -> interfaces.objects.ObjectInterface: - if constants.BANG not in self.vol.type_name: raise ValueError( f"Invalid symbol table name syntax (no {constants.BANG} found)" @@ -771,7 +765,6 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): return False def get_vad_root(self): - # windows 8 and 2012 (_MM_AVL_TABLE) if self.VadRoot.has_member("BalancedRoot"): return self.VadRoot.BalancedRoot @@ -1346,7 +1339,6 @@ class SHARED_CACHE_MAP(objects.StructType): limit_depth = level_depth if section_size > self.VACB_SIZE_OF_FIRST_LEVEL: - # Create an array of 128 entries for the VACB index array. vacb_array = self._context.object( object_type=symbol_table_name + constants.BANG + "array", diff --git a/volatility3/framework/symbols/windows/extensions/network.py b/volatility3/framework/symbols/windows/extensions/network.py index c0f2bd61a..9b7573c2e 100644 --- a/volatility3/framework/symbols/windows/extensions/network.py +++ b/volatility3/framework/symbols/windows/extensions/network.py @@ -64,7 +64,6 @@ class _TCP_LISTENER(objects.StructType): size: int, members: Dict[str, Tuple[int, interfaces.objects.Template]], ) -> None: - super().__init__( context=context, type_name=type_name, @@ -167,7 +166,6 @@ class _TCP_LISTENER(objects.StructType): yield "v6", inaddr6_any, inaddr6_any def is_valid(self): - try: if not self.get_address_family() in (AF_INET, AF_INET6): vollog.debug( @@ -189,7 +187,6 @@ class _TCP_ENDPOINT(_TCP_LISTENER): """Class for objects found in TcpE pools""" def _ipv4_or_ipv6(self, inaddr): - if self.get_address_family() == AF_INET: return inet_ntop(socket.AF_INET, inaddr.addr4) else: @@ -214,7 +211,6 @@ class _TCP_ENDPOINT(_TCP_LISTENER): return None def is_valid(self): - if self.State not in self.State.choices.values(): vollog.debug( f"{type(self)} 0x{self.vol.offset:x} invalid due to invalid tcp state {self.State}" diff --git a/volatility3/framework/symbols/windows/extensions/pe.py b/volatility3/framework/symbols/windows/extensions/pe.py index adee956f7..3f34fc3dd 100644 --- a/volatility3/framework/symbols/windows/extensions/pe.py +++ b/volatility3/framework/symbols/windows/extensions/pe.py @@ -151,7 +151,6 @@ class IMAGE_DOS_HEADER(objects.StructType): counter = 0 for sect in nt_header.get_sections(): - if sect.VirtualAddress > size_of_image: raise ValueError( f"Section VirtualAddress is too large: {sect.VirtualAddress}" diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 74fd0e4e8..a43933ccf 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -249,7 +249,6 @@ class PDBUtility(interfaces.configuration.VersionableInterface): # Check for writability filter_string = os.path.join(pdb_name, guid + "-" + str(age)) for path in symbols.__path__: - # Store any temporary files created by downloading PDB files tmp_files = [] potential_output_filename = os.path.join( @@ -353,7 +352,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface): if end is None: end = ctx.layers[layer_name].maximum_address - for (GUID, age, pdb_name, signature_offset) in ctx.layers[layer_name].scan( + for GUID, age, pdb_name, signature_offset in ctx.layers[layer_name].scan( ctx, PdbSignatureScanner(pdb_names), progress_callback=progress_callback, @@ -426,7 +425,6 @@ class PDBUtility(interfaces.configuration.VersionableInterface): module_size: int = None, create_module: bool = False, ) -> Tuple[Optional[str], Optional[str]]: - if module_offset is None: module_offset = context.layers[layer_name].minimum_address if module_size is None: diff --git a/volatility3/plugins/windows/registry/certificates.py b/volatility3/plugins/windows/registry/certificates.py index 3212cb465..5ef840f32 100644 --- a/volatility3/plugins/windows/registry/certificates.py +++ b/volatility3/plugins/windows/registry/certificates.py @@ -77,7 +77,6 @@ class Certificates(interfaces.plugins.PluginInterface): layer_name=kernel.layer_name, symbol_table=kernel.symbol_table_name, ): - for top_key in [ "Microsoft\\SystemCertificates", "Software\\Microsoft\\SystemCertificates", From aac4c735280537c55c8f6eb738f08aaa8304b8e2 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 3 Feb 2023 09:22:30 +0000 Subject: [PATCH 17/39] Actions: Bump black checkout to Node16/wqv3 --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index dba5b5b80..5f4523072 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -6,7 +6,7 @@ jobs: lint: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: psf/black@stable with: options: "--check --diff --verbose" From 4bb6d93e122693b89a5a3947c12271400e25be3e Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:18:06 +0000 Subject: [PATCH 18/39] Update linux.psscan --- volatility3/framework/plugins/linux/psscan.py | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 volatility3/framework/plugins/linux/psscan.py diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py new file mode 100644 index 000000000..f87b78eb1 --- /dev/null +++ b/volatility3/framework/plugins/linux/psscan.py @@ -0,0 +1,158 @@ +# This file is Copyright 2023 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +import logging +from typing import Iterable, List, Tuple +import struct +from enum import Enum + +from volatility3.framework import renderers, interfaces, symbols, constants +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.layers import scanners +from volatility3.framework.renderers import format_hints + +vollog = logging.getLogger(__name__) + + +class DescExitStateEnum(Enum): + """Enum for linux task exit_state as defined in include/linux/sched.h""" + + TASK_RUNNING = 0x00000000 + EXIT_DEAD = 0x00000010 + EXIT_ZOMBIE = 0x00000020 + EXIT_TRACE = EXIT_ZOMBIE | EXIT_DEAD + + +class PsScan(interfaces.plugins.PluginInterface): + """Scans for processes present in a particular linux image.""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + ] + + def _get_task_fields( + self, task: interfaces.objects.ObjectInterface + ) -> Tuple[int, int, int, str, str]: + """Extract the fields needed for the final output + + Args: + task: A task object from where to get the fields. + 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) + exit_state = DescExitStateEnum(task.exit_state).name + + task_fields = ( + format_hints.Hex(task.vol.offset), + pid, + tid, + ppid, + name, + exit_state, + ) + return task_fields + + def _generator(self): + """Generates the tasks found from scanning.""" + + for task in self.scan_tasks( + self.context, self.config["kernel"], self.config["kernel.layer_name"] + ): + row = self._get_task_fields(task) + yield (0, row) + + @classmethod + def scan_tasks( + cls, + context: interfaces.context.ContextInterface, + vmlinux_module_name: str, + kernel_layer_name: str, + ) -> Iterable[interfaces.objects.ObjectInterface]: + """Scans for tasks in the memory 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 + kernel_layer_name: The name for the kernel layer + Yields: + Task objects + """ + vmlinux = context.modules[vmlinux_module_name] + + # check if this image is 32bit or 64bit + is_32bit = not symbols.symbol_table_is_64bit(context, vmlinux.symbol_table_name) + if is_32bit: + pack_format = "I" + else: + pack_format = "Q" + + # get task_struct to find the offset to the sched_class pointer + sched_class_offset = vmlinux.get_type("task_struct").members["sched_class"][0] + kernel_layer = context.layers[kernel_layer_name] + + needles = [] + for symbol in vmlinux.symbols: + + # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' + if "_sched_class" in symbol: + + # use canonicalize to set the appropriate sign extension for the addr + addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) + + # append to needles list the packed hex for searching + needles.append(struct.pack(pack_format, addr)) + + # scan the memory_layer for these needles + memory_layer = context.layers["memory_layer"] + for address, _ in memory_layer.scan( + context, scanners.MultiStringScanner(needles) + ): + # create task in the memory_layer + ptask = context.object( + vmlinux.symbol_table_name + constants.BANG + "task_struct", + offset=address - sched_class_offset, + layer_name="memory_layer", + ) + + # sanity check exit_state + try: + # attempt tp parse the exist_state using the enum + DescExitStateEnum(ptask.exit_state) + except ValueError: + vollog.debug( + f"Skipping task_struct at {hex(ptask.vol.offset)} as exit_state {ptask.exit_state} is likely not valid" + ) + continue + + # sanity check pid + if not (0 < ptask.pid < 65535): + vollog.debug( + f"Skipping task_struct at {hex(ptask.vol.offset)} as pid {ptask.pid} is likely not valid" + ) + continue + + yield ptask + + def run(self): + columns = [ + ("OFFSET (P)", format_hints.Hex), + ("PID", int), + ("TID", int), + ("PPID", int), + ("COMM", str), + ("EXIT_STATE", str), + ] + return renderers.TreeGrid(columns, self._generator()) From e81935869ab51f5b792aaa0c390418ab64e90755 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:37:29 +0000 Subject: [PATCH 19/39] Update linux.psscan to find kernel layer name correctly --- volatility3/framework/plugins/linux/psscan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index f87b78eb1..25e7206ec 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -68,8 +68,11 @@ class PsScan(interfaces.plugins.PluginInterface): def _generator(self): """Generates the tasks found from scanning.""" + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + for task in self.scan_tasks( - self.context, self.config["kernel"], self.config["kernel.layer_name"] + self.context, vmlinux_module_name, vmlinux.layer_name ): row = self._get_task_fields(task) yield (0, row) From 9e5a98ca40e5614df28ba5b09ffc64a656f19fb9 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:51:18 +0000 Subject: [PATCH 20/39] Update linux.psscan with black linting and version --- volatility3/framework/plugins/linux/psscan.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 25e7206ec..ba233e53d 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -28,6 +28,7 @@ class PsScan(interfaces.plugins.PluginInterface): """Scans for processes present in a particular linux image.""" _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -101,23 +102,19 @@ class PsScan(interfaces.plugins.PluginInterface): pack_format = "I" else: pack_format = "Q" - # get task_struct to find the offset to the sched_class pointer sched_class_offset = vmlinux.get_type("task_struct").members["sched_class"][0] kernel_layer = context.layers[kernel_layer_name] needles = [] for symbol in vmlinux.symbols: - # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: - # use canonicalize to set the appropriate sign extension for the addr addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) # append to needles list the packed hex for searching needles.append(struct.pack(pack_format, addr)) - # scan the memory_layer for these needles memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( @@ -139,14 +136,12 @@ class PsScan(interfaces.plugins.PluginInterface): f"Skipping task_struct at {hex(ptask.vol.offset)} as exit_state {ptask.exit_state} is likely not valid" ) continue - # sanity check pid if not (0 < ptask.pid < 65535): vollog.debug( f"Skipping task_struct at {hex(ptask.vol.offset)} as pid {ptask.pid} is likely not valid" ) continue - yield ptask def run(self): From d6ebad8235060a257e9af9a5e9831bb64a607606 Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 9 Feb 2023 21:19:11 -0700 Subject: [PATCH 21/39] Update simple-plugin.rst Very minor typo fix. --- doc/source/simple-plugin.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/simple-plugin.rst b/doc/source/simple-plugin.rst index c4908caf3..39670a62d 100644 --- a/doc/source/simple-plugin.rst +++ b/doc/source/simple-plugin.rst @@ -259,7 +259,7 @@ The plugin then takes the process's ``BaseDllName`` value, and calls :py:meth:`~ as defined by the symbols, are directly accessible and use the case-style of the symbol library it came from (in Windows, attributes are CamelCase), such as ``entry.BaseDllName`` in this instance. Any attributes not defined by the symbol but added by Volatility extensions cannot be properties (in case they overlap with the attributes defined in the symbol libraries) -and are therefore always methods and pretended with ``get_``, in this example ``BaseDllName.get_string()``. +and are therefore always methods and prepended with ``get_``, in this example ``BaseDllName.get_string()``. Finally, ``FullDllName`` is populated. These operations read from memory, and as such, the memory image may be unable to read the data at a particular offset. This will cause an exception to be thrown. In Volatility 3, exceptions are thrown From 4734a3d1f83295af45997758f1b31c07ba4e79fe Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 18 Feb 2023 21:14:02 +0000 Subject: [PATCH 22/39] Automagic: Fix cache issue with missing files --- volatility3/framework/automagic/symbol_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 63c6fc7fa..1ca5ba210 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -332,7 +332,7 @@ class SqliteCache(CacheManagerInterface): if inner_url.scheme == "file": pathname = inner_url.path.split("!")[0] - if pathname: + if pathname and os.path.exists(pathname): timestamp = datetime.datetime.fromtimestamp( os.stat(pathname).st_mtime ) From 471b19b037deab511bc7e9144bc5b25b47cfc81d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 18 Feb 2023 21:05:05 +0000 Subject: [PATCH 23/39] Layers: Use ctypes for snappy support --- requirements-dev.txt | 4 ---- requirements.txt | 4 ---- volatility3/framework/layers/avml.py | 35 ++++++++++++++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 7c372da2a..9db14d441 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,7 +20,3 @@ jsonschema>=2.3.0 # This is required for memory acquisition via leechcore/pcileech. leechcorepyc>=2.4.0 - -# This is required for analyzing Linux samples compressed using AVMLs native -# compression format. It is not required for AVML's standard LiME compression. -python-snappy==0.6.0 diff --git a/requirements.txt b/requirements.txt index 1793012f1..99e0786cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,3 @@ pycryptodome # This is required for memory acquisition via leechcore/pcileech. leechcorepyc>=2.4.0 - -# This is required for analyzing Linux samples compressed using AVMLs native -# compression format. It is not required for AVML's standard LiME compression. -python-snappy==0.6.0 diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 66f3f0e4f..3ce25ca6f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -6,6 +6,7 @@ The user of the file doesn't have to worry about the compression, but random access is not allowed.""" +import ctypes import logging import struct from typing import Tuple, List, Optional @@ -16,13 +17,35 @@ from volatility3.framework.layers import segmented vollog = logging.getLogger(__name__) try: - import snappy + from ctypes import cdll + + # TODO: Find library for windows if needed + lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + __snappy_uncompress = lib_snappy.snappy_uncompress + __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length HAS_SNAPPY = True -except ImportError: +except OSError: HAS_SNAPPY = False +class SnappyException(Exception): + pass + + +def uncompress(s): + """Uncompress a snappy compressed string.""" + ulen = ctypes.c_int(0) + cresult = __snappy_uncompressed_length(s, len(s), ctypes.byref(ulen)) + if cresult != 0: + raise SnappyException(f"Error in snappy_uncompressed_length: {cresult}") + ubuf = ctypes.create_string_buffer(ulen.value) + __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) + if cresult != 0: + raise SnappyException(f"Error in snappy_uncompress: {cresult}") + return ubuf.raw + + class AVMLLayer(segmented.NonLinearlySegmentedLayer): """A Lime format TranslationLayer. @@ -44,9 +67,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): if magic not in [0x4C4D5641] or version != 2: raise exceptions.LayerException("File not completely in AVML format") if not HAS_SNAPPY: - vollog.warning( - "AVML file detected, but snappy python library not installed" - ) + vollog.warning("AVML file detected, but snappy library could not be found") raise exceptions.LayerException( "AVML format dependencies not satisfied (snappy)" ) @@ -131,7 +152,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ] if frame_type == 0x00: # Compressed data - frame_data = snappy.decompress(frame_data) + frame_data = uncompress(frame_data) # TODO: Verify CRC segments.append( ( @@ -156,7 +177,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ) -> bytes: start_offset, _, _, _ = self._find_segment(offset) if self._compressed[mapped_offset]: - decoded_data = snappy.decompress(data) + decoded_data = uncompress(data) else: decoded_data = data decoded_data = decoded_data[offset - start_offset :] From c7252e9707ac0fb96c5fd65036cf8a8ff4b96672 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 19 Feb 2023 10:03:23 +0000 Subject: [PATCH 24/39] Automagic: Handle snappy for windows. --- volatility3/framework/layers/avml.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 3ce25ca6f..83c10186f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -20,7 +20,23 @@ try: from ctypes import cdll # TODO: Find library for windows if needed - lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + try: + # Linux/Mac + lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + except OSError: + lib_snappy = None + + try: + if not lib_snappy: + # Windows 64 + lib_snappy = cdll.LoadLibrary("snappy64") + except OSError: + lib_snappy = None + + if lib_snappy: + # Windows 32 + lib_snappy = cdll.LoadLibrary("snappy32") + __snappy_uncompress = lib_snappy.snappy_uncompress __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length @@ -29,7 +45,7 @@ except OSError: HAS_SNAPPY = False -class SnappyException(Exception): +class SnappyException(exceptions.VolatilityException): pass @@ -65,9 +81,12 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): layer.read(layer.minimum_address, struct.calcsize(header_structure)), ) if magic not in [0x4C4D5641] or version != 2: - raise exceptions.LayerException("File not completely in AVML format") + raise exceptions.LayerException("File not in AVML format") if not HAS_SNAPPY: - vollog.warning("AVML file detected, but snappy library could not be found") + vollog.warning( + "AVML file detected, but snappy library could not be found\n" + "Please install the snappy from your distribution or https://google.github.io/snappy/." + ) raise exceptions.LayerException( "AVML format dependencies not satisfied (snappy)" ) From ad3773d89884650cba6573280588f29e14e6ba0e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 19 Feb 2023 10:23:04 +0000 Subject: [PATCH 25/39] Automagic: Improve identified AVML CodeQL issues --- volatility3/framework/layers/avml.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 83c10186f..1ba564c61 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -17,25 +17,23 @@ from volatility3.framework.layers import segmented vollog = logging.getLogger(__name__) try: - from ctypes import cdll - # TODO: Find library for windows if needed try: # Linux/Mac - lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + lib_snappy = ctypes.cdll.LoadLibrary("libsnappy.so.1") except OSError: lib_snappy = None try: if not lib_snappy: # Windows 64 - lib_snappy = cdll.LoadLibrary("snappy64") + lib_snappy = ctypes.cdll.LoadLibrary("snappy64") except OSError: lib_snappy = None if lib_snappy: # Windows 32 - lib_snappy = cdll.LoadLibrary("snappy32") + lib_snappy = ctypes.cdll.LoadLibrary("snappy32") __snappy_uncompress = lib_snappy.snappy_uncompress __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length @@ -56,7 +54,7 @@ def uncompress(s): if cresult != 0: raise SnappyException(f"Error in snappy_uncompressed_length: {cresult}") ubuf = ctypes.create_string_buffer(ulen.value) - __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) + cresult = __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) if cresult != 0: raise SnappyException(f"Error in snappy_uncompress: {cresult}") return ubuf.raw From 1770edf6fa7a87f5c714aaeaedb4e02ce91e040f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 22 Feb 2023 17:24:12 +0000 Subject: [PATCH 26/39] Automagic: Fix typo in cache stats --- volatility3/framework/automagic/symbol_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 1ca5ba210..a58bf0091 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -371,7 +371,7 @@ class SqliteCache(CacheManagerInterface): # Get stats stats_base_types = len(json_obj.get("base_types", {})) - stats_types = len(json_obj.get("types", {})) + stats_types = len(json_obj.get("user_types", {})) stats_enums = len(json_obj.get("enums", {})) stats_symbols = len(json_obj.get("symbols", {})) From 588b0962541887dbfddedc779e5d93c5ceda87d2 Mon Sep 17 00:00:00 2001 From: Maxime THIEBAUT <46688461+0xThiebaut@users.noreply.github.com> Date: Sat, 25 Feb 2023 18:23:42 +0100 Subject: [PATCH 27/39] Add PID filtering to `windows.pstree` --- .../framework/plugins/windows/pstree.py | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/windows/pstree.py b/volatility3/framework/plugins/windows/pstree.py index 88a3697da..5c78d1682 100644 --- a/volatility3/framework/plugins/windows/pstree.py +++ b/volatility3/framework/plugins/windows/pstree.py @@ -3,7 +3,7 @@ # import datetime import logging -from typing import Dict, Set, Tuple +from typing import Callable, Dict, Set, Tuple from volatility3.framework import objects, interfaces, renderers from volatility3.framework.configuration import requirements @@ -24,6 +24,7 @@ class PsTree(interfaces.plugins.PluginInterface): self._processes: Dict[int, Tuple[interfaces.objects.ObjectInterface, int]] = {} self._levels: Dict[int, int] = {} self._children: Dict[int, Set[int]] = {} + self._ancestors: Set[int] = set([]) @classmethod def get_requirements(cls): @@ -45,18 +46,26 @@ class PsTree(interfaces.plugins.PluginInterface): requirements.ListRequirement( name="pid", element_type=int, - description="Process ID to include (all other processes are excluded)", + description="Process ID to include (with ancestors and descendants, all other processes are excluded)", optional=True, ), ] - def find_level(self, pid: objects.Pointer) -> None: + def find_level( + self, + pid: objects.Pointer, + filter_func: Callable[ + [interfaces.objects.ObjectInterface], bool + ] = lambda _: False, + ) -> None: """Finds how deep the pid is in the processes list.""" - seen = set([]) - seen.add(pid) + seen = {pid} level = 0 proc, _ = self._processes.get(pid, None) + filtered = not filter_func(proc) while proc is not None and proc.InheritedFromUniqueProcessId not in seen: + if filtered: + self._ancestors.add(proc.UniqueProcessId) child_list = self._children.get(proc.InheritedFromUniqueProcessId, set([])) child_list.add(proc.UniqueProcessId) self._children[proc.InheritedFromUniqueProcessId] = child_list @@ -67,7 +76,12 @@ class PsTree(interfaces.plugins.PluginInterface): level += 1 self._levels[pid] = level - def _generator(self): + def _generator( + self, + filter_func: Callable[ + [interfaces.objects.ObjectInterface], bool + ] = lambda _: False, + ): """Generates the Tree of processes.""" kernel = self.context.modules[self.config["kernel"]] @@ -87,15 +101,21 @@ class PsTree(interfaces.plugins.PluginInterface): # Build the child/level maps for pid in self._processes: - self.find_level(pid) + self.find_level(pid, filter_func) process_pids = set([]) - def yield_processes(pid): + def yield_processes(pid, descendant: bool = False): if pid in process_pids: vollog.debug(f"Pid cycle: already processed pid {pid}") return + process_pids.add(pid) + + if pid not in self._ancestors and not descendant: + vollog.debug(f"Pid cycle: pid {pid} not in filtered tree") + return + proc, offset = self._processes[pid] row = ( proc.UniqueProcessId, @@ -114,7 +134,9 @@ class PsTree(interfaces.plugins.PluginInterface): yield (self._levels[pid] - 1, row) for child_pid in self._children.get(pid, []): - yield from yield_processes(child_pid) + yield from yield_processes( + child_pid, descendant or not filter_func(proc) + ) for pid in self._levels: if self._levels[pid] == 1: @@ -140,5 +162,9 @@ class PsTree(interfaces.plugins.PluginInterface): ("CreateTime", datetime.datetime), ("ExitTime", datetime.datetime), ], - self._generator(), + self._generator( + filter_func=pslist.PsList.create_pid_filter( + self.config.get("pid", None) + ), + ), ) From 3791b21695083f522fe6dd6009ffef1e5b05fc2f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 6 Mar 2023 00:13:47 +0000 Subject: [PATCH 28/39] Layers: Fix new snappy implementation error --- volatility3/framework/layers/avml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 1ba564c61..c9c682ac4 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -39,7 +39,7 @@ try: __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length HAS_SNAPPY = True -except OSError: +except (AttributeError, OSError): HAS_SNAPPY = False From a34fb8497633394062e10366553a2c69ba10c85a Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 13:31:37 +0000 Subject: [PATCH 29/39] Fix linux.psscan to use kernel offset when finding symbol location --- volatility3/framework/plugins/linux/psscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index ba233e53d..60b96ba40 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,7 @@ class PsScan(interfaces.plugins.PluginInterface): # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr - addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) + addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) # append to needles list the packed hex for searching needles.append(struct.pack(pack_format, addr)) From bdf57f071697aa7688595efed438c8b80aa336d6 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 13:44:08 +0000 Subject: [PATCH 30/39] Add extra debug messages to linux.psscan when finding symbol locations --- volatility3/framework/plugins/linux/psscan.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 60b96ba40..7cf0aa631 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -112,9 +112,16 @@ class PsScan(interfaces.plugins.PluginInterface): if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) + packed_addr = struct.pack(pack_format, addr) + + # debug message to show needles being searched for and symbol names + vollog.debug( + f"Found a sched_class named {symbol} at offset {hex(addr)}. Will scan for these bytes: {packed_addr.hex()}" + ) # append to needles list the packed hex for searching - needles.append(struct.pack(pack_format, addr)) + needles.append(packed_addr) + # scan the memory_layer for these needles memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( From abfe104eb96e5ddd4a07e7a7a4dd5073fc88e5d0 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 14:02:13 +0000 Subject: [PATCH 31/39] Fix linux.pscan to find memory layer to scan using kernel layers dependencies rather than hard coded value. --- volatility3/framework/plugins/linux/psscan.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 7cf0aa631..e8cc17a40 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -6,7 +6,7 @@ from typing import Iterable, List, Tuple import struct from enum import Enum -from volatility3.framework import renderers, interfaces, symbols, constants +from volatility3.framework import renderers, interfaces, symbols, constants, exceptions from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility from volatility3.framework.layers import scanners @@ -122,8 +122,20 @@ class PsScan(interfaces.plugins.PluginInterface): # append to needles list the packed hex for searching needles.append(packed_addr) + # find the memory layer to scan + if len(kernel_layer.dependencies) > 1: + vollog.warning( + f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." + ) + elif len(kernel_layer.dependencies) == 0: + vollog.error( + f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." + ) + raise exceptions.LayerException(kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies") + + memory_layer = context.layers[kernel_layer.dependencies[0]] + # scan the memory_layer for these needles - memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( context, scanners.MultiStringScanner(needles) ): From 32db4c0e5f3804b33f4cc1a4f66fae90494c0df8 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 14:08:44 +0000 Subject: [PATCH 32/39] Fix black linting for linux.psscan. --- volatility3/framework/plugins/linux/psscan.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index e8cc17a40..f4bfd347e 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,9 @@ class PsScan(interfaces.plugins.PluginInterface): # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr - addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) + addr = kernel_layer.canonicalize( + vmlinux.get_symbol(symbol).address + vmlinux.offset + ) packed_addr = struct.pack(pack_format, addr) # debug message to show needles being searched for and symbol names @@ -121,20 +123,20 @@ class PsScan(interfaces.plugins.PluginInterface): # append to needles list the packed hex for searching needles.append(packed_addr) - # find the memory layer to scan if len(kernel_layer.dependencies) > 1: vollog.warning( - f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." - ) + f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." + ) elif len(kernel_layer.dependencies) == 0: vollog.error( - f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." - ) - raise exceptions.LayerException(kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies") - + f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." + ) + raise exceptions.LayerException( + kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies" + ) memory_layer = context.layers[kernel_layer.dependencies[0]] - + # scan the memory_layer for these needles for address, _ in memory_layer.scan( context, scanners.MultiStringScanner(needles) From a35afd4f343c10d7f8d1df2cb5eec8364c3dbd5a Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 8 Mar 2023 20:40:04 +0000 Subject: [PATCH 33/39] Core: Bump framwork version after release branch --- volatility3/framework/constants/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 8f1163fe1..3a6b24ea8 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -45,7 +45,7 @@ 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 = 4 # Number of changes that only add to the interface -VERSION_PATCH = 1 # Number of changes that do not change the interface +VERSION_PATCH = 2 # Number of changes that do not change the interface VERSION_SUFFIX = "" # TODO: At version 2.0.0, remove the symbol_shift feature From b8755ac574e8226321ed169a1d6cac3a39505617 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 8 Mar 2023 20:42:24 +0000 Subject: [PATCH 34/39] Linux: fix black lint issues --- volatility3/framework/plugins/linux/envars.py | 1 - volatility3/framework/plugins/linux/iomem.py | 1 - 2 files changed, 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index 028eb2a57..5cbf0f502 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -70,7 +70,6 @@ class Envars(plugins.PluginInterface): # if mm exists attempt to get envars if mm: - # get process layer to read envars from proc_layer_name = task.add_process_layer() if proc_layer_name is None: diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index fddea4668..8efbf3b57 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -128,7 +128,6 @@ class IOMem(interfaces.plugins.PluginInterface): # only continue if iomem_root address was located if iomem_root_offset is not None: - # recursively parse the resources starting from the root resource at 'iomem_resource' for depth, (name, start, end) in self.parse_resource( self.context, vmlinux_module_name, iomem_root_offset From 99417cdfcc87d93d82b7288d8ee06ec4e06ada07 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 14:17:37 +0000 Subject: [PATCH 35/39] Linux: Psscan check parent pointer is valid --- volatility3/framework/plugins/linux/psscan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index f4bfd347e..ca9d30586 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -52,7 +52,10 @@ class PsScan(interfaces.plugins.PluginInterface): """ pid = task.tgid tid = task.pid - ppid = task.parent.tgid if task.parent else 0 + ppid = 0 + + if task.parent.is_readable(): + ppid = task.parent.tgid name = utility.array_to_string(task.comm) exit_state = DescExitStateEnum(task.exit_state).name From 36ec5164703d2b5eaf0b3ff0d5f3a5f59572a5ff Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:17:24 +0000 Subject: [PATCH 36/39] Linux: Fix psscan task native_layer --- volatility3/framework/plugins/linux/psscan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index ca9d30586..462577e58 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -138,6 +138,7 @@ class PsScan(interfaces.plugins.PluginInterface): raise exceptions.LayerException( kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies" ) + memory_layer_name = kernel_layer.dependencies[0] memory_layer = context.layers[kernel_layer.dependencies[0]] # scan the memory_layer for these needles @@ -148,7 +149,8 @@ class PsScan(interfaces.plugins.PluginInterface): ptask = context.object( vmlinux.symbol_table_name + constants.BANG + "task_struct", offset=address - sched_class_offset, - layer_name="memory_layer", + layer_name=memory_layer_name, + native_layer_name=kernel_layer_name, ) # sanity check exit_state From 46f56770af1785f5f0bcfb887849bb96e164f23c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:18:58 +0000 Subject: [PATCH 37/39] Core: Pointer is_readable should check the native layer --- volatility3/framework/objects/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index a04eedd87..3b1745718 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -442,7 +442,7 @@ class Pointer(Integer): def is_readable(self, layer_name: Optional[str] = None) -> bool: """Determines whether the address of this pointer can be read from memory.""" - layer_name = layer_name or self.vol.layer_name + layer_name = layer_name or self.vol.native_layer_name return self._context.layers[layer_name].is_valid(self, self.vol.subtype.size) def __getattr__(self, attr: str) -> Any: From 9216bab61b3187fc248760ffdda5b86c8a694c9a Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 17:32:46 +0000 Subject: [PATCH 38/39] Core: Improve import exception reporting --- volatility3/framework/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/__init__.py b/volatility3/framework/__init__.py index ec0edc2fe..479925fb7 100644 --- a/volatility3/framework/__init__.py +++ b/volatility3/framework/__init__.py @@ -26,6 +26,7 @@ import importlib import inspect import logging import os +import traceback from typing import Any, Dict, Generator, List, Tuple, Type, TypeVar from volatility3.framework import constants, interfaces @@ -183,7 +184,11 @@ def import_file(module: str, path: str, ignore_errors: bool = False) -> List[str try: importlib.import_module(module) except ImportError as e: - vollog.debug(str(e)) + vollog.debug( + "".join( + traceback.TracebackException.from_exception(e).format(chain=True) + ) + ) vollog.debug( "Failed to import module {} based on file: {}".format(module, path) ) From d9a365d96fcd990c7faba32ab7aa63523203e9f8 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 17:33:20 +0000 Subject: [PATCH 39/39] Linux: Rename linux.envars to linux.envvars --- volatility3/framework/plugins/linux/envars.py | 121 +----------------- .../framework/plugins/linux/envvars.py | 121 ++++++++++++++++++ 2 files changed, 127 insertions(+), 115 deletions(-) create mode 100644 volatility3/framework/plugins/linux/envvars.py diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index 5cbf0f502..c4e3ed3c9 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -1,121 +1,12 @@ -# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 -# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 -# - +from volatility3.plugins import envvars import logging -from volatility3.framework import exceptions, renderers -from volatility3.framework.configuration import requirements -from volatility3.framework.interfaces import plugins -from volatility3.framework.objects import utility -from volatility3.plugins.linux import pslist - vollog = logging.getLogger(__name__) -class Envars(plugins.PluginInterface): - """Lists processes with their environment variables""" - - _required_framework_version = (2, 0, 0) - - @classmethod - def get_requirements(cls): - # Since we're calling the plugin, make sure we have the plugin's requirements - return [ - requirements.ModuleRequirement( - name="kernel", - description="Linux kernel", - architectures=["Intel32", "Intel64"], - ), - requirements.PluginRequirement( - name="pslist", plugin=pslist.PsList, version=(2, 0, 0) - ), - requirements.ListRequirement( - name="pid", - description="Filter on specific process IDs", - element_type=int, - optional=True, - ), - ] - - def _generator(self, tasks): - """Generates a listing of processes along with environment variables""" - - # walk the process list and return the envars - for task in tasks: - pid = task.pid - - # get process name as string - name = utility.array_to_string(task.comm) - - # try and get task parent - try: - ppid = task.parent.pid - except exceptions.InvalidAddressException: - vollog.debug( - f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." - ) - ppid = 0 - - # kernel threads never have an mm as they do not have userland mappings - try: - mm = task.mm - except exceptions.InvalidAddressException: - # no mm so cannot get envars - vollog.debug( - f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." - ) - mm = None - continue - - # if mm exists attempt to get envars - if mm: - # get process layer to read envars from - proc_layer_name = task.add_process_layer() - if proc_layer_name is None: - vollog.debug( - f"Unable to construct process layer for task {pid} {name}, will not extract any envars." - ) - continue - proc_layer = self.context.layers[proc_layer_name] - - # get the size of the envars with sanity checking - envars_size = task.mm.env_end - task.mm.env_start - if not (0 < envars_size <= 8192): - vollog.debug( - f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." - ) - continue - - # attempt to read all envars data - try: - envar_data = proc_layer.read(task.mm.env_start, envars_size) - except exceptions.InvalidAddressException: - vollog.debug( - f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." - ) - continue - - # parse envar data, envars are null terminated, keys and values are separated by '=' - envar_data = envar_data.rstrip(b"\x00") - for envar_pair in envar_data.split(b"\x00"): - try: - key, value = envar_pair.decode().split("=", 1) - except ValueError: - vollog.debug( - f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" - ) - continue - yield (0, (pid, ppid, name, key, value)) - - def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - - return renderers.TreeGrid( - [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], - self._generator( - pslist.PsList.list_tasks( - self.context, self.config["kernel"], filter_func=filter_func - ) - ), +class Envars(envvars.Envvars): + def run(self, *args, **kwargs): + vollog.warning( + "The linux.envars plugin has been renamed to linux.envvars and will only be accessible through the new name in a future release" ) + return super().run(*args, **kwargs) diff --git a/volatility3/framework/plugins/linux/envvars.py b/volatility3/framework/plugins/linux/envvars.py new file mode 100644 index 000000000..1d6c8b784 --- /dev/null +++ b/volatility3/framework/plugins/linux/envvars.py @@ -0,0 +1,121 @@ +# This file is Copyright 2022 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import logging + +from volatility3.framework import exceptions, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.interfaces import plugins +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +vollog = logging.getLogger(__name__) + + +class Envvars(plugins.PluginInterface): + """Lists processes with their environment variables""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.PluginRequirement( + name="pslist", plugin=pslist.PsList, version=(2, 0, 0) + ), + requirements.ListRequirement( + name="pid", + description="Filter on specific process IDs", + element_type=int, + optional=True, + ), + ] + + def _generator(self, tasks): + """Generates a listing of processes along with environment variables""" + + # walk the process list and return the envars + for task in tasks: + pid = task.pid + + # get process name as string + name = utility.array_to_string(task.comm) + + # try and get task parent + try: + ppid = task.parent.pid + except exceptions.InvalidAddressException: + vollog.debug( + f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." + ) + ppid = 0 + + # kernel threads never have an mm as they do not have userland mappings + try: + mm = task.mm + except exceptions.InvalidAddressException: + # no mm so cannot get envars + vollog.debug( + f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." + ) + mm = None + continue + + # if mm exists attempt to get envars + if mm: + # get process layer to read envars from + proc_layer_name = task.add_process_layer() + if proc_layer_name is None: + vollog.debug( + f"Unable to construct process layer for task {pid} {name}, will not extract any envars." + ) + continue + proc_layer = self.context.layers[proc_layer_name] + + # get the size of the envars with sanity checking + envars_size = task.mm.env_end - task.mm.env_start + if not (0 < envars_size <= 8192): + vollog.debug( + f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." + ) + continue + + # attempt to read all envars data + try: + envar_data = proc_layer.read(task.mm.env_start, envars_size) + except exceptions.InvalidAddressException: + vollog.debug( + f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." + ) + continue + + # parse envar data, envars are null terminated, keys and values are separated by '=' + envar_data = envar_data.rstrip(b"\x00") + for envar_pair in envar_data.split(b"\x00"): + try: + key, value = envar_pair.decode().split("=", 1) + except ValueError: + vollog.debug( + f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" + ) + continue + yield (0, (pid, ppid, name, key, value)) + + def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + + return renderers.TreeGrid( + [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], + self._generator( + pslist.PsList.list_tasks( + self.context, self.config["kernel"], filter_func=filter_func + ) + ), + )