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

160 lines
5.6 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 the Property List (Plist) Parser.
Plaso's engine calls PlistParser when it encounters Plist files to be processed.
"""
import binascii
import logging
from binplist import binplist
from plaso.lib import errors
from plaso.lib import utils
from plaso.parsers import interface
from plaso.parsers import manager
class PlistParser(interface.BasePluginsParser):
"""De-serializes and parses plists the event objects are generated by plist.
The Plaso engine calls parsers by their Parse() method. This parser's
Parse() has GetTopLevel() which deserializes plist files using the binplist
library and calls plugins (PlistPlugin) registered through the
interface by their Process() to produce event objects.
Plugins are how this parser understands the content inside a plist file,
each plugin holds logic specific to a particular plist file. See the
interface and plist_plugins/ directory for examples of how plist plugins are
implemented.
"""
NAME = 'plist'
DESCRIPTION = u'Parser for binary and text plist files.'
_plugin_classes = {}
def __init__(self):
"""Initializes a parser object."""
super(PlistParser, self).__init__()
self._plugins = PlistParser.GetPluginObjects()
def GetTopLevel(self, file_object, file_name=''):
"""Returns the deserialized content of a plist as a dictionary object.
Args:
file_object: A file-like object to parse.
file_name: The name of the file-like object.
Returns:
A dictionary object representing the contents of the plist.
"""
try:
top_level_object = binplist.readPlist(file_object)
except binplist.FormatError as exception:
raise errors.UnableToParseFile(
u'[{0:s}] File is not a plist file: {1:s}'.format(
self.NAME, utils.GetUnicodeString(exception)))
except (
LookupError, binascii.Error, ValueError, AttributeError) as exception:
raise errors.UnableToParseFile(
u'[{0:s}] Unable to parse XML file, reason: {1:s}'.format(
self.NAME, exception))
except OverflowError as exception:
raise errors.UnableToParseFile(
u'[{0:s}] Unable to parse: {1:s} with error: {2:s}'.format(
self.NAME, file_name, exception))
if not top_level_object:
raise errors.UnableToParseFile(
u'[{0:s}] File is not a plist: {1:s}'.format(
self.NAME, utils.GetUnicodeString(file_name)))
# Since we are using readPlist from binplist now instead of manually
# opening up the BinarPlist file we loose this option. Keep it commented
# out for now but this needs to be tested a bit more.
# TODO: Re-evaluate if we can delete this or still require it.
#if bpl.is_corrupt:
# logging.warning(
# u'[{0:s}] corruption detected in binary plist: {1:s}'.format(
# self.NAME, file_name))
return top_level_object
def Parse(self, parser_context, file_entry, parser_chain=None):
"""Parse and extract values from a plist file.
Args:
parser_context: A parser context object (instance of ParserContext).
file_entry: A file entry object (instance of dfvfs.FileEntry).
parser_chain: Optional string containing the parsing chain up to this
point. The default is None.
"""
# TODO: Should we rather query the stats object to get the size here?
file_object = file_entry.GetFileObject()
file_size = file_object.get_size()
if file_size <= 0:
file_object.close()
raise errors.UnableToParseFile(
u'[{0:s}] file size: {1:d} bytes is less equal 0.'.format(
self.NAME, file_size))
# 50MB is 10x larger than any plist seen to date.
if file_size > 50000000:
file_object.close()
raise errors.UnableToParseFile(
u'[{0:s}] file size: {1:d} bytes is larger than 50 MB.'.format(
self.NAME, file_size))
# Add ourselves to the parser chain, which will be used in all subsequent
# event creation in this parser.
parser_chain = self._BuildParserChain(parser_chain)
top_level_object = None
try:
top_level_object = self.GetTopLevel(file_object, file_entry.name)
except errors.UnableToParseFile:
file_object.close()
raise
if not top_level_object:
file_object.close()
raise errors.UnableToParseFile(
u'[{0:s}] unable to parse: {1:s} skipping.'.format(
self.NAME, file_entry.name))
file_system = file_entry.GetFileSystem()
plist_name = file_system.BasenamePath(file_entry.name)
for plugin_object in self._plugins:
try:
plugin_object.Process(
parser_context, file_entry=file_entry, parser_chain=parser_chain,
plist_name=plist_name, top_level=top_level_object)
except errors.WrongPlistPlugin as exception:
logging.debug(u'[{0:s}] Wrong plugin: {1:s} for: {2:s}'.format(
self.NAME, exception[0], exception[1]))
file_object.close()
manager.ParsersManager.RegisterParser(PlistParser)