Skip to content

Commit

Permalink
Added parser for setupapi log file #2549 (#2687)
Browse files Browse the repository at this point in the history
  • Loading branch information
dfjxs authored and joachimmetz committed Jul 10, 2019
1 parent 0c2daf1 commit 3f04b5e
Show file tree
Hide file tree
Showing 8 changed files with 12,407 additions and 0 deletions.
1 change: 1 addition & 0 deletions plaso/formatters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
from plaso.formatters import sccm
from plaso.formatters import selinux
from plaso.formatters import services
from plaso.formatters import setupapi
from plaso.formatters import shell_items
from plaso.formatters import shutdown
from plaso.formatters import skydrivelog
Expand Down
30 changes: 30 additions & 0 deletions plaso/formatters/setupapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
"""Windows Setupapi log event formatter."""

from __future__ import unicode_literals

from plaso.formatters import interface
from plaso.formatters import manager


class SetupapiLogFormatter(interface.ConditionalEventFormatter):
"""Formatter for a Windows Setupapi log file event."""

DATA_TYPE = 'setupapi:log:line'

FORMAT_STRING_PIECES = [
'{entry_type}',
'{entry_status}']

# Reversing fields for short description to prevent truncating the status
FORMAT_STRING_SHORT_PIECES = [
'{entry_status}',
'{entry_type}']

FORMAT_STRING_SEPARATOR = ' - '

SOURCE_LONG = 'Windows Setupapi Log'
SOURCE_SHORT = 'LOG'


manager.FormattersManager.RegisterFormatter(SetupapiLogFormatter)
1 change: 1 addition & 0 deletions plaso/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from plaso.parsers import santa
from plaso.parsers import sccm
from plaso.parsers import selinux
from plaso.parsers import setupapi
from plaso.parsers import skydrivelog
from plaso.parsers import sophos_av
from plaso.parsers import sqlite
Expand Down
192 changes: 192 additions & 0 deletions plaso/parsers/setupapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
"""Parser for Windows Setupapi log files."""

from __future__ import unicode_literals

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


class SetupapiLogEventData(events.EventData):
"""Setupapi log event data.
Attributes:
entry_type (str): log entry type such as "Device Install".
entry_status (str): the status of the entry.
"""

DATA_TYPE = 'setupapi:log:line'

def __init__(self):
"""Initializes event data."""
super(SetupapiLogEventData, self).__init__(data_type=self.DATA_TYPE)
self.entry_type = None
# TODO: Parse additional fields from the body of setupapi messages
self.entry_status = None


class SetupapiLogParser(text_parser.PyparsingMultiLineTextParser):
"""Parses events from Windows Setupapi log files."""

NAME = 'setupapi'

DESCRIPTION = 'Parser for Windows Setupapi log files.'

_ENCODING = 'utf-8'

# Increase the buffer size, as log messages can be very long.
BUFFER_SIZE = 262144

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

_SETUPAPI_LINE = (
pyparsing.SkipTo('>>> [', include=True).suppress() +
pyparsing.SkipTo(']').setResultsName('entry_type') +
pyparsing.SkipTo('>>> Section start', include=True).suppress() +
_SETUPAPI_DATE_TIME.setResultsName('start_time') +
pyparsing.SkipTo('<<< Section end ').setResultsName('message') +
pyparsing.GoToColumn(17) +
_SETUPAPI_DATE_TIME.setResultsName('end_time') +
pyparsing.SkipTo('<<< [Exit status: ', include=True).suppress() +
pyparsing.SkipTo(']').setResultsName('entry_status') +
pyparsing.SkipTo(pyparsing.lineEnd()) +
pyparsing.ZeroOrMore(pyparsing.lineEnd()))

LINE_STRUCTURES = [
('logline', _SETUPAPI_LINE),
]

def _ParseRecordLogline(self, parser_mediator, structure):
"""Parses a logline record structure and produces events.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfvfs.
structure (pyparsing.ParseResults): structure of tokens derived from
log entry.
"""
time_zone = parser_mediator.timezone
time_elements_structure = self._GetValueFromStructure(
structure, 'start_time')
try:
date_time = dfdatetime_time_elements.TimeElementsInMilliseconds(
time_elements_tuple=time_elements_structure)
# Setupapi logs stores date and time values in local time.
date_time.is_local_time = True
except ValueError:
parser_mediator.ProduceExtractionWarning(
'invalid date time value: {0!s}'.format(time_elements_structure))
return

event_data = SetupapiLogEventData()
event_data.entry_type = self._GetValueFromStructure(structure, 'entry_type')
event_data.entry_status = 'START'

event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_START, time_zone=time_zone)

# Create event for the start of the setupapi section
parser_mediator.ProduceEventWithEventData(event, event_data)

event_data.entry_status = self._GetValueFromStructure(
structure, 'entry_status')

time_elements_structure = self._GetValueFromStructure(
structure, 'end_time')
try:
date_time = dfdatetime_time_elements.TimeElementsInMilliseconds(
time_elements_tuple=time_elements_structure)
date_time.is_local_time = True
except ValueError:
parser_mediator.ProduceExtractionWarning(
'invalid date time value: {0!s}'.format(time_elements_structure))
return

event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_END, time_zone=time_zone)

# Create event for the end of the setupapi section
parser_mediator.ProduceEventWithEventData(event, event_data)

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 != 'logline':
raise errors.ParseError(
'Unable to parse record, unknown structure: {0:s}'.format(key))

self._ParseRecordLogline(parser_mediator, structure)

def VerifyStructure(self, parser_mediator, lines):
"""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.
lines (str): one or more lines from the text file.
Returns:
bool: True if this is the correct parser, False otherwise.
"""
try:
structure = self._SETUPAPI_LINE.parseString(lines)
except pyparsing.ParseException as exception:
logger.debug('Not a Windows Setupapi log file: {0!s}'.format(exception))
return False

time_elements_structure = self._GetValueFromStructure(
structure, 'start_time')

try:
date_time = dfdatetime_time_elements.TimeElementsInMilliseconds(
time_elements_tuple=time_elements_structure)
except ValueError as exception:
logger.debug((
'Not a Windows Setupapi log file, invalid date/time: {0!s} '
'with error: {1!s}').format(time_elements_structure, exception))
return False

if not date_time:
logger.debug((
'Not a Windows Setupapi log file, '
'invalid date/time: {0!s}').format(time_elements_structure))
return False

return True


manager.ParsersManager.RegisterParser(SetupapiLogParser)
Loading

0 comments on commit 3f04b5e

Please sign in to comment.