Source code for winregrc.programscache

"""Windows Programs Cache information collector."""

import logging
import uuid

from dtfabric.runtime import data_maps as dtfabric_data_maps

import pyfwsi

from winregrc import data_format
from winregrc import errors
from winregrc import interface


[docs] class ProgramsCacheDataParser(data_format.BinaryDataFormat): """Programs Cache data parser.""" _DEFINITION_FILE = "programscache.yaml" def _DebugPrintEntryFooter(self, entry_footer): """Prints entry footer value debug information. Args: entry_footer (programscache_entry_footer): entry footer. """ self._DebugPrintValue("Sentinel", f"0x{entry_footer.sentinel:02x}") def _DebugPrintEntryHeader(self, entry_header): """Prints entry header value debug information. Args: entry_header (programscache_entry_header): entry header. """ self._DebugPrintDecimalValue("Entry data size", entry_header.data_size) def _DebugPrintHeader(self, header): """Prints header value debug information. Args: header (programscache_header): header. """ self._DebugPrintDecimalValue("Format version", header.format_version) def _DebugPrintShellItem(self, shell_item): """Prints shell item value debug information. Args: shell_item (pyfwsi.shell_item): shell item. """ self._DebugPrintValue("Shell item class type", f"0x{shell_item.class_type:02x}") value_string = getattr(shell_item, "name", "") self._DebugPrintValue("Shell item name", value_string) def _ParseEntryFooter(self, value_data, value_data_offset): """Parses an entry footer from the value data. Args: value_data (bytes): value data. value_data_offset (int): offset of the entry footer relative to the start of the value data. Returns: tuple: containing: programscache_entry_footer: entry footer. int: entry footer data size. Raises: ParseError: if the entry footer could not be parsed. """ data_type_map = self._GetDataTypeMap("programscache_entry_footer") data_size = data_type_map.GetSizeHint() if self._debug: self._DebugPrintData( "Entry footer data", value_data[value_data_offset : value_data_offset + data_size], ) try: entry_footer = self._ReadStructureFromByteStream( value_data[value_data_offset:], value_data_offset, data_type_map, "entry footer", ) except (ValueError, errors.ParseError) as exception: raise errors.ParseError( f"Unable to parse entry footer value with error: {exception!s}" ) if self._debug: self._DebugPrintEntryFooter(entry_footer) return entry_footer, data_size def _ParseHeader(self, value_data): """Parses a header from the value data. Args: value_data (bytes): value data. Returns: tuple: containing: programscache_header: header. int: header data size. Raises: ParseError: if the header could not be parsed. """ data_type_map = self._GetDataTypeMap("programscache_header") data_size = data_type_map.GetSizeHint() if self._debug: self._DebugPrintData("Header data", value_data[:data_size]) try: header = self._ReadStructureFromByteStream( value_data, 0, data_type_map, "header" ) except (ValueError, errors.ParseError) as exception: raise errors.ParseError( f"Unable to parse header value with error: {exception!s}" ) if self._debug: self._DebugPrintHeader(header) if header.format_version not in (1, 9, 12, 19): raise errors.ParseError("Unsupported format.") return header, data_size
[docs] def Parse(self, value_data): """Parses the value data. Args: value_data (bytes): value data. Raises: ParseError: if the value data could not be parsed. """ if self._debug: self._DebugPrintData("Value data", value_data) header, value_data_offset = self._ParseHeader(value_data) if header.format_version == 1: value_data_offset += 4 elif header.format_version == 9: data_type_map = self._GetDataTypeMap("programscache_header9") context = dtfabric_data_maps.DataTypeMapContext() try: header9 = self._ReadStructureFromByteStream( value_data[value_data_offset:], value_data_offset, data_type_map, "header9", context=context, ) except (ValueError, errors.ParseError) as exception: raise errors.ParseError( f"Unable to parse header9 value with error: {exception!s}" ) value_data_offset += context.byte_size if self._debug: self._DebugPrintValue("Unknown1", f"0x{header9.unknown1:08x}") elif header.format_version in (12, 19): uuid_object = uuid.UUID(bytes_le=value_data[4:20]) value_data_offset += 16 if self._debug: self._DebugPrintValue("Known folder identifier", f"{uuid_object!s}") sentinel = 0 if header.format_version != 9: entry_footer, data_size = self._ParseEntryFooter( value_data, value_data_offset ) value_data_offset += data_size sentinel = entry_footer.sentinel if self._debug: self._DebugPrintText("\n") value_data_size = len(value_data) while sentinel in (0, 1): if value_data_offset >= value_data_size: break data_type_map = self._GetDataTypeMap("programscache_entry_header") context = dtfabric_data_maps.DataTypeMapContext() try: entry_header = self._ReadStructureFromByteStream( value_data[value_data_offset:], value_data_offset, data_type_map, "entry header", context=context, ) except (ValueError, errors.ParseError) as exception: raise errors.ParseError( f"Unable to parse entry header value with error: {exception!s}" ) if self._debug: self._DebugPrintValue("Entry data offset", f"0x{value_data_offset:08x}") self._DebugPrintEntryHeader(entry_header) value_data_offset += context.byte_size entry_data_size = entry_header.data_size shell_item_list = pyfwsi.item_list() shell_item_list.copy_from_byte_stream(value_data[value_data_offset:]) for shell_item in iter(shell_item_list.items): if self._debug: self._DebugPrintShellItem(shell_item) value_data_offset += entry_data_size entry_footer, data_size = self._ParseEntryFooter( value_data, value_data_offset ) value_data_offset += data_size if self._debug: self._DebugPrintText("\n") if entry_footer.sentinel == 2 and value_data_offset < value_data_size: # TODO: determine the logic to this value. while ord(value_data[value_data_offset]) != 0x00: value_data_offset += 1 value_data_offset += 7 entry_footer, data_size = self._ParseEntryFooter( value_data, value_data_offset ) value_data_offset += data_size if self._debug: self._DebugPrintText("\n") if value_data_offset < value_data_size: self._DebugPrintValue("Trailing data offset", f"0x{value_data_offset:08x}") self._DebugPrintData("Trailing data:", value_data[value_data_offset:])
[docs] class ProgramsCacheCollector(interface.WindowsRegistryKeyCollector): """Windows program cache collector.""" _STARTPAGE_KEY_PATH = ( "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\" "Explorer\\StartPage" ) _STARTPAGE2_KEY_PATH = ( "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\" "Explorer\\StartPage2" )
[docs] def __init__(self, debug=False, output_writer=None): """Initializes a Windows program cache collector. Args: debug (Optional[bool]): True if debug information should be printed. output_writer (Optional[OutputWriter]): output writer. """ super().__init__(debug=debug) self._parser = ProgramsCacheDataParser(debug=debug, output_writer=output_writer)
def _CollectProgramsCacheFromValue(self, registry, key_path, value_name): """Collects Programs Cache from a Windows Registry value. Args: registry (dfwinreg.WinRegistry): Windows Registry. key_path (str): path of the Programs Cache key. value_name (str): name of the Programs Cache value. Returns: bool: True if the Programs Cache information key was found, False if not. """ startpage_key = registry.GetKeyByPath(key_path) if not startpage_key: return False value = startpage_key.GetValueByName(value_name) if not value: logging.warning(f"Missing {value_name:s} value in key: {key_path:s}") return True self._parser.Parse(value.data) return True
[docs] def Collect(self, registry): # pylint: disable=arguments-differ """Collects the Programs Cache information. Args: registry (dfwinreg.WinRegistry): Windows Registry. Returns: bool: True if the Programs Cache information key was found, False if not. """ result = False if self._CollectProgramsCacheFromValue( registry, self._STARTPAGE_KEY_PATH, "ProgramsCache" ): result = True if self._CollectProgramsCacheFromValue( registry, self._STARTPAGE2_KEY_PATH, "ProgramsCache" ): result = True if self._CollectProgramsCacheFromValue( registry, self._STARTPAGE2_KEY_PATH, "ProgramsCacheSMP" ): result = True if self._CollectProgramsCacheFromValue( registry, self._STARTPAGE2_KEY_PATH, "ProgramsCacheTBP" ): result = True return result