385 lines
11 KiB
Python
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()
|