Exemple #1
0
    def testParseLinkTargetIdentifier(self):
        """Tests the Parse function on an LNK with a link target identifier."""
        parser = winlnk.WinLnkParser()
        storage_writer = self._ParseFile(['NeroInfoTool.lnk'], parser)

        self.assertEqual(storage_writer.number_of_warnings, 0)
        self.assertEqual(storage_writer.number_of_events, 20)

        events = list(storage_writer.GetEvents())

        # A shortcut event.
        event = events[16]

        expected_message = (
            '[Nero InfoTool provides you with information about the most '
            'important features of installed drives, inserted discs, installed '
            'software and much more. With Nero InfoTool you can find out all '
            'about your drive and your system configuration.] '
            'File size: 4635160 '
            'File attribute flags: 0x00000020 '
            'Drive type: 3 '
            'Drive serial number: 0x70ecfa33 '
            'Volume label: OS '
            'Local path: C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool\\'
            'InfoTool.exe '
            'cmd arguments: -ScParameter=30002   '
            'Relative path: ..\\..\\..\\..\\..\\..\\..\\..\\Program Files (x86)\\'
            'Nero\\Nero 9\\Nero InfoTool\\InfoTool.exe '
            'Working dir: C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool '
            'Icon location: %ProgramFiles%\\Nero\\Nero 9\\Nero InfoTool\\'
            'InfoTool.exe '
            'Link target: <My Computer> C:\\Program Files (x86)\\Nero\\Nero 9\\'
            'Nero InfoTool\\InfoTool.exe')

        expected_short_message = (
            '[Nero InfoTool provides you with information about the most '
            'important feature...')

        self._TestGetMessageStrings(event, expected_message,
                                    expected_short_message)

        # A shell item event.
        event = events[12]

        self.CheckTimestamp(event.timestamp, '2009-06-05 20:13:20.000000')

        expected_message = (
            'Name: InfoTool.exe '
            'Long name: InfoTool.exe '
            'NTFS file reference: 81349-1 '
            'Shell item path: <My Computer> C:\\Program Files (x86)\\Nero\\'
            'Nero 9\\Nero InfoTool\\InfoTool.exe '
            'Origin: NeroInfoTool.lnk')

        expected_short_message = ('Name: InfoTool.exe '
                                  'NTFS file reference: 81349-1 '
                                  'Origin: NeroInfoTool.lnk')

        self._TestGetMessageStrings(event, expected_message,
                                    expected_short_message)
Exemple #2
0
  def testParseLinkTargetIdentifier(self):
    """Tests the Parse function on an LNK with a link target identifier."""
    parser = winlnk.WinLnkParser()
    storage_writer = self._ParseFile(['NeroInfoTool.lnk'], parser)

    number_of_events = storage_writer.GetNumberOfAttributeContainers('event')
    self.assertEqual(number_of_events, 20)

    number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
        'extraction_warning')
    self.assertEqual(number_of_warnings, 0)

    number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
        'recovery_warning')
    self.assertEqual(number_of_warnings, 0)

    events = list(storage_writer.GetEvents())

    # A shortcut creation event.
    expected_event_values = {
        'date_time': '2009-06-05 20:13:20.0000000',
        'data_type': 'windows:lnk:link',
        'description': (
            'Nero InfoTool provides you with information about the most '
            'important features of installed drives, inserted discs, installed '
            'software and much more. With Nero InfoTool you can find out all '
            'about your drive and your system configuration.'),
        'drive_serial_number': 0x70ecfa33,
        'drive_type': 3,
        'file_attribute_flags': 0x00000020,
        'file_size': 4635160,
        'icon_location': (
            '%ProgramFiles%\\Nero\\Nero 9\\Nero InfoTool\\InfoTool.exe'),
        'local_path': (
            'C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool\\'
            'InfoTool.exe'),
        'relative_path': (
            '..\\..\\..\\..\\..\\..\\..\\..\\Program Files (x86)\\'
            'Nero\\Nero 9\\Nero InfoTool\\InfoTool.exe'),
        'timestamp_desc': definitions.TIME_DESCRIPTION_CREATION,
        'volume_label': 'OS',
        'working_directory': (
            'C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool')}

    self.CheckEventValues(storage_writer, events[16], expected_event_values)

    # A shell item event.
    expected_event_values = {
        'date_time': '2009-06-05 20:13:20',
        'data_type': 'windows:shell_item:file_entry',
        'file_reference': '81349-1',
        'long_name': 'InfoTool.exe',
        'name': 'InfoTool.exe',
        'origin': 'NeroInfoTool.lnk',
        'shell_item_path': (
            '<My Computer> C:\\Program Files (x86)\\Nero\\Nero 9\\'
            'Nero InfoTool\\InfoTool.exe'),
        'timestamp_desc': definitions.TIME_DESCRIPTION_CREATION}

    self.CheckEventValues(storage_writer, events[12], expected_event_values)
Exemple #3
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))
Exemple #4
0
 def setUp(self):
   """Sets up the needed objects used throughout the test."""
   self._parser = winlnk.WinLnkParser()
Exemple #5
0
class CustomDestinationsParser(interface.SingleFileBaseParser):
    """Parses .customDestinations-ms files."""

    _INITIAL_FILE_OFFSET = None

    NAME = '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 = '\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46'

    _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 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_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'Unable to parse Custom Destination file header with error: '
                 u'{0:s}').format(exception))

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

        if file_header.header_values_type > 2:
            raise errors.UnableToParseFile((
                u'Unsupported Custom Destination file - invalid header value type: '
                u'{0:d}.').format(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'Unable to parse Custom Destination file header value with error: '
                u'{0:s}').format(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:
                if not first_guid_checked:
                    raise errors.UnableToParseFile((
                        u'Unable to parse Custom Destination file entry header with '
                        u'error: {0:s}').format(exception))
                else:
                    logging.warning((
                        u'Unable to parse Custom Destination file entry header with '
                        u'error: {0:s}').format(exception))
                break

            if entry_header.guid != self._LNK_GUID:
                if not first_guid_checked:
                    raise errors.UnableToParseFile(
                        u'Unsupported Custom Destination file - invalid entry header.'
                    )
                else:
                    logging.warning(
                        u'Unsupported Custom Destination file - invalid entry header.'
                    )
                break

            first_guid_checked = True
            file_offset += 16
            remaining_file_size -= 16

            path_spec = path_spec_factory.Factory.NewPathSpec(
                definitions.TYPE_INDICATOR_DATA_RANGE,
                range_offset=file_offset,
                range_size=remaining_file_size,
                parent=parser_mediator.GetFileEntry().path_spec)

            try:
                lnk_file_object = resolver.Resolver.OpenFileObject(path_spec)
            except RuntimeError as exception:
                message = u'Unable to open LNK file with error'.format(
                    exception)
                parser_mediator.ProduceParseError(message)
                return

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

            self._WINLNK_PARSER.UpdateChainAndParseFileObject(
                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()

            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'Unable to parse Custom Destination file footer with error: '
                 u'{0:s}').format(exception))

        if file_footer.signature != 0xbabffbab:
            logging.warning(
                u'Unsupported Custom Destination file - invalid footer signature.'
            )
class AutomaticDestinationsOLECFPlugin(
    interface.OLECFPlugin, dtfabric_helper.DtFabricHelper):
  """Plugin that parses an .automaticDestinations-ms OLECF file."""

  NAME = 'olecf_automatic_destinations'
  DATA_FORMAT = (
      'Automatic destinations jump list OLE compound file '
      '(.automaticDestinations-ms)')

  REQUIRED_ITEMS = frozenset(['DestList'])

  _DEFINITION_FILE = os.path.join(
      os.path.dirname(__file__), 'automatic_destinations.yaml')

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

  def _ParseDistributedTrackingIdentifier(
      self, parser_mediator, uuid_object, origin):
    """Extracts data from a Distributed Tracking identifier.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      uuid_object (uuid.UUID): UUID of the Distributed Tracking identifier.
      origin (str): origin of the event (event source).

    Returns:
      str: UUID string of the Distributed Tracking identifier.
    """
    if uuid_object.version == 1:
      event_data = windows_events.WindowsDistributedLinkTrackingEventData(
          uuid_object, origin)
      date_time = dfdatetime_uuid_time.UUIDTime(timestamp=uuid_object.time)
      event = time_events.DateTimeValuesEvent(
          date_time, definitions.TIME_DESCRIPTION_CREATION)
      parser_mediator.ProduceEventWithEventData(event, event_data)

    return '{{{0!s}}}'.format(uuid_object)

  def ParseDestList(self, parser_mediator, olecf_item):
    """Parses the DestList OLECF item.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      olecf_item (pyolecf.item): OLECF item.

    Raises:
      UnableToParseFile: if the DestList cannot be parsed.
    """
    if olecf_item.size == 0:
      parser_mediator.ProduceExtractionWarning('empty DestList stream')
      return

    header_map = self._GetDataTypeMap('dest_list_header')

    try:
      header, entry_offset = self._ReadStructureFromFileObject(
          olecf_item, 0, header_map)
    except (ValueError, errors.ParseError) as exception:
      raise errors.UnableToParseFile(
          'Unable to parse DestList header with error: {0!s}'.format(
              exception))

    if header.format_version == 1:
      entry_map = self._GetDataTypeMap('dest_list_entry_v1')
    elif header.format_version in (3, 4):
      entry_map = self._GetDataTypeMap('dest_list_entry_v3')
    else:
      parser_mediator.ProduceExtractionWarning(
          'unsupported format version: {0:d}.'.format(header.format_version))
      return

    while entry_offset < olecf_item.size:
      try:
        entry, entry_data_size = self._ReadStructureFromFileObject(
            olecf_item, entry_offset, entry_map)
      except (ValueError, errors.ParseError) as exception:
        raise errors.UnableToParseFile(
            'Unable to parse DestList entry with error: {0!s}'.format(
                exception))

      display_name = 'DestList entry at offset: 0x{0:08x}'.format(entry_offset)

      try:
        droid_volume_identifier = self._ParseDistributedTrackingIdentifier(
            parser_mediator, entry.droid_volume_identifier, display_name)

      except (TypeError, ValueError) as exception:
        droid_volume_identifier = ''
        parser_mediator.ProduceExtractionWarning(
            'unable to read droid volume identifier with error: {0!s}'.format(
                exception))

      try:
        droid_file_identifier = self._ParseDistributedTrackingIdentifier(
            parser_mediator, entry.droid_file_identifier, display_name)

      except (TypeError, ValueError) as exception:
        droid_file_identifier = ''
        parser_mediator.ProduceExtractionWarning(
            'unable to read droid file identifier with error: {0!s}'.format(
                exception))

      try:
        birth_droid_volume_identifier = (
            self._ParseDistributedTrackingIdentifier(
                parser_mediator, entry.birth_droid_volume_identifier,
                display_name))

      except (TypeError, ValueError) as exception:
        birth_droid_volume_identifier = ''
        parser_mediator.ProduceExtractionWarning((
            'unable to read birth droid volume identifier with error: '
            '{0:s}').format(
                exception))

      try:
        birth_droid_file_identifier = self._ParseDistributedTrackingIdentifier(
            parser_mediator, entry.birth_droid_file_identifier, display_name)

      except (TypeError, ValueError) as exception:
        birth_droid_file_identifier = ''
        parser_mediator.ProduceExtractionWarning((
            'unable to read birth droid file identifier with error: '
            '{0:s}').format(
                exception))

      if entry.last_modification_time == 0:
        date_time = dfdatetime_semantic_time.NotSet()
      else:
        date_time = dfdatetime_filetime.Filetime(
            timestamp=entry.last_modification_time)

      event_data = AutomaticDestinationsDestListEntryEventData()
      event_data.birth_droid_file_identifier = birth_droid_file_identifier
      event_data.birth_droid_volume_identifier = birth_droid_volume_identifier
      event_data.droid_file_identifier = droid_file_identifier
      event_data.droid_volume_identifier = droid_volume_identifier
      event_data.entry_number = entry.entry_number
      event_data.hostname = entry.hostname.rstrip('\x00')
      event_data.offset = entry_offset
      event_data.path = entry.path.rstrip('\x00')
      event_data.pin_status = entry.pin_status

      event = time_events.DateTimeValuesEvent(
          date_time, definitions.TIME_DESCRIPTION_MODIFICATION)
      parser_mediator.ProduceEventWithEventData(event, event_data)

      entry_offset += entry_data_size

  def Process(self, parser_mediator, root_item=None, **kwargs):
    """Extracts events from an OLECF file.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      root_item (Optional[pyolecf.item]): root item of the OLECF file.

    Raises:
      ValueError: If the root_item is not set.
    """
    # This will raise if unhandled keyword arguments are passed.
    super(AutomaticDestinationsOLECFPlugin, self).Process(
        parser_mediator, **kwargs)

    if not root_item:
      raise ValueError('Root item not set.')

    for item in root_item.sub_items:
      if item.name == 'DestList':
        self.ParseDestList(parser_mediator, item)

      elif self._RE_LNK_ITEM_NAME.match(item.name):
        display_name = parser_mediator.GetDisplayName()
        if display_name:
          display_name = '{0:s} # {1:s}'.format(display_name, item.name)
        else:
          display_name = '# {0:s}'.format(item.name)

        parser_mediator.AppendToParserChain(self._WINLNK_PARSER)
        try:
          item.seek(0, os.SEEK_SET)
          self._WINLNK_PARSER.ParseFileLNKFile(
              parser_mediator, item, display_name)
        finally:
          parser_mediator.PopFromParserChain()
Exemple #7
0
    def testParse(self):
        """Tests the Parse function."""
        parser = winlnk.WinLnkParser()
        storage_writer = self._ParseFile(['example.lnk'], parser)

        # Link information:
        # 	Creation time			: Jul 13, 2009 23:29:02.849131000 UTC
        # 	Modification time		: Jul 14, 2009 01:39:18.220000000 UTC
        # 	Access time			: Jul 13, 2009 23:29:02.849131000 UTC
        # 	Description			: @%windir%\system32\migwiz\wet.dll,-590
        # 	Relative path			: .\migwiz\migwiz.exe
        # 	Working directory		: %windir%\system32\migwiz
        # 	Icon location			: %windir%\system32\migwiz\migwiz.exe
        # 	Environment variables location	: %windir%\system32\migwiz\migwiz.exe

        self.assertEqual(storage_writer.number_of_events, 5)

        events = list(storage_writer.GetEvents())

        # A shortcut event.
        event = events[0]

        expected_string = '@%windir%\\system32\\migwiz\\wet.dll,-590'
        self.assertEqual(event.description, expected_string)

        expected_string = '.\\migwiz\\migwiz.exe'
        self.assertEqual(event.relative_path, expected_string)

        expected_string = '%windir%\\system32\\migwiz'
        self.assertEqual(event.working_directory, expected_string)

        expected_string = '%windir%\\system32\\migwiz\\migwiz.exe'
        self.assertEqual(event.icon_location, expected_string)
        self.assertEqual(event.env_var_location, expected_string)

        # The last accessed timestamp.
        expected_timestamp = timelib.Timestamp.CopyFromString(
            '2009-07-13 23:29:02.849131')
        self.assertEqual(event.timestamp_desc,
                         definitions.TIME_DESCRIPTION_LAST_ACCESS)
        self.assertEqual(event.timestamp, expected_timestamp)

        # The creation timestamp.
        event = events[1]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            '2009-07-13 23:29:02.849131')
        self.assertEqual(event.timestamp_desc,
                         definitions.TIME_DESCRIPTION_CREATION)
        self.assertEqual(event.timestamp, expected_timestamp)

        # The last modification timestamp.
        event = events[2]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            '2009-07-14 01:39:18.220000')
        self.assertEqual(event.timestamp_desc,
                         definitions.TIME_DESCRIPTION_MODIFICATION)
        self.assertEqual(event.timestamp, expected_timestamp)

        expected_message = (
            '[@%windir%\\system32\\migwiz\\wet.dll,-590] '
            'File size: 544768 '
            'File attribute flags: 0x00000020 '
            'env location: %windir%\\system32\\migwiz\\migwiz.exe '
            'Relative path: .\\migwiz\\migwiz.exe '
            'Working dir: %windir%\\system32\\migwiz '
            'Icon location: %windir%\\system32\\migwiz\\migwiz.exe')

        expected_short_message = (
            '[@%windir%\\system32\\migwiz\\wet.dll,-590] '
            '%windir%\\system32\\migwiz\\.\\migwiz\\mi...')

        self._TestGetMessageStrings(event, expected_message,
                                    expected_short_message)

        # A distributed link tracking event.
        event = events[4]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            '2009-07-14 05:45:20.500012')
        self.assertEqual(event.timestamp_desc,
                         definitions.TIME_DESCRIPTION_CREATION)
        self.assertEqual(event.timestamp, expected_timestamp)

        expected_uuid = '846ee3bb-7039-11de-9d20-001d09fa5a1c'
        self.assertEqual(event.uuid, expected_uuid)
        self.assertEqual(event.mac_address, '00:1d:09:fa:5a:1c')
Exemple #8
0
 def setUp(self):
     """Sets up the needed objects used throughout the test."""
     pre_obj = event.PreprocessObject()
     self._parser = winlnk.WinLnkParser(pre_obj, None)
Exemple #9
0
class CustomDestinationsParser(dtfabric_parser.DtFabricBaseParser):
    """Parses custom destinations jump list (.customDestinations-ms) files."""

    NAME = 'custom_destinations'
    DATA_FORMAT = 'Custom destinations jump list (.customDestinations-ms) file'

    _INITIAL_FILE_OFFSET = None

    _DEFINITION_FILE = 'custom_destinations.yaml'

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

    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 (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_entry (dfvfs.FileEntry): a file entry.
      file_offset (int): offset to the LNK file, relative to the start of the
          .customDestinations-ms file.
      remaining_file_size (int): size of the data remaining in the
          .customDestinations-ms file.

    Returns:
      int: 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 = '{0:s} # 0x{1:08x}'.format(file_entry.name, file_offset)

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

        parser_mediator.AppendToParserChain(self._WINLNK_PARSER)
        try:
            lnk_file_object.seek(0, os.SEEK_SET)
            self._WINLNK_PARSER.ParseFileLNKFile(parser_mediator,
                                                 lnk_file_object, display_name)
        finally:
            parser_mediator.PopFromParserChain()

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

    @classmethod
    def GetFormatSpecification(cls):
        """Retrieves the format specification.

    Returns:
      FormatSpecification: format specification.
    """
        format_specification = specification.FormatSpecification(cls.NAME)
        format_specification.AddNewSignature(b'\xab\xfb\xbf\xba', offset=-4)
        return format_specification

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

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): a file-like object.

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

        file_header_map = self._GetDataTypeMap('custom_file_header')

        try:
            file_header, file_offset = self._ReadStructureFromFileObject(
                file_object, 0, file_header_map)
        except (ValueError, errors.ParseError) as exception:
            raise errors.UnableToParseFile((
                'Invalid Custom Destination: {0:s} - unable to parse file header '
                'with error: {1!s}').format(display_name, exception))

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

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

        if file_header.header_values_type == 0:
            data_map_name = 'custom_file_header_value_type_0'
        else:
            data_map_name = 'custom_file_header_value_type_1_or_2'

        file_header_value_map = self._GetDataTypeMap(data_map_name)

        try:
            _, value_data_size = self._ReadStructureFromFileObject(
                file_object, file_offset, file_header_value_map)
        except (ValueError, errors.ParseError) as exception:
            raise errors.UnableToParseFile((
                'Invalid Custom Destination: {0:s} - unable to parse file header '
                'value with error: {1!s}').format(display_name, exception))

        file_offset += value_data_size
        file_size = file_object.get_size()
        remaining_file_size = file_size - file_offset

        entry_header_map = self._GetDataTypeMap('custom_entry_header')
        file_footer_map = self._GetDataTypeMap('custom_file_footer')

        # 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, entry_data_size = self._ReadStructureFromFileObject(
                    file_object, file_offset, entry_header_map)

            except (ValueError, errors.ParseError) as exception:
                if not first_guid_checked:
                    raise errors.UnableToParseFile((
                        'Invalid Custom Destination file: {0:s} - unable to parse '
                        'entry header with error: {1!s}').format(
                            display_name, exception))

                parser_mediator.ProduceExtractionWarning(
                    'unable to parse entry header with error: {0!s}'.format(
                        exception))
                break

            if entry_header.guid != self._LNK_GUID:
                if not first_guid_checked:
                    raise errors.UnableToParseFile((
                        'Unsupported Custom Destination file: {0:s} - invalid entry '
                        'header signature offset: 0x{1:08x}.').format(
                            display_name, file_offset))

                try:
                    # Check if we found the footer instead of an entry header.
                    self._ReadStructureFromFileObject(file_object, file_offset,
                                                      file_footer_map)

                except (ValueError, errors.ParseError) as exception:
                    parser_mediator.ProduceExtractionWarning((
                        'unable to parse footer at offset: 0x{0:08x} with error: '
                        '{1!s}').format(file_offset, exception))
                    break

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

            first_guid_checked = True
            file_offset += entry_data_size
            remaining_file_size -= entry_data_size

            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

        try:
            self._ReadStructureFromFileObject(file_object, file_offset,
                                              file_footer_map)

        except (ValueError, errors.ParseError) as exception:
            parser_mediator.ProduceExtractionWarning(
                ('unable to parse footer at offset: 0x{0:08x} with error: '
                 '{1!s}').format(file_offset, exception))
Exemple #10
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)
Exemple #11
0
 def setUp(self):
     """Makes preparations before running an individual test."""
     self._parser = winlnk.WinLnkParser()
Exemple #12
0
    def testParse(self):
        """Tests the Parse function."""
        parser_object = winlnk.WinLnkParser()

        test_file = self._GetTestFilePath([u'example.lnk'])
        event_queue_consumer = self._ParseFile(parser_object, test_file)
        event_objects = self._GetEventObjectsFromQueue(event_queue_consumer)

        # Link information:
        # 	Creation time			: Jul 13, 2009 23:29:02.849131000 UTC
        # 	Modification time		: Jul 14, 2009 01:39:18.220000000 UTC
        # 	Access time			: Jul 13, 2009 23:29:02.849131000 UTC
        # 	Description			: @%windir%\system32\migwiz\wet.dll,-590
        # 	Relative path			: .\migwiz\migwiz.exe
        # 	Working directory		: %windir%\system32\migwiz
        # 	Icon location			: %windir%\system32\migwiz\migwiz.exe
        # 	Environment variables location	: %windir%\system32\migwiz\migwiz.exe

        self.assertEqual(len(event_objects), 5)

        # A shortcut event object.
        event_object = event_objects[0]

        expected_string = u'@%windir%\\system32\\migwiz\\wet.dll,-590'
        self.assertEqual(event_object.description, expected_string)

        expected_string = u'.\\migwiz\\migwiz.exe'
        self.assertEqual(event_object.relative_path, expected_string)

        expected_string = u'%windir%\\system32\\migwiz'
        self.assertEqual(event_object.working_directory, expected_string)

        expected_string = u'%windir%\\system32\\migwiz\\migwiz.exe'
        self.assertEqual(event_object.icon_location, expected_string)
        self.assertEqual(event_object.env_var_location, expected_string)

        # The last accessed timestamp.
        expected_timestamp = timelib.Timestamp.CopyFromString(
            u'2009-07-13 23:29:02.849131')
        self.assertEqual(event_object.timestamp_desc,
                         eventdata.EventTimestamp.ACCESS_TIME)
        self.assertEqual(event_object.timestamp, expected_timestamp)

        # The creation timestamp.
        event_object = event_objects[1]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            u'2009-07-13 23:29:02.849131')
        self.assertEqual(event_object.timestamp_desc,
                         eventdata.EventTimestamp.CREATION_TIME)
        self.assertEqual(event_object.timestamp, expected_timestamp)

        # The last modification timestamp.
        event_object = event_objects[2]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            u'2009-07-14 01:39:18.220000')
        self.assertEqual(event_object.timestamp_desc,
                         eventdata.EventTimestamp.MODIFICATION_TIME)
        self.assertEqual(event_object.timestamp, expected_timestamp)

        expected_msg = (
            u'[@%windir%\\system32\\migwiz\\wet.dll,-590] '
            u'File size: 544768 '
            u'File attribute flags: 0x00000020 '
            u'env location: %windir%\\system32\\migwiz\\migwiz.exe '
            u'Relative path: .\\migwiz\\migwiz.exe '
            u'Working dir: %windir%\\system32\\migwiz '
            u'Icon location: %windir%\\system32\\migwiz\\migwiz.exe')

        expected_msg_short = (u'[@%windir%\\system32\\migwiz\\wet.dll,-590]')

        self._TestGetMessageStrings(event_object, expected_msg,
                                    expected_msg_short)

        # A distributed link tracking event object.
        event_object = event_objects[4]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            u'2009-07-14 05:45:20.500012')
        self.assertEqual(event_object.timestamp_desc,
                         eventdata.EventTimestamp.CREATION_TIME)
        self.assertEqual(event_object.timestamp, expected_timestamp)

        expected_uuid = u'846ee3bb-7039-11de-9d20-001d09fa5a1c'
        self.assertEqual(event_object.uuid, expected_uuid)
        self.assertEqual(event_object.mac_address, u'00:1d:09:fa:5a:1c')
Exemple #13
0
    def testParseLinkTargetIdentifier(self):
        """Tests the Parse function on an LNK with a link target identifier."""
        parser_object = winlnk.WinLnkParser()

        test_file = self._GetTestFilePath([u'NeroInfoTool.lnk'])
        event_queue_consumer = self._ParseFile(parser_object, test_file)
        event_objects = self._GetEventObjectsFromQueue(event_queue_consumer)

        self.assertEqual(len(event_objects), 20)

        # A shortcut event object.
        event_object = event_objects[16]

        expected_msg = (
            u'[Nero InfoTool provides you with information about the most '
            u'important features of installed drives, inserted discs, installed '
            u'software and much more. With Nero InfoTool you can find out all '
            u'about your drive and your system configuration.] '
            u'File size: 4635160 '
            u'File attribute flags: 0x00000020 '
            u'Drive type: 3 '
            u'Drive serial number: 0x70ecfa33 '
            u'Volume label: OS '
            u'Local path: C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool\\'
            u'InfoTool.exe '
            u'cmd arguments: -ScParameter=30002   '
            u'Relative path: ..\\..\\..\\..\\..\\..\\..\\..\\Program Files (x86)\\'
            u'Nero\\Nero 9\\Nero InfoTool\\InfoTool.exe '
            u'Working dir: C:\\Program Files (x86)\\Nero\\Nero 9\\Nero InfoTool '
            u'Icon location: %ProgramFiles%\\Nero\\Nero 9\\Nero InfoTool\\'
            u'InfoTool.exe '
            u'Link target: <My Computer> C:\\Program Files (x86)\\Nero\\Nero 9\\'
            u'Nero InfoTool\\InfoTool.exe')

        expected_msg_short = (
            u'[Nero InfoTool provides you with information about the most '
            u'important feature...')

        self._TestGetMessageStrings(event_object, expected_msg,
                                    expected_msg_short)

        # A shell item event object.
        event_object = event_objects[12]

        expected_timestamp = timelib.Timestamp.CopyFromString(
            u'2009-06-05 20:13:20')
        self.assertEqual(event_object.timestamp, expected_timestamp)

        expected_msg = (
            u'Name: InfoTool.exe '
            u'Long name: InfoTool.exe '
            u'NTFS file reference: 81349-1 '
            u'Shell item path: <My Computer> C:\\Program Files (x86)\\Nero\\'
            u'Nero 9\\Nero InfoTool\\InfoTool.exe '
            u'Origin: NeroInfoTool.lnk')

        expected_msg_short = (u'Name: InfoTool.exe '
                              u'NTFS file reference: 81349-1 '
                              u'Origin: NeroInfoTool.lnk')

        self._TestGetMessageStrings(event_object, expected_msg,
                                    expected_msg_short)
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'format_version'),
        construct.ULInt32(u'number_of_entries'),
        construct.ULInt32(u'number_of_pinned_entries'), construct.Padding(4),
        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_V1 = construct.Struct(
        u'dest_list_stream_entry_v1', construct.Padding(8),
        construct.Bytes(u'droid_volume_identifier', 16),
        construct.Bytes(u'droid_file_identifier', 16),
        construct.Bytes(u'birth_droid_volume_identifier', 16),
        construct.Bytes(u'birth_droid_file_identifier', 16),
        construct.String(u'hostname', 16), construct.ULInt32(u'entry_number'),
        construct.Padding(8), 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))

    _DEST_LIST_STREAM_ENTRY_V3 = construct.Struct(
        u'dest_list_stream_entry_v3', construct.Padding(8),
        construct.Bytes(u'droid_volume_identifier', 16),
        construct.Bytes(u'droid_file_identifier', 16),
        construct.Bytes(u'birth_droid_volume_identifier', 16),
        construct.Bytes(u'birth_droid_file_identifier', 16),
        construct.String(u'hostname', 16), construct.ULInt32(u'entry_number'),
        construct.Padding(8), construct.ULInt64(u'last_modification_time'),
        construct.ULInt32(u'pin_status'), construct.Padding(16),
        construct.ULInt16(u'path_size'),
        construct.String(u'path', lambda ctx: ctx.path_size * 2),
        construct.Padding(4))

    def _ParseDistributedTrackingIdentifier(self, parser_mediator, uuid_data,
                                            origin):
        """Extracts data from a Distributed Tracking identifier.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      uuid_data (bytes): UUID data of the Distributed Tracking identifier.
      origin (str): origin of the event (event source).

    Returns:
      str: UUID string of the Distributed Tracking identifier.
    """
        uuid_object = uuid.UUID(bytes_le=uuid_data)

        if uuid_object.version == 1:
            event_data = windows_events.WindowsDistributedLinkTrackingEventData(
                uuid_object, origin)
            date_time = dfdatetime_uuid_time.UUIDTime(
                timestamp=uuid_object.time)
            event = time_events.DateTimeValuesEvent(
                date_time, definitions.TIME_DESCRIPTION_CREATION)
            parser_mediator.ProduceEventWithEventData(event, event_data)

        return u'{{{0!s}}}'.format(uuid_object)

    def ParseDestList(self, parser_mediator, olecf_item):
        """Parses the DestList OLECF item.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      olecf_item (pyolecf.item): OLECF 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.format_version not in (1, 3, 4):
            parser_mediator.ProduceExtractionError(
                u'unsupported format version: {0:d}.'.format(
                    header.format_version))

        if header.format_version == 1:
            dest_list_stream_entry = self._DEST_LIST_STREAM_ENTRY_V1
        elif header.format_version in (3, 4):
            dest_list_stream_entry = self._DEST_LIST_STREAM_ENTRY_V3

        entry_offset = olecf_item.get_offset()
        while entry_offset < olecf_item.size:
            try:
                entry = 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

            display_name = u'DestList entry at offset: 0x{0:08x}'.format(
                entry_offset)

            try:
                droid_volume_identifier = self._ParseDistributedTrackingIdentifier(
                    parser_mediator, entry.droid_volume_identifier,
                    display_name)

            except (TypeError, ValueError) as exception:
                droid_volume_identifier = u''
                parser_mediator.ProduceExtractionError(
                    u'unable to read droid volume identifier with error: {0:s}'
                    .format(exception))

            try:
                droid_file_identifier = self._ParseDistributedTrackingIdentifier(
                    parser_mediator, entry.droid_file_identifier, display_name)

            except (TypeError, ValueError) as exception:
                droid_file_identifier = u''
                parser_mediator.ProduceExtractionError(
                    u'unable to read droid file identifier with error: {0:s}'.
                    format(exception))

            try:
                birth_droid_volume_identifier = (
                    self._ParseDistributedTrackingIdentifier(
                        parser_mediator, entry.birth_droid_volume_identifier,
                        display_name))

            except (TypeError, ValueError) as exception:
                birth_droid_volume_identifier = u''
                parser_mediator.ProduceExtractionError((
                    u'unable to read birth droid volume identifier with error: '
                    u'{0:s}').format(exception))

            try:
                birth_droid_file_identifier = self._ParseDistributedTrackingIdentifier(
                    parser_mediator, entry.birth_droid_file_identifier,
                    display_name)

            except (TypeError, ValueError) as exception:
                birth_droid_file_identifier = u''
                parser_mediator.ProduceExtractionError(
                    (u'unable to read birth droid file identifier with error: '
                     u'{0:s}').format(exception))

            if entry.last_modification_time == 0:
                date_time = dfdatetime_semantic_time.SemanticTime(u'Not set')
            else:
                date_time = dfdatetime_filetime.Filetime(
                    timestamp=entry.last_modification_time)

            event = AutomaticDestinationsDestListEntryEvent(
                date_time, definitions.TIME_DESCRIPTION_MODIFICATION,
                entry_offset, entry, droid_volume_identifier,
                droid_file_identifier, birth_droid_volume_identifier,
                birth_droid_file_identifier)
            parser_mediator.ProduceEvent(event)

            entry_offset = olecf_item.get_offset()

    def Process(self, parser_mediator, root_item=None, **kwargs):
        """Parses an OLECF file.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      root_item (Optional[pyolecf.item]): root item of the OLECF file.

    Raises:
      ValueError: If the root_item is not set.
    """
        # This will raise if unhandled keyword arguments are passed.
        super(AutomaticDestinationsOLECFPlugin,
              self).Process(parser_mediator, **kwargs)

        if not root_item:
            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):
                display_name = parser_mediator.GetDisplayName()
                if display_name:
                    display_name = u'{0:s} # {1:s}'.format(
                        display_name, item.name)
                else:
                    display_name = u'# {0:s}'.format(item.name)

                self._WINLNK_PARSER.Parse(parser_mediator,
                                          item,
                                          display_name=display_name)
Exemple #15
0
  def testParse(self):
    """Tests the Parse function."""
    parser = winlnk.WinLnkParser()
    storage_writer = self._ParseFile(['example.lnk'], parser)

    # Link information:
    #   Creation time                  : Jul 13, 2009 23:29:02.849131000 UTC
    #   Modification time              : Jul 14, 2009 01:39:18.220000000 UTC
    #   Access time                    : Jul 13, 2009 23:29:02.849131000 UTC
    #   Description                    : @%windir%\system32\migwiz\wet.dll,-590
    #   Relative path                  : .\migwiz\migwiz.exe
    #   Working directory              : %windir%\system32\migwiz
    #   Icon location                  : %windir%\system32\migwiz\migwiz.exe
    #   Environment variables location : %windir%\system32\migwiz\migwiz.exe

    number_of_events = storage_writer.GetNumberOfAttributeContainers('event')
    self.assertEqual(number_of_events, 5)

    number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
        'extraction_warning')
    self.assertEqual(number_of_warnings, 0)

    number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
        'recovery_warning')
    self.assertEqual(number_of_warnings, 0)

    events = list(storage_writer.GetEvents())

    # A shortcut last accessed event.
    expected_event_values = {
        'date_time': '2009-07-13 23:29:02.8491310',
        'data_type': 'windows:lnk:link',
        'description': '@%windir%\\system32\\migwiz\\wet.dll,-590',
        'env_var_location': '%windir%\\system32\\migwiz\\migwiz.exe',
        'file_attribute_flags': 0x00000020,
        'file_size': 544768,
        'icon_location': '%windir%\\system32\\migwiz\\migwiz.exe',
        'relative_path': '.\\migwiz\\migwiz.exe',
        'timestamp_desc': definitions.TIME_DESCRIPTION_LAST_ACCESS,
        'working_directory': '%windir%\\system32\\migwiz'}

    self.CheckEventValues(storage_writer, events[0], expected_event_values)

    # A shortcut creation event.
    expected_event_values = {
        'date_time': '2009-07-13 23:29:02.8491310',
        'data_type': 'windows:lnk:link',
        'timestamp_desc': definitions.TIME_DESCRIPTION_CREATION}

    self.CheckEventValues(storage_writer, events[1], expected_event_values)

    # A shortcut last modification event.
    expected_event_values = {
        'date_time': '2009-07-14 01:39:18.2200000',
        'data_type': 'windows:lnk:link',
        'description': '@%windir%\\system32\\migwiz\\wet.dll,-590',
        'env_var_location': '%windir%\\system32\\migwiz\\migwiz.exe',
        'file_attribute_flags': 0x00000020,
        'file_size': 544768,
        'icon_location': '%windir%\\system32\\migwiz\\migwiz.exe',
        'relative_path': '.\\migwiz\\migwiz.exe',
        'timestamp_desc': definitions.TIME_DESCRIPTION_MODIFICATION,
        'working_directory': '%windir%\\system32\\migwiz'}

    self.CheckEventValues(storage_writer, events[2], expected_event_values)

    # A distributed link tracking creation event.
    expected_event_values = {
        'date_time': '2009-07-14 05:45:20.5000123',
        'data_type': 'windows:distributed_link_tracking:creation',
        'mac_address': '00:1d:09:fa:5a:1c',
        'timestamp_desc': definitions.TIME_DESCRIPTION_CREATION,
        'uuid': '846ee3bb-7039-11de-9d20-001d09fa5a1c'}

    self.CheckEventValues(storage_writer, events[4], expected_event_values)
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'format_version'),
        construct.ULInt32(u'number_of_entries'),
        construct.ULInt32(u'number_of_pinned_entries'), construct.Padding(4),
        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_V1 = construct.Struct(
        u'dest_list_stream_entry_v1', construct.Padding(8),
        construct.Bytes(u'droid_volume_identifier', 16),
        construct.Bytes(u'droid_file_identifier', 16),
        construct.Bytes(u'birth_droid_volume_identifier', 16),
        construct.Bytes(u'birth_droid_file_identifier', 16),
        construct.String(u'hostname', 16), construct.ULInt32(u'entry_number'),
        construct.Padding(8), 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))

    _DEST_LIST_STREAM_ENTRY_V3 = construct.Struct(
        u'dest_list_stream_entry_v3', construct.Padding(8),
        construct.Bytes(u'droid_volume_identifier', 16),
        construct.Bytes(u'droid_file_identifier', 16),
        construct.Bytes(u'birth_droid_volume_identifier', 16),
        construct.Bytes(u'birth_droid_file_identifier', 16),
        construct.String(u'hostname', 16), construct.ULInt32(u'entry_number'),
        construct.Padding(8), construct.ULInt64(u'last_modification_time'),
        construct.ULInt32(u'pin_status'), construct.Padding(16),
        construct.ULInt16(u'path_size'),
        construct.String(u'path', lambda ctx: ctx.path_size * 2),
        construct.Padding(4))

    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.format_version not in (1, 3, 4):
            parser_mediator.ProduceParseError(
                u'unsupported format version: {0:d}.'.format(
                    header.format_version))

        if header.format_version == 1:
            dest_list_stream_entry = self._DEST_LIST_STREAM_ENTRY_V1
        elif header.format_version in (3, 4):
            dest_list_stream_entry = self._DEST_LIST_STREAM_ENTRY_V3

        entry_offset = olecf_item.get_offset()
        while entry_offset < olecf_item.size:
            try:
                entry = 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

            display_name = u'Dest list entry at offset: 0x{0:08x}'.format(
                entry_offset)

            try:
                uuid_object = uuid.UUID(bytes_le=entry.droid_volume_identifier)
                droid_volume_identifier = u'{{{0!s}}}'.format(uuid_object)

                if uuid_object.version == 1:
                    event_object = (
                        windows_events.
                        WindowsDistributedLinkTrackingCreationEvent(
                            uuid_object, display_name))
                    parser_mediator.ProduceEvent(event_object)

            except (TypeError, ValueError) as exception:
                droid_volume_identifier = u''
                parser_mediator.ProduceParseError(
                    u'unable to read droid volume identifier with error: {0:s}'
                    .format(exception))

            try:
                uuid_object = uuid.UUID(bytes_le=entry.droid_file_identifier)
                droid_file_identifier = u'{{{0!s}}}'.format(uuid_object)

                if uuid_object.version == 1:
                    event_object = (
                        windows_events.
                        WindowsDistributedLinkTrackingCreationEvent(
                            uuid_object, display_name))
                    parser_mediator.ProduceEvent(event_object)

            except (TypeError, ValueError) as exception:
                droid_file_identifier = u''
                parser_mediator.ProduceParseError(
                    u'unable to read droid file identifier with error: {0:s}'.
                    format(exception))

            try:
                uuid_object = uuid.UUID(
                    bytes_le=entry.birth_droid_volume_identifier)
                birth_droid_volume_identifier = u'{{{0!s}}}'.format(
                    uuid_object)

                if uuid_object.version == 1:
                    event_object = (
                        windows_events.
                        WindowsDistributedLinkTrackingCreationEvent(
                            uuid_object, display_name))
                    parser_mediator.ProduceEvent(event_object)

            except (TypeError, ValueError) as exception:
                birth_droid_volume_identifier = u''
                parser_mediator.ProduceParseError((
                    u'unable to read birth droid volume identifier with error: '
                    u'{0:s}').format(exception))

            try:
                uuid_object = uuid.UUID(
                    bytes_le=entry.birth_droid_file_identifier)
                birth_droid_file_identifier = u'{{{0!s}}}'.format(uuid_object)

                if uuid_object.version == 1:
                    event_object = (
                        windows_events.
                        WindowsDistributedLinkTrackingCreationEvent(
                            uuid_object, display_name))
                    parser_mediator.ProduceEvent(event_object)

            except (TypeError, ValueError) as exception:
                birth_droid_file_identifier = u''
                parser_mediator.ProduceParseError(
                    (u'unable to read birth droid file identifier with error: '
                     u'{0:s}').format(exception))

            event_object = AutomaticDestinationsDestListEntryEvent(
                entry.last_modification_time,
                eventdata.EventTimestamp.MODIFICATION_TIME, entry_offset,
                entry, droid_volume_identifier, droid_file_identifier,
                birth_droid_volume_identifier, birth_droid_file_identifier)
            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).
      root_item: Optional root item of the OLECF file.

    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.Parse(parser_mediator,
                                          item,
                                          display_name=display_name)