Source code for plaso.parsers.ntfs

# -*- coding: utf-8 -*-
"""Parser for NTFS metadata files."""

from __future__ import unicode_literals

import uuid

import pyfsntfs  # pylint: disable=wrong-import-order

from dfdatetime import filetime as dfdatetime_filetime
from dfdatetime import semantic_time as dfdatetime_semantic_time
from dfdatetime import uuid_time as dfdatetime_uuid_time

from plaso.containers import events
from plaso.containers import time_events
from plaso.containers import windows_events
from plaso.lib import definitions
from plaso.lib import errors
from plaso.lib import specification
from plaso.parsers import dtfabric_parser
from plaso.parsers import interface
from plaso.parsers import manager


[docs]class NTFSFileStatEventData(events.EventData): """NTFS file system stat event data. Attributes: attribute_type (int): attribute type e.g. 0x00000030 which represents $FILE_NAME. file_attribute_flags (int): NTFS file attribute flags. file_reference (int): NTFS file reference. file_system_type (str): file system type. is_allocated (bool): True if the MFT entry is allocated (marked as in use). name (str): name associated with the stat event, e.g. that of a $FILE_NAME attribute or None if not available. parent_file_reference (int): NTFS file reference of the parent. """ DATA_TYPE = 'fs:stat:ntfs' def __init__(self): """Initializes event data.""" super(NTFSFileStatEventData, self).__init__(data_type=self.DATA_TYPE) self.attribute_type = None self.file_attribute_flags = None self.file_reference = None self.file_system_type = 'NTFS' self.is_allocated = None self.name = None self.parent_file_reference = None
[docs]class NTFSUSNChangeEventData(events.EventData): """NTFS USN change event data. Attributes: file_attribute_flags (int): NTFS file attribute flags. filename (str): name of the file associated with the event. file_reference (int): NTFS file reference. file_system_type (str): file system type. parent_file_reference (int): NTFS file reference of the parent. update_reason_flags (int): update reason flags. update_sequence_number (int): update sequence number. update_source_flags (int): update source flags. """ DATA_TYPE = 'fs:ntfs:usn_change' def __init__(self): """Initializes event data.""" super(NTFSUSNChangeEventData, self).__init__(data_type=self.DATA_TYPE) self.file_attribute_flags = None self.filename = None self.file_reference = None self.parent_file_reference = None self.update_reason_flags = None self.update_sequence_number = None self.update_source_flags = None
[docs]class NTFSMFTParser(interface.FileObjectParser): """Parses a NTFS $MFT metadata file.""" _INITIAL_FILE_OFFSET = None NAME = 'mft' DESCRIPTION = 'Parser for NTFS $MFT metadata files.' _MFT_ATTRIBUTE_STANDARD_INFORMATION = 0x00000010 _MFT_ATTRIBUTE_FILE_NAME = 0x00000030 _MFT_ATTRIBUTE_OBJECT_ID = 0x00000040
[docs] @classmethod def GetFormatSpecification(cls): """Retrieves the format specification. Returns: FormatSpecification: format specification. """ format_specification = specification.FormatSpecification(cls.NAME) format_specification.AddNewSignature(b'BAAD', offset=0) format_specification.AddNewSignature(b'FILE', offset=0) return format_specification
def _GetDateTime(self, filetime): """Retrieves the date and time from a FILETIME timestamp. Args: filetime (int): FILETIME timestamp. Returns: dfdatetime.DateTimeValues: date and time. """ if filetime == 0: return dfdatetime_semantic_time.SemanticTime('Not set') return dfdatetime_filetime.Filetime(timestamp=filetime) def _ParseDistributedTrackingIdentifier( self, parser_mediator, uuid_string, 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_string (str): UUID string of the Distributed Tracking identifier. origin (str): origin of the event (event source). """ uuid_object = uuid.UUID(uuid_string) 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) def _ParseMFTAttribute(self, parser_mediator, mft_entry, mft_attribute): """Extract data from a NFTS $MFT attribute. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. mft_entry (pyfsntfs.file_entry): MFT entry. mft_attribute (pyfsntfs.attribute): MFT attribute. """ if mft_entry.is_empty() or mft_entry.base_record_file_reference != 0: return if mft_attribute.attribute_type in [ self._MFT_ATTRIBUTE_STANDARD_INFORMATION, self._MFT_ATTRIBUTE_FILE_NAME]: file_attribute_flags = getattr( mft_attribute, 'file_attribute_flags', None) name = getattr(mft_attribute, 'name', None) parent_file_reference = getattr( mft_attribute, 'parent_file_reference', None) event_data = NTFSFileStatEventData() event_data.attribute_type = mft_attribute.attribute_type event_data.file_attribute_flags = file_attribute_flags event_data.file_reference = mft_entry.file_reference event_data.is_allocated = mft_entry.is_allocated() event_data.name = name event_data.parent_file_reference = parent_file_reference try: creation_time = mft_attribute.get_creation_time_as_integer() except OverflowError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read the creation timestamp from MFT attribute: ' '0x{0:08x} with error: {1!s}').format( mft_attribute.attribute_type, exception)) creation_time = None if creation_time is not None: date_time = self._GetDateTime(creation_time) event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_CREATION) parser_mediator.ProduceEventWithEventData(event, event_data) try: modification_time = mft_attribute.get_modification_time_as_integer() except OverflowError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read the modification timestamp from MFT attribute: ' '0x{0:08x} with error: {1!s}').format( mft_attribute.attribute_type, exception)) modification_time = None if modification_time is not None: date_time = self._GetDateTime(modification_time) event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_MODIFICATION) parser_mediator.ProduceEventWithEventData(event, event_data) try: access_time = mft_attribute.get_access_time_as_integer() except OverflowError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read the access timestamp from MFT attribute: ' '0x{0:08x} with error: {1!s}').format( exception, mft_attribute.attribute_type)) access_time = None if access_time is not None: date_time = self._GetDateTime(access_time) event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_LAST_ACCESS) parser_mediator.ProduceEventWithEventData(event, event_data) try: entry_modification_time = ( mft_attribute.get_entry_modification_time_as_integer()) except OverflowError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read the entry modification timestamp from MFT ' 'attribute: 0x{0:08x} with error: {1!s}').format( mft_attribute.attribute_type, exception)) entry_modification_time = None if entry_modification_time is not None: date_time = self._GetDateTime(entry_modification_time) event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_ENTRY_MODIFICATION) parser_mediator.ProduceEventWithEventData(event, event_data) elif mft_attribute.attribute_type == self._MFT_ATTRIBUTE_OBJECT_ID: display_name = '$MFT: {0:d}-{1:d}'.format( mft_entry.file_reference & 0xffffffffffff, mft_entry.file_reference >> 48) if mft_attribute.droid_file_identifier: try: self._ParseDistributedTrackingIdentifier( parser_mediator, mft_attribute.droid_file_identifier, display_name) except (TypeError, ValueError) as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read droid file identifier from attribute: 0x{0:08x} ' 'with error: {1!s}').format( mft_attribute.attribute_type, exception)) if mft_attribute.birth_droid_file_identifier: try: self._ParseDistributedTrackingIdentifier( parser_mediator, mft_attribute.droid_file_identifier, display_name) except (TypeError, ValueError) as exception: parser_mediator.ProduceExtractionWarning(( 'unable to read birth droid file identifier from attribute: ' '0x{0:08x} with error: {1!s}').format( mft_attribute.attribute_type, exception)) def _ParseMFTEntry(self, parser_mediator, mft_entry): """Extracts data from a NFTS $MFT entry. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. mft_entry (pyfsntfs.file_entry): MFT entry. """ for attribute_index in range(0, mft_entry.number_of_attributes): try: mft_attribute = mft_entry.get_attribute(attribute_index) self._ParseMFTAttribute(parser_mediator, mft_entry, mft_attribute) except IOError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to parse MFT attribute: {0:d} with error: {1!s}').format( attribute_index, exception))
[docs] def ParseFileObject(self, parser_mediator, file_object): """Parses a NTFS $MFT metadata file-like object. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. file_object (dfvfs.FileIO): file-like object. """ mft_metadata_file = pyfsntfs.mft_metadata_file() try: mft_metadata_file.open_file_object(file_object) except IOError as exception: parser_mediator.ProduceExtractionWarning( 'unable to open file with error: {0!s}'.format(exception)) for entry_index in range(0, mft_metadata_file.number_of_file_entries): try: mft_entry = mft_metadata_file.get_file_entry(entry_index) self._ParseMFTEntry(parser_mediator, mft_entry) except IOError as exception: parser_mediator.ProduceExtractionWarning(( 'unable to parse MFT entry: {0:d} with error: {1!s}').format( entry_index, exception)) mft_metadata_file.close()
[docs]class NTFSUsnJrnlParser(dtfabric_parser.DtFabricBaseParser): """Parses a NTFS USN change journal.""" _INITIAL_FILE_OFFSET = None NAME = 'usnjrnl' DESCRIPTION = 'Parser for NTFS USN change journal ($UsnJrnl).' _DEFINITION_FILE = 'ntfs.yaml' # TODO: add support for USN_RECORD_V3 and USN_RECORD_V4 when actually # seen to be used. def _ParseUSNChangeJournal(self, parser_mediator, usn_change_journal): """Parses an USN change journal. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. usn_change_journal (pyfsntsfs.usn_change_journal): USN change journal. Raises: ParseError: if an USN change journal record cannot be parsed. """ if not usn_change_journal: return usn_record_map = self._GetDataTypeMap('usn_record_v2') usn_record_data = usn_change_journal.read_usn_record() while usn_record_data: current_offset = usn_change_journal.get_offset() try: usn_record = self._ReadStructureFromByteStream( usn_record_data, current_offset, usn_record_map) except (ValueError, errors.ParseError) as exception: raise errors.ParseError(( 'Unable to parse USN record at offset: 0x{0:08x} with error: ' '{1!s}').format(current_offset, exception)) # Per MSDN we need to use name offset for forward compatibility. name_offset = usn_record.name_offset - 60 utf16_stream = usn_record.name[name_offset:usn_record.name_size] try: name_string = utf16_stream.decode('utf-16-le') except (UnicodeDecodeError, UnicodeEncodeError) as exception: name_string = utf16_stream.decode('utf-16-le', errors='replace') parser_mediator.ProduceExtractionWarning(( 'unable to decode USN record name string with error: ' '{0:s}. Characters that cannot be decoded will be replaced ' 'with "?" or "\\ufffd".').format(exception)) event_data = NTFSUSNChangeEventData() event_data.file_attribute_flags = usn_record.file_attribute_flags event_data.file_reference = usn_record.file_reference event_data.filename = name_string event_data.offset = current_offset event_data.parent_file_reference = usn_record.parent_file_reference event_data.update_reason_flags = usn_record.update_reason_flags event_data.update_sequence_number = usn_record.update_sequence_number event_data.update_source_flags = usn_record.update_source_flags if not usn_record.update_date_time: date_time = dfdatetime_semantic_time.SemanticTime('Not set') else: date_time = dfdatetime_filetime.Filetime( timestamp=usn_record.update_date_time) event = time_events.DateTimeValuesEvent( date_time, definitions.TIME_DESCRIPTION_ENTRY_MODIFICATION) parser_mediator.ProduceEventWithEventData(event, event_data) usn_record_data = usn_change_journal.read_usn_record()
[docs] def ParseFileObject(self, parser_mediator, file_object): """Parses a NTFS $UsnJrnl metadata file-like object. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. file_object (dfvfs.FileIO): file-like object. """ volume = pyfsntfs.volume() try: volume.open_file_object(file_object) except IOError as exception: parser_mediator.ProduceExtractionWarning( '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()
manager.ParsersManager.RegisterParsers([NTFSMFTParser, NTFSUsnJrnlParser])