Example #1
0
 def __init__(self):
     super(AudioHandler, self).__init__()
     self.header_base = construct.BitStruct(
         'ASTRMBaseHeader', construct.BitField('fmt', 3),
         construct.Bit('channel'), construct.Flag('vibrate'),
         construct.Bit('packet_type'), construct.BitField('seq_id', 10),
         construct.BitField('payload_size', 16))
     self.header_aud = construct.Struct('ASTRMAudioHeader',
                                        construct.ULInt32('timestamp'))
     self.header_msg = construct.Struct(
         'ASTRMMsgHeader',
         # This is kind of a hack, (there are two timestamp fields, which one is used
         # depends on packet_type
         construct.ULInt32('timestamp_audio'),
         construct.ULInt32('timestamp'),
         construct.Array(2, construct.ULInt32('freq_0')),  # -> mc_video
         construct.Array(2, construct.ULInt32('freq_1')),  # -> mc_sync
         construct.ULInt8('vid_format'),
         construct.Padding(3))
     self.header = construct.Struct(
         'ASTRMHeader', construct.Embed(self.header_base),
         construct.Switch('format_hdr',
                          lambda ctx: ctx.packet_type, {
                              0: construct.Embed(self.header_aud),
                              1: construct.Embed(self.header_msg),
                          },
                          default=construct.Pass))
Example #2
0
def MakeAssemblyRefOSRow():
    return construct.Struct('AssemblyRefOSRow',
        construct.ULInt32('OSPlatformID'),
        construct.ULInt32('OSMajorVersion'),
        construct.ULInt32('OSMinorVersion'),
        MDTag.AssemblyRefRId.parse('AssemblyRef')
    )
Example #3
0
class Wdigest_x64(Wdigest, Mimikatz_x64):
  """TODO: add description."""

  WDIGEST_LIST_ENTRY = construct.Struct('WdigestListEntry',
      construct.ULInt64('previous'),
      construct.ULInt64('next'),
      construct.ULInt32('usage_count'),
      construct.ULInt32('align1'),
      construct.ULInt64('this_entry'),
      construct.ULInt64('luid'),
      construct.ULInt64('flag'),
      construct.ULInt16('user_len'),
      construct.ULInt16('user_max_len'),
      construct.ULInt32('align2'),
      construct.ULInt64('user_string_ptr'),
      construct.ULInt16('domain_len'),
      construct.ULInt16('domain_max_len'),
      construct.ULInt32('align3'),
      construct.ULInt64('domain_string_ptr'),
      construct.ULInt16('password_len'),
      construct.ULInt16('password_max_len'),
      construct.ULInt32('align4'),
      construct.ULInt64('password_encrypted_ptr'))
  
  def __init__(self, lsass_task, credentials_obj):
    Mimikatz_x64.__init__(self, lsass_task)
    Wdigest.__init__(self, credentials_obj)
Example #4
0
def MakeManifestResourceRow():
    return construct.Struct('ManifestResourceRow',
        construct.ULInt32('Offset'),
        construct.ULInt32('Flags'),
        MDTag.StringHeapRef.parse('Name'),
        MDTag.Implementation.parse('Implementation')
    )
Example #5
0
def MakeExportedTypeRow():
    return construct.Struct('ExportedTypeRow',
        construct.ULInt32('Flags'),
        construct.ULInt32('TypeDefId'),
        MDTag.StringHeapRef.parse('TypeName'),
        MDTag.StringHeapRef.parse('TypeNamespace'),
        MDTag.Implementation.parse('Implementation')
    )
Example #6
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])
Example #7
0
def MakeAssemblyRow():
    return construct.Struct('AssemblyRow',
        construct.ULInt32('HashAlgId'),
        construct.ULInt16('MajorVersion'),
        construct.ULInt16('MinorVersion'),
        construct.ULInt16('BuildNumber'),
        construct.ULInt16('RevisionNumber'),
        construct.ULInt32('Flags'),
        MDTag.BlobHeapRef.parse('PublicKey'),
        MDTag.StringHeapRef.parse('Name'),
        MDTag.StringHeapRef.parse('Culture')
    )
Example #8
0
    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
Example #9
0
class LsaDecryptor_Vista_x64(LsaDecryptor_x64):
  """Class for Vista x64."""
  SIGNATURE	= '\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d'
  PTR_IV_OFFSET = 63;
  PTR_AES_KEY_OFFSET = 25;
  PTR_DES_KEY_OFFSET = -69;

  BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY',
      construct.ULInt32('size'),
      construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552.
      construct.ULInt64('ptr_void_algorithm'),
      construct.ULInt64('ptr_kiwi_bcrypt_key'),
      construct.ULInt64('ptr_unknown'))
  
  def __init__(self, lsass_task):
    LsaDecryptor_x64.__init__(self, lsass_task)
Example #10
0
 def _decode(self, obj, context):
     return '{:08x}-{:04x}-{:04x}-{:04x}-{:s}'.format(
         construct.ULInt32('foo').parse(obj[0:4]),
         construct.ULInt16('foo').parse(obj[4:6]),
         construct.ULInt16('foo').parse(obj[6:8]),
         construct.UBInt16('foo').parse(obj[8:10]),
         obj[10:16].encode('hex'))
Example #11
0
    def _CheckSignature(self, value_data):
        """Parses and validates the signature.

    Args:
      value_data (bytes): value data.

    Returns:
      int: format type or None if format could not be determined.
    """
        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 in self._HEADER_SIGNATURES_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
Example #12
0
class LsaDecryptor_Win7_x64(LsaDecryptor_x64):
  """Class for Windows 7 x64."""
  # MIMIKATZ x64: BYTE PTRN_WNO8_LsaInitializeProtectedMemory_KEY[]
  SIGNATURE	= '\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d'
  PTR_IV_OFFSET = 59;
  PTR_AES_KEY_OFFSET = 25;
  PTR_DES_KEY_OFFSET = -61;

  BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY',
      construct.ULInt32('size'),
      construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552.
      construct.ULInt64('ptr_void_algorithm'),
      construct.ULInt64('ptr_kiwi_bcrypt_key'),
      construct.ULInt64('ptr_unknown'))
  
  def __init__(self, lsass_task):
    LsaDecryptor_x64.__init__(self, lsass_task)
Example #13
0
class LsaDecryptor_Win7_x86(LsaDecryptor_x86):
  """Class for Windows 7 x86."""
  # MIMIKATZ x86: BYTE PTRN_WNO8_LsaInitializeProtectedMemory_KEY[]
  SIGNATURE	= '\x8b\xf0\x3b\xf3\x7c\x2c\x6a\x02\x6a\x10\x68'
  PTR_IV_OFFSET = 11;
  PTR_AES_KEY_OFFSET = -15;
  PTR_DES_KEY_OFFSET = -70;

  BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY',
      construct.ULInt32('size'),
      construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552.
      construct.ULInt32('ptr_void_algorithm'),
      construct.ULInt32('ptr_kiwi_bcrypt_key'),
      construct.ULInt32('ptr_unknown'))
  
  def __init__(self, lsass_task):
    LsaDecryptor_x86.__init__(self, lsass_task)
Example #14
0
def decode_itempos(itempos):
    """
    Decodes a single itempos and returns extracted information
    """
    itempos_io = StringIO.StringIO(itempos)
    itempos_struct = construct.Struct("itempos",
                                      construct.ULInt16("itempos_size"),
                                      construct.Padding(2),
                                      construct.ULInt32("filesize"),
                                      construct.Bytes("dos_date", 2),
                                      construct.Bytes("dos_time", 2),
                                      construct.ULInt16("file_attr"),
                                      construct.CString("filename")
                                      )
    parse_res = itempos_struct.parse_stream(itempos_io)
    if itempos_io.pos % 2 == 1:
        itempos_io.read(1)
    ext_struct = construct.Struct("ext",
                                  construct.ULInt16("ext_size"),
                                  construct.ULInt16("ext_version")
                                  )
    parse_ext = ext_struct.parse_stream(itempos_io)
    if parse_ext["ext_version"] >= 0x3:
        itempos2_struct = construct.Struct("itempos2",
                                           construct.Padding(2),  # 0004
                                           construct.Padding(2),  # BEEF
                                           construct.Bytes("creation_dos_date", 2),
                                           construct.Bytes("creation_dos_time", 2),
                                           construct.Bytes("access_dos_date", 2),
                                           construct.Bytes("access_dos_time", 2),
                                           construct.Padding(4)
                                           )
        parse_res2 = itempos2_struct.parse_stream(itempos_io)
    unicode_filename = ""
    if parse_ext["ext_version"] >= 0x7:
        itempos3_struct = construct.Struct("itempos3",
                                           construct.ULInt64("file_ref"),
                                           construct.Padding(8),
                                           construct.Padding(2),
                                           construct.Padding(4)
                                           )
        parse_res3 = itempos3_struct.parse_stream(itempos_io)
        unicode_filename = itempos_io.read().decode("utf16")
        if not unicode_filename.endswith("\0"):
            unicode_filename = unicode_filename[:-2]  # ditch last unused 2 bytes and \0 char
    elif parse_ext["ext_version"] >= 0x3:
        unicode_filename = itempos_io.read().decode("utf16")
        if not unicode_filename.endswith("\0"):
            unicode_filename = unicode_filename[:-2]  # ditch last unused 2 bytes and \0 char

    timestamp_modified = dosdate(parse_res["dos_date"], parse_res["dos_time"]).strftime("%d/%m/%Y %H:%M:%S")
    timestamp_created = dosdate(parse_res2["creation_dos_date"], parse_res2["creation_dos_time"]).strftime(
        "%d/%m/%Y %H:%M:%S")
    timestamp_access = dosdate(parse_res2["access_dos_date"], parse_res2["access_dos_time"]).strftime(
        "%d/%m/%Y %H:%M:%S")

    return [unicode(parse_res["itempos_size"]), unicode(parse_res["filesize"]), timestamp_modified,
            parse_res["filename"], timestamp_created, timestamp_access, unicode_filename]
Example #15
0
def MakeTypeDefRow():
    return construct.Struct('TypeDefRow',
        construct.ULInt32('Flags'),
        MDTag.StringHeapRef.parse('Name'),
        MDTag.StringHeapRef.parse('Namespace'),
        MDTag.TypeDefOrRef.parse('Extends'),
        MDTag.FieldRef.parse('FieldList'),
        MDTag.MethodRef.parse('MethodList')
    )
Example #16
0
class WinVerPlugin(interface.KeyPlugin):
    """Plug-in to collect information about the Windows version."""

    NAME = 'winreg_winver'

    REG_KEYS = [u'\\Microsoft\\Windows NT\\CurrentVersion']
    REG_TYPE = 'SOFTWARE'
    URLS = []

    INT_STRUCT = construct.ULInt32('install')

    # TODO: Refactor remove this function in a later CL.
    def GetValueString(self, key, value_name):
        """Retrieves a specific string value from the Registry key.

    Args:
      key: A Windows Registry key (instance of WinRegKey).
      value_name: The name of the value.

    Returns:
      A string value if one is available, otherwise an empty string.
    """
        value = key.GetValue(value_name)

        if not value:
            return ''

        if not value.data or not value.DataIsString():
            return ''
        return value.data

    def GetEntries(self, key, **unused_kwargs):
        """Gather minimal information about system install and return an event."""
        text_dict = {}
        text_dict[u'Owner'] = self.GetValueString(key, 'RegisteredOwner')
        text_dict[u'sp'] = self.GetValueString(key, 'CSDBuildNumber')
        text_dict[u'Product name'] = self.GetValueString(key, 'ProductName')
        text_dict[u' Windows Version Information'] = u''

        install_raw = key.GetValue('InstallDate').raw_data
        # TODO: move this to a function in utils with a more descriptive name
        # e.g. CopyByteStreamToInt32BigEndian.
        try:
            install = self.INT_STRUCT.parse(install_raw)
        except construct.FieldError:
            install = 0

        event_object = event.WinRegistryEvent(
            key.path,
            text_dict,
            timestamp=timelib.Timestamp.FromPosixTime(install))
        event_object.prodname = text_dict[u'Product name']
        event_object.source_long = 'SOFTWARE WinVersion key'
        if text_dict[u'Owner']:
            event_object.owner = text_dict[u'Owner']
        yield event_object
Example #17
0
  def _ReadIndexTable(self):
    """Reads the index table."""
    cache_address_data = self._file_object.read(4)

    while len(cache_address_data) == 4:
      value = construct.ULInt32(u'cache_address').parse(cache_address_data)

      if value:
        cache_address = CacheAddress(value)
        self.index_table.append(cache_address)

      cache_address_data = self._file_object.read(4)
Example #18
0
 def __init__(s):
     super(ServiceASTRM, s).__init__()
     s.header_base = construct.BitStruct('ASTRMBaseHeader',
         construct.BitField('fmt', 3),
         construct.Bit('channel'),
         construct.Flag('vibrate'),
         construct.Bit('packet_type'),
         construct.BitField('seq_id', 10),
         construct.BitField('payload_size', 16)
     )
     s.header_aud = construct.Struct('ASTRMAudioHeader',
         construct.ULInt32('timestamp'),
     #    construct.Array(lambda ctx: ctx.payload_size, construct.UBInt8("data"))
     )
     s.header_msg = construct.Struct('ASTRMMsgHeader',
         # This is kind of a hack, (there are two timestamp fields, which one is used depends on packet_type
         construct.ULInt32('timestamp_audio'),
         construct.ULInt32('timestamp'),
         construct.Array(2, construct.ULInt32('freq_0')), # -> mc_video
         construct.Array(2, construct.ULInt32('freq_1')), # -> mc_sync
         construct.ULInt8('vid_format'),
         construct.Padding(3)
     )
     s.header = construct.Struct('ASTRMHeader',
         construct.Embed(s.header_base),
         construct.Switch('format_hdr', lambda ctx: ctx.packet_type,
             {
                 0 : construct.Embed(s.header_aud),
                 1 : construct.Embed(s.header_msg),
             },
             default = construct.Pass
         )
     )
     s.is_streaming = False
     s.p = pyaudio.PyAudio()
     s.stream = None
     
     s.pa_num_bufs = 15
     s.pa_ring = [array.array('H', '\0' * 416 * 2)] * s.pa_num_bufs
     s.pa_wpos = s.pa_rpos = 0
Example #19
0
 def get_key(self, pos, key_offset):
     ptr_key = self.get_ptr_with_offset(pos + key_offset)
     if ptr_key:
         ptr_key = self.get_ptr(ptr_key)
         if ptr_key:
             size = self.BCRYPT_HANDLE_KEY.sizeof()
             data = self.get_data(ptr_key, size)
             if data:
                 kbhk = self.BCRYPT_HANDLE_KEY.parse(data)
                 if kbhk.tag == self.UUUR_TAG:
                     ptr_key = kbhk.ptr_kiwi_bcrypt_key
                     size = self.BCRYPT_KEY.sizeof()
                     data = self.get_data(ptr_key, size)
                     if data:
                         kbk = self.BCRYPT_KEY.parse(data)
                         if kbk.tag == self.MSSK_TAG:
                             adjust = construct.ULInt32('').sizeof()
                             size = kbk.cbSecret + adjust
                             ptr_key = ptr_key + self.BCRYPT_KEY.sizeof(
                             ) - adjust
                             data = self.get_data(ptr_key, size)
                             if data:
                                 khk = self.HARD_KEY.parse(data)
                                 return khk.data
                             else:
                                 debug.warning(
                                     'get_key() unable to get HARD_KEY.')
                         else:
                             debug.warning(
                                 'get_key() BCRYPT_KEY invalid tag')
                     else:
                         debug.warning(
                             'get_key() unable to read BCRYPT_KEY data.')
                 else:
                     debug.warning(
                         'get_key() BCRYPT_HANDLE_KEY invalid tag')
                     debug.warning(kbhk)
             else:
                 debug.warning(
                     'get_key() unable to read BCRYPT_HANDLE_KEY data.')
         else:
             debug.warning(
                 'get_key() unable to get BCRYPT_HANDLE_KEY pointer.')
     else:
         debug.warning('get_key()unable to get first pointer.')
Example #20
0
    def _ReadUtmpEvent(self, file_object):
        """Returns an UtmpEvent from a single UTMP entry.

    Args:
      file_object: a file-like object that points to an UTMP file.

    Returns:
      An event object constructed from a single UTMP record or None if we
      have reached the end of the file (or EOF).
    """
        offset = file_object.tell()
        data = file_object.read(self.LINUX_UTMP_ENTRY_SIZE)
        if not data or len(data) != self.LINUX_UTMP_ENTRY_SIZE:
            return
        try:
            entry = self.LINUX_UTMP_ENTRY.parse(data)
        except (IOError, construct.FieldError):
            logging.warning(
                (u'UTMP entry at 0x{:x} couldn\'t be parsed.').format(offset))
            return self._ReadUtmpEvent(file_object)

        user = self._GetTextFromNullTerminatedString(entry.username)
        terminal = self._GetTextFromNullTerminatedString(entry.terminal)
        if terminal == '~':
            terminal = u'system boot'
        computer_name = self._GetTextFromNullTerminatedString(entry.hostname)
        if computer_name == u'N/A' or computer_name == u':0':
            computer_name = u'localhost'
        status = self.STATUS_TYPE.get(entry.type, u'N/A')

        if not entry.address_b:
            try:
                ip_address = socket.inet_ntoa(
                    construct.ULInt32('int').build(entry.address_a))
                if ip_address == '0.0.0.0':
                    ip_address = u'localhost'
            except (IOError, construct.FieldError, socket.error):
                ip_address = u'N/A'
        else:
            ip_address = u'{0:d}.{1:d}.{2:d}.{3:d}'.format(
                entry.address_a, entry.address_b, entry.address_c,
                entry.address_d)

        return UtmpEvent(entry.timestamp, entry.microsecond, user,
                         computer_name, terminal, status, ip_address, entry)
Example #21
0
 def __init__(s):
     s.header_cmd0 = construct.Struct('CMD0Header',
         construct.UBInt8('magic'),
         construct.UBInt8('unk_0'),
         construct.UBInt8('unk_1'),
         construct.UBInt8('unk_2'),
         construct.UBInt8('unk_3'),
         construct.UBInt8('flags'),
         construct.UBInt8('id_primary'),
         construct.UBInt8('id_secondary'),
         construct.UBInt16('error_code'),
         construct.UBInt16('payload_size_cmd0')
     )
     s.header_cmd1 = construct.Struct('CMD1Header',
         construct.Padding(48)
     )
     s.header_cmd2 = construct.Struct('CMD2Header',
         construct.ULInt16('JDN_base'),
         construct.Padding(2),
         construct.ULInt32('seconds')
     )
     s.header = construct.Struct('CMDHeader',
         construct.ULInt16('packet_type'),
         construct.ULInt16('cmd_id'),
         construct.ULInt16('payload_size'),
         construct.ULInt16('seq_id'),
         construct.Switch('cmd_hdr', lambda ctx: ctx.cmd_id,
             {
                 0 : construct.If(lambda ctx: ctx.payload_size >= s.header_cmd0.sizeof(), construct.Embed(s.header_cmd0)),
                 1 : construct.If(lambda ctx: ctx.payload_size == s.header_cmd1.sizeof(), construct.Embed(s.header_cmd1)),
                 2 : construct.If(lambda ctx: ctx.payload_size == s.header_cmd2.sizeof(), construct.Embed(s.header_cmd2))
             },
             default = construct.Pass
         )
     )
     s.cmd_handlers = {
         0 : s.cmd0,
         1 : s.cmd1,
         2 : s.cmd2
     }
     s.cmd0_handlers = {
         5 : { 6 : s.cmd0_5_6 },
     }
Example #22
0
  def _ReadIndexTable(self):
    """Reads the index table."""
    cache_address_index = 0
    cache_address_data = self._file_object.read(4)

    while len(cache_address_data) == 4:
      value = construct.ULInt32(u'cache_address').parse(cache_address_data)

      if value:
        cache_address = CacheAddress(value)

        if self._debug:
          print(u'Cache address: {0:d}\t\t\t\t\t\t\t: {1:s}'.format(
              cache_address_index, cache_address.GetDebugString()))

        self.index_table[cache_address_index] = cache_address

      cache_address_index += 1
      cache_address_data = self._file_object.read(4)

    if self._debug:
      print(u'')
Example #23
0
    def _ParseItem(self, file_object, offset):
        """Parses a Systemd journal DATA object.

    This method will read, and decompress if needed, the content of a DATA
    object.

    Args:
      file_object (dfvfs.FileIO): a file-like object.
      offset (int): offset to the DATA object.

    Returns:
      tuple[str, str]: key and value of this item.

    Raises:
      ParseError: When an unexpected object type is parsed.
    """
        object_header, payload_size = self._ParseObjectHeader(
            file_object, offset)
        file_object.read(self._DATA_OBJECT_SIZE)

        if object_header.type != 'DATA':
            raise errors.ParseError(
                'Expected an object of type DATA, but got {0:s}'.format(
                    object_header.type))

        event_data = file_object.read(payload_size - self._DATA_OBJECT_SIZE)
        if object_header.flags & self._OBJECT_COMPRESSED_FLAG_XZ:
            event_data = lzma.decompress(event_data)
            event_string = event_data.decode('utf-8')
        elif object_header.flags & self._OBJECT_COMPRESSED_FLAG_LZ4:
            decompressed_size = construct.ULInt32('size').parse(
                event_data[0:7])
            event_data = lz4.block.decompress(
                event_data[8:], uncompressed_size=decompressed_size)

        event_string = event_data.decode('utf-8')
        event_key, event_value = event_string.split('=', 1)
        return (event_key, event_value)
Example #24
0
class UtmpParser(interface.SingleFileBaseParser):
    """Parser for Linux/Unix UTMP files."""

    _INITIAL_FILE_OFFSET = None

    NAME = 'utmp'
    DESCRIPTION = u'Parser for Linux/Unix UTMP files.'

    LINUX_UTMP_ENTRY = construct.Struct('utmp_linux',
                                        construct.ULInt32('type'),
                                        construct.ULInt32('pid'),
                                        construct.String('terminal', 32),
                                        construct.ULInt32('terminal_id'),
                                        construct.String('username', 32),
                                        construct.String('hostname', 256),
                                        construct.ULInt16('termination'),
                                        construct.ULInt16('exit'),
                                        construct.ULInt32('session'),
                                        construct.ULInt32('timestamp'),
                                        construct.ULInt32('microsecond'),
                                        construct.ULInt32('address_a'),
                                        construct.ULInt32('address_b'),
                                        construct.ULInt32('address_c'),
                                        construct.ULInt32('address_d'),
                                        construct.Padding(20))

    LINUX_UTMP_ENTRY_SIZE = LINUX_UTMP_ENTRY.sizeof()

    STATUS_TYPE = {
        0: 'EMPTY',
        1: 'RUN_LVL',
        2: 'BOOT_TIME',
        3: 'NEW_TIME',
        4: 'OLD_TIME',
        5: 'INIT_PROCESS',
        6: 'LOGIN_PROCESS',
        7: 'USER_PROCESS',
        8: 'DEAD_PROCESS',
        9: 'ACCOUNTING'
    }

    # Set a default test value for few fields, this is supposed to be a text
    # that is highly unlikely to be seen in a terminal field, or a username field.
    # It is important that this value does show up in such fields, but otherwise
    # it can be a free flowing text field.
    _DEFAULT_TEST_VALUE = u'Ekki Fraedilegur Moguleiki, thetta er bull ! = + _<>'

    def ParseFileObject(self, parser_mediator, file_object, **kwargs):
        """Parses an UTMP file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: The file-like object to extract data from.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
        file_object.seek(0, os.SEEK_SET)
        try:
            structure = self.LINUX_UTMP_ENTRY.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse UTMP Header with error: {0:s}'.format(
                    exception))

        if structure.type not in self.STATUS_TYPE:
            raise errors.UnableToParseFile(
                (u'Not an UTMP file, unknown type '
                 u'[{0:d}].').format(structure.type))

        if not self._VerifyTextField(structure.terminal):
            raise errors.UnableToParseFile(
                u'Not an UTMP file, unknown terminal.')

        if not self._VerifyTextField(structure.username):
            raise errors.UnableToParseFile(
                u'Not an UTMP file, unknown username.')

        if not self._VerifyTextField(structure.hostname):
            raise errors.UnableToParseFile(
                u'Not an UTMP file, unknown hostname.')

        # Check few values.
        terminal = self._GetTextFromNullTerminatedString(
            structure.terminal, self._DEFAULT_TEST_VALUE)
        if terminal == self._DEFAULT_TEST_VALUE:
            raise errors.UnableToParseFile(
                u'Not an UTMP file, no terminal set.')

        username = self._GetTextFromNullTerminatedString(
            structure.username, self._DEFAULT_TEST_VALUE)

        if username == self._DEFAULT_TEST_VALUE:
            raise errors.UnableToParseFile(
                u'Not an UTMP file, no username set.')

        if not structure.timestamp:
            raise errors.UnableToParseFile(
                u'Not an UTMP file, no timestamp set in the first record.')

        file_object.seek(0, os.SEEK_SET)
        event_object = self._ReadUtmpEvent(file_object)
        while event_object:
            event_object.offset = file_object.tell()
            parser_mediator.ProduceEvent(event_object)
            event_object = self._ReadUtmpEvent(file_object)

    def _VerifyTextField(self, text):
        """Check if a byte stream is a null terminated string.

    Args:
      event_object: text field from the structure.

    Return:
      True if it is a null terminated string, False otherwise.
    """
        _, _, null_chars = text.partition(b'\x00')
        if not null_chars:
            return False
        return len(null_chars) == null_chars.count(b'\x00')

    def _ReadUtmpEvent(self, file_object):
        """Returns an UtmpEvent from a single UTMP entry.

    Args:
      file_object: a file-like object that points to an UTMP file.

    Returns:
      An event object constructed from a single UTMP record or None if we
      have reached the end of the file (or EOF).
    """
        offset = file_object.tell()
        data = file_object.read(self.LINUX_UTMP_ENTRY_SIZE)
        if not data or len(data) != self.LINUX_UTMP_ENTRY_SIZE:
            return
        try:
            entry = self.LINUX_UTMP_ENTRY.parse(data)
        except (IOError, construct.FieldError):
            logging.warning(
                (u'UTMP entry at 0x{:x} couldn\'t be parsed.').format(offset))
            return self._ReadUtmpEvent(file_object)

        user = self._GetTextFromNullTerminatedString(entry.username)
        terminal = self._GetTextFromNullTerminatedString(entry.terminal)
        if terminal == '~':
            terminal = u'system boot'
        computer_name = self._GetTextFromNullTerminatedString(entry.hostname)
        if computer_name == u'N/A' or computer_name == u':0':
            computer_name = u'localhost'
        status = self.STATUS_TYPE.get(entry.type, u'N/A')

        if not entry.address_b:
            try:
                ip_address = socket.inet_ntoa(
                    construct.ULInt32('int').build(entry.address_a))
                if ip_address == '0.0.0.0':
                    ip_address = u'localhost'
            except (IOError, construct.FieldError, socket.error):
                ip_address = u'N/A'
        else:
            ip_address = u'{0:d}.{1:d}.{2:d}.{3:d}'.format(
                entry.address_a, entry.address_b, entry.address_c,
                entry.address_d)

        return UtmpEvent(entry.timestamp, entry.microsecond, user,
                         computer_name, terminal, status, ip_address, entry)

    def _GetTextFromNullTerminatedString(self,
                                         null_terminated_string,
                                         default_string=u'N/A'):
        """Get a UTF-8 text from a raw null terminated string.

    Args:
      null_terminated_string: Raw string terminated with null character.
      default_string: The default string returned if the parser fails.

    Returns:
      A decoded UTF-8 string or if unable to decode, the supplied default
      string.
    """
        text, _, _ = null_terminated_string.partition('\x00')
        try:
            text = text.decode('utf-8')
        except UnicodeDecodeError:
            logging.warning(
                u'[UTMP] Decode UTF8 failed, the message string may be cut short.'
            )
            text = text.decode('utf-8', 'ignore')
        if not text:
            return default_string
        return text
Example #25
0
class BagMRUPlugin(interface.KeyPlugin):
    """Class that defines a BagMRU Windows Registry plugin."""

    NAME = 'winreg_bagmru'
    DESCRIPTION = u'Parser for BagMRU Registry data.'

    # TODO: remove REG_TYPE and use HKEY_CURRENT_USER instead.
    REG_TYPE = 'any'

    REG_KEYS = frozenset([
        u'\\Software\\Microsoft\\Windows\\Shell\\BagMRU',
        u'\\Software\\Microsoft\\Windows\\ShellNoRoam\\BagMRU',
        (u'\\Local Settings\\Software\\Microsoft\\Windows\\'
         u'Shell\\BagMRU'),
        (u'\\Local Settings\\Software\\Microsoft\\Windows\\'
         u'ShellNoRoam\\BagMRU'),
        (u'\\Wow6432Node\\Local Settings\\Software\\'
         u'Microsoft\\Windows\\Shell\\BagMRU'),
        (u'\\Wow6432Node\\Local Settings\\Software\\'
         u'Microsoft\\Windows\\ShellNoRoam\\BagMRU')
    ])

    URLS = [u'https://code.google.com/p/winreg-kb/wiki/MRUKeys']

    _MRULISTEX_ENTRY = construct.ULInt32(u'entry_number')

    def _ParseMRUListExEntryValue(self,
                                  parser_mediator,
                                  key,
                                  entry_index,
                                  entry_number,
                                  text_dict,
                                  value_strings,
                                  parent_path_segments,
                                  codepage='cp1252',
                                  **unused_kwargs):
        """Parses the MRUListEx entry value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: the Registry key (instance of winreg.WinRegKey) that contains
           the MRUListEx value.
      entry_index: integer value representing the MRUListEx entry index.
      entry_number: integer value representing the entry number.
      text_dict: text dictionary object to append textual strings.
      value_strings: value string dictionary object to append value strings.
      parent_path_segments: list containing the parent shell item path segments.
      codepage: Optional extended ASCII string codepage. The default is cp1252.

    Returns:
      The path segment of the shell item.
    """
        value = key.GetValue(u'{0:d}'.format(entry_number))
        path_segment = u'N/A'
        value_string = u''
        if value is None:
            parser_mediator.ProduceParseError(
                u'Missing MRUListEx entry value: {0:d} in key: {1:s}.'.format(
                    entry_number, key.path))

        elif not value.DataIsBinaryData():
            parser_mediator.ProduceParseError(
                u'Non-binary MRUListEx entry value: {0:d} in key: {1:s}.'.
                format(entry_number, key.path))

        elif value.data:
            shell_items_parser = shell_items.ShellItemsParser(key.path)
            shell_items_parser.UpdateChainAndParse(parser_mediator,
                                                   value.data,
                                                   parent_path_segments,
                                                   codepage=codepage)

            path_segment = shell_items_parser.GetUpperPathSegment()
            value_string = shell_items_parser.CopyToPath()

            value_strings[entry_number] = value_string

            value_string = u'Shell item path: {0:s}'.format(value_string)

        value_text = u'Index: {0:d} [MRU Value {1:d}]'.format(
            entry_index + 1, entry_number)

        text_dict[value_text] = value_string

        return path_segment

    def _ParseMRUListExValue(self, parser_mediator, key):
        """Parses the MRUListEx value in a given Registry key.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: the Registry key (instance of winreg.WinRegKey) that contains
           the MRUListEx value.

    Yields:
      A tuple of the MRUListEx index and entry number, where 0 is the first
      index value.
    """
        mru_list_value = key.GetValue(u'MRUListEx')
        if mru_list_value:
            mrulistex_data = mru_list_value.data
            data_size = len(mrulistex_data)
            _, remainder = divmod(data_size, 4)

            if remainder != 0:
                parser_mediator.ProduceParseError(
                    (u'MRUListEx value data size is not a multitude of 4 '
                     u'in MRU key: {0:s}').format(key.path))
                data_size -= remainder

            entry_index = 0
            data_offset = 0
            while data_offset < data_size:
                try:
                    entry_number = self._MRULISTEX_ENTRY.parse(
                        mrulistex_data[data_offset:])
                    yield entry_index, entry_number
                except construct.FieldError:
                    parser_mediator.ProduceParseError((
                        u'Unable to parse MRUListEx value data at offset: {0:d} '
                        u'in MRU key: {1:s}').format(data_offset, key.path))

                entry_index += 1
                data_offset += 4

    def _ParseSubKey(self,
                     parser_mediator,
                     key,
                     parent_path_segments,
                     registry_type=None,
                     codepage='cp1252'):
        """Extract event objects from a MRUListEx Registry key.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: the Registry key (instance of winreg.WinRegKey).
      parent_path_segments: list containing the parent shell item path segments.
      registry_type: Optional Registry type string. The default is None.
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
        entry_numbers = {}
        text_dict = {}
        value_strings = {}

        found_terminator = False
        for index, entry_number in self._ParseMRUListExValue(
                parser_mediator, key):
            if entry_number == 0xffffffff:
                found_terminator = True
                continue

            if found_terminator:
                parser_mediator.ProduceParseError(
                    (u'Found additional MRUListEx entries after terminator '
                     u'in key: {0:s}.').format(key.path))

                # Only create one parser error per terminator.
                found_terminator = False

            path_segment = self._ParseMRUListExEntryValue(parser_mediator,
                                                          key,
                                                          index,
                                                          entry_number,
                                                          text_dict,
                                                          value_strings,
                                                          parent_path_segments,
                                                          codepage=codepage)

            entry_numbers[entry_number] = path_segment

        event_object = windows_events.WindowsRegistryEvent(
            key.last_written_timestamp,
            key.path,
            text_dict,
            offset=key.offset,
            registry_type=registry_type,
            urls=self.URLS,
            source_append=u': BagMRU')
        parser_mediator.ProduceEvent(event_object)

        for entry_number, path_segment in entry_numbers.iteritems():
            sub_key = key.GetSubkey(u'{0:d}'.format(entry_number))
            if not sub_key:
                parser_mediator.ProduceParseError(
                    u'Missing BagMRU sub key: {0:d} in key: {1:s}.'.format(
                        entry_number, key.path))
                continue

            parent_path_segments.append(path_segment)
            self._ParseSubKey(parser_mediator,
                              sub_key,
                              parent_path_segments,
                              codepage=codepage)
            _ = parent_path_segments.pop()

    def GetEntries(self,
                   parser_mediator,
                   key=None,
                   registry_type=None,
                   codepage='cp1252',
                   **unused_kwargs):
        """Extract event objects from a Registry key containing a MRUListEx value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: Optional Registry key (instance of winreg.WinRegKey).
           The default is None.
      registry_type: Optional Registry type string. The default is None.
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
        self._ParseSubKey(parser_mediator,
                          key, [],
                          registry_type=registry_type,
                          codepage=codepage)
Example #26
0
    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
Example #27
0
class CustomDestinationsParser(interface.FileObjectParser):
    """Parses .customDestinations-ms files."""

    _INITIAL_FILE_OFFSET = None

    NAME = u'custom_destinations'
    DESCRIPTION = u'Parser for *.customDestinations-ms files.'

    # 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()

    _LNK_GUID = (
        b'\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46')

    _FOOTER_SIGNATURE = 0xbabffbab

    _FILE_HEADER = construct.Struct(u'file_header',
                                    construct.ULInt32(u'unknown1'),
                                    construct.ULInt32(u'unknown2'),
                                    construct.ULInt32(u'unknown3'),
                                    construct.ULInt32(u'header_values_type'))

    _HEADER_VALUE_TYPE_0 = construct.Struct(
        u'header_value_type_0', construct.ULInt32(u'number_of_characters'),
        construct.String(u'string', lambda ctx: ctx.number_of_characters * 2),
        construct.ULInt32(u'unknown1'))

    _HEADER_VALUE_TYPE_1_OR_2 = construct.Struct(
        u'header_value_type_1_or_2', construct.ULInt32(u'unknown1'))

    _ENTRY_HEADER = construct.Struct(u'entry_header',
                                     construct.String(u'guid', 16))

    _FILE_FOOTER = construct.Struct(u'file_footer',
                                    construct.ULInt32(u'signature'))

    def _ParseLNKFile(self, parser_mediator, file_entry, file_offset,
                      remaining_file_size):
        """Parses a LNK file stored within the .customDestinations-ms file.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_entry: A file entry object (instance of dfvfs.FileEntry).
      file_offset: The offset of the LNK file data.
      remaining_file_size: The size of the data remaining in the
                           .customDestinations-ms file.

    Returns:
      The size of the LNK file data or 0 if the LNK file could not be read.
    """
        path_spec = path_spec_factory.Factory.NewPathSpec(
            definitions.TYPE_INDICATOR_DATA_RANGE,
            range_offset=file_offset,
            range_size=remaining_file_size,
            parent=file_entry.path_spec)

        display_name = u'{0:s} # 0x{1:08x}'.format(file_entry.name,
                                                   file_offset)

        try:
            lnk_file_object = resolver.Resolver.OpenFileObject(path_spec)
        except (dfvfs_errors.BackEndError, RuntimeError) as exception:
            message = (
                u'Unable to open LNK file: {0:s} with error {1:s}').format(
                    display_name, exception)
            parser_mediator.ProduceParseError(message)
            return 0

        self._WINLNK_PARSER.Parse(parser_mediator,
                                  lnk_file_object,
                                  display_name=display_name)

        # We cannot trust the file size in the LNK data so we get the last offset
        # that was read instead.
        lnk_file_size = lnk_file_object.get_offset()

        lnk_file_object.close()

        return lnk_file_size

    def ParseFileObject(self, parser_mediator, file_object, **kwargs):
        """Parses a .customDestinations-ms file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
        file_entry = parser_mediator.GetFileEntry()
        display_name = parser_mediator.GetDisplayName()

        file_object.seek(0, os.SEEK_SET)
        try:
            file_header = self._FILE_HEADER.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                (u'Invalid Custom Destination: {0:s} - unable to parse '
                 u'file header with error: {1:s}').format(
                     display_name, exception))

        if file_header.unknown1 != 2:
            raise errors.UnableToParseFile((
                u'Unsupported Custom Destination file: {0:s} - invalid unknown1: '
                u'{1:d}.').format(display_name, file_header.unknown1))

        if file_header.header_values_type > 2:
            raise errors.UnableToParseFile((
                u'Unsupported Custom Destination file: {0:s} - invalid header value '
                u'type: {1:d}.').format(display_name,
                                        file_header.header_values_type))

        if file_header.header_values_type == 0:
            data_structure = self._HEADER_VALUE_TYPE_0
        else:
            data_structure = self._HEADER_VALUE_TYPE_1_OR_2

        try:
            _ = data_structure.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                (u'Invalid Custom Destination file: {0:s} - unable to parse '
                 u'header value with error: {1:s}').format(
                     display_name, exception))

        file_size = file_object.get_size()
        file_offset = file_object.get_offset()
        remaining_file_size = file_size - file_offset

        # The Custom Destination file does not have a unique signature in
        # the file header that is why we use the first LNK class identifier (GUID)
        # as a signature.
        first_guid_checked = False
        while remaining_file_size > 4:
            try:
                entry_header = self._ENTRY_HEADER.parse_stream(file_object)
            except (IOError, construct.FieldError) as exception:
                error_message = (
                    u'Invalid Custom Destination file: {0:s} - unable to parse '
                    u'entry header with error: {1:s}').format(
                        display_name, exception)

                if not first_guid_checked:
                    raise errors.UnableToParseFile(error_message)

                logging.warning(error_message)
                break

            if entry_header.guid != self._LNK_GUID:
                error_message = (
                    u'Unsupported Custom Destination file: {0:s} - invalid entry '
                    u'header.').format(display_name)

                if not first_guid_checked:
                    raise errors.UnableToParseFile(error_message)

                file_object.seek(-16, os.SEEK_CUR)
                try:
                    file_footer = self._FILE_FOOTER.parse_stream(file_object)
                except (IOError, construct.FieldError) as exception:
                    raise IOError(
                        (u'Unable to parse file footer at offset: 0x{0:08x} '
                         u'with error: {1:s}').format(file_offset, exception))

                if file_footer.signature != self._FOOTER_SIGNATURE:
                    logging.warning(error_message)

                file_object.seek(-4, os.SEEK_CUR)

                # TODO: add support for Jump List LNK file recovery.
                break

            first_guid_checked = True
            file_offset += 16
            remaining_file_size -= 16

            lnk_file_size = self._ParseLNKFile(parser_mediator, file_entry,
                                               file_offset,
                                               remaining_file_size)

            file_offset += lnk_file_size
            remaining_file_size -= lnk_file_size

            file_object.seek(file_offset, os.SEEK_SET)

        try:
            file_footer = self._FILE_FOOTER.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            logging.warning(
                (u'Invalid Custom Destination file: {0:s} - unable to parse '
                 u'footer with error: {1:s}').format(display_name, exception))

        if file_footer.signature != self._FOOTER_SIGNATURE:
            logging.warning((
                u'Unsupported Custom Destination file: {0:s} - invalid footer '
                u'signature.').format(display_name))
Example #28
0
class NTFSUsnJrnlParser(interface.FileObjectParser):
  """Parses a NTFS USN change journal."""

  _INITIAL_FILE_OFFSET = None

  NAME = u'usnjrnl'
  DESCRIPTION = u'Parser for NTFS USN change journal ($UsnJrnl).'

  _USN_RECORD_V2 = construct.Struct(
      u'usn_record_v2',
      construct.ULInt32(u'size'),
      construct.ULInt16(u'major_version'),
      construct.ULInt16(u'minor_version'),
      construct.ULInt64(u'file_reference'),
      construct.ULInt64(u'parent_file_reference'),
      construct.ULInt64(u'update_sequence_number'),
      construct.ULInt64(u'update_date_time'),
      construct.ULInt32(u'update_reason_flags'),
      construct.ULInt32(u'update_source_flags'),
      construct.ULInt32(u'security_descriptor_identifier'),
      construct.ULInt32(u'file_attribute_flags'),
      construct.ULInt16(u'name_size'),
      construct.ULInt16(u'name_offset'),
      construct.String(u'name', lambda ctx: ctx.size - 60))

  # TODO: add support for USN_RECORD_V3 when actually seen to be used.

  def _ParseUSNChangeJournal(self, parser_mediator, usn_change_journal):
    """Parses an USN change journal.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      usn_change_journal: An USN change journal object (instance of
                          pyfsntsfs.usn_change_journal).
    """
    if not usn_change_journal:
      return

    usn_record_data = usn_change_journal.read_usn_record()
    while usn_record_data:
      current_offset = usn_change_journal.get_offset()

      try:
        usn_record_struct = self._USN_RECORD_V2.parse(usn_record_data)
      except (IOError, construct.FieldError) as exception:
        parser_mediator.ProduceParseError((
            u'unable to parse USN record at offset: 0x{0:08x} '
            u'with error: {1:s}').format(current_offset, exception))
        continue

      name_offset = usn_record_struct.name_offset - 60
      utf16_stream = usn_record_struct.name[
          name_offset:usn_record_struct.name_size]

      try:
        name_string = utf16_stream.decode(u'utf-16-le')
      except (UnicodeDecodeError, UnicodeEncodeError) as exception:
        name_string = utf16_stream.decode(u'utf-16-le', errors=u'replace')
        parser_mediator.ProduceParseError((
            u'unable to decode USN record name string with error: '
            u'{0:s}. Characters that cannot be decoded will be replaced '
            u'with "?" or "\\ufffd".').format(exception))

      event_object = file_system_events.NTFSUSNChangeEvent(
          usn_record_struct.update_date_time, current_offset,
          name_string, usn_record_struct.file_reference,
          usn_record_struct.update_sequence_number,
          usn_record_struct.update_source_flags,
          usn_record_struct.update_reason_flags,
          file_attribute_flags=usn_record_struct.file_attribute_flags,
          parent_file_reference=usn_record_struct.parent_file_reference)
      parser_mediator.ProduceEvent(event_object)

      usn_record_data = usn_change_journal.read_usn_record()

  def ParseFileObject(self, parser_mediator, file_object, **kwargs):
    """Parses a NTFS $UsnJrnl metadata file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.
    """
    volume = pyfsntfs.volume()
    try:
      volume.open_file_object(file_object)
    except IOError as exception:
      parser_mediator.ProduceParseError(
          u'unable to open NTFS volume with error: {0:s}'.format(exception))

    try:
      usn_change_journal = volume.get_usn_change_journal()
      self._ParseUSNChangeJournal(parser_mediator, usn_change_journal)
    finally:
      volume.close()
Example #29
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
Example #30
0
    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