plaso-rubanetra/plaso/parsers/custom_destinations.py
2020-04-06 18:48:34 +02:00

213 lines
7.5 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2014 The Plaso Project Authors.
# Please see the AUTHORS file for details on individual authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Parser for .customDestinations-ms files."""
import logging
import os
import construct
from dfvfs.lib import definitions
from dfvfs.path import factory as path_spec_factory
from dfvfs.resolver import resolver
from plaso.lib import errors
from plaso.parsers import interface
from plaso.parsers import manager
from plaso.parsers import winlnk
class CustomDestinationsParser(interface.BaseParser):
"""Parses .customDestinations-ms files."""
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(
'file_header',
construct.ULInt32('unknown1'),
construct.ULInt32('unknown2'),
construct.ULInt32('unknown3'),
construct.ULInt32('header_values_type'))
_HEADER_VALUE_TYPE_0 = construct.Struct(
'header_value_type_0',
construct.ULInt32('number_of_characters'),
construct.String('string', lambda ctx: ctx.number_of_characters * 2),
construct.ULInt32('unknown1'))
_HEADER_VALUE_TYPE_1_OR_2 = construct.Struct(
'header_value_type_1_or_2',
construct.ULInt32('unknown1'))
_ENTRY_HEADER = construct.Struct(
'entry_header',
construct.String('guid', 16))
_FILE_FOOTER = construct.Struct(
'file_footer',
construct.ULInt32('signature'))
def Parse(self, parser_context, file_entry, parser_chain=None):
"""Extract data from an *.customDestinations-ms file.
Args:
parser_context: A parser context object (instance of ParserContext).
file_entry: A file entry object (instance of dfvfs.FileEntry).
parser_chain: Optional string containing the parsing chain up to this
point. The default is None.
"""
file_object = file_entry.GetFileObject()
self.ParseFileObject(
parser_context, file_object, file_entry=file_entry,
parser_chain=parser_chain)
file_object.close()
def ParseFileObject(
self, parser_context, file_object, file_entry=None, parser_chain=None):
"""Extract data from an *.customDestinations-ms file.
Args:
parser_context: A parser context object (instance of ParserContext).
file_object: A file-like object.
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
The default is None.
parser_chain: Optional string containing the parsing chain up to this
point. The default is None.
Raises:
UnableToParseFile: when the file cannot be parsed.
"""
parser_chain = self._BuildParserChain(parser_chain)
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=file_entry.path_spec)
try:
lnk_file_object = resolver.Resolver.OpenFileObject(path_spec)
except RuntimeError as exception:
logging.error((
u'[{0:s}] Unable to open LNK file from {1:s} with error: '
u'{2:s}').format(
parser_chain,
file_entry.path_spec.comparable.replace(u'\n', u';'),
exception))
return
display_name = u'{0:s} # 0x{1:08x}'.format(
parser_context.GetDisplayName(file_entry), file_offset)
self._WINLNK_PARSER.ParseFileObject(
parser_context, lnk_file_object, file_entry=file_entry,
parser_chain=parser_chain, 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.')
manager.ParsersManager.RegisterParser(CustomDestinationsParser)