# # Copyright (C) 2020-2024 Soleta Networks # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the # Free Software Foundation; either version 3 of the License, or # (at your option) any later version. import libhivexmod import hivex from enum import Enum from src.log import OgError from uuid import UUID from src.utils.disk import * from src.utils.uefi import is_uefi_supported WINDOWS_HIVES_PATH = '/Windows/System32/config' WINDOWS_HIVE_SOFTWARE = f'{WINDOWS_HIVES_PATH}/SOFTWARE' WINDOWS_HIVE_SYSTEM = f'{WINDOWS_HIVES_PATH}/SYSTEM' WINDOWS_HIVE_ENCODING = 'utf-16-le' class RegistryType(Enum): NONE = 0 SZ = 1 EXPAND_SZ = 2 BINARY = 3 DWORD = 4 DWORD_BIG_ENDIAN = 5 LINK = 6 MULTI_SZ = 7 RESOURCE_LIST = 8 FULL_RESOURCE_DESCRIPTIOR = 9 RESOURCE_REQUIREMENTS_LISTS = 10 QWORD = 11 def get_value_from_node(hive, node, value): type_node_value = hive.node_get_value(node, value) val = None val_type = hive.value_type(type_node_value)[0] rtype = RegistryType(val_type) if rtype == RegistryType.SZ: val = hive.value_string(type_node_value) elif rtype == RegistryType.MULTI_SZ: val = hive.value_multiple_strings(type_node_value) elif rtype == RegistryType.DWORD: val = hive.value_dword(type_node_value) elif rtype == RegistryType.QWORD: val = hive.value_qword(type_node_value) elif rtype == RegistryType.BINARY: _, val = hive.value_value(type_node_value) if not val: raise OgError(f'No valid value found for the node {hive.node_name(node)}') return val def check_hive_consistency(hive_path): """ The Hivex class tries to call libhivexmod.close in the __del__ method. That is not reliable as trying to free resources from a corrupted hivex file will not work, we first check if the hivex can be loaded. """ try: h = libhivexmod.open(hive_path, 0) libhivexmod.close(h) except RuntimeError as e: raise OgError(f'Unable to load the registry hive at {hive_path}: {e}') from e def hive_handler_open(hive_path, write): check_hive_consistency(hive_path) h = hivex.Hivex(hive_path, write = write) if not h.root(): raise OgError(f'Unable to find the root node for hive at {hive_path}') from e return h def get_node_child(hive, node, name): child_node = hive.node_get_child(node, name) if not child_node: raise OgError(f'Could not find child node {name} in {hive.node_name(node)}') return child_node def get_node_child_from_path(hive, node, path): path_components = path.split('/') child_node = node for node_name in path_components: child_node = get_node_child(hive, child_node, node_name) return child_node def uuid_to_bytes(uuid): uuid = uuid.replace('-', '') group0 = f'{uuid[6:8]}{uuid[4:6]}{uuid[2:4]}{uuid[0:2]}' group1 = f'{uuid[10:12]}{uuid[8:10]}' group2 = f'{uuid[14:16]}{uuid[12:14]}' group3 = uuid[16:20] group4 = uuid[20:32] res = f'{group0}-{group1}-{group2}-{group3}-{group4}' return UUID(res).bytes def get_disk_id_bytes(disk): disk_id = get_disk_id(disk) if is_uefi_supported(): return uuid_to_bytes(disk_id) return bytes.fromhex(disk_id)[::-1] def get_part_id_bytes(disk, partition): if is_uefi_supported(): part_id = get_partition_id(disk, partition) return uuid_to_bytes(part_id) partition_start_offset = get_partition_start_offset(disk, partition) sector_size = get_sector_size(disk) byte_offset = partition_start_offset * sector_size byte_offset = "{0:016x}".format(byte_offset) return bytes.fromhex(byte_offset)[::-1]