From 338fcdd0ab7f59491017ed63d8e780e744c0fe88 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Wed, 2 Feb 2022 15:48:41 +0000 Subject: [PATCH 01/37] Add the psaux plugin for Linux command line argument listing --- volatility3/framework/plugins/linux/psaux.py | 90 ++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 volatility3/framework/plugins/linux/psaux.py diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py new file mode 100644 index 000000000..31777f458 --- /dev/null +++ b/volatility3/framework/plugins/linux/psaux.py @@ -0,0 +1,90 @@ +# 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 typing import Optional + +from volatility3.framework import symbols, exceptions, renderers, interfaces +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +class PsAux(pslist.PsList): + """ Lists processes with their command line arguments """ + + def _get_command_line_args(self, task: interfaces.objects.ObjectInterface, + name: str) -> Optional[str]: + """ + Reads the command line arguments of a process + These are stored on the userland stack + Kernel threads re-use the process data structure, but do not have a valid 'mm' pointer + + Parameters: + task: task_struct object of the process + name: string name of the process (from task.comm) + """ + + # kernel theads never have an mm as they do not have userland mappings + try: + mm = task.mm + except exceptions.InvalidAddressException: + mm = None + + if mm: + proc_layer_name = task.add_process_layer() + if proc_layer_name is None: + return renderers.UnreadableValue() + + proc_layer = self.context.layers[proc_layer_name] + + # read argv from userland + start = task.mm.arg_start + + # get the size of the arguments with sanity checking + size_to_read = task.mm.arg_end - task.mm.arg_start + if size_to_read < 1 or size_to_read > 4096: + return renderers.UnreadableValue() + + # attempt to read it all as partial values are invalid and misleading + try: + argv = proc_layer.read(start, size_to_read) + except exceptions.InvalidAddressException: + return renderers.UnreadableValue() + + # the arguments are null byte terminated, replace the nulls with spaces + s = argv.decode().split('\x00') + args = " ".join(s) + else: + # kernel thread + # [ ] mimics ps on a live system + # also helps identify malware masquerading as a kernel thread, which is fairly common + args = "[" + name + "]" + + # remove trailing space, if present + if len(args) > 1 and args[-1] == " ": + args = args[:-1] + + return args + + def _generator(self): + """ Generates a listing of processes along with command line arguments """ + + vmlinux = self.context.modules[self.config['kernel']] + + # walk the process list and report the arguments + for task in self.list_tasks(self.context, vmlinux.name): + pid = task.pid + + try: + ppid = task.parent.pid + except exceptions.InvalidAddressException: + ppid = 0 + + name = utility.array_to_string(task.comm) + + args = self._get_command_line_args(task, name) + + yield (0, (pid, ppid, name, args)) + + def run(self): + return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], self._generator()) + From b43d61ca036926047a13343eb401ad920cd5e62b Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Thu, 28 Apr 2022 15:42:10 +0000 Subject: [PATCH 02/37] Address feedback from ikelos --- volatility3/framework/plugins/linux/psaux.py | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py index 31777f458..089bb61c7 100644 --- a/volatility3/framework/plugins/linux/psaux.py +++ b/volatility3/framework/plugins/linux/psaux.py @@ -4,6 +4,7 @@ from typing import Optional +from volatility3.framework.configuration import requirements from volatility3.framework import symbols, exceptions, renderers, interfaces from volatility3.framework.objects import utility from volatility3.plugins.linux import pslist @@ -11,6 +12,19 @@ from volatility3.plugins.linux import pslist class PsAux(pslist.PsList): """ Lists processes with their command line arguments """ + @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 _get_command_line_args(self, task: interfaces.objects.ObjectInterface, name: str) -> Optional[str]: """ @@ -41,7 +55,7 @@ class PsAux(pslist.PsList): # get the size of the arguments with sanity checking size_to_read = task.mm.arg_end - task.mm.arg_start - if size_to_read < 1 or size_to_read > 4096: + if not (0 < size_to_read <= 4096): return renderers.UnreadableValue() # attempt to read it all as partial values are invalid and misleading @@ -65,13 +79,11 @@ class PsAux(pslist.PsList): return args - def _generator(self): + def _generator(self, tasks): """ Generates a listing of processes along with command line arguments """ - vmlinux = self.context.modules[self.config['kernel']] - # walk the process list and report the arguments - for task in self.list_tasks(self.context, vmlinux.name): + for task in tasks: pid = task.pid try: @@ -86,5 +98,11 @@ class PsAux(pslist.PsList): yield (0, (pid, ppid, name, args)) def run(self): - return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], self._generator()) + filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) + + return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str), ("ARGS", str)], + self._generator( + pslist.PsList.list_tasks(self.context, + self.config['kernel'], + filter_func = filter_func))) From 3175e25420095f237fcc987c1efd970b8cfc3305 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Thu, 28 Apr 2022 16:11:51 +0000 Subject: [PATCH 03/37] Remove the inheritance from pslist --- volatility3/framework/plugins/linux/psaux.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py index 089bb61c7..c62712907 100644 --- a/volatility3/framework/plugins/linux/psaux.py +++ b/volatility3/framework/plugins/linux/psaux.py @@ -8,10 +8,13 @@ from volatility3.framework.configuration import requirements from volatility3.framework import symbols, exceptions, renderers, interfaces from volatility3.framework.objects import utility from volatility3.plugins.linux import pslist +from volatility3.framework.interfaces import plugins -class PsAux(pslist.PsList): +class PsAux(plugins.PluginInterface): """ Lists processes with their command line arguments """ + _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 From 05ae20c78bd982e95ab3dd99d92a2efaf68be3b3 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Sun, 22 May 2022 11:49:18 +0300 Subject: [PATCH 04/37] fix off by in filelayer --- volatility3/framework/layers/physical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/physical.py b/volatility3/framework/layers/physical.py index 73d46b211..5d5fd17a9 100644 --- a/volatility3/framework/layers/physical.py +++ b/volatility3/framework/layers/physical.py @@ -118,7 +118,7 @@ class FileLayer(interfaces.layers.DataLayerInterface): with self._lock: orig = self._file.tell() self._file.seek(0, 2) - self._size = self._file.tell() + self._size = self._file.tell() - 1 self._file.seek(orig) return self._size From 98001e7dd72ac6e39f440191da019ec33a9e64c5 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sun, 22 May 2022 23:58:38 +0900 Subject: [PATCH 05/37] Fix: typo for code comments --- volatility3/framework/contexts/__init__.py | 2 +- volatility3/framework/interfaces/symbols.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index ab81beb5e..85a7d32b7 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -321,7 +321,7 @@ class SizedModule(Module): The mapping should be sorted and should be quicker than reading the data We turn it into JSON to make a common string and use a - quick hash, because collissions are unlikely + quick hash, because collisions are unlikely """ layer = self._context.layers[self.layer_name] if not isinstance(layer, interfaces.layers.TranslationLayerInterface): diff --git a/volatility3/framework/interfaces/symbols.py b/volatility3/framework/interfaces/symbols.py index 9f2cb9fc9..1ad30cfdf 100644 --- a/volatility3/framework/interfaces/symbols.py +++ b/volatility3/framework/interfaces/symbols.py @@ -169,7 +169,7 @@ class BaseSymbolTableInterface: def optional_set_type_class(self, name: str, clazz: Type[objects.ObjectInterface]) -> bool: """Calls the set_type_class function but does not throw an exception. - Returns whether setting the type class was successfull. + Returns whether setting the type class was successful. Args: name: The name of the type to override the class for clazz: The actual class to override for the provided type name From 2a011487a91c9f8e71b86a80e0d186e03b84a5b4 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 22 May 2022 23:23:02 +0100 Subject: [PATCH 06/37] Core: Old linux systems may not have mnt_namespace structures --- volatility3/framework/symbols/linux/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 0c5ce395c..d59a95db5 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -1,10 +1,10 @@ # This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -from typing import List, Tuple, Iterator +from typing import Iterator, List, Tuple from volatility3 import framework -from volatility3.framework import exceptions, constants, interfaces, objects +from volatility3.framework import constants, exceptions, interfaces, objects from volatility3.framework.objects import utility from volatility3.framework.symbols import intermed from volatility3.framework.symbols.linux import extensions @@ -29,7 +29,9 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('files_struct', extensions.files_struct) self.set_type_class('vfsmount', extensions.vfsmount) self.set_type_class('kobject', extensions.kobject) - self.set_type_class('mnt_namespace', extensions.mnt_namespace) + + if 'mnt_namespace' in self.types: + self.set_type_class('mnt_namespace', extensions.mnt_namespace) if 'module' in self.types: self.set_type_class('module', extensions.module) @@ -267,4 +269,4 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): while list_start: list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset) yield list_struct - list_start = getattr(list_struct, list_member) \ No newline at end of file + list_start = getattr(list_struct, list_member) From 786fd61fc9b6b2978e0de7595bdb22aca9e91843 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 9 Apr 2022 20:57:34 +0100 Subject: [PATCH 07/37] Layers: Add architecture to qemu layer --- volatility3/framework/layers/qemu.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index f1ba1e468..65f8eed9a 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -3,9 +3,9 @@ # import functools import json -from typing import Optional, Dict, Any, Tuple, List, Set +from typing import Any, Dict, List, Optional, Set, Tuple -from volatility3.framework import interfaces, exceptions, constants +from volatility3.framework import constants, exceptions, interfaces from volatility3.framework.layers import segmented from volatility3.framework.symbols import intermed @@ -39,6 +39,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): metadata: Optional[Dict[str, Any]] = None) -> None: self._qemu_table_name = intermed.IntermediateSymbolTable.create(context, config_path, 'generic', 'qemu') self._configuration = None + self._architecture = None self._compressed: Set[int] = set() self._current_segment_name = b'' super().__init__(context = context, config_path = config_path, name = name, metadata = metadata) @@ -139,6 +140,9 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): section_len = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long', offset = index, layer_name = self._base_layer) + self._architecture = self.context.object(self._qemu_table_name + constants.BANG + 'string', + offset = index + 4, layer_name = self._base_layer, + max_length = section_len) index += 4 + section_len elif section_byte == self.QEVM_SECTION_START or section_byte == self.QEVM_SECTION_FULL: section_id = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long', From 302bb63645af3b9b20b2a361a73c06a2c27e3513 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 9 Apr 2022 21:46:47 +0100 Subject: [PATCH 08/37] Layers: Detect and compensate for QEVM pci-hole --- volatility3/framework/layers/qemu.py | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 65f8eed9a..4e6252b2f 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -3,12 +3,17 @@ # import functools import json +import logging +import re +import struct from typing import Any, Dict, List, Optional, Set, Tuple from volatility3.framework import constants, exceptions, interfaces from volatility3.framework.layers import segmented from volatility3.framework.symbols import intermed +vollog = logging.getLogger(__name__) + class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): """A Qemu suspend-to-disk translation layer.""" @@ -32,6 +37,13 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): SEGMENT_FLAG_XBZRLE = 0x40 SEGMENT_FLAG_HOOK = 0x80 + pci_hole_table = {re.compile(r"^pc-i440fx-\d\.\d$"): (0xc0000000, 0x100000000), + re.compile(r"^pc-1440fx-eoan$"): (0xe0000000, 0x100000000), + re.compile(r"^pc-q35$"): (0x80000000, 0x100000000), + re.compile(r"^microvm$"): (0xc0000000, 0x100000000), + re.compile(r"^xen$"): (0xf0000000, 0x100000000) + } + def __init__(self, context: interfaces.context.ContextInterface, config_path: str, @@ -42,6 +54,8 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): self._architecture = None self._compressed: Set[int] = set() self._current_segment_name = b'' + self._pci_hole_start = 0 + self._pci_hole_end = 0 super().__init__(context = context, config_path = config_path, name = name, metadata = metadata) @classmethod @@ -77,9 +91,9 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): base_layer = self.context.layers[self._base_layer] while not done: - addr = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long long', - offset = index, - layer_name = self._base_layer) + # Use struct.unpack here for performance improvements + addr = struct.unpack('>Q', base_layer.read(index, 8))[0] + # Flags are stored in the n least significant bits, where n equals the bit-length of pagesize flags = addr & (page_size - 1) # addr equals the highest multiple of pagesize <= offset @@ -87,6 +101,9 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): addr = addr ^ (addr & (page_size - 1)) index += 8 + if addr > self._pci_hole_start: + addr += self._pci_hole_end - self._pci_hole_start + if flags & self.SEGMENT_FLAG_MEM_SIZE: namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char', offset = index, @@ -143,6 +160,13 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): self._architecture = self.context.object(self._qemu_table_name + constants.BANG + 'string', offset = index + 4, layer_name = self._base_layer, max_length = section_len) + for regex in self.pci_hole_table: + if regex.match(self._architecture): + self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] + vollog.log(constants.LOGLEVEL_VVVV, f"QEVM archicture detected as: {self._architecture}") + break + else: + vollog.debug(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") index += 4 + section_len elif section_byte == self.QEVM_SECTION_START or section_byte == self.QEVM_SECTION_FULL: section_id = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long', From dea8e1dac9090b40823a941fdcfc1a828f80de9e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 18 Apr 2022 01:45:12 +0100 Subject: [PATCH 09/37] Layers: Add QEVM architecture fallback detection --- volatility3/framework/layers/qemu.py | 97 ++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 4e6252b2f..7835cad17 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -9,7 +9,7 @@ import struct from typing import Any, Dict, List, Optional, Set, Tuple from volatility3.framework import constants, exceptions, interfaces -from volatility3.framework.layers import segmented +from volatility3.framework.layers import scanners, segmented from volatility3.framework.symbols import intermed vollog = logging.getLogger(__name__) @@ -37,11 +37,32 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): SEGMENT_FLAG_XBZRLE = 0x40 SEGMENT_FLAG_HOOK = 0x80 - pci_hole_table = {re.compile(r"^pc-i440fx-\d\.\d$"): (0xc0000000, 0x100000000), - re.compile(r"^pc-1440fx-eoan$"): (0xe0000000, 0x100000000), - re.compile(r"^pc-q35$"): (0x80000000, 0x100000000), - re.compile(r"^microvm$"): (0xc0000000, 0x100000000), - re.compile(r"^xen$"): (0xf0000000, 0x100000000) + # See https://qemu.readthedocs.io/en/latest/devel/memory.html for more info + # + # At least the following values could occur for devices using > 3-4 GB RAM: + # +--------------------------------+--------------------------------+------------+-------------+ + # | Architecture | Reference Code | Hole Start | Hole End | + # +--------------------------------+--------------------------------+------------+-------------+ + # | PC i440FX + PIIX "New Default" | qemu/hw/i386/pc_piix.c:98 | 0xc0000000 | 0x100000000 | + # | PC i440FX + PIIX "Old Default" | qemu/hw/i386/pc_piix.c:98 | 0xe0000000 | 0x100000000 | + # | PC Q35 + ICH9 | qemu/hw/i386/pc_q35.c:141 | 0x80000000 | 0x100000000 | + # | MicroVM | qemu/hw/i386/microvm.c:291 | 0xc0000000 | 0x100000000 | + # | Xen | qemu/hw/i386/xen/xen-hvm.c:248 | 0xf0000000 | 0x100000000 | + # +--------------------------------+--------------------------------+------------+-------------+ + # + # For now, we assume that the parameter max-ram-below-4g is not set, since this parameter influences the size + # and location of the memory gap. Deviating hole sizes could eventually be detected for Linux by e.g. scanning + # for dmesg entries with a regex like rb'\[mem (0x[0-9a-f]{4,10})-0x[0-9a-f]{4,10}\] available for PCI devices' + + debian_re = r"artful|eoan" + + pci_hole_table = {re.compile(r"^pc-i440fx-([23456789]|\d\d+)\.\d$"): (0xe0000000, 0xc0000000, 0x100000000), + re.compile(r"^pc-i440fx-[01].\d$"): (0xe0000000, 0xe0000000, 0x100000000), + re.compile(r"^pc-q35-\d.\d$"): (0xe0000000, 0x80000000, 0x100000000), + re.compile(r"^microvm$"): (0xe0000000, 0xc0000000, 0x100000000), + re.compile(r"^xen$"): (0xe0000000, 0xf0000000, 0x100000000), + re.compile(r"^pc-i440fx-" + debian_re + r"$"): (0xe0000000, 0xc0000000, 0x100000000), + re.compile(r"^pc-q35-" + debian_re + r"$"): (0xe0000000, 0x80000000, 0x100000000), } def __init__(self, @@ -65,6 +86,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): raise exceptions.LayerException(name, 'No QEMU magic bytes') if header[4:] != b'\x00\x00\x00\x03': raise exceptions.LayerException(name, 'Unsupported QEMU version found') + vollog.debug("QEVM header found") def _read_configuration(self, base_layer: interfaces.layers.DataLayerInterface, name: str) -> Any: """Reads the JSON configuration from the end of the file""" @@ -160,13 +182,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): self._architecture = self.context.object(self._qemu_table_name + constants.BANG + 'string', offset = index + 4, layer_name = self._base_layer, max_length = section_len) - for regex in self.pci_hole_table: - if regex.match(self._architecture): - self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] - vollog.log(constants.LOGLEVEL_VVVV, f"QEVM archicture detected as: {self._architecture}") - break - else: - vollog.debug(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") index += 4 + section_len elif section_byte == self.QEVM_SECTION_START or section_byte == self.QEVM_SECTION_FULL: section_id = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned long', @@ -217,6 +232,62 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): else: raise exceptions.LayerException(self._name, f'QEMU unknown section encountered: {section_byte}') + # If no architecture has been set, try to determine it using fallback mechanisms + if not self._architecture: + self._architecture = self._fallback_determine_architecture() + if self._architecture is None: + vollog.log(constants.LOGLEVEL_VV, f"QEVM architecture could not be determined") + + # Once all segments have been read, determine the PCI hole if any + for regex in self.pci_hole_table: + if regex.match(self._architecture): + self._pci_hole_minimum, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] + if self.maximum_address < self._pci_hole_minimum: + # The PCI hole isn't present because we're below the minimum value + self._pci_hole_start, self._pci_hole_end = 0, 0 + vollog.log(constants.LOGLEVEL_VVVV, f"QEVM architecture detected as: {self._architecture}") + break + else: + vollog.log(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") + + def _fallback_determine_architecture(self) -> str: + architecture_pattern = rb'pc-(i440fx|q35)-([0-9]{1,2}.[0-9]{1,2}(?:.[0-9]{1,2})?)' + base_layer = self.context.layers[self._base_layer] + + vollog.log(constants.LOGLEVEL_VVVV, "QEVM fallback architecture detection used") + + res = scanners.RegExScanner(architecture_pattern) + for offset in base_layer.scan(context = self.context, scanner = res): + line = base_layer.read(offset, 64) + regex_results = re.search(architecture_pattern, line) + architecture = "pc-" + regex_results.groups()[0].decode() + return architecture + + # If that does not work, look in configuration JSON for devices specific to a certain architecture + architecture = None + for device in self._configuration.get('devices', []): + device_name = device.get('vmsd_name', '').lower() + if 'i440fx' in device_name or 'piix' in device_name: + architecture = 'pc-i440fx-2.0' + break + elif 'ich9' in device_name: + architecture = 'pc-q35-1.0' + break + if architecture: + return architecture + + # Still haven't found architecture, switch to fallback-method + architecture_pattern = rb'Standard PC \((i440FX|Q35)' + res = scanners.RegExScanner(architecture_pattern) + for offset in base_layer.scan(context = self.context, scanner = res): + line = base_layer.read(offset, 64) + regex_results = re.search(architecture_pattern, line) + architecture = "pc-" + regex_results.groups()[0].decode().lower() + return architecture + + vollog.warning("Could not determine QEMU target architecture!") + return None + def extract_data(self, index, name, version_id): if name == 'ram': if version_id != 4: From 1fca57ffc3603ab383332076f05e54f973c21452 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 20 Apr 2022 21:43:30 +0100 Subject: [PATCH 10/37] Layers: QEVM more fixes for minimum addresses --- volatility3/framework/layers/qemu.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 7835cad17..23c29dc39 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -54,15 +54,15 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): # and location of the memory gap. Deviating hole sizes could eventually be detected for Linux by e.g. scanning # for dmesg entries with a regex like rb'\[mem (0x[0-9a-f]{4,10})-0x[0-9a-f]{4,10}\] available for PCI devices' - debian_re = r"artful|eoan" + distro_re = r"(artful|eoan|rhel[\d\.]+)" pci_hole_table = {re.compile(r"^pc-i440fx-([23456789]|\d\d+)\.\d$"): (0xe0000000, 0xc0000000, 0x100000000), - re.compile(r"^pc-i440fx-[01].\d$"): (0xe0000000, 0xe0000000, 0x100000000), - re.compile(r"^pc-q35-\d.\d$"): (0xe0000000, 0x80000000, 0x100000000), - re.compile(r"^microvm$"): (0xe0000000, 0xc0000000, 0x100000000), - re.compile(r"^xen$"): (0xe0000000, 0xf0000000, 0x100000000), - re.compile(r"^pc-i440fx-" + debian_re + r"$"): (0xe0000000, 0xc0000000, 0x100000000), - re.compile(r"^pc-q35-" + debian_re + r"$"): (0xe0000000, 0x80000000, 0x100000000), + re.compile(r"^pc-i440fx-[01]\.\d$"): (0xe0000000, 0xe0000000, 0x100000000), + re.compile(r"^pc-q35-\d\.\d$"): (0xb0000000, 0x80000000, 0x100000000), + re.compile(r"^microvm$"): (0xc0000000, 0xc0000000, 0x100000000), + re.compile(r"^xen$"): (0xf0000000, 0xf0000000, 0x100000000), + re.compile(r"^pc-i440fx-" + distro_re + r"$"): (0xe0000000, 0xe0000000, 0x100000000), + re.compile(r"^pc-q35-" + distro_re + r"$"): (0xb0000000, 0x80000000, 0x100000000), } def __init__(self, @@ -252,6 +252,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): def _fallback_determine_architecture(self) -> str: architecture_pattern = rb'pc-(i440fx|q35)-([0-9]{1,2}.[0-9]{1,2}(?:.[0-9]{1,2})?)' + old_suffix = "-1.0" base_layer = self.context.layers[self._base_layer] vollog.log(constants.LOGLEVEL_VVVV, "QEVM fallback architecture detection used") @@ -260,7 +261,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for offset in base_layer.scan(context = self.context, scanner = res): line = base_layer.read(offset, 64) regex_results = re.search(architecture_pattern, line) - architecture = "pc-" + regex_results.groups()[0].decode() + architecture = "pc-" + regex_results.groups()[0].decode() + old_suffix return architecture # If that does not work, look in configuration JSON for devices specific to a certain architecture @@ -268,10 +269,10 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for device in self._configuration.get('devices', []): device_name = device.get('vmsd_name', '').lower() if 'i440fx' in device_name or 'piix' in device_name: - architecture = 'pc-i440fx-2.0' + architecture = 'pc-i440fx' + old_suffix break elif 'ich9' in device_name: - architecture = 'pc-q35-1.0' + architecture = 'pc-q35' + old_suffix break if architecture: return architecture @@ -282,7 +283,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for offset in base_layer.scan(context = self.context, scanner = res): line = base_layer.read(offset, 64) regex_results = re.search(architecture_pattern, line) - architecture = "pc-" + regex_results.groups()[0].decode().lower() + architecture = "pc-" + regex_results.groups()[0].decode().lower() + old_suffix return architecture vollog.warning("Could not determine QEMU target architecture!") From afec05106127a8de73c22e7cc7f87ab3dd67ef9d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 21 Apr 2022 10:29:26 +0100 Subject: [PATCH 11/37] Layers: Make QEVM changes based on @cstation 's feedback --- volatility3/framework/layers/qemu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 23c29dc39..16f18cca1 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -123,7 +123,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): addr = addr ^ (addr & (page_size - 1)) index += 8 - if addr > self._pci_hole_start: + if addr >= self._pci_hole_start: addr += self._pci_hole_end - self._pci_hole_start if flags & self.SEGMENT_FLAG_MEM_SIZE: @@ -252,7 +252,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): def _fallback_determine_architecture(self) -> str: architecture_pattern = rb'pc-(i440fx|q35)-([0-9]{1,2}.[0-9]{1,2}(?:.[0-9]{1,2})?)' - old_suffix = "-1.0" + old_suffix = "-2.0" base_layer = self.context.layers[self._base_layer] vollog.log(constants.LOGLEVEL_VVVV, "QEVM fallback architecture detection used") @@ -261,7 +261,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for offset in base_layer.scan(context = self.context, scanner = res): line = base_layer.read(offset, 64) regex_results = re.search(architecture_pattern, line) - architecture = "pc-" + regex_results.groups()[0].decode() + old_suffix + architecture = regex_results.group().decode() return architecture # If that does not work, look in configuration JSON for devices specific to a certain architecture From fb861eb6dfc528ec8f4c2a3f71a0995bdada0cba Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 21 Apr 2022 10:33:29 +0100 Subject: [PATCH 12/37] Layers: Simplification of QEVM fallback regex --- volatility3/framework/layers/qemu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 16f18cca1..11bec2dd3 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -61,7 +61,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): re.compile(r"^pc-q35-\d\.\d$"): (0xb0000000, 0x80000000, 0x100000000), re.compile(r"^microvm$"): (0xc0000000, 0xc0000000, 0x100000000), re.compile(r"^xen$"): (0xf0000000, 0xf0000000, 0x100000000), - re.compile(r"^pc-i440fx-" + distro_re + r"$"): (0xe0000000, 0xe0000000, 0x100000000), + re.compile(r"^pc-i440fx-" + distro_re + r"$"): (0xe0000000, 0xc0000000, 0x100000000), re.compile(r"^pc-q35-" + distro_re + r"$"): (0xb0000000, 0x80000000, 0x100000000), } @@ -251,7 +251,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): vollog.log(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") def _fallback_determine_architecture(self) -> str: - architecture_pattern = rb'pc-(i440fx|q35)-([0-9]{1,2}.[0-9]{1,2}(?:.[0-9]{1,2})?)' + architecture_pattern = rb'pc-(i440fx|q35)-(\d{1,2}\.\d{1,2}|[\w\d\.]+)' old_suffix = "-2.0" base_layer = self.context.layers[self._base_layer] From 21b0cb56a5746410f8ff9c96fba9d0e0ee86730f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 21 Apr 2022 23:50:13 +0100 Subject: [PATCH 13/37] Layers: Shift when we calculate the QEVM PCI hole --- volatility3/framework/layers/qemu.py | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 11bec2dd3..270405645 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -170,7 +170,28 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): index = 8 section_info = dict() current_section_id = -1 + version_id = -1 + name = None + arch_detected = False while section_byte != self.QEVM_EOF and index <= base_layer.maximum_address: + if index > 20 and not arch_detected: + # We're past where the QEVM_CONFIGURATION might be, so set the values + # If no architecture has been set, try to determine it using fallback mechanisms + if not self._architecture: + self._architecture = self._fallback_determine_architecture() + if self._architecture is None: + vollog.log(constants.LOGLEVEL_VV, f"QEVM architecture could not be determined") + + # Once all segments have been read, determine the PCI hole if any + for regex in self.pci_hole_table: + if regex.match(self._architecture): + _, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] + vollog.log(constants.LOGLEVEL_VVVV, f"QEVM architecture detected as: {self._architecture}") + break + else: + vollog.log(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") + arch_detected = True + section_byte = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned char', offset = index, layer_name = self._base_layer) @@ -232,24 +253,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): else: raise exceptions.LayerException(self._name, f'QEMU unknown section encountered: {section_byte}') - # If no architecture has been set, try to determine it using fallback mechanisms - if not self._architecture: - self._architecture = self._fallback_determine_architecture() - if self._architecture is None: - vollog.log(constants.LOGLEVEL_VV, f"QEVM architecture could not be determined") - - # Once all segments have been read, determine the PCI hole if any - for regex in self.pci_hole_table: - if regex.match(self._architecture): - self._pci_hole_minimum, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] - if self.maximum_address < self._pci_hole_minimum: - # The PCI hole isn't present because we're below the minimum value - self._pci_hole_start, self._pci_hole_end = 0, 0 - vollog.log(constants.LOGLEVEL_VVVV, f"QEVM architecture detected as: {self._architecture}") - break - else: - vollog.log(constants.LOGLEVEL_VVVV, f"QEVM unknown architecture found: {self._architecture}") - def _fallback_determine_architecture(self) -> str: architecture_pattern = rb'pc-(i440fx|q35)-(\d{1,2}\.\d{1,2}|[\w\d\.]+)' old_suffix = "-2.0" From 4eacdd9bea6be1be5506273abba5a4cc7715e7ff Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 25 Apr 2022 00:09:07 +0100 Subject: [PATCH 14/37] Layers: use QEVM size to turn off pci hole if needed --- volatility3/framework/layers/qemu.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 270405645..c755b8a9b 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -77,6 +77,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): self._current_segment_name = b'' self._pci_hole_start = 0 self._pci_hole_end = 0 + self._pci_hole_minimum = 0 super().__init__(context = context, config_path = config_path, name = name, metadata = metadata) @classmethod @@ -110,6 +111,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): done = None segments = [] + size_array = {} base_layer = self.context.layers[self._base_layer] while not done: @@ -131,14 +133,21 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): offset = index, layer_name = self._base_layer) while namelen != 0: - # if base_layer.read(index + 1, namelen) == b'pc.ram': - # total_size = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned long long', - # offset = index + 1 + namelen, - # layer_name = self._base_layer) + total_size = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned long long', + offset = index + 1 + namelen, + layer_name = self._base_layer) + size_array[base_layer.read(index + 1, namelen)] = total_size index += 1 + namelen + 8 namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char', offset = index, layer_name = self._base_layer) + if size_array.get(b'pc.ram', + max([x[0] for x in self.pci_hole_table.values()]) + 1) <= self._pci_hole_minimum: + # Turns off the pci_hole if it's not supposed to be there + vollog.debug( + f"QEVM tunrning off PCI hole due to small image size: {size_array.get(b'pc.ram'):x} < {self._pci_hole_minimum:x}") + self._pci_hole_start, self._pci_hole_end = 0, 0 + if flags & (self.SEGMENT_FLAG_COMPRESS | self.SEGMENT_FLAG_PAGE): if not (flags & self.SEGMENT_FLAG_CONTINUE): namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char', @@ -185,7 +194,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): # Once all segments have been read, determine the PCI hole if any for regex in self.pci_hole_table: if regex.match(self._architecture): - _, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] + self._pci_hole_minimum, self._pci_hole_start, self._pci_hole_end = self.pci_hole_table[regex] vollog.log(constants.LOGLEVEL_VVVV, f"QEVM architecture detected as: {self._architecture}") break else: From dd8fcce2ef683aa4bad211ee9273a865a59e84e1 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 27 Apr 2022 22:30:14 +0100 Subject: [PATCH 15/37] Layers: QEMU improvements suggested by @cstation --- volatility3/framework/layers/qemu.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index c755b8a9b..e3d70ba6e 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -54,7 +54,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): # and location of the memory gap. Deviating hole sizes could eventually be detected for Linux by e.g. scanning # for dmesg entries with a regex like rb'\[mem (0x[0-9a-f]{4,10})-0x[0-9a-f]{4,10}\] available for PCI devices' - distro_re = r"(artful|eoan|rhel[\d\.]+)" + distro_re = r"(\w+[\d\.]?)" pci_hole_table = {re.compile(r"^pc-i440fx-([23456789]|\d\d+)\.\d$"): (0xe0000000, 0xc0000000, 0x100000000), re.compile(r"^pc-i440fx-[01]\.\d$"): (0xe0000000, 0xe0000000, 0x100000000), @@ -141,8 +141,8 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): namelen = self._context.object(self._qemu_table_name + constants.BANG + 'unsigned char', offset = index, layer_name = self._base_layer) - if size_array.get(b'pc.ram', - max([x[0] for x in self.pci_hole_table.values()]) + 1) <= self._pci_hole_minimum: + highest_possible_maximum = max([x[0] for x in self.pci_hole_table.values()]) + 1 + if size_array.get(b'pc.ram', highest_possible_maximum) < self._pci_hole_minimum: # Turns off the pci_hole if it's not supposed to be there vollog.debug( f"QEVM tunrning off PCI hole due to small image size: {size_array.get(b'pc.ram'):x} < {self._pci_hole_minimum:x}") @@ -264,7 +264,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): def _fallback_determine_architecture(self) -> str: architecture_pattern = rb'pc-(i440fx|q35)-(\d{1,2}\.\d{1,2}|[\w\d\.]+)' - old_suffix = "-2.0" + default_suffix = "-2.0" base_layer = self.context.layers[self._base_layer] vollog.log(constants.LOGLEVEL_VVVV, "QEVM fallback architecture detection used") @@ -281,10 +281,10 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for device in self._configuration.get('devices', []): device_name = device.get('vmsd_name', '').lower() if 'i440fx' in device_name or 'piix' in device_name: - architecture = 'pc-i440fx' + old_suffix + architecture = 'pc-i440fx' + default_suffix break elif 'ich9' in device_name: - architecture = 'pc-q35' + old_suffix + architecture = 'pc-q35' + default_suffix break if architecture: return architecture @@ -295,7 +295,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): for offset in base_layer.scan(context = self.context, scanner = res): line = base_layer.read(offset, 64) regex_results = re.search(architecture_pattern, line) - architecture = "pc-" + regex_results.groups()[0].decode().lower() + old_suffix + architecture = "pc-" + regex_results.groups()[0].decode().lower() + default_suffix return architecture vollog.warning("Could not determine QEMU target architecture!") From 7ce95117484e13ab0ba6a4a50051b56252cca978 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 23 May 2022 01:20:46 +0100 Subject: [PATCH 16/37] Layers: QEMU recommendations from @cstation --- volatility3/framework/layers/qemu.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index e3d70ba6e..985c4534a 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -54,7 +54,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): # and location of the memory gap. Deviating hole sizes could eventually be detected for Linux by e.g. scanning # for dmesg entries with a regex like rb'\[mem (0x[0-9a-f]{4,10})-0x[0-9a-f]{4,10}\] available for PCI devices' - distro_re = r"(\w+[\d\.]?)" + distro_re = r"(\w+[\d{1,2}\.]*)" pci_hole_table = {re.compile(r"^pc-i440fx-([23456789]|\d\d+)\.\d$"): (0xe0000000, 0xc0000000, 0x100000000), re.compile(r"^pc-i440fx-[01]\.\d$"): (0xe0000000, 0xe0000000, 0x100000000), @@ -145,7 +145,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): if size_array.get(b'pc.ram', highest_possible_maximum) < self._pci_hole_minimum: # Turns off the pci_hole if it's not supposed to be there vollog.debug( - f"QEVM tunrning off PCI hole due to small image size: {size_array.get(b'pc.ram'):x} < {self._pci_hole_minimum:x}") + f"QEVM tunrning off PCI hole due to small image size: 0x{size_array.get(b'pc.ram'):x} < 0x{self._pci_hole_minimum:x}") self._pci_hole_start, self._pci_hole_end = 0, 0 if flags & (self.SEGMENT_FLAG_COMPRESS | self.SEGMENT_FLAG_PAGE): @@ -179,8 +179,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): index = 8 section_info = dict() current_section_id = -1 - version_id = -1 - name = None arch_detected = False while section_byte != self.QEVM_EOF and index <= base_layer.maximum_address: if index > 20 and not arch_detected: @@ -263,7 +261,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): raise exceptions.LayerException(self._name, f'QEMU unknown section encountered: {section_byte}') def _fallback_determine_architecture(self) -> str: - architecture_pattern = rb'pc-(i440fx|q35)-(\d{1,2}\.\d{1,2}|[\w\d\.]+)' + architecture_pattern = rb'pc-(i440fx|q35)-(\d{1,2}\.\d{1,2}|\w+[\d{1,2}\.]*)' default_suffix = "-2.0" base_layer = self.context.layers[self._base_layer] @@ -287,6 +285,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): architecture = 'pc-q35' + default_suffix break if architecture: + vollog.log(constants.LOGLEVEL_VVV, f'Architecture version unknown, default used: {default_suffix}') return architecture # Still haven't found architecture, switch to fallback-method @@ -296,6 +295,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): line = base_layer.read(offset, 64) regex_results = re.search(architecture_pattern, line) architecture = "pc-" + regex_results.groups()[0].decode().lower() + default_suffix + vollog.log(constants.LOGLEVEL_VVV, f'Architecture version unknown, default used: {default_suffix}') return architecture vollog.warning("Could not determine QEMU target architecture!") From 6276b984008868161638971c8e7bf0eeddc1ea1a Mon Sep 17 00:00:00 2001 From: ikelos Date: Mon, 23 May 2022 01:48:07 +0100 Subject: [PATCH 17/37] Update volatility3/framework/layers/qemu.py Fix typo courtesy of @digitalisx Co-authored-by: Donghyun Kim --- volatility3/framework/layers/qemu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 985c4534a..907116e99 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -145,7 +145,7 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): if size_array.get(b'pc.ram', highest_possible_maximum) < self._pci_hole_minimum: # Turns off the pci_hole if it's not supposed to be there vollog.debug( - f"QEVM tunrning off PCI hole due to small image size: 0x{size_array.get(b'pc.ram'):x} < 0x{self._pci_hole_minimum:x}") + f"QEVM turning off PCI hole due to small image size: 0x{size_array.get(b'pc.ram'):x} < 0x{self._pci_hole_minimum:x}") self._pci_hole_start, self._pci_hole_end = 0, 0 if flags & (self.SEGMENT_FLAG_COMPRESS | self.SEGMENT_FLAG_PAGE): From a3c63fbdf893324df2754b999e51db6655f3b06a Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 23 May 2022 09:25:30 +0300 Subject: [PATCH 18/37] rename variable --- volatility3/framework/layers/physical.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/layers/physical.py b/volatility3/framework/layers/physical.py index 5d5fd17a9..728fdcf31 100644 --- a/volatility3/framework/layers/physical.py +++ b/volatility3/framework/layers/physical.py @@ -113,14 +113,15 @@ class FileLayer(interfaces.layers.DataLayerInterface): def maximum_address(self) -> int: """Returns the largest available address in the space.""" # Zero based, so we return the size of the file minus 1 - if self._size: - return self._size + if self._maximum_address + return self._maximum_address with self._lock: orig = self._file.tell() self._file.seek(0, 2) - self._size = self._file.tell() - 1 + self._size = self._file.tell() self._file.seek(orig) - return self._size + self._maximum_address = self._size - 1 + return self._maximum_address @property def minimum_address(self) -> int: From be82c1639c051f929cbc17c6aa8e7250d6711f8b Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 23 May 2022 09:40:25 +0300 Subject: [PATCH 19/37] fix missing --- volatility3/framework/layers/physical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/physical.py b/volatility3/framework/layers/physical.py index 728fdcf31..5d757f482 100644 --- a/volatility3/framework/layers/physical.py +++ b/volatility3/framework/layers/physical.py @@ -113,7 +113,7 @@ class FileLayer(interfaces.layers.DataLayerInterface): def maximum_address(self) -> int: """Returns the largest available address in the space.""" # Zero based, so we return the size of the file minus 1 - if self._maximum_address + if self._maximum_address: return self._maximum_address with self._lock: orig = self._file.tell() From e15fa0ebad1a46fd990d31181d2dbe8f6b5b994d Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 23 May 2022 09:43:49 +0300 Subject: [PATCH 20/37] declared in constructor --- volatility3/framework/layers/physical.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/layers/physical.py b/volatility3/framework/layers/physical.py index 5d757f482..5cf0b776d 100644 --- a/volatility3/framework/layers/physical.py +++ b/volatility3/framework/layers/physical.py @@ -88,6 +88,7 @@ class FileLayer(interfaces.layers.DataLayerInterface): self._accessor = resources.ResourceAccessor() self._file_: Optional[IO[Any]] = None self._size: Optional[int] = None + self._maximum_address: Optional[int] = None # Construct the lock now (shared if made before threading) in case we ever need it self._lock: Union[DummyLock, threading.Lock] = DummyLock() if constants.PARALLELISM == constants.Parallelism.Threading: From 946d2302bbc92e781b1281632c5ea5a669228bd0 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 23 May 2022 17:44:52 +0300 Subject: [PATCH 21/37] log resource cache usage --- volatility3/framework/layers/resources.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/volatility3/framework/layers/resources.py b/volatility3/framework/layers/resources.py index ac25b5cc2..8a0e96208 100644 --- a/volatility3/framework/layers/resources.py +++ b/volatility3/framework/layers/resources.py @@ -171,6 +171,8 @@ class ResourceAccessor(object): cache_file.write(block) block = fp.read(block_size) cache_file.close() + else: + vollog.debug(f"Using already cached file at: {temp_filename}") # Re-open the cache with a different mode # Since we don't want people thinking they're able to save to the cache file, # open it in read mode only and allow breakages to happen if they wanted to write From 4dd8114dcb565daddbd105809252b5517f87a5a1 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Sun, 29 May 2022 11:28:40 +0300 Subject: [PATCH 22/37] check return value from is_valid --- .../framework/symbols/windows/extensions/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 69e8ba94e..e9264e0a0 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -746,7 +746,10 @@ class LIST_ENTRY(objects.StructType, collections.abc.Iterable): trans_layer = self._context.layers[layer] try: - trans_layer.is_valid(self.vol.offset) + is_valid = trans_layer.is_valid(self.vol.offset) + if not is_valid: + return + link = getattr(self, direction).dereference() except exceptions.InvalidAddressException: return @@ -762,7 +765,9 @@ class LIST_ENTRY(objects.StructType, collections.abc.Iterable): obj_offset = link.vol.offset - relative_offset try: - trans_layer.is_valid(obj_offset) + is_valid = trans_layer.is_valid(obj_offset) + if not is_valid: + return except exceptions.InvalidAddressException: return From cdbe41dbf5a2a43714d3bc3579746a057055e07a Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Sun, 29 May 2022 12:20:24 +0300 Subject: [PATCH 23/37] removed redundant try catch --- .../framework/symbols/windows/extensions/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index e9264e0a0..b5ee272a0 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -764,11 +764,7 @@ class LIST_ENTRY(objects.StructType, collections.abc.Iterable): while link.vol.offset not in seen: obj_offset = link.vol.offset - relative_offset - try: - is_valid = trans_layer.is_valid(obj_offset) - if not is_valid: - return - except exceptions.InvalidAddressException: + if not trans_layer.is_valid(obj_offset): return obj = self._context.object(symbol_type, From b9694e109a03f9589f124cb3d88c4ec4e58cc719 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 30 May 2022 02:48:46 +0900 Subject: [PATCH 24/37] Add: JSON EOF for config file --- volatility3/cli/__init__.py | 1 + volatility3/cli/volshell/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index e3fb726a1..8851e2b18 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -332,6 +332,7 @@ class CommandLine: parser.error(f"Cannot write configuration: file {args.save_config} already exists") with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) + f.write("\n") except exceptions.UnsatisfiedException as excp: self.process_unsatisfied_exceptions(excp) parser.exit(1, f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n") diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index 30fe75e06..769e958fd 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -246,6 +246,7 @@ class VolShell(cli.CommandLine): parser.error(f"Cannot write configuration: file {args.save_config} already exists") with open(args.save_config, "w") as f: json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2) + f.write("\n") except exceptions.UnsatisfiedException as excp: self.process_unsatisfied_exceptions(excp) parser.exit(1, f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n") From bd332261dede65118114e6484c4d8ce446d3b165 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 30 May 2022 05:39:37 +0900 Subject: [PATCH 25/37] Fix: __del__ to __exit__ --- volatility3/framework/layers/physical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/physical.py b/volatility3/framework/layers/physical.py index 5cf0b776d..0633637ca 100644 --- a/volatility3/framework/layers/physical.py +++ b/volatility3/framework/layers/physical.py @@ -191,7 +191,7 @@ class FileLayer(interfaces.layers.DataLayerInterface): """Closes the file handle.""" self._file.close() - def __del__(self) -> None: + def __exit__(self) -> None: self.destroy() @classmethod From a4e162c7f1dd8597cd0b9a0a8e175573c7fb8c62 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 30 May 2022 07:24:45 +0900 Subject: [PATCH 26/37] Fix: minor for better code --- volatility3/framework/plugins/mac/kauth_listeners.py | 2 +- volatility3/framework/plugins/windows/skeleton_key_check.py | 2 +- volatility3/framework/symbols/metadata.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/mac/kauth_listeners.py b/volatility3/framework/plugins/mac/kauth_listeners.py index 7002d88e2..fba6a8e0a 100644 --- a/volatility3/framework/plugins/mac/kauth_listeners.py +++ b/volatility3/framework/plugins/mac/kauth_listeners.py @@ -1,4 +1,4 @@ -# This file is opyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0 +# This file is Copyright 2020 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # diff --git a/volatility3/framework/plugins/windows/skeleton_key_check.py b/volatility3/framework/plugins/windows/skeleton_key_check.py index cd4a5baec..4a1b48c9a 100644 --- a/volatility3/framework/plugins/windows/skeleton_key_check.py +++ b/volatility3/framework/plugins/windows/skeleton_key_check.py @@ -9,7 +9,7 @@ # For a thorough walkthrough on how the R&D was performed to develop this plugin, # please see our blogpost here: # -# +# https://volatility-labs.blogspot.com/2021/10/memory-forensics-r-illustrated.html import io import logging diff --git a/volatility3/framework/symbols/metadata.py b/volatility3/framework/symbols/metadata.py index 7cde686ee..350bb0a53 100644 --- a/volatility3/framework/symbols/metadata.py +++ b/volatility3/framework/symbols/metadata.py @@ -38,4 +38,4 @@ class WindowsMetadata(interfaces.symbols.MetadataInterface): class LinuxMetadata(interfaces.symbols.MetadataInterface): - """Class to handle the etadata from a Linux symbol table.""" + """Class to handle the metadata from a Linux symbol table.""" From 1ef8c5167722aaed65e163be3ab1d1f06c6117bb Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 30 May 2022 08:29:13 +0900 Subject: [PATCH 27/37] Fix: minor code for improve --- volatility3/framework/plugins/linux/check_syscall.py | 2 +- volatility3/framework/plugins/linux/mountinfo.py | 1 - volatility3/framework/plugins/windows/ssdt.py | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/linux/check_syscall.py b/volatility3/framework/plugins/linux/check_syscall.py index 87d252cd5..50fd05fa5 100644 --- a/volatility3/framework/plugins/linux/check_syscall.py +++ b/volatility3/framework/plugins/linux/check_syscall.py @@ -80,7 +80,7 @@ class Check_syscall(plugins.PluginInterface): def _get_table_info_disassembly(self, ptr_sz, vmlinux): """Find the size of the system call table by disassembling functions - that immediately reference it in their first isntruction This is in the + that immediately reference it in their first instruction This is in the form 'cmp reg,NR_syscalls'.""" table_size = 0 diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index 6f3cb712d..551d128ad 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -1,7 +1,6 @@ # This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -# Author: Gustavo Moreira import logging from collections import namedtuple diff --git a/volatility3/framework/plugins/windows/ssdt.py b/volatility3/framework/plugins/windows/ssdt.py index 0d921535d..78fd72630 100644 --- a/volatility3/framework/plugins/windows/ssdt.py +++ b/volatility3/framework/plugins/windows/ssdt.py @@ -95,10 +95,10 @@ class SSDT(plugins.PluginInterface): if is_kernel_64: array_subtype = "long" - def kvo_calulator(func: int) -> int: + def kvo_calculator(func: int) -> int: return kvo + service_table_address + (func >> 4) - find_address = kvo_calulator + find_address = kvo_calculator else: array_subtype = "unsigned long" From 8b128b05f834c210ce607ab40d386718b5b363b5 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Mon, 30 May 2022 22:17:33 +0900 Subject: [PATCH 28/37] Fix: typo for code comments --- volatility3/framework/objects/templates.py | 2 +- volatility3/framework/plugins/linux/psaux.py | 2 +- volatility3/framework/plugins/linux/pstree.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/objects/templates.py b/volatility3/framework/objects/templates.py index b544d117f..56754d255 100644 --- a/volatility3/framework/objects/templates.py +++ b/volatility3/framework/objects/templates.py @@ -63,7 +63,7 @@ class ObjectTemplate(interfaces.objects.Template): object_info: interfaces.objects.ObjectInformation) -> interfaces.objects.ObjectInterface: """Constructs the object. - Returns: an object adhereing to the :class:`~volatility3.framework.interfaces.objects.ObjectInterface` + Returns: an object adhering to the :class:`~volatility3.framework.interfaces.objects.ObjectInterface` """ arguments: Dict[str, Any] = {} for arg in self.vol: diff --git a/volatility3/framework/plugins/linux/psaux.py b/volatility3/framework/plugins/linux/psaux.py index c62712907..ed91c66f2 100644 --- a/volatility3/framework/plugins/linux/psaux.py +++ b/volatility3/framework/plugins/linux/psaux.py @@ -40,7 +40,7 @@ class PsAux(plugins.PluginInterface): name: string name of the process (from task.comm) """ - # kernel theads never have an mm as they do not have userland mappings + # kernel threads never have an mm as they do not have userland mappings try: mm = task.mm except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/linux/pstree.py b/volatility3/framework/plugins/linux/pstree.py index a44310147..3ad5f3e19 100644 --- a/volatility3/framework/plugins/linux/pstree.py +++ b/volatility3/framework/plugins/linux/pstree.py @@ -19,7 +19,7 @@ class PsTree(pslist.PsList): """Finds how deep the PID is in the tasks hierarchy. Args: - pid: PID to find the level in the hierachy + pid: PID to find the level in the hierarchy """ seen = set([pid]) level = 0 From bb80d7067e99d55748e1067e6d11e22c2ffe5e4d Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 31 May 2022 23:24:49 +0900 Subject: [PATCH 29/37] Fix: typo of timeliner parameter --- volatility3/framework/plugins/timeliner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/timeliner.py b/volatility3/framework/plugins/timeliner.py index 8785f62e1..c1d29062d 100644 --- a/volatility3/framework/plugins/timeliner.py +++ b/volatility3/framework/plugins/timeliner.py @@ -101,7 +101,7 @@ class Timeliner(interfaces.plugins.PluginInterface): return [sortable(timestamp) for timestamp in data[2:]] - def _generator(self, runable_plugins: List[TimeLinerInterface]) -> Optional[Iterable[Tuple[int, Tuple]]]: + def _generator(self, runnable_plugins: List[TimeLinerInterface]) -> Optional[Iterable[Tuple[int, Tuple]]]: """Takes a timeline, sorts it and output the data from each relevant row from each plugin.""" # Generate the results for each plugin @@ -115,9 +115,9 @@ class Timeliner(interfaces.plugins.PluginInterface): file_data = None fp = None - for plugin in runable_plugins: + for plugin in runnable_plugins: plugin_name = plugin.__class__.__name__ - self._progress_callback((runable_plugins.index(plugin) * 100) // len(runable_plugins), + self._progress_callback((runnable_plugins.index(plugin) * 100) // len(runnable_plugins), f"Running plugin {plugin_name}...") try: vollog.log(logging.INFO, f"Running {plugin_name}") From 0abd2e53abefd0856c83bfad2cf61ab500868d38 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 5 Jun 2022 10:56:42 +0100 Subject: [PATCH 30/37] Pyinstaller: Fix path need to current directory to be correct --- vol.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vol.spec b/vol.spec index 42b69af3f..666526dde 100644 --- a/vol.spec +++ b/vol.spec @@ -26,7 +26,7 @@ except ImportError: # Volatility must be findable in sys.path in order for collect_submodules to work # This adds the current working directory, which should usually do the trick -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.abspath(SPEC))) vol_analysis = Analysis(['vol.py'], pathex = [], From db3408bdaa978de5b23eecd2d4411df8a6de1f16 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 5 Jun 2022 22:23:30 +0100 Subject: [PATCH 31/37] Windows: Extend the pdb support to modules --- .../framework/symbols/windows/pdbutil.py | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 585e96b6d..41037d464 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -10,10 +10,10 @@ import os import re import struct from typing import Any, Dict, Generator, List, Optional, Tuple, Union -from urllib import request, parse +from urllib import parse, request from volatility3 import symbols -from volatility3.framework import constants, interfaces, exceptions +from volatility3.framework import constants, contexts, exceptions, interfaces from volatility3.framework.configuration.requirements import SymbolTableRequirement from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows import pdbconv @@ -24,7 +24,7 @@ vollog = logging.getLogger(__name__) class PDBUtility(interfaces.configuration.VersionableInterface): """Class to handle and manage all getting symbols based on MZ header""" - _version = (1, 0, 0) + _version = (1, 0, 1) _required_framework_version = (2, 0, 0) @classmethod @@ -131,14 +131,14 @@ class PDBUtility(interfaces.configuration.VersionableInterface): # Check it is actually the MZ header if mz_sig != b"MZ": return None - + nt_header_start, = struct.unpack(" str: + pdb_name: str, module_offset: int = None, module_size: int = None) -> str: """Creates symbol table for a module in the specified layer_name. Searches the memory section of the loaded module for its PDB GUID @@ -307,6 +307,19 @@ class PDBUtility(interfaces.configuration.VersionableInterface): Returns: The name of the constructed and loaded symbol table """ + _, symbol_table_name = cls._modtable_from_pdb(context, config_path, layer_name, pdb_name, module_offset, + module_size) + return symbol_table_name + + @classmethod + def _modtable_from_pdb(cls, context: interfaces.context.ContextInterface, config_path: str, layer_name: str, + pdb_name: str, module_offset: int = None, 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: + module_size = context.layers[layer_name].maximum_address - module_offset guids = list( cls.pdbname_scan(context, @@ -323,12 +336,46 @@ class PDBUtility(interfaces.configuration.VersionableInterface): vollog.debug(f"Found {guid['pdb_name']}: {guid['GUID']}-{guid['age']}") - return cls.load_windows_symbol_table(context, - guid["GUID"], - guid["age"], - guid["pdb_name"], - "volatility3.framework.symbols.intermed.IntermediateSymbolTable", - config_path = config_path) + module_name = guid["pdb_name"].strip('.pdb') + + symbol_table_name = cls.load_windows_symbol_table(context, + guid["GUID"], + guid["age"], + guid["pdb_name"], + "volatility3.framework.symbols.intermed.IntermediateSymbolTable", + config_path = config_path) + + new_module_name = None + if create_module: + new_module = contexts.Module.create(context, module_name, layer_name, offset = guid['mz_offset'], + symbol_table_name = symbol_table_name) + new_module_name = new_module.name + + return new_module_name, symbol_table_name + + @classmethod + def module_from_pdb(cls, context: interfaces.context.ContextInterface, config_path: str, layer_name: str, + pdb_name: str, module_offset: int = None, module_size: int = None) -> str: + """Creates a module in the specified layer_name based on a pdb name. + + Searches the memory section of the loaded module for its PDB GUID + and loads the associated symbol table into the symbol space. + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + config_path: The config path where to find symbol files + layer_name: The name of the layer on which to operate + module_offset: This memory dump's module image offset + module_size: The size of the module for this dump + + Returns: + The name of the constructed and loaded symbol table + """ + + module_name, _ = cls._modtable_from_pdb(context, config_path, layer_name, pdb_name, module_offset, + module_size, create_module = True) + + return module_name class PdbSignatureScanner(interfaces.layers.ScannerInterface): From 21d916be0a08eccc91bbd4884f458ae6ff489b95 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 6 Jun 2022 14:53:52 +0100 Subject: [PATCH 32/37] Pyinstaller: Support pyinstaller 5 and later --- volatility3/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/__init__.py b/volatility3/__init__.py index db52aa9b0..b6da6e01e 100644 --- a/volatility3/__init__.py +++ b/volatility3/__init__.py @@ -37,9 +37,9 @@ class WarningFindSpec(abc.MetaPathFinder): first.""" if fullname.startswith("volatility3.framework.plugins."): warning = "Please do not use the volatility3.framework.plugins namespace directly, only use volatility3.plugins" - # Pyinstaller uses walk_packages to import, but needs to read the modules to figure out dependencies - # As such, we only print the warning when directly imported rather than from within walk_packages - if inspect.stack()[-2].function != 'walk_packages': + # Pyinstaller uses walk_packages/_collect_submodules to import, but needs to read the modules to figure out dependencies + # As such, we only print the warning when directly imported rather than from within walk_packages/_collect_submodules + if inspect.stack()[-2].function in ['walk_packages', '_collect_submodules']: raise Warning(warning) From aa06ed6e674761c8ec1238daeff9a64603aff392 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sun, 12 Jun 2022 20:32:06 +0900 Subject: [PATCH 33/37] Add: new options for vol-cli.rst --- doc/source/vol-cli.rst | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/source/vol-cli.rst b/doc/source/vol-cli.rst index 9db29c818..902787c9c 100644 --- a/doc/source/vol-cli.rst +++ b/doc/source/vol-cli.rst @@ -9,7 +9,11 @@ Synopsis **volatility** [-h] [-c CONFIG] [--parallelism [{processes,threads,off}]] [-e EXTEND] [-p PLUGIN_DIRS] [-s SYMBOL_DIRS] [-v] [-l LOG] [-o OUTPUT_DIR] [-q] [-r RENDERER] [-f FILE] - [--write-config] [--single-location SINGLE_LOCATION] + [--write-config] [--save-config SAVE_CONFIG] + [--clear-cache] [--cache-path CACHE_PATH] + [--offline] + [--single-location SINGLE_LOCATION] + [--stackers [STACKERS ...]] [--single-swap-locations SINGLE_SWAP_LOCATIONS] ... @@ -105,11 +109,31 @@ Options other plugins, but there's no guarantee that plugins use the same configuration options. +--save-config + This flag specifies that volatility should write or overwrite a file + called config.json in the current directory. The file will contain + the necessary JSON configuration to recreate the environment that the + plugin was previously run in. This configuration *may* be accepted by + other plugins, but there's no guarantee that plugins use the same + configuration options. + +--clear-cache + Clears out all short-term cached items. + +--cache-path + Change the default path ({constants.CACHE_PATH}) used to store the cache. + +--offline + Do not search online for additional JSON files. + --single-location SINGLE_LOCATION This specifies a URL which will be downloaded if necessary, and built upon by the automagic and, since most plugins require a single memory image, can be considered the input for the program. +--stackers STACKERS + + --single-swap-locations SINGLE_SWAP_LOCATIONS A comma-separated list of swap files to be considered as part of the memory image specified by the single-location or file parameters. From 2d14e4e012d6745862fafa1a857414102a412eb1 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sun, 12 Jun 2022 20:50:03 +0900 Subject: [PATCH 34/37] Add: descriptions of new options --- doc/source/vol-cli.rst | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/doc/source/vol-cli.rst b/doc/source/vol-cli.rst index 902787c9c..b9e16623d 100644 --- a/doc/source/vol-cli.rst +++ b/doc/source/vol-cli.rst @@ -102,12 +102,8 @@ Options attempt to build upon, and can be considered the input for the program. --write-config - This flag specifies that volatility should write or overwrite a file - called config.json in the current directory. The file will contain - the necessary JSON configuration to recreate the environment that the - plugin was previously run in. This configuration *may* be accepted by - other plugins, but there's no guarantee that plugins use the same - configuration options. + *Deprecated* + Use of `--write-config` has been deprecated, replaced by `--save-config` --save-config This flag specifies that volatility should write or overwrite a file @@ -121,19 +117,18 @@ Options Clears out all short-term cached items. --cache-path - Change the default path ({constants.CACHE_PATH}) used to store the cache. + Change the default path used to store the cache. --offline Do not search online for additional JSON files. + Run offline mode (defaults to false) and for + remote windows symbol tables, linux/mac banner repositories. --single-location SINGLE_LOCATION This specifies a URL which will be downloaded if necessary, and built upon by the automagic and, since most plugins require a single memory image, can be considered the input for the program. ---stackers STACKERS - - --single-swap-locations SINGLE_SWAP_LOCATIONS A comma-separated list of swap files to be considered as part of the memory image specified by the single-location or file parameters. From 3ef505641eb2f7d3d76174effb18cba69434298f Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Sun, 12 Jun 2022 20:55:15 +0900 Subject: [PATCH 35/37] Add: stacker descriptions --- doc/source/vol-cli.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/vol-cli.rst b/doc/source/vol-cli.rst index b9e16623d..cc6f7fe6a 100644 --- a/doc/source/vol-cli.rst +++ b/doc/source/vol-cli.rst @@ -129,6 +129,9 @@ Options upon by the automagic and, since most plugins require a single memory image, can be considered the input for the program. +--stackers STACKERS + Creates the list of stackers to use based on the config option. + --single-swap-locations SINGLE_SWAP_LOCATIONS A comma-separated list of swap files to be considered as part of the memory image specified by the single-location or file parameters. From e1f3f65202d7eb23901a4c9639ad1523f4429369 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 14 Jun 2022 21:03:33 +0900 Subject: [PATCH 36/37] Fix: typo for code comment, requirements name --- volatility3/framework/interfaces/configuration.py | 2 +- volatility3/framework/interfaces/layers.py | 4 ++-- volatility3/framework/plugins/mac/kevents.py | 2 +- volatility3/framework/plugins/windows/modscan.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/interfaces/configuration.py b/volatility3/framework/interfaces/configuration.py index c39dba680..e271ef6d4 100644 --- a/volatility3/framework/interfaces/configuration.py +++ b/volatility3/framework/interfaces/configuration.py @@ -523,7 +523,7 @@ class ConstructableRequirementInterface(RequirementInterface): must happen after the class configuration value has been provided). These values are then provided to the object's constructor by name as arguments (as well as the standard `context` and `config_path` - arguments. + arguments). """ def __init__(self, *args, **kwargs) -> None: diff --git a/volatility3/framework/interfaces/layers.py b/volatility3/framework/interfaces/layers.py index a42282c39..7ff110c6e 100644 --- a/volatility3/framework/interfaces/layers.py +++ b/volatility3/framework/interfaces/layers.py @@ -307,7 +307,7 @@ class DataLayerInterface(interfaces.configuration.ConfigurableInterface, metacla while length > 0: chunk_size = min(length, scanner.chunk_size + scanner.overlap) yield [(layer_name, mapped_offset, chunk_size)], offset + chunk_size - # It we've got more than the scanner's chunk_size, only move up by the chunk_size + # If we've got more than the scanner's chunk_size, only move up by the chunk_size if chunk_size > scanner.chunk_size: chunk_size -= scanner.overlap length -= chunk_size @@ -517,7 +517,7 @@ class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta): yield output, chunk_position output = [] chunk_position = chunk_start - # Take from chunk_position as far as far as the block can go, + # Take from chunk_position as far as the block can go, # or as much left of a scanner chunk as we can chunk_size = min(block_end - chunk_position, scanner.chunk_size + scanner.overlap - (chunk_position - chunk_start)) diff --git a/volatility3/framework/plugins/mac/kevents.py b/volatility3/framework/plugins/mac/kevents.py index 6f82c75cd..4a82d81cd 100644 --- a/volatility3/framework/plugins/mac/kevents.py +++ b/volatility3/framework/plugins/mac/kevents.py @@ -74,7 +74,7 @@ class Kevents(interfaces.plugins.PluginInterface): @classmethod def _walk_klist_array(cls, kernel, fdp, array_pointer_member, array_size_member): """ - Convience wrapper for walking an array of lists of kernel events + Convenience wrapper for walking an array of lists of kernel events Handles invalid address references """ try: diff --git a/volatility3/framework/plugins/windows/modscan.py b/volatility3/framework/plugins/windows/modscan.py index b661d71d7..e352c21fe 100644 --- a/volatility3/framework/plugins/windows/modscan.py +++ b/volatility3/framework/plugins/windows/modscan.py @@ -25,7 +25,7 @@ class ModScan(interfaces.plugins.PluginInterface): return [ requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', architectures = ["Intel32", "Intel64"]), - requirements.VersionRequirement(name = 'poolerscanner', + requirements.VersionRequirement(name = 'poolscanner', component = poolscanner.PoolScanner, version = (1, 0, 0)), requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)), From dd92955a99249fe9e8863cb2754229e01a917d73 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Thu, 16 Jun 2022 05:40:26 +0900 Subject: [PATCH 37/37] Remove: unreachable code --- volatility3/cli/volshell/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index 769e958fd..5eeef77cf 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -257,7 +257,6 @@ class VolShell(cli.CommandLine): constructed.run() except exceptions.VolatilityException as excp: self.process_exceptions(excp) - parser.exit(1, f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n") def main():