Source code for winregrc.programscache

# -*- coding: utf-8 -*-
"""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(ProgramsCacheCollector, self).__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