From aee81276a2d921e44e58db28174f7e4782fe7423 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 11:27:32 -0500 Subject: [PATCH 01/32] Add processghosting plugin to detect process ghosting and related techniques. Add process filter that gathers only active, non-smeared userland processes --- .../plugins/windows/processghosting.py | 99 +++++++++++++++++++ .../framework/plugins/windows/pslist.py | 21 ++++ 2 files changed, 120 insertions(+) create mode 100644 volatility3/framework/plugins/windows/processghosting.py diff --git a/volatility3/framework/plugins/windows/processghosting.py b/volatility3/framework/plugins/windows/processghosting.py new file mode 100644 index 000000000..6c311f555 --- /dev/null +++ b/volatility3/framework/plugins/windows/processghosting.py @@ -0,0 +1,99 @@ +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +import logging + +from volatility3.framework import interfaces, exceptions +from volatility3.framework import renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints +from volatility3.plugins.windows import pslist + +vollog = logging.getLogger(__name__) + + +class ProcessGhosting(interfaces.plugins.PluginInterface): + """Lists processes whose DeletePending bit is set or whose FILE_OBJECT is set to 0""" + + _required_framework_version = (2, 4, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="pslist", component=pslist.PsList, version=(2, 0, 0) + ), + ] + + def _generator(self, procs): + # determine if we're on a 32 or 64 bit kernel + kernel = self.context.modules[self.config["kernel"]] + + if not kernel.get_type("_EPROCESS").has_member("ImageFilePointer"): + vollog.warning("This plugin only supports Windows 10 builds when the ImageFilePointer member of _EPROCESS is present") + return + + for proc in procs: + delete_pending = renderers.UnreadableValue() + process_name = utility.array_to_string(proc.ImageFileName) + + # if it is 0 then its a side effect of process ghosting + if proc.ImageFilePointer.vol.offset != 0: + try: + file_object = proc.ImageFilePointer + delete_pending = file_object.DeletePending + except exceptions.InvalidAddressException: + file_object = 0 + + # ImageFilePointer equal to 0 means process ghosting or similar techniques were used + else: + file_object = 0 + + # delete_pending besides 0 or 1 = smear + if file_object == 0 or delete_pending == 1: + path = renderers.UnreadableValue() + if file_object: + try: + path = file_object.FileName.String + except exceptions.InvalidAddressException: + path = renderers.UnreadableValue() + + yield ( + 0, + ( + proc.UniqueProcessId, + process_name, + format_hints.Hex(file_object), + delete_pending, + path + ), + ) + + def run(self): + filter_func = pslist.PsList.create_active_process_filter() + kernel = self.context.modules[self.config["kernel"]] + + return renderers.TreeGrid( + [ + ("PID", int), + ("Process", str), + ("FILE_OBJECT", format_hints.Hex), + ("DeletePending", str), + ("Path", str), + ], + self._generator( + pslist.PsList.list_processes( + context=self.context, + layer_name=kernel.layer_name, + symbol_table=kernel.symbol_table_name, + filter_func=filter_func, + ) + ), + ) diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index e7a0d5dd4..8ad0f9a05 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -136,6 +136,27 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): filter_func = lambda x: x.UniqueProcessId not in filter_list return filter_func + @classmethod + def create_active_process_filter( + cls + ) -> Callable[[interfaces.objects.ObjectInterface], bool]: + """A factory for producing a filter function that only returns + active, userland processes. This prevents plugins from operating on terminated + processes that are still in the process list due to smear or handle leaks as well + as kernel processes (System, Registry, etc.). Use of this filter for plugins searching + for system state anomalies significantly reduces false positive in smeared and terminated + processes. + Returns: + Filter function for passing to the `list_processes` method + """ + + return lambda x: not (x.is_valid() and \ + x.ActiveThreads > 0 and \ + x.UniqueProcessId != 4 and \ + x.InheritedFromUniqueProcessId != 4 and \ + x.ExitTime.QuadPart == 0 and \ + x.get_handle_count() != renderers.UnreadableValue()) + @classmethod def create_name_filter( cls, name_list: List[str] = None, exclude: bool = False From 6c42c51a8a6201255f120321885309cd92611c77 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 11:31:43 -0500 Subject: [PATCH 02/32] Fixes for black --- .../framework/plugins/windows/processghosting.py | 7 ++++--- volatility3/framework/plugins/windows/pslist.py | 16 +++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/windows/processghosting.py b/volatility3/framework/plugins/windows/processghosting.py index 6c311f555..b29ff04f0 100644 --- a/volatility3/framework/plugins/windows/processghosting.py +++ b/volatility3/framework/plugins/windows/processghosting.py @@ -33,11 +33,12 @@ class ProcessGhosting(interfaces.plugins.PluginInterface): ] def _generator(self, procs): - # determine if we're on a 32 or 64 bit kernel kernel = self.context.modules[self.config["kernel"]] if not kernel.get_type("_EPROCESS").has_member("ImageFilePointer"): - vollog.warning("This plugin only supports Windows 10 builds when the ImageFilePointer member of _EPROCESS is present") + vollog.warning( + "This plugin only supports Windows 10 builds when the ImageFilePointer member of _EPROCESS is present" + ) return for proc in procs: @@ -72,7 +73,7 @@ class ProcessGhosting(interfaces.plugins.PluginInterface): process_name, format_hints.Hex(file_object), delete_pending, - path + path, ), ) diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 8ad0f9a05..55a3fc280 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -138,7 +138,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): @classmethod def create_active_process_filter( - cls + cls, ) -> Callable[[interfaces.objects.ObjectInterface], bool]: """A factory for producing a filter function that only returns active, userland processes. This prevents plugins from operating on terminated @@ -150,12 +150,14 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): Filter function for passing to the `list_processes` method """ - return lambda x: not (x.is_valid() and \ - x.ActiveThreads > 0 and \ - x.UniqueProcessId != 4 and \ - x.InheritedFromUniqueProcessId != 4 and \ - x.ExitTime.QuadPart == 0 and \ - x.get_handle_count() != renderers.UnreadableValue()) + return lambda x: not ( + x.is_valid() and + x.ActiveThreads > 0 and + x.UniqueProcessId != 4 and + x.InheritedFromUniqueProcessId != 4 and + x.ExitTime.QuadPart == 0 and + x.get_handle_count() != renderers.UnreadableValue() + ) @classmethod def create_name_filter( From 4e15019c46ae24899ea0527dd4855ade5fd7af00 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 11:34:06 -0500 Subject: [PATCH 03/32] Fixes for black --- volatility3/framework/plugins/windows/pslist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 55a3fc280..8234b210f 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -151,12 +151,12 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): """ return lambda x: not ( - x.is_valid() and - x.ActiveThreads > 0 and - x.UniqueProcessId != 4 and - x.InheritedFromUniqueProcessId != 4 and - x.ExitTime.QuadPart == 0 and - x.get_handle_count() != renderers.UnreadableValue() + x.is_valid() + and x.ActiveThreads > 0 + and x.UniqueProcessId != 4 + and x.InheritedFromUniqueProcessId != 4 + and x.ExitTime.QuadPart == 0 + and x.get_handle_count() != renderers.UnreadableValue() ) @classmethod From c4e7e50180a45dfb80b453b61e9b94860d5a01d3 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 14:42:23 -0500 Subject: [PATCH 04/32] Add svclist and svcdiff plugins. Make svcscan more modular to support inheritance and cleaner code --- .../framework/plugins/windows/svcdiff.py | 74 ++++++++++++ .../framework/plugins/windows/svclist.py | 86 ++++++++++++++ .../framework/plugins/windows/svcscan.py | 111 +++++++++++------- .../framework/symbols/windows/versions.py | 9 ++ 4 files changed, 235 insertions(+), 45 deletions(-) create mode 100644 volatility3/framework/plugins/windows/svcdiff.py create mode 100644 volatility3/framework/plugins/windows/svclist.py diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py new file mode 100644 index 000000000..03f0afcd3 --- /dev/null +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -0,0 +1,74 @@ +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +# This module attempts to locate skeleton-key like function hooks. +# It does this by locating the CSystems array through a variety of methods, +# and then validating the entry for RC4 HMAC (0x17 / 23) +# +# 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 logging + +from volatility3.framework import symbols +from volatility3.framework.configuration import requirements +from volatility3.plugins.windows import svclist, svcscan +from volatility3.framework.symbols.windows import versions + +vollog = logging.getLogger(__name__) + +class SvcDiff(svclist.SvcList, svcscan.SvcScan): + """Compares services found through list walking versus scanning to find rootkits""" + + _required_framework_version = (2, 4, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="svclist", component=svclist.SvcList, version=(1, 0, 0) + ), + requirements.VersionRequirement( + name="svcscan", component=svcscan.SvcScan, version=(2, 0, 0) + ), + ] + + def _generator(self): + """ + Finds services by walking the services.exe list on supported Windows 10 versions + """ + kernel = self.context.modules[self.config["kernel"]] + + if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) or \ + not versions.is_win10_15063_or_later(context=self.context, symbol_table=kernel.symbol_table_name): + vollog.info("This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples") + return + + from_scan = set() + from_list = set() + records = {} + + service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + + # collect unique service names from scanning + for service in self.service_scan(service_table_name, service_binary_dll_map, filter_func): + from_scan.add(service[6]) + records[service[6]] = service + + # collect services from listing walking + for service in self.service_list(service_table_name, service_binary_dll_map, filter_func): + from_list.add(service[6]) + + # report services found from scanning but not list walking + for hidden_service in from_scan-from_list: + yield (0, records[hidden_service]) + diff --git a/volatility3/framework/plugins/windows/svclist.py b/volatility3/framework/plugins/windows/svclist.py new file mode 100644 index 000000000..b4541981d --- /dev/null +++ b/volatility3/framework/plugins/windows/svclist.py @@ -0,0 +1,86 @@ +# 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 +# + +import logging + +from typing import List + +from volatility3.framework import interfaces, exceptions, symbols +from volatility3.framework.configuration import requirements +from volatility3.framework.symbols.windows import versions +from volatility3.plugins.windows import svcscan, pslist +from volatility3.framework.layers import scanners + +vollog = logging.getLogger(__name__) + + +class SvcList(svcscan.SvcScan): + """Lists services contained with the services.exe doubly linked list of services""" + + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.PluginRequirement( + name="svcscan", plugin=svcscan.SvcScan, version=(2, 0, 0) + ), + ] + + def _get_exe_range(self, proc): + """ + Returns a tuple of starting,ending address for + the VAD containing services.exe + """ + + vad_root = proc.get_vad_root() + for vad in vad_root.traverse(): + filename = vad.get_file_name() + if isinstance(filename, str) and filename.lower().endswith("\\services.exe"): + return [(vad.get_start(), vad.get_size())] + + return None + + def service_list(self, service_table_name, service_binary_dll_map, filter_func): + kernel = self.context.modules[self.config["kernel"]] + + if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) or \ + not versions.is_win10_15063_or_later(context=self.context, symbol_table=kernel.symbol_table_name): + vollog.info("This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples") + return + + for proc in pslist.PsList.list_processes( + context=self.context, + layer_name=kernel.layer_name, + symbol_table=kernel.symbol_table_name, + filter_func=filter_func, + ): + try: + layer_name = proc.add_process_layer() + except exceptions.InvalidAddressException: + vollog.warning("Unable to access memory of services.exe running with PID: {}".format(proc.UniqueProcessId)) + continue + + layer = self.context.layers[layer_name] + + exe_range = self._get_exe_range(proc) + if not exe_range: + vollog.warning("Could not find the application executable VAD for services.exe. Unable to proceed.") + continue + + for offset in layer.scan( + context=self.context, + scanner=scanners.BytesScanner(needle = b"Sc27"), + sections=exe_range, + ): + for record in self.enumerate_vista_or_later_header(service_table_name, service_binary_dll_map, layer_name, offset): + yield record + + def _generator(self): + service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + + for record in self.service_list(service_table_name, service_binary_dll_map, filter_func): + yield (0, record) + diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 10de46e2a..08d11a946 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -19,7 +19,7 @@ from volatility3.framework.layers import scanners from volatility3.framework.renderers import format_hints from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows import versions -from volatility3.framework.symbols.windows.extensions import services +from volatility3.framework.symbols.windows.extensions import services as services_types from volatility3.plugins.windows import poolscanner, pslist, vadyarascan from volatility3.plugins.windows.registry import hivelist @@ -140,7 +140,7 @@ class SvcScan(interfaces.plugins.PluginInterface): config_path, os.path.join("windows", "services"), symbol_filename, - class_types=services.class_types, + class_types=services_types.class_types, native_types=native_types, ) @@ -232,28 +232,44 @@ class SvcScan(interfaces.plugins.PluginInterface): for service_key in services } - def _generator(self): + def enumerate_vista_or_later_header( + self, + service_table_name, + service_binary_dll_map, + proc_layer_name, + offset + ): + if offset % 8: + return + + service_header = self.context.object( + service_table_name + constants.BANG + "_SERVICE_HEADER", + offset=offset, + layer_name=proc_layer_name, + ) + + if not service_header.is_valid(): + return + + # since we walk the s-list backwards, if we've seen + # an object, then we've also seen all objects that + # exist before it, thus we can break at that time. + for service_record in service_header.ServiceRecord.traverse(): + service_info = service_binary_dll_map.get( + service_record.get_name(), + ServiceBinaryInfo( + renderers.UnreadableValue(), renderers.UnreadableValue() + ), + ) + yield self.get_record_tuple(service_record, service_info) + + def service_scan(self, service_table_name, service_binary_dll_map, filter_func): kernel = self.context.modules[self.config["kernel"]] - service_table_name = self.create_service_table( - self.context, kernel.symbol_table_name, self.config_path - ) - - # Building the dictionary ahead of time is much better for performance - # vs looking up each service's DLL individually. - services_key = self._get_service_key(kernel) - service_binary_dll_map = ( - self._get_service_binary_map(services_key) - if services_key is not None - else {} - ) - relative_tag_offset = self.context.symbol_space.get_type( service_table_name + constants.BANG + "_SERVICE_RECORD" ).relative_child_offset("Tag") - filter_func = pslist.PsList.create_name_filter(["services.exe"]) - is_vista_or_later = versions.is_vista_or_later( context=self.context, symbol_table=kernel.symbol_table_name ) @@ -306,37 +322,42 @@ class SvcScan(interfaces.plugins.PluginInterface): renderers.UnreadableValue(), renderers.UnreadableValue() ), ) - yield ( - 0, - self.get_record_tuple(service_record, service_info), - ) + yield self.get_record_tuple(service_record, service_info) else: - service_header = self.context.object( - service_table_name + constants.BANG + "_SERVICE_HEADER", - offset=offset, - layer_name=proc_layer_name, - ) - - if not service_header.is_valid(): - continue - - # since we walk the s-list backwards, if we've seen - # an object, then we've also seen all objects that - # exist before it, thus we can break at that time. - for service_record in service_header.ServiceRecord.traverse(): + for service_record in self.enumerate_vista_or_later_header(service_table_name, service_binary_dll_map, proc_layer_name, offset): if service_record in seen: break seen.append(service_record) - service_info = service_binary_dll_map.get( - service_record.get_name(), - ServiceBinaryInfo( - renderers.UnreadableValue(), renderers.UnreadableValue() - ), - ) - yield ( - 0, - self.get_record_tuple(service_record, service_info), - ) + yield service_record + + + def get_prereq_info(self): + """ + Data structures and information needed to analyze service information + """ + kernel = self.context.modules[self.config["kernel"]] + + service_table_name = self.create_service_table( + self.context, kernel.symbol_table_name, self.config_path + ) + + services_key = self._get_service_key(kernel) + + service_binary_dll_map = ( + self._get_service_binary_map(services_key) + if services_key is not None + else {} + ) + + filter_func = pslist.PsList.create_name_filter(["services.exe"]) + + return service_table_name, service_binary_dll_map, filter_func + + def _generator(self): + service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + + for record in self.service_scan(service_table_name, service_binary_dll_map, filter_func): + yield (0, record) def run(self): return renderers.TreeGrid( diff --git a/volatility3/framework/symbols/windows/versions.py b/volatility3/framework/symbols/windows/versions.py index e1e74afc0..d8964f575 100644 --- a/volatility3/framework/symbols/windows/versions.py +++ b/volatility3/framework/symbols/windows/versions.py @@ -141,6 +141,15 @@ is_win10_15063 = OsDistinguisher( ], ) +is_win10_15063_or_later = OsDistinguisher( + version_check=lambda x: x >= (10, 0, 15063), + fallback_checks=[ + ("ObHeaderCookie", None, True), + ("_HANDLE_TABLE", "HandleCount", False), + ("_EPROCESS", "KeepAliveCounter", False), + ], +) + is_win10_16299_or_later = OsDistinguisher( version_check=lambda x: x >= (10, 0, 16299), fallback_checks=[ From 91184c7d92f84cdd3371d3a2371cf1784a2eb1a2 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 14:54:20 -0500 Subject: [PATCH 05/32] Format fixes --- .../framework/plugins/windows/svcdiff.py | 23 ++++++++---- .../framework/plugins/windows/svclist.py | 36 +++++++++++++------ .../framework/plugins/windows/svcscan.py | 18 +++++----- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py index 03f0afcd3..71aa03636 100644 --- a/volatility3/framework/plugins/windows/svcdiff.py +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -48,27 +48,36 @@ class SvcDiff(svclist.SvcList, svcscan.SvcScan): """ kernel = self.context.modules[self.config["kernel"]] - if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) or \ - not versions.is_win10_15063_or_later(context=self.context, symbol_table=kernel.symbol_table_name): - vollog.info("This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples") + if not symbols.symbol_table_is_64bit( + self.context, kernel.symbol_table_name + ) or not versions.is_win10_15063_or_later( + context=self.context, symbol_table=kernel.symbol_table_name + ): + vollog.info( + "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" + ) return from_scan = set() from_list = set() records = {} - + service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() # collect unique service names from scanning - for service in self.service_scan(service_table_name, service_binary_dll_map, filter_func): + for service in self.service_scan( + service_table_name, service_binary_dll_map, filter_func + ): from_scan.add(service[6]) records[service[6]] = service # collect services from listing walking - for service in self.service_list(service_table_name, service_binary_dll_map, filter_func): + for service in self.service_list( + service_table_name, service_binary_dll_map, filter_func + ): from_list.add(service[6]) # report services found from scanning but not list walking - for hidden_service in from_scan-from_list: + for hidden_service in from_scan - from_list: yield (0, records[hidden_service]) diff --git a/volatility3/framework/plugins/windows/svclist.py b/volatility3/framework/plugins/windows/svclist.py index b4541981d..53ca68da7 100644 --- a/volatility3/framework/plugins/windows/svclist.py +++ b/volatility3/framework/plugins/windows/svclist.py @@ -6,7 +6,7 @@ import logging from typing import List -from volatility3.framework import interfaces, exceptions, symbols +from volatility3.framework import interfaces, exceptions, symbols from volatility3.framework.configuration import requirements from volatility3.framework.symbols.windows import versions from volatility3.plugins.windows import svcscan, pslist @@ -38,7 +38,9 @@ class SvcList(svcscan.SvcScan): vad_root = proc.get_vad_root() for vad in vad_root.traverse(): filename = vad.get_file_name() - if isinstance(filename, str) and filename.lower().endswith("\\services.exe"): + if isinstance(filename, str) and filename.lower().endswith( + "\\services.exe" + ): return [(vad.get_start(), vad.get_size())] return None @@ -46,9 +48,14 @@ class SvcList(svcscan.SvcScan): def service_list(self, service_table_name, service_binary_dll_map, filter_func): kernel = self.context.modules[self.config["kernel"]] - if not symbols.symbol_table_is_64bit(self.context, kernel.symbol_table_name) or \ - not versions.is_win10_15063_or_later(context=self.context, symbol_table=kernel.symbol_table_name): - vollog.info("This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples") + if not symbols.symbol_table_is_64bit( + self.context, kernel.symbol_table_name + ) or not versions.is_win10_15063_or_later( + context=self.context, symbol_table=kernel.symbol_table_name + ): + vollog.info( + "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" + ) return for proc in pslist.PsList.list_processes( @@ -60,27 +67,34 @@ class SvcList(svcscan.SvcScan): try: layer_name = proc.add_process_layer() except exceptions.InvalidAddressException: - vollog.warning("Unable to access memory of services.exe running with PID: {}".format(proc.UniqueProcessId)) + vollog.warning( + "Unable to access memory of services.exe running with PID: {}".format( + proc.UniqueProcessId + ) + ) continue layer = self.context.layers[layer_name] exe_range = self._get_exe_range(proc) if not exe_range: - vollog.warning("Could not find the application executable VAD for services.exe. Unable to proceed.") + vollog.warning( + "Could not find the application executable VAD for services.exe. Unable to proceed." + ) continue for offset in layer.scan( context=self.context, - scanner=scanners.BytesScanner(needle = b"Sc27"), + scanner=scanners.BytesScanner(needle=b"Sc27"), sections=exe_range, + ): + for record in self.enumerate_vista_or_later_header( + service_table_name, service_binary_dll_map, layer_name, offset ): - for record in self.enumerate_vista_or_later_header(service_table_name, service_binary_dll_map, layer_name, offset): yield record def _generator(self): service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - + for record in self.service_list(service_table_name, service_binary_dll_map, filter_func): yield (0, record) - diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 08d11a946..c991ec943 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -233,11 +233,7 @@ class SvcScan(interfaces.plugins.PluginInterface): } def enumerate_vista_or_later_header( - self, - service_table_name, - service_binary_dll_map, - proc_layer_name, - offset + self, service_table_name, service_binary_dll_map, proc_layer_name, offset ): if offset % 8: return @@ -324,13 +320,17 @@ class SvcScan(interfaces.plugins.PluginInterface): ) yield self.get_record_tuple(service_record, service_info) else: - for service_record in self.enumerate_vista_or_later_header(service_table_name, service_binary_dll_map, proc_layer_name, offset): + for service_record in self.enumerate_vista_or_later_header( + service_table_name, + service_binary_dll_map, + proc_layer_name, + offset + ): if service_record in seen: break seen.append(service_record) yield service_record - def get_prereq_info(self): """ Data structures and information needed to analyze service information @@ -356,7 +356,9 @@ class SvcScan(interfaces.plugins.PluginInterface): def _generator(self): service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - for record in self.service_scan(service_table_name, service_binary_dll_map, filter_func): + for record in self.service_scan( + service_table_name, service_binary_dll_map, filter_func + ): yield (0, record) def run(self): From d42fb0a1451ca0a0cc00ee4a2a431be092c193b9 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 14:56:57 -0500 Subject: [PATCH 06/32] Format fixes --- volatility3/framework/plugins/windows/svclist.py | 4 +++- volatility3/framework/plugins/windows/svcscan.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/svclist.py b/volatility3/framework/plugins/windows/svclist.py index 53ca68da7..1938dd182 100644 --- a/volatility3/framework/plugins/windows/svclist.py +++ b/volatility3/framework/plugins/windows/svclist.py @@ -96,5 +96,7 @@ class SvcList(svcscan.SvcScan): def _generator(self): service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - for record in self.service_list(service_table_name, service_binary_dll_map, filter_func): + for record in self.service_list( + service_table_name, service_binary_dll_map, filter_func + ): yield (0, record) diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index c991ec943..07274f03d 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -324,7 +324,7 @@ class SvcScan(interfaces.plugins.PluginInterface): service_table_name, service_binary_dll_map, proc_layer_name, - offset + offset, ): if service_record in seen: break From f6a053d7b3db2421c9d4d8501eff1fcb78001f1f Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 19 Jun 2024 14:58:05 -0500 Subject: [PATCH 07/32] Format fixes --- volatility3/framework/plugins/windows/svcdiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py index 71aa03636..809064946 100644 --- a/volatility3/framework/plugins/windows/svcdiff.py +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -20,6 +20,7 @@ from volatility3.framework.symbols.windows import versions vollog = logging.getLogger(__name__) + class SvcDiff(svclist.SvcList, svcscan.SvcScan): """Compares services found through list walking versus scanning to find rootkits""" @@ -80,4 +81,3 @@ class SvcDiff(svclist.SvcList, svcscan.SvcScan): # report services found from scanning but not list walking for hidden_service in from_scan - from_list: yield (0, records[hidden_service]) - From e172676d3caaecd3592211cd561aa612d934bcbc Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Fri, 21 Jun 2024 15:07:18 -0500 Subject: [PATCH 08/32] #118 - initial timers plugin --- volatility3/framework/objects/utility.py | 21 ++ .../framework/plugins/windows/timers.py | 264 ++++++++++++++++++ .../framework/symbols/windows/__init__.py | 1 + .../symbols/windows/extensions/__init__.py | 80 ++++++ 4 files changed, 366 insertions(+) create mode 100644 volatility3/framework/plugins/windows/timers.py diff --git a/volatility3/framework/objects/utility.py b/volatility3/framework/objects/utility.py index 0292608c1..8aa527cdb 100644 --- a/volatility3/framework/objects/utility.py +++ b/volatility3/framework/objects/utility.py @@ -7,6 +7,27 @@ from typing import Optional, Union from volatility3.framework import interfaces, objects, constants +def rol(value: int, count: int, max_bits: int = 64) -> int: + """A rotate-left instruction in Python""" + max_bits_mask = (1 << max_bits) - 1 + return (value << count % max_bits) & max_bits_mask | ( + (value & max_bits_mask) >> (max_bits - (count % max_bits)) + ) + + +def bswap_32(value: int) -> int: + value = ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0x00FF00FF) + + return ((value << 16) | (value >> 16)) & 0xFFFFFFFF + + +def bswap_64(value: int) -> int: + low = bswap_32((value >> 32)) + high = bswap_32((value & 0xFFFFFFFF)) + + return ((high << 32) | low) & 0xFFFFFFFFFFFFFFFF + + def array_to_string( array: "objects.Array", count: Optional[int] = None, errors: str = "replace" ) -> interfaces.objects.ObjectInterface: diff --git a/volatility3/framework/plugins/windows/timers.py b/volatility3/framework/plugins/windows/timers.py new file mode 100644 index 000000000..19d52d9bf --- /dev/null +++ b/volatility3/framework/plugins/windows/timers.py @@ -0,0 +1,264 @@ +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import logging + +from typing import Iterator, List, Tuple, Iterable + +from volatility3.framework import exceptions, layers, renderers, interfaces, constants, symbols +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints +from volatility3.framework.symbols.windows import versions +from volatility3.plugins.windows import ssdt + +vollog = logging.getLogger(__name__) + + +class Timers(interfaces.plugins.PluginInterface): + """Print kernel timers and associated module DPCs""" + + _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.PluginRequirement( + name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0) + ), + ] + + @classmethod + def get_kernel_module( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + ): + """Returns the kernel module based on the layer and symbol_table""" + virtual_layer = context.layers[layer_name] + if not isinstance(virtual_layer, layers.intel.Intel): + raise TypeError("Virtual Layer is not an intel layer") + + kvo = virtual_layer.config["kernel_virtual_offset"] + + ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo) + return ntkrnlmp + + @classmethod + def get_kpcrs( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + ) -> interfaces.objects.ObjectInterface: + """Returns the KPCR structure for each processor + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + symbol_table: The name of an existing symbol table containing the kernel symbols + config_path: The configuration path within the context of the symbol table to create + + Returns: + The _KPCR structure for each processor + """ + + ntkrnlmp = cls.get_kernel_module(context, layer_name, symbol_table) + cpu_count_offset = ntkrnlmp.get_symbol("KeNumberProcessors").address + cpu_count = ntkrnlmp.object( + object_type="unsigned int", layer_name=layer_name, offset=cpu_count_offset + ) + processor_block = ntkrnlmp.object( + object_type="pointer", + layer_name=layer_name, + offset=ntkrnlmp.get_symbol("KiProcessorBlock").address, + ) + processor_pointers = utility.array_of_pointers( + context=context, + array=processor_block, + count=cpu_count, + subtype=symbol_table + constants.BANG + "_KPRCB", + ) + for pointer in processor_pointers: + kprcb = pointer.dereference() + reloff = ntkrnlmp.get_type("_KPCR").relative_child_offset("Prcb") + kpcr = context.object( + symbol_table + constants.BANG + "_KPCR", + offset=kprcb.vol.offset - reloff, + layer_name=layer_name, + ) + yield kpcr + + @classmethod + def list_timers( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + ) -> Iterable[Tuple[str, int, str]]: + """Lists all kernel timers. + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + layer_name: The name of the layer on which to operate + symbol_table: The name of the table containing the kernel symbols + + Yields: + A _KTIMER entry + """ + ntkrnlmp = cls.get_kernel_module(context, layer_name, symbol_table) + + if versions.is_windows_7( + context=context, symbol_table=symbol_table + ) or versions.is_windows_8_or_later( + context=context, symbol_table=symbol_table + ): + # Starting with Windows 7, there is no more KiTimerTableListHead. The list is + # at _KPCR.PrcbData.TimerTable.TimerEntries + # See http://pastebin.com/FiRsGW3f + for kpcr in cls.get_kpcrs(context, layer_name, symbol_table): + if hasattr(kpcr.Prcb.TimerTable, "TableState"): + for timer_entries in kpcr.Prcb.TimerTable.TimerEntries: + for timer_entry in timer_entries: + for timer in timer_entry.Entry.to_list( + symbol_table + constants.BANG + "_KTIMER", + "TimerListEntry", + ): + yield timer + + else: + for timer_entries in kpcr.Prcb.TimerTable.TimerEntries: + for timer in timer_entries.Entry.to_list( + symbol_table + constants.BANG + "_KTIMER", + "TimerListEntry", + ): + yield timer + + elif versions.is_xp_or_2003( + context=context, symbol_table=symbol_table + ) or versions.is_vista_or_later( + context=context, symbol_table=symbol_table + ): + is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) + if is_64bit or versions.is_vista_or_later(context=context, symbol_table=symbol_table): + # On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead + # is an array of 512 _KTIMER_TABLE_ENTRY structs. + array_size = 512 + else: + # On XP SP0-SP3 x86 and Windows 2003 SP0, KiTimerTableListHead + # is an array of 256 _LIST_ENTRY for _KTIMERs. + array_size = 256 + + timer_table_list_head = ntkrnlmp.object( + object_type="array", + offset=ntkrnlmp.get_symbol("KiTimerTableListHead").address, + subtype=ntkrnlmp.get_type("_LIST_ENTRY"), + count=array_size, + ) + for table in timer_table_list_head: + for timer in table.to_list( + symbol_table + constants.BANG + "_KTIMER", + "TimerListEntry", + ): + yield timer + + else: + raise NotImplementedError("This version of Windows is not supported!") + + + def _generator(self) -> Iterator[Tuple]: + kernel = self.context.modules[self.config["kernel"]] + layer_name = kernel.layer_name + symbol_table = kernel.symbol_table_name + + collection = ssdt.SSDT.build_module_collection( + self.context, kernel.layer_name, kernel.symbol_table_name + ) + + for timer in self.list_timers(self.context, layer_name, symbol_table): + if not timer.valid_type(): + continue + try: + dpc = timer.get_dpc() + if dpc == 0: + continue + if dpc.DeferredRoutine == 0: + continue + deferred_routine = dpc.DeferredRoutine + except Exception as e: + continue + + module_symbols = list( + collection.get_module_symbols_by_absolute_location(deferred_routine) + ) + + if module_symbols: + for module_name, symbol_generator in module_symbols: + symbols_found = False + + # we might have multiple symbols pointing to the same location + for symbol in symbol_generator: + symbols_found = True + yield ( + 0, + ( + format_hints.Hex(timer.vol.offset), + timer.get_due_time(), + timer.Period, + timer.get_signaled(), + format_hints.Hex(deferred_routine), + module_name, + symbol.split(constants.BANG)[1], + ), + ) + + # no symbols, but we at least can report the module name + if not symbols_found: + yield ( + 0, + ( + format_hints.Hex(timer.vol.offset), + timer.get_due_time(), + timer.Period, + timer.get_signaled(), + format_hints.Hex(deferred_routine), + module_name, + renderers.NotAvailableValue(), + ), + ) + else: + # no module was found at the absolute location + yield ( + 0, + ( + format_hints.Hex(timer.vol.offset), + timer.get_due_time(), + timer.Period, + timer.get_signaled(), + format_hints.Hex(deferred_routine), + renderers.NotAvailableValue(), + renderers.NotAvailableValue(), + ), + ) + + + def run(self): + return renderers.TreeGrid( + [ + ("Offset", format_hints.Hex), + ("DueTime", str), + ("Period(ms)", int), + ("Signaled", str), + ("Routine", format_hints.Hex), + ("Module", str), + ("Symbol", str), + ], + self._generator(), + ) diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index abf9f6da3..4aeb22dcf 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -39,6 +39,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class("_VACB", extensions.VACB) self.set_type_class("_POOL_TRACKER_BIG_PAGES", pool.POOL_TRACKER_BIG_PAGES) self.set_type_class("_IMAGE_DOS_HEADER", pe.IMAGE_DOS_HEADER) + self.set_type_class("_KTIMER", extensions.KTIMER) # Might not necessarily defined in every version of windows self.optional_set_type_class("_IMAGE_NT_HEADERS", pe.IMAGE_NT_HEADERS) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index a8fa7b2ff..040c57bae 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -20,6 +20,7 @@ from volatility3.framework import ( ) from volatility3.framework.interfaces.objects import ObjectInterface from volatility3.framework.layers import intel +from volatility3.framework.objects import utility from volatility3.framework.renderers import conversion from volatility3.framework.symbols import generic from volatility3.framework.symbols.windows.extensions import kdbg, pe, pool @@ -994,6 +995,85 @@ class TOKEN(objects.StructType): vollog.log(constants.LOGLEVEL_VVVV, "Broken Token Privileges.") +class KTIMER(objects.StructType): + """A class for Kernel Timers""" + + VALID_TYPES = { + 8: "TimerNotificationObject", + 9: "TimerSynchronizationObject", + } + + def get_signaled(self): + if self.Header.SignalState: + return "Yes" + return "-" + + def get_raw_dpc(self): + """Returns the encoded DPC since it may not look like a pointer after encoding""" + symbol_table_name = self.get_symbol_table_name() + ulonglong_type = self._context.symbol_space.get_type( + symbol_table_name + constants.BANG + "unsigned long long" + ) + + return self._context.object( + object_type=ulonglong_type, + layer_name=self.vol.layer_name, + offset=self.Dpc.vol.offset, + ) + def valid_type(self): + return self.Header.Type in self.VALID_TYPES + + def get_due_time(self): + return "{0:#010x}:{1:#010x}".format(self.DueTime.HighPart, self.DueTime.LowPart) + + def get_dpc(self): + """Return Dpc, and if Windows 7 or later, decode it""" + symbol_table_name = self.get_symbol_table_name() + kvo = self._context.layers[self.vol.native_layer_name].config[ + "kernel_virtual_offset" + ] + ntkrnlmp = self._context.module( + symbol_table_name, + layer_name=self.vol.native_layer_name, + offset=kvo, + native_layer_name=self.vol.native_layer_name, + ) + + try: + wait_never = ntkrnlmp.object( + object_type="unsigned long long", + offset=ntkrnlmp.get_symbol("KiWaitNever").address, + ) + + wait_always = ntkrnlmp.object( + object_type="unsigned long long", + offset=ntkrnlmp.get_symbol("KiWaitAlways").address, + ) + except exceptions.SymbolError: + wait_never = None + wait_always = None + + if wait_never is None or wait_always is None: + return self.Dpc + else: + low_byte = (wait_never) & 0xFF + entry = utility.rol(self.get_raw_dpc() ^ wait_never, low_byte) + swap_xor = self.vol.offset | 0xFFFF000000000000 + entry = utility.bswap_64(entry ^ swap_xor) + dpc = entry ^ wait_always + + symbol_table_name = self.get_symbol_table_name() + kdpc_type = self._context.symbol_space.get_type( + symbol_table_name + constants.BANG + "_KDPC" + ) + + return self._context.object( + object_type=kdpc_type, + layer_name=self.vol.layer_name, + offset=dpc, + ) + + class KTHREAD(objects.StructType): """A class for thread control block objects.""" From c79dc52a854d2a5be41283a720081f63471c0df8 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Fri, 21 Jun 2024 15:09:21 -0500 Subject: [PATCH 09/32] #118 - black formatted --- .../framework/plugins/windows/timers.py | 23 +++++++++++-------- .../symbols/windows/extensions/__init__.py | 7 +++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/plugins/windows/timers.py b/volatility3/framework/plugins/windows/timers.py index 19d52d9bf..e54b2a13f 100644 --- a/volatility3/framework/plugins/windows/timers.py +++ b/volatility3/framework/plugins/windows/timers.py @@ -6,7 +6,14 @@ import logging from typing import Iterator, List, Tuple, Iterable -from volatility3.framework import exceptions, layers, renderers, interfaces, constants, symbols +from volatility3.framework import ( + exceptions, + layers, + renderers, + interfaces, + constants, + symbols, +) from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility from volatility3.framework.renderers import format_hints @@ -117,9 +124,7 @@ class Timers(interfaces.plugins.PluginInterface): if versions.is_windows_7( context=context, symbol_table=symbol_table - ) or versions.is_windows_8_or_later( - context=context, symbol_table=symbol_table - ): + ) or versions.is_windows_8_or_later(context=context, symbol_table=symbol_table): # Starting with Windows 7, there is no more KiTimerTableListHead. The list is # at _KPCR.PrcbData.TimerTable.TimerEntries # See http://pastebin.com/FiRsGW3f @@ -143,11 +148,11 @@ class Timers(interfaces.plugins.PluginInterface): elif versions.is_xp_or_2003( context=context, symbol_table=symbol_table - ) or versions.is_vista_or_later( - context=context, symbol_table=symbol_table - ): + ) or versions.is_vista_or_later(context=context, symbol_table=symbol_table): is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) - if is_64bit or versions.is_vista_or_later(context=context, symbol_table=symbol_table): + if is_64bit or versions.is_vista_or_later( + context=context, symbol_table=symbol_table + ): # On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead # is an array of 512 _KTIMER_TABLE_ENTRY structs. array_size = 512 @@ -172,7 +177,6 @@ class Timers(interfaces.plugins.PluginInterface): else: raise NotImplementedError("This version of Windows is not supported!") - def _generator(self) -> Iterator[Tuple]: kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -248,7 +252,6 @@ class Timers(interfaces.plugins.PluginInterface): ), ) - def run(self): return renderers.TreeGrid( [ diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 040c57bae..b830a9d9f 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -453,9 +453,9 @@ class FILE_OBJECT(objects.StructType, pool.ExecutiveObject): ].is_valid(self.FileName.Buffer) def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: - name: Union[str, interfaces.renderers.BaseAbsentValue] = ( - renderers.UnreadableValue() - ) + name: Union[ + str, interfaces.renderers.BaseAbsentValue + ] = renderers.UnreadableValue() # this pointer needs to be checked against native_layer_name because the object may # be instantiated from a primary (virtual) layer or a memory (physical) layer. @@ -1020,6 +1020,7 @@ class KTIMER(objects.StructType): layer_name=self.vol.layer_name, offset=self.Dpc.vol.offset, ) + def valid_type(self): return self.Header.Type in self.VALID_TYPES From 7065446e8ab9efb02254e4415235072d30422596 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Fri, 21 Jun 2024 15:17:40 -0500 Subject: [PATCH 10/32] #118 - fix black issues --- volatility3/framework/plugins/windows/timers.py | 1 - volatility3/framework/symbols/windows/extensions/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/timers.py b/volatility3/framework/plugins/windows/timers.py index e54b2a13f..ec8bc17e9 100644 --- a/volatility3/framework/plugins/windows/timers.py +++ b/volatility3/framework/plugins/windows/timers.py @@ -7,7 +7,6 @@ import logging from typing import Iterator, List, Tuple, Iterable from volatility3.framework import ( - exceptions, layers, renderers, interfaces, diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index b830a9d9f..895a8c094 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -1045,7 +1045,6 @@ class KTIMER(objects.StructType): object_type="unsigned long long", offset=ntkrnlmp.get_symbol("KiWaitNever").address, ) - wait_always = ntkrnlmp.object( object_type="unsigned long long", offset=ntkrnlmp.get_symbol("KiWaitAlways").address, From 2d7789f8509664f3930705bf83524d6b5ec8de33 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Fri, 21 Jun 2024 15:49:03 -0500 Subject: [PATCH 11/32] #118 - fix black issues --- .../framework/symbols/windows/extensions/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 895a8c094..4d4ffc055 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -453,9 +453,9 @@ class FILE_OBJECT(objects.StructType, pool.ExecutiveObject): ].is_valid(self.FileName.Buffer) def file_name_with_device(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: - name: Union[ - str, interfaces.renderers.BaseAbsentValue - ] = renderers.UnreadableValue() + name: Union[str, interfaces.renderers.BaseAbsentValue] = ( + renderers.UnreadableValue() + ) # this pointer needs to be checked against native_layer_name because the object may # be instantiated from a primary (virtual) layer or a memory (physical) layer. From 146baab14c4a17bdcbc35715b30a4d190ed82214 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Fri, 21 Jun 2024 15:54:16 -0500 Subject: [PATCH 12/32] #118 - refactor get_dpc --- .../framework/symbols/windows/extensions/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 4d4ffc055..b9eabcda6 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -1040,7 +1040,7 @@ class KTIMER(objects.StructType): native_layer_name=self.vol.native_layer_name, ) - try: + if ntkrnlmp.has_symbol("KiWaitNever") and ntkrnlmp.has_symbol("KiWaitAlways"): wait_never = ntkrnlmp.object( object_type="unsigned long long", offset=ntkrnlmp.get_symbol("KiWaitNever").address, @@ -1049,13 +1049,7 @@ class KTIMER(objects.StructType): object_type="unsigned long long", offset=ntkrnlmp.get_symbol("KiWaitAlways").address, ) - except exceptions.SymbolError: - wait_never = None - wait_always = None - if wait_never is None or wait_always is None: - return self.Dpc - else: low_byte = (wait_never) & 0xFF entry = utility.rol(self.get_raw_dpc() ^ wait_never, low_byte) swap_xor = self.vol.offset | 0xFFFF000000000000 @@ -1072,6 +1066,8 @@ class KTIMER(objects.StructType): layer_name=self.vol.layer_name, offset=dpc, ) + else: + return self.Dpc class KTHREAD(objects.StructType): From 8c1a5c46e32c8ffaacd6507ec036cbd07c9072b7 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 27 Jun 2024 17:13:37 -0500 Subject: [PATCH 13/32] Add timeliner support to userassist --- .../plugins/windows/registry/userassist.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index 70c75b50b..54ec2fc74 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -17,11 +17,12 @@ from volatility3.framework.layers.registry import RegistryHive from volatility3.framework.renderers import conversion, format_hints from volatility3.framework.symbols import intermed from volatility3.plugins.windows.registry import hivelist +from volatility3.plugins import timeliner vollog = logging.getLogger(__name__) -class UserAssist(interfaces.plugins.PluginInterface): +class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): """Print userassist registry keys and information.""" _required_framework_version = (2, 0, 0) @@ -335,6 +336,19 @@ class UserAssist(interfaces.plugins.PluginInterface): ) yield result + + def generate_timeline(self): + self._reg_table_name = intermed.IntermediateSymbolTable.create( + self.context, self._config_path, "windows", "registry" + ) + + for row in self._generator(): + _depth, row_data = row + # check the name and the timestamp to not be empty + if isinstance(row_data[5], str) and not isinstance(row_data[10], renderers.NotApplicableValue): + description = f"UserAssist: {row_data[5]} {row_data[2]} ({row_data[7]})" + yield (description, timeliner.TimeLinerType.MODIFIED, row_data[10]) + def run(self): self._reg_table_name = intermed.IntermediateSymbolTable.create( self.context, self._config_path, "windows", "registry" From 8525edd3331c6ccd967c8048d61adf5d2c3e1015 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 27 Jun 2024 17:15:49 -0500 Subject: [PATCH 14/32] Formatting fixes --- volatility3/framework/plugins/windows/registry/userassist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index 54ec2fc74..cf345c901 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -336,7 +336,6 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac ) yield result - def generate_timeline(self): self._reg_table_name = intermed.IntermediateSymbolTable.create( self.context, self._config_path, "windows", "registry" @@ -345,7 +344,9 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac for row in self._generator(): _depth, row_data = row # check the name and the timestamp to not be empty - if isinstance(row_data[5], str) and not isinstance(row_data[10], renderers.NotApplicableValue): + if isinstance(row_data[5], str) and not isinstance( + row_data[10], renderers.NotApplicableValue + ): description = f"UserAssist: {row_data[5]} {row_data[2]} ({row_data[7]})" yield (description, timeliner.TimeLinerType.MODIFIED, row_data[10]) From cb6929163b31726d811f7e825bcfad8690170b9c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 15 Jul 2024 18:14:50 +0100 Subject: [PATCH 15/32] Windows: Fix vadyarascan sanity check and bad documentation --- volatility3/framework/plugins/windows/vadyarascan.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/plugins/windows/vadyarascan.py b/volatility3/framework/plugins/windows/vadyarascan.py index 4b00fee57..219faf028 100644 --- a/volatility3/framework/plugins/windows/vadyarascan.py +++ b/volatility3/framework/plugins/windows/vadyarascan.py @@ -56,7 +56,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface): filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - sanity_check = 0x1000 * 0x1000 * 0x1000 + sanity_check = 0x2000 * 0x1000 * 0x1000 for task in pslist.PsList.list_processes( context=self.context, @@ -66,15 +66,14 @@ class VadYaraScan(interfaces.plugins.PluginInterface): ): layer_name = task.add_process_layer() layer = self.context.layers[layer_name] - for start, end in self.get_vad_maps(task): - size = end - start + for start, size in self.get_vad_maps(task): if size > sanity_check: vollog.warn( f"VAD at 0x{start:x} over sanity-check size, not scanning" ) continue - for match in rules.match(data=layer.read(start, end - start, True)): + for match in rules.match(data=layer.read(start, size, True)): if yarascan.YaraScan.yara_returns_instances(): for match_string in match.strings: for instance in match_string.instances: @@ -106,7 +105,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface): task: The EPROCESS object of which to traverse the vad tree Returns: - An iterable of tuples containing start and end addresses for each descriptor + An iterable of tuples containing start and size for each descriptor """ vad_root = task.get_vad_root() for vad in vad_root.traverse(): From 55fe4ba47aece0882a0b5c690710cba1fa438989 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 15 Jul 2024 15:20:10 -0500 Subject: [PATCH 16/32] #118 - MR feedback --- .../framework/plugins/windows/kpcrs.py | 106 ++++++++++++++++++ .../framework/plugins/windows/timers.py | 86 +++----------- .../symbols/windows/extensions/__init__.py | 6 +- 3 files changed, 125 insertions(+), 73 deletions(-) create mode 100644 volatility3/framework/plugins/windows/kpcrs.py diff --git a/volatility3/framework/plugins/windows/kpcrs.py b/volatility3/framework/plugins/windows/kpcrs.py new file mode 100644 index 000000000..558ea844c --- /dev/null +++ b/volatility3/framework/plugins/windows/kpcrs.py @@ -0,0 +1,106 @@ +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +import logging + +from typing import Iterator, List, Tuple + +from volatility3.framework import ( + renderers, + interfaces, + constants, +) +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints + +vollog = logging.getLogger(__name__) + + +class KPCRs(interfaces.plugins.PluginInterface): + """Print KPCR structure for each processor""" + + _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + ] + + @classmethod + def list_kpcrs( + cls, + context: interfaces.context.ContextInterface, + kernel_module_name: str, + layer_name: str, + symbol_table: str, + ) -> interfaces.objects.ObjectInterface: + """Returns the KPCR structure for each processor + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + kernel_module_name: The name of the kernel module on which to operate + layer_name: The name of the layer on which to operate + symbol_table: The name of the table containing the kernel symbols + + Returns: + The _KPCR structure for each processor + """ + + kernel = context.modules[kernel_module_name] + cpu_count_offset = kernel.get_symbol("KeNumberProcessors").address + cpu_count = kernel.object( + object_type="unsigned int", layer_name=layer_name, offset=cpu_count_offset + ) + processor_block = kernel.object( + object_type="pointer", + layer_name=layer_name, + offset=kernel.get_symbol("KiProcessorBlock").address, + ) + processor_pointers = utility.array_of_pointers( + context=context, + array=processor_block, + count=cpu_count, + subtype=symbol_table + constants.BANG + "_KPRCB", + ) + for pointer in processor_pointers: + kprcb = pointer.dereference() + reloff = kernel.get_type("_KPCR").relative_child_offset("Prcb") + kpcr = context.object( + symbol_table + constants.BANG + "_KPCR", + offset=kprcb.vol.offset - reloff, + layer_name=layer_name, + ) + yield kpcr + + def _generator(self) -> Iterator[Tuple]: + kernel = self.context.modules[self.config["kernel"]] + layer_name = kernel.layer_name + symbol_table = kernel.symbol_table_name + + for kpcr in self.list_kpcrs( + self.context, self.config["kernel"], layer_name, symbol_table + ): + yield ( + 0, + ( + format_hints.Hex(kpcr.vol.offset), + format_hints.Hex(kpcr.CurrentPrcb), + ), + ) + + def run(self): + return renderers.TreeGrid( + [ + ("Offset", format_hints.Hex), + ("PRCB Offset", format_hints.Hex), + ], + self._generator(), + ) diff --git a/volatility3/framework/plugins/windows/timers.py b/volatility3/framework/plugins/windows/timers.py index ec8bc17e9..d49c28784 100644 --- a/volatility3/framework/plugins/windows/timers.py +++ b/volatility3/framework/plugins/windows/timers.py @@ -7,17 +7,15 @@ import logging from typing import Iterator, List, Tuple, Iterable from volatility3.framework import ( - layers, renderers, interfaces, constants, symbols, ) from volatility3.framework.configuration import requirements -from volatility3.framework.objects import utility from volatility3.framework.renderers import format_hints from volatility3.framework.symbols.windows import versions -from volatility3.plugins.windows import ssdt +from volatility3.plugins.windows import ssdt, kpcrs vollog = logging.getLogger(__name__) @@ -39,73 +37,16 @@ class Timers(interfaces.plugins.PluginInterface): requirements.PluginRequirement( name="ssdt", plugin=ssdt.SSDT, version=(1, 0, 0) ), + requirements.PluginRequirement( + name="kpcrs", plugin=kpcrs.KPCRs, version=(1, 0, 0) + ), ] - @classmethod - def get_kernel_module( - cls, - context: interfaces.context.ContextInterface, - layer_name: str, - symbol_table: str, - ): - """Returns the kernel module based on the layer and symbol_table""" - virtual_layer = context.layers[layer_name] - if not isinstance(virtual_layer, layers.intel.Intel): - raise TypeError("Virtual Layer is not an intel layer") - - kvo = virtual_layer.config["kernel_virtual_offset"] - - ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo) - return ntkrnlmp - - @classmethod - def get_kpcrs( - cls, - context: interfaces.context.ContextInterface, - layer_name: str, - symbol_table: str, - ) -> interfaces.objects.ObjectInterface: - """Returns the KPCR structure for each processor - - Args: - context: The context to retrieve required elements (layers, symbol tables) from - symbol_table: The name of an existing symbol table containing the kernel symbols - config_path: The configuration path within the context of the symbol table to create - - Returns: - The _KPCR structure for each processor - """ - - ntkrnlmp = cls.get_kernel_module(context, layer_name, symbol_table) - cpu_count_offset = ntkrnlmp.get_symbol("KeNumberProcessors").address - cpu_count = ntkrnlmp.object( - object_type="unsigned int", layer_name=layer_name, offset=cpu_count_offset - ) - processor_block = ntkrnlmp.object( - object_type="pointer", - layer_name=layer_name, - offset=ntkrnlmp.get_symbol("KiProcessorBlock").address, - ) - processor_pointers = utility.array_of_pointers( - context=context, - array=processor_block, - count=cpu_count, - subtype=symbol_table + constants.BANG + "_KPRCB", - ) - for pointer in processor_pointers: - kprcb = pointer.dereference() - reloff = ntkrnlmp.get_type("_KPCR").relative_child_offset("Prcb") - kpcr = context.object( - symbol_table + constants.BANG + "_KPCR", - offset=kprcb.vol.offset - reloff, - layer_name=layer_name, - ) - yield kpcr - @classmethod def list_timers( cls, context: interfaces.context.ContextInterface, + kernel_module_name: str, layer_name: str, symbol_table: str, ) -> Iterable[Tuple[str, int, str]]: @@ -113,21 +54,24 @@ class Timers(interfaces.plugins.PluginInterface): Args: context: The context to retrieve required elements (layers, symbol tables) from + kernel_module_name: The name of the kernel module on which to operate layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols Yields: A _KTIMER entry """ - ntkrnlmp = cls.get_kernel_module(context, layer_name, symbol_table) + kernel = context.modules[kernel_module_name] if versions.is_windows_7( context=context, symbol_table=symbol_table ) or versions.is_windows_8_or_later(context=context, symbol_table=symbol_table): # Starting with Windows 7, there is no more KiTimerTableListHead. The list is # at _KPCR.PrcbData.TimerTable.TimerEntries # See http://pastebin.com/FiRsGW3f - for kpcr in cls.get_kpcrs(context, layer_name, symbol_table): + for kpcr in kpcrs.KPCRs.list_kpcrs( + context, kernel_module_name, layer_name, symbol_table + ): if hasattr(kpcr.Prcb.TimerTable, "TableState"): for timer_entries in kpcr.Prcb.TimerTable.TimerEntries: for timer_entry in timer_entries: @@ -160,10 +104,10 @@ class Timers(interfaces.plugins.PluginInterface): # is an array of 256 _LIST_ENTRY for _KTIMERs. array_size = 256 - timer_table_list_head = ntkrnlmp.object( + timer_table_list_head = kernel.object( object_type="array", - offset=ntkrnlmp.get_symbol("KiTimerTableListHead").address, - subtype=ntkrnlmp.get_type("_LIST_ENTRY"), + offset=kernel.get_symbol("KiTimerTableListHead").address, + subtype=kernel.get_type("_LIST_ENTRY"), count=array_size, ) for table in timer_table_list_head: @@ -185,7 +129,9 @@ class Timers(interfaces.plugins.PluginInterface): self.context, kernel.layer_name, kernel.symbol_table_name ) - for timer in self.list_timers(self.context, layer_name, symbol_table): + for timer in self.list_timers( + self.context, self.config["kernel"], layer_name, symbol_table + ): if not timer.valid_type(): continue try: diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index b9eabcda6..86ea2febb 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -1011,12 +1011,12 @@ class KTIMER(objects.StructType): def get_raw_dpc(self): """Returns the encoded DPC since it may not look like a pointer after encoding""" symbol_table_name = self.get_symbol_table_name() - ulonglong_type = self._context.symbol_space.get_type( - symbol_table_name + constants.BANG + "unsigned long long" + pointer_type = self._context.symbol_space.get_type( + symbol_table_name + constants.BANG + "pointer" ) return self._context.object( - object_type=ulonglong_type, + object_type=pointer_type, layer_name=self.vol.layer_name, offset=self.Dpc.vol.offset, ) From 99cf48597abbc1126ef06731ed7836c04614514d Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 15 Jul 2024 15:46:27 -0500 Subject: [PATCH 17/32] #118 - use canonicalize for offset --- volatility3/framework/symbols/windows/extensions/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 86ea2febb..d5c3d3f96 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -1052,7 +1052,7 @@ class KTIMER(objects.StructType): low_byte = (wait_never) & 0xFF entry = utility.rol(self.get_raw_dpc() ^ wait_never, low_byte) - swap_xor = self.vol.offset | 0xFFFF000000000000 + swap_xor = self._context.layers[self.vol.native_layer_name].canonicalize(self.vol.offset) entry = utility.bswap_64(entry ^ swap_xor) dpc = entry ^ wait_always From 43e22d72bdaa2cbb6366b1911a6ff38ced83394f Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Tue, 16 Jul 2024 09:23:06 -0500 Subject: [PATCH 18/32] #118 - black formatting --- volatility3/framework/symbols/windows/extensions/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index d5c3d3f96..b333755f7 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -1052,7 +1052,9 @@ class KTIMER(objects.StructType): low_byte = (wait_never) & 0xFF entry = utility.rol(self.get_raw_dpc() ^ wait_never, low_byte) - swap_xor = self._context.layers[self.vol.native_layer_name].canonicalize(self.vol.offset) + swap_xor = self._context.layers[self.vol.native_layer_name].canonicalize( + self.vol.offset + ) entry = utility.bswap_64(entry ^ swap_xor) dpc = entry ^ wait_always From a5cf635fd8b81c56f14ee490bcf63e4e5aafd32d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 17 Jul 2024 17:31:44 +0100 Subject: [PATCH 19/32] Windows: Fix the vadyarascan sanity check --- volatility3/framework/plugins/windows/vadyarascan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/vadyarascan.py b/volatility3/framework/plugins/windows/vadyarascan.py index 219faf028..dc318dd93 100644 --- a/volatility3/framework/plugins/windows/vadyarascan.py +++ b/volatility3/framework/plugins/windows/vadyarascan.py @@ -56,7 +56,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface): filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - sanity_check = 0x2000 * 0x1000 * 0x1000 + sanity_check = 1024 * 1024 * 1024 # 1 GB for task in pslist.PsList.list_processes( context=self.context, From a684e284ccca7675d4e3cd07c39679ecba703627 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Fri, 5 Jul 2024 15:45:46 -0500 Subject: [PATCH 20/32] Windows: Adds shimcache symbol files + extensions --- .../framework/symbols/windows/__init__.py | 1 + .../symbols/windows/extensions/__init__.py | 49 +- .../symbols/windows/extensions/shimcache.py | 278 ++++++++++ .../windows/shimcache/shimcache-2003-x64.json | 327 ++++++++++++ .../windows/shimcache/shimcache-2003-x86.json | 334 ++++++++++++ .../shimcache/shimcache-vista-x64.json | 334 ++++++++++++ .../shimcache/shimcache-vista-x86.json | 334 ++++++++++++ .../shimcache/shimcache-win10-x64.json | 371 ++++++++++++++ .../shimcache/shimcache-win10-x86.json | 371 ++++++++++++++ .../windows/shimcache/shimcache-win7-x64.json | 348 +++++++++++++ .../windows/shimcache/shimcache-win7-x86.json | 348 +++++++++++++ .../windows/shimcache/shimcache-win8-x64.json | 392 ++++++++++++++ .../windows/shimcache/shimcache-win8-x86.json | 386 ++++++++++++++ .../shimcache/shimcache-xp-sp2-x86.json | 485 ++++++++++++++++++ .../shimcache/shimcache-xp-sp3-x86.json | 485 ++++++++++++++++++ .../framework/symbols/windows/versions.py | 27 + 16 files changed, 4863 insertions(+), 7 deletions(-) create mode 100644 volatility3/framework/symbols/windows/extensions/shimcache.py create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-2003-x64.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-2003-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-vista-x64.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-vista-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win10-x64.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win10-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win7-x64.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win7-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win8-x64.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-win8-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp2-x86.json create mode 100644 volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp3-x86.json diff --git a/volatility3/framework/symbols/windows/__init__.py b/volatility3/framework/symbols/windows/__init__.py index abf9f6da3..e43a2486b 100755 --- a/volatility3/framework/symbols/windows/__init__.py +++ b/volatility3/framework/symbols/windows/__init__.py @@ -17,6 +17,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class("_KTHREAD", extensions.KTHREAD) self.set_type_class("_LIST_ENTRY", extensions.LIST_ENTRY) self.set_type_class("_EPROCESS", extensions.EPROCESS) + self.set_type_class("_ERESOURCE", extensions.ERESOURCE) self.set_type_class("_UNICODE_STRING", extensions.UNICODE_STRING) self.set_type_class("_EX_FAST_REF", extensions.EX_FAST_REF) self.set_type_class("_TOKEN", extensions.TOKEN) diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index 93c19599e..39b50e180 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -306,16 +306,19 @@ class MMVAD_SHORT(objects.StructType): raise AttributeError("Unable to find the private memory member") + @property + def Protection(self): + if self.has_member("u"): + return self.u.VadFlags.Protection + elif self.has_member("Core"): + return self.Core.u.VadFlags.Protection + else: + return None + def get_protection(self, protect_values, winnt_protections): """Get the VAD's protection constants as a string.""" - protect = None - - if self.has_member("u"): - protect = self.u.VadFlags.Protection - - elif self.has_member("Core"): - protect = self.Core.u.VadFlags.Protection + protect = self.Protection try: value = protect_values[protect] @@ -593,6 +596,38 @@ class UNICODE_STRING(objects.StructType): String = property(get_string) +class ERESOURCE(objects.StructType): + def is_valid(self) -> bool: + vollog.debug(f"Checking ERESOURCE Validity: {hex(self.vol.offset)}") + + if not self._context.layers[self.vol.layer_name].is_valid(self.vol.offset): + return False + + sym_table = self.get_symbol_table_name() + + waiters_valid = self.SharedWaiters == 0 or self._context.layers[ + self.vol.layer_name + ].is_valid( + self.SharedWaiters.vol.offset, + self._context.symbol_space.get_type( + sym_table + constants.BANG + "_KSEMAPHORE" + ).size, + ) + + try: + return ( + waiters_valid + and self.SystemResourcesList.Flink is not None + and self.SystemResourcesList.Blink is not None + and self.SystemResourcesList.Flink != self.SystemResourcesList.Blink + and self.SystemResourcesList.Flink.Blink == self.vol.offset + and self.SystemResourcesList.Blink.Flink == self.vol.offset + and self.NumberOfSharedWaiters == 0 + ) + except exceptions.InvalidAddressException: + return False + + class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): """A class for executive kernel processes objects.""" diff --git a/volatility3/framework/symbols/windows/extensions/shimcache.py b/volatility3/framework/symbols/windows/extensions/shimcache.py new file mode 100644 index 000000000..b84a7df6f --- /dev/null +++ b/volatility3/framework/symbols/windows/extensions/shimcache.py @@ -0,0 +1,278 @@ +# 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 +# + +import logging +import struct +from datetime import datetime +from typing import Dict, Optional, Tuple, Union + +from volatility3.framework import constants, exceptions, interfaces, objects, renderers +from volatility3.framework.symbols.windows.extensions import conversion + +vollog = logging.getLogger(__name__) + + +class SHIM_CACHE_ENTRY(objects.StructType): + """Class for abstracting variations in the shimcache LRU list entry structure""" + + def __init__( + self, + context: interfaces.context.ContextInterface, + type_name: str, + object_info: interfaces.objects.ObjectInformation, + size: int, + members: Dict[str, Tuple[int, interfaces.objects.Template]], + ) -> None: + super().__init__(context, type_name, object_info, size, members) + self._exec_flag = None + self._file_path = None + self._file_size = None + self._last_modified = None + self._last_updated = None + + @property + def exec_flag(self) -> Union[bool, interfaces.renderers.BaseAbsentValue]: + """Checks if InsertFlags fields has been bitwise OR'd with a value of 2. + This behavior was observed when processes are created by CSRSS.""" + if self._exec_flag is not None: + return self._exec_flag + + if hasattr(self, "ListEntryDetail") and hasattr( + self.ListEntryDetail, "InsertFlags" + ): + self._exec_flag = self.ListEntryDetail.InsertFlags & 0x2 == 2 + + elif hasattr(self, "InsertFlags"): + self._exec_flag = self.InsertFlags & 0x2 == 2 + + elif hasattr(self, "ListEntryDetail") and hasattr( + self.ListEntryDetail, "BlobBuffer" + ): + blob_offset = self.ListEntryDetail.BlobBuffer + blob_size = self.ListEntryDetail.BlobSize + + if not self._context.layers[self.vol.native_layer_name].is_valid( + blob_offset, blob_size + ): + self._exec_flag = renderers.UnparsableValue() + + raw_flag = self._context.layers[self.vol.native_layer_name].read( + blob_offset, blob_size + ) + if not raw_flag: + self._exec_flag = renderers.UnparsableValue() + + try: + self._exec_flag = bool(struct.unpack(" Union[int, interfaces.renderers.BaseAbsentValue]: + if self._file_size is not None: + return self._file_size + try: + self._file_size = self.FileSize + if self._file_size < 0: + self._file_size = 0 + + except AttributeError: + self._file_size = renderers.NotApplicableValue() + except exceptions.InvalidAddressException: + self._file_size = renderers.UnreadableValue() + + return self._file_size + + @property + def last_modified(self) -> Union[datetime, interfaces.renderers.BaseAbsentValue]: + if self._last_modified is not None: + return self._last_modified + try: + self._last_modified = conversion.wintime_to_datetime( + self.ListEntryDetail.LastModified.QuadPart + ) + except AttributeError: + self._last_modified = conversion.wintime_to_datetime( + self.LastModified.QuadPart + ) + except exceptions.InvalidAddressException: + self._last_modified = renderers.UnreadableValue() + + return self._last_modified + + @property + def last_update(self) -> Union[datetime, interfaces.renderers.BaseAbsentValue]: + if self._last_updated is not None: + return self._last_updated + + try: + self._last_updated = conversion.wintime_to_datetime( + self.LastUpdate.QuadPart + ) + except AttributeError: + self._last_updated = renderers.NotApplicableValue() + + return self._last_updated + + @property + def file_path(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: + if self._file_path is not None: + return self._file_path + + if not hasattr(self.Path, "Buffer"): + return self.Path.cast( + "string", max_length=self.Path.vol.count, encoding="utf-16le" + ) + + try: + file_path_raw = ( + self._context.layers[self.vol.native_layer_name].read( + self.Path.Buffer, self.Path.Length + ) + or b"" + ) + self._file_path = file_path_raw.decode("utf-16", errors="replace") + except exceptions.InvalidAddressException: + self._file_path = renderers.UnreadableValue() + + return self._file_path + + def is_valid(self) -> bool: + """Shim cache validation is limited to ensuring that a subset of the + pointers in the LIST_ENTRY field are valid (similar to validation of + ERESOURCE)""" + + # shim entries on Windows XP do not have list entry attributes; in this case, + # perform a different set of validations + try: + if not hasattr(self, "ListEntry"): + return bool(self.last_modified and self.last_update and self.file_size) + + # on some platforms ListEntry.Blink is null, so this cannot be validated + if ( + self.ListEntry.Flink != 0 + and ( + self.ListEntry.Blink.dereference() + != self.ListEntry.Flink.dereference() + ) + and ( + self.ListEntry.Flink.Blink + == self.ListEntry.Flink.Blink.dereference().vol.offset + ) + ): + + return True + else: + return False + except exceptions.InvalidAddressException: + return False + + +class SHIM_CACHE_HANDLE(objects.StructType): + def __init__( + self, + context: interfaces.context.ContextInterface, + type_name: str, + object_info: interfaces.objects.ObjectInformation, + size: int, + members: Dict[str, Tuple[int, interfaces.objects.Template]], + ) -> None: + super().__init__(context, type_name, object_info, size, members) + + @property + def head(self) -> Optional[SHIM_CACHE_ENTRY]: + try: + if not self.eresource.is_valid(): + return None + except exceptions.InvalidAddressException: + return None + + rtl_avl_table = self._context.object( + self.get_symbol_table_name() + constants.BANG + "_RTL_AVL_TABLE", + self.vol.layer_name, + self.rtl_avl_table, + self.vol.native_layer_name, + ) + + if not self._context.layers[self.vol.layer_name].is_valid( + self.rtl_avl_table.vol.offset + ): + return None + + offset_head = rtl_avl_table.vol.offset + rtl_avl_table.vol.size + + head = self._context.object( + self.get_symbol_table_name() + constants.BANG + "SHIM_CACHE_ENTRY", + self.vol.layer_name, + offset_head, + ) + + if not head.is_valid(): + return None + + return head + + def is_valid(self, avl_section_start: int, avl_section_end: int) -> bool: + if self.vol.offset == 0: + return False + + vollog.debug(f"Checking SHIM_CACHE_HANDLE validity @ {hex(self.vol.offset)}") + + if not ( + self._context.layers[self.vol.layer_name].is_valid(self.vol.offset) + and self.eresource.is_valid() + and self.rtl_avl_table.is_valid(avl_section_start, avl_section_end) + and self.head + ): + return False + + return self.head.is_valid() + + +class RTL_AVL_TABLE(objects.StructType): + def is_valid(self, page_start: int, page_end: int) -> bool: + try: + if self.BalancedRoot.Parent != self.BalancedRoot.vol.offset: + vollog.debug( + f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed BalancedRoot parent equality check" + ) + return False + + elif self.AllocateRoutine < page_start or self.AllocateRoutine > page_end: + vollog.debug( + f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed AllocateRoutine range check" + ) + return False + + elif self.CompareRoutine < page_start or self.CompareRoutine > page_end: + vollog.debug( + f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed CompareRoutine range check" + ) + return False + + elif ( + (self.AllocateRoutine.vol.offset == self.CompareRoutine.vol.offset) + or (self.AllocateRoutine.vol.offset == self.FreeRoutine.vol.offset) + or (self.CompareRoutine.vol.offset == self.FreeRoutine.vol.offset) + ): + vollog.debug( + f"RTL_AVL_TABLE @ {self.vol.offset} Invalid: Failed (Compare|Allocate|Free)Routine uniqueness check" + ) + return False + + return True + except exceptions.InvalidAddressException: + return False + + +class_types = { + "SHIM_CACHE_HANDLE": SHIM_CACHE_HANDLE, + "SHIM_CACHE_ENTRY": SHIM_CACHE_ENTRY, + "_RTL_AVL_TABLE": RTL_AVL_TABLE, +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x64.json b/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x64.json new file mode 100644 index 000000000..d1540f12f --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x64.json @@ -0,0 +1,327 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 16 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 24 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 25 + } + }, + "kind": "struct", + "size": 32 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 32 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 40 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 44 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 48 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 56 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 64 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 72 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 80 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 88 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 96 + } + }, + "kind": "struct", + "size": 104 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 16 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 16, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 32, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "FileSize": { + "offset": 40, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 48 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x86.json new file mode 100644 index 000000000..e4739c4a0 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-2003-x86.json @@ -0,0 +1,334 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 8, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 16, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "FileSize": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Padding": { + "offset": 32, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 36 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x64.json b/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x64.json new file mode 100644 index 000000000..0c5183a4a --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x64.json @@ -0,0 +1,334 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 16 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 24 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 25 + } + }, + "kind": "struct", + "size": 32 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 32 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 40 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 44 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 48 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 56 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 64 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 72 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 80 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 88 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 96 + } + }, + "kind": "struct", + "size": 104 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 16 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 16, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 32, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 40, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 44, + "type": { + "kind": "base", + "name": "unsigned int" + } + } + }, + "kind": "struct", + "size": 48 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x86.json new file mode 100644 index 000000000..91290b1ba --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-vista-x86.json @@ -0,0 +1,334 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 8, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 16, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 28, + "type": { + "kind": "base", + "name": "unsigned int" + } + } + }, + "kind": "struct", + "size": 36 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x64.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x64.json new file mode 100644 index 000000000..fe9593af3 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x64.json @@ -0,0 +1,371 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 16 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 24 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 25 + } + }, + "kind": "struct", + "size": 32 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 32 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 40 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 44 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 48 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 56 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 64 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 72 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 80 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 88 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 96 + } + }, + "kind": "struct", + "size": 104 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 16 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "u1": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Path": { + "offset": 24, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "ListEntryDetail": { + "offset": 40, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "SHIM_CACHE_ENTRY_DETAIL" + } + } + } + }, + "kind": "struct", + "size": 48 + }, + "SHIM_CACHE_ENTRY_DETAIL": { + "fields": { + "u1": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "LastModified": { + "offset": 8, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "BlobSize": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "u2": { + "offset": 20, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "BlobBuffer": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 32 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x86.json new file mode 100644 index 000000000..26d493c37 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win10-x86.json @@ -0,0 +1,371 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 4 + } + }, + "kind": "struct", + "size": 8 + }, + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "u1": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Path": { + "offset": 12, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "ListEntryDetail": { + "offset": 20, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "SHIM_CACHE_ENTRY_DETAIL" + } + } + } + }, + "kind": "struct", + "size": 24 + }, + "SHIM_CACHE_ENTRY_DETAIL": { + "fields": { + "u1": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "InsertFlags": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "LastModified": { + "offset": 8, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "BlobSize": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "BlobBuffer": { + "offset": 20, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 24 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x64.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x64.json new file mode 100644 index 000000000..eac5407ba --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x64.json @@ -0,0 +1,348 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 16 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 24 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 25 + } + }, + "kind": "struct", + "size": 32 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 32 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 40 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 44 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 48 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 56 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 64 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 72 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 80 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 88 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 96 + } + }, + "kind": "struct", + "size": 104 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 16, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 32, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 40, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 44, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "BlobSize": { + "offset": 48, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "BlobBuffer": { + "offset": 56, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 64 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x86.json new file mode 100644 index 000000000..423f7e255 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win7-x86.json @@ -0,0 +1,348 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 4 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "Path": { + "offset": 8, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "LastModified": { + "offset": 16, + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 28, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "BlobSize": { + "offset": 32, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "BlobBuffer": { + "offset": 36, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 40 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x64.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x64.json new file mode 100644 index 000000000..a40c8d680 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x64.json @@ -0,0 +1,392 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 16 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 24 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 25 + } + }, + "kind": "struct", + "size": 32 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 32 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 40 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 44 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 48 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 56 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 64 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 72 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 80 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 88 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 96 + } + }, + "kind": "struct", + "size": 104 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 8 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "u1": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Path": { + "offset": 24, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "u2": { + "offset": 40, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "u3": { + "offset": 48, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "ListEntryDetail": { + "offset": 56, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "SHIM_CACHE_ENTRY_DETAIL" + } + } + } + }, + "kind": "struct", + "size": 64 + }, + "SHIM_CACHE_ENTRY_DETAIL": { + "fields": { + "LastModified": { + "offset": 0, + "type": { + "kind": "struct", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "BlobSize": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Padding": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "BlobBuffer": { + "offset": 32, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 40 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x86.json new file mode 100644 index 000000000..c3cee5feb --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-win8-x86.json @@ -0,0 +1,386 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 4 + } + }, + "kind": "struct", + "size": 8 + }, + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "ListEntry": { + "offset": 0, + "type": { + "kind": "struct", + "name": "nt_symbols!_LIST_ENTRY" + } + }, + "u1": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "u2": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Path": { + "offset": 16, + "type": { + "kind": "struct", + "name": "nt_symbols!_UNICODE_STRING" + } + }, + "u3": { + "offset": 24, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "ListEntryDetail": { + "offset": 32, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "SHIM_CACHE_ENTRY_DETAIL" + } + } + } + }, + "kind": "struct", + "size": 36 + }, + "SHIM_CACHE_ENTRY_DETAIL": { + "fields": { + "LastModified": { + "offset": 0, + "type": { + "kind": "struct", + "name": "_LARGE_INTEGER" + } + }, + "InsertFlags": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned int" + } + }, + "ShimFlags": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "BlobSize": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "BlobBuffer": { + "offset": 20, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 24 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} + diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp2-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp2-x86.json new file mode 100644 index 000000000..6114e6c85 --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp2-x86.json @@ -0,0 +1,485 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 4 + } + }, + "kind": "struct", + "size": 8 + }, + "SHIM_CACHE_HEADER": { + "fields": { + "Magic": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 0 + }, + "u1": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 4 + }, + "NumEntries": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 8 + }, + "u2": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 400 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "Path": { + "type": { + "count": 520, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 0 + }, + "LastModified": { + "type": { + "kind": "union", + "name": "LARGE_INTEGER" + }, + "offset": 4 + }, + "FileSize": { + "type": { + "kind": "base", + "name": "long long" + }, + "offset": 8 + }, + "LastUpdate": { + "type": { + "kind": "union", + "name": "LARGE_INTEGER" + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 552 + }, + "_SEGMENT": { + "fields": { + "ControlArea": { + "offset": 0, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_CONTROL_AREA" + } + } + }, + "TotalNumberOfPtes": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "NonExtendedPtes": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "WritableUserReferences": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "SizeOfSegment": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "SegmentPteTemplate": { + "offset": 24, + "type": { + "kind": "struct", + "name": "nt_symbols!_MMPTE" + } + }, + "NumberOfCommittedPages": { + "offset": 28, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "ExtendInfo": { + "offset": 32, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_MMEXTEND_INFO" + } + } + }, + "SystemImageBase": { + "offset": 36, + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + } + }, + "BasedAddress": { + "offset": 40, + "type": { + "kind": "base", + "name": "long" + } + }, + "u1": { + "offset": 44, + "type": { + "kind": "base", + "name": "long" + } + }, + "u2": { + "offset": 48, + "type": { + "kind": "base", + "name": "long" + } + }, + "PrototypePte": { + "offset": 52, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_MMPTE" + } + } + }, + "ThePtes": { + "offset": 60, + "type": { + "kind": "array", + "count": 1, + "subtype": { + "kind": "base", + "name": "nt_symbols!_MMPTE" + } + } + } + }, + "kind": "struct", + "size": 64 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp3-x86.json b/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp3-x86.json new file mode 100644 index 000000000..a9b86d93c --- /dev/null +++ b/volatility3/framework/symbols/windows/shimcache/shimcache-xp-sp3-x86.json @@ -0,0 +1,485 @@ +{ + "symbols": {}, + "enums": {}, + "base_types": { + "unsigned long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned long long": { + "kind": "int", + "size": 8, + "signed": false, + "endian": "little" + }, + "unsigned char": { + "kind": "char", + "size": 1, + "signed": false, + "endian": "little" + }, + "pointer": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned int": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + }, + "unsigned short": { + "kind": "int", + "size": 2, + "signed": false, + "endian": "little" + }, + "long": { + "kind": "int", + "size": 4, + "signed": false, + "endian": "little" + } + }, + "user_types": { + "_LARGE_INTEGER": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "QuadPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "long long" + } + }, + "u": { + "offset": 0, + "type": { + "kind": "struct", + "name": "__unnamed_2" + } + } + }, + "kind": "union", + "size": 8 + }, + "__unnamed_2": { + "fields": { + "HighPart": { + "offset": 4, + "type": { + "kind": "base", + "name": "long" + } + }, + "LowPart": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + }, + "_RTL_BALANCED_LINKS": { + "fields": { + "Parent": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 0 + }, + "LeftChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 4 + }, + "RightChild": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 8 + }, + "Balance": { + "type": { + "kind": "base", + "name": "unsigned char" + }, + "offset": 12 + }, + "Reserved": { + "type": { + "kind": "array", + "count": 3, + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 16 + }, + "_RTL_AVL_TABLE": { + "fields": { + "BalancedRoot": { + "type": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + }, + "offset": 0 + }, + "OrderedPointer": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 16 + }, + "WhichOrderedElement": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 20 + }, + "NumberGenericTableElements": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 24 + }, + "DepthOfTree": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 28 + }, + "RestartKey": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_BALANCED_LINKS" + } + }, + "offset": 32 + }, + "DeleteCount": { + "type": { + "kind": "base", + "name": "unsigned long" + }, + "offset": 36 + }, + "CompareRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 40 + }, + "AllocateRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 44 + }, + "FreeRoutine": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 48 + }, + "TableContext": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + }, + "offset": 52 + } + }, + "kind": "struct", + "size": 56 + }, + "SHIM_CACHE_HEADER": { + "fields": { + "Magic": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 0 + }, + "u1": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 4 + }, + "NumEntries": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 8 + }, + "u2": { + "type": { + "kind": "base", + "name": "unsigned int" + }, + "offset": 12 + } + }, + "kind": "struct", + "size": 400 + }, + "SHIM_CACHE_ENTRY": { + "fields": { + "Path": { + "type": { + "count": 520, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned char" + } + }, + "offset": 0 + }, + "LastModified": { + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + }, + "offset": 528 + }, + "FileSize": { + "type": { + "kind": "base", + "name": "long long" + }, + "offset": 536 + }, + "LastUpdate": { + "type": { + "kind": "union", + "name": "_LARGE_INTEGER" + }, + "offset": 544 + } + }, + "kind": "struct", + "size": 552 + }, + "SHIM_CACHE_HANDLE": { + "fields": { + "eresource": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!ERESOURCE" + } + }, + "offset": 0 + }, + "rtl_avl_table": { + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "_RTL_AVL_TABLE" + } + }, + "offset": 4 + } + }, + "kind": "struct", + "size": 8 + }, + "_SEGMENT": { + "fields": { + "ControlArea": { + "offset": 0, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_CONTROL_AREA" + } + } + }, + "TotalNumberOfPtes": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "NonExtendedPtes": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "WritableUserReferences": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "SizeOfSegment": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "SegmentPteTemplate": { + "offset": 24, + "type": { + "kind": "struct", + "name": "nt_symbols!_MMPTE" + } + }, + "NumberOfCommittedPages": { + "offset": 32, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "ExtendInfo": { + "offset": 36, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_MMEXTEND_INFO" + } + } + }, + "SystemImageBase": { + "offset": 40, + "type": { + "kind": "pointer", + "subtype": { + "kind": "base", + "name": "void" + } + } + }, + "BasedAddress": { + "offset": 44, + "type": { + "kind": "base", + "name": "long" + } + }, + "u1": { + "offset": 48, + "type": { + "kind": "base", + "name": "long" + } + }, + "u2": { + "offset": 52, + "type": { + "kind": "base", + "name": "long" + } + }, + "PrototypePte": { + "offset": 56, + "type": { + "kind": "pointer", + "subtype": { + "kind": "struct", + "name": "nt_symbols!_MMPTE" + } + } + }, + "ThePtes": { + "offset": 64, + "type": { + "kind": "array", + "count": 1, + "subtype": { + "kind": "base", + "name": "nt_symbols!_MMPTE" + } + } + } + }, + "kind": "struct", + "size": 72 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dgmcdona by hand", + "datetime": "2024-07-05T18:28:00.000000+00:00" + }, + "format": "4.0.0" + } +} diff --git a/volatility3/framework/symbols/windows/versions.py b/volatility3/framework/symbols/windows/versions.py index e1e74afc0..78e90a1d4 100644 --- a/volatility3/framework/symbols/windows/versions.py +++ b/volatility3/framework/symbols/windows/versions.py @@ -114,6 +114,24 @@ is_windows_xp = OsDistinguisher( ], ) +is_windows_xp_sp2 = OsDistinguisher( + version_check=lambda x: (5, 1) <= x < (5, 2), + fallback_checks=[ + ("KdCopyDataBlock", None, False), + ("_MMFREE_POOL_ENTRY", None, False), + ("_HANDLE_TABLE", "HandleCount", True), + ], +) + +is_windows_xp_sp3 = OsDistinguisher( + version_check=lambda x: (5, 1) <= x < (5, 2), + fallback_checks=[ + ("KdCopyDataBlock", None, False), + ("_MMFREE_POOL_ENTRY", None, True), + ("_HANDLE_TABLE", "HandleCount", True), + ], +) + is_xp_or_2003 = OsDistinguisher( version_check=lambda x: (5, 1) <= x < (6, 0), fallback_checks=[ @@ -122,6 +140,15 @@ is_xp_or_2003 = OsDistinguisher( ], ) +is_2003 = OsDistinguisher( + version_check=lambda x: (5, 2) <= x < (5, 3), + fallback_checks=[ + ("KdCopyDataBlock", None, False), + ("_HANDLE_TABLE", "HandleCount", True), + ("_MM_AVL_TABLE", None, True), + ], +) + is_win10_up_to_15063 = OsDistinguisher( version_check=lambda x: (10, 0) <= x < (10, 0, 15063), fallback_checks=[ From 4048d0b122eb9030a011012fd4f1ff59d1e0ca46 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Tue, 9 Jul 2024 11:43:29 -0500 Subject: [PATCH 21/32] Windows: Adds shimcachemem plugin --- .../framework/plugins/windows/shimcachemem.py | 610 ++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 volatility3/framework/plugins/windows/shimcachemem.py diff --git a/volatility3/framework/plugins/windows/shimcachemem.py b/volatility3/framework/plugins/windows/shimcachemem.py new file mode 100644 index 000000000..6afaf4356 --- /dev/null +++ b/volatility3/framework/plugins/windows/shimcachemem.py @@ -0,0 +1,610 @@ +# 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 +# +import logging +import os +from datetime import datetime +from itertools import count +from typing import Iterator, List, Optional, Tuple + +from volatility3.framework import constants, exceptions, interfaces, renderers, symbols +from volatility3.framework.configuration import requirements +from volatility3.framework.objects.utility import array_to_string +from volatility3.framework.renderers import format_hints +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows import versions +from volatility3.framework.symbols.windows.extensions import pe, shimcache +from volatility3.plugins import timeliner +from volatility3.plugins.windows import modules, pslist, vadinfo + +# from volatility3.plugins.windows import pslist, vadinfo, modules + +vollog = logging.getLogger(__name__) + + +class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): + """Reads Shimcache entries from the ahcache.sys AVL tree""" + + _required_framework_version = (2, 0, 0) + + # These checks must be completed from newest -> oldest OS version. + _win_version_file_map: List[Tuple[versions.OsDistinguisher, bool, str]] = [ + (versions.is_win10, True, "shimcache-win10-x64"), + (versions.is_win10, False, "shimcache-win10-x86"), + (versions.is_windows_8_or_later, True, "shimcache-win8-x64"), + (versions.is_windows_8_or_later, False, "shimcache-win8-x86"), + (versions.is_windows_7, True, "shimcache-win7-x64"), + (versions.is_windows_7, False, "shimcache-win7-x86"), + (versions.is_vista_or_later, True, "shimcache-vista-x64"), + (versions.is_vista_or_later, False, "shimcache-vista-x86"), + (versions.is_2003, False, "shimcache-2003-x86"), + (versions.is_2003, True, "shimcache-2003-x64"), + (versions.is_windows_xp_sp3, False, "shimcache-xp-sp3-x86"), + (versions.is_windows_xp_sp2, False, "shimcache-xp-sp2-x86"), + (versions.is_xp_or_2003, True, "shimcache-xp-2003-x64"), + (versions.is_xp_or_2003, False, "shimcache-xp-2003-x86"), + ] + + NT_KRNL_MODS = ["ntoskrnl.exe", "ntkrnlpa.exe", "ntkrnlmp.exe", "ntkrpamp.exe"] + + def generate_timeline( + self, + ) -> Iterator[Tuple[str, timeliner.TimeLinerType, datetime]]: + for _, (_, last_modified, last_update, _, _, file_path) in self._generator(): + if isinstance(last_update, datetime): + yield f"Shimcache: File {file_path} executed", timeliner.TimeLinerType.ACCESSED, last_update + if isinstance(last_modified, datetime): + yield f"Shimcache: File {file_path} modified", timeliner.TimeLinerType.MODIFIED, last_modified + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.PluginRequirement( + name="pslist", plugin=pslist.PsList, version=(2, 0, 0) + ), + requirements.VersionRequirement( + name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0) + ), + requirements.VersionRequirement( + name="modules", component=modules.Modules, version=(2, 0, 0) + ), + ] + + @staticmethod + def create_shimcache_table( + context: interfaces.context.ContextInterface, + symbol_table: str, + config_path: str, + ) -> str: + """Creates a shimcache symbol table + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + symbol_table: The name of an existing symbol table containing the kernel symbols + config_path: The configuration path within the context of the symbol table to create + + Returns: + The name of the constructed shimcache table + """ + native_types = context.symbol_space[symbol_table].natives + is_64bit = symbols.symbol_table_is_64bit(context, symbol_table) + table_mapping = {"nt_symbols": symbol_table} + + try: + symbol_filename = next( + filename + for version_check, for_64bit, filename in ShimcacheMem._win_version_file_map + if is_64bit == for_64bit + and version_check(context=context, symbol_table=symbol_table) + ) + except StopIteration: + raise NotImplementedError("This version of Windows is not supported!") + + vollog.debug(f"Using shimcache table {symbol_filename}") + + return intermed.IntermediateSymbolTable.create( + context, + config_path, + os.path.join("windows", "shimcache"), + symbol_filename, + class_types=shimcache.class_types, + native_types=native_types, + table_mapping=table_mapping, + ) + + @classmethod + def find_shimcache_win_xp( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + kernel_symbol_table: str, + shimcache_symbol_table: str, + ) -> Iterator[shimcache.SHIM_CACHE_ENTRY]: + """Attempts to find the shimcache in a Windows XP memory image + + :param context: The context to retrieve required elements (layers, symbol tables) from + :param layer_name: The name of the memory layer on which to operate. + :param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols + :param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols + """ + + SHIM_NUM_ENTRIES_OFFSET = 0x8 + SHIM_MAX_ENTRIES = 0x60 # 96 max entries in XP shim cache + SHIM_LRU_OFFSET = 0x10 + SHIM_HEADER_SIZE = 0x190 + SHIM_CACHE_ENTRY_SIZE = 0x228 + + seen = set() + + for process in pslist.PsList.list_processes( + context, layer_name, kernel_symbol_table + ): + pid = process.UniqueProcessId + vollog.debug("checking process %d" % pid) + for vad in vadinfo.VadInfo.list_vads( + process, lambda x: x.get_tag() == b"Vad " and x.Protection == 4 + ): + try: + proc_layer_name = process.add_process_layer() + proc_layer = context.layers[proc_layer_name] + except exceptions.InvalidAddressException: + continue + + try: + if proc_layer.read(vad.get_start(), 4) != b"\xEF\xBE\xAD\xDE": + if pid == 624: + vollog.debug("VAD magic bytes don't match DEADBEEF") + continue + except exceptions.InvalidAddressException: + continue + + num_entries = context.object( + shimcache_symbol_table + constants.BANG + "unsigned int", + proc_layer_name, + vad.get_start() + SHIM_NUM_ENTRIES_OFFSET, + ) + + if num_entries > SHIM_MAX_ENTRIES: + continue + + cache_idx_ptr = vad.get_start() + SHIM_LRU_OFFSET + + for _ in range(num_entries): + cache_idx_val = proc_layer.context.object( + shimcache_symbol_table + constants.BANG + "unsigned long", + proc_layer_name, + cache_idx_ptr, + ) + + cache_idx_ptr += 4 + + if cache_idx_val > SHIM_MAX_ENTRIES - 1: + continue + + shim_entry_offset = ( + vad.get_start() + + SHIM_HEADER_SIZE + + (SHIM_CACHE_ENTRY_SIZE * cache_idx_val) + ) + + if not proc_layer.is_valid(shim_entry_offset): + continue + + physical_addr = proc_layer.translate(shim_entry_offset) + + if physical_addr in seen: + continue + seen.add(physical_addr) + + shim_entry = proc_layer.context.object( + shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", + proc_layer_name, + shim_entry_offset, + ) + if not proc_layer.is_valid(shim_entry.vol.offset): + continue + if not shim_entry.is_valid(): + continue + + yield shim_entry + + @classmethod + def find_shimcache_win_2k3_to_7( + cls, + context: interfaces.context.ContextInterface, + config_path: str, + kernel_layer_name: str, + nt_symbol_table: str, + shimcache_symbol_table: str, + ) -> Iterator[shimcache.SHIM_CACHE_ENTRY]: + """Implements the algorithm to search for the shim cache on Windows 2000 + (x64) through Windows 7 / 2008 R2. The algorithm consists of the following: + + 1) Find the NT kernel module's .data and PAGE sections + 2) Iterate over every 4/8 bytes (depending on OS bitness) in the .data + section and test for the following: + a) offset represents a valid RTL_AVL_TABLE object + b) RTL_AVL_TABLE is preceeded by an ERESOURCE object + c) RTL_AVL_TABLE is followed by the beginning of the SHIM LRU list + + :param context: The context to retrieve required elements (layers, symbol tables) from + :param layer_name: The name of the memory layer on which to operate. + :param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols + :param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols + """ + + data_sec = cls.get_module_section_range( + context, + config_path, + kernel_layer_name, + nt_symbol_table, + cls.NT_KRNL_MODS, + ".data", + ) + mod_page = cls.get_module_section_range( + context, + config_path, + kernel_layer_name, + nt_symbol_table, + cls.NT_KRNL_MODS, + "PAGE", + ) + + # We require both in order to accurately handle AVL table + if not (data_sec and mod_page): + return None + + data_sec_offset, data_sec_size = data_sec + mod_page_offset, mod_page_size = mod_page + + addr_size = 8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4 + + shim_head = None + for offset in range( + data_sec_offset, data_sec_offset + data_sec_size, addr_size + ): + shim_head = cls.try_get_shim_head_at_offset( + context, + shimcache_symbol_table, + nt_symbol_table, + kernel_layer_name, + mod_page_offset, + mod_page_offset + mod_page_size, + offset, + ) + + if shim_head: + break + + if not shim_head: + return + + for shim_entry in shim_head.ListEntry.to_list( + shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", "ListEntry" + ): + yield shim_entry + + @classmethod + def try_get_shim_head_at_offset( + cls, + context: interfaces.context.ContextInterface, + symbol_table: str, + kernel_symbol_table: str, + layer_name: str, + mod_page_start: int, + mod_page_end: int, + offset: int, + ) -> Optional[shimcache.SHIM_CACHE_ENTRY]: + """Attempts to construct a SHIM_CACHE_HEAD within a layer of the given context, + using the provided offset within that layer, as well as the start and end offsets + of the kernel module's `PAGE` section start and end offsets. + + If a number of validity checks are passed, this method will return the `SHIM_CACHE_HEAD` + object. Otherwise, `None` is returned. + """ + # print("checking RTL_AVL_TABLE at offset %s" % hex(offset)) + rtl_avl_table = context.object( + symbol_table + constants.BANG + "_RTL_AVL_TABLE", layer_name, offset + ) + if not rtl_avl_table.is_valid(mod_page_start, mod_page_end): + return None + + vollog.debug(f"Candidate RTL_AVL_TABLE found at offset {hex(offset)}") + + ersrc_size = context.symbol_space.get_type( + kernel_symbol_table + constants.BANG + "_ERESOURCE" + ).size + ersrc_alignment = ( + 0x20 + if symbols.symbol_table_is_64bit(context, kernel_symbol_table) + else 0x10 + # 0x20 if context.symbol_space.get_type("pointer").size == 8 else 0x10 + ) + vollog.debug( + f"ERESOURCE size: {hex(ersrc_size)}, ERESOURCE alignment: {hex(ersrc_alignment)}" + ) + + eresource_rel_off = ersrc_size + ((offset - ersrc_size) % ersrc_alignment) + eresource_offset = offset - eresource_rel_off + + vollog.debug("Constructing ERESOURCE at %s" % hex(eresource_offset)) + eresource = context.object( + kernel_symbol_table + constants.BANG + "_ERESOURCE", + layer_name, + eresource_offset, + ) + if not eresource.is_valid(): + vollog.debug("ERESOURCE Invalid") + return None + + shim_head_offset = offset + rtl_avl_table.vol.size + + if not context.layers[layer_name].is_valid(shim_head_offset): + return None + + shim_head = context.object( + symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", + layer_name, + shim_head_offset, + ) + + if not shim_head.is_valid(): + vollog.debug("shim head invalid") + return None + else: + vollog.debug("returning shim head") + return shim_head + + @classmethod + def find_shimcache_win_8_or_later( + cls, + context: interfaces.context.ContextInterface, + config_path: str, + kernel_layer_name: str, + nt_symbol_table: str, + shimcache_symbol_table: str, + ) -> Iterator[shimcache.SHIM_CACHE_ENTRY]: + """Attempts to locate and yield shimcache entries from a Windows 8 or later memory image. + + :param context: The context to retrieve required elements (layers, symbol tables) from + :param layer_name: The name of the memory layer on which to operate. + :param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols + :param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols + """ + + is_8_1_or_later = versions.is_windows_8_1_or_later( + context, nt_symbol_table + ) or versions.is_win10(context, nt_symbol_table) + + module_names = ["ahcache.sys"] if is_8_1_or_later else cls.NT_KRNL_MODS + vollog.debug(f"Searching for modules {module_names}") + + data_sec = cls.get_module_section_range( + context, + config_path, + kernel_layer_name, + nt_symbol_table, + module_names, + ".data", + ) + mod_page = cls.get_module_section_range( + context, + config_path, + kernel_layer_name, + nt_symbol_table, + module_names, + "PAGE", + ) + + if not (data_sec and mod_page): + return None + + mod_page_offset, mod_page_size = mod_page + data_sec_offset, data_sec_size = data_sec + + # iterate over ahcache kernel module's .data section in search of *two* SHIM handles + shim_heads = [] + + vollog.debug(f"PAGE offset: {hex(mod_page_offset)}") + vollog.debug(f".data offset: {hex(data_sec_offset)}") + + handle_type = context.symbol_space.get_type( + shimcache_symbol_table + constants.BANG + "SHIM_CACHE_HANDLE" + ) + for offset in range( + data_sec_offset, + data_sec_offset + data_sec_size, + 8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4, + ): + vollog.debug(f"Building shim handle pointer at {hex(offset)}") + shim_handle = context.object( + object_type=shimcache_symbol_table + constants.BANG + "pointer", + layer_name=kernel_layer_name, + subtype=handle_type, + offset=offset, + ) + + if shim_handle.is_valid(mod_page_offset, mod_page_offset + mod_page_size): + if shim_handle.head is not None: + vollog.debug( + f"Found valid shim handle @ {hex(shim_handle.vol.offset)}" + ) + shim_heads.append(shim_handle.head) + if len(shim_heads) == 2: + break + + if len(shim_heads) != 2: + vollog.debug("Failed to identify two valid SHIM_CACHE_HANDLE structures") + return + + # On Windows 8 x64, the frist cache contains the shim cache + # On Windows 8 x86, 8.1 x86/x64, and 10, the second cache contains the shim cache. + if ( + not symbols.symbol_table_is_64bit(context, nt_symbol_table) + and not is_8_1_or_later + ): + valid_head = shim_heads[1] + elif not is_8_1_or_later: + valid_head = shim_heads[0] + else: + valid_head = shim_heads[1] + + for shim_entry in valid_head.ListEntry.to_list( + shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY", "ListEntry" + ): + if shim_entry.is_valid(): + yield shim_entry + + def _generator(self): + kernel = self.context.modules[self.config["kernel"]] + + shimcache_table_name = self.create_shimcache_table( + self.context, kernel.symbol_table_name, self.config_path + ) + + c = count() + + if versions.is_windows_8_or_later(self._context, kernel.symbol_table_name): + vollog.info("Finding shimcache entries for Windows 8.0+") + entries = self.find_shimcache_win_8_or_later( + self.context, + self.config_path, + kernel.layer_name, + kernel.symbol_table_name, + shimcache_table_name, + ) + + elif ( + versions.is_2003(self.context, kernel.symbol_table_name) + or versions.is_vista_or_later(self.context, kernel.symbol_table_name) + or versions.is_windows_7(self.context, kernel.symbol_table_name) + ): + vollog.info("Finding shimcache entries for Windows 2k3/Vista/7") + entries = self.find_shimcache_win_2k3_to_7( + self.context, + self.config_path, + kernel.layer_name, + kernel.symbol_table_name, + shimcache_table_name, + ) + + elif versions.is_windows_xp_sp2( + self._context, kernel.symbol_table_name + ) or versions.is_windows_xp_sp3(self.context, kernel.symbol_table_name): + vollog.info("Finding shimcache entries for WinXP") + entries = self.find_shimcache_win_xp( + self._context, + kernel.layer_name, + kernel.symbol_table_name, + shimcache_table_name, + ) + else: + vollog.warn("Cannot parse shimcache entries for this version of Windows") + return + + for entry in entries: + try: + vollog.debug(f"SHIM_CACHE_ENTRY type: {entry.__class__}") + shim_entry = ( + entry.last_modified, + entry.last_update, + entry.exec_flag, + ( + format_hints.Hex(entry.file_size) + if isinstance(entry.file_size, int) + else entry.file_size + ), + entry.file_path, + ) + except exceptions.InvalidAddressException: + continue + + yield ( + 0, + (next(c), *shim_entry), + ) + + def run(self): + return renderers.TreeGrid( + [ + ("Order", int), + ("Last Modified", datetime), + ("Last Update", datetime), + ("Exec Flag", bool), + ("File Size", format_hints.Hex), + ("File Path", str), + ], + self._generator(), + ) + + @classmethod + def get_module_section_range( + cls, + context: interfaces.context.ContextInterface, + config_path: str, + layer_name: str, + symbol_table: str, + module_list: List[str], + section_name: str, + ) -> Optional[Tuple[int, int]]: + """Locates the size and offset of the first found module section + specified by name from the list of modules. + + :param context: The context to operate on + :param layer_name: The memory layer to read from + :param module_list: A list of module names to search for the given section + :param section_name: The name of the section to search for. + + :return: The offset and size of the module, if found; Otherwise, returns `None` + """ + + try: + krnl_mod = next( + module + for module in modules.Modules.list_modules( + context, layer_name, symbol_table + ) + if module.BaseDllName.String in module_list + ) + except StopIteration: + return None + + pe_table_name = intermed.IntermediateSymbolTable.create( + context, + interfaces.configuration.path_join(config_path, "pe"), + "windows", + "pe", + class_types=pe.class_types, + ) + + # code taken from Win32KBase._section_chunks (win32_core.py) + dos_header = context.object( + pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", + layer_name, + offset=krnl_mod.DllBase, + ) + + if not dos_header: + return None + + nt_header = dos_header.get_nt_header() + + try: + section = next( + sec + for sec in nt_header.get_sections() + if section_name.lower() == array_to_string(sec.Name).lower() + ) + except StopIteration: + return None + + section_offset = krnl_mod.DllBase + section.VirtualAddress + section_size = section.Misc.VirtualSize + + return section_offset, section_size From 1508a414992ec958859ec5de84c3fbb51209d55f Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Thu, 18 Jul 2024 11:33:30 -0500 Subject: [PATCH 22/32] Move registry table init into the generator function --- .../framework/plugins/windows/registry/userassist.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index cf345c901..bd832b20c 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -286,6 +286,10 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac hive_offsets = [self.config.get("offset", None)] kernel = self.context.modules[self.config["kernel"]] + self._reg_table_name = intermed.IntermediateSymbolTable.create( + self.context, self._config_path, "windows", "registry" + ) + # get all the user hive offsets or use the one specified for hive in hivelist.HiveList.list_hives( context=self.context, @@ -337,10 +341,6 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac yield result def generate_timeline(self): - self._reg_table_name = intermed.IntermediateSymbolTable.create( - self.context, self._config_path, "windows", "registry" - ) - for row in self._generator(): _depth, row_data = row # check the name and the timestamp to not be empty @@ -351,10 +351,6 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac yield (description, timeliner.TimeLinerType.MODIFIED, row_data[10]) def run(self): - self._reg_table_name = intermed.IntermediateSymbolTable.create( - self.context, self._config_path, "windows", "registry" - ) - return renderers.TreeGrid( [ ("Hive Offset", renderers.format_hints.Hex), From 8da046530fd7ccd3f8a75fbe4e0b63d213207117 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 14:57:31 -0500 Subject: [PATCH 23/32] Address feedback --- .../framework/plugins/windows/svcdiff.py | 22 ++++--- .../framework/plugins/windows/svclist.py | 58 +++++++++++-------- .../framework/plugins/windows/svcscan.py | 43 ++++++++------ 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py index 809064946..c41a7b86c 100644 --- a/volatility3/framework/plugins/windows/svcdiff.py +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -20,8 +20,7 @@ from volatility3.framework.symbols.windows import versions vollog = logging.getLogger(__name__) - -class SvcDiff(svclist.SvcList, svcscan.SvcScan): +class SvcDiff(svcscan.SvcScan): """Compares services found through list walking versus scanning to find rootkits""" _required_framework_version = (2, 4, 0) @@ -39,22 +38,23 @@ class SvcDiff(svclist.SvcList, svcscan.SvcScan): name="svclist", component=svclist.SvcList, version=(1, 0, 0) ), requirements.VersionRequirement( - name="svcscan", component=svcscan.SvcScan, version=(2, 0, 0) + name="svcscan", component=svcscan.SvcScan, version=(3, 0, 0) ), ] def _generator(self): """ - Finds services by walking the services.exe list on supported Windows 10 versions + On Windows 10 version 15063+ 64bit Windows memory samples, walk the services list + and scan for services then report differences """ - kernel = self.context.modules[self.config["kernel"]] + kernel, service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() if not symbols.symbol_table_is_64bit( self.context, kernel.symbol_table_name ) or not versions.is_win10_15063_or_later( context=self.context, symbol_table=kernel.symbol_table_name ): - vollog.info( + vollog.warning( "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" ) return @@ -63,18 +63,16 @@ class SvcDiff(svclist.SvcList, svcscan.SvcScan): from_list = set() records = {} - service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - # collect unique service names from scanning - for service in self.service_scan( - service_table_name, service_binary_dll_map, filter_func + for service in svcscan.SvcScan.service_scan( + self.context, kernel, service_table_name, service_binary_dll_map, filter_func ): from_scan.add(service[6]) records[service[6]] = service # collect services from listing walking - for service in self.service_list( - service_table_name, service_binary_dll_map, filter_func + for service in svclist.SvcList.service_list( + self.context, kernel, service_table_name, service_binary_dll_map, filter_func ): from_list.add(service[6]) diff --git a/volatility3/framework/plugins/windows/svclist.py b/volatility3/framework/plugins/windows/svclist.py index 1938dd182..832b3d129 100644 --- a/volatility3/framework/plugins/windows/svclist.py +++ b/volatility3/framework/plugins/windows/svclist.py @@ -4,7 +4,7 @@ import logging -from typing import List +from typing import List, Optional, Tuple from volatility3.framework import interfaces, exceptions, symbols from volatility3.framework.configuration import requirements @@ -20,16 +20,26 @@ class SvcList(svcscan.SvcScan): _version = (1, 0, 0) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._enumeration_method = self.service_list + @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: # Since we're calling the plugin, make sure we have the plugin's requirements return [ requirements.PluginRequirement( - name="svcscan", plugin=svcscan.SvcScan, version=(2, 0, 0) + name="svcscan", plugin=svcscan.SvcScan, version=(3, 0, 0) + ), + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], ), ] - def _get_exe_range(self, proc): + @classmethod + def _get_exe_range(cls, proc) -> Optional[Tuple[int, int]]: """ Returns a tuple of starting,ending address for the VAD containing services.exe @@ -45,21 +55,27 @@ class SvcList(svcscan.SvcScan): return None - def service_list(self, service_table_name, service_binary_dll_map, filter_func): - kernel = self.context.modules[self.config["kernel"]] - + @classmethod + def service_list( + cls, + context: interfaces.context.ContextInterface, + kernel, + service_table_name: str, + service_binary_dll_map, + filter_func, + ): if not symbols.symbol_table_is_64bit( - self.context, kernel.symbol_table_name + context, kernel.symbol_table_name ) or not versions.is_win10_15063_or_later( - context=self.context, symbol_table=kernel.symbol_table_name + context=context, symbol_table=kernel.symbol_table_name ): - vollog.info( + vollog.warning( "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" ) return for proc in pslist.PsList.list_processes( - context=self.context, + context=context, layer_name=kernel.layer_name, symbol_table=kernel.symbol_table_name, filter_func=filter_func, @@ -74,9 +90,9 @@ class SvcList(svcscan.SvcScan): ) continue - layer = self.context.layers[layer_name] + layer = context.layers[layer_name] - exe_range = self._get_exe_range(proc) + exe_range = cls._get_exe_range(proc) if not exe_range: vollog.warning( "Could not find the application executable VAD for services.exe. Unable to proceed." @@ -84,19 +100,15 @@ class SvcList(svcscan.SvcScan): continue for offset in layer.scan( - context=self.context, + context=context, scanner=scanners.BytesScanner(needle=b"Sc27"), sections=exe_range, ): - for record in self.enumerate_vista_or_later_header( - service_table_name, service_binary_dll_map, layer_name, offset + for record in cls.enumerate_vista_or_later_header( + context, + service_table_name, + service_binary_dll_map, + layer_name, + offset, ): yield record - - def _generator(self): - service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - - for record in self.service_list( - service_table_name, service_binary_dll_map, filter_func - ): - yield (0, record) diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 07274f03d..4368b83ce 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -39,7 +39,11 @@ class SvcScan(interfaces.plugins.PluginInterface): """Scans for windows services.""" _required_framework_version = (2, 0, 0) - _version = (2, 0, 0) + _version = (3, 0, 0) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._enumeration_method = self.service_scan @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -232,13 +236,14 @@ class SvcScan(interfaces.plugins.PluginInterface): for service_key in services } + @classmethod def enumerate_vista_or_later_header( - self, service_table_name, service_binary_dll_map, proc_layer_name, offset + cls, context, service_table_name, service_binary_dll_map, proc_layer_name, offset ): if offset % 8: return - service_header = self.context.object( + service_header =context.object( service_table_name + constants.BANG + "_SERVICE_HEADER", offset=offset, layer_name=proc_layer_name, @@ -257,17 +262,16 @@ class SvcScan(interfaces.plugins.PluginInterface): renderers.UnreadableValue(), renderers.UnreadableValue() ), ) - yield self.get_record_tuple(service_record, service_info) + yield cls.get_record_tuple(service_record, service_info) - def service_scan(self, service_table_name, service_binary_dll_map, filter_func): - kernel = self.context.modules[self.config["kernel"]] - - relative_tag_offset = self.context.symbol_space.get_type( + @classmethod + def service_scan(cls, context: interfaces.context.ContextInterface, kernel, service_table_name: str, service_binary_dll_map, filter_func): + relative_tag_offset = context.symbol_space.get_type( service_table_name + constants.BANG + "_SERVICE_RECORD" ).relative_child_offset("Tag") is_vista_or_later = versions.is_vista_or_later( - context=self.context, symbol_table=kernel.symbol_table_name + context=context, symbol_table=kernel.symbol_table_name ) if is_vista_or_later: @@ -278,7 +282,7 @@ class SvcScan(interfaces.plugins.PluginInterface): seen = [] for task in pslist.PsList.list_processes( - context=self.context, + context=context, layer_name=kernel.layer_name, symbol_table=kernel.symbol_table_name, filter_func=filter_func, @@ -295,15 +299,15 @@ class SvcScan(interfaces.plugins.PluginInterface): ) continue - layer = self.context.layers[proc_layer_name] + layer = context.layers[proc_layer_name] for offset in layer.scan( - context=self.context, + context=context, scanner=scanners.BytesScanner(needle=service_tag), sections=vadyarascan.VadYaraScan.get_vad_maps(task), ): if not is_vista_or_later: - service_record = self.context.object( + service_record = context.object( service_table_name + constants.BANG + "_SERVICE_RECORD", offset=offset - relative_tag_offset, layer_name=proc_layer_name, @@ -318,9 +322,10 @@ class SvcScan(interfaces.plugins.PluginInterface): renderers.UnreadableValue(), renderers.UnreadableValue() ), ) - yield self.get_record_tuple(service_record, service_info) + yield cls.get_record_tuple(service_record, service_info) else: - for service_record in self.enumerate_vista_or_later_header( + for service_record in cls.enumerate_vista_or_later_header( + context, service_table_name, service_binary_dll_map, proc_layer_name, @@ -351,13 +356,13 @@ class SvcScan(interfaces.plugins.PluginInterface): filter_func = pslist.PsList.create_name_filter(["services.exe"]) - return service_table_name, service_binary_dll_map, filter_func + return kernel, service_table_name, service_binary_dll_map, filter_func def _generator(self): - service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + kernel, service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() - for record in self.service_scan( - service_table_name, service_binary_dll_map, filter_func + for record in self._enumeration_method( + self.context, kernel, service_table_name, service_binary_dll_map, filter_func ): yield (0, record) From a7af052509417c08eae702cd59fbfaa5306cc686 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 14:59:40 -0500 Subject: [PATCH 24/32] Black fixes --- .../framework/plugins/windows/svcdiff.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py index c41a7b86c..4325771db 100644 --- a/volatility3/framework/plugins/windows/svcdiff.py +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -20,6 +20,7 @@ from volatility3.framework.symbols.windows import versions vollog = logging.getLogger(__name__) + class SvcDiff(svcscan.SvcScan): """Compares services found through list walking versus scanning to find rootkits""" @@ -47,7 +48,9 @@ class SvcDiff(svcscan.SvcScan): On Windows 10 version 15063+ 64bit Windows memory samples, walk the services list and scan for services then report differences """ - kernel, service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + kernel, service_table_name, service_binary_dll_map, filter_func = ( + self.get_prereq_info() + ) if not symbols.symbol_table_is_64bit( self.context, kernel.symbol_table_name @@ -65,14 +68,22 @@ class SvcDiff(svcscan.SvcScan): # collect unique service names from scanning for service in svcscan.SvcScan.service_scan( - self.context, kernel, service_table_name, service_binary_dll_map, filter_func + self.context, + kernel, + service_table_name, + service_binary_dll_map, + filter_func, ): from_scan.add(service[6]) records[service[6]] = service # collect services from listing walking for service in svclist.SvcList.service_list( - self.context, kernel, service_table_name, service_binary_dll_map, filter_func + self.context, + kernel, + service_table_name, + service_binary_dll_map, + filter_func, ): from_list.add(service[6]) From 2a5e94a0946e4c74f92588df6ceafdd89acd0be4 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 15:01:10 -0500 Subject: [PATCH 25/32] Black fixes --- .../framework/plugins/windows/svcscan.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 4368b83ce..8b9706625 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -238,12 +238,17 @@ class SvcScan(interfaces.plugins.PluginInterface): @classmethod def enumerate_vista_or_later_header( - cls, context, service_table_name, service_binary_dll_map, proc_layer_name, offset + cls, + context, + service_table_name, + service_binary_dll_map, + proc_layer_name, + offset, ): if offset % 8: return - service_header =context.object( + service_header = context.object( service_table_name + constants.BANG + "_SERVICE_HEADER", offset=offset, layer_name=proc_layer_name, @@ -265,7 +270,14 @@ class SvcScan(interfaces.plugins.PluginInterface): yield cls.get_record_tuple(service_record, service_info) @classmethod - def service_scan(cls, context: interfaces.context.ContextInterface, kernel, service_table_name: str, service_binary_dll_map, filter_func): + def service_scan( + cls, + context: interfaces.context.ContextInterface, + kernel, + service_table_name: str, + service_binary_dll_map, + filter_func, + ): relative_tag_offset = context.symbol_space.get_type( service_table_name + constants.BANG + "_SERVICE_RECORD" ).relative_child_offset("Tag") @@ -359,10 +371,16 @@ class SvcScan(interfaces.plugins.PluginInterface): return kernel, service_table_name, service_binary_dll_map, filter_func def _generator(self): - kernel, service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info() + kernel, service_table_name, service_binary_dll_map, filter_func = ( + self.get_prereq_info() + ) for record in self._enumeration_method( - self.context, kernel, service_table_name, service_binary_dll_map, filter_func + self.context, + kernel, + service_table_name, + service_binary_dll_map, + filter_func, ): yield (0, record) From b0a89f210977663c8d106c50ac4602eb48b7b211 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 15:47:37 -0500 Subject: [PATCH 26/32] Address feedback --- volatility3/framework/plugins/windows/processghosting.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/processghosting.py b/volatility3/framework/plugins/windows/processghosting.py index b29ff04f0..dda0d7675 100644 --- a/volatility3/framework/plugins/windows/processghosting.py +++ b/volatility3/framework/plugins/windows/processghosting.py @@ -2,6 +2,7 @@ # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # import logging +import contextlib from volatility3.framework import interfaces, exceptions from volatility3.framework import renderers @@ -57,14 +58,15 @@ class ProcessGhosting(interfaces.plugins.PluginInterface): else: file_object = 0 + if isinstance(delete_pending, int) and delete_pending not in [0, 1]: + vollog.debug(f"Invalid delete_pending value {delete_pending} found for {process_name} {proc.UniqueProcessId}") + # delete_pending besides 0 or 1 = smear if file_object == 0 or delete_pending == 1: path = renderers.UnreadableValue() if file_object: - try: + with contextlib.suppress(exceptions.InvalidAddressException): path = file_object.FileName.String - except exceptions.InvalidAddressException: - path = renderers.UnreadableValue() yield ( 0, From 920b3ec615b91ec4dcc4bdf0ef9d15715a5b1c8d Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 15:48:21 -0500 Subject: [PATCH 27/32] Black fix --- volatility3/framework/plugins/windows/processghosting.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/processghosting.py b/volatility3/framework/plugins/windows/processghosting.py index dda0d7675..50f02c926 100644 --- a/volatility3/framework/plugins/windows/processghosting.py +++ b/volatility3/framework/plugins/windows/processghosting.py @@ -59,7 +59,9 @@ class ProcessGhosting(interfaces.plugins.PluginInterface): file_object = 0 if isinstance(delete_pending, int) and delete_pending not in [0, 1]: - vollog.debug(f"Invalid delete_pending value {delete_pending} found for {process_name} {proc.UniqueProcessId}") + vollog.debug( + f"Invalid delete_pending value {delete_pending} found for {process_name} {proc.UniqueProcessId}" + ) # delete_pending besides 0 or 1 = smear if file_object == 0 or delete_pending == 1: From 75ccf1bfab5ee6bc3d0f84baf427c11b8e61137c Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 17:54:33 -0500 Subject: [PATCH 28/32] Add dedicated plugin and API for extracting PE files from kernel and process memory --- .../framework/plugins/windows/dlllist.py | 76 +----- .../framework/plugins/windows/modscan.py | 11 +- .../framework/plugins/windows/modules.py | 12 +- .../framework/plugins/windows/pedump.py | 239 ++++++++++++++++++ 4 files changed, 258 insertions(+), 80 deletions(-) create mode 100644 volatility3/framework/plugins/windows/pedump.py diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index d48a53663..eef826ed5 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -1,10 +1,9 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # import contextlib import datetime import logging -import ntpath import re from typing import List, Optional, Type @@ -14,7 +13,7 @@ from volatility3.framework.renderers import conversion, format_hints from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows.extensions import pe from volatility3.plugins import timeliner -from volatility3.plugins.windows import info, pslist, psscan +from volatility3.plugins.windows import info, pslist, psscan, pedump vollog = logging.getLogger(__name__) @@ -23,7 +22,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): """Lists the loaded modules in a particular windows memory image.""" _required_framework_version = (2, 0, 0) - _version = (2, 0, 1) + _version = (3, 0, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -76,66 +75,10 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): default=False, optional=True, ), - ] - - @classmethod - def dump_pe( - cls, - context: interfaces.context.ContextInterface, - pe_table_name: str, - dll_entry: interfaces.objects.ObjectInterface, - open_method: Type[interfaces.plugins.FileHandlerInterface], - layer_name: str = None, - prefix: str = "", - ) -> Optional[interfaces.plugins.FileHandlerInterface]: - """Extracts the complete data for a process as a FileInterface - - Args: - context: the context to operate upon - pe_table_name: the name for the symbol table containing the PE format symbols - dll_entry: the object representing the module - layer_name: the layer that the DLL lives within - open_method: class for constructing output files - - Returns: - An open FileHandlerInterface object containing the complete data for the DLL or None in the case of failure - """ - try: - try: - name = dll_entry.FullDllName.get_string() - except exceptions.InvalidAddressException: - name = "UnreadableDLLName" - - if layer_name is None: - layer_name = dll_entry.vol.layer_name - - file_handle = open_method( - "{}{}.{:#x}.{:#x}.dmp".format( - prefix, - ntpath.basename(name), - dll_entry.vol.offset, - dll_entry.DllBase, - ) - ) - - dos_header = context.object( - pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", - offset=dll_entry.DllBase, - layer_name=layer_name, - ) - - for offset, data in dos_header.reconstruct(): - file_handle.seek(offset) - file_handle.write(data) - except ( - IOError, - exceptions.VolatilityException, - OverflowError, - ValueError, - ) as excp: - vollog.debug(f"Unable to dump dll at offset {dll_entry.DllBase}: {excp}") - return None - return file_handle + requirements.VersionRequirement( + name="pedump", component=pedump.PEDump, version=(1, 0, 0) + ), + ] def _generator(self, procs): pe_table_name = intermed.IntermediateSymbolTable.create( @@ -204,7 +147,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): file_output = "Disabled" if self.config["dump"]: - file_handle = self.dump_pe( + file_handle = pedump.PEDump.dump_ldr_entry( self.context, pe_table_name, entry, @@ -214,8 +157,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): ) file_output = "Error outputting file" if file_handle: - file_handle.close() - file_output = file_handle.preferred_filename + file_output = file_handle try: dllbase = format_hints.Hex(entry.DllBase) except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/modscan.py b/volatility3/framework/plugins/windows/modscan.py index 98546bc9c..fc45e6913 100644 --- a/volatility3/framework/plugins/windows/modscan.py +++ b/volatility3/framework/plugins/windows/modscan.py @@ -6,7 +6,7 @@ from typing import Iterable from volatility3.framework import interfaces from volatility3.framework.configuration import requirements -from volatility3.plugins.windows import poolscanner, dlllist, pslist, modules +from volatility3.plugins.windows import poolscanner, modules, pedump vollog = logging.getLogger(__name__) @@ -35,12 +35,6 @@ class ModScan(modules.Modules): requirements.VersionRequirement( name="modules", component=modules.Modules, version=(2, 0, 0) ), - requirements.VersionRequirement( - name="pslist", component=pslist.PsList, version=(2, 0, 0) - ), - requirements.VersionRequirement( - name="dlllist", component=dlllist.DllList, version=(2, 0, 0) - ), requirements.BooleanRequirement( name="dump", description="Extract listed modules", @@ -58,6 +52,9 @@ class ModScan(modules.Modules): optional=True, default=None, ), + requirements.VersionRequirement( + name="pedump", component=pedump.PEDump, version=(1, 0, 0) + ), ] @classmethod diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index 79eea1cd7..283d4dcb9 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -9,7 +9,7 @@ from volatility3.framework.configuration import requirements from volatility3.framework.renderers import format_hints from volatility3.framework.symbols import intermed from volatility3.framework.symbols.windows.extensions import pe -from volatility3.plugins.windows import pslist, dlllist +from volatility3.plugins.windows import pslist, pedump vollog = logging.getLogger(__name__) @@ -35,9 +35,6 @@ class Modules(interfaces.plugins.PluginInterface): requirements.VersionRequirement( name="pslist", component=pslist.PsList, version=(2, 0, 0) ), - requirements.VersionRequirement( - name="dlllist", component=dlllist.DllList, version=(2, 0, 0) - ), requirements.BooleanRequirement( name="dump", description="Extract listed modules", @@ -55,6 +52,9 @@ class Modules(interfaces.plugins.PluginInterface): optional=True, default=None, ), + requirements.VersionRequirement( + name="pedump", component=pedump.PEDump, version=(1, 0, 0) + ), ] def dump_module(self, session_layers, pe_table_name, mod): @@ -63,7 +63,7 @@ class Modules(interfaces.plugins.PluginInterface): ) file_output = f"Cannot find a viable session layer for {mod.DllBase:#x}" if session_layer_name: - file_handle = dlllist.DllList.dump_pe( + file_handle = pedump.PEDump.dump_ldr_entry( self.context, pe_table_name, mod, @@ -72,7 +72,7 @@ class Modules(interfaces.plugins.PluginInterface): ) file_output = "Error outputting file" if file_handle: - file_output = file_handle.preferred_filename + file_output = file_handle return file_output diff --git a/volatility3/framework/plugins/windows/pedump.py b/volatility3/framework/plugins/windows/pedump.py new file mode 100644 index 000000000..395d28ce5 --- /dev/null +++ b/volatility3/framework/plugins/windows/pedump.py @@ -0,0 +1,239 @@ +# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +import logging +import ntpath +from typing import List, Type, Optional + +from volatility3.framework import constants, exceptions, interfaces, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows.extensions import pe +from volatility3.plugins.windows import pslist, modules + +vollog = logging.getLogger(__name__) + + +class PEDump(interfaces.plugins.PluginInterface): + """Allows extracting PE Files from a specific address in a specific address space""" + + _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="pslist", component=pslist.PsList, version=(2, 0, 0) + ), + requirements.ListRequirement( + name="pid", + element_type=int, + description="Process IDs to include (all other processes are excluded)", + optional=True, + ), + requirements.IntRequirement( + name="base", + description="Base address to reconstruct a PE file", + optional=False, + ), + requirements.BooleanRequirement( + name="kernel_module", + description="Extract from kernel address space.", + default=False, + optional=True, + ), + ] + + @classmethod + def dump_pe( + cls, + context: interfaces.context.ContextInterface, + pe_table_name: str, + layer_name: str, + open_method: Type[interfaces.plugins.FileHandlerInterface], + file_name: str, + base: int + ) -> Optional[str]: + """ + Returns the filename of the dump file or None + """ + try: + file_handle = open_method(file_name) + + dos_header = context.object( + pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", + offset=base, + layer_name=layer_name, + ) + + for offset, data in dos_header.reconstruct(): + file_handle.seek(offset) + file_handle.write(data) + except ( + IOError, + exceptions.VolatilityException, + OverflowError, + ValueError, + ) as excp: + vollog.debug(f"Unable to dump PE file at offset {base}: {excp}") + return None + finally: + file_handle.close() + + return file_handle.preferred_filename + + @classmethod + def dump_ldr_entry( + cls, + context: interfaces.context.ContextInterface, + pe_table_name: str, + ldr_entry: interfaces.objects.ObjectInterface, + open_method: Type[interfaces.plugins.FileHandlerInterface], + layer_name: str = None, + prefix: str = "", + ) -> Optional[str]: + """Extracts the PE file referenced an LDR_DATA_TABLE_ENTRY (DLL, kernel module) instance + + Args: + context: the context to operate upon + pe_table_name: the name for the symbol table containing the PE format symbols + ldr_entry: the object representing the module + open_method: class for constructing output files + layer_name: the layer that the DLL lives within + prefix: optional string to prepend to filename + Returns: + The output file name or None in the case of failure + """ + try: + name = ldr_entry.FullDllName.get_string() + except exceptions.InvalidAddressException: + name = "UnreadableDLLName" + + if layer_name is None: + layer_name = ldr_entry.vol.layer_name + + file_name = "{}{}.{:#x}.{:#x}.dmp".format( + prefix, + ntpath.basename(name), + ldr_entry.vol.offset, + ldr_entry.DllBase, + ) + + return PEDump.dump_pe(context, pe_table_name, layer_name, open_method, file_name, ldr_entry.DllBase) + + @classmethod + def dump_pe_at_base( + cls, + context: interfaces.context.ContextInterface, + pe_table_name: str, + layer_name: str, + open_method: Type[interfaces.plugins.FileHandlerInterface], + proc_offset: int, + pid: int, + base: int, + ) -> Optional[str]: + file_name = "PE.{:#x}.{:d}.{:#x}.dmp".format( + proc_offset, + pid, + base, + ) + + return PEDump.dump_pe(context, pe_table_name, layer_name, open_method, file_name, base) + + @classmethod + def dump_kernel_pe_at_base(cls, context, kernel, pe_table_name, open_method, base): + session_layers = modules.Modules.get_session_layers( + context, kernel.layer_name, kernel.symbol_table_name + ) + + session_layer_name = modules.Modules.find_session_layer( + context, session_layers, base + ) + + if session_layer_name: + system_pid = 4 + + file_output = PEDump.dump_pe_at_base( + context, pe_table_name, session_layer_name, open_method, 0, system_pid, base + ) + + if file_output: + yield system_pid, "Kernel", file_output + else: + vollog.warning( + "Unable to find a session layer with the provided base address mapped in the kernel." + ) + + @classmethod + def dump_processes(cls, context, kernel, pe_table_name, open_method, filter_func, base): + """ + """ + + for proc in pslist.PsList.list_processes( + context=context, + layer_name=kernel.layer_name, + symbol_table=kernel.symbol_table_name, + filter_func=filter_func, + ): + pid = proc.UniqueProcessId + proc_name = proc.ImageFileName.cast( + "string", + max_length=proc.ImageFileName.vol.count, + errors="replace", + ) + proc_layer_name = proc.add_process_layer() + + file_output = PEDump.dump_pe_at_base( + context, pe_table_name, proc_layer_name, open_method, proc.vol.offset, pid, base + ) + + if file_output: + yield pid, proc_name, file_output + + def _generator(self): + kernel = self.context.modules[self.config["kernel"]] + + pe_table_name = intermed.IntermediateSymbolTable.create( + self.context, self.config_path, "windows", "pe", class_types=pe.class_types + ) + + if self.config["kernel_module"] and self.config["pid"]: + vollog.error("Only --kernel_module or --pid should be set. Not both") + return + + if not self.config["kernel_module"] and not self.config["pid"]: + vollog.error("--kernel_module or --pid must be set") + return + + if self.config["kernel_module"]: + pe_files = self.dump_kernel_pe_at_base(self.context, kernel, pe_table_name, self.open, self.config["base"]) + else: + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + pe_files = self.dump_processes(self.context, kernel, pe_table_name, self.open, filter_func, self.config["base"]) + + for pid, proc_name, file_output in pe_files: + yield ( + 0, + ( + pid, + proc_name, + file_output, + ), + ) + + def run(self): + return renderers.TreeGrid( + [ + ("PID", int), + ("Process", str), + ("File output", str), + ], + self._generator(), + ) From 12f3beacfcb3ab16f8418cee2821ad74c3738045 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 18 Jul 2024 17:56:24 -0500 Subject: [PATCH 29/32] Add dedicated plugin and API for extracting PE files from kernel and process memory --- .../framework/plugins/windows/dlllist.py | 2 +- .../framework/plugins/windows/pedump.py | 69 ++++++++++++++----- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index eef826ed5..6c8c96dc3 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -78,7 +78,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): requirements.VersionRequirement( name="pedump", component=pedump.PEDump, version=(1, 0, 0) ), - ] + ] def _generator(self, procs): pe_table_name = intermed.IntermediateSymbolTable.create( diff --git a/volatility3/framework/plugins/windows/pedump.py b/volatility3/framework/plugins/windows/pedump.py index 395d28ce5..0b4649abb 100644 --- a/volatility3/framework/plugins/windows/pedump.py +++ b/volatility3/framework/plugins/windows/pedump.py @@ -59,7 +59,7 @@ class PEDump(interfaces.plugins.PluginInterface): layer_name: str, open_method: Type[interfaces.plugins.FileHandlerInterface], file_name: str, - base: int + base: int, ) -> Optional[str]: """ Returns the filename of the dump file or None @@ -120,13 +120,20 @@ class PEDump(interfaces.plugins.PluginInterface): layer_name = ldr_entry.vol.layer_name file_name = "{}{}.{:#x}.{:#x}.dmp".format( - prefix, - ntpath.basename(name), - ldr_entry.vol.offset, - ldr_entry.DllBase, - ) + prefix, + ntpath.basename(name), + ldr_entry.vol.offset, + ldr_entry.DllBase, + ) - return PEDump.dump_pe(context, pe_table_name, layer_name, open_method, file_name, ldr_entry.DllBase) + return PEDump.dump_pe( + context, + pe_table_name, + layer_name, + open_method, + file_name, + ldr_entry.DllBase, + ) @classmethod def dump_pe_at_base( @@ -140,12 +147,14 @@ class PEDump(interfaces.plugins.PluginInterface): base: int, ) -> Optional[str]: file_name = "PE.{:#x}.{:d}.{:#x}.dmp".format( - proc_offset, - pid, - base, - ) + proc_offset, + pid, + base, + ) - return PEDump.dump_pe(context, pe_table_name, layer_name, open_method, file_name, base) + return PEDump.dump_pe( + context, pe_table_name, layer_name, open_method, file_name, base + ) @classmethod def dump_kernel_pe_at_base(cls, context, kernel, pe_table_name, open_method, base): @@ -161,7 +170,13 @@ class PEDump(interfaces.plugins.PluginInterface): system_pid = 4 file_output = PEDump.dump_pe_at_base( - context, pe_table_name, session_layer_name, open_method, 0, system_pid, base + context, + pe_table_name, + session_layer_name, + open_method, + 0, + system_pid, + base, ) if file_output: @@ -172,9 +187,10 @@ class PEDump(interfaces.plugins.PluginInterface): ) @classmethod - def dump_processes(cls, context, kernel, pe_table_name, open_method, filter_func, base): - """ - """ + def dump_processes( + cls, context, kernel, pe_table_name, open_method, filter_func, base + ): + """ """ for proc in pslist.PsList.list_processes( context=context, @@ -191,7 +207,13 @@ class PEDump(interfaces.plugins.PluginInterface): proc_layer_name = proc.add_process_layer() file_output = PEDump.dump_pe_at_base( - context, pe_table_name, proc_layer_name, open_method, proc.vol.offset, pid, base + context, + pe_table_name, + proc_layer_name, + open_method, + proc.vol.offset, + pid, + base, ) if file_output: @@ -213,10 +235,19 @@ class PEDump(interfaces.plugins.PluginInterface): return if self.config["kernel_module"]: - pe_files = self.dump_kernel_pe_at_base(self.context, kernel, pe_table_name, self.open, self.config["base"]) + pe_files = self.dump_kernel_pe_at_base( + self.context, kernel, pe_table_name, self.open, self.config["base"] + ) else: filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - pe_files = self.dump_processes(self.context, kernel, pe_table_name, self.open, filter_func, self.config["base"]) + pe_files = self.dump_processes( + self.context, + kernel, + pe_table_name, + self.open, + filter_func, + self.config["base"], + ) for pid, proc_name, file_output in pe_files: yield ( From 9454181b9892f5c4245435bb868f24aaee23ae01 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Sun, 21 Jul 2024 09:42:19 -0500 Subject: [PATCH 30/32] Address feedback --- volatility3/framework/plugins/windows/dlllist.py | 9 +++++---- volatility3/framework/plugins/windows/modules.py | 7 +++---- volatility3/framework/plugins/windows/pedump.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index 6c8c96dc3..5a1b37fcf 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -147,7 +147,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): file_output = "Disabled" if self.config["dump"]: - file_handle = pedump.PEDump.dump_ldr_entry( + file_output = pedump.PEDump.dump_ldr_entry( self.context, pe_table_name, entry, @@ -155,9 +155,10 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): proc_layer_name, prefix=f"pid.{proc_id}.", ) - file_output = "Error outputting file" - if file_handle: - file_output = file_handle + + if not file_output: + file_output = "Error outputting file" + try: dllbase = format_hints.Hex(entry.DllBase) except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index 283d4dcb9..ba45834d5 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -63,16 +63,15 @@ class Modules(interfaces.plugins.PluginInterface): ) file_output = f"Cannot find a viable session layer for {mod.DllBase:#x}" if session_layer_name: - file_handle = pedump.PEDump.dump_ldr_entry( + file_output = pedump.PEDump.dump_ldr_entry( self.context, pe_table_name, mod, self.open, layer_name=session_layer_name, ) - file_output = "Error outputting file" - if file_handle: - file_output = file_handle + if not file_output: + file_output = "Error outputting file" return file_output diff --git a/volatility3/framework/plugins/windows/pedump.py b/volatility3/framework/plugins/windows/pedump.py index 0b4649abb..858d0615a 100644 --- a/volatility3/framework/plugins/windows/pedump.py +++ b/volatility3/framework/plugins/windows/pedump.py @@ -126,7 +126,7 @@ class PEDump(interfaces.plugins.PluginInterface): ldr_entry.DllBase, ) - return PEDump.dump_pe( + return cls.dump_pe( context, pe_table_name, layer_name, From 21396185d0faaab15a217cff2072aa7de2652b67 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Sun, 21 Jul 2024 11:10:38 -0500 Subject: [PATCH 31/32] Address feedback --- .../framework/plugins/windows/svcdiff.py | 37 ++++++++++----- .../framework/plugins/windows/svclist.py | 11 +++-- .../framework/plugins/windows/svcscan.py | 47 +++++++++++-------- 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcdiff.py b/volatility3/framework/plugins/windows/svcdiff.py index 4325771db..84d06a695 100644 --- a/volatility3/framework/plugins/windows/svcdiff.py +++ b/volatility3/framework/plugins/windows/svcdiff.py @@ -13,7 +13,7 @@ import logging -from volatility3.framework import symbols +from volatility3.framework import symbols, interfaces from volatility3.framework.configuration import requirements from volatility3.plugins.windows import svclist, svcscan from volatility3.framework.symbols.windows import versions @@ -26,6 +26,10 @@ class SvcDiff(svcscan.SvcScan): _required_framework_version = (2, 4, 0) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._enumeration_method = self.service_diff + @classmethod def get_requirements(cls): # Since we're calling the plugin, make sure we have the plugin's requirements @@ -43,19 +47,24 @@ class SvcDiff(svcscan.SvcScan): ), ] - def _generator(self): + @classmethod + def service_diff( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + service_table_name: str, + service_binary_dll_map, + filter_func, + ): """ On Windows 10 version 15063+ 64bit Windows memory samples, walk the services list and scan for services then report differences """ - kernel, service_table_name, service_binary_dll_map, filter_func = ( - self.get_prereq_info() - ) - if not symbols.symbol_table_is_64bit( - self.context, kernel.symbol_table_name + context, symbol_table ) or not versions.is_win10_15063_or_later( - context=self.context, symbol_table=kernel.symbol_table_name + context=context, symbol_table=symbol_table ): vollog.warning( "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" @@ -68,8 +77,9 @@ class SvcDiff(svcscan.SvcScan): # collect unique service names from scanning for service in svcscan.SvcScan.service_scan( - self.context, - kernel, + context, + layer_name, + symbol_table, service_table_name, service_binary_dll_map, filter_func, @@ -79,8 +89,9 @@ class SvcDiff(svcscan.SvcScan): # collect services from listing walking for service in svclist.SvcList.service_list( - self.context, - kernel, + context, + layer_name, + symbol_table, service_table_name, service_binary_dll_map, filter_func, @@ -89,4 +100,4 @@ class SvcDiff(svcscan.SvcScan): # report services found from scanning but not list walking for hidden_service in from_scan - from_list: - yield (0, records[hidden_service]) + yield records[hidden_service] diff --git a/volatility3/framework/plugins/windows/svclist.py b/volatility3/framework/plugins/windows/svclist.py index 832b3d129..a59581063 100644 --- a/volatility3/framework/plugins/windows/svclist.py +++ b/volatility3/framework/plugins/windows/svclist.py @@ -59,15 +59,16 @@ class SvcList(svcscan.SvcScan): def service_list( cls, context: interfaces.context.ContextInterface, - kernel, + layer_name: str, + symbol_table: str, service_table_name: str, service_binary_dll_map, filter_func, ): if not symbols.symbol_table_is_64bit( - context, kernel.symbol_table_name + context, symbol_table ) or not versions.is_win10_15063_or_later( - context=context, symbol_table=kernel.symbol_table_name + context=context, symbol_table=symbol_table ): vollog.warning( "This plugin only supports Windows 10 version 15063+ 64bit Windows memory samples" @@ -76,8 +77,8 @@ class SvcList(svcscan.SvcScan): for proc in pslist.PsList.list_processes( context=context, - layer_name=kernel.layer_name, - symbol_table=kernel.symbol_table_name, + layer_name=layer_name, + symbol_table=symbol_table, filter_func=filter_func, ): try: diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 8b9706625..bf676acb7 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -148,14 +148,17 @@ class SvcScan(interfaces.plugins.PluginInterface): native_types=native_types, ) - def _get_service_key(self, kernel) -> Optional[objects.StructType]: + @classmethod + def _get_service_key( + cls, context, config_path: str, layer_name: str, symbol_table: str + ) -> Optional[objects.StructType]: for hive in hivelist.HiveList.list_hives( - context=self.context, + context=context, base_config_path=interfaces.configuration.path_join( - self.config_path, "hivelist" + config_path, "hivelist" ), - layer_name=kernel.layer_name, - symbol_table=kernel.symbol_table_name, + layer_name=layer_name, + symbol_table=symbol_table, filter_string="machine\\system", ): # Get ControlSet\Services. @@ -273,7 +276,8 @@ class SvcScan(interfaces.plugins.PluginInterface): def service_scan( cls, context: interfaces.context.ContextInterface, - kernel, + layer_name: str, + symbol_table: str, service_table_name: str, service_binary_dll_map, filter_func, @@ -283,7 +287,7 @@ class SvcScan(interfaces.plugins.PluginInterface): ).relative_child_offset("Tag") is_vista_or_later = versions.is_vista_or_later( - context=context, symbol_table=kernel.symbol_table_name + context=context, symbol_table=symbol_table ) if is_vista_or_later: @@ -295,8 +299,8 @@ class SvcScan(interfaces.plugins.PluginInterface): for task in pslist.PsList.list_processes( context=context, - layer_name=kernel.layer_name, - symbol_table=kernel.symbol_table_name, + layer_name=layer_name, + symbol_table=symbol_table, filter_func=filter_func, ): proc_id = "Unknown" @@ -348,36 +352,41 @@ class SvcScan(interfaces.plugins.PluginInterface): seen.append(service_record) yield service_record - def get_prereq_info(self): + @classmethod + def get_prereq_info(cls, context, config_path, layer_name: str, symbol_table: str): """ Data structures and information needed to analyze service information """ - kernel = self.context.modules[self.config["kernel"]] - service_table_name = self.create_service_table( - self.context, kernel.symbol_table_name, self.config_path + service_table_name = cls.create_service_table( + context, symbol_table, config_path ) - services_key = self._get_service_key(kernel) + services_key = cls._get_service_key( + context, config_path, layer_name, symbol_table + ) service_binary_dll_map = ( - self._get_service_binary_map(services_key) + cls._get_service_binary_map(services_key) if services_key is not None else {} ) filter_func = pslist.PsList.create_name_filter(["services.exe"]) - return kernel, service_table_name, service_binary_dll_map, filter_func + return service_table_name, service_binary_dll_map, filter_func def _generator(self): - kernel, service_table_name, service_binary_dll_map, filter_func = ( - self.get_prereq_info() + kernel = self.context.modules[self.config["kernel"]] + + service_table_name, service_binary_dll_map, filter_func = self.get_prereq_info( + self.context, self.config_path, kernel.layer_name, kernel.symbol_table_name ) for record in self._enumeration_method( self.context, - kernel, + kernel.layer_name, + kernel.symbol_table_name, service_table_name, service_binary_dll_map, filter_func, From 111873e173b1bc639754b851ce46ff5056f8edb8 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Sun, 21 Jul 2024 11:54:06 -0500 Subject: [PATCH 32/32] Fix class vs static method and leading underscores --- volatility3/framework/plugins/windows/svcscan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index bf676acb7..52ed5e759 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -110,7 +110,7 @@ class SvcScan(interfaces.plugins.PluginInterface): ] @staticmethod - def create_service_table( + def _create_service_table( context: interfaces.context.ContextInterface, symbol_table: str, config_path: str, @@ -148,9 +148,9 @@ class SvcScan(interfaces.plugins.PluginInterface): native_types=native_types, ) - @classmethod + @staticmethod def _get_service_key( - cls, context, config_path: str, layer_name: str, symbol_table: str + context, config_path: str, layer_name: str, symbol_table: str ) -> Optional[objects.StructType]: for hive in hivelist.HiveList.list_hives( context=context, @@ -358,7 +358,7 @@ class SvcScan(interfaces.plugins.PluginInterface): Data structures and information needed to analyze service information """ - service_table_name = cls.create_service_table( + service_table_name = cls._create_service_table( context, symbol_table, config_path )