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

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.
"""Parser for the Sleuthkit (TSK) bodyfile or mactime format.
The format specifications can be read here:
http://wiki.sleuthkit.org/index.php?title=Body_file
"""
import re
from plaso.events import time_events
from plaso.lib import eventdata
from plaso.parsers import manager
from plaso.parsers import text_parser
class MactimeEvent(time_events.PosixTimeEvent):
"""Convenience class for a mactime-based event."""
DATA_TYPE = 'fs:mactime:line'
def __init__(self, posix_time, usage, row_offset, data):
"""Initializes a mactime-based event object.
Args:
posix_time: The POSIX time value.
usage: The description of the usage of the time value.
row_offset: The offset of the row.
data: A dict object containing extracted data from the body file.
"""
super(MactimeEvent, self).__init__(posix_time, usage)
self.offset = row_offset
self.user_sid = unicode(data.get('uid', u''))
self.user_gid = data.get('gid', None)
self.md5 = data.get('md5', None)
self.filename = data.get('name', 'N/A')
# Check if the filename field is not a string, eg in the instances where a
# filename only conists of numbers. In that case the self.filename field
# becomes an integer value instead of a string value. That causes issues
# later in the process, where we expect the filename value to be a string.
if not isinstance(self.filename, basestring):
self.filename = unicode(self.filename)
self.mode_as_string = data.get('mode_as_string', None)
self.size = data.get('size', None)
inode_number = data.get('inode', 0)
if isinstance(inode_number, basestring):
if '-' in inode_number:
inode_number, _, _ = inode_number.partition('-')
try:
inode_number = int(inode_number, 10)
except ValueError:
inode_number = 0
self.inode = inode_number
class MactimeParser(text_parser.TextCSVParser):
"""Parses SleuthKit's mactime bodyfiles."""
NAME = 'mactime'
DESCRIPTION = u'Parser for SleuthKit\'s mactime bodyfiles.'
COLUMNS = [
'md5', 'name', 'inode', 'mode_as_string', 'uid', 'gid', 'size',
'atime', 'mtime', 'ctime', 'crtime']
VALUE_SEPARATOR = '|'
MD5_RE = re.compile('^[0-9a-fA-F]+$')
_TIMESTAMP_DESC_MAP = {
'atime': eventdata.EventTimestamp.ACCESS_TIME,
'crtime': eventdata.EventTimestamp.CREATION_TIME,
'ctime': eventdata.EventTimestamp.CHANGE_TIME,
'mtime': eventdata.EventTimestamp.MODIFICATION_TIME,
}
def VerifyRow(self, unused_parser_context, row):
"""Verify we are dealing with a mactime bodyfile.
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.
"""
if not self.MD5_RE.match(row['md5']):
return False
try:
# Verify that the "size" field is an integer, thus cast it to int
# and then back to string so it can be compared, if the value is
# not a string representation of an integer, eg: '12a' then this
# conversion will fail and we return a False value.
if str(int(row.get('size', '0'), 10)) != row.get('size', None):
return False
except ValueError:
return False
# TODO: Add additional verification.
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.
"""
for key, value in row.iteritems():
if isinstance(row[key], basestring):
try:
row[key] = int(value, 10)
except ValueError:
pass
for key, timestamp_description in self._TIMESTAMP_DESC_MAP.iteritems():
value = row.get(key, None)
if not value:
continue
event_object = MactimeEvent(
value, timestamp_description, row_offset, row)
parser_context.ProduceEvent(
event_object, parser_chain=parser_chain, file_entry=file_entry)
manager.ParsersManager.RegisterParser(MactimeParser)