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

557 lines
20 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 preprocessors for Windows."""
import abc
import logging
from dfvfs.helpers import file_system_searcher
from plaso.lib import errors
from plaso.preprocessors import interface
from plaso.preprocessors import manager
from plaso.winreg import cache
from plaso.winreg import path_expander as winreg_path_expander
from plaso.winreg import utils
from plaso.winreg import winregistry
class WindowsRegistryPreprocessPlugin(interface.PreprocessPlugin):
"""Class that defines the Windows Registry preprocess plugin object.
By default registry needs information about system paths, which excludes
them to run in priority 1, in some cases they may need to run in priority
3, for instance if the Registry key is dependent on which version of Windows
is running, information that is collected during priority 2.
"""
__abstract = True
SUPPORTED_OS = ['Windows']
WEIGHT = 2
REG_KEY = '\\'
REG_PATH = '{sysregistry}'
REG_FILE = 'SOFTWARE'
def __init__(self):
"""Initializes the Window Registry preprocess plugin object."""
super(WindowsRegistryPreprocessPlugin, self).__init__()
self._file_path_expander = winreg_path_expander.WinRegistryKeyPathExpander()
self._key_path_expander = None
def GetValue(self, searcher, knowledge_base):
"""Returns a value gathered from a Registry key for preprocessing.
Args:
searcher: The file system searcher object (instance of
dfvfs.FileSystemSearcher).
knowledge_base: A knowledge base object (instance of KnowledgeBase),
which contains information from the source data needed
for parsing.
Raises:
errors.PreProcessFail: If the preprocessing fails.
"""
# TODO: optimize this in one find.
try:
# TODO: do not pass the full pre_obj here but just the necessary values.
path = self._file_path_expander.ExpandPath(
self.REG_PATH, pre_obj=knowledge_base.pre_obj)
except KeyError:
path = u''
if not path:
raise errors.PreProcessFail(
u'Unable to expand path: {0:s}'.format(self.REG_PATH))
find_spec = file_system_searcher.FindSpec(
location=path, case_sensitive=False)
path_specs = list(searcher.Find(find_specs=[find_spec]))
if not path_specs or len(path_specs) != 1:
raise errors.PreProcessFail(
u'Unable to find directory: {0:s}'.format(self.REG_PATH))
directory_location = searcher.GetRelativePath(path_specs[0])
if not directory_location:
raise errors.PreProcessFail(
u'Missing directory location for: {0:s}'.format(self.REG_PATH))
# The path is split in segments to make it path segement separator
# independent (and thus platform independent).
path_segments = searcher.SplitPath(directory_location)
path_segments.append(self.REG_FILE)
find_spec = file_system_searcher.FindSpec(
location=path_segments, case_sensitive=False)
path_specs = list(searcher.Find(find_specs=[find_spec]))
if not path_specs:
raise errors.PreProcessFail(
u'Unable to find file: {0:s} in directory: {1:s}'.format(
self.REG_FILE, directory_location))
if len(path_specs) != 1:
raise errors.PreProcessFail((
u'Find for file: {1:s} in directory: {0:s} returned {2:d} '
u'results.').format(
self.REG_FILE, directory_location, len(path_specs)))
file_location = getattr(path_specs[0], 'location', None)
if not directory_location:
raise errors.PreProcessFail(
u'Missing file location for: {0:s} in directory: {1:s}'.format(
self.REG_FILE, directory_location))
try:
file_entry = searcher.GetFileEntryByPathSpec(path_specs[0])
except IOError as exception:
raise errors.PreProcessFail(
u'Unable to open file entry: {0:s} with error: {1:s}'.format(
file_location, exception))
if not file_entry:
raise errors.PreProcessFail(
u'Unable to open file entry: {0:s}'.format(file_location))
# TODO: remove this check win_registry.OpenFile doesn't fail instead?
try:
file_object = file_entry.GetFileObject()
file_object.close()
except IOError as exception:
raise errors.PreProcessFail(
u'Unable to open file object: {0:s} with error: {1:s}'.format(
file_location, exception))
win_registry = winregistry.WinRegistry(
winregistry.WinRegistry.BACKEND_PYREGF)
try:
winreg_file = win_registry.OpenFile(
file_entry, codepage=knowledge_base.codepage)
except IOError as exception:
raise errors.PreProcessFail(
u'Unable to open Registry file: {0:s} with error: {1:s}'.format(
file_location, exception))
self.winreg_file = winreg_file
if not self._key_path_expander:
# TODO: it is more efficient to have one cache that is passed to every
# plugin, or maybe one path expander. Or replace the path expander by
# dfvfs WindowsPathResolver?
reg_cache = cache.WinRegistryCache()
reg_cache.BuildCache(winreg_file, self.REG_FILE)
self._key_path_expander = winreg_path_expander.WinRegistryKeyPathExpander(
reg_cache=reg_cache)
try:
# TODO: do not pass the full pre_obj here but just the necessary values.
key_path = self._key_path_expander.ExpandPath(
self.REG_KEY, pre_obj=knowledge_base.pre_obj)
except KeyError:
key_path = u''
if not key_path:
raise errors.PreProcessFail(
u'Unable to expand path: {0:s}'.format(self.REG_KEY))
try:
key = winreg_file.GetKeyByPath(key_path)
except IOError as exception:
raise errors.PreProcessFail(
u'Unable to fetch Registry key: {0:s} with error: {1:s}'.format(
key_path, exception))
if not key:
raise errors.PreProcessFail(
u'Registry key {0:s} does not exist.'.format(self.REG_KEY))
return self.ParseKey(key)
@abc.abstractmethod
def ParseKey(self, key):
"""Extract information from a Registry key and save in storage."""
class WindowsCodepage(WindowsRegistryPreprocessPlugin):
"""A preprocessing class that fetches codepage information."""
# Defines the preprocess attribute to be set.
ATTRIBUTE = 'code_page'
# Depend upon the current control set, thus lower the priority.
WEIGHT = 3
REG_KEY = '{current_control_set}\\Control\\Nls\\CodePage'
REG_FILE = 'SYSTEM'
def ParseKey(self, key):
"""Retrieves the codepage or cp1252 by default."""
value = key.GetValue('ACP')
if value and type(value.data) == unicode:
return u'cp{0:s}'.format(value.data)
logging.warning(
u'Unable to determine ASCII string codepage, defaulting to cp1252.')
return u'cp1252'
class WindowsHostname(WindowsRegistryPreprocessPlugin):
"""A preprocessing class that fetches the hostname information."""
ATTRIBUTE = 'hostname'
# Depend upon the current control set to be found.
WEIGHT = 3
REG_KEY = '{current_control_set}\\Control\\ComputerName\\ComputerName'
REG_FILE = 'SYSTEM'
def ParseKey(self, key):
"""Extract the hostname from the registry."""
value = key.GetValue('ComputerName')
if value and type(value.data) == unicode:
return value.data
class WindowsProgramFilesPath(WindowsRegistryPreprocessPlugin):
"""Fetch about the location for the Program Files directory."""
ATTRIBUTE = 'programfiles'
REGFILE = 'SOFTWARE'
REG_KEY = '\\Microsoft\\Windows\\CurrentVersion'
def ParseKey(self, key):
"""Extract the version information from the key."""
value = key.GetValue('ProgramFilesDir')
if value:
# Remove the first drive letter, eg: "C:\Program Files".
return u'{0:s}'.format(value.data.partition('\\')[2])
class WindowsProgramFilesX86Path(WindowsRegistryPreprocessPlugin):
"""Fetch about the location for the Program Files directory."""
ATTRIBUTE = 'programfilesx86'
REGFILE = 'SOFTWARE'
REG_KEY = '\\Microsoft\\Windows\\CurrentVersion'
def ParseKey(self, key):
"""Extract the version information from the key."""
value = key.GetValue(u'ProgramFilesDir (x86)')
if value:
# Remove the first drive letter, eg: "C:\Program Files".
return u'{0:s}'.format(value.data.partition('\\')[2])
class WindowsSystemRegistryPath(interface.PathPreprocessPlugin):
"""Get the system registry path."""
SUPPORTED_OS = ['Windows']
ATTRIBUTE = 'sysregistry'
PATH = '/(Windows|WinNT|WINNT35|WTSRV)/System32/config'
class WindowsSystemRootPath(interface.PathPreprocessPlugin):
"""Get the system root path."""
SUPPORTED_OS = ['Windows']
ATTRIBUTE = 'systemroot'
PATH = '/(Windows|WinNT|WINNT35|WTSRV)'
class WindowsTimeZone(WindowsRegistryPreprocessPlugin):
"""A preprocessing class that fetches timezone information."""
# Defines the preprocess attribute to be set.
ATTRIBUTE = 'time_zone_str'
# Depend upon the current control set, thus lower the priority.
WEIGHT = 3
REG_KEY = '{current_control_set}\\Control\\TimeZoneInformation'
REG_FILE = 'SYSTEM'
# transform gathered from these sources:
# Prebuilt from:
# HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
ZONE_LIST = {
'IndiaStandardTime': 'Asia/Kolkata',
'EasternStandardTime': 'EST5EDT',
'EasternDaylightTime': 'EST5EDT',
'MountainStandardTime': 'MST7MDT',
'MountainDaylightTime': 'MST7MDT',
'PacificStandardTime': 'PST8PDT',
'PacificDaylightTime': 'PST8PDT',
'CentralStandardTime': 'CST6CDT',
'CentralDaylightTime': 'CST6CDT',
'SamoaStandardTime': 'US/Samoa',
'HawaiianStandardTime': 'US/Hawaii',
'AlaskanStandardTime': 'US/Alaska',
'MexicoStandardTime2': 'MST7MDT',
'USMountainStandardTime': 'MST7MDT',
'CanadaCentralStandardTime': 'CST6CDT',
'MexicoStandardTime': 'CST6CDT',
'CentralAmericaStandardTime': 'CST6CDT',
'USEasternStandardTime': 'EST5EDT',
'SAPacificStandardTime': 'EST5EDT',
'MalayPeninsulaStandardTime': 'Asia/Kuching',
'PacificSAStandardTime': 'Canada/Atlantic',
'AtlanticStandardTime': 'Canada/Atlantic',
'SAWesternStandardTime': 'Canada/Atlantic',
'NewfoundlandStandardTime': 'Canada/Newfoundland',
'AzoresStandardTime': 'Atlantic/Azores',
'CapeVerdeStandardTime': 'Atlantic/Azores',
'GMTStandardTime': 'GMT',
'GreenwichStandardTime': 'GMT',
'W.CentralAfricaStandardTime': 'Europe/Belgrade',
'W.EuropeStandardTime': 'Europe/Belgrade',
'CentralEuropeStandardTime': 'Europe/Belgrade',
'RomanceStandardTime': 'Europe/Belgrade',
'CentralEuropeanStandardTime': 'Europe/Belgrade',
'E.EuropeStandardTime': 'Egypt',
'SouthAfricaStandardTime': 'Egypt',
'IsraelStandardTime': 'Egypt',
'EgyptStandardTime': 'Egypt',
'NorthAsiaEastStandardTime': 'Asia/Bangkok',
'SingaporeStandardTime': 'Asia/Bangkok',
'ChinaStandardTime': 'Asia/Bangkok',
'W.AustraliaStandardTime': 'Australia/Perth',
'TaipeiStandardTime': 'Asia/Bangkok',
'TokyoStandardTime': 'Asia/Tokyo',
'KoreaStandardTime': 'Asia/Seoul',
'@tzres.dll,-10': 'Atlantic/Azores',
'@tzres.dll,-11': 'Atlantic/Azores',
'@tzres.dll,-12': 'Atlantic/Azores',
'@tzres.dll,-20': 'Atlantic/Cape_Verde',
'@tzres.dll,-21': 'Atlantic/Cape_Verde',
'@tzres.dll,-22': 'Atlantic/Cape_Verde',
'@tzres.dll,-40': 'Brazil/East',
'@tzres.dll,-41': 'Brazil/East',
'@tzres.dll,-42': 'Brazil/East',
'@tzres.dll,-70': 'Canada/Newfoundland',
'@tzres.dll,-71': 'Canada/Newfoundland',
'@tzres.dll,-72': 'Canada/Newfoundland',
'@tzres.dll,-80': 'Canada/Atlantic',
'@tzres.dll,-81': 'Canada/Atlantic',
'@tzres.dll,-82': 'Canada/Atlantic',
'@tzres.dll,-104': 'America/Cuiaba',
'@tzres.dll,-105': 'America/Cuiaba',
'@tzres.dll,-110': 'EST5EDT',
'@tzres.dll,-111': 'EST5EDT',
'@tzres.dll,-112': 'EST5EDT',
'@tzres.dll,-120': 'EST5EDT',
'@tzres.dll,-121': 'EST5EDT',
'@tzres.dll,-122': 'EST5EDT',
'@tzres.dll,-130': 'EST5EDT',
'@tzres.dll,-131': 'EST5EDT',
'@tzres.dll,-132': 'EST5EDT',
'@tzres.dll,-140': 'CST6CDT',
'@tzres.dll,-141': 'CST6CDT',
'@tzres.dll,-142': 'CST6CDT',
'@tzres.dll,-150': 'America/Guatemala',
'@tzres.dll,-151': 'America/Guatemala',
'@tzres.dll,-152': 'America/Guatemala',
'@tzres.dll,-160': 'CST6CDT',
'@tzres.dll,-161': 'CST6CDT',
'@tzres.dll,-162': 'CST6CDT',
'@tzres.dll,-170': 'America/Mexico_City',
'@tzres.dll,-171': 'America/Mexico_City',
'@tzres.dll,-172': 'America/Mexico_City',
'@tzres.dll,-180': 'MST7MDT',
'@tzres.dll,-181': 'MST7MDT',
'@tzres.dll,-182': 'MST7MDT',
'@tzres.dll,-190': 'MST7MDT',
'@tzres.dll,-191': 'MST7MDT',
'@tzres.dll,-192': 'MST7MDT',
'@tzres.dll,-200': 'MST7MDT',
'@tzres.dll,-201': 'MST7MDT',
'@tzres.dll,-202': 'MST7MDT',
'@tzres.dll,-210': 'PST8PDT',
'@tzres.dll,-211': 'PST8PDT',
'@tzres.dll,-212': 'PST8PDT',
'@tzres.dll,-220': 'US/Alaska',
'@tzres.dll,-221': 'US/Alaska',
'@tzres.dll,-222': 'US/Alaska',
'@tzres.dll,-230': 'US/Hawaii',
'@tzres.dll,-231': 'US/Hawaii',
'@tzres.dll,-232': 'US/Hawaii',
'@tzres.dll,-260': 'GMT',
'@tzres.dll,-261': 'GMT',
'@tzres.dll,-262': 'GMT',
'@tzres.dll,-271': 'UTC',
'@tzres.dll,-272': 'UTC',
'@tzres.dll,-280': 'Europe/Budapest',
'@tzres.dll,-281': 'Europe/Budapest',
'@tzres.dll,-282': 'Europe/Budapest',
'@tzres.dll,-290': 'Europe/Warsaw',
'@tzres.dll,-291': 'Europe/Warsaw',
'@tzres.dll,-292': 'Europe/Warsaw',
'@tzres.dll,-331': 'Europe/Nicosia',
'@tzres.dll,-332': 'Europe/Nicosia',
'@tzres.dll,-340': 'Africa/Cairo',
'@tzres.dll,-341': 'Africa/Cairo',
'@tzres.dll,-342': 'Africa/Cairo',
'@tzres.dll,-350': 'Europe/Sofia',
'@tzres.dll,-351': 'Europe/Sofia',
'@tzres.dll,-352': 'Europe/Sofia',
'@tzres.dll,-365': 'Egypt',
'@tzres.dll,-390': 'Asia/Kuwait',
'@tzres.dll,-391': 'Asia/Kuwait',
'@tzres.dll,-392': 'Asia/Kuwait',
'@tzres.dll,-400': 'Asia/Baghdad',
'@tzres.dll,-401': 'Asia/Baghdad',
'@tzres.dll,-402': 'Asia/Baghdad',
'@tzres.dll,-410': 'Africa/Nairobi',
'@tzres.dll,-411': 'Africa/Nairobi',
'@tzres.dll,-412': 'Africa/Nairobi',
'@tzres.dll,-434': 'Asia/Tbilisi',
'@tzres.dll,-435': 'Asia/Tbilisi',
'@tzres.dll,-440': 'Asia/Muscat',
'@tzres.dll,-441': 'Asia/Muscat',
'@tzres.dll,-442': 'Asia/Muscat',
'@tzres.dll,-447': 'Asia/Baku',
'@tzres.dll,-448': 'Asia/Baku',
'@tzres.dll,-449': 'Asia/Baku',
'@tzres.dll,-450': 'Asia/Yerevan',
'@tzres.dll,-451': 'Asia/Yerevan',
'@tzres.dll,-452': 'Asia/Yerevan',
'@tzres.dll,-460': 'Asia/Kabul',
'@tzres.dll,-461': 'Asia/Kabul',
'@tzres.dll,-462': 'Asia/Kabul',
'@tzres.dll,-471': 'Asia/Yekaterinburg',
'@tzres.dll,-472': 'Asia/Yekaterinburg',
'@tzres.dll,-480': 'Asia/Karachi',
'@tzres.dll,-481': 'Asia/Karachi',
'@tzres.dll,-482': 'Asia/Karachi',
'@tzres.dll,-490': 'Asia/Kolkata',
'@tzres.dll,-491': 'Asia/Kolkata',
'@tzres.dll,-492': 'Asia/Kolkata',
'@tzres.dll,-500': 'Asia/Kathmandu',
'@tzres.dll,-501': 'Asia/Kathmandu',
'@tzres.dll,-502': 'Asia/Kathmandu',
'@tzres.dll,-510': 'Asia/Dhaka',
'@tzres.dll,-511': 'Asia/Aqtau',
'@tzres.dll,-512': 'Asia/Aqtau',
'@tzres.dll,-570': 'Asia/Chongqing',
'@tzres.dll,-571': 'Asia/Chongqing',
'@tzres.dll,-572': 'Asia/Chongqing',
'@tzres.dll,-650': 'Australia/Darwin',
'@tzres.dll,-651': 'Australia/Darwin',
'@tzres.dll,-652': 'Australia/Darwin',
'@tzres.dll,-660': 'Australia/Adelaide',
'@tzres.dll,-661': 'Australia/Adelaide',
'@tzres.dll,-662': 'Australia/Adelaide',
'@tzres.dll,-670': 'Australia/Sydney',
'@tzres.dll,-671': 'Australia/Sydney',
'@tzres.dll,-672': 'Australia/Sydney',
'@tzres.dll,-680': 'Australia/Brisbane',
'@tzres.dll,-681': 'Australia/Brisbane',
'@tzres.dll,-682': 'Australia/Brisbane',
'@tzres.dll,-721': 'Pacific/Port_Moresby',
'@tzres.dll,-722': 'Pacific/Port_Moresby',
'@tzres.dll,-731': 'Pacific/Fiji',
'@tzres.dll,-732': 'Pacific/Fiji',
'@tzres.dll,-840': 'America/Argentina/Buenos_Aires',
'@tzres.dll,-841': 'America/Argentina/Buenos_Aires',
'@tzres.dll,-842': 'America/Argentina/Buenos_Aires',
'@tzres.dll,-880': 'UTC',
'@tzres.dll,-930': 'UTC',
'@tzres.dll,-931': 'UTC',
'@tzres.dll,-932': 'UTC',
'@tzres.dll,-1010': 'Asia/Aqtau',
'@tzres.dll,-1020': 'Asia/Dhaka',
'@tzres.dll,-1021': 'Asia/Dhaka',
'@tzres.dll,-1022': 'Asia/Dhaka',
'@tzres.dll,-1070': 'Asia/Tbilisi',
'@tzres.dll,-1120': 'America/Cuiaba',
'@tzres.dll,-1140': 'Pacific/Fiji',
'@tzres.dll,-1460': 'Pacific/Port_Moresby',
'@tzres.dll,-1530': 'Asia/Yekaterinburg',
'@tzres.dll,-1630': 'Europe/Nicosia',
'@tzres.dll,-1660': 'America/Bahia',
'@tzres.dll,-1661': 'America/Bahia',
'@tzres.dll,-1662': 'America/Bahia',
'Central Standard Time': 'CST6CDT',
'Pacific Standard Time': 'PST8PDT',
}
def ParseKey(self, key):
"""Extract timezone information from the registry."""
value = key.GetValue('StandardName')
if value and type(value.data) == unicode:
# Do a mapping to a value defined as in the Olson database.
return self.ZONE_LIST.get(value.data.replace(' ', ''), value.data)
class WindowsUsers(WindowsRegistryPreprocessPlugin):
"""Fetch information about user profiles."""
ATTRIBUTE = 'users'
REG_FILE = 'SOFTWARE'
REG_KEY = '\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList'
def ParseKey(self, key):
"""Extract current control set information."""
users = []
for sid in key.GetSubkeys():
# TODO: as part of artifacts, create a proper object for this.
user = {}
user['sid'] = sid.name
value = sid.GetValue('ProfileImagePath')
if value:
user['path'] = value.data
user['name'] = utils.WinRegBasename(user['path'])
users.append(user)
return users
class WindowsVersion(WindowsRegistryPreprocessPlugin):
"""Fetch information about the current Windows version."""
ATTRIBUTE = 'osversion'
REGFILE = 'SOFTWARE'
REG_KEY = '\\Microsoft\\Windows NT\\CurrentVersion'
def ParseKey(self, key):
"""Extract the version information from the key."""
value = key.GetValue('ProductName')
if value:
return u'{0:s}'.format(value.data)
class WindowsWinDirPath(interface.PathPreprocessPlugin):
"""Get the system path."""
SUPPORTED_OS = ['Windows']
ATTRIBUTE = 'windir'
PATH = '/(Windows|WinNT|WINNT35|WTSRV)'
manager.PreprocessPluginsManager.RegisterPlugins([
WindowsCodepage, WindowsHostname, WindowsProgramFilesPath,
WindowsProgramFilesX86Path, WindowsSystemRegistryPath,
WindowsSystemRootPath, WindowsTimeZone, WindowsUsers, WindowsVersion,
WindowsWinDirPath])