Source code for dfwinreg.registry

"""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: """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().__init__() self.key_path_prefix = key_path_prefix self.unique_key_paths = unique_key_paths self.windows_path = windows_path
[docs] class WinRegistry: """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().__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) 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 OSError: 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 OSError: 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)