From 100c23ba4bc132e25c1c70f6f48fc80cd547e29d Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 8 Oct 2024 18:37:32 +1100 Subject: [PATCH 01/12] fix black stable version issue with Python 3.8 --- .github/workflows/black.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 5adab3259..e29ab6f29 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -11,3 +11,5 @@ jobs: with: options: "--check --diff --verbose" src: "./volatility3" + # FIXME: Remove when Volatility3 minimum Python version is >3.8 + version: "24.8.0" From 104f430b2c49cb5887ab90d5574eb515f5d1b8a8 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 21 Dec 2024 20:04:02 +0000 Subject: [PATCH 02/12] Plugins: Update vmayarascan and vadyarascan --- .../framework/plugins/linux/vmayarascan.py | 61 ++++++++++------- .../framework/plugins/windows/vadyarascan.py | 66 +++++++++---------- 2 files changed, 68 insertions(+), 59 deletions(-) diff --git a/volatility3/framework/plugins/linux/vmayarascan.py b/volatility3/framework/plugins/linux/vmayarascan.py index 9fe06b0c8..9f6a7a9b8 100644 --- a/volatility3/framework/plugins/linux/vmayarascan.py +++ b/volatility3/framework/plugins/linux/vmayarascan.py @@ -2,6 +2,7 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # +import logging from typing import Iterable, List, Tuple from volatility3.framework import interfaces, renderers @@ -10,12 +11,14 @@ from volatility3.framework.renderers import format_hints from volatility3.plugins import yarascan from volatility3.plugins.linux import pslist +vollog = logging.getLogger(__name__) + class VmaYaraScan(interfaces.plugins.PluginInterface): """Scans all virtual memory areas for tasks using yara.""" _required_framework_version = (2, 4, 0) - _version = (1, 0, 0) + _version = (1, 0, 2) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -33,6 +36,9 @@ class VmaYaraScan(interfaces.plugins.PluginInterface): requirements.PluginRequirement( name="yarascan", plugin=yarascan.YaraScan, version=(2, 0, 0) ), + requirements.VersionRequirement( + name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0) + ), requirements.ModuleRequirement( name="kernel", description="Linux kernel", @@ -50,6 +56,8 @@ class VmaYaraScan(interfaces.plugins.PluginInterface): # use yarascan to parse the yara options provided and create the rules rules = yarascan.YaraScan.process_yara_options(dict(self.config)) + sanity_check = 1024 * 1024 * 1024 # 1 GB + # filter based on the pid option if provided filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) for task in pslist.PsList.list_tasks( @@ -66,29 +74,36 @@ class VmaYaraScan(interfaces.plugins.PluginInterface): # get the proc_layer object from the context proc_layer = self.context.layers[proc_layer_name] - for start, end in self.get_vma_maps(task): - for match in rules.match( - data=proc_layer.read(start, end - start, True) + max_vma_size = 0 + vma_maps_to_scan = [] + for start, size in self.get_vma_maps(task): + if size > sanity_check: + vollog.debug( + f"VMA at 0x{start:x} over sanity-check size, not scanning" + ) + continue + max_vma_size = max(max_vma_size, size) + vma_maps_to_scan.append((start, size)) + + if not vma_maps_to_scan: + vollog.warning(f"No VMAs were found for task {task.tgid}, not scanning") + continue + + scanner = yarascan.YaraScanner(rules=rules) + scanner.chunk_size = max_vma_size + + # scan the VMA data (in one contiguous block) with the yarascanner + for start, size in vma_maps_to_scan: + for offset, rule_name, name, value in scanner( + proc_layer.read(start, size, pad=True), start ): - if yarascan.YaraScan.yara_returns_instances(): - for match_string in match.strings: - for instance in match_string.instances: - yield 0, ( - format_hints.Hex(instance.offset + start), - task.UniqueProcessId, - match.rule, - match_string.identifier, - instance.matched_data, - ) - else: - for offset, name, value in match.strings: - yield 0, ( - format_hints.Hex(offset + start), - task.tgid, - match.rule, - name, - value, - ) + yield 0, ( + format_hints.Hex(offset), + task.tgid, + rule_name, + name, + value, + ) @staticmethod def get_vma_maps( diff --git a/volatility3/framework/plugins/windows/vadyarascan.py b/volatility3/framework/plugins/windows/vadyarascan.py index efcc70d07..2e9cc44ea 100644 --- a/volatility3/framework/plugins/windows/vadyarascan.py +++ b/volatility3/framework/plugins/windows/vadyarascan.py @@ -32,6 +32,9 @@ class VadYaraScan(interfaces.plugins.PluginInterface): requirements.PluginRequirement( name="pslist", plugin=pslist.PsList, version=(2, 0, 0) ), + requirements.VersionRequirement( + name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0) + ), requirements.PluginRequirement( name="yarascan", plugin=yarascan.YaraScan, version=(2, 0, 0) ), @@ -66,49 +69,40 @@ class VadYaraScan(interfaces.plugins.PluginInterface): ): layer_name = task.add_process_layer() layer = self.context.layers[layer_name] + + max_vad_size = 0 + vad_maps_to_scan = [] + for start, size in self.get_vad_maps(task): if size > sanity_check: vollog.debug( f"VAD at 0x{start:x} over sanity-check size, not scanning" ) continue + max_vad_size = max(max_vad_size, size) + vad_maps_to_scan.append((start, size)) - data = layer.read(start, size, True) - if not yarascan.YaraScan._yara_x: - for match in rules.match(data=data): - if yarascan.YaraScan.yara_returns_instances(): - for match_string in match.strings: - for instance in match_string.instances: - yield 0, ( - format_hints.Hex(instance.offset + start), - task.UniqueProcessId, - match.rule, - match_string.identifier, - instance.matched_data, - ) - else: - for offset, name, value in match.strings: - yield 0, ( - format_hints.Hex(offset + start), - task.UniqueProcessId, - match.rule, - name, - value, - ) - else: - for match in rules.scan(data).matching_rules: - for match_string in match.patterns: - for instance in match_string.matches: - yield 0, ( - format_hints.Hex(instance.offset + start), - task.UniqueProcessId, - f"{match.namespace}.{match.identifier}", - match_string.identifier, - data[ - instance.offset : instance.offset - + instance.length - ], - ) + if not vad_maps_to_scan: + vollog.warning( + f"No VADs were found for task {task.UniqueProcessID}, not scanning" + ) + continue + + scanner = yarascan.YaraScanner(rules=rules) + scanner.chunk_size = max_vad_size + + # scan the VAD data (in one contiguous block) with the yarascanner + for start, size in vad_maps_to_scan: + for offset, rule_name, name, value in scanner( + layer.read(start, size, pad=True), start + ): + yield 0, ( + format_hints.Hex(offset), + task.UniqueProcessId, + rule_name, + name, + value, + ) @staticmethod def get_vad_maps( From 3ab5fa90424b86904bca0a4ca987370aa890249f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 16 Jan 2025 19:59:31 +0000 Subject: [PATCH 03/12] Update copyright dates --- README.md | 2 +- doc/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1463c2bde..4e6953592 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ The latest generated copy of the documentation can be found at: Date: Mon, 7 Apr 2025 14:56:19 +0100 Subject: [PATCH 04/12] Ensure the release branch has the right version number --- volatility3/framework/constants/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/constants/_version.py b/volatility3/framework/constants/_version.py index a299f15a2..64707b782 100644 --- a/volatility3/framework/constants/_version.py +++ b/volatility3/framework/constants/_version.py @@ -1,7 +1,7 @@ # We use the SemVer 2.0.0 versioning scheme VERSION_MAJOR = 2 # Number of releases of the library with a breaking change VERSION_MINOR = 26 # Number of changes that only add to the interface -VERSION_PATCH = 1 # Number of changes that do not change the interface +VERSION_PATCH = 0 # Number of changes that do not change the interface VERSION_SUFFIX = "" PACKAGE_VERSION = ( From 0882fd0b779f718981f92179ec9a99152b7e70a2 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Mon, 7 Apr 2025 15:39:23 -0500 Subject: [PATCH 05/12] Fix traceback in volshell's `dt()` A `SymbolError` can occur when a type contains a pointer to an opaque type. For example, `_EPROCESS` can have a member that points to an `_EPROCESS_QUOTA_BLOCK`, but there is no definition for that type, so its size and readability can't be determined. This wraps the block in a try/except, and reports that the type has an unknown size in the suffix if a `SymbolError` occurs. --- volatility3/cli/volshell/generic.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/volatility3/cli/volshell/generic.py b/volatility3/cli/volshell/generic.py index 32a5bd933..7c85eec5f 100644 --- a/volatility3/cli/volshell/generic.py +++ b/volatility3/cli/volshell/generic.py @@ -621,12 +621,15 @@ class Volshell(interfaces.plugins.PluginInterface): if isinstance(value, objects.Pointer): # show pointers in hex to match output for struct addrs # highlight null or unreadable pointers - if value == 0: - suffix = " (null pointer)" - elif not value.is_readable(): - suffix = " (unreadable pointer)" - else: - suffix = "" + try: + if value == 0: + suffix = " (null pointer)" + elif not value.is_readable(): + suffix = " (unreadable pointer)" + else: + suffix = "" + except exceptions.SymbolError as exc: + suffix = f" (pointer to {exc.symbol_name} - unknown size)" return f"{hex(value)}{suffix}" elif isinstance(value, objects.PrimitiveObject): return repr(value) From 4a528d55a57298c81efb63900e92f2c5f406f6b5 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Mon, 7 Apr 2025 16:18:22 -0500 Subject: [PATCH 06/12] Shorten suffix --- volatility3/cli/volshell/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/volshell/generic.py b/volatility3/cli/volshell/generic.py index 7c85eec5f..2ea722a37 100644 --- a/volatility3/cli/volshell/generic.py +++ b/volatility3/cli/volshell/generic.py @@ -629,7 +629,7 @@ class Volshell(interfaces.plugins.PluginInterface): else: suffix = "" except exceptions.SymbolError as exc: - suffix = f" (pointer to {exc.symbol_name} - unknown size)" + suffix = f" (unknown sized {exc.symbol_name})" return f"{hex(value)}{suffix}" elif isinstance(value, objects.PrimitiveObject): return repr(value) From 1ab8ddcb7faee4b4a28976e86397f8d9df1a862a Mon Sep 17 00:00:00 2001 From: ikelos Date: Wed, 9 Apr 2025 20:35:19 +0100 Subject: [PATCH 07/12] Merge pull request #1765 from Abyss-W4tcher/minimum_alignment_adjustment [Parity/modules] Adjust module alignment for scanners --- volatility3/framework/symbols/linux/utilities/modules.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/volatility3/framework/symbols/linux/utilities/modules.py b/volatility3/framework/symbols/linux/utilities/modules.py index 62ebeef03..1c675a283 100644 --- a/volatility3/framework/symbols/linux/utilities/modules.py +++ b/volatility3/framework/symbols/linux/utilities/modules.py @@ -474,10 +474,7 @@ class Modules(interfaces.configuration.VersionableInterface): Returns: The struct module alignment """ - # FIXME: When dwarf2json/ISF supports type alignments. Read it directly from the type metadata - # Additionally, while 'context' and 'vmlinux_module_name' are currently unused, they will be - # essential for retrieving type metadata in the future. - return 64 + return context.modules[vmlinux_module_name].get_type("pointer").size @classmethod def list_modules( From 537efa60e28d0a9ad23c5b0017618c853f338441 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Tue, 15 Apr 2025 13:24:59 -0500 Subject: [PATCH 08/12] Thrdscan: Remove filtering based on VAD count This was preventing enumeration of valid processes (confirmed by disassembly of the start address/Win32 start address). Heuristic-based filtering should probably be left to consumers of the APIs. --- volatility3/framework/plugins/windows/thrdscan.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index 0ac3d0c33..8fe13ba64 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -119,11 +119,6 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) vads = pe_symbols.PESymbols.get_vads_for_process_cache( vads_cache, owner_proc ) - if not vads or len(vads) < 5: - vollog.debug( - f"Not enough vads for process at {owner_proc.vol.offset:#x}. Skipping thread at {ethread.vol.offset:#x}" - ) - return None start_path = pe_symbols.PESymbols.filepath_for_address( vads, thread_start_addr From b04a498cd755ad511e85c923a00943551b991b41 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Tue, 15 Apr 2025 13:23:41 -0500 Subject: [PATCH 09/12] ThrdScan: Fix process filtering This check was both causing an `InvalidAddressException` due to the member access, while at the same time not being a useful check, since it prevents VADs from being mapped in children of the `System` process. --- .../framework/plugins/windows/thrdscan.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index 8fe13ba64..1e7466dc5 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -111,24 +111,23 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) return None # don't look for VADs in kernel threads, just let them get reported with empty paths - if ( - owner_proc_pid != 4 - and owner_proc.InheritedFromUniqueProcessId != 4 - and vads_cache is not None - ): + if owner_proc_pid != 4 and vads_cache is not None: vads = pe_symbols.PESymbols.get_vads_for_process_cache( vads_cache, owner_proc ) - - start_path = pe_symbols.PESymbols.filepath_for_address( - vads, thread_start_addr - ) - win32start_path = pe_symbols.PESymbols.filepath_for_address( - vads, thread_win32start_addr - ) else: - start_path = None - win32start_path = None + vads = None + + start_path = ( + pe_symbols.PESymbols.filepath_for_address(vads, thread_start_addr) + if vads + else None + ) + win32start_path = ( + pe_symbols.PESymbols.filepath_for_address(vads, thread_win32start_addr) + if vads + else None + ) return ( format_hints.Hex(thread_offset), From 718f8ca2b583d8fa8626502566f31fe1d5b92b3e Mon Sep 17 00:00:00 2001 From: ikelos Date: Tue, 6 May 2025 22:41:21 +0100 Subject: [PATCH 10/12] Update volatility3/framework/plugins/linux/vmayarascan.py --- volatility3/framework/plugins/linux/vmayarascan.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/vmayarascan.py b/volatility3/framework/plugins/linux/vmayarascan.py index 9a413dd9d..64f5827c1 100644 --- a/volatility3/framework/plugins/linux/vmayarascan.py +++ b/volatility3/framework/plugins/linux/vmayarascan.py @@ -39,9 +39,6 @@ class VmaYaraScan(interfaces.plugins.PluginInterface): requirements.VersionRequirement( name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0) ), - requirements.VersionRequirement( - name="yarascanner", component=yarascan.YaraScanner, version=(2, 0, 0) - ), requirements.ModuleRequirement( name="kernel", description="Linux kernel", From c0c7119c38e18ed92349d2ee7affe837bcba3aa0 Mon Sep 17 00:00:00 2001 From: ikelos Date: Tue, 6 May 2025 22:41:27 +0100 Subject: [PATCH 11/12] Update volatility3/framework/plugins/windows/thrdscan.py --- volatility3/framework/plugins/windows/thrdscan.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index 81ed9976f..d5a1a0b07 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -143,18 +143,8 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) else None ) else: - vads = None - - start_path = ( - pe_symbols.PESymbols.filepath_for_address(vads, thread_start_addr) - if vads - else None - ) - win32start_path = ( - pe_symbols.PESymbols.filepath_for_address(vads, thread_win32start_addr) - if vads - else None - ) + start_path = None + win32start_path = None return cls.ThreadInfo( thread_offset, From 606fecf5f81b5adcc5fee1ea82b870ceafb826d1 Mon Sep 17 00:00:00 2001 From: ikelos Date: Tue, 6 May 2025 22:41:33 +0100 Subject: [PATCH 12/12] Update volatility3/framework/plugins/windows/vadyarascan.py --- volatility3/framework/plugins/windows/vadyarascan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/vadyarascan.py b/volatility3/framework/plugins/windows/vadyarascan.py index 62d3f8862..04b9aadd9 100644 --- a/volatility3/framework/plugins/windows/vadyarascan.py +++ b/volatility3/framework/plugins/windows/vadyarascan.py @@ -99,7 +99,6 @@ class VadYaraScan(interfaces.plugins.PluginInterface): layer_name=layer.name, length=len(value), ) - yield 0, ( format_hints.Hex(offset), task.UniqueProcessId,