Пример #1
0
 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])
Пример #2
0
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
Пример #3
0
    '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',
Пример #4
0
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'),
)
Пример #5
0
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
Пример #6
0
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)
Пример #7
0
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)
Пример #8
0
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:]))
Пример #9
0
 def __init__(self, name):
     construct.Adapter.__init__(
         self,
         construct.RepeatUntil(lambda obj, ctx: obj == 1,
                               construct.Byte(name)))
Пример #10
0
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:
Пример #11
0
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)
Пример #12
0
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
Пример #13
0
# 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),
Пример #14
0
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