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

385 lines
11 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.
"""Pyregf specific implementation for the Windows Registry file access."""
import logging
from plaso.lib import errors
from plaso.lib import timelib
from plaso.winreg import interface
import pyregf
if pyregf.get_version() < '20130716':
raise ImportWarning('WinPyregf requires at least pyregf 20130716.')
class WinPyregfKey(interface.WinRegKey):
"""Implementation of a Windows Registry key using pyregf."""
def __init__(self, pyregf_key, parent_path=u'', root=False):
"""Initializes a Windows Registry key object.
Args:
pyregf_key: An instance of a pyregf.key object.
parent_path: The path of the parent key.
root: A boolean key indicating we are dealing with a root key.
"""
super(WinPyregfKey, self).__init__()
self._pyregf_key = pyregf_key
# Adding few checks to make sure the root key is not
# invalid in plugin checks (root key is equal to the
# path separator).
if parent_path == self.PATH_SEPARATOR:
parent_path = u''
if root:
self._path = self.PATH_SEPARATOR
else:
self._path = self.PATH_SEPARATOR.join(
[parent_path, self._pyregf_key.name])
# pylint: disable=method-hidden
@property
def path(self):
"""The path of the key."""
return self._path
# pylint: disable=function-redefined,arguments-differ,method-hidden
@path.setter
def path(self, value):
"""Set the value of the path explicitly."""
self._path = value
@property
def name(self):
"""The name of the key."""
return self._pyregf_key.name
@property
def offset(self):
"""The offset of the key within the Windows Registry file."""
return self._pyregf_key.offset
@property
def last_written_timestamp(self):
"""The last written time of the key represented as a timestamp."""
return timelib.Timestamp.FromFiletime(
self._pyregf_key.get_last_written_time_as_integer())
@property
def number_of_values(self):
"""The number of values within the key."""
return self._pyregf_key.number_of_values
def GetValue(self, name):
"""Retrieves a value by name.
Args:
name: Name of the value or an empty string for the default value.
Returns:
A Windows Registry value object (instance of WinRegValue) if
a corresponding value was found or None if not.
"""
# Value names are not unique and pyregf provides first match for
# the value. If this becomes problematic this method needs to
# be changed into a generator, iterating through all returned value
# for a given name.
pyregf_value = self._pyregf_key.get_value_by_name(name)
if pyregf_value:
return WinPyregfValue(pyregf_value)
return None
@property
def number_of_subkeys(self):
"""The number of subkeys within the key."""
return self._pyregf_key.number_of_sub_keys
def GetValues(self):
"""Retrieves all values within the key.
Yields:
Windows Registry value objects (instances of WinRegValue) that represent
the values stored within the key.
"""
for pyregf_value in self._pyregf_key.values:
yield WinPyregfValue(pyregf_value)
def GetSubkey(self, name):
"""Retrive a subkey by name.
Args:
name: The relative path of the current key to the desired one.
Returns:
The subkey with the relative path of name or None if not found.
"""
subkey = self._pyregf_key.get_sub_key_by_name(name)
if subkey:
return WinPyregfKey(subkey, self.path)
path_subkey = self._pyregf_key.get_sub_key_by_path(name)
if path_subkey:
path, _, _ = name.rpartition('\\')
path = u'\\'.join([self.path, path])
return WinPyregfKey(path_subkey, path)
def GetSubkeys(self):
"""Retrieves all subkeys within the key.
Yields:
Windows Registry key objects (instances of WinRegKey) that represent
the subkeys stored within the key.
"""
for pyregf_key in self._pyregf_key.sub_keys:
yield WinPyregfKey(pyregf_key, self.path)
class WinPyregfValue(interface.WinRegValue):
"""Implementation of a Windows Registry value using pyregf."""
def __init__(self, pyregf_value):
"""Initializes a Windows Registry value object.
Args:
pyregf_value: An instance of a pyregf.value object.
"""
super(WinPyregfValue, self).__init__()
self._pyregf_value = pyregf_value
self._type_str = ''
@property
def name(self):
"""The name of the value."""
return self._pyregf_value.name
@property
def offset(self):
"""The offset of the value within the Windows Registry file."""
return self._pyregf_value.offset
@property
def data_type(self):
"""Numeric value that contains the data type."""
return self._pyregf_value.type
@property
def raw_data(self):
"""The value data as a byte string."""
try:
return self._pyregf_value.data
except IOError:
raise errors.WinRegistryValueError(
'Unable to read data from value: {0:s}'.format(
self._pyregf_value.name))
@property
def data(self):
"""The value data as a native Python object."""
if self._pyregf_value.type in [
self.REG_SZ, self.REG_EXPAND_SZ, self.REG_LINK]:
try:
return self._pyregf_value.data_as_string
except IOError:
pass
elif self._pyregf_value.type in [
self.REG_DWORD, self.REG_DWORD_BIG_ENDIAN, self.REG_QWORD]:
try:
return self._pyregf_value.data_as_integer
except (IOError, OverflowError):
# TODO: Rethink this approach. The value is not -1, but we cannot
# return the raw data, since the calling plugin expects an integer
# here.
return -1
# TODO: Add support for REG_MULTI_SZ to pyregf.
elif self._pyregf_value.type == self.REG_MULTI_SZ:
if self._pyregf_value.data is None:
return u''
try:
utf16_string = unicode(self._pyregf_value.data.decode('utf-16-le'))
return filter(None, utf16_string.split('\x00'))
except UnicodeError:
pass
return self._pyregf_value.data
class WinPyregfFile(interface.WinRegFile):
"""Implementation of a Windows Registry file pyregf."""
def __init__(self):
"""Initializes a Windows Registry key object."""
super(WinPyregfFile, self).__init__()
self._pyregf_file = pyregf.file()
self.name = ''
self._base_key = None
def Open(self, file_entry, codepage='cp1252'):
"""Opens the Windows Registry file.
Args:
file_entry: The file entry object.
name: The name of the file.
codepage: Optional codepage for ASCII strings, default is cp1252.
"""
# TODO: Add a more elegant error handling to this issue. There are some
# code pages that are not supported by the parent library. However we
# need to properly set the codepage so the library can properly interpret
# values in the Registry.
try:
self._pyregf_file.set_ascii_codepage(codepage)
except (TypeError, IOError):
logging.error((
u'Unable to set the Windows Registry file codepage: {0:s}. '
u'Ignoring provided value.').format(codepage))
self._file_object = file_entry.GetFileObject()
self._pyregf_file.open_file_object(self._file_object)
self._base_key = self._pyregf_file.get_root_key()
# TODO: move to a pyvfs like Registry sub-system.
self.name = file_entry.name
def Close(self):
"""Closes the Windows Registry file."""
self._pyregf_file.close()
self._file_object.close()
def GetKeyByPath(self, path):
"""Retrieves a specific key defined by the Registry path.
Args:
path: the Registry path.
Returns:
The key (instance of WinRegKey) if available or None otherwise.
"""
if not path:
return None
if not self._base_key:
return None
pyregf_key = self._base_key.get_sub_key_by_path(path)
if not pyregf_key:
return None
if pyregf_key.name == self._base_key.name:
root = True
else:
root = False
parent_path, _, _ = path.rpartition(interface.WinRegKey.PATH_SEPARATOR)
return WinPyregfKey(pyregf_key, parent_path, root)
class WinRegistry(object):
"""Provides access to the Windows Registry file."""
# TODO: deprecate this class.
def __init__(self, file_entry, codepage='cp1252'):
"""Constructor for the Registry object.
Args:
file_entry: A file entry object.
codepage: The codepage of the Registry hive, used for string
representation.
"""
self._pyregf_file = pyregf.file()
try:
# TODO: Add a more elegant error handling to this issue. There are some
# code pages that are not supported by the parent library. However we
# need to properly set the codepage so the library can properly interpret
# values in the Registry.
self._pyregf_file.set_ascii_codepage(codepage)
except (TypeError, IOError):
logging.error(
u'Unable to set the Registry codepage to: {}. Not setting it'.format(
codepage))
file_object = file_entry.GetFileObject()
self._pyregf_file.open_file_object(file_object)
def GetRoot(self):
"""Return the root key of the Registry hive."""
key = WinPyregfKey(self._pyregf_file.get_root_key())
# Change root key name to avoid key based plugins failing.
key.path = ''
return key
def GetKey(self, key):
"""Return a Registry key as a WinPyregfKey object."""
if not key:
return None
my_key = self._pyregf_file.get_key_by_path(key)
if not my_key:
return None
path, _, _ = key.rpartition('\\')
return WinPyregfKey(my_key, path)
def __contains__(self, key):
"""Check if a certain Registry key exists within the hive."""
try:
return bool(self.GetKey(key))
except KeyError:
return False
def GetAllSubkeys(self, key):
"""Generator that returns all sub keys of any given Registry key.
Args:
key: A Windows Registry key string or object (instance of WinPyregfKey).
Yields:
Windows Registry key objects (instances of WinPyregfKey) that represent
the subkeys stored within the key.
"""
# TODO: refactor this function.
# TODO: remove the hasattr check.
if not hasattr(key, 'GetSubkeys'):
key = self.GetKey(key)
for subkey in key.GetSubkeys():
yield subkey
if subkey.number_of_subkeys != 0:
for s in self.GetAllSubkeys(subkey):
yield s
def __iter__(self):
"""Default iterator, returns all subkeys of the Windows Registry file."""
root = self.GetRoot()
for key in self.GetAllSubkeys(root):
yield key
def GetLibraryVersion():
"""Return the pyregf and libregf version."""
return pyregf.get_version()