Source code for plaso.parsers.setupapi

# -*- coding: utf-8 -*-
"""Parser for Windows Setupapi log files.

The format is documented at:

import pyparsing

from dfdatetime import time_elements as dfdatetime_time_elements

from plaso.containers import events
from plaso.containers import time_events
from plaso.lib import errors
from plaso.lib import definitions
from plaso.parsers import logger
from plaso.parsers import manager
from plaso.parsers import text_parser

[docs]class SetupapiLogEventData(events.EventData): """Setupapi log event data. Attributes: entry_type (str): log entry type, for examaple "Device Install - PCI\\VEN_104C&DEV_8019&SUBSYS_8010104C&REV_00\\3&61aaa01&0&38" or "Sysprep Respecialize - {804b345a-ffd7-854c-a1b5-ca9598907846}". exit_status (str): the exit status of the logged operation. """ DATA_TYPE = 'setupapi:log:line' def __init__(self): """Initializes event data.""" super(SetupapiLogEventData, self).__init__(data_type=self.DATA_TYPE) self.entry_type = None self.exit_status = None
[docs]class SetupapiLogParser(text_parser.PyparsingSingleLineTextParser): """Parses events from Windows Setupapi log files.""" NAME = 'setupapi' DATA_FORMAT = 'Windows SetupAPI log file' _ENCODING = 'utf-8' _SLASH = pyparsing.Literal('/').suppress() _FOUR_DIGITS = text_parser.PyparsingConstants.FOUR_DIGITS _THREE_DIGITS = text_parser.PyparsingConstants.THREE_DIGITS _TWO_DIGITS = text_parser.PyparsingConstants.TWO_DIGITS _SETUPAPI_DATE_TIME = pyparsing.Group( _FOUR_DIGITS + _SLASH + _TWO_DIGITS + _SLASH + _TWO_DIGITS + _TWO_DIGITS + pyparsing.Suppress(':') + _TWO_DIGITS + pyparsing.Suppress(':') + _TWO_DIGITS + pyparsing.Word('.,', exact=1).suppress() + _THREE_DIGITS ) # Disable pylint due to long URLs for documenting structures. # pylint: disable=line-too-long # See _LOG_HEADER_START = ( pyparsing.Literal('[Device Install Log]') + pyparsing.lineEnd()) # See _LOG_HEADER_END = ( pyparsing.Literal('[BeginLog]') + pyparsing.lineEnd()) # See _SECTION_HEADER = ( pyparsing.Literal('>>> [').suppress() + pyparsing.CharsNotIn(']').setResultsName('entry_type') + pyparsing.Literal(']') + pyparsing.lineEnd()) # See _SECTION_HEADER_START = ( pyparsing.Literal('>>> Section start').suppress() + _SETUPAPI_DATE_TIME.setResultsName('start_time') + pyparsing.lineEnd()) # See _SECTION_END = ( pyparsing.Literal('<<< Section end ').suppress() + _SETUPAPI_DATE_TIME.setResultsName('end_time') + pyparsing.lineEnd()) # See _SECTION_END_EXIT_STATUS = ( pyparsing.Literal('<<< [Exit status: ').suppress() + pyparsing.CharsNotIn(']').setResultsName('exit_status') + pyparsing.Literal(']') + pyparsing.lineEnd()) # See _SECTION_BODY_LINE = ( pyparsing.stringStart + pyparsing.MatchFirst([ pyparsing.Literal('!!! '), pyparsing.Literal('! '), pyparsing.Literal(' ')]) + pyparsing.restOfLine).leaveWhitespace() # See _NON_SECTION_LINE = ( pyparsing.stringStart + pyparsing.MatchFirst([ pyparsing.Literal(' . '), pyparsing.Literal('!!! '), pyparsing.Literal('! '), pyparsing.Literal(' ')]) + pyparsing.restOfLine).leaveWhitespace() # These lines do not appear to be documented in the Microsoft documentation. _BOOT_SESSION_LINE = ( pyparsing.Literal('[Boot Session:') + _SETUPAPI_DATE_TIME + pyparsing.Literal(']')) # pylint: enable=line-too-long LINE_STRUCTURES = [ ('ignorable_line', _BOOT_SESSION_LINE), ('ignorable_line', _LOG_HEADER_END), ('ignorable_line', _LOG_HEADER_START), ('ignorable_line', _NON_SECTION_LINE), ('ignorable_line', _SECTION_BODY_LINE), ('section_end', _SECTION_END), ('section_end_exit_status', _SECTION_END_EXIT_STATUS), ('section_header', _SECTION_HEADER), ('section_start', _SECTION_HEADER_START)] def __init__(self): """Initializes a setupapi parser.""" super(SetupapiLogParser, self).__init__() self._last_end_time = None self._last_entry_type = None def _GetTimeElements(self, time_structure): """Builds time elements from a setupapi time_stamp field. Args: time_structure (pyparsing.ParseResults): structure of tokens derived from a setupapi time_stamp field. Returns: dfdatetime.TimeElements: date and time extracted from the value or None if the structure does not represent a valid date and time value. """ try: date_time = dfdatetime_time_elements.TimeElementsInMilliseconds( time_elements_tuple=time_structure) # Setupapi logs store date and time values in local time. date_time.is_local_time = True return date_time except ValueError: return None
[docs] def ParseRecord(self, parser_mediator, key, structure): """Parses a log record structure and produces events. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. key (str): identifier of the structure of tokens. structure (pyparsing.ParseResults): structure of tokens derived from a log entry. Raises: ParseError: when the structure type is unknown. """ if key == 'ignorable_line': return if key == 'section_header': self._last_entry_type = self._GetValueFromStructure( structure, 'entry_type') return if key == 'section_start': time_structure = self._GetValueFromStructure(structure, 'start_time') start_time = self._GetTimeElements(time_structure) if not start_time: parser_mediator.ProduceExtractionWarning( 'invalid date time value: {0!s}'.format(time_structure)) return event_data = SetupapiLogEventData() event_data.entry_type = self._last_entry_type event = time_events.DateTimeValuesEvent( start_time, definitions.TIME_DESCRIPTION_START, time_zone=parser_mediator.timezone) # Create event for the start of the setupapi section parser_mediator.ProduceEventWithEventData(event, event_data) return if key == 'section_end': time_structure = self._GetValueFromStructure(structure, 'end_time') end_time = self._GetTimeElements(time_structure) if not end_time: parser_mediator.ProduceExtractionWarning( 'invalid date time value: {0!s}'.format(time_structure)) # Store last end time so that an event with the data from the # following exit status section can be created. self._last_end_time = end_time return if key == 'section_end_exit_status': exit_status = self._GetValueFromStructure( structure, 'exit_status') if self._last_end_time: event_data = SetupapiLogEventData() event_data.entry_type = self._last_entry_type event_data.exit_status = exit_status event = time_events.DateTimeValuesEvent( self._last_end_time, definitions.TIME_DESCRIPTION_END, time_zone=parser_mediator.timezone) parser_mediator.ProduceEventWithEventData(event, event_data) # Reset entry type and status and end time in case a line is missing. self._last_entry_type = None self._last_end_time = None return raise errors.ParseError( 'Unable to parse record, unknown structure: {0:s}'.format(key))
[docs] def VerifyStructure(self, parser_mediator, line): """Verify that this file is a Windows Setupapi log file. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfvfs. line (str): single line from the text file. Returns: bool: True if this is the correct parser, False otherwise. """ try: self._LOG_HEADER_START.parseString(line) # Reset stored values for parsing a new file. self._last_end_time = None self._last_entry_type = None except pyparsing.ParseException as exception: logger.debug('Not a Windows Setupapi log file: {0!s}'.format(exception)) return False return True