def _decode(self, obj, context): el = [ int(construct.Byte('foo').parse(obj[0:1])), (int(construct.ULInt32('foo').parse(obj[1:5])) + (int(construct.ULInt16('foo').parse(obj[5:7])) << 32))] auth_sub_count = construct.Byte('foo').parse(obj[7:8]) for i in range(0, auth_sub_count): el.append(construct.ULInt32('foo').parse(obj[8+i*4:])) return 'S-' + '-'.join([str(x) for x in el])
class AppCompatCacheKeyParser(object): """Class that parses the Application Compatibility Cache data.""" FORMAT_TYPE_2000 = 1 FORMAT_TYPE_XP = 2 FORMAT_TYPE_2003 = 3 FORMAT_TYPE_VISTA = 4 FORMAT_TYPE_7 = 5 FORMAT_TYPE_8 = 6 # AppCompatCache format signature used in Windows XP. _HEADER_SIGNATURE_XP = 0xdeadbeef # AppCompatCache format used in Windows XP. _HEADER_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_header_xp', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.ULInt32('unknown1'), construct.ULInt32('unknown2'), construct.Padding(384)) _CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_xp_32bit', construct.Array(528, construct.Byte('path')), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size'), construct.ULInt64('last_update_time')) # AppCompatCache format signature used in Windows 2003, Vista and 2008. _HEADER_SIGNATURE_2003 = 0xbadc0ffe # AppCompatCache format used in Windows 2003. _HEADER_2003_STRUCT = construct.Struct( 'appcompatcache_header_2003', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries')) _CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) _CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) # AppCompatCache format used in Windows Vista and 2008. _CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) _CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) # AppCompatCache format signature used in Windows 7 and 2008 R2. _HEADER_SIGNATURE_7 = 0xbadc0fee # AppCompatCache format used in Windows 7 and 2008 R2. _HEADER_7_STRUCT = construct.Struct( 'appcompatcache_header_7', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.Padding(120)) _CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt32('data_size'), construct.ULInt32('data_offset')) _CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt64('data_size'), construct.ULInt64('data_offset')) # AppCompatCache format used in Windows 8.0 and 8.1. _HEADER_SIGNATURE_8 = 0x00000080 _HEADER_8_STRUCT = construct.Struct('appcompatcache_header_8', construct.ULInt32('signature'), construct.Padding(124)) _CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct( 'appcompatcache_cached_entry_header_8', construct.ULInt32('signature'), construct.ULInt32('unknown1'), construct.ULInt32('cached_entry_data_size'), construct.ULInt16('path_size')) # AppCompatCache format used in Windows 8.0. _CACHED_ENTRY_SIGNATURE_8_0 = '00ts' # AppCompatCache format used in Windows 8.1. _CACHED_ENTRY_SIGNATURE_8_1 = '10ts' def CheckSignature(self, value_data): """Parses the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32('signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self.FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self.FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self.FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self.FORMAT_TYPE_8 def ParseHeader(self, format_type, value_data): """Parses the header. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. Returns: A header object (instance of AppCompatCacheHeader). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) # TODO: change to collections.namedtuple or use __slots__ if the overhead # of a regular object becomes a problem. header_object = AppCompatCacheHeader() if format_type == self.FORMAT_TYPE_XP: header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof() header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_2003: header_object.header_size = self._HEADER_2003_STRUCT.sizeof() header_struct = self._HEADER_2003_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_VISTA: header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof() header_struct = self._HEADER_VISTA_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_7: header_object.header_size = self._HEADER_7_STRUCT.sizeof() header_struct = self._HEADER_7_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_8: header_object.header_size = self._HEADER_8_STRUCT.sizeof() header_struct = self._HEADER_8_STRUCT.parse(value_data) if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: header_object.number_of_cached_entries = header_struct.get( 'number_of_cached_entries') return header_object def DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self.FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = construct.ULInt16('path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16('maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32('path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32('path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'Path size value out of bounds.') return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'Unsupported path size values.') return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_8: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size def ParseCachedEntry(self, format_type, value_data, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] cached_entry_struct = None if format_type == self.FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_8: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError(u'Unsupported cache entry signature') if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( 'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError(u'Unsupported cache entry size: {0:d}'.format( cached_entry_size)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self.FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[0:string_size]) elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get('path_size') path_offset = cached_entry_struct.get('path_offset') elif format_type == self.FORMAT_TYPE_8: path_size = cached_entry_struct.get('path_size') cached_entry_data_offset = 14 + path_size cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[14:cached_entry_data_offset]) remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( 'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( 'shim_flags').parse(remaining_data[4:8]) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( 'last_modification_time') elif format_type == self.FORMAT_TYPE_8: cached_entry_object.last_modification_time = construct.ULInt64( 'last_modification_time').parse(remaining_data[0:8]) if format_type in [self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( 'file_size') elif format_type in [self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( 'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( 'shim_flags') if format_type == self.FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( 'last_update_time') if format_type == self.FORMAT_TYPE_7: data_offset = cached_entry_struct.get('data_offset') data_size = cached_entry_struct.get('data_size') elif format_type == self.FORMAT_TYPE_8: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32('data_size').parse( remaining_data[8:12]) if path_offset > 0 and path_size > 0: path_size += path_offset cached_entry_object.path = binary.Ut16StreamCopyToString( value_data[path_offset:path_size]) if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] return cached_entry_object
'CREDENTIAL_DEC_BLOCK_ENC', construct.ULInt32('empty'), construct.Rename('block_name', UNICODE_STRING), construct.ULInt32('size'), construct.Bytes('raw_data', lambda ctx: ctx.size)) CREDENTIAL_DECRYPTED = construct.Struct( 'CREDENTIAL_DECRYPTED', construct.Rename('header', CREDENTIAL_DEC_HEADER), construct.Rename('main', CREDENTIAL_DEC_MAIN), construct.If( lambda ctx: ctx.header.unk_type == 2, construct.Array(lambda ctx: ctx.header.unk_blocks, CREDENTIAL_DEC_BLOCK_ENC))) # VAULT file structs. VAULT_ATTRIBUTE_ENCRYPTED = construct.Struct( 'VAULT_ATTRIBUTE_ENCRYPTED', construct.Byte('has_iv'), construct.IfThenElse( '', lambda ctx: ctx.has_iv, construct.Embed( construct.Struct( 'encrypted', construct.ULInt32('iv_size'), construct.Bytes('iv', lambda ctx: ctx.iv_size), construct.Bytes('data', lambda ctx: ctx.size - 1 - 4 - ctx.iv_size))), construct.Embed( construct.Struct('encrypted', construct.Bytes('data', lambda ctx: ctx.size - 1))))) VAULT_ATTRIBUTE = construct.Struct( 'VAULT_ATTRIBUTE',
PAGE_CFG = cst.BitStruct( 'page_cfg', cst.Magic('\x01'), cst.Flag('background_on'), cst.Flag('non_english'), cst.Flag('autocenter'), cst.Flag('bold_joins_78'), cst.Flag('bold_joins_56'), cst.Flag('bold_joins_34'), cst.Flag('bold_joins_12'), ) CMD_SEQ = cst.Sequence( '_cmd', cst.Magic('\x1c'), cst.Enum(cst.Byte('cmd'), FLASH='F', ENLARGE='E', RED='R', GREEN='G', YELLOW='Y', MULTICOLOUR='M', DEFAULT='D')) PAGE = cst.Struct('page', PAGE_IDX, cst.Embed(TEMPO), cst.Embed(PAGE_FUNC), cst.Embed(PAGE_CFG), cst.CString('body', terminators='\x04')) DATETIME_BODY = cst.Struct( 'datetime_page', cst.Const(PAGE_IDX, '000'), )
class AppCompatCachePlugin(interface.WindowsRegistryPlugin): """Class that parses the Application Compatibility Cache Registry data.""" NAME = u'appcompatcache' DESCRIPTION = u'Parser for Application Compatibility Cache Registry data.' FILTERS = frozenset([ interface.WindowsRegistryKeyPathFilter( u'HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\' u'Session Manager\\AppCompatibility'), interface.WindowsRegistryKeyPathFilter( u'HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\' u'Session Manager\\AppCompatCache') ]) URLS = [(u'https://github.com/libyal/winreg-kb/blob/master/documentation/' u'Application%20Compatibility%20Cache%20key.asciidoc')] _FORMAT_TYPE_2000 = 1 _FORMAT_TYPE_XP = 2 _FORMAT_TYPE_2003 = 3 _FORMAT_TYPE_VISTA = 4 _FORMAT_TYPE_7 = 5 _FORMAT_TYPE_8 = 6 _FORMAT_TYPE_10 = 7 # AppCompatCache format signature used in Windows XP. _HEADER_SIGNATURE_XP = 0xdeadbeef # AppCompatCache format used in Windows XP. _HEADER_XP_32BIT_STRUCT = construct.Struct( u'appcompatcache_header_xp', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries'), construct.ULInt32(u'unknown1'), construct.ULInt32(u'unknown2'), construct.Padding(384)) _CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_xp_32bit', construct.Array(528, construct.Byte(u'path')), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size'), construct.ULInt64(u'last_update_time')) # AppCompatCache format signature used in Windows 2003, Vista and 2008. _HEADER_SIGNATURE_2003 = 0xbadc0ffe # AppCompatCache format used in Windows 2003. _HEADER_2003_STRUCT = construct.Struct( u'appcompatcache_header_2003', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries')) _CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_2003_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size')) _CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_2003_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size')) # AppCompatCache format used in Windows Vista and 2008. _CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_vista_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags')) _CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_vista_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags')) # AppCompatCache format signature used in Windows 7 and 2008 R2. _HEADER_SIGNATURE_7 = 0xbadc0fee # AppCompatCache format used in Windows 7 and 2008 R2. _HEADER_7_STRUCT = construct.Struct( u'appcompatcache_header_7', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries'), construct.Padding(120)) _CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_7_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags'), construct.ULInt32(u'data_size'), construct.ULInt32(u'data_offset')) _CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_7_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags'), construct.ULInt64(u'data_size'), construct.ULInt64(u'data_offset')) # AppCompatCache format used in Windows 8.0 and 8.1. _HEADER_SIGNATURE_8 = 0x00000080 _HEADER_8_STRUCT = construct.Struct(u'appcompatcache_header_8', construct.ULInt32(u'signature'), construct.Padding(124)) _CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct( u'appcompatcache_cached_entry_header_8', construct.ULInt32(u'signature'), construct.ULInt32(u'unknown1'), construct.ULInt32(u'cached_entry_data_size'), construct.ULInt16(u'path_size')) # AppCompatCache format used in Windows 8.0. _CACHED_ENTRY_SIGNATURE_8_0 = b'00ts' # AppCompatCache format used in Windows 8.1. _CACHED_ENTRY_SIGNATURE_8_1 = b'10ts' # AppCompatCache format used in Windows 10 _HEADER_SIGNATURE_10 = 0x00000030 _HEADER_10_STRUCT = construct.Struct( u'appcompatcache_header_8', construct.ULInt32(u'signature'), construct.ULInt32(u'unknown1'), construct.Padding(28), construct.ULInt32(u'number_of_cached_entries'), construct.Padding(8)) def _CheckSignature(self, value_data): """Parses and validates the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32(u'signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self._FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self._FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self._FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self._FORMAT_TYPE_8 elif signature == self._HEADER_SIGNATURE_10: # Windows 10 uses the same cache entry signature as Windows 8.1 if value_data[signature:signature + 4] in [self._CACHED_ENTRY_SIGNATURE_8_1]: return self._FORMAT_TYPE_10 def _DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Determines the size of a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8, self._FORMAT_TYPE_10 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self._FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: path_size = construct.ULInt16(u'path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16(u'maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32(u'path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32(u'path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'[{0:s}] Path size value out of bounds.'.format( self.NAME)) return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'[{0:s}] Unsupported path size values.'.format( self.NAME)) return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self._FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self._FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type in [self._FORMAT_TYPE_8, self._FORMAT_TYPE_10]: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size def _ParseHeader(self, format_type, value_data): """Parses the header. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. Returns: A header object (instance of AppCompatCacheHeader). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8, self._FORMAT_TYPE_10 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) # TODO: change to collections.namedtuple or use __slots__ if the overhead # of a regular object becomes a problem. header_object = AppCompatCacheHeader() if format_type == self._FORMAT_TYPE_XP: header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof() header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_2003: header_object.header_size = self._HEADER_2003_STRUCT.sizeof() header_struct = self._HEADER_2003_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_VISTA: header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof() header_struct = self._HEADER_VISTA_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_7: header_object.header_size = self._HEADER_7_STRUCT.sizeof() header_struct = self._HEADER_7_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_8: header_object.header_size = self._HEADER_8_STRUCT.sizeof() header_struct = self._HEADER_8_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_10: header_object.header_size = self._HEADER_10_STRUCT.sizeof() header_struct = self._HEADER_10_STRUCT.parse(value_data) if format_type in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_10 ]: header_object.number_of_cached_entries = header_struct.get( u'number_of_cached_entries') return header_object def _ParseCachedEntry(self, format_type, value_data, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8, self._FORMAT_TYPE_10 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] cached_entry_struct = None if format_type == self._FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type in [self._FORMAT_TYPE_8, self._FORMAT_TYPE_10]: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError( u'[{0:s}] Unsupported cache entry signature'.format( self.NAME)) if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( u'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError( u'[{0:s}] Unsupported cache entry size: {1:d}'.format( self.NAME, cached_entry_size)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self._FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = binary.UTF16StreamCopyToString( cached_entry_data[0:string_size]) elif format_type in [ self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get(u'path_size') path_offset = cached_entry_struct.get(u'path_offset') elif format_type in [self._FORMAT_TYPE_8, self._FORMAT_TYPE_10]: path_size = cached_entry_struct.get(u'path_size') cached_entry_data_offset = 14 + path_size cached_entry_object.path = binary.UTF16StreamCopyToString( cached_entry_data[14:cached_entry_data_offset]) if format_type == self._FORMAT_TYPE_8: remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( u'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( u'shim_flags').parse(remaining_data[4:8]) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[ 0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( u'last_modification_time') elif format_type in [self._FORMAT_TYPE_8, self._FORMAT_TYPE_10]: cached_entry_object.last_modification_time = construct.ULInt64( u'last_modification_time').parse(remaining_data[0:8]) if format_type in [self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( u'file_size') elif format_type in [self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( u'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( u'shim_flags') if format_type == self._FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( u'last_update_time') if format_type == self._FORMAT_TYPE_7: data_offset = cached_entry_struct.get(u'data_offset') data_size = cached_entry_struct.get(u'data_size') elif format_type in [self._FORMAT_TYPE_8, self._FORMAT_TYPE_10]: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32(u'data_size').parse( remaining_data[8:12]) if path_offset > 0 and path_size > 0: path_size += path_offset cached_entry_object.path = binary.UTF16StreamCopyToString( value_data[path_offset:path_size]) if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] return cached_entry_object def GetEntries(self, parser_mediator, registry_key, **kwargs): """Extracts event objects from a Application Compatibility Cache key. Args: parser_mediator: A parser mediator object (instance of ParserMediator). registry_key: A Windows Registry key (instance of dfwinreg.WinRegistryKey). """ value = registry_key.GetValueByName(u'AppCompatCache') if not value: return value_data = value.data value_data_size = len(value.data) format_type = self._CheckSignature(value_data) if not format_type: parser_mediator.ProduceParseError( u'Unsupported signature in AppCompatCache key: {0:s}'.format( registry_key.path)) return header_object = self._ParseHeader(format_type, value_data) # On Windows Vista and 2008 when the cache is empty it will # only consist of the header. if value_data_size <= header_object.header_size: return cached_entry_offset = header_object.header_size cached_entry_size = self._DetermineCacheEntrySize( format_type, value_data, cached_entry_offset) if not cached_entry_size: parser_mediator.ProduceParseError(( u'Unsupported cached entry size at offset {0:d} in AppCompatCache ' u'key: {1:s}').format(cached_entry_offset, registry_key.path)) return cached_entry_index = 0 while cached_entry_offset < value_data_size: cached_entry_object = self._ParseCachedEntry( format_type, value_data, cached_entry_offset, cached_entry_size) if cached_entry_object.last_modification_time is not None: # TODO: refactor to file modification event. event_object = AppCompatCacheEvent( cached_entry_object.last_modification_time, u'File Last Modification Time', registry_key.path, cached_entry_index + 1, cached_entry_object.path, cached_entry_offset) parser_mediator.ProduceEvent(event_object) if cached_entry_object.last_update_time is not None: # TODO: refactor to process run event. event_object = AppCompatCacheEvent( cached_entry_object.last_update_time, eventdata.EventTimestamp.LAST_RUNTIME, registry_key.path, cached_entry_index + 1, cached_entry_object.path, cached_entry_offset) parser_mediator.ProduceEvent(event_object) cached_entry_offset += cached_entry_object.cached_entry_size cached_entry_index += 1 if (header_object.number_of_cached_entries != 0 and cached_entry_index >= header_object.number_of_cached_entries): break
class ExplorerProgramsCachePlugin(interface.WindowsRegistryPlugin): """Class that parses the Explorer ProgramsCache Registry data.""" NAME = 'explorer_programscache' DESCRIPTION = 'Parser for Explorer ProgramsCache Registry data.' FILTERS = frozenset([ interface.WindowsRegistryKeyPathFilter( 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\' 'Explorer\\StartPage'), interface.WindowsRegistryKeyPathFilter( 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\' 'Explorer\\StartPage2') ]) URLS = [('https://github.com/libyal/winreg-kb/blob/master/documentation/' 'Programs%20Cache%20values.asciidoc')] _HEADER_STRUCT = construct.Struct('programscache_header', construct.ULInt32('format_version')) _ENTRY_HEADER_STRUCT = construct.Struct('programscache_entry_header', construct.ULInt32('data_size')) _ENTRY_FOOTER_STRUCT = construct.Struct('programscache_entry_footer', construct.Byte('sentinel')) def _ParseValueData(self, parser_mediator, registry_key, registry_value): """Extracts event objects from a Explorer ProgramsCache value data. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. registry_key (dfwinreg.WinRegistryKey): Windows Registry key. registry_value (dfwinreg.WinRegistryValue): Windows Registry value. """ value_data = registry_value.data if len(value_data) < 4: return try: header_struct = self._HEADER_STRUCT.parse(value_data) except construct.FieldError as exception: parser_mediator.ProduceExtractionError( 'unable to parse header with error: {0!s}'.format(exception)) return format_version = header_struct.get('format_version') if format_version not in (0x01, 0x09, 0x0c, 0x13): parser_mediator.ProduceExtractionError( 'unsupported format version: 0x{0:08x}'.format(format_version)) return if format_version == 0x01: value_data_offset = 8 elif format_version == 0x09: value_data_offset = 6 else: # TODO: get known folder identifier? value_data_offset = 20 if format_version == 0x09: sentinel = 0 else: try: entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceExtractionError( ('unable to parse sentinel at offset: 0x{0:08x} ' 'with error: {1!s}').format(value_data_offset, exception)) return value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get('sentinel') link_targets = [] while sentinel in (0x00, 0x01): try: entry_header_struct = self._ENTRY_HEADER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceExtractionError( ('unable to parse entry header at offset: 0x{0:08x} ' 'with error: {1!s}').format(value_data_offset, exception)) break value_data_offset += self._ENTRY_HEADER_STRUCT.sizeof() entry_data_size = entry_header_struct.get('data_size') display_name = '{0:s} {1:s}'.format(registry_key.path, registry_value.name) shell_items_parser = shell_items.ShellItemsParser(display_name) shell_items_parser.ParseByteStream( parser_mediator, value_data[value_data_offset:], codepage=parser_mediator.codepage) link_target = shell_items_parser.CopyToPath() link_targets.append(link_target) value_data_offset += entry_data_size try: entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceExtractionError( ('unable to parse entry footer at offset: 0x{0:08x} ' 'with error: {1!s}').format(value_data_offset, exception)) break value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get('sentinel') # TODO: recover remaining items. event_data = windows_events.WindowsRegistryListEventData() event_data.key_path = registry_key.path event_data.list_name = registry_value.name event_data.list_values = ' '.join([ '{0:d}: {1:s}'.format(index, link_target) for index, link_target in enumerate(link_targets) ]) event_data.value_name = registry_value.name event = time_events.DateTimeValuesEvent( registry_key.last_written_time, definitions.TIME_DESCRIPTION_WRITTEN) parser_mediator.ProduceEventWithEventData(event, event_data) def ExtractEvents(self, parser_mediator, registry_key, **kwargs): """Extracts events from a Windows Registry key. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. registry_key (dfwinreg.WinRegistryKey): Windows Registry key. """ registry_value = registry_key.GetValueByName('ProgramsCache') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) registry_value = registry_key.GetValueByName('ProgramsCacheSMP') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) registry_value = registry_key.GetValueByName('ProgramsCacheTBP') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) values_dict = {} for registry_value in registry_key.GetValues(): # Ignore the default value. if not registry_value.name or registry_value.name in ( 'ProgramsCache', 'ProgramsCacheSMP', 'ProgramsCacheTBP'): continue # Ignore any value that is empty or that does not contain a string. if not registry_value.data or not registry_value.DataIsString(): continue values_dict[registry_value.name] = registry_value.GetDataAsObject() event_data = windows_events.WindowsRegistryEventData() event_data.key_path = registry_key.path event_data.offset = registry_key.offset event_data.regvalue = values_dict event = time_events.DateTimeValuesEvent( registry_key.last_written_time, definitions.TIME_DESCRIPTION_WRITTEN) parser_mediator.ProduceEventWithEventData(event, event_data)
class AutomaticDestinationsOlecfPlugin(interface.OlecfPlugin): """Plugin that parses an .automaticDestinations-ms OLECF file.""" NAME = u'olecf_automatic_destinations' DESCRIPTION = u'Parser for *.automaticDestinations-ms OLECF files.' REQUIRED_ITEMS = frozenset([u'DestList']) _RE_LNK_ITEM_NAME = re.compile(r'^[1-9a-f][0-9a-f]*$') # We cannot use the parser registry here since winlnk could be disabled. # TODO: see if there is a more elegant solution for this. _WINLNK_PARSER = winlnk.WinLnkParser() _DEST_LIST_STREAM_HEADER = construct.Struct( u'dest_list_stream_header', construct.ULInt32(u'unknown1'), construct.ULInt32(u'number_of_entries'), construct.ULInt32(u'number_of_pinned_entries'), construct.LFloat32(u'unknown2'), construct.ULInt32(u'last_entry_number'), construct.Padding(4), construct.ULInt32(u'last_revision_number'), construct.Padding(4)) _DEST_LIST_STREAM_HEADER_SIZE = _DEST_LIST_STREAM_HEADER.sizeof() # Using Construct's utf-16 encoding here will create strings with their # end-of-string characters exposed. Instead the strings are read as # binary strings and converted using ReadUtf16(). _DEST_LIST_STREAM_ENTRY = construct.Struct( u'dest_list_stream_entry', construct.ULInt64(u'unknown1'), construct.Array(16, construct.Byte(u'droid_volume_identifier')), construct.Array(16, construct.Byte(u'droid_file_identifier')), construct.Array(16, construct.Byte(u'birth_droid_volume_identifier')), construct.Array(16, construct.Byte(u'birth_droid_file_identifier')), construct.String(u'hostname', 16), construct.ULInt32(u'entry_number'), construct.ULInt32(u'unknown2'), construct.LFloat32(u'unknown3'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'pin_status'), construct.ULInt16(u'path_size'), construct.String(u'path', lambda ctx: ctx.path_size * 2)) def ParseDestList(self, parser_mediator, olecf_item): """Parses the DestList OLECF item. Args: parser_mediator: A parser mediator object (instance of ParserMediator). olecf_item: An OLECF item (instance of pyolecf.item). """ try: header = self._DEST_LIST_STREAM_HEADER.parse_stream(olecf_item) except (IOError, construct.FieldError) as exception: raise errors.UnableToParseFile( u'Unable to parse DestList header with error: {0:s}'.format( exception)) if header.unknown1 != 1: # TODO: add format debugging notes to parser mediator. logging.debug(u'[{0:s}] unknown1 value: {1:d}.'.format( self.NAME, header.unknown1)) entry_offset = olecf_item.get_offset() while entry_offset < olecf_item.size: try: entry = self._DEST_LIST_STREAM_ENTRY.parse_stream(olecf_item) except (IOError, construct.FieldError) as exception: raise errors.UnableToParseFile( u'Unable to parse DestList entry with error: {0:s}'.format( exception)) if not entry: break event_object = AutomaticDestinationsDestListEntryEvent( entry.last_modification_time, eventdata.EventTimestamp.MODIFICATION_TIME, entry_offset, entry) parser_mediator.ProduceEvent(event_object) entry_offset = olecf_item.get_offset() def ParseItems(self, parser_mediator, file_entry=None, root_item=None, **unused_kwargs): """Parses OLECF items. Args: parser_mediator: A parser mediator object (instance of ParserMediator). file_entry: Optional file entry object (instance of dfvfs.FileEntry). The default is None. root_item: Optional root item of the OLECF file. The default is None. Raises: ValueError: If the root_item is not set. """ if root_item is None: raise ValueError(u'Root item not set.') for item in root_item.sub_items: if item.name == u'DestList': self.ParseDestList(parser_mediator, item) elif self._RE_LNK_ITEM_NAME.match(item.name): if file_entry: display_name = u'{0:s} # {1:s}'.format( parser_mediator.GetDisplayName(), item.name) else: display_name = u'# {0:s}'.format(item.name) self._WINLNK_PARSER.UpdateChainAndParseFileObject( parser_mediator, item, display_name=display_name)
class ProgramsCacheDataParser(object): """Class that parses the Programs Cache value data.""" _HEADER_STRUCT = construct.Struct(u'programscache_header', construct.ULInt32(u'format_version')) _HEADER_9_STRUCT = construct.Struct(u'programscache_header_9', construct.ULInt16(u'unknown2')) _ENTRY_HEADER_STRUCT = construct.Struct(u'programscache_entry_header', construct.ULInt32(u'data_size')) _ENTRY_FOOTER_STRUCT = construct.Struct(u'programscache_entry_footer', construct.Byte(u'sentinel')) def __init__(self, debug=False): """Initializes the parser object. Args: debug: optional boolean value to indicate if debug information should be printed. """ super(ProgramsCacheDataParser, self).__init__() self._debug = debug def Parse(self, value_data, value_data_size): """Parses the value data. Args: value_data: a binary string containing the value data. value_data_size: the size of the value data. Returns: TODO Raises: RuntimeError: if the format is not supported. """ header_struct = self._HEADER_STRUCT.parse(value_data) value_data_offset = self._HEADER_STRUCT.sizeof() format_version = header_struct.get(u'format_version') if self._debug: print(u'Format version\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( format_version)) if format_version == 0x01: value_data_offset += 4 elif format_version == 0x09: header_struct = self._HEADER_9_STRUCT.parse(value_data) value_data_offset += self._HEADER_9_STRUCT.sizeof() if self._debug: print(u'Unknown2\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( header_struct.get(u'unknown2'))) elif format_version in [0x0c, 0x13]: uuid_object = uuid.UUID(bytes_le=value_data[4:20]) value_data_offset += 16 if self._debug: print(u'Known folder identifier\t\t\t\t\t\t\t: {0!s}'.format( uuid_object)) else: raise RuntimeError(u'Unsupported format.') if format_version == 0x09: sentinel = 0 else: entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get(u'sentinel') if self._debug: print(u'Sentinel\t\t\t\t\t\t\t\t: 0x{0:02x}'.format(sentinel)) if self._debug: print(u'') while sentinel in [0x00, 0x01]: entry_header_struct = self._ENTRY_HEADER_STRUCT.parse( value_data[value_data_offset:]) value_data_offset += self._ENTRY_HEADER_STRUCT.sizeof() entry_data_size = entry_header_struct.get(u'data_size') if self._debug: print(u'Entry data offset\t\t\t\t\t\t\t: 0x{0:08x}'.format( value_data_offset)) print(u'Entry data size\t\t\t\t\t\t\t\t: {0:d}'.format( entry_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: print(u'Shell item: 0x{0:02x}'.format( shell_item.class_type)) print(u'Shell item: {0:s}'.format( getattr(shell_item, u'name', u''))) value_data_offset += entry_data_size entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get(u'sentinel') if self._debug: print(u'Sentinel\t\t\t\t\t\t\t\t: 0x{0:02x}'.format(sentinel)) print(u'') if sentinel == 0x02 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_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get(u'sentinel') if self._debug: print(u'Sentinel\t\t\t\t\t\t\t\t: 0x{0:02x}'.format( sentinel)) print(u'') if value_data_offset < value_data_size: print(u'Trailing data:') print(u'Trailing data offset\t\t\t\t\t\t\t: 0x{0:08x}'.format( value_data_offset)) print(hexdump.Hexdump(value_data[value_data_offset:]))
def __init__(self, name): construct.Adapter.__init__( self, construct.RepeatUntil(lambda obj, ctx: obj == 1, construct.Byte(name)))
PAGE_CFG = cst.BitStruct( 'page_cfg', cst.Magic('\x01'), cst.Flag('background_on'), cst.Flag('non_english'), cst.Flag('autocenter'), cst.Flag('bold_joins_78'), cst.Flag('bold_joins_56'), cst.Flag('bold_joins_34'), cst.Flag('bold_joins_12'), ) CMD_SEQ = cst.Struct( '_cmd', cst.Magic('\x1c'), cst.Enum(cst.Byte('cmd'), FLASH=70, ENLARGE=69, DEFAULT=68)) PAGE = cst.Struct('page', PAGE_IDX, cst.Embed(TEMPO), cst.Embed(PAGE_FUNC), cst.Embed(PAGE_CFG), cst.Embed(CMD_SEQ), cst.CString('body', terminators='\x04')) DATETIME_BODY = cst.Struct( 'datetime_page', cst.Const(PAGE_IDX, '000'), ) # values as ascii numbers 0x30-0x39 MESSAGE = cst.Struct('msg', HEADER, SER_STATUS, PAGE) class Protocol:
class ExplorerProgramsCachePlugin(interface.WindowsRegistryPlugin): """Class that parses the Explorer ProgramsCache Registry data.""" NAME = u'explorer_programscache' DESCRIPTION = u'Parser for Explorer ProgramsCache Registry data.' FILTERS = frozenset([ interface.WindowsRegistryKeyPathFilter( u'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\' u'Explorer\\StartPage'), interface.WindowsRegistryKeyPathFilter( u'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\' u'Explorer\\StartPage2')]) URLS = [ (u'https://github.com/libyal/winreg-kb/blob/master/documentation/' u'Programs%20Cache%20values.asciidoc')] _HEADER_STRUCT = construct.Struct( u'programscache_header', construct.ULInt32(u'format_version')) _ENTRY_HEADER_STRUCT = construct.Struct( u'programscache_entry_header', construct.ULInt32(u'data_size')) _ENTRY_FOOTER_STRUCT = construct.Struct( u'programscache_entry_footer', construct.Byte(u'sentinel')) def _ParseValueData(self, parser_mediator, registry_key, registry_value): """Extracts event objects from a Explorer ProgramsCache value data. Args: parser_mediator: A parser mediator object (instance of ParserMediator). registry_key: A Windows Registry key (instance of dfwinreg.WinRegistryKey). registry_value: A Windows Registry value (instance of dfwinreg.WinRegistryValue). """ value_data = registry_value.data if len(value_data) < 4: return try: header_struct = self._HEADER_STRUCT.parse(value_data) except construct.FieldError as exception: parser_mediator.ProduceParseError( u'unable to parse header with error: {0:s}'.format( exception)) return format_version = header_struct.get(u'format_version') if format_version not in [0x01, 0x09, 0x0c, 0x13]: parser_mediator.ProduceParseError( u'unsupported format version: 0x{0:08x}'.format(format_version)) return if format_version == 0x01: value_data_offset = 8 elif format_version == 0x09: value_data_offset = 6 else: # TODO: get known folder identifier? value_data_offset = 20 if format_version == 0x09: sentinel = 0 else: try: entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceParseError(( u'unable to parse sentinel at offset: 0x{0:08x} ' u'with error: {1:s}').format(value_data_offset, exception)) return value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get(u'sentinel') link_targets = [] while sentinel in [0x00, 0x01]: try: entry_header_struct = self._ENTRY_HEADER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceParseError(( u'unable to parse entry header at offset: 0x{0:08x} ' u'with error: {1:s}').format(value_data_offset, exception)) break value_data_offset += self._ENTRY_HEADER_STRUCT.sizeof() entry_data_size = entry_header_struct.get(u'data_size') display_name = u'{0:s} {1:s}'.format( registry_key.path, registry_value.name) shell_items_parser = shell_items.ShellItemsParser(display_name) shell_items_parser.UpdateChainAndParse( parser_mediator, value_data[value_data_offset:], None, codepage=parser_mediator.codepage) link_target = shell_items_parser.CopyToPath() link_targets.append(link_target) value_data_offset += entry_data_size try: entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( value_data[value_data_offset:]) except construct.FieldError as exception: parser_mediator.ProduceParseError(( u'unable to parse entry footer at offset: 0x{0:08x} ' u'with error: {1:s}').format(value_data_offset, exception)) break value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() sentinel = entry_footer_struct.get(u'sentinel') # TODO: recover remaining items. list_name = registry_value.name list_values = u' '.join([ u'{0:d}: {1:s}'.format(index, link_target) for index, link_target in enumerate(link_targets)]) event_object = windows_events.WindowsRegistryListEvent( registry_key.last_written_time, registry_key.path, list_name, list_values, value_name=registry_value.name) parser_mediator.ProduceEvent(event_object) def GetEntries(self, parser_mediator, registry_key, **kwargs): """Extracts event objects from a Explorer ProgramsCache Registry key. Args: parser_mediator: A parser mediator object (instance of ParserMediator). registry_key: A Windows Registry key (instance of dfwinreg.WinRegistryKey). """ registry_value = registry_key.GetValueByName(u'ProgramsCache') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) registry_value = registry_key.GetValueByName(u'ProgramsCacheSMP') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) registry_value = registry_key.GetValueByName(u'ProgramsCacheTBP') if registry_value: self._ParseValueData(parser_mediator, registry_key, registry_value) values_dict = {} for registry_value in registry_key.GetValues(): # Ignore the default value. if not registry_value.name or registry_value.name in [ u'ProgramsCache', u'ProgramsCacheSMP', u'ProgramsCacheTBP']: continue # Ignore any value that is empty or that does not contain a string. if not registry_value.data or not registry_value.DataIsString(): continue values_dict[registry_value.name] = registry_value.data event_object = windows_events.WindowsRegistryEvent( registry_key.last_written_time, registry_key.path, values_dict, offset=registry_key.offset) parser_mediator.ProduceEvent(event_object)
class AppCompatCacheDataParser(object): """Class that parses the Application Compatibility Cache data.""" FORMAT_TYPE_2000 = 1 FORMAT_TYPE_XP = 2 FORMAT_TYPE_2003 = 3 FORMAT_TYPE_VISTA = 4 FORMAT_TYPE_7 = 5 FORMAT_TYPE_8 = 6 FORMAT_TYPE_10 = 7 # AppCompatCache format signature used in Windows XP. _HEADER_SIGNATURE_XP = 0xdeadbeef # AppCompatCache format used in Windows XP. _HEADER_XP_32BIT_STRUCT = construct.Struct( u'appcompatcache_header_xp', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries'), construct.ULInt32(u'number_of_lru_entries'), construct.ULInt32(u'unknown1'), construct.Array(96, construct.ULInt32("lru_entry"))) _CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_xp_32bit', construct.Array(528, construct.Byte(u'path')), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size'), construct.ULInt64(u'last_update_time')) # AppCompatCache format signature used in Windows 2003, Vista and 2008. _HEADER_SIGNATURE_2003 = 0xbadc0ffe # AppCompatCache format used in Windows 2003. _HEADER_2003_STRUCT = construct.Struct( u'appcompatcache_header_2003', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries')) _CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_2003_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size')) _CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_2003_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt64(u'file_size')) # AppCompatCache format used in Windows Vista and 2008. _CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_vista_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags')) _CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_vista_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags')) # AppCompatCache format signature used in Windows 7 and 2008 R2. _HEADER_SIGNATURE_7 = 0xbadc0fee # AppCompatCache format used in Windows 7 and 2008 R2. _HEADER_7_STRUCT = construct.Struct( u'appcompatcache_header_7', construct.ULInt32(u'signature'), construct.ULInt32(u'number_of_cached_entries'), construct.Padding(120)) _CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_7_32bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags'), construct.ULInt32(u'data_size'), construct.ULInt32(u'data_offset')) _CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct( u'appcompatcache_cached_entry_7_64bit', construct.ULInt16(u'path_size'), construct.ULInt16(u'maximum_path_size'), construct.ULInt32(u'unknown1'), construct.ULInt64(u'path_offset'), construct.ULInt64(u'last_modification_time'), construct.ULInt32(u'insertion_flags'), construct.ULInt32(u'shim_flags'), construct.ULInt64(u'data_size'), construct.ULInt64(u'data_offset')) # AppCompatCache format used in Windows 8.0 and 8.1. _HEADER_SIGNATURE_8 = 0x00000080 _HEADER_8_STRUCT = construct.Struct(u'appcompatcache_header_8', construct.ULInt32(u'signature'), construct.ULInt32(u'unknown1'), construct.Padding(120)) _CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct( u'appcompatcache_cached_entry_header_8', construct.ULInt32(u'signature'), construct.ULInt32(u'unknown1'), construct.ULInt32(u'cached_entry_data_size'), construct.ULInt16(u'path_size')) # AppCompatCache format used in Windows 8.0. _CACHED_ENTRY_SIGNATURE_8_0 = u'00ts' # AppCompatCache format used in Windows 8.1. _CACHED_ENTRY_SIGNATURE_8_1 = u'10ts' # AppCompatCache format used in Windows 10 _HEADER_SIGNATURE_10 = 0x00000030 _HEADER_10_STRUCT = construct.Struct( u'appcompatcache_header_8', construct.ULInt32(u'signature'), construct.ULInt32(u'unknown1'), construct.Padding(28), construct.ULInt32(u'number_of_cached_entries'), construct.Padding(8)) def __init__(self, debug=False): """Initializes the parser object. Args: debug: optional boolean value to indicate if debug information should be printed. """ super(AppCompatCacheDataParser, self).__init__() self._debug = debug def CheckSignature(self, value_data): """Parses the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32(u'signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self.FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self.FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self.FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self.FORMAT_TYPE_8 elif signature == self._HEADER_SIGNATURE_10: # Windows 10 uses the same cache entry signature as Windows 8.1 if value_data[signature:signature + 4] in [self._CACHED_ENTRY_SIGNATURE_8_1]: return self.FORMAT_TYPE_10 return def ParseHeader(self, format_type, value_data): """Parses the header. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. Returns: A header object (instance of AppCompatCacheHeader). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8, self.FORMAT_TYPE_10 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) header_object = AppCompatCacheHeader() if format_type == self.FORMAT_TYPE_XP: header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof() header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_2003: header_object.header_size = self._HEADER_2003_STRUCT.sizeof() header_struct = self._HEADER_2003_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_VISTA: header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof() header_struct = self._HEADER_VISTA_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_7: header_object.header_size = self._HEADER_7_STRUCT.sizeof() header_struct = self._HEADER_7_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_8: header_object.header_size = self._HEADER_8_STRUCT.sizeof() header_struct = self._HEADER_8_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_10: header_object.header_size = self._HEADER_10_STRUCT.sizeof() header_struct = self._HEADER_10_STRUCT.parse(value_data) if self._debug: print(u'Header data:') print(hexdump.Hexdump(value_data[0:header_object.header_size])) if self._debug: print(u'Signature\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( header_struct.get(u'signature'))) if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_10 ]: header_object.number_of_cached_entries = header_struct.get( u'number_of_cached_entries') if self._debug: print(u'Number of cached entries\t\t\t\t\t\t: {0:d}'.format( header_object.number_of_cached_entries)) if format_type == self.FORMAT_TYPE_XP: number_of_lru_entries = header_struct.get(u'number_of_lru_entries') if self._debug: print(u'Number of LRU entries\t\t\t\t\t\t\t: 0x{0:08x}'.format( number_of_lru_entries)) print(u'Unknown1\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( header_struct.get(u'unknown1'))) print(u'LRU entries:') data_offset = 16 if number_of_lru_entries > 0 and number_of_lru_entries <= 96: for lru_entry_index in range(number_of_lru_entries): lru_entry = construct.ULInt32(u'cache_entry_index').parse( value_data[data_offset:data_offset + 4]) data_offset += 4 if self._debug: print((u'LRU entry: {0:d}\t\t\t\t\t\t\t\t: {1:d} ' u'(offset: 0x{2:08x})').format( lru_entry_index, lru_entry, 400 + (lru_entry * 552))) if self._debug: print(u'') if self._debug: print(u'Unknown data:') print(hexdump.Hexdump(value_data[data_offset:400])) elif format_type == self.FORMAT_TYPE_8: if self._debug: print(u'Unknown1\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( header_struct.get(u'unknown1'))) if self._debug: print(u'') return header_object def DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8, self.FORMAT_TYPE_10 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self.FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = construct.ULInt16(u'path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16(u'maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32(u'path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32(u'path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'Path size value out of bounds.') return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'Unsupported path size values.') return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size def ParseCachedEntry(self, format_type, value_data, cached_entry_index, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_index: integer value that contains the cached entry index. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8, self.FORMAT_TYPE_10 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: if self._debug: print(u'Cached entry: {0:d} data:'.format(cached_entry_index)) print(hexdump.Hexdump(cached_entry_data)) elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: if self._debug: print(u'Cached entry: {0:d} header data:'.format( cached_entry_index)) print(hexdump.Hexdump(cached_entry_data[:-2])) cached_entry_struct = None if format_type == self.FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError(u'Unsupported cache entry signature') if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( u'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError(u'Unsupported cache entry size: {0:d}'.format( cached_entry_size)) if format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: if self._debug: print(u'Cached entry: {0:d} data:'.format(cached_entry_index)) print(hexdump.Hexdump(cached_entry_data)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self.FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = cached_entry_data[0:string_size].decode( u'utf-16-le') if self._debug: print(u'Path\t\t\t\t\t\t\t\t\t: {0:s}'.format( cached_entry_object.path)) elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get(u'path_size') maximum_path_size = cached_entry_struct.get(u'maximum_path_size') path_offset = cached_entry_struct.get(u'path_offset') if self._debug: print(u'Path size\t\t\t\t\t\t\t\t: {0:d}'.format(path_size)) print(u'Maximum path size\t\t\t\t\t\t\t: {0:d}'.format( maximum_path_size)) print(u'Path offset\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( path_offset)) elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: path_size = cached_entry_struct.get(u'path_size') if self._debug: print(u'Signature\t\t\t\t\t\t\t\t: {0:s}'.format( cached_entry_data[0:4])) print(u'Unknown1\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_struct.get(u'unknown1'))) print(u'Cached entry data size\t\t\t\t\t\t\t: {0:d}'.format( cached_entry_data_size)) print(u'Path size\t\t\t\t\t\t\t\t: {0:d}'.format(path_size)) cached_entry_data_offset = 14 + path_size cached_entry_object.path = cached_entry_data[ 14:cached_entry_data_offset].decode(u'utf-16-le') if self._debug: print(u'Path\t\t\t\t\t\t\t\t\t: {0:s}'.format( cached_entry_object.path)) if format_type == self.FORMAT_TYPE_8: remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( u'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( u'shim_flags').parse(remaining_data[4:8]) if self._debug: print(u'Insertion flags\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.insertion_flags)) print(u'Shim flags\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.shim_flags)) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[ 0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: if self._debug: print(u'Unknown1\t\t\t\t\t\t\t\t: 0x{0:04x}'.format( construct.ULInt16(u'unknown1').parse( remaining_data[8:10]))) cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( u'last_modification_time') elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: cached_entry_object.last_modification_time = construct.ULInt64( u'last_modification_time').parse(remaining_data[0:8]) if not cached_entry_object.last_modification_time: if self._debug: print( u'Last modification time\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.last_modification_time)) else: timestamp = cached_entry_object.last_modification_time // 10 date_string = (datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=timestamp)) if self._debug: print( u'Last modification time\t\t\t\t\t\t\t: {0!s} (0x{1:08x})'. format(date_string, cached_entry_object.last_modification_time)) if format_type in [self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( u'file_size') if self._debug: print(u'File size\t\t\t\t\t\t\t\t: {0:d}'.format( cached_entry_object.file_size)) elif format_type in [self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( u'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( u'shim_flags') if self._debug: print(u'Insertion flags\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.insertion_flags)) print(u'Shim flags\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.shim_flags)) if format_type == self.FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( u'last_update_time') if not cached_entry_object.last_update_time: if self._debug: print(u'Last update time\t\t\t\t\t\t\t: 0x{0:08x}'.format( cached_entry_object.last_update_time)) else: timestamp = cached_entry_object.last_update_time // 10 date_string = (datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=timestamp)) if self._debug: print(u'Last update time\t\t\t\t\t\t\t: {0!s} (0x{1:08x})'. format(date_string, cached_entry_object.last_update_time)) if format_type == self.FORMAT_TYPE_7: data_offset = cached_entry_struct.get(u'data_offset') data_size = cached_entry_struct.get(u'data_size') if self._debug: print(u'Data offset\t\t\t\t\t\t\t\t: 0x{0:08x}'.format( data_offset)) print(u'Data size\t\t\t\t\t\t\t\t: {0:d}'.format(data_size)) elif format_type in [self.FORMAT_TYPE_8, self.FORMAT_TYPE_10]: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32(u'data_size').parse( remaining_data[8:12]) if self._debug: print(u'Data size\t\t\t\t\t\t\t\t: {0:d}'.format(data_size)) if self._debug: print(u'') if path_offset > 0 and path_size > 0: path_size += path_offset maximum_path_size += path_offset if self._debug: print(u'Path data:') print( hexdump.Hexdump(value_data[path_offset:maximum_path_size])) cached_entry_object.path = value_data[ path_offset:path_size].decode(u'utf-16-le') if self._debug: print(u'Path\t\t\t\t\t\t\t\t\t: {0:s}'.format( cached_entry_object.path)) print(u'') if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] if self._debug: print(u'Data:') print(hexdump.Hexdump(cached_entry_object.data)) return cached_entry_object
# Vault Generic Schema VAULT_SCHEMA_GENERIC = construct.Struct( 'VAULT_SCHEMA_GENERIC', construct.ULInt32('version'), construct.ULInt32('count'), construct.ULInt32('vault_schema_generic_unknown1'), construct.Array( lambda ctx: ctx.count, VAULT_ATTRIBUTE_ITEM) ) # Vault Simple Schema VAULT_SCHEMA_SIMPLE = VaultSchemaSimpleAdapter( construct.Struct( 'VAULT_SCHEMA_SIMPLE', construct.OptionalGreedyRange(construct.Byte('data')) ) ) # PIN Logon Vault Resource Schema VAULT_SCHEMA_PIN = VaultSchemaPinAdapter( construct.Struct( 'VAULT_SCHEMA_PIN', construct.ULInt32('version'), construct.Const(construct.ULInt32('count'), 4), construct.ULInt32('vault_schema_pin_unknown1'), construct.Const(construct.ULInt32('id_sid'), 2), construct.ULInt32('sid_len'), SID('sid', lambda ctx: ctx.sid_len), construct.Const(construct.ULInt32('id_resource'), 1), construct.Rename('resource', UNICODE_STRING_STRIP),
class AppCompatCachePlugin(interface.KeyPlugin): """Class that parses the Application Compatibility Cache Registry data.""" NAME = 'winreg_appcompatcache' DESCRIPTION = u'Parser for Application Compatibility Cache Registry data.' REG_KEYS = [ u'\\{current_control_set}\\Control\\Session Manager\\AppCompatibility', u'\\{current_control_set}\\Control\\Session Manager\\AppCompatCache' ] REG_TYPE = 'SYSTEM' URL = [(u'https://code.google.com/p/winreg-kb/wiki/' u'ApplicationCompatibilityCacheKey')] _FORMAT_TYPE_2000 = 1 _FORMAT_TYPE_XP = 2 _FORMAT_TYPE_2003 = 3 _FORMAT_TYPE_VISTA = 4 _FORMAT_TYPE_7 = 5 _FORMAT_TYPE_8 = 6 # AppCompatCache format signature used in Windows XP. _HEADER_SIGNATURE_XP = 0xdeadbeef # AppCompatCache format used in Windows XP. _HEADER_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_header_xp', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.ULInt32('unknown1'), construct.ULInt32('unknown2'), construct.Padding(384)) _CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_xp_32bit', construct.Array(528, construct.Byte('path')), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size'), construct.ULInt64('last_update_time')) # AppCompatCache format signature used in Windows 2003, Vista and 2008. _HEADER_SIGNATURE_2003 = 0xbadc0ffe # AppCompatCache format used in Windows 2003. _HEADER_2003_STRUCT = construct.Struct( 'appcompatcache_header_2003', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries')) _CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) _CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) # AppCompatCache format used in Windows Vista and 2008. _CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) _CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) # AppCompatCache format signature used in Windows 7 and 2008 R2. _HEADER_SIGNATURE_7 = 0xbadc0fee # AppCompatCache format used in Windows 7 and 2008 R2. _HEADER_7_STRUCT = construct.Struct( 'appcompatcache_header_7', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.Padding(120)) _CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt32('data_size'), construct.ULInt32('data_offset')) _CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt64('data_size'), construct.ULInt64('data_offset')) # AppCompatCache format used in Windows 8.0 and 8.1. _HEADER_SIGNATURE_8 = 0x00000080 _HEADER_8_STRUCT = construct.Struct('appcompatcache_header_8', construct.ULInt32('signature'), construct.Padding(124)) _CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct( 'appcompatcache_cached_entry_header_8', construct.ULInt32('signature'), construct.ULInt32('unknown1'), construct.ULInt32('cached_entry_data_size'), construct.ULInt16('path_size')) # AppCompatCache format used in Windows 8.0. _CACHED_ENTRY_SIGNATURE_8_0 = '00ts' # AppCompatCache format used in Windows 8.1. _CACHED_ENTRY_SIGNATURE_8_1 = '10ts' def _CheckSignature(self, value_data): """Parses and validates the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32('signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self._FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self._FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self._FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self._FORMAT_TYPE_8 def _DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Determines the size of a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self._FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: path_size = construct.ULInt16('path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16('maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32('path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32('path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'[{0:s}] Path size value out of bounds.'.format( self.NAME)) return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'[{0:s}] Unsupported path size values.'.format( self.NAME)) return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self._FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self._FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type == self._FORMAT_TYPE_8: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size def _ParseHeader(self, format_type, value_data): """Parses the header. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. Returns: A header object (instance of AppCompatCacheHeader). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) # TODO: change to collections.namedtuple or use __slots__ if the overhead # of a regular object becomes a problem. header_object = AppCompatCacheHeader() if format_type == self._FORMAT_TYPE_XP: header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof() header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_2003: header_object.header_size = self._HEADER_2003_STRUCT.sizeof() header_struct = self._HEADER_2003_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_VISTA: header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof() header_struct = self._HEADER_VISTA_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_7: header_object.header_size = self._HEADER_7_STRUCT.sizeof() header_struct = self._HEADER_7_STRUCT.parse(value_data) elif format_type == self._FORMAT_TYPE_8: header_object.header_size = self._HEADER_8_STRUCT.sizeof() header_struct = self._HEADER_8_STRUCT.parse(value_data) if format_type in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: header_object.number_of_cached_entries = header_struct.get( 'number_of_cached_entries') return header_object def _ParseCachedEntry(self, format_type, value_data, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7, self._FORMAT_TYPE_8 ]: raise RuntimeError( u'[{0:s}] Unsupported format type: {1:d}'.format( self.NAME, format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] cached_entry_struct = None if format_type == self._FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self._FORMAT_TYPE_8: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError( u'[{0:s}] Unsupported cache entry signature'.format( self.NAME)) if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( 'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError( u'[{0:s}] Unsupported cache entry size: {1:d}'.format( self.NAME, cached_entry_size)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self._FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[0:string_size]) elif format_type in [ self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get('path_size') path_offset = cached_entry_struct.get('path_offset') elif format_type == self._FORMAT_TYPE_8: path_size = cached_entry_struct.get('path_size') cached_entry_data_offset = 14 + path_size cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[14:cached_entry_data_offset]) remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( 'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( 'shim_flags').parse(remaining_data[4:8]) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( 'last_modification_time') elif format_type == self._FORMAT_TYPE_8: cached_entry_object.last_modification_time = construct.ULInt64( 'last_modification_time').parse(remaining_data[0:8]) if format_type in [self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( 'file_size') elif format_type in [self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( 'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( 'shim_flags') if format_type == self._FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( 'last_update_time') if format_type == self._FORMAT_TYPE_7: data_offset = cached_entry_struct.get('data_offset') data_size = cached_entry_struct.get('data_size') elif format_type == self._FORMAT_TYPE_8: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32('data_size').parse( remaining_data[8:12]) if path_offset > 0 and path_size > 0: path_size += path_offset cached_entry_object.path = binary.Ut16StreamCopyToString( value_data[path_offset:path_size]) if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] return cached_entry_object def GetEntries(self, parser_context, key=None, **unused_kwargs): """Extracts event objects from a Application Compatibility Cache key. Args: parser_context: A parser context object (instance of ParserContext). key: Optional Registry key (instance of winreg.WinRegKey). The default is None. """ value = key.GetValue('AppCompatCache') if not value: return value_data = value.data value_data_size = len(value.data) format_type = self._CheckSignature(value_data) if not format_type: # TODO: Instead of logging emit a parser error object that once that # mechanism is implemented. logging.error( u'AppCompatCache format error: [{0:s}] Unsupported signature'. format(key.path)) return header_object = self._ParseHeader(format_type, value_data) # On Windows Vista and 2008 when the cache is empty it will # only consist of the header. if value_data_size <= header_object.header_size: return cached_entry_offset = header_object.header_size cached_entry_size = self._DetermineCacheEntrySize( format_type, value_data, cached_entry_offset) if not cached_entry_size: # TODO: Instead of logging emit a parser error object that once that # mechanism is implemented. logging.error( u'AppCompatCache format error: [{0:s}] Unsupported cached entry ' u'size.'.format(key.path)) return cached_entry_index = 0 while cached_entry_offset < value_data_size: cached_entry_object = self._ParseCachedEntry( format_type, value_data, cached_entry_offset, cached_entry_size) if cached_entry_object.last_modification_time is not None: # TODO: refactor to file modification event. event_object = AppCompatCacheEvent( cached_entry_object.last_modification_time, u'File Last Modification Time', key.path, cached_entry_index + 1, cached_entry_object.path, cached_entry_offset) parser_context.ProduceEvent(event_object, plugin_name=self.NAME) if cached_entry_object.last_update_time is not None: # TODO: refactor to process run event. event_object = AppCompatCacheEvent( cached_entry_object.last_update_time, eventdata.EventTimestamp.LAST_RUNTIME, key.path, cached_entry_index + 1, cached_entry_object.path, cached_entry_offset) parser_context.ProduceEvent(event_object, plugin_name=self.NAME) cached_entry_offset += cached_entry_object.cached_entry_size cached_entry_index += 1 if (header_object.number_of_cached_entries != 0 and cached_entry_index >= header_object.number_of_cached_entries): break