utils: add win edit registry utilities

Add winreg.py to the utils folder. Implement hive enum types,
hive handler validation and validated get_* functions for
nodes and registry values.

Implement the utility hive operations through the Hivex library.

This serves as preparatory work for BCD manipulation but it also
has potential to improve registry usage in previous code.

UCS-2 Little Endian is the prefered windows registry text encoding
for binary content. Define a WINDOWS_HIVE_ENCODING global variable
to use when encoding string to write in the win registry.

This commit is preparatory work for the new native postinstall code.
master
Alejandro Sirgo Rica 2024-04-17 11:26:21 +02:00
parent 7ab965c0b5
commit bb03e92840
1 changed files with 114 additions and 0 deletions

114
src/utils/winreg.py 100644
View File

@ -0,0 +1,114 @@
#
# Copyright (C) 2022 Soleta Networks <info@soleta.eu>
#
# 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
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