Source code for dfwinreg.registry

# -*- coding: utf-8 -*-
"""Classes for Windows Registry access."""

from dfwinreg import definitions
from dfwinreg import key_paths
from dfwinreg import regf
from dfwinreg import virtual


[docs] class WinRegistryFileMapping(object): """Windows Registry file mapping. Attributes: key_path_prefix (str): Windows Registry key path prefix. unique_key_paths (list[str]): key paths unique to the Windows Registry file. windows_path (str): Windows path to the Windows Registry file, such as: C:\\Windows\\System32\\config\\SYSTEM """
[docs] def __init__(self, key_path_prefix, windows_path, unique_key_paths): """Initializes the Windows Registry file mapping. Args: key_path_prefix (str): Windows Registry key path prefix. windows_path (str): Windows path to the Windows Registry file, such as: C:\\Windows\\System32\\config\\SYSTEM unique_key_paths (list[str]): key paths unique to the Windows Registry file. """ super(WinRegistryFileMapping, self).__init__() self.key_path_prefix = key_path_prefix self.unique_key_paths = unique_key_paths self.windows_path = windows_path
[docs] class WinRegistry(object): """Windows Registry.""" _REGISTRY_FILE_MAPPINGS = [ # TODO: Windows 3.1 # Windows 9x/Me WinRegistryFileMapping( 'HKEY_LOCAL_MACHINE', '%SystemRoot%\\SYSTEM.DAT', ['\\Config', '\\Enum', '\\Hardware', '\\Network', '\\Software', '\\System']), WinRegistryFileMapping( 'HKEY_USERS', '%SystemRoot%\\USER.DAT', ['\\.DEFAULT\\AppEvents', '\\.DEFAULT\\Control Panel', '\\.DEFAULT\\Keyboard Layout', '\\.DEFAULT\\Network', '\\.DEFAULT\\Software']), # Windows NT WinRegistryFileMapping( 'HKEY_CURRENT_USER', '%UserProfile%\\NTUSER.DAT', ['\\AppEvents', '\\Console', '\\Control Panel', '\\Environment', '\\Keyboard Layout', '\\Software']), # Windows Vista and later UsrClass.dat mapping WinRegistryFileMapping( 'HKEY_CURRENT_USER\\Software\\Classes', '%UserProfile%\\AppData\\Local\\Microsoft\\Windows\\UsrClass.dat', ['\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion']), # Windows 2000, XP and 2003 UsrClass.dat mapping WinRegistryFileMapping( 'HKEY_CURRENT_USER\\Software\\Classes', ('%UserProfile%\\Local Settings\\Application Data\\Microsoft\\' 'Windows\\UsrClass.dat'), ['\\Software\\Microsoft\\Windows\\CurrentVersion']), WinRegistryFileMapping( 'HKEY_LOCAL_MACHINE\\SAM', '%SystemRoot%\\System32\\config\\SAM', ['\\SAM\\Domains\\Account\\Users']), WinRegistryFileMapping( 'HKEY_LOCAL_MACHINE\\Security', '%SystemRoot%\\System32\\config\\SECURITY', ['\\Policy\\PolAdtEv']), WinRegistryFileMapping( 'HKEY_LOCAL_MACHINE\\Software', '%SystemRoot%\\System32\\config\\SOFTWARE', ['\\Microsoft\\Windows\\CurrentVersion\\App Paths']), WinRegistryFileMapping( 'HKEY_LOCAL_MACHINE\\System', '%SystemRoot%\\System32\\config\\SYSTEM', ['\\MountedDevices', '\\Select', '\\Setup'])] _ROOT_KEY_ALIASES = { 'HKCC': 'HKEY_CURRENT_CONFIG', 'HKCR': 'HKEY_CLASSES_ROOT', 'HKCU': 'HKEY_CURRENT_USER', 'HKLM': 'HKEY_LOCAL_MACHINE', 'HKU': 'HKEY_USERS'} _ROOT_SUB_KEYS = frozenset([ 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_CONFIG', 'HKEY_CURRENT_USER', 'HKEY_DYN_DATA', 'HKEY_LOCAL_MACHINE', 'HKEY_PERFORMANCE_DATA', 'HKEY_USERS']) _LOCAL_MACHINE_SUB_KEYS = frozenset([ 'SAM', 'Security', 'Software', 'System']) _SELECT_KEY_PATH = 'HKEY_LOCAL_MACHINE\\System\\Select' _USER_PROFILE_LIST_KEY_PATH = ( 'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\' 'ProfileList') _USER_SOFTWARE_CLASSES_KEY_PATH = 'HKEY_CURRENT_USER\\SOFTWARE\\CLASSES' # TODO: add support for HKEY_CLASSES_ROOT # TODO: add support for HKEY_CURRENT_CONFIG # TODO: add support for HKEY_CURRENT_USER # TODO: add support for HKEY_DYN_DATA _VIRTUAL_KEYS = [ ('HKEY_USERS', '_GetUsersVirtualKey')]
[docs] def __init__(self, ascii_codepage='cp1252', registry_file_reader=None): """Initializes the Windows Registry. Args: ascii_codepage (Optional[str]): ASCII string codepage. registry_file_reader (Optional[WinRegistryFileReader]): Windows Registry file reader. """ super(WinRegistry, self).__init__() self._ascii_codepage = ascii_codepage self._registry_file_reader = registry_file_reader self._registry_files = {} self._root_key = None self._user_registry_files = {}
[docs] def __del__(self): """Cleans up the Windows Registry object.""" for key_path_prefix_upper, registry_file in self._registry_files.items(): self._registry_files[key_path_prefix_upper] = None if registry_file: registry_file.Close() for profile_path_upper, registry_file in self._user_registry_files.items(): self._user_registry_files[profile_path_upper] = None if registry_file: registry_file.Close()
def _GetCachedFileByPath(self, key_path_upper): """Retrieves a cached Windows Registry file for a key path. Args: key_path_upper (str): Windows Registry key path, in upper case with a resolved root key alias. Returns: tuple: consist: str: key path prefix WinRegistryFile: corresponding Windows Registry file or None if not available. """ longest_key_path_prefix_upper = '' longest_key_path_prefix_length = len(longest_key_path_prefix_upper) for key_path_prefix_upper in self._registry_files: if key_path_upper.startswith(key_path_prefix_upper): key_path_prefix_length = len(key_path_prefix_upper) if key_path_prefix_length > longest_key_path_prefix_length: longest_key_path_prefix_upper = key_path_prefix_upper longest_key_path_prefix_length = key_path_prefix_length if not longest_key_path_prefix_upper: return None, None registry_file = self._registry_files.get( longest_key_path_prefix_upper, None) return longest_key_path_prefix_upper, registry_file def _GetCachedUserFileByPath(self, profile_path_upper): """Retrieves a cached user Windows Registry file for a profile path. Args: profile_path_upper (str): user profile path, in upper case. Returns: WinRegistryFile: corresponding Windows Registry file or None if not available. """ return self._user_registry_files.get(profile_path_upper, None) def _GetCandidateFileMappingsByPath(self, key_path_upper): """Retrieves candidate Windows Registry file mappings for a specific path. Args: key_path_upper (str): Windows Registry key path, in upper case with a resolved root key alias. Returns: list[WinRegistryFileMapping]: candidate Windows Registry file mappings. """ if key_path_upper[-1] == '\\': key_path_upper = key_path_upper[:-1] candidate_mappings = [] for mapping in self._REGISTRY_FILE_MAPPINGS: if key_path_upper.startswith(mapping.key_path_prefix.upper()): candidate_mappings.append(mapping) return candidate_mappings def _GetFileByPath(self, key_path_upper): """Retrieves a Windows Registry file for a specific path. Args: key_path_upper (str): Windows Registry key path, in upper case with a resolved root key alias. Returns: tuple[st, WinRegistryFile]: upper case key path prefix and corresponding Windows Registry file or None if not available. """ key_path_prefix, cached_registry_file = self._GetCachedFileByPath( key_path_upper) # NTUSER.DAT and UsrClass.dat have overlapping keys hence for key paths that # start "HKEY_CURRENT_USER\\Software\\Classes" do not assume the Windows # Registry file for "HKEY_CURRENT_USER" contains the classes key as well. if cached_registry_file and ( key_path_prefix != 'HKEY_CURRENT_USER' or not key_path_upper.startswith('HKEY_CURRENT_USER\\SOFTWARE\\CLASSES')): return key_path_prefix, cached_registry_file key_path_prefix = '' candidate_mappings = self._GetCandidateFileMappingsByPath(key_path_upper) matching_mappings = [] for mapping in candidate_mappings: mapping_key_path_prefix = mapping.key_path_prefix.upper() if mapping_key_path_prefix in self._registry_files: # Skip the Windows Registry file if it is already cached. continue try: registry_file = self._OpenFile(mapping.windows_path) except IOError: registry_file = None if len(key_path_prefix) < len(mapping_key_path_prefix): key_path_prefix = mapping_key_path_prefix if not registry_file: continue match = True if mapping.unique_key_paths: # If all unique key paths are found consider the file to match. for unique_key_path in mapping.unique_key_paths: registry_key = registry_file.GetKeyByPath(unique_key_path) if not registry_key: match = False if match: matching_mappings.append((mapping, registry_file)) if not matching_mappings: return key_path_prefix, cached_registry_file key_path_prefix = None registry_file = None while matching_mappings: mapping, matching_registry_file = matching_mappings.pop(0) if (not key_path_prefix or len(mapping.key_path_prefix) > len(key_path_prefix)): if registry_file: registry_file.Close() key_path_prefix = mapping.key_path_prefix registry_file = matching_registry_file key_path_prefix_upper = key_path_prefix.upper() self.MapFile(key_path_prefix_upper, registry_file) return key_path_prefix_upper, registry_file def _GetKeyByPathFromFile(self, key_path): """Retrieves the key for a specific path from file. Args: key_path (str): Windows Registry key path. Returns: WinRegistryKey: Windows Registry key or None if not available. Raises: RuntimeError: if the key path prefix does not match the key path. """ key_path_upper = key_path.upper() key_path_prefix_upper, registry_file = self._GetFileByPath(key_path_upper) if not registry_file: return None if not key_path_upper.startswith(key_path_prefix_upper): raise RuntimeError('Key path prefix mismatch.') key_path_suffix = key_path[len(key_path_prefix_upper):] relative_key_path = key_path_suffix or definitions.KEY_PATH_SEPARATOR return registry_file.GetKeyByPath(relative_key_path) def _GetRootVirtualKey(self): """Retrieves the root key. Returns: VirtualWinRegistryKey: Windows Registry root key. Raises: RuntimeError: if there are multiple matching mappings and the correct mapping cannot be resolved. """ if not self._root_key: self._root_key = virtual.VirtualWinRegistryKey('') local_machine_key = None for sub_key_name in self._ROOT_SUB_KEYS: sub_registry_key = self.GetKeyByPath(sub_key_name) if not sub_registry_key: sub_registry_key = virtual.VirtualWinRegistryKey( sub_key_name, registry=self, relative_key_path=sub_key_name) if sub_key_name == 'HKEY_LOCAL_MACHINE': local_machine_key = sub_registry_key self._root_key.AddSubkey(sub_key_name, sub_registry_key) if local_machine_key: for sub_key_name in self._LOCAL_MACHINE_SUB_KEYS: sub_key_path = definitions.KEY_PATH_SEPARATOR.join([ 'HKEY_LOCAL_MACHINE', sub_key_name]) sub_registry_key = self.GetKeyByPath(sub_key_path) if not sub_registry_key: sub_registry_key = virtual.VirtualWinRegistryKey( sub_key_name, registry=self, relative_key_path=sub_key_name) local_machine_key.AddSubkey(sub_key_name, sub_registry_key) return self._root_key def _GetUserFileByPath(self, path): """Retrieves an user Windows Registry file for a specific path. Args: path (str): path of the user Windows Registry file. Returns: WinRegistryFile: corresponding Windows Registry file or None if not available. """ path_upper = path.upper() registry_file = self._GetCachedUserFileByPath(path_upper) if not registry_file: try: registry_file = self._OpenFile(path) except IOError: registry_file = None return registry_file def _GetUsersVirtualKey(self, key_path_suffix): """Virtual key callback to determine the users sub keys. Args: key_path_suffix (str): users Windows Registry key path suffix with leading path separator. Returns: WinRegistryKey: the users Windows Registry key or None if not available. Raises: RuntimeError: if the key path suffix is not supported. """ if key_path_suffix[0] != definitions.KEY_PATH_SEPARATOR: raise RuntimeError(f'Unsupported key path suffix: {key_path_suffix:s}') user_key_name, _, key_path_suffix = key_path_suffix[1:].partition( definitions.KEY_PATH_SEPARATOR) user_key_name = user_key_name.upper() # HKEY_USERS\.DEFAULT is an alias for HKEY_USERS\S-1-5-18 which is # the Local System account. if user_key_name == '.DEFAULT': search_key_name = 'S-1-5-18' else: search_key_name = user_key_name if search_key_name.endswith('_CLASSES'): search_key_name = search_key_name[:-8] user_profile_list_key = self.GetKeyByPath(self._USER_PROFILE_LIST_KEY_PATH) if not user_profile_list_key: return None for user_profile_key in user_profile_list_key.GetSubkeys(): if search_key_name == user_profile_key.name: profile_path_value = user_profile_key.GetValueByName('ProfileImagePath') if not profile_path_value: break profile_path = profile_path_value.GetDataAsObject() if not profile_path: break # HKEY_USERS\%SID%_CLASSES maps to UsrClass.dat if user_key_name.endswith('_CLASSES'): profile_path = definitions.KEY_PATH_SEPARATOR.join([ profile_path, 'AppData', 'Local', 'Microsoft', 'Windows', 'UsrClass.dat']) else: profile_path = definitions.KEY_PATH_SEPARATOR.join([ profile_path, 'NTUSER.DAT']) registry_file = self._GetUserFileByPath(profile_path) if not registry_file: break key_path_prefix = definitions.KEY_PATH_SEPARATOR.join([ 'HKEY_USERS', user_key_name]) key_path = definitions.KEY_PATH_SEPARATOR.join([ key_path_prefix, key_path_suffix]) registry_file.SetKeyPathPrefix(key_path_prefix) return registry_file.GetKeyByPath(key_path) return None def _GetVirtualKeyByPath(self, key_path): """Retrieves the virtual key for a specific path. Args: key_path (str): Windows Registry key path. Returns: VirtualWinRegistryKey: virtual Windows Registry key or None if not available. """ key_path_upper = key_path.upper() for virtual_key_path, virtual_key_callback in self._VIRTUAL_KEYS: virtual_key_path_upper = virtual_key_path.upper() if key_path_upper.startswith(virtual_key_path_upper): key_path_suffix = key_path[len(virtual_key_path):] callback_function = getattr(self, virtual_key_callback) virtual_key = callback_function(key_path_suffix) if virtual_key: return virtual_key return None def _OpenFile(self, path): """Opens a Windows Registry file. Args: path (str): path of the Windows Registry file. Returns: WinRegistryFile: Windows Registry file or None if not available. """ if not self._registry_file_reader: return None return self._registry_file_reader.Open( path, ascii_codepage=self._ascii_codepage)
[docs] def GetKeyByPath(self, key_path): """Retrieves the key for a specific path. Args: key_path (str): Windows Registry key path. Returns: WinRegistryKey: Windows Registry key or None if not available. Raises: RuntimeError: if the root key is not supported or the key path prefix does not match the key path. """ root_key_path, _, key_path = key_path.partition( definitions.KEY_PATH_SEPARATOR) # Resolve a root key alias. root_key_path = root_key_path.upper() root_key_path = self._ROOT_KEY_ALIASES.get(root_key_path, root_key_path) if root_key_path not in self._ROOT_SUB_KEYS: raise RuntimeError(f'Unsupported root subkey: {root_key_path:s}') key_path = definitions.KEY_PATH_SEPARATOR.join([root_key_path, key_path]) registry_key = self._GetKeyByPathFromFile(key_path) if not registry_key: registry_key = self._GetVirtualKeyByPath(key_path) return registry_key
[docs] def GetRegistryFileMapping(self, registry_file): """Determines the Registry file mapping based on the content of the file. Args: registry_file (WinRegistyFile): Windows Registry file. Returns: str: key path prefix or an empty string. Raises: RuntimeError: if there are multiple matching mappings and the correct mapping cannot be resolved. """ if not registry_file: return '' candidate_mappings = [] for mapping in self._REGISTRY_FILE_MAPPINGS: if not mapping.unique_key_paths: continue # If all unique key paths are found consider the file to match. match = True for key_path in mapping.unique_key_paths: registry_key = registry_file.GetKeyByPath(key_path) if not registry_key: match = False if match: candidate_mappings.append(mapping) if not candidate_mappings: return '' if len(candidate_mappings) == 1: return candidate_mappings[0].key_path_prefix key_path_prefixes = frozenset([ mapping.key_path_prefix for mapping in candidate_mappings]) expected_key_path_prefixes = frozenset([ 'HKEY_CURRENT_USER', 'HKEY_CURRENT_USER\\Software\\Classes']) if key_path_prefixes == expected_key_path_prefixes: return 'HKEY_CURRENT_USER' raise RuntimeError('Unable to resolve Windows Registry file mapping.')
[docs] def GetRootKey(self): """Retrieves the Windows Registry root key. Returns: VirtualWinRegistryKey: Windows Registry root key. """ return self._GetRootVirtualKey()
[docs] def MapFile(self, key_path_prefix, registry_file): """Maps the Windows Registry file to a specific key path prefix. Args: key_path_prefix (str): key path prefix. registry_file (WinRegistryFile): Windows Registry file. """ key_path_prefix_upper = key_path_prefix.upper() self._registry_files[key_path_prefix_upper] = registry_file registry_file.SetKeyPathPrefix(key_path_prefix) # If HKEY_CURRENT_USER set HKEY_CURRENT_USER\\Software\\Classes as a virtual # key in the file. if key_path_prefix_upper == 'HKEY_CURRENT_USER' and isinstance( registry_file, regf.REGFWinRegistryFile): key_path_prefix_upper, usrclass_registry_file = self._GetFileByPath( self._USER_SOFTWARE_CLASSES_KEY_PATH) if (key_path_prefix_upper == self._USER_SOFTWARE_CLASSES_KEY_PATH and usrclass_registry_file is not None): registry_key = usrclass_registry_file.GetKeyByPath( '\\Software\\Classes') if registry_key and isinstance(registry_key, regf.REGFWinRegistryKey): # pylint: disable=protected-access pyregf_key = registry_key._pyregf_key registry_file.AddVirtualKey('\\Software\\Classes', pyregf_key) # If HKEY_LOCAL_MACHINE\\System set HKEY_LOCAL_MACHINE\\System\\ # CurrentControlSet as a virtual key in the file. elif key_path_prefix_upper == 'HKEY_LOCAL_MACHINE\\SYSTEM' and isinstance( registry_file, regf.REGFWinRegistryFile): registry_file.AddCurrentControlSetKey()
[docs] def MapUserFile(self, profile_path, registry_file): """Maps the user Windows Registry file to a specific profile path. Args: profile_path (str): profile path. registry_file (WinRegistryFile): user Windows Registry file. """ self._user_registry_files[profile_path.upper()] = registry_file
[docs] def OpenAndMapFile(self, path): """Opens Windows Registry file and maps it to its key path prefix. Args: path (str): path of the Windows Registry file. """ registry_file = self._OpenFile(path) key_path_prefix = self.GetRegistryFileMapping(registry_file) self.MapFile(key_path_prefix, registry_file)
[docs] def SplitKeyPath(self, key_path): """Splits the key path into path segments. Args: key_path (str): key path. Returns: list[str]: key path segments without the root path segment, which is an empty string. """ return key_paths.SplitKeyPath(key_path)