154 lines
5.1 KiB
Python
154 lines
5.1 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2013 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.
|
|
"""This file contains a Symantec parser in plaso."""
|
|
|
|
from plaso.events import text_events
|
|
from plaso.lib import timelib
|
|
from plaso.parsers import manager
|
|
from plaso.parsers import text_parser
|
|
|
|
import pytz
|
|
|
|
|
|
__author__ = 'David Nides (david.nides@gmail.com)'
|
|
|
|
|
|
class SymantecEvent(text_events.TextEvent):
|
|
"""Convenience class for a Symantec line event."""
|
|
DATA_TYPE = 'av:symantec:scanlog'
|
|
|
|
|
|
class SymantecParser(text_parser.TextCSVParser):
|
|
"""Parse Symantec AV Corporate Edition and Endpoint Protection log files."""
|
|
|
|
NAME = 'symantec_scanlog'
|
|
DESCRIPTION = u'Parser for Symantec Anti-Virus log files.'
|
|
|
|
# Define the columns that make up the structure of a Symantec log file.
|
|
# http://www.symantec.com/docs/TECH100099
|
|
COLUMNS = [
|
|
'time', 'event', 'cat', 'logger', 'computer', 'user',
|
|
'virus', 'file', 'action1', 'action2', 'action0', 'virustype',
|
|
'flags', 'description', 'scanid', 'new_ext', 'groupid',
|
|
'event_data', 'vbin_id', 'virus_id', 'quarfwd_status',
|
|
'access', 'snd_status', 'compressed', 'depth', 'still_infected',
|
|
'definfo', 'defseqnumber', 'cleaninfo', 'deleteinfo',
|
|
'backup_id', 'parent', 'guid', 'clientgroup', 'address',
|
|
'domainname', 'ntdomain', 'macaddr', 'version:',
|
|
'remote_machine', 'remote_machine_ip', 'action1_status',
|
|
'action2_status', 'license_feature_name', 'license_feature_ver',
|
|
'license_serial_num', 'license_fulfillment_id', 'license_start_dt',
|
|
'license_expiration_dt', 'license_lifecycle', 'license_seats_total',
|
|
'license_seats', 'err_code', 'license_seats_delta', 'status',
|
|
'domain_guid', 'log_session_guid', 'vbin_session_id',
|
|
'login_domain', 'extra']
|
|
|
|
def _GetTimestamp(self, timestamp_raw, timezone=pytz.utc):
|
|
"""Return a 64-bit signed timestamp value in micro seconds since Epoch.
|
|
|
|
The timestamp consists of six hexadecimal octets.
|
|
They represent the following:
|
|
First octet: Number of years since 1970
|
|
Second octet: Month, where January = 0
|
|
Third octet: Day
|
|
Fourth octet: Hour
|
|
Fifth octet: Minute
|
|
Sixth octet: Second
|
|
|
|
For example, 200A13080122 represents November 19, 2002, 8:01:34 AM.
|
|
|
|
Args:
|
|
timestamp_raw: The hexadecimal encoded timestamp value.
|
|
timezone: Optional timezone (instance of pytz.timezone).
|
|
The default is UTC.
|
|
|
|
Returns:
|
|
A plaso timestamp value, micro seconds since Epoch in UTC.
|
|
"""
|
|
if timestamp_raw == '':
|
|
return 0
|
|
|
|
year, month, day, hours, minutes, seconds = (
|
|
int(x[0] + x[1], 16) for x in zip(
|
|
timestamp_raw[::2], timestamp_raw[1::2]))
|
|
|
|
return timelib.Timestamp.FromTimeParts(
|
|
year + 1970, month + 1, day, hours, minutes, seconds, timezone=timezone)
|
|
|
|
def VerifyRow(self, parser_context, row):
|
|
"""Verify a single line of a Symantec log file.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
row: A single row from the CSV file.
|
|
|
|
Returns:
|
|
True if this is the correct parser, False otherwise.
|
|
"""
|
|
try:
|
|
timestamp = self._GetTimestamp(row['time'], parser_context.timezone)
|
|
except (TypeError, ValueError):
|
|
return False
|
|
|
|
if not timestamp:
|
|
return False
|
|
|
|
# Check few entries.
|
|
try:
|
|
my_event = int(row['event'])
|
|
except TypeError:
|
|
return False
|
|
|
|
if my_event < 1 or my_event > 77:
|
|
return False
|
|
|
|
try:
|
|
category = int(row['cat'])
|
|
except TypeError:
|
|
return False
|
|
|
|
if category < 1 or category > 4:
|
|
return False
|
|
|
|
return True
|
|
|
|
def ParseRow(
|
|
self, parser_context, row_offset, row, file_entry=None,
|
|
parser_chain=None):
|
|
"""Parses a row and extract event objects.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
row_offset: The offset of the row.
|
|
row: A dictionary containing all the fields as denoted in the
|
|
COLUMNS class list.
|
|
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.
|
|
"""
|
|
timestamp = self._GetTimestamp(row['time'], parser_context.timezone)
|
|
|
|
# TODO: Create new dict object that only contains valuable attributes.
|
|
event_object = SymantecEvent(timestamp, row_offset, row)
|
|
parser_context.ProduceEvent(
|
|
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
|
|
|
|
|
manager.ParsersManager.RegisterParser(SymantecParser)
|