using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Win32; namespace amecs { public enum RegistryOperation { Delete = 0, Add = 1 } public class Reg { public class Key { public string KeyName { get; set; } //public Scope Scope { get; set; } = Scope.AllUsers; public RegistryOperation Operation { get; set; } = RegistryOperation.Delete; private List GetRoots() { var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); var list = new List(); list.Add(hive switch { "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") }); return list; } public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); public bool IsEqual() { try { var roots = GetRoots(); foreach (var _root in roots) { var root = _root; var subKey = GetSubKey(); var openedSubKey = root.OpenSubKey(subKey); if (Operation == RegistryOperation.Delete && openedSubKey != null) { return false; } if (Operation == RegistryOperation.Add && openedSubKey == null) { return false; } } } catch (Exception e) { return false; } return true; } public bool Apply() { var roots = GetRoots(); foreach (var _root in roots) { var root = _root; var subKey = GetSubKey(); var openedSubKey = root.OpenSubKey(subKey); if (openedSubKey != null) openedSubKey.Close(); if (Operation == RegistryOperation.Add && openedSubKey == null) { root.CreateSubKey(subKey)?.Close(); } if (Operation == RegistryOperation.Delete) { root.DeleteSubKeyTree(subKey, false); } root.Close(); } return true; } } public enum RegistryValueOperation { Delete = 0, Add = 1, // This indicates to skip the action if the specified value does not already exist Set = 2 } public enum RegistryValueType { REG_SZ = RegistryValueKind.String, REG_MULTI_SZ = RegistryValueKind.MultiString, REG_EXPAND_SZ = RegistryValueKind.ExpandString, REG_DWORD = RegistryValueKind.DWord, REG_QWORD = RegistryValueKind.QWord, REG_BINARY = RegistryValueKind.Binary, REG_NONE = RegistryValueKind.None, REG_UNKNOWN = RegistryValueKind.Unknown } public class Value { public string KeyName { get; set; } public string ValueName { get; set; } = ""; public object? Data { get; set; } public RegistryValueType Type { get; set; } //public Scope Scope { get; set; } = Scope.AllUsers; public RegistryValueOperation Operation { get; set; } = RegistryValueOperation.Add; private List GetRoots() { var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); var list = new List(); list.Add(hive switch { "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") }); return list; } public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); public object? GetCurrentValue(RegistryKey root) { var subkey = GetSubKey(); return Registry.GetValue(root.Name + "\\" + subkey, ValueName, null); } public static byte[] StringToByteArray(string hex) { return Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); } public bool IsEqual() { try { var roots = GetRoots(); foreach (var _root in roots) { var root = _root; var subKey = GetSubKey(); var openedSubKey = root.OpenSubKey(subKey); if (openedSubKey == null && (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete)) continue; if (openedSubKey == null) return false; var value = openedSubKey.GetValue(ValueName); if (value == null) { if (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete) continue; return false; } if (Operation == RegistryValueOperation.Delete) return false; if (Data == null) return false; bool matches; try { matches = Type switch { RegistryValueType.REG_SZ => Data.ToString() == value.ToString(), RegistryValueType.REG_EXPAND_SZ => // RegistryValueOptions.DoNotExpandEnvironmentNames above did not seem to work. Environment.ExpandEnvironmentVariables(Data.ToString()) == value.ToString(), RegistryValueType.REG_MULTI_SZ => Data.ToString() == "" ? ((string[])value).SequenceEqual(new string[] { }) : ((string[])value).SequenceEqual(Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None)), RegistryValueType.REG_DWORD => unchecked((int)Convert.ToUInt32(Data)) == (int)value, RegistryValueType.REG_QWORD => Convert.ToUInt64(Data) == (ulong)value, RegistryValueType.REG_BINARY => ((byte[])value).SequenceEqual(StringToByteArray(Data.ToString())), RegistryValueType.REG_NONE => ((byte[])value).SequenceEqual(new byte[0]), RegistryValueType.REG_UNKNOWN => Data.ToString() == value.ToString(), _ => throw new ArgumentException("Impossible.") }; } catch (InvalidCastException) { matches = false; } if (!matches) return false; } } catch (Exception e) { return false; } return true; } public bool Apply() { var roots = GetRoots(); foreach (var _root in roots) { var root = _root; var subKey = GetSubKey(); if (GetCurrentValue(root) == Data) continue; var opened = root.OpenSubKey(subKey); if (opened == null && Operation == RegistryValueOperation.Set) continue; if (opened == null && Operation == RegistryValueOperation.Add) root.CreateSubKey(subKey)?.Close(); if (opened != null) opened.Close(); if (Operation == RegistryValueOperation.Delete) { var key = root.OpenSubKey(subKey, true); key?.DeleteValue(ValueName); key?.Close(); root.Close(); continue; } if (Type == RegistryValueType.REG_BINARY) { var data = StringToByteArray(Data.ToString()); Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type); } else if (Type == RegistryValueType.REG_DWORD) { // DWORD values using the highest bit set fail without this, for example '2962489444'. // See https://stackoverflow.com/questions/6608400/how-to-put-a-dword-in-the-registry-with-the-highest-bit-set; var value = unchecked((int)Convert.ToUInt32(Data)); Registry.SetValue(root.Name + "\\" + subKey, ValueName, value, (RegistryValueKind)Type); } else if (Type == RegistryValueType.REG_QWORD) { Registry.SetValue(root.Name + "\\" + subKey, ValueName, Convert.ToUInt64(Data), (RegistryValueKind)Type); } else if (Type == RegistryValueType.REG_NONE) { byte[] none = new byte[0]; Registry.SetValue(root.Name + "\\" + subKey, ValueName, none, (RegistryValueKind)Type); } else if (Type == RegistryValueType.REG_MULTI_SZ) { string[] data; if (Data.ToString() == "") data = new string[] { }; else data = Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None); Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type); } else { Registry.SetValue(root.Name + "\\" + subKey, ValueName, Data, (RegistryValueKind)Type); } root.Close(); } return true; } } [DllImport("advapi32.dll", SetLastError = true)] static extern int RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile); [DllImport("advapi32.dll", SetLastError = true)] static extern int RegSaveKey(IntPtr hKey, string lpFile, uint securityAttrPtr = 0); [DllImport("advapi32.dll", SetLastError = true)] static extern int RegUnLoadKey(IntPtr hKey, string lpSubKey); [DllImport("ntdll.dll", SetLastError = true)] static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue); [DllImport("advapi32.dll")] static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref UInt64 lpLuid); [DllImport("advapi32.dll")] static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid); public static void LoadDefaultUserHive() { var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); IntPtr parentHandle = parentKey.Handle.DangerousGetHandle(); AcquirePrivileges(); RegLoadKey(parentHandle, "DefaultUserHive", Environment.ExpandEnvironmentVariables(@"%SYSTEMDRIVE%\Users\Default\NTUSER.dat")); parentKey.Close(); } public static void UnloadDefaultUserHive() { var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); AcquirePrivileges(); RegUnLoadKey(parentKey.Handle.DangerousGetHandle(), "DefaultUserHive"); parentKey.Close(); } public static void AcquirePrivileges() { ulong luid = 0; bool throwaway; LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid); RtlAdjustPrivilege((int)luid, true, true, out throwaway); LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid); RtlAdjustPrivilege((int)luid, true, true, out throwaway); } public static void ReturnPrivileges() { ulong luid = 0; bool throwaway; LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid); RtlAdjustPrivilege((int)luid, false, true, out throwaway); LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid); RtlAdjustPrivilege((int)luid, false, true, out throwaway); } } }