From 5e96327cb0851fe75d1c7a4cb9ef27641bf119f7 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 21 Jul 2024 22:58:43 +0100 Subject: [PATCH 1/5] Add in threads that only provides an implmentation method --- .../framework/plugins/windows/thrdscan.py | 32 +++++++++++------ .../framework/plugins/windows/threads.py | 34 +++++++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index bbf65cd6c..be7097347 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -3,7 +3,7 @@ ## import logging import datetime -from typing import Iterable +from typing import Callable, Iterable from volatility3.framework import renderers, interfaces, exceptions from volatility3.framework.configuration import requirements @@ -21,6 +21,10 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) _required_framework_version = (2, 6, 0) _version = (1, 0, 0) + def __init__(self, *args, **kwargs): + self.implementation = self.scan_threads + super().__init__(*args, **kwargs) + @classmethod def get_requirements(cls): return [ @@ -38,8 +42,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) def scan_threads( cls, context: interfaces.context.ContextInterface, - layer_name: str, - symbol_table: str, + module_name: str, ) -> Iterable[interfaces.objects.ObjectInterface]: """Scans for threads using the poolscanner module and constraints. @@ -52,6 +55,10 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) A list of _ETHREAD objects found by scanning memory for the "Thre" / "Thr\\xE5" pool signatures """ + module = context.modules[module_name] + layer_name = module.layer_name + symbol_table = module.symbol_table_name + constraints = poolscanner.PoolScanner.builtin_constraints( symbol_table, [b"Thr\xe5", b"Thre"] ) @@ -76,7 +83,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) ethread.get_exit_time() ) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object except exceptions.InvalidAddressException: - vollog.debug("Thread invalid address {:#x}".format(thread.vol.offset)) + vollog.debug("Thread invalid address {:#x}".format(ethread.vol.offset)) return None return ( @@ -88,12 +95,10 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) thread_exit_time, ) - def _generator(self): - kernel = self.context.modules[self.config["kernel"]] + def _generator(self, filter_func: Callable): + kernel_name = self.config["kernel"] - for ethread in self.scan_threads( - self.context, kernel.layer_name, kernel.symbol_table_name - ): + for ethread in self.implementation(self.context, kernel_name): info = self.gather_thread_info(ethread) if info: yield (0, info) @@ -126,7 +131,14 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) row_dict["ExitTime"], ) + @classmethod + def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable: + """Returns a function that can filter this plugin's implementation method based on the config""" + return lambda x: False + def run(self): + filt_func = self.filter_func(self.config) + return renderers.TreeGrid( [ ("Offset", format_hints.Hex), @@ -136,5 +148,5 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) ("CreateTime", datetime.datetime), ("ExitTime", datetime.datetime), ], - self._generator(), + self._generator(filt_func), ) diff --git a/volatility3/framework/plugins/windows/threads.py b/volatility3/framework/plugins/windows/threads.py index 83d231abb..55720c6f3 100644 --- a/volatility3/framework/plugins/windows/threads.py +++ b/volatility3/framework/plugins/windows/threads.py @@ -3,7 +3,7 @@ # import logging -from typing import List, Generator +from typing import Callable, Iterable, List, Generator from volatility3.framework import interfaces, constants from volatility3.framework.configuration import requirements @@ -18,6 +18,9 @@ class Threads(thrdscan.ThrdScan): _required_framework_version = (2, 4, 0) _version = (1, 0, 0) + def __init__(self): + self.implementation = self.list_process_threads + @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: # Since we're calling the plugin, make sure we have the plugin's requirements @@ -60,18 +63,27 @@ class Threads(thrdscan.ThrdScan): seen.add(thread.vol.offset) yield thread - def _generator(self): - kernel = self.context.modules[self.config["kernel"]] + @classmethod + def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable: + return pslist.PsList.create_pid_filter(config.get("pid", None)) - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + @classmethod + def list_process_threads( + cls, + context: interfaces.context.ContextInterface, + module_name: str, + filter_func: Callable, + ) -> Iterable[interfaces.objects.ObjectInterface]: + """Runs through all processes and lists threads for each process""" + module = context.modules[module_name] + layer_name = module.layer_name + symbol_table_name = module.symbol_table_name for proc in pslist.PsList.list_processes( - context=self.context, - layer_name=kernel.layer_name, - symbol_table=kernel.symbol_table_name, + context=context, + layer_name=layer_name, + symbol_table=symbol_table_name, filter_func=filter_func, ): - for thread in self.list_threads(kernel, proc): - info = self.gather_thread_info(thread) - if info: - yield (0, info) + for thread in cls.list_threads(module, proc): + yield thread From b8b146a4441f054bccf5697868c26250ea99d86d Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 24 Jul 2024 10:59:52 +0100 Subject: [PATCH 2/5] Windows: update handles plugin to use a default SAR value of 0x10 if decoding fails. Produce warnings when this happens. Ref issue #1147 --- volatility3/framework/plugins/windows/handles.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/windows/handles.py b/volatility3/framework/plugins/windows/handles.py index a19e7a397..2edf28e86 100644 --- a/volatility3/framework/plugins/windows/handles.py +++ b/volatility3/framework/plugins/windows/handles.py @@ -25,7 +25,7 @@ class Handles(interfaces.plugins.PluginInterface): """Lists process open handles.""" _required_framework_version = (2, 0, 0) - _version = (1, 0, 1) + _version = (1, 0, 2) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -175,10 +175,11 @@ class Handles(interfaces.plugins.PluginInterface): virtual_layer_name, func_addr_to_read, num_bytes_to_read ) except exceptions.InvalidAddressException: - vollog.debug( - f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}" + vollog.warning( + f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}. Unable to decode SAR value. Failing back to a common value of 0x10" ) - return None + self._sar_value = 0x10 + return self._sar_value md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) @@ -198,9 +199,10 @@ class Handles(interfaces.plugins.PluginInterface): break if self._sar_value is None: - vollog.debug( - f"Failed to to locate SAR value having parsed {instruction_count} instructions" + vollog.warning( + f"Failed to to locate SAR value having parsed {instruction_count} instructions, failing back to a common value of 0x10" ) + self._sar_value = 0x10 return self._sar_value From 7d52f7992d187e4c18425ca71171dc27a9710903 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 24 Jul 2024 11:06:26 +0100 Subject: [PATCH 3/5] Windows: Make the default sar value used in handles plugin a variable so if it needs to be changed it gets updated in one place only --- volatility3/framework/plugins/windows/handles.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/handles.py b/volatility3/framework/plugins/windows/handles.py index 2edf28e86..abe154fa1 100644 --- a/volatility3/framework/plugins/windows/handles.py +++ b/volatility3/framework/plugins/windows/handles.py @@ -142,6 +142,7 @@ class Handles(interfaces.plugins.PluginInterface): pointers in the _HANDLE_TABLE_ENTRY which allows us to find the associated _OBJECT_HEADER. """ + DEFAULT_SAR_VALUE = 0x10 # to be used only when decoding fails if self._sar_value is None: if not has_capstone: @@ -178,7 +179,7 @@ class Handles(interfaces.plugins.PluginInterface): vollog.warning( f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}. Unable to decode SAR value. Failing back to a common value of 0x10" ) - self._sar_value = 0x10 + self._sar_value = DEFAULT_SAR_VALUE return self._sar_value md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) @@ -202,7 +203,7 @@ class Handles(interfaces.plugins.PluginInterface): vollog.warning( f"Failed to to locate SAR value having parsed {instruction_count} instructions, failing back to a common value of 0x10" ) - self._sar_value = 0x10 + self._sar_value = DEFAULT_SAR_VALUE return self._sar_value From e1065f9e788322faded83520da29cd5cbcd5d5ed Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 24 Jul 2024 20:32:36 +0100 Subject: [PATCH 4/5] Fix up a missing super which @atcuno spotted --- volatility3/framework/plugins/windows/threads.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/plugins/windows/threads.py b/volatility3/framework/plugins/windows/threads.py index 55720c6f3..d57911650 100644 --- a/volatility3/framework/plugins/windows/threads.py +++ b/volatility3/framework/plugins/windows/threads.py @@ -20,6 +20,7 @@ class Threads(thrdscan.ThrdScan): def __init__(self): self.implementation = self.list_process_threads + super().__init__() @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: From f4cbed856bcfb011707588d5563cd7144450ba1e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 24 Jul 2024 20:47:05 +0100 Subject: [PATCH 5/5] Fix up missing filter parameter --- volatility3/framework/plugins/windows/thrdscan.py | 7 +++++-- volatility3/framework/plugins/windows/threads.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/thrdscan.py b/volatility3/framework/plugins/windows/thrdscan.py index be7097347..6e664e052 100644 --- a/volatility3/framework/plugins/windows/thrdscan.py +++ b/volatility3/framework/plugins/windows/thrdscan.py @@ -19,7 +19,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) # version 2.6.0 adds support for scanning for 'Ethread' structures by pool tags _required_framework_version = (2, 6, 0) - _version = (1, 0, 0) + _version = (1, 1, 0) def __init__(self, *args, **kwargs): self.implementation = self.scan_threads @@ -100,11 +100,14 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) for ethread in self.implementation(self.context, kernel_name): info = self.gather_thread_info(ethread) + if info: yield (0, info) def generate_timeline(self): - for row in self._generator(): + filt_func = self.filter_func(self.config) + + for row in self._generator(filt_func): _depth, row_data = row row_dict = {} ( diff --git a/volatility3/framework/plugins/windows/threads.py b/volatility3/framework/plugins/windows/threads.py index d57911650..ae70e717b 100644 --- a/volatility3/framework/plugins/windows/threads.py +++ b/volatility3/framework/plugins/windows/threads.py @@ -38,7 +38,7 @@ class Threads(thrdscan.ThrdScan): optional=True, ), requirements.PluginRequirement( - name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 0, 0) + name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 1, 0) ), ]