309 lines
12 KiB
Python
309 lines
12 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2012 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 MRUList Registry plugin."""
|
|
|
|
import abc
|
|
import logging
|
|
|
|
import construct
|
|
|
|
from plaso.events import windows_events
|
|
from plaso.lib import binary
|
|
from plaso.parsers import winreg
|
|
from plaso.parsers.shared import shell_items
|
|
from plaso.parsers.winreg_plugins import interface
|
|
|
|
|
|
# A mixin class is used here to not to have the duplicate functionality
|
|
# to parse the MRUList Registry values. However multiple inheritance
|
|
# and thus mixins are to be used sparsely in this codebase, hence we need
|
|
# to find a better solution in not needing to distinguish between key and
|
|
# value plugins.
|
|
# TODO: refactor Registry key and value plugin to rid ourselves of the mixin.
|
|
class MRUListPluginMixin(object):
|
|
"""Class for common MRUList Windows Registry plugin functionality."""
|
|
|
|
_MRULIST_STRUCT = construct.Range(1, 500, construct.ULInt16('entry_letter'))
|
|
|
|
@abc.abstractmethod
|
|
def _ParseMRUListEntryValue(
|
|
self, parser_context, key, entry_index, entry_letter, file_entry=None,
|
|
parser_chain=None, **kwargs):
|
|
"""Parses the MRUList entry value.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
key: the Registry key (instance of winreg.WinRegKey) that contains
|
|
the MRUList value.
|
|
entry_index: integer value representing the MRUList entry index.
|
|
entry_letter: character value representing the entry.
|
|
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.
|
|
|
|
Returns:
|
|
A string containing the value.
|
|
"""
|
|
|
|
def _ParseMRUListValue(self, key):
|
|
"""Parses the MRUList value in a given Registry key.
|
|
|
|
Args:
|
|
key: the Registry key (instance of winreg.WinRegKey) that contains
|
|
the MRUList value.
|
|
|
|
Returns:
|
|
A MRUList value generator, which returns the MRU index number
|
|
and entry value.
|
|
"""
|
|
mru_list_value = key.GetValue('MRUList')
|
|
|
|
# The key exists but does not contain a value named "MRUList".
|
|
if not mru_list_value:
|
|
return enumerate([])
|
|
|
|
try:
|
|
mru_list = self._MRULIST_STRUCT.parse(mru_list_value.raw_data)
|
|
except construct.FieldError:
|
|
logging.warning(u'[{0:s}] Unable to parse the MRU key: {1:s}'.format(
|
|
self.NAME, key.path))
|
|
return enumerate([])
|
|
|
|
return enumerate(mru_list)
|
|
|
|
def _ParseMRUListKey(
|
|
self, parser_context, key, registry_type=None, file_entry=None,
|
|
parser_chain=None, codepage='cp1252'):
|
|
"""Extract event objects from a MRUList Registry key.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
key: the Registry key (instance of winreg.WinRegKey).
|
|
registry_type: Optional Registry type string. The default is None.
|
|
codepage: Optional extended ASCII string codepage. The default is cp1252.
|
|
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.
|
|
"""
|
|
text_dict = {}
|
|
for entry_index, entry_letter in self._ParseMRUListValue(key):
|
|
# TODO: detect if list ends prematurely.
|
|
# MRU lists are terminated with \0 (0x0000).
|
|
if entry_letter == 0:
|
|
break
|
|
|
|
entry_letter = chr(entry_letter)
|
|
|
|
value_string = self._ParseMRUListEntryValue(
|
|
parser_context, key, entry_index, entry_letter,
|
|
codepage=codepage, file_entry=file_entry, parser_chain=parser_chain)
|
|
|
|
value_text = u'Index: {0:d} [MRU Value {1:s}]'.format(
|
|
entry_index + 1, entry_letter)
|
|
|
|
text_dict[value_text] = value_string
|
|
|
|
event_object = windows_events.WindowsRegistryEvent(
|
|
key.last_written_timestamp, key.path, text_dict,
|
|
offset=key.offset, registry_type=registry_type,
|
|
source_append=': MRU List')
|
|
parser_context.ProduceEvent(
|
|
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
|
|
|
|
|
class MRUListStringPlugin(interface.ValuePlugin, MRUListPluginMixin):
|
|
"""Windows Registry plugin to parse a string MRUList."""
|
|
|
|
NAME = 'winreg_mrulist_string'
|
|
DESCRIPTION = u'Parser for Most Recently Used (MRU) Registry data.'
|
|
|
|
REG_TYPE = 'any'
|
|
REG_VALUES = frozenset(['MRUList', 'a'])
|
|
URLS = [u'http://forensicartifacts.com/tag/mru/']
|
|
|
|
def _ParseMRUListEntryValue(
|
|
self, parser_context, key, entry_index, entry_letter, **unused_kwargs):
|
|
"""Parses the MRUList entry value.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
key: the Registry key (instance of winreg.WinRegKey) that contains
|
|
the MRUList value.
|
|
entry_index: integer value representing the MRUList entry index.
|
|
entry_letter: character value representing the entry.
|
|
|
|
Returns:
|
|
A string containing the value.
|
|
"""
|
|
value_string = u''
|
|
|
|
value = key.GetValue(u'{0:s}'.format(entry_letter))
|
|
if value is None:
|
|
logging.debug(
|
|
u'[{0:s}] Missing MRUList entry value: {1:s} in key: {2:s}.'.format(
|
|
self.NAME, entry_letter, key.path))
|
|
|
|
elif value.DataIsString():
|
|
value_string = value.data
|
|
|
|
elif value.DataIsBinaryData():
|
|
logging.debug((
|
|
u'[{0:s}] Non-string MRUList entry value: {1:s} parsed as string '
|
|
u'in key: {2:s}.').format(self.NAME, entry_letter, key.path))
|
|
utf16_stream = binary.ByteStreamCopyToUtf16Stream(value.data)
|
|
|
|
try:
|
|
value_string = utf16_stream.decode('utf-16-le')
|
|
except UnicodeDecodeError as exception:
|
|
value_string = binary.HexifyBuffer(utf16_stream)
|
|
logging.warning((
|
|
u'[{0:s}] Unable to decode UTF-16 stream: {1:s} in MRUList entry '
|
|
u'value: {2:s} in key: {3:s} with error: {4:s}').format(
|
|
self.NAME, value_string, entry_letter, key.path, exception))
|
|
|
|
return value_string
|
|
|
|
def GetEntries(
|
|
self, parser_context, file_entry=None, key=None, registry_type=None,
|
|
parser_chain=None, codepage='cp1252', **unused_kwargs):
|
|
"""Extracts event objects from a MRU list.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
file_entry: optional file entry object (instance of dfvfs.FileEntry).
|
|
The default is None.
|
|
key: Optional Registry key (instance of winreg.WinRegKey).
|
|
The default is None.
|
|
registry_type: Optional Registry type string. The default is None.
|
|
parser_chain: Optional string containing the parsing chain up to this
|
|
point. The default is None.
|
|
codepage: Optional extended ASCII string codepage. The default is cp1252.
|
|
"""
|
|
self._ParseMRUListKey(
|
|
parser_context, key, registry_type=registry_type,
|
|
parser_chain=parser_chain, file_entry=file_entry, codepage=codepage)
|
|
|
|
def Process(
|
|
self, parser_context, file_entry=None, key=None, registry_type=None,
|
|
codepage='cp1252', parser_chain=None, **kwargs):
|
|
"""Determine if we can process this Registry key or not.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
|
The default is None.
|
|
key: Optional Registry key (instance of winreg.WinRegKey).
|
|
The default is None.
|
|
registry_type: Optional Registry type string. The default is None.
|
|
codepage: Optional extended ASCII string codepage. The default is cp1252.
|
|
parser_chain: Optional string containing the parsing chain up to this
|
|
point. The default is None.
|
|
"""
|
|
# Prevent this plugin triggering on sub paths of non-string MRUList values.
|
|
if u'Explorer\\DesktopStreamMRU' in key.path:
|
|
return
|
|
|
|
super(MRUListStringPlugin, self).Process(
|
|
parser_context, file_entry=file_entry, key=key,
|
|
registry_type=registry_type, codepage=codepage,
|
|
parser_chain=parser_chain, **kwargs)
|
|
|
|
|
|
class MRUListShellItemListPlugin(interface.KeyPlugin, MRUListPluginMixin):
|
|
"""Windows Registry plugin to parse a shell item list MRUList."""
|
|
|
|
NAME = 'winreg_mrulist_shell_item_list'
|
|
DESCRIPTION = u'Parser for Most Recently Used (MRU) Registry data.'
|
|
|
|
REG_TYPE = 'any'
|
|
REG_KEYS = frozenset([
|
|
(u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\'
|
|
u'DesktopStreamMRU')])
|
|
|
|
URLS = [u'https://github.com/libyal/winreg-kb/wiki/MRU-keys']
|
|
|
|
def _ParseMRUListEntryValue(
|
|
self, parser_context, key, entry_index, entry_letter, codepage='cp1252',
|
|
file_entry=None, parser_chain=None, **unused_kwargs):
|
|
"""Parses the MRUList entry value.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
key: the Registry key (instance of winreg.WinRegKey) that contains
|
|
the MRUList value.
|
|
entry_index: integer value representing the MRUList entry index.
|
|
entry_letter: character value representing the entry.
|
|
codepage: Optional extended ASCII string codepage. The default is cp1252.
|
|
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.
|
|
|
|
Returns:
|
|
A string containing the value.
|
|
"""
|
|
value_string = u''
|
|
|
|
value = key.GetValue(u'{0:s}'.format(entry_letter))
|
|
if value is None:
|
|
logging.debug(
|
|
u'[{0:s}] Missing MRUList entry value: {1:s} in key: {2:s}.'.format(
|
|
self.NAME, entry_letter, key.path))
|
|
|
|
elif not value.DataIsBinaryData():
|
|
logging.debug((
|
|
u'[{0:s}] Non-binary MRUList entry value: {1:s} in key: '
|
|
u'{2:s}.').format(self.NAME, entry_letter, key.path))
|
|
|
|
elif value.data:
|
|
shell_items_parser = shell_items.ShellItemsParser(key.path)
|
|
shell_items_parser.Parse(
|
|
parser_context, value.data, codepage=codepage, file_entry=file_entry,
|
|
parser_chain=parser_chain)
|
|
|
|
value_string = u'Shell item list: [{0:s}]'.format(
|
|
shell_items_parser.CopyToPath())
|
|
|
|
return value_string
|
|
|
|
def GetEntries(
|
|
self, parser_context, key=None, registry_type=None, codepage='cp1252',
|
|
file_entry=None, parser_chain=None, **unused_kwargs):
|
|
"""Extract event objects from a Registry key containing a MRUList value.
|
|
|
|
Args:
|
|
parser_context: A parser context object (instance of ParserContext).
|
|
key: Optional Registry key (instance of winreg.WinRegKey).
|
|
The default is None.
|
|
registry_type: Optional Registry type string. The default is None.
|
|
codepage: Optional extended ASCII string codepage. The default is cp1252.
|
|
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.
|
|
"""
|
|
self._ParseMRUListKey(
|
|
parser_context, key, registry_type=registry_type, codepage=codepage,
|
|
parser_chain=parser_chain, file_entry=file_entry)
|
|
|
|
|
|
winreg.WinRegistryParser.RegisterPlugins([
|
|
MRUListStringPlugin, MRUListShellItemListPlugin])
|