From 0a5c9376e27722444913f621451ce216550a39cd Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Wed, 26 Feb 2025 16:41:40 -0600 Subject: [PATCH 1/9] #1476 - fix typo and missing exception --- volatility3/framework/layers/registry.py | 2 +- volatility3/framework/symbols/windows/extensions/registry.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/layers/registry.py b/volatility3/framework/layers/registry.py index 21e1a938e..abae23e2d 100644 --- a/volatility3/framework/layers/registry.py +++ b/volatility3/framework/layers/registry.py @@ -262,7 +262,7 @@ class RegistryHive(linear.LinearlyMappedLayer): self.name, hex(offset & 0x7FFFFFFF), hex(self._get_hive_maxaddr(volatile)), - "volative" if volatile else "non-volatile", + "volatile" if volatile else "non-volatile", self.get_name(), ), ) diff --git a/volatility3/framework/symbols/windows/extensions/registry.py b/volatility3/framework/symbols/windows/extensions/registry.py index a8cc7703c..c807c2cd6 100644 --- a/volatility3/framework/symbols/windows/extensions/registry.py +++ b/volatility3/framework/symbols/windows/extensions/registry.py @@ -199,7 +199,7 @@ class CM_KEY_NODE(objects.StructType): # We could change the array type to a struct with both parts try: signature = node.cast("string", max_length=2, encoding="latin-1") - except (exceptions.InvalidAddressException, RegistryFormatException): + except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): return None listjump = None @@ -329,7 +329,7 @@ class CM_KEY_VALUE(objects.StructType): data = layer.read(self.Data.vol.offset, datalen) elif layer.hive.Version == 5 and datalen > 0x4000: # We're bigdata - big_data = layer.get_node(self.Data) + big_data = layer.get_node(self.Data).cast("_CM_BIG_DATA") # Oddly, we get a list of addresses, at which are addresses, which then point to data blocks for i in range(big_data.Count): # The value 4 should actually be unsigned-int.size, but since it's a file format that shouldn't change From 8b302ff7fc568177d97e865a2031ac35ad10dab6 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 10 Mar 2025 13:39:00 -0500 Subject: [PATCH 2/9] #1476 - additional exception handling for registry plugins --- .../framework/plugins/windows/envars.py | 15 +- .../plugins/windows/getservicesids.py | 17 +- .../framework/plugins/windows/getsids.py | 11 +- .../framework/plugins/windows/hashdump.py | 30 +- .../framework/plugins/windows/lsadump.py | 23 +- .../framework/plugins/windows/prefetch.py | 478 ++++++++++++++++++ .../plugins/windows/registry/printkey.py | 16 +- .../plugins/windows/registry/userassist.py | 14 +- .../plugins/windows/scheduled_tasks.py | 30 +- .../symbols/windows/extensions/registry.py | 16 +- 10 files changed, 604 insertions(+), 46 deletions(-) create mode 100644 volatility3/framework/plugins/windows/prefetch.py diff --git a/volatility3/framework/plugins/windows/envars.py b/volatility3/framework/plugins/windows/envars.py index 61414778d..f1cca9b94 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -73,13 +73,13 @@ class Envars(interfaces.plugins.PluginInterface): sys = hive.get_key( "CurrentControlSet\\Control\\Session Manager\\Environment" ) - except (KeyError, registry.RegistryFormatException): - with contextlib.suppress(KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): sys = hive.get_key( "ControlSet001\\Control\\Session Manager\\Environment" ) if sys: - with contextlib.suppress(KeyError, registry.RegistryFormatException): + with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): for node in sys.get_values(): try: value_node_name = node.get_name() @@ -88,6 +88,7 @@ class Envars(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, registry.RegistryFormatException, + registry.RegistryInvalidIndex ): vollog.log( constants.LOGLEVEL_VVV, @@ -97,10 +98,10 @@ class Envars(interfaces.plugins.PluginInterface): ntuser = None ## The user-specific variables - with contextlib.suppress(KeyError, registry.RegistryFormatException): + with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): ntuser = hive.get_key("Environment") if ntuser: - with contextlib.suppress(KeyError, registry.RegistryFormatException): + with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): for node in ntuser.get_values(): try: value_node_name = node.get_name() @@ -109,6 +110,7 @@ class Envars(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, registry.RegistryFormatException, + registry.RegistryInvalidIndex, ): vollog.log( constants.LOGLEVEL_VVV, @@ -119,7 +121,7 @@ class Envars(interfaces.plugins.PluginInterface): ## The volatile user variables try: key = hive.get_key("Volatile Environment") - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): continue try: for node in key.get_values(): @@ -130,6 +132,7 @@ class Envars(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, registry.RegistryFormatException, + registry.RegistryInvalidIndex, ): vollog.log( constants.LOGLEVEL_VVV, diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index 207d0e2ad..4dbdf5106 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -91,6 +91,7 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): KeyError, exceptions.InvalidAddressException, registry.RegistryFormatException, + registry.RegistryInvalidIndex, ): try: services = hive.get_key(r"ControlSet001\Services") @@ -98,14 +99,24 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): KeyError, exceptions.InvalidAddressException, registry.RegistryFormatException, + registry.RegistryInvalidIndex, ): continue if services: for s in services.get_subkeys(): - if s.get_name() not in self.servicesids.values(): - sid = createservicesid(s.get_name()) - yield (0, (sid, s.get_name())) + try: + sid_name = s.get_name() + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): + continue + + if sid_name not in self.servicesids.values(): + sid = createservicesid(sid_name) + yield (0, (sid, sid_name)) def run(self): return renderers.TreeGrid([("SID", str), ("Service", str)], self._generator()) diff --git a/volatility3/framework/plugins/windows/getsids.py b/volatility3/framework/plugins/windows/getsids.py index a75bbe7ea..0c8374455 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -114,7 +114,15 @@ class GetSIDs(interfaces.plugins.PluginInterface): ): try: for subkey in hive.get_key(key).get_subkeys(): - sid = str(subkey.get_name()) + try: + sid = str(subkey.get_name()) + except ( + exceptions.InvalidAddressException, + layers.registry.RegistryFormatException, + layers.registry.RegistryInvalidIndex, + ): + continue + path = "" for node in subkey.get_values(): try: @@ -122,6 +130,7 @@ class GetSIDs(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, layers.registry.RegistryFormatException, + layers.registry.RegistryInvalidIndex, ): continue try: diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index 1fea3d49d..9f481781d 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -9,8 +9,9 @@ from typing import List, Optional, Tuple from Crypto.Cipher import AES, ARC4, DES -from volatility3.framework import interfaces, renderers +from volatility3.framework import interfaces, renderers, constants from volatility3.framework.configuration import requirements +from volatility3.framework.exceptions import InvalidAddressException from volatility3.framework.symbols.windows.extensions import registry from volatility3.plugins.windows.registry import hivelist @@ -356,21 +357,27 @@ class Hashdump(interfaces.plugins.PluginInterface): lsa_keys = ["JD", "Skew1", "GBG", "Data"] lsa = cls.get_hive_key(syshive, lsa_base) - if not lsa: return None bootkey = "" for lk in lsa_keys: - key = cls.get_hive_key(syshive, lsa_base + "\\" + lk) - class_data = None - if key: - class_data = syshive.read(key.Class + 4, key.ClassLength) + try: + key = cls.get_hive_key(syshive, lsa_base + "\\" + lk) + class_data = None + if key: + class_data = syshive.read(key.Class + 4, key.ClassLength) - if class_data is None: + if class_data is None: + return None + bootkey += class_data.decode("utf-16-le") + except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex) as excp: + vollog.log( + constants.LOGLEVEL_VVV, + f"Unable to read Lsa key {lk}: {excp}" + ) return None - bootkey += class_data.decode("utf-16-le") bootkey_str = binascii.unhexlify(bootkey) bootkey_scrambled = bytes( @@ -443,8 +450,11 @@ class Hashdump(interfaces.plugins.PluginInterface): return None sam_data = None for v in user.get_values(): - if v.get_name() == "V": - sam_data = samhive.read(v.Data + 4, v.DataLength) + try: + if v.get_name() == "V": + sam_data = samhive.read(v.Data + 4, v.DataLength) + except (InvalidAddressException, registry.RegistryInvalidIndex): + continue if not sam_data: return None diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index 50f4da30d..511ec0126 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -10,6 +10,8 @@ from Crypto.Cipher import ARC4, DES, AES from volatility3.framework import interfaces, renderers, exceptions from volatility3.framework.configuration import requirements +from volatility3.framework.exceptions import InvalidAddressException +from volatility3.framework.interfaces.layers import IteratorValue from volatility3.framework.layers import registry from volatility3.framework.symbols.windows import versions from volatility3.plugins.windows import hashdump @@ -119,7 +121,11 @@ class Lsadump(interfaces.plugins.PluginInterface): secret = None if enc_secret_key: - enc_secret_value = next(enc_secret_key.get_values()) + try: + enc_secret_value = next(enc_secret_key.get_values()) + except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + enc_secret_value = None + if enc_secret_value: enc_secret = sechive.read( enc_secret_value.Data + 4, enc_secret_value.DataLength @@ -168,11 +174,11 @@ class Lsadump(interfaces.plugins.PluginInterface): ) bootkey = hashdump.Hashdump.get_bootkey(syshive) - lsakey = self.get_lsa_key(sechive, bootkey, vista_or_later) if not bootkey: vollog.warning("Unable to find bootkey") return None + lsakey = self.get_lsa_key(sechive, bootkey, vista_or_later) if not lsakey: vollog.warning("Unable to find lsa key") return None @@ -190,7 +196,11 @@ class Lsadump(interfaces.plugins.PluginInterface): if not sec_val_key: continue - enc_secret_value = next(sec_val_key.get_values()) + try: + enc_secret_value = next(sec_val_key.get_values()) + except (StopIteration, InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + enc_secret_value = None + if not enc_secret_value: continue @@ -204,7 +214,12 @@ class Lsadump(interfaces.plugins.PluginInterface): else: secret = self.decrypt_aes(enc_secret, lsakey) - yield (0, (key.get_name(), secret.decode("latin1"), secret)) + try: + key_name = key.get_name() + except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + key_name = renderers.UnreadableValue() + + yield (0, (key_name, secret.decode("latin1"), secret)) def run(self): offset = self.config.get("offset", None) diff --git a/volatility3/framework/plugins/windows/prefetch.py b/volatility3/framework/plugins/windows/prefetch.py new file mode 100644 index 000000000..3997bc012 --- /dev/null +++ b/volatility3/framework/plugins/windows/prefetch.py @@ -0,0 +1,478 @@ +# References : +# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-XCA/%5bMS-XCA%5d.pdf +# https://github.com/libyal/libscca/blob/main/documentation/Windows%20Prefetch%20File%20(PF)%20format.asciidoc +# https://github.com/volatilityfoundation/volatility3/ +# https://github.com/EricZimmerman/Prefetch/tree/master/Prefetch +import logging, pathlib, datetime, io, struct +from volatility3.framework import renderers, interfaces, exceptions, constants +from volatility3.framework.configuration import requirements +from volatility3.plugins.windows import filescan +from volatility3.framework.renderers import format_hints, conversion + +vollog = logging.getLogger(__name__) +from typing import Tuple, List, Union + + +class BitStream: + def __init__(self, source: bytes, in_pos: int): + self.source = source + self.index = in_pos + 4 + # read UInt16 little endian + mask = struct.unpack_from(' int: + if n == 0: + return 0 + return self.mask >> (32 - n) + + def skip(self, n: int) -> Union[None, Exception]: + self.mask = ((self.mask << n) & 0xFFFFFFFF) + self.bits -= n + if self.bits < 16: + if self.index + 2 > len(self.source): + return Exception("EOF Error") + # read UInt16 little endian + self.mask += ((struct.unpack_from(' int: + node = treeNodes[0] + i = leafIndex + 1 + childIndex = None + + while bits > 1: + bits -= 1 + childIndex = (mask >> bits) & 1 + if node.child[childIndex] == None: + node.child[childIndex] = treeNodes[i] + treeNodes[i].leaf = False + i += 1 + node = node.child[childIndex] + + node.child[mask & 1] = treeNodes[leafIndex] + + return i + + +def prefix_code_tree_rebuild(input: bytes) -> PREFIX_CODE_NODE: + treeNodes = [PREFIX_CODE_NODE() for _ in range(1024)] + symbolInfo = [PREFIX_CODE_SYMBOL() for _ in range(512)] + + for i in range(256): + value = input[i] + + symbolInfo[2 * i].id = 2 * i + symbolInfo[2 * i].symbol = 2 * i + symbolInfo[2 * i].length = value & 0xf + + value >>= 4 + + symbolInfo[2 * i + 1].id = 2 * i + 1 + symbolInfo[2 * i + 1].symbol = 2 * i + 1 + symbolInfo[2 * i + 1].length = value & 0xf + + symbolInfo = sorted(symbolInfo, key=lambda x: (x.length, x.symbol)) + + i = 0 + while i < 512 and symbolInfo[i].length == 0: + i += 1 + + mask = 0 + bits = 1 + + root = treeNodes[0] + root.leaf = False + + j = 1 + while i < 512: + treeNodes[j].id = j + treeNodes[j].symbol = symbolInfo[i].symbol + treeNodes[j].leaf = True + mask = mask << (symbolInfo[i].length - bits) + bits = symbolInfo[i].length + j = prefix_code_tree_add_leaf(treeNodes, j, mask, bits) + mask += 1 + i += 1 + + return root + + +def prefix_code_tree_decode_symbol(bstr: BitStream, root: PREFIX_CODE_NODE) -> Tuple[int, Union[None, Exception]]: + node = root + i = 0 + while True: + bit = bstr.lookup(1) + err = bstr.skip(1) + if err is not None: + return 0, err + + node = node.child[bit] + if node == None: + return 0, Exception("Corruption detected") + + if node.leaf: + break + return node.symbol, None + + +def lz77_huffman_decompress_chunck(in_idx: int, + input: bytes, + out_idx: int, + output: bytearray, + chunk_size: int) -> Tuple[int, int, Union[None, Exception]]: + # Ensure there are at least 256 bytes available to read + if in_idx + 256 > len(input): + return 0, 0, Exception("EOF Error") + + root = prefix_code_tree_rebuild(input[in_idx:]) + # print_tree(root) + bstr = BitStream(input, in_idx + 256) + + i = out_idx + + while i < out_idx + chunk_size: + symbol, err = prefix_code_tree_decode_symbol(bstr, root) + + if err is not None: + return int(bstr.index), i, err + + if symbol < 256: + output[i] = symbol + i += 1 + else: + symbol -= 256 + length = symbol & 15 + symbol >>= 4 + + offset = 0 + if symbol != 0: + offset = int(bstr.lookup(symbol)) + + offset |= 1 << symbol + offset = -offset + + if length == 15: + length = bstr.source[bstr.index] + 15 + bstr.index += 1 + + if length == 270: + length = struct.unpack_from(' 0: + if i + offset < 0: + print(i + offset) + return int(bstr.index), i, Exception("Decompression Error") + + output[i] = output[i + offset] + i += 1 + length -= 1 + if length == 0: + break + return int(bstr.index), i, None + + +def lz77_huffman_decompress(input: bytes, output_size: int) -> Tuple[bytes, Union[None, Exception]]: + output = bytearray(output_size) + err = None + + # Index into the input buffer. + in_idx = 0 + + # Index into the output buffer. + out_idx = 0 + + while True: + # How much data belongs in the current chunk. Chunks + # are split into maximum 65536 bytes. + chunk_size = output_size - out_idx + if chunk_size > 65536: + chunk_size = 65536 + + in_idx, out_idx, err = lz77_huffman_decompress_chunck( + in_idx, input, out_idx, output, chunk_size) + if err is not None: + return output, err + if out_idx >= len(output) or in_idx >= len(input): + break + return output, None + + +class Prefetch(interfaces.plugins.PluginInterface): + """Get and parse the prefetch files""" + _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls): + return [requirements.ModuleRequirement(name='kernel', description='Windows kernel', + architectures=["Intel32", "Intel64"]), + requirements.PluginRequirement(name='filescan', plugin=filescan.FileScan, version=(0, 0, 0)), ] + + @classmethod + def version_17(cls, prefetch_file): + """Extract pf information for Version 17""" + stream = io.BytesIO(prefetch_file) + + stream.seek(0x000C) + file_size = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0010) + executable_raw = stream.read(60).decode('utf-16') + executable_name = executable_raw.split('\u0000')[0] + + stream.seek(0x004C) + prefetch_hash = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0078) + last_execution_filetime = int.from_bytes(stream.read(8), "little") + last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + + stream.seek(0x0090) + execution_counter = int.from_bytes(stream.read(4), "little") + + yield ( + executable_name, + file_size, + format_hints.Hex(prefetch_hash), + last_execution_filetime_human, + execution_counter + ) + + @classmethod + def version_23(cls, prefetch_file): + """Extract pf information for Version 23""" + stream = io.BytesIO(prefetch_file) + + stream.seek(0x000C) + file_size = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0010) + executable_raw = stream.read(60).decode('utf-16') + executable_name = executable_raw.split('\u0000')[0] + + stream.seek(0x004C) + prefetch_hash = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0080) + last_execution_filetime = int.from_bytes(stream.read(8), "little") + last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + + stream.seek(0x0098) + execution_counter = int.from_bytes(stream.read(4), "little") + + yield ( + executable_name, + file_size, + format_hints.Hex(prefetch_hash), + last_execution_filetime_human, + execution_counter + ) + + @classmethod + def version_26(cls, prefetch_file): + """Extract pf information for Version 26""" + stream = io.BytesIO(prefetch_file) + + stream.seek(0x000C) + file_size = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0010) + executable_raw = stream.read(60).decode('utf-16') + executable_name = executable_raw.split('\u0000')[0] + + stream.seek(0x004C) + prefetch_hash = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0080) + last_execution_filetime = int.from_bytes(stream.read(8), "little") + last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + + stream.seek(0x00D0) + execution_counter = int.from_bytes(stream.read(4), "little") + + yield ( + executable_name, + file_size, + format_hints.Hex(prefetch_hash), + last_execution_filetime_human, + execution_counter + ) + + @classmethod + def version_30(cls, prefetch_file): + """Extract pf information for Version 30""" + stream = io.BytesIO(prefetch_file) + + stream.seek(0x000C) + file_size = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0010) + executable_raw = stream.read(60).decode('utf-16') + executable_name = executable_raw.split('\u0000')[0] + + stream.seek(0x004C) + prefetch_hash = int.from_bytes(stream.read(4), "little") + + stream.seek(0x0080) + # The first FILETIME is the most recent run time + last_execution_filetime = int.from_bytes(stream.read(8), "little") + last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + + stream.seek(0x00C8) # Variant 1 + execution_counter = int.from_bytes(stream.read(4), "little") + if execution_counter == 0: + stream.seek(0x00D0) # Variant 2 + execution_counter = int.from_bytes(stream.read(4), "little") + + yield ( + executable_name, + file_size, + format_hints.Hex(prefetch_hash), + last_execution_filetime_human, + execution_counter + ) + + @classmethod + def parse_prefetch(cls, prefetch_file): + WinXpOrWin2K3 = 17 + VistaOrWin7 = 23 + Win8xOrWin2012x = 26 + Win10OrWin11 = 30 + stream = io.BytesIO(prefetch_file) + # First, we need to know if the prefetch is compressed (Win10/11) + signature = prefetch_file[:3].decode() + if signature == "MAM": + vollog.info("Windows 1X prefetch file detected.") + # The size of decompressed data is at offset 4 + stream.seek(0x0004) + decompressed_size = int.from_bytes(stream.read(4), "little") + vollog.info(f"decompressed size : {decompressed_size}") + stream.seek(0x0008) + compressed_bytes = stream.read() + prefetch_file = lz77_huffman_decompress(bytearray(compressed_bytes), decompressed_size)[0] + try: + file_version = int.from_bytes(prefetch_file[:4], "little") + signature = prefetch_file[4:8].decode() + vollog.info(f'File version : {file_version}') + vollog.info(f"Signature : {signature}") + except: + # We can not even read the header + pass + + if signature != "SCCA": + vollog.info("Wrong signature, should be SCCA") + return + if file_version == WinXpOrWin2K3: + for result in cls.version_17(prefetch_file): + yield result + elif file_version == VistaOrWin7: + for result in cls.version_23(prefetch_file): + yield result + elif file_version == Win8xOrWin2012x: + for result in cls.version_26(prefetch_file): + yield result + elif file_version == Win10OrWin11: + for result in cls.version_30(prefetch_file): + yield result + + def _generator(self, files): + kernel = self.context.modules[self.config['kernel']] + offsets = [] + for file_obj in files: + """Get the prefetch recovered files from the “filescan” plugin; """ + try: + file_name = file_obj.FileName.String + file_extension = pathlib.Path(file_name).suffix + if file_extension == ".pf": + """If found, try to dump the prefetch file (inspired from the "DumpFiles" plugin)""" + memory_objects = [] + memory_layer_name = self.context.layers[kernel.layer_name].config['memory_layer'] + memory_layer = self.context.layers[memory_layer_name] + primary_layer = self.context.layers[kernel.layer_name] + for member_name in ["DataSectionObject", "ImageSectionObject"]: + try: + section_obj = getattr(file_obj.SectionObjectPointer, member_name) + control_area = section_obj.dereference().cast("_CONTROL_AREA") + if control_area.is_valid(): + vollog.info(f"Found : {file_obj.FileName.String}") + memory_objects.append((control_area, memory_layer)) + except exceptions.InvalidAddressException: + vollog.log(constants.LOGLEVEL_VVV, + f"{member_name} is unavailable for file {file_obj.vol.offset:#x}") + try: + scm_pointer = file_obj.SectionObjectPointer.SharedCacheMap + shared_cache_map = scm_pointer.dereference().cast("_SHARED_CACHE_MAP") + if shared_cache_map.is_valid(): + memory_objects.append((shared_cache_map, primary_layer)) + except exceptions.InvalidAddressException: + vollog.log(constants.LOGLEVEL_VVV, + f"SharedCacheMap is unavailable for file {file_obj.vol.offset:#x}") + vollog.info(f"memory_objects : {memory_objects}") + + """Now, read and parse our PF to retrieve our artifacts""" + for memory_object, layer in memory_objects: + bytes_read = 0 + prefetch_raw = b'' + try: + for mem_offset, _, datasize in memory_object.get_available_pages(): + prefetch_raw += layer.read(mem_offset, datasize, pad=True) + bytes_read += len(prefetch_raw) + vollog.info(f"Read {bytes_read}") + if not bytes_read: + vollog.info(f"Prefetch is empty") + else: + """Prefetch parsing""" + for result in self.parse_prefetch(prefetch_raw): + yield 0, result + + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to dump file at {file_obj.vol.offset:#x}") + pass + except exceptions.InvalidAddressException: + continue + + def run(self): + kernel = self.context.modules[self.config['kernel']] + return renderers.TreeGrid([ + ("ExecutableName", str), + ("FileSize", int), + ("PrefetchHash", format_hints.Hex), + ("LastExecution", datetime.datetime), ("ExecutionCounter", int)], + self._generator(filescan.FileScan.scan_files(self.context, kernel.layer_name, kernel.symbol_table_name))) \ No newline at end of file diff --git a/volatility3/framework/plugins/windows/registry/printkey.py b/volatility3/framework/plugins/windows/registry/printkey.py index ed926805b..10079e41b 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -8,7 +8,8 @@ from typing import List, Optional, Sequence, Iterable, Tuple, Union from volatility3.framework import objects, renderers, exceptions, interfaces, constants from volatility3.framework.configuration import requirements -from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException +from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException, InvalidAddressException, \ + RegistryInvalidIndex from volatility3.framework.renderers import TreeGrid, conversion, format_hints from volatility3.framework.symbols.windows.extensions.registry import RegValueTypes from volatility3.plugins.windows.registry import hivelist @@ -77,7 +78,14 @@ class PrintKey(interfaces.plugins.PluginInterface): return None node = node_path[-1] key_path_items = [hive] + node_path[1:] - key_path = "\\".join([k.get_name() for k in key_path_items]) + key_path_names = [] + for k in key_path_items: + try: + key_path_names.append(k.get_name()) + except (InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + key_path_names.append('-') + key_path = "\\".join([k for k in key_path_names]) + if node.vol.type_name.endswith(constants.BANG + "_CELL_DATA"): raise RegistryFormatException( hive.name, "Encountered _CELL_DATA instead of _CM_KEY_NODE" @@ -99,7 +107,7 @@ class PrintKey(interfaces.plugins.PluginInterface): if key_node.vol.offset not in [x.vol.offset for x in node_path]: try: key_node.get_name() - except exceptions.InvalidAddressException as excp: + except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex) as excp: vollog.debug(excp) continue @@ -149,6 +157,7 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, RegistryFormatException, + RegistryInvalidIndex ) as excp: vollog.debug(excp) key_node_name = renderers.UnreadableValue() @@ -176,6 +185,7 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, RegistryFormatException, + RegistryInvalidIndex ) as excp: vollog.debug(excp) value_node_name = renderers.UnreadableValue() diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index 87016553a..3bc4e48d8 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -13,7 +13,7 @@ from typing import Any, Generator, List, Tuple from volatility3.framework import constants, exceptions, interfaces, renderers from volatility3.framework.configuration import requirements from volatility3.framework.layers.physical import BufferDataLayer -from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException +from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException, RegistryInvalidIndex from volatility3.framework.renderers import conversion, format_hints from volatility3.framework.symbols import intermed from volatility3.plugins.windows.registry import hivelist @@ -238,7 +238,11 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac # output any subkeys under Count for subkey in countkey.get_subkeys(): - subkey_name = subkey.get_name() + try: + subkey_name = subkey.get_name() + except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + subkey_name = renderers.UnreadableValue() + result = ( 1, ( @@ -260,7 +264,11 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac # output any values under Count for value in countkey.get_values(): - value_name = value.get_name() + try: + value_name = value.get_name() + except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + value_name = renderers.UnreadableValue() + with contextlib.suppress(UnicodeDecodeError): value_name = codecs.encode(value_name, "rot_13") diff --git a/volatility3/framework/plugins/windows/scheduled_tasks.py b/volatility3/framework/plugins/windows/scheduled_tasks.py index 31aaec4f0..2f2f7c254 100644 --- a/volatility3/framework/plugins/windows/scheduled_tasks.py +++ b/volatility3/framework/plugins/windows/scheduled_tasks.py @@ -311,7 +311,7 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: if value.get_name() == "Id": task_id_value = value break - except exceptions.InvalidAddressException: + except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): continue if ( @@ -323,10 +323,13 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: except exceptions.InvalidAddressException: id_str = None - if isinstance(id_str, bytes): - mapping[id_str.decode("utf-16le", errors="replace").rstrip(NULL)] = str( - key.get_name() - ) + try: + if isinstance(id_str, bytes): + mapping[id_str.decode("utf-16le", errors="replace").rstrip(NULL)] = str( + key.get_name() + ) + except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + pass for subkey in key.get_subkeys(): mapping.update(_build_guid_name_map(subkey)) @@ -1231,13 +1234,22 @@ information about triggers, actions, run times, and creation times.""" for value in key.get_values(): try: name = str(value.get_name()) - except exceptions.InvalidAddressException: + except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): continue if name in ["Actions", "Triggers", "DynamicInfo"]: values[name] = value - task_name = guid_mapping.get(str(key.get_name()), renderers.NotAvailableValue()) + + try: + key_name = str(key.get_name()) + except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): + key_name = None + + try: + task_name = guid_mapping.get(key_name, renderers.NotAvailableValue()) + except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): + task_name = renderers.NotAvailableValue() try: action_set = cls.parse_actions_value(values["Actions"]) @@ -1348,11 +1360,11 @@ information about triggers, actions, run times, and creation times.""" args, ( action_set.context - if action_set is not None + if action_set is not None and action_set.context is not None else renderers.NotAvailableValue() ), working_directory, - str(key.get_name()), + key_name or renderers.NotAvailableValue(), ) def _generator(self) -> Iterator[Tuple[int, _ScheduledTaskEntry]]: diff --git a/volatility3/framework/symbols/windows/extensions/registry.py b/volatility3/framework/symbols/windows/extensions/registry.py index c807c2cd6..7f284a36f 100644 --- a/volatility3/framework/symbols/windows/extensions/registry.py +++ b/volatility3/framework/symbols/windows/extensions/registry.py @@ -103,7 +103,7 @@ class CMHIVE(objects.StructType): for attr in ["FileFullPath", "FileUserName", "HiveRootPath"]: with contextlib.suppress( - AttributeError, exceptions.InvalidAddressException + AttributeError, exceptions.InvalidAddressException, RegistryInvalidIndex ): name = getattr(self, attr) if name.Length > 0: @@ -228,6 +228,7 @@ class CM_KEY_NODE(objects.StructType): except ( exceptions.InvalidAddressException, RegistryFormatException, + RegistryInvalidIndex, ): vollog.log( constants.LOGLEVEL_VVV, @@ -244,21 +245,22 @@ class CM_KEY_NODE(objects.StructType): hive = self._context.layers[self.vol.layer_name] if not isinstance(hive, RegistryHive): raise TypeError("CM_KEY_NODE was not instantiated on a RegistryHive layer") - child_list = hive.get_cell(self.ValueList.List).u.KeyList - child_list.count = self.ValueList.Count try: + child_list = hive.get_cell(self.ValueList.List).u.KeyList + child_list.count = self.ValueList.Count + for v in child_list: if v != 0: try: node = hive.get_node(v) - except (RegistryInvalidIndex, RegistryFormatException) as excp: + except (RegistryInvalidIndex, RegistryFormatException, RegistryInvalidIndex) as excp: vollog.debug(f"Invalid address {excp}") continue if isinstance(node, CM_KEY_VALUE): yield node - except (exceptions.InvalidAddressException, RegistryFormatException) as excp: + except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex) as excp: vollog.debug(f"Invalid address in get_values iteration: {excp}") return None @@ -347,7 +349,7 @@ class CM_KEY_VALUE(objects.StructType): offset=layer.get_cell(block_offset).vol.offset, length=amount, ) - except exceptions.InvalidAddressException: + except (exceptions.InvalidAddressException, RegistryInvalidIndex): vollog.debug( f"Failed to read {amount:x} bytes of data, padding with {amount:x}" ) @@ -357,7 +359,7 @@ class CM_KEY_VALUE(objects.StructType): # but the length at the start could be negative so just adding 4 to jump past it try: data = layer.read(self.Data + 4, datalen) - except exceptions.InvalidAddressException: + except (exceptions.InvalidAddressException, RegistryInvalidIndex): vollog.debug( f"Failed to read {datalen:x} bytes of data, returning {datalen:x} null bytes" ) From e1c88f065b547bada3622632a381322a94c4c5c3 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 10 Mar 2025 13:57:14 -0500 Subject: [PATCH 3/9] #1476 - black fixes --- .../framework/plugins/windows/envars.py | 38 +++++++++++++++---- .../plugins/windows/getservicesids.py | 6 +-- .../framework/plugins/windows/getsids.py | 6 +-- .../framework/plugins/windows/hashdump.py | 9 +++-- .../framework/plugins/windows/lsadump.py | 20 ++++++++-- .../plugins/windows/registry/printkey.py | 26 +++++++++---- .../plugins/windows/registry/userassist.py | 18 +++++++-- .../plugins/windows/scheduled_tasks.py | 31 ++++++++++++--- .../symbols/windows/extensions/registry.py | 18 +++++++-- 9 files changed, 133 insertions(+), 39 deletions(-) diff --git a/volatility3/framework/plugins/windows/envars.py b/volatility3/framework/plugins/windows/envars.py index 98d9e2553..d197fbc98 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -71,13 +71,25 @@ class Envars(interfaces.plugins.PluginInterface): sys = hive.get_key( "CurrentControlSet\\Control\\Session Manager\\Environment" ) - except (KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): - with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): + with contextlib.suppress( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): sys = hive.get_key( "ControlSet001\\Control\\Session Manager\\Environment" ) if sys: - with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + with contextlib.suppress( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): for node in sys.get_values(): try: value_node_name = node.get_name() @@ -86,7 +98,7 @@ class Envars(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, registry.RegistryFormatException, - registry.RegistryInvalidIndex + registry.RegistryInvalidIndex, ): vollog.log( constants.LOGLEVEL_VVV, @@ -96,10 +108,18 @@ class Envars(interfaces.plugins.PluginInterface): ntuser = None ## The user-specific variables - with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + with contextlib.suppress( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): ntuser = hive.get_key("Environment") if ntuser: - with contextlib.suppress(KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + with contextlib.suppress( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): for node in ntuser.get_values(): try: value_node_name = node.get_name() @@ -119,7 +139,11 @@ class Envars(interfaces.plugins.PluginInterface): ## The volatile user variables try: key = hive.get_key("Volatile Environment") - except (KeyError, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + KeyError, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): continue try: for node in key.get_values(): diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index 9e9c5fa4d..c334fe722 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -106,9 +106,9 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): try: sid_name = s.get_name() except ( - exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, ): continue diff --git a/volatility3/framework/plugins/windows/getsids.py b/volatility3/framework/plugins/windows/getsids.py index 6cbba059b..0d54ea12c 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -115,9 +115,9 @@ class GetSIDs(interfaces.plugins.PluginInterface): try: sid = str(subkey.get_name()) except ( - exceptions.InvalidAddressException, - layers.registry.RegistryFormatException, - layers.registry.RegistryInvalidIndex, + exceptions.InvalidAddressException, + layers.registry.RegistryFormatException, + layers.registry.RegistryInvalidIndex, ): continue diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index 6529cba19..d90e23802 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -380,10 +380,13 @@ class Hashdump(interfaces.plugins.PluginInterface): if class_data is None: return None bootkey += class_data.decode("utf-16-le") - except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex) as excp: + except ( + InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ) as excp: vollog.log( - constants.LOGLEVEL_VVV, - f"Unable to read Lsa key {lk}: {excp}" + constants.LOGLEVEL_VVV, f"Unable to read Lsa key {lk}: {excp}" ) return None diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index c4e8ef48a..aeea239c7 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -123,7 +123,11 @@ class Lsadump(interfaces.plugins.PluginInterface): if enc_secret_key: try: enc_secret_value = next(enc_secret_key.get_values(), None) - except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): enc_secret_value = None if enc_secret_value: @@ -202,7 +206,12 @@ class Lsadump(interfaces.plugins.PluginInterface): try: enc_secret_value = next(sec_val_key.get_values(), None) - except (StopIteration, InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + StopIteration, + InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): enc_secret_value = None if not enc_secret_value: @@ -222,12 +231,15 @@ class Lsadump(interfaces.plugins.PluginInterface): try: key_name = key.get_name() - except (InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): key_name = renderers.UnreadableValue() yield (0, (key_name, format_hints.HexBytes(secret), secret)) - def run(self): offset = self.config.get("offset", None) syshive = sechive = None diff --git a/volatility3/framework/plugins/windows/registry/printkey.py b/volatility3/framework/plugins/windows/registry/printkey.py index 585ec6b5e..c6d216760 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -8,8 +8,12 @@ from typing import List, Optional, Sequence, Iterable, Tuple, Union from volatility3.framework import objects, renderers, exceptions, interfaces, constants from volatility3.framework.configuration import requirements -from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException, InvalidAddressException, \ - RegistryInvalidIndex +from volatility3.framework.layers.registry import ( + RegistryHive, + RegistryFormatException, + InvalidAddressException, + RegistryInvalidIndex, +) from volatility3.framework.renderers import TreeGrid, conversion, format_hints from volatility3.framework.symbols.windows.extensions.registry import RegValueTypes from volatility3.plugins.windows.registry import hivelist @@ -82,8 +86,12 @@ class PrintKey(interfaces.plugins.PluginInterface): for k in key_path_items: try: key_path_names.append(k.get_name()) - except (InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): - key_path_names.append('-') + except ( + InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ): + key_path_names.append("-") key_path = "\\".join([k for k in key_path_names]) if node.vol.type_name.endswith(constants.BANG + "_CELL_DATA"): @@ -107,7 +115,11 @@ class PrintKey(interfaces.plugins.PluginInterface): if key_node.vol.offset not in [x.vol.offset for x in node_path]: try: key_node.get_name() - except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex) as excp: + except ( + exceptions.InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ) as excp: vollog.debug(excp) continue @@ -157,7 +169,7 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, RegistryFormatException, - RegistryInvalidIndex + RegistryInvalidIndex, ) as excp: vollog.debug(excp) key_node_name = renderers.UnreadableValue() @@ -185,7 +197,7 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, RegistryFormatException, - RegistryInvalidIndex + RegistryInvalidIndex, ) as excp: vollog.debug(excp) value_node_name = renderers.UnreadableValue() diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index 82139fdf2..738f230b7 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -13,7 +13,11 @@ from typing import Any, Generator, List, Tuple from volatility3.framework import constants, exceptions, interfaces, renderers from volatility3.framework.configuration import requirements from volatility3.framework.layers.physical import BufferDataLayer -from volatility3.framework.layers.registry import RegistryHive, RegistryFormatException, RegistryInvalidIndex +from volatility3.framework.layers.registry import ( + RegistryHive, + RegistryFormatException, + RegistryInvalidIndex, +) from volatility3.framework.renderers import conversion, format_hints from volatility3.framework.symbols import intermed from volatility3.plugins.windows.registry import hivelist @@ -240,7 +244,11 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac for subkey in countkey.get_subkeys(): try: subkey_name = subkey.get_name() - except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + except ( + exceptions.InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ): subkey_name = renderers.UnreadableValue() result = ( @@ -266,7 +274,11 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac for value in countkey.get_values(): try: value_name = value.get_name() - except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + except ( + exceptions.InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ): value_name = renderers.UnreadableValue() with contextlib.suppress(UnicodeDecodeError): diff --git a/volatility3/framework/plugins/windows/scheduled_tasks.py b/volatility3/framework/plugins/windows/scheduled_tasks.py index b901139c3..fc4d46338 100644 --- a/volatility3/framework/plugins/windows/scheduled_tasks.py +++ b/volatility3/framework/plugins/windows/scheduled_tasks.py @@ -311,7 +311,11 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: if value.get_name() == "Id": task_id_value = value break - except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): continue if ( @@ -328,7 +332,11 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: mapping[id_str.decode("utf-16le", errors="replace").rstrip(NULL)] = str( key.get_name() ) - except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex): + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryInvalidIndex, + ): pass for subkey in key.get_subkeys(): @@ -1233,21 +1241,32 @@ information about triggers, actions, run times, and creation times.""" for value in key.get_values(): try: name = str(value.get_name()) - except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryFormatException, + ): continue if name in ["Actions", "Triggers", "DynamicInfo"]: values[name] = value - try: key_name = str(key.get_name()) - except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryFormatException, + ): key_name = None try: task_name = guid_mapping.get(key_name, renderers.NotAvailableValue()) - except (exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryFormatException): + except ( + exceptions.InvalidAddressException, + registry.RegistryFormatException, + registry.RegistryFormatException, + ): task_name = renderers.NotAvailableValue() try: diff --git a/volatility3/framework/symbols/windows/extensions/registry.py b/volatility3/framework/symbols/windows/extensions/registry.py index 7f284a36f..c6c2ee358 100644 --- a/volatility3/framework/symbols/windows/extensions/registry.py +++ b/volatility3/framework/symbols/windows/extensions/registry.py @@ -199,7 +199,11 @@ class CM_KEY_NODE(objects.StructType): # We could change the array type to a struct with both parts try: signature = node.cast("string", max_length=2, encoding="latin-1") - except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex): + except ( + exceptions.InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ): return None listjump = None @@ -254,13 +258,21 @@ class CM_KEY_NODE(objects.StructType): if v != 0: try: node = hive.get_node(v) - except (RegistryInvalidIndex, RegistryFormatException, RegistryInvalidIndex) as excp: + except ( + RegistryInvalidIndex, + RegistryFormatException, + RegistryInvalidIndex, + ) as excp: vollog.debug(f"Invalid address {excp}") continue if isinstance(node, CM_KEY_VALUE): yield node - except (exceptions.InvalidAddressException, RegistryFormatException, RegistryInvalidIndex) as excp: + except ( + exceptions.InvalidAddressException, + RegistryFormatException, + RegistryInvalidIndex, + ) as excp: vollog.debug(f"Invalid address in get_values iteration: {excp}") return None From 6061270816c75f019e4ce185becfee5901aa7730 Mon Sep 17 00:00:00 2001 From: superponible Date: Mon, 10 Mar 2025 14:03:34 -0500 Subject: [PATCH 4/9] Potential fix for code scanning alert no. 381: Unused import Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- volatility3/framework/plugins/windows/lsadump.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index aeea239c7..989d4d473 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -11,7 +11,7 @@ from Crypto.Cipher import ARC4, DES, AES from volatility3.framework import interfaces, renderers, exceptions from volatility3.framework.configuration import requirements from volatility3.framework.exceptions import InvalidAddressException -from volatility3.framework.interfaces.layers import IteratorValue + from volatility3.framework.layers import registry from volatility3.framework.symbols.windows import versions from volatility3.plugins.windows import hashdump From e0168c7becf017f2cfbee326b5b3a847c7f69e4c Mon Sep 17 00:00:00 2001 From: superponible Date: Mon, 10 Mar 2025 14:03:45 -0500 Subject: [PATCH 5/9] Potential fix for code scanning alert no. 374: Testing equality to None Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- volatility3/framework/plugins/windows/prefetch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/prefetch.py b/volatility3/framework/plugins/windows/prefetch.py index 3997bc012..30642b269 100644 --- a/volatility3/framework/plugins/windows/prefetch.py +++ b/volatility3/framework/plugins/windows/prefetch.py @@ -74,7 +74,7 @@ def prefix_code_tree_add_leaf(treeNodes: List[PREFIX_CODE_NODE], leafIndex: int, while bits > 1: bits -= 1 childIndex = (mask >> bits) & 1 - if node.child[childIndex] == None: + if node.child[childIndex] is None: node.child[childIndex] = treeNodes[i] treeNodes[i].leaf = False i += 1 From f52a857aa06986d6d7f4c12241ff1bf5d8625963 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 10 Mar 2025 14:10:59 -0500 Subject: [PATCH 6/9] #1476 - code cleanup --- .../framework/plugins/windows/prefetch.py | 178 +++++++++++------- .../plugins/windows/scheduled_tasks.py | 4 +- 2 files changed, 117 insertions(+), 65 deletions(-) diff --git a/volatility3/framework/plugins/windows/prefetch.py b/volatility3/framework/plugins/windows/prefetch.py index 30642b269..6e74f466f 100644 --- a/volatility3/framework/plugins/windows/prefetch.py +++ b/volatility3/framework/plugins/windows/prefetch.py @@ -18,8 +18,8 @@ class BitStream: self.source = source self.index = in_pos + 4 # read UInt16 little endian - mask = struct.unpack_from('> (32 - n) def skip(self, n: int) -> Union[None, Exception]: - self.mask = ((self.mask << n) & 0xFFFFFFFF) + self.mask = (self.mask << n) & 0xFFFFFFFF self.bits -= n if self.bits < 16: if self.index + 2 > len(self.source): return Exception("EOF Error") # read UInt16 little endian - self.mask += ((struct.unpack_from(' int: +def prefix_code_tree_add_leaf( + treeNodes: List[PREFIX_CODE_NODE], leafIndex: int, mask: int, bits: int +) -> int: node = treeNodes[0] i = leafIndex + 1 childIndex = None @@ -94,13 +99,13 @@ def prefix_code_tree_rebuild(input: bytes) -> PREFIX_CODE_NODE: symbolInfo[2 * i].id = 2 * i symbolInfo[2 * i].symbol = 2 * i - symbolInfo[2 * i].length = value & 0xf + symbolInfo[2 * i].length = value & 0xF value >>= 4 symbolInfo[2 * i + 1].id = 2 * i + 1 symbolInfo[2 * i + 1].symbol = 2 * i + 1 - symbolInfo[2 * i + 1].length = value & 0xf + symbolInfo[2 * i + 1].length = value & 0xF symbolInfo = sorted(symbolInfo, key=lambda x: (x.length, x.symbol)) @@ -128,9 +133,10 @@ def prefix_code_tree_rebuild(input: bytes) -> PREFIX_CODE_NODE: return root -def prefix_code_tree_decode_symbol(bstr: BitStream, root: PREFIX_CODE_NODE) -> Tuple[int, Union[None, Exception]]: +def prefix_code_tree_decode_symbol( + bstr: BitStream, root: PREFIX_CODE_NODE +) -> Tuple[int, Union[None, Exception]]: node = root - i = 0 while True: bit = bstr.lookup(1) err = bstr.skip(1) @@ -138,7 +144,7 @@ def prefix_code_tree_decode_symbol(bstr: BitStream, root: PREFIX_CODE_NODE) -> T return 0, err node = node.child[bit] - if node == None: + if node is None: return 0, Exception("Corruption detected") if node.leaf: @@ -146,11 +152,9 @@ def prefix_code_tree_decode_symbol(bstr: BitStream, root: PREFIX_CODE_NODE) -> T return node.symbol, None -def lz77_huffman_decompress_chunck(in_idx: int, - input: bytes, - out_idx: int, - output: bytearray, - chunk_size: int) -> Tuple[int, int, Union[None, Exception]]: +def lz77_huffman_decompress_chunck( + in_idx: int, input: bytes, out_idx: int, output: bytearray, chunk_size: int +) -> Tuple[int, int, Union[None, Exception]]: # Ensure there are at least 256 bytes available to read if in_idx + 256 > len(input): return 0, 0, Exception("EOF Error") @@ -187,7 +191,7 @@ def lz77_huffman_decompress_chunck(in_idx: int, bstr.index += 1 if length == 270: - length = struct.unpack_from(' Tuple[bytes, Union[None, Exception]]: +def lz77_huffman_decompress( + input: bytes, output_size: int +) -> Tuple[bytes, Union[None, Exception]]: output = bytearray(output_size) err = None @@ -226,7 +232,8 @@ def lz77_huffman_decompress(input: bytes, output_size: int) -> Tuple[bytes, Unio chunk_size = 65536 in_idx, out_idx, err = lz77_huffman_decompress_chunck( - in_idx, input, out_idx, output, chunk_size) + in_idx, input, out_idx, output, chunk_size + ) if err is not None: return output, err if out_idx >= len(output) or in_idx >= len(input): @@ -236,14 +243,22 @@ def lz77_huffman_decompress(input: bytes, output_size: int) -> Tuple[bytes, Unio class Prefetch(interfaces.plugins.PluginInterface): """Get and parse the prefetch files""" + _required_framework_version = (2, 0, 0) _version = (1, 0, 0) @classmethod def get_requirements(cls): - return [requirements.ModuleRequirement(name='kernel', description='Windows kernel', - architectures=["Intel32", "Intel64"]), - requirements.PluginRequirement(name='filescan', plugin=filescan.FileScan, version=(0, 0, 0)), ] + return [ + requirements.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.PluginRequirement( + name="filescan", plugin=filescan.FileScan, version=(0, 0, 0) + ), + ] @classmethod def version_17(cls, prefetch_file): @@ -254,15 +269,17 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size = int.from_bytes(stream.read(4), "little") stream.seek(0x0010) - executable_raw = stream.read(60).decode('utf-16') - executable_name = executable_raw.split('\u0000')[0] + executable_raw = stream.read(60).decode("utf-16") + executable_name = executable_raw.split("\u0000")[0] stream.seek(0x004C) prefetch_hash = int.from_bytes(stream.read(4), "little") stream.seek(0x0078) last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + last_execution_filetime_human = conversion.wintime_to_datetime( + last_execution_filetime + ) stream.seek(0x0090) execution_counter = int.from_bytes(stream.read(4), "little") @@ -272,7 +289,7 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size, format_hints.Hex(prefetch_hash), last_execution_filetime_human, - execution_counter + execution_counter, ) @classmethod @@ -284,15 +301,17 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size = int.from_bytes(stream.read(4), "little") stream.seek(0x0010) - executable_raw = stream.read(60).decode('utf-16') - executable_name = executable_raw.split('\u0000')[0] + executable_raw = stream.read(60).decode("utf-16") + executable_name = executable_raw.split("\u0000")[0] stream.seek(0x004C) prefetch_hash = int.from_bytes(stream.read(4), "little") stream.seek(0x0080) last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + last_execution_filetime_human = conversion.wintime_to_datetime( + last_execution_filetime + ) stream.seek(0x0098) execution_counter = int.from_bytes(stream.read(4), "little") @@ -302,7 +321,7 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size, format_hints.Hex(prefetch_hash), last_execution_filetime_human, - execution_counter + execution_counter, ) @classmethod @@ -314,15 +333,17 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size = int.from_bytes(stream.read(4), "little") stream.seek(0x0010) - executable_raw = stream.read(60).decode('utf-16') - executable_name = executable_raw.split('\u0000')[0] + executable_raw = stream.read(60).decode("utf-16") + executable_name = executable_raw.split("\u0000")[0] stream.seek(0x004C) prefetch_hash = int.from_bytes(stream.read(4), "little") stream.seek(0x0080) last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + last_execution_filetime_human = conversion.wintime_to_datetime( + last_execution_filetime + ) stream.seek(0x00D0) execution_counter = int.from_bytes(stream.read(4), "little") @@ -332,7 +353,7 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size, format_hints.Hex(prefetch_hash), last_execution_filetime_human, - execution_counter + execution_counter, ) @classmethod @@ -344,8 +365,8 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size = int.from_bytes(stream.read(4), "little") stream.seek(0x0010) - executable_raw = stream.read(60).decode('utf-16') - executable_name = executable_raw.split('\u0000')[0] + executable_raw = stream.read(60).decode("utf-16") + executable_name = executable_raw.split("\u0000")[0] stream.seek(0x004C) prefetch_hash = int.from_bytes(stream.read(4), "little") @@ -353,7 +374,9 @@ class Prefetch(interfaces.plugins.PluginInterface): stream.seek(0x0080) # The first FILETIME is the most recent run time last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime(last_execution_filetime) + last_execution_filetime_human = conversion.wintime_to_datetime( + last_execution_filetime + ) stream.seek(0x00C8) # Variant 1 execution_counter = int.from_bytes(stream.read(4), "little") @@ -366,7 +389,7 @@ class Prefetch(interfaces.plugins.PluginInterface): file_size, format_hints.Hex(prefetch_hash), last_execution_filetime_human, - execution_counter + execution_counter, ) @classmethod @@ -386,13 +409,15 @@ class Prefetch(interfaces.plugins.PluginInterface): vollog.info(f"decompressed size : {decompressed_size}") stream.seek(0x0008) compressed_bytes = stream.read() - prefetch_file = lz77_huffman_decompress(bytearray(compressed_bytes), decompressed_size)[0] + prefetch_file = lz77_huffman_decompress( + bytearray(compressed_bytes), decompressed_size + )[0] try: file_version = int.from_bytes(prefetch_file[:4], "little") signature = prefetch_file[4:8].decode() - vollog.info(f'File version : {file_version}') + vollog.info(f"File version : {file_version}") vollog.info(f"Signature : {signature}") - except: + except Exception: # We can not even read the header pass @@ -413,46 +438,63 @@ class Prefetch(interfaces.plugins.PluginInterface): yield result def _generator(self, files): - kernel = self.context.modules[self.config['kernel']] - offsets = [] + kernel = self.context.modules[self.config["kernel"]] for file_obj in files: - """Get the prefetch recovered files from the “filescan” plugin; """ + """Get the prefetch recovered files from the “filescan” plugin;""" try: file_name = file_obj.FileName.String file_extension = pathlib.Path(file_name).suffix if file_extension == ".pf": """If found, try to dump the prefetch file (inspired from the "DumpFiles" plugin)""" memory_objects = [] - memory_layer_name = self.context.layers[kernel.layer_name].config['memory_layer'] + memory_layer_name = self.context.layers[kernel.layer_name].config[ + "memory_layer" + ] memory_layer = self.context.layers[memory_layer_name] primary_layer = self.context.layers[kernel.layer_name] for member_name in ["DataSectionObject", "ImageSectionObject"]: try: - section_obj = getattr(file_obj.SectionObjectPointer, member_name) - control_area = section_obj.dereference().cast("_CONTROL_AREA") + section_obj = getattr( + file_obj.SectionObjectPointer, member_name + ) + control_area = section_obj.dereference().cast( + "_CONTROL_AREA" + ) if control_area.is_valid(): vollog.info(f"Found : {file_obj.FileName.String}") memory_objects.append((control_area, memory_layer)) except exceptions.InvalidAddressException: - vollog.log(constants.LOGLEVEL_VVV, - f"{member_name} is unavailable for file {file_obj.vol.offset:#x}") + vollog.log( + constants.LOGLEVEL_VVV, + f"{member_name} is unavailable for file {file_obj.vol.offset:#x}", + ) try: scm_pointer = file_obj.SectionObjectPointer.SharedCacheMap - shared_cache_map = scm_pointer.dereference().cast("_SHARED_CACHE_MAP") + shared_cache_map = scm_pointer.dereference().cast( + "_SHARED_CACHE_MAP" + ) if shared_cache_map.is_valid(): memory_objects.append((shared_cache_map, primary_layer)) except exceptions.InvalidAddressException: - vollog.log(constants.LOGLEVEL_VVV, - f"SharedCacheMap is unavailable for file {file_obj.vol.offset:#x}") + vollog.log( + constants.LOGLEVEL_VVV, + f"SharedCacheMap is unavailable for file {file_obj.vol.offset:#x}", + ) vollog.info(f"memory_objects : {memory_objects}") """Now, read and parse our PF to retrieve our artifacts""" for memory_object, layer in memory_objects: bytes_read = 0 - prefetch_raw = b'' + prefetch_raw = b"" try: - for mem_offset, _, datasize in memory_object.get_available_pages(): - prefetch_raw += layer.read(mem_offset, datasize, pad=True) + for ( + mem_offset, + _, + datasize, + ) in memory_object.get_available_pages(): + prefetch_raw += layer.read( + mem_offset, datasize, pad=True + ) bytes_read += len(prefetch_raw) vollog.info(f"Read {bytes_read}") if not bytes_read: @@ -463,16 +505,26 @@ class Prefetch(interfaces.plugins.PluginInterface): yield 0, result except exceptions.InvalidAddressException: - vollog.debug(f"Unable to dump file at {file_obj.vol.offset:#x}") - pass + vollog.debug( + f"Unable to dump file at {file_obj.vol.offset:#x}" + ) + except exceptions.InvalidAddressException: continue def run(self): - kernel = self.context.modules[self.config['kernel']] - return renderers.TreeGrid([ - ("ExecutableName", str), - ("FileSize", int), - ("PrefetchHash", format_hints.Hex), - ("LastExecution", datetime.datetime), ("ExecutionCounter", int)], - self._generator(filescan.FileScan.scan_files(self.context, kernel.layer_name, kernel.symbol_table_name))) \ No newline at end of file + kernel = self.context.modules[self.config["kernel"]] + return renderers.TreeGrid( + [ + ("ExecutableName", str), + ("FileSize", int), + ("PrefetchHash", format_hints.Hex), + ("LastExecution", datetime.datetime), + ("ExecutionCounter", int), + ], + self._generator( + filescan.FileScan.scan_files( + self.context, kernel.layer_name, kernel.symbol_table_name + ) + ), + ) diff --git a/volatility3/framework/plugins/windows/scheduled_tasks.py b/volatility3/framework/plugins/windows/scheduled_tasks.py index fc4d46338..dd821bb35 100644 --- a/volatility3/framework/plugins/windows/scheduled_tasks.py +++ b/volatility3/framework/plugins/windows/scheduled_tasks.py @@ -336,8 +336,8 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: exceptions.InvalidAddressException, registry.RegistryFormatException, registry.RegistryInvalidIndex, - ): - pass + ) as excp: + vollog.debug(f"Exception occurred while decoding id_str: {excp}") for subkey in key.get_subkeys(): mapping.update(_build_guid_name_map(subkey)) From 9b23feef2be35e079f7dba02e2a1e31343d8315e Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Mon, 10 Mar 2025 14:17:24 -0500 Subject: [PATCH 7/9] #1476 - ruff fixes --- volatility3/framework/plugins/windows/prefetch.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/windows/prefetch.py b/volatility3/framework/plugins/windows/prefetch.py index 6e74f466f..a5f054f15 100644 --- a/volatility3/framework/plugins/windows/prefetch.py +++ b/volatility3/framework/plugins/windows/prefetch.py @@ -3,14 +3,19 @@ # https://github.com/libyal/libscca/blob/main/documentation/Windows%20Prefetch%20File%20(PF)%20format.asciidoc # https://github.com/volatilityfoundation/volatility3/ # https://github.com/EricZimmerman/Prefetch/tree/master/Prefetch -import logging, pathlib, datetime, io, struct +import logging +import pathlib +import datetime +import io +import struct + from volatility3.framework import renderers, interfaces, exceptions, constants from volatility3.framework.configuration import requirements from volatility3.plugins.windows import filescan from volatility3.framework.renderers import format_hints, conversion +from typing import Tuple, List, Union vollog = logging.getLogger(__name__) -from typing import Tuple, List, Union class BitStream: @@ -498,7 +503,7 @@ class Prefetch(interfaces.plugins.PluginInterface): bytes_read += len(prefetch_raw) vollog.info(f"Read {bytes_read}") if not bytes_read: - vollog.info(f"Prefetch is empty") + vollog.info("Prefetch is empty") else: """Prefetch parsing""" for result in self.parse_prefetch(prefetch_raw): From 7f7295cdb7718da9719617297722de0bcd0b17f3 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Tue, 11 Mar 2025 09:52:24 -0500 Subject: [PATCH 8/9] #1476 - remove unfinished prefetch plugin --- .../framework/plugins/windows/prefetch.py | 535 ------------------ 1 file changed, 535 deletions(-) delete mode 100644 volatility3/framework/plugins/windows/prefetch.py diff --git a/volatility3/framework/plugins/windows/prefetch.py b/volatility3/framework/plugins/windows/prefetch.py deleted file mode 100644 index a5f054f15..000000000 --- a/volatility3/framework/plugins/windows/prefetch.py +++ /dev/null @@ -1,535 +0,0 @@ -# References : -# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-XCA/%5bMS-XCA%5d.pdf -# https://github.com/libyal/libscca/blob/main/documentation/Windows%20Prefetch%20File%20(PF)%20format.asciidoc -# https://github.com/volatilityfoundation/volatility3/ -# https://github.com/EricZimmerman/Prefetch/tree/master/Prefetch -import logging -import pathlib -import datetime -import io -import struct - -from volatility3.framework import renderers, interfaces, exceptions, constants -from volatility3.framework.configuration import requirements -from volatility3.plugins.windows import filescan -from volatility3.framework.renderers import format_hints, conversion -from typing import Tuple, List, Union - -vollog = logging.getLogger(__name__) - - -class BitStream: - def __init__(self, source: bytes, in_pos: int): - self.source = source - self.index = in_pos + 4 - # read UInt16 little endian - mask = struct.unpack_from(" int: - if n == 0: - return 0 - return self.mask >> (32 - n) - - def skip(self, n: int) -> Union[None, Exception]: - self.mask = (self.mask << n) & 0xFFFFFFFF - self.bits -= n - if self.bits < 16: - if self.index + 2 > len(self.source): - return Exception("EOF Error") - # read UInt16 little endian - self.mask += ( - (struct.unpack_from(" int: - node = treeNodes[0] - i = leafIndex + 1 - childIndex = None - - while bits > 1: - bits -= 1 - childIndex = (mask >> bits) & 1 - if node.child[childIndex] is None: - node.child[childIndex] = treeNodes[i] - treeNodes[i].leaf = False - i += 1 - node = node.child[childIndex] - - node.child[mask & 1] = treeNodes[leafIndex] - - return i - - -def prefix_code_tree_rebuild(input: bytes) -> PREFIX_CODE_NODE: - treeNodes = [PREFIX_CODE_NODE() for _ in range(1024)] - symbolInfo = [PREFIX_CODE_SYMBOL() for _ in range(512)] - - for i in range(256): - value = input[i] - - symbolInfo[2 * i].id = 2 * i - symbolInfo[2 * i].symbol = 2 * i - symbolInfo[2 * i].length = value & 0xF - - value >>= 4 - - symbolInfo[2 * i + 1].id = 2 * i + 1 - symbolInfo[2 * i + 1].symbol = 2 * i + 1 - symbolInfo[2 * i + 1].length = value & 0xF - - symbolInfo = sorted(symbolInfo, key=lambda x: (x.length, x.symbol)) - - i = 0 - while i < 512 and symbolInfo[i].length == 0: - i += 1 - - mask = 0 - bits = 1 - - root = treeNodes[0] - root.leaf = False - - j = 1 - while i < 512: - treeNodes[j].id = j - treeNodes[j].symbol = symbolInfo[i].symbol - treeNodes[j].leaf = True - mask = mask << (symbolInfo[i].length - bits) - bits = symbolInfo[i].length - j = prefix_code_tree_add_leaf(treeNodes, j, mask, bits) - mask += 1 - i += 1 - - return root - - -def prefix_code_tree_decode_symbol( - bstr: BitStream, root: PREFIX_CODE_NODE -) -> Tuple[int, Union[None, Exception]]: - node = root - while True: - bit = bstr.lookup(1) - err = bstr.skip(1) - if err is not None: - return 0, err - - node = node.child[bit] - if node is None: - return 0, Exception("Corruption detected") - - if node.leaf: - break - return node.symbol, None - - -def lz77_huffman_decompress_chunck( - in_idx: int, input: bytes, out_idx: int, output: bytearray, chunk_size: int -) -> Tuple[int, int, Union[None, Exception]]: - # Ensure there are at least 256 bytes available to read - if in_idx + 256 > len(input): - return 0, 0, Exception("EOF Error") - - root = prefix_code_tree_rebuild(input[in_idx:]) - # print_tree(root) - bstr = BitStream(input, in_idx + 256) - - i = out_idx - - while i < out_idx + chunk_size: - symbol, err = prefix_code_tree_decode_symbol(bstr, root) - - if err is not None: - return int(bstr.index), i, err - - if symbol < 256: - output[i] = symbol - i += 1 - else: - symbol -= 256 - length = symbol & 15 - symbol >>= 4 - - offset = 0 - if symbol != 0: - offset = int(bstr.lookup(symbol)) - - offset |= 1 << symbol - offset = -offset - - if length == 15: - length = bstr.source[bstr.index] + 15 - bstr.index += 1 - - if length == 270: - length = struct.unpack_from(" 0: - if i + offset < 0: - print(i + offset) - return int(bstr.index), i, Exception("Decompression Error") - - output[i] = output[i + offset] - i += 1 - length -= 1 - if length == 0: - break - return int(bstr.index), i, None - - -def lz77_huffman_decompress( - input: bytes, output_size: int -) -> Tuple[bytes, Union[None, Exception]]: - output = bytearray(output_size) - err = None - - # Index into the input buffer. - in_idx = 0 - - # Index into the output buffer. - out_idx = 0 - - while True: - # How much data belongs in the current chunk. Chunks - # are split into maximum 65536 bytes. - chunk_size = output_size - out_idx - if chunk_size > 65536: - chunk_size = 65536 - - in_idx, out_idx, err = lz77_huffman_decompress_chunck( - in_idx, input, out_idx, output, chunk_size - ) - if err is not None: - return output, err - if out_idx >= len(output) or in_idx >= len(input): - break - return output, None - - -class Prefetch(interfaces.plugins.PluginInterface): - """Get and parse the prefetch files""" - - _required_framework_version = (2, 0, 0) - _version = (1, 0, 0) - - @classmethod - def get_requirements(cls): - return [ - requirements.ModuleRequirement( - name="kernel", - description="Windows kernel", - architectures=["Intel32", "Intel64"], - ), - requirements.PluginRequirement( - name="filescan", plugin=filescan.FileScan, version=(0, 0, 0) - ), - ] - - @classmethod - def version_17(cls, prefetch_file): - """Extract pf information for Version 17""" - stream = io.BytesIO(prefetch_file) - - stream.seek(0x000C) - file_size = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0010) - executable_raw = stream.read(60).decode("utf-16") - executable_name = executable_raw.split("\u0000")[0] - - stream.seek(0x004C) - prefetch_hash = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0078) - last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime( - last_execution_filetime - ) - - stream.seek(0x0090) - execution_counter = int.from_bytes(stream.read(4), "little") - - yield ( - executable_name, - file_size, - format_hints.Hex(prefetch_hash), - last_execution_filetime_human, - execution_counter, - ) - - @classmethod - def version_23(cls, prefetch_file): - """Extract pf information for Version 23""" - stream = io.BytesIO(prefetch_file) - - stream.seek(0x000C) - file_size = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0010) - executable_raw = stream.read(60).decode("utf-16") - executable_name = executable_raw.split("\u0000")[0] - - stream.seek(0x004C) - prefetch_hash = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0080) - last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime( - last_execution_filetime - ) - - stream.seek(0x0098) - execution_counter = int.from_bytes(stream.read(4), "little") - - yield ( - executable_name, - file_size, - format_hints.Hex(prefetch_hash), - last_execution_filetime_human, - execution_counter, - ) - - @classmethod - def version_26(cls, prefetch_file): - """Extract pf information for Version 26""" - stream = io.BytesIO(prefetch_file) - - stream.seek(0x000C) - file_size = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0010) - executable_raw = stream.read(60).decode("utf-16") - executable_name = executable_raw.split("\u0000")[0] - - stream.seek(0x004C) - prefetch_hash = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0080) - last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime( - last_execution_filetime - ) - - stream.seek(0x00D0) - execution_counter = int.from_bytes(stream.read(4), "little") - - yield ( - executable_name, - file_size, - format_hints.Hex(prefetch_hash), - last_execution_filetime_human, - execution_counter, - ) - - @classmethod - def version_30(cls, prefetch_file): - """Extract pf information for Version 30""" - stream = io.BytesIO(prefetch_file) - - stream.seek(0x000C) - file_size = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0010) - executable_raw = stream.read(60).decode("utf-16") - executable_name = executable_raw.split("\u0000")[0] - - stream.seek(0x004C) - prefetch_hash = int.from_bytes(stream.read(4), "little") - - stream.seek(0x0080) - # The first FILETIME is the most recent run time - last_execution_filetime = int.from_bytes(stream.read(8), "little") - last_execution_filetime_human = conversion.wintime_to_datetime( - last_execution_filetime - ) - - stream.seek(0x00C8) # Variant 1 - execution_counter = int.from_bytes(stream.read(4), "little") - if execution_counter == 0: - stream.seek(0x00D0) # Variant 2 - execution_counter = int.from_bytes(stream.read(4), "little") - - yield ( - executable_name, - file_size, - format_hints.Hex(prefetch_hash), - last_execution_filetime_human, - execution_counter, - ) - - @classmethod - def parse_prefetch(cls, prefetch_file): - WinXpOrWin2K3 = 17 - VistaOrWin7 = 23 - Win8xOrWin2012x = 26 - Win10OrWin11 = 30 - stream = io.BytesIO(prefetch_file) - # First, we need to know if the prefetch is compressed (Win10/11) - signature = prefetch_file[:3].decode() - if signature == "MAM": - vollog.info("Windows 1X prefetch file detected.") - # The size of decompressed data is at offset 4 - stream.seek(0x0004) - decompressed_size = int.from_bytes(stream.read(4), "little") - vollog.info(f"decompressed size : {decompressed_size}") - stream.seek(0x0008) - compressed_bytes = stream.read() - prefetch_file = lz77_huffman_decompress( - bytearray(compressed_bytes), decompressed_size - )[0] - try: - file_version = int.from_bytes(prefetch_file[:4], "little") - signature = prefetch_file[4:8].decode() - vollog.info(f"File version : {file_version}") - vollog.info(f"Signature : {signature}") - except Exception: - # We can not even read the header - pass - - if signature != "SCCA": - vollog.info("Wrong signature, should be SCCA") - return - if file_version == WinXpOrWin2K3: - for result in cls.version_17(prefetch_file): - yield result - elif file_version == VistaOrWin7: - for result in cls.version_23(prefetch_file): - yield result - elif file_version == Win8xOrWin2012x: - for result in cls.version_26(prefetch_file): - yield result - elif file_version == Win10OrWin11: - for result in cls.version_30(prefetch_file): - yield result - - def _generator(self, files): - kernel = self.context.modules[self.config["kernel"]] - for file_obj in files: - """Get the prefetch recovered files from the “filescan” plugin;""" - try: - file_name = file_obj.FileName.String - file_extension = pathlib.Path(file_name).suffix - if file_extension == ".pf": - """If found, try to dump the prefetch file (inspired from the "DumpFiles" plugin)""" - memory_objects = [] - memory_layer_name = self.context.layers[kernel.layer_name].config[ - "memory_layer" - ] - memory_layer = self.context.layers[memory_layer_name] - primary_layer = self.context.layers[kernel.layer_name] - for member_name in ["DataSectionObject", "ImageSectionObject"]: - try: - section_obj = getattr( - file_obj.SectionObjectPointer, member_name - ) - control_area = section_obj.dereference().cast( - "_CONTROL_AREA" - ) - if control_area.is_valid(): - vollog.info(f"Found : {file_obj.FileName.String}") - memory_objects.append((control_area, memory_layer)) - except exceptions.InvalidAddressException: - vollog.log( - constants.LOGLEVEL_VVV, - f"{member_name} is unavailable for file {file_obj.vol.offset:#x}", - ) - try: - scm_pointer = file_obj.SectionObjectPointer.SharedCacheMap - shared_cache_map = scm_pointer.dereference().cast( - "_SHARED_CACHE_MAP" - ) - if shared_cache_map.is_valid(): - memory_objects.append((shared_cache_map, primary_layer)) - except exceptions.InvalidAddressException: - vollog.log( - constants.LOGLEVEL_VVV, - f"SharedCacheMap is unavailable for file {file_obj.vol.offset:#x}", - ) - vollog.info(f"memory_objects : {memory_objects}") - - """Now, read and parse our PF to retrieve our artifacts""" - for memory_object, layer in memory_objects: - bytes_read = 0 - prefetch_raw = b"" - try: - for ( - mem_offset, - _, - datasize, - ) in memory_object.get_available_pages(): - prefetch_raw += layer.read( - mem_offset, datasize, pad=True - ) - bytes_read += len(prefetch_raw) - vollog.info(f"Read {bytes_read}") - if not bytes_read: - vollog.info("Prefetch is empty") - else: - """Prefetch parsing""" - for result in self.parse_prefetch(prefetch_raw): - yield 0, result - - except exceptions.InvalidAddressException: - vollog.debug( - f"Unable to dump file at {file_obj.vol.offset:#x}" - ) - - except exceptions.InvalidAddressException: - continue - - def run(self): - kernel = self.context.modules[self.config["kernel"]] - return renderers.TreeGrid( - [ - ("ExecutableName", str), - ("FileSize", int), - ("PrefetchHash", format_hints.Hex), - ("LastExecution", datetime.datetime), - ("ExecutionCounter", int), - ], - self._generator( - filescan.FileScan.scan_files( - self.context, kernel.layer_name, kernel.symbol_table_name - ) - ), - ) From 95aee9a66402480c24f042dde1085bdbfc92d4ea Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Tue, 11 Mar 2025 16:04:06 -0500 Subject: [PATCH 9/9] #1476 - introduce RegistryException for simpler exception handling --- volatility3/framework/layers/registry.py | 10 ++++--- .../framework/plugins/windows/amcache.py | 10 +++---- .../framework/plugins/windows/envars.py | 27 +++++++------------ .../plugins/windows/getservicesids.py | 9 +++---- .../framework/plugins/windows/getsids.py | 10 +++---- .../framework/plugins/windows/hashdump.py | 11 +++++--- .../framework/plugins/windows/lsadump.py | 9 +++---- .../plugins/windows/registry/printkey.py | 22 +++++++-------- .../plugins/windows/registry/userassist.py | 11 +++----- .../plugins/windows/scheduled_tasks.py | 19 +++++-------- .../framework/plugins/windows/svcscan.py | 4 +-- .../symbols/windows/extensions/registry.py | 24 ++++++----------- .../plugins/windows/registry/certificates.py | 2 +- 13 files changed, 69 insertions(+), 99 deletions(-) diff --git a/volatility3/framework/layers/registry.py b/volatility3/framework/layers/registry.py index 9ca32ed31..1c16cedcf 100644 --- a/volatility3/framework/layers/registry.py +++ b/volatility3/framework/layers/registry.py @@ -19,11 +19,15 @@ from volatility3.plugins.windows import pslist vollog = logging.getLogger(__name__) -class RegistryFormatException(exceptions.LayerException): +class RegistryException(exceptions.LayerException): + """Base Registry Exception class for catching Registry layer errors.""" + + +class RegistryFormatException(RegistryException): """Thrown when an error occurs with the underlying Registry file format.""" -class RegistryInvalidIndex(exceptions.LayerException): +class RegistryInvalidIndex(RegistryException): """Thrown when an index that doesn't exist or can't be found occurs.""" @@ -142,7 +146,7 @@ class RegistryHive(linear.LinearlyMappedLayer): cell = self.get_cell(cell_offset) try: signature = cell.cast("string", max_length=2, encoding="latin-1") - except (RegistryInvalidIndex, exceptions.InvalidAddressException): + except (RegistryException, exceptions.InvalidAddressException): vollog.debug( f"Failed to get cell signature for cell (0x{cell.vol.offset:x})" ) diff --git a/volatility3/framework/plugins/windows/amcache.py b/volatility3/framework/plugins/windows/amcache.py index 133297de3..5920cd266 100644 --- a/volatility3/framework/plugins/windows/amcache.py +++ b/volatility3/framework/plugins/windows/amcache.py @@ -544,7 +544,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): amcache.get_key("Root\\InventoryDriverBinary") # type: ignore ) ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): # Registry key not found pass @@ -555,7 +555,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): amcache.get_key("Root\\Programs") ) # type: ignore } - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): programs = {} try: @@ -565,7 +565,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): ), key=_entry_sort_key, ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): files = [] for program_id, file_entries in itertools.groupby( @@ -594,7 +594,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): amcache.get_key("Root\\InventoryApplication") # type: ignore ) ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): programs = {} try: @@ -604,7 +604,7 @@ class Amcache(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): ), key=_entry_sort_key, ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): files = [] for program_id, file_entries in itertools.groupby( diff --git a/volatility3/framework/plugins/windows/envars.py b/volatility3/framework/plugins/windows/envars.py index d197fbc98..6360ca10b 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -73,13 +73,11 @@ class Envars(interfaces.plugins.PluginInterface): ) except ( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): with contextlib.suppress( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): sys = hive.get_key( "ControlSet001\\Control\\Session Manager\\Environment" @@ -87,8 +85,7 @@ class Envars(interfaces.plugins.PluginInterface): if sys: with contextlib.suppress( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): for node in sys.get_values(): try: @@ -97,8 +94,7 @@ class Envars(interfaces.plugins.PluginInterface): values.append(value_node_name) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): vollog.log( constants.LOGLEVEL_VVV, @@ -110,15 +106,13 @@ class Envars(interfaces.plugins.PluginInterface): ## The user-specific variables with contextlib.suppress( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): ntuser = hive.get_key("Environment") if ntuser: with contextlib.suppress( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): for node in ntuser.get_values(): try: @@ -127,8 +121,7 @@ class Envars(interfaces.plugins.PluginInterface): values.append(value_node_name) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): vollog.log( constants.LOGLEVEL_VVV, @@ -141,8 +134,7 @@ class Envars(interfaces.plugins.PluginInterface): key = hive.get_key("Volatile Environment") except ( KeyError, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): continue try: @@ -153,8 +145,7 @@ class Envars(interfaces.plugins.PluginInterface): values.append(value_node_name) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): vollog.log( constants.LOGLEVEL_VVV, diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index c334fe722..19a73fba8 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -88,16 +88,14 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): except ( KeyError, exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): try: services = hive.get_key(r"ControlSet001\Services") except ( KeyError, exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): continue @@ -107,8 +105,7 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): sid_name = s.get_name() except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): continue diff --git a/volatility3/framework/plugins/windows/getsids.py b/volatility3/framework/plugins/windows/getsids.py index 0d54ea12c..786dc3394 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -116,8 +116,7 @@ class GetSIDs(interfaces.plugins.PluginInterface): sid = str(subkey.get_name()) except ( exceptions.InvalidAddressException, - layers.registry.RegistryFormatException, - layers.registry.RegistryInvalidIndex, + layers.registry.RegistryException, ): continue @@ -127,8 +126,7 @@ class GetSIDs(interfaces.plugins.PluginInterface): value_node_name = node.get_name() or "(Default)" except ( exceptions.InvalidAddressException, - layers.registry.RegistryFormatException, - layers.registry.RegistryInvalidIndex, + layers.registry.RegistryException, ): continue try: @@ -162,13 +160,13 @@ class GetSIDs(interfaces.plugins.PluginInterface): except ( ValueError, exceptions.InvalidAddressException, - layers.registry.RegistryFormatException, + layers.registry.RegistryException, ): continue except ( KeyError, exceptions.InvalidAddressException, - layers.registry.RegistryFormatException, + layers.registry.RegistryException, ): continue diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index d90e23802..68d5f834a 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -12,6 +12,7 @@ from Crypto.Cipher import AES, ARC4, DES from volatility3.framework import interfaces, renderers, exceptions, constants from volatility3.framework.configuration import requirements from volatility3.framework.exceptions import InvalidAddressException +from volatility3.framework.layers import registry as registrylayer from volatility3.framework.symbols.windows.extensions import registry from volatility3.plugins.windows.registry import hivelist @@ -334,7 +335,7 @@ class Hashdump(interfaces.plugins.PluginInterface): try: if hive: result = hive.get_key(key) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registrylayer.RegistryException): vollog.info( f"Unable to load the required registry key {hive.get_name()}\\{key} from this memory image" ) @@ -382,8 +383,7 @@ class Hashdump(interfaces.plugins.PluginInterface): bootkey += class_data.decode("utf-16-le") except ( InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registrylayer.RegistryException, ) as excp: vollog.log( constants.LOGLEVEL_VVV, f"Unable to read Lsa key {lk}: {excp}" @@ -468,7 +468,10 @@ class Hashdump(interfaces.plugins.PluginInterface): if v.get_name() == "V": try: sam_data = samhive.read(v.Data + 4, v.DataLength) - except (exceptions.InvalidAddressException, registry.RegistryHive): + except ( + exceptions.InvalidAddressException, + registrylayer.RegistryException, + ): return None if not sam_data: diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index 989d4d473..72f2fa146 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -125,8 +125,7 @@ class Lsadump(interfaces.plugins.PluginInterface): enc_secret_value = next(enc_secret_key.get_values(), None) except ( InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): enc_secret_value = None @@ -209,8 +208,7 @@ class Lsadump(interfaces.plugins.PluginInterface): except ( StopIteration, InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): enc_secret_value = None @@ -233,8 +231,7 @@ class Lsadump(interfaces.plugins.PluginInterface): key_name = key.get_name() except ( InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): key_name = renderers.UnreadableValue() diff --git a/volatility3/framework/plugins/windows/registry/printkey.py b/volatility3/framework/plugins/windows/registry/printkey.py index c6d216760..c8b8f9cfb 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -12,7 +12,7 @@ from volatility3.framework.layers.registry import ( RegistryHive, RegistryFormatException, InvalidAddressException, - RegistryInvalidIndex, + RegistryException, ) from volatility3.framework.renderers import TreeGrid, conversion, format_hints from volatility3.framework.symbols.windows.extensions.registry import RegValueTypes @@ -88,8 +88,7 @@ class PrintKey(interfaces.plugins.PluginInterface): key_path_names.append(k.get_name()) except ( InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ): key_path_names.append("-") key_path = "\\".join([k for k in key_path_names]) @@ -117,8 +116,7 @@ class PrintKey(interfaces.plugins.PluginInterface): key_node.get_name() except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ) as excp: vollog.debug(excp) continue @@ -168,8 +166,7 @@ class PrintKey(interfaces.plugins.PluginInterface): key_node_name = node.get_name() except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ) as excp: vollog.debug(excp) key_node_name = renderers.UnreadableValue() @@ -196,8 +193,7 @@ class PrintKey(interfaces.plugins.PluginInterface): value_node_name = node.get_name() or "(Default)" except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ) as excp: vollog.debug(excp) value_node_name = renderers.UnreadableValue() @@ -206,7 +202,7 @@ class PrintKey(interfaces.plugins.PluginInterface): value_type = RegValueTypes(node.Type).name except ( exceptions.InvalidAddressException, - RegistryFormatException, + RegistryException, ) as excp: vollog.debug(excp) value_type = renderers.UnreadableValue() @@ -241,7 +237,7 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( ValueError, exceptions.InvalidAddressException, - RegistryFormatException, + RegistryException, ) as excp: vollog.debug(excp) value_data = renderers.UnreadableValue() @@ -283,13 +279,13 @@ class PrintKey(interfaces.plugins.PluginInterface): except ( exceptions.InvalidAddressException, KeyError, - RegistryFormatException, + RegistryException, ) as excp: if isinstance(excp, KeyError): vollog.debug( f"Key '{key}' not found in Hive at offset {hex(hive.hive_offset)}." ) - elif isinstance(excp, RegistryFormatException): + elif isinstance(excp, RegistryException): vollog.debug(excp) elif isinstance(excp, exceptions.InvalidAddressException): vollog.debug( diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index 738f230b7..ef51b91bf 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -15,8 +15,7 @@ from volatility3.framework.configuration import requirements from volatility3.framework.layers.physical import BufferDataLayer from volatility3.framework.layers.registry import ( RegistryHive, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ) from volatility3.framework.renderers import conversion, format_hints from volatility3.framework.symbols import intermed @@ -176,7 +175,7 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac "software\\microsoft\\windows\\currentversion\\explorer\\userassist", return_list=True, ) - except RegistryFormatException as e: + except RegistryException as e: vollog.warning( f"Error accessing UserAssist key in {hive_name} at {hive.hive_offset:#x}: {e}" ) @@ -246,8 +245,7 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac subkey_name = subkey.get_name() except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ): subkey_name = renderers.UnreadableValue() @@ -276,8 +274,7 @@ class UserAssist(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfac value_name = value.get_name() except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ): value_name = renderers.UnreadableValue() diff --git a/volatility3/framework/plugins/windows/scheduled_tasks.py b/volatility3/framework/plugins/windows/scheduled_tasks.py index dd821bb35..ba54e19ec 100644 --- a/volatility3/framework/plugins/windows/scheduled_tasks.py +++ b/volatility3/framework/plugins/windows/scheduled_tasks.py @@ -313,8 +313,7 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: break except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ): continue @@ -334,8 +333,7 @@ def _build_guid_name_map(key: reg_extensions.CM_KEY_NODE) -> Dict[str, str]: ) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryInvalidIndex, + registry.RegistryException, ) as excp: vollog.debug(f"Exception occurred while decoding id_str: {excp}") @@ -1221,14 +1219,14 @@ information about triggers, actions, run times, and creation times.""" task_key = software_hive.get_key( "Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks" ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): task_key = None try: task_tree = software_hive.get_key( "Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree" ) - except (KeyError, registry.RegistryFormatException): + except (KeyError, registry.RegistryException): task_tree = None return (task_key, task_tree) # type: ignore @@ -1243,8 +1241,7 @@ information about triggers, actions, run times, and creation times.""" name = str(value.get_name()) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryFormatException, + registry.RegistryException, ): continue @@ -1255,8 +1252,7 @@ information about triggers, actions, run times, and creation times.""" key_name = str(key.get_name()) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryFormatException, + registry.RegistryException, ): key_name = None @@ -1264,8 +1260,7 @@ information about triggers, actions, run times, and creation times.""" task_name = guid_mapping.get(key_name, renderers.NotAvailableValue()) except ( exceptions.InvalidAddressException, - registry.RegistryFormatException, - registry.RegistryFormatException, + registry.RegistryException, ): task_name = renderers.NotAvailableValue() diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index 915850574..80400ec5a 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -162,7 +162,7 @@ class SvcScan(interfaces.plugins.PluginInterface): except ( KeyError, exceptions.InvalidAddressException, - registry.RegistryFormatException, + registry.RegistryException, ): try: return cast( @@ -171,7 +171,7 @@ class SvcScan(interfaces.plugins.PluginInterface): except ( KeyError, exceptions.InvalidAddressException, - registry.RegistryFormatException, + registry.RegistryException, ): vollog.log( constants.LOGLEVEL_VVVV, diff --git a/volatility3/framework/symbols/windows/extensions/registry.py b/volatility3/framework/symbols/windows/extensions/registry.py index c6c2ee358..987f01ac1 100644 --- a/volatility3/framework/symbols/windows/extensions/registry.py +++ b/volatility3/framework/symbols/windows/extensions/registry.py @@ -9,9 +9,8 @@ from typing import Iterator, Optional, Union, cast from volatility3.framework import constants, exceptions, interfaces, objects from volatility3.framework.layers.registry import ( - RegistryFormatException, + RegistryException, RegistryHive, - RegistryInvalidIndex, ) vollog = logging.getLogger(__name__) @@ -103,7 +102,7 @@ class CMHIVE(objects.StructType): for attr in ["FileFullPath", "FileUserName", "HiveRootPath"]: with contextlib.suppress( - AttributeError, exceptions.InvalidAddressException, RegistryInvalidIndex + AttributeError, exceptions.InvalidAddressException, RegistryException ): name = getattr(self, attr) if name.Length > 0: @@ -201,8 +200,7 @@ class CM_KEY_NODE(objects.StructType): signature = node.cast("string", max_length=2, encoding="latin-1") except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ): return None @@ -231,8 +229,7 @@ class CM_KEY_NODE(objects.StructType): subnode = hive.get_node(subnode_offset) except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ): vollog.log( constants.LOGLEVEL_VVV, @@ -258,11 +255,7 @@ class CM_KEY_NODE(objects.StructType): if v != 0: try: node = hive.get_node(v) - except ( - RegistryInvalidIndex, - RegistryFormatException, - RegistryInvalidIndex, - ) as excp: + except (RegistryException,) as excp: vollog.debug(f"Invalid address {excp}") continue if isinstance(node, CM_KEY_VALUE): @@ -270,8 +263,7 @@ class CM_KEY_NODE(objects.StructType): except ( exceptions.InvalidAddressException, - RegistryFormatException, - RegistryInvalidIndex, + RegistryException, ) as excp: vollog.debug(f"Invalid address in get_values iteration: {excp}") return None @@ -361,7 +353,7 @@ class CM_KEY_VALUE(objects.StructType): offset=layer.get_cell(block_offset).vol.offset, length=amount, ) - except (exceptions.InvalidAddressException, RegistryInvalidIndex): + except (exceptions.InvalidAddressException, RegistryException): vollog.debug( f"Failed to read {amount:x} bytes of data, padding with {amount:x}" ) @@ -371,7 +363,7 @@ class CM_KEY_VALUE(objects.StructType): # but the length at the start could be negative so just adding 4 to jump past it try: data = layer.read(self.Data + 4, datalen) - except (exceptions.InvalidAddressException, RegistryInvalidIndex): + except (exceptions.InvalidAddressException, RegistryException): vollog.debug( f"Failed to read {datalen:x} bytes of data, returning {datalen:x} null bytes" ) diff --git a/volatility3/plugins/windows/registry/certificates.py b/volatility3/plugins/windows/registry/certificates.py index eea05548b..fd33d75a7 100644 --- a/volatility3/plugins/windows/registry/certificates.py +++ b/volatility3/plugins/windows/registry/certificates.py @@ -80,7 +80,7 @@ class Certificates(interfaces.plugins.PluginInterface): ]: with contextlib.suppress( KeyError, - registry.RegistryFormatException, + registry.RegistryException, exceptions.InvalidAddressException, ): # Walk it