Script for automating a large assortment of AME related actions
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

374 lines
15 KiB

9 months ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text.RegularExpressions;
  6. using System.Threading.Tasks;
  7. using Microsoft.Win32;
  8. namespace amecs
  9. {
  10. public enum RegistryOperation
  11. {
  12. Delete = 0,
  13. Add = 1
  14. }
  15. public class Reg
  16. {
  17. public class Key
  18. {
  19. public string KeyName { get; set; }
  20. //public Scope Scope { get; set; } = Scope.AllUsers;
  21. public RegistryOperation Operation { get; set; } = RegistryOperation.Delete;
  22. private List<RegistryKey> GetRoots()
  23. {
  24. var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper();
  25. var list = new List<RegistryKey>();
  26. list.Add(hive switch
  27. {
  28. "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  29. "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  30. "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  31. "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  32. "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  33. "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  34. "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  35. "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  36. _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.")
  37. });
  38. return list;
  39. }
  40. public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1);
  41. public bool IsEqual()
  42. {
  43. try
  44. {
  45. var roots = GetRoots();
  46. foreach (var _root in roots)
  47. {
  48. var root = _root;
  49. var subKey = GetSubKey();
  50. var openedSubKey = root.OpenSubKey(subKey);
  51. if (Operation == RegistryOperation.Delete && openedSubKey != null)
  52. {
  53. return false;
  54. }
  55. if (Operation == RegistryOperation.Add && openedSubKey == null)
  56. {
  57. return false;
  58. }
  59. }
  60. } catch (Exception e)
  61. {
  62. return false;
  63. }
  64. return true;
  65. }
  66. public bool Apply()
  67. {
  68. var roots = GetRoots();
  69. foreach (var _root in roots)
  70. {
  71. var root = _root;
  72. var subKey = GetSubKey();
  73. var openedSubKey = root.OpenSubKey(subKey);
  74. if (openedSubKey != null) openedSubKey.Close();
  75. if (Operation == RegistryOperation.Add && openedSubKey == null)
  76. {
  77. root.CreateSubKey(subKey)?.Close();
  78. }
  79. if (Operation == RegistryOperation.Delete)
  80. {
  81. root.DeleteSubKeyTree(subKey, false);
  82. }
  83. root.Close();
  84. }
  85. return true;
  86. }
  87. }
  88. public enum RegistryValueOperation
  89. {
  90. Delete = 0,
  91. Add = 1,
  92. // This indicates to skip the action if the specified value does not already exist
  93. Set = 2
  94. }
  95. public enum RegistryValueType
  96. {
  97. REG_SZ = RegistryValueKind.String,
  98. REG_MULTI_SZ = RegistryValueKind.MultiString,
  99. REG_EXPAND_SZ = RegistryValueKind.ExpandString,
  100. REG_DWORD = RegistryValueKind.DWord,
  101. REG_QWORD = RegistryValueKind.QWord,
  102. REG_BINARY = RegistryValueKind.Binary,
  103. REG_NONE = RegistryValueKind.None,
  104. REG_UNKNOWN = RegistryValueKind.Unknown
  105. }
  106. public class Value
  107. {
  108. public string KeyName { get; set; }
  109. public string ValueName { get; set; } = "";
  110. public object? Data { get; set; }
  111. public RegistryValueType Type { get; set; }
  112. //public Scope Scope { get; set; } = Scope.AllUsers;
  113. public RegistryValueOperation Operation { get; set; } = RegistryValueOperation.Add;
  114. private List<RegistryKey> GetRoots()
  115. {
  116. var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper();
  117. var list = new List<RegistryKey>();
  118. list.Add(hive switch
  119. {
  120. "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  121. "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  122. "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  123. "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  124. "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  125. "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  126. "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  127. "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  128. _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.")
  129. });
  130. return list;
  131. }
  132. public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1);
  133. public object? GetCurrentValue(RegistryKey root)
  134. {
  135. var subkey = GetSubKey();
  136. return Registry.GetValue(root.Name + "\\" + subkey, ValueName, null);
  137. }
  138. public static byte[] StringToByteArray(string hex)
  139. {
  140. return Enumerable.Range(0, hex.Length)
  141. .Where(x => x % 2 == 0)
  142. .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
  143. .ToArray();
  144. }
  145. public bool IsEqual()
  146. {
  147. try
  148. {
  149. var roots = GetRoots();
  150. foreach (var _root in roots)
  151. {
  152. var root = _root;
  153. var subKey = GetSubKey();
  154. var openedSubKey = root.OpenSubKey(subKey);
  155. if (openedSubKey == null && (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete))
  156. continue;
  157. if (openedSubKey == null) return false;
  158. var value = openedSubKey.GetValue(ValueName);
  159. if (value == null)
  160. {
  161. if (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete)
  162. continue;
  163. return false;
  164. }
  165. if (Operation == RegistryValueOperation.Delete) return false;
  166. if (Data == null) return false;
  167. bool matches;
  168. try
  169. {
  170. matches = Type switch
  171. {
  172. RegistryValueType.REG_SZ =>
  173. Data.ToString() == value.ToString(),
  174. RegistryValueType.REG_EXPAND_SZ =>
  175. // RegistryValueOptions.DoNotExpandEnvironmentNames above did not seem to work.
  176. Environment.ExpandEnvironmentVariables(Data.ToString()) == value.ToString(),
  177. RegistryValueType.REG_MULTI_SZ =>
  178. Data.ToString() == "" ? ((string[])value).SequenceEqual(new string[] { }) : ((string[])value).SequenceEqual(Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None)),
  179. RegistryValueType.REG_DWORD =>
  180. unchecked((int)Convert.ToUInt32(Data)) == (int)value,
  181. RegistryValueType.REG_QWORD =>
  182. Convert.ToUInt64(Data) == (ulong)value,
  183. RegistryValueType.REG_BINARY =>
  184. ((byte[])value).SequenceEqual(StringToByteArray(Data.ToString())),
  185. RegistryValueType.REG_NONE =>
  186. ((byte[])value).SequenceEqual(new byte[0]),
  187. RegistryValueType.REG_UNKNOWN =>
  188. Data.ToString() == value.ToString(),
  189. _ => throw new ArgumentException("Impossible.")
  190. };
  191. } catch (InvalidCastException)
  192. {
  193. matches = false;
  194. }
  195. if (!matches) return false;
  196. }
  197. } catch (Exception e)
  198. {
  199. return false;
  200. }
  201. return true;
  202. }
  203. public bool Apply()
  204. {
  205. var roots = GetRoots();
  206. foreach (var _root in roots)
  207. {
  208. var root = _root;
  209. var subKey = GetSubKey();
  210. if (GetCurrentValue(root) == Data) continue;
  211. var opened = root.OpenSubKey(subKey);
  212. if (opened == null && Operation == RegistryValueOperation.Set) continue;
  213. if (opened == null && Operation == RegistryValueOperation.Add) root.CreateSubKey(subKey)?.Close();
  214. if (opened != null) opened.Close();
  215. if (Operation == RegistryValueOperation.Delete)
  216. {
  217. var key = root.OpenSubKey(subKey, true);
  218. key?.DeleteValue(ValueName);
  219. key?.Close();
  220. root.Close();
  221. continue;
  222. }
  223. if (Type == RegistryValueType.REG_BINARY)
  224. {
  225. var data = StringToByteArray(Data.ToString());
  226. Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type);
  227. }
  228. else if (Type == RegistryValueType.REG_DWORD)
  229. {
  230. // DWORD values using the highest bit set fail without this, for example '2962489444'.
  231. // See https://stackoverflow.com/questions/6608400/how-to-put-a-dword-in-the-registry-with-the-highest-bit-set;
  232. var value = unchecked((int)Convert.ToUInt32(Data));
  233. Registry.SetValue(root.Name + "\\" + subKey, ValueName, value, (RegistryValueKind)Type);
  234. }
  235. else if (Type == RegistryValueType.REG_QWORD)
  236. {
  237. Registry.SetValue(root.Name + "\\" + subKey, ValueName, Convert.ToUInt64(Data), (RegistryValueKind)Type);
  238. }
  239. else if (Type == RegistryValueType.REG_NONE)
  240. {
  241. byte[] none = new byte[0];
  242. Registry.SetValue(root.Name + "\\" + subKey, ValueName, none, (RegistryValueKind)Type);
  243. }
  244. else if (Type == RegistryValueType.REG_MULTI_SZ)
  245. {
  246. string[] data;
  247. if (Data.ToString() == "") data = new string[] { };
  248. else data = Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None);
  249. Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type);
  250. }
  251. else
  252. {
  253. Registry.SetValue(root.Name + "\\" + subKey, ValueName, Data, (RegistryValueKind)Type);
  254. }
  255. root.Close();
  256. }
  257. return true;
  258. }
  259. }
  260. [DllImport("advapi32.dll", SetLastError = true)]
  261. static extern int RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile);
  262. [DllImport("advapi32.dll", SetLastError = true)]
  263. static extern int RegSaveKey(IntPtr hKey, string lpFile, uint securityAttrPtr = 0);
  264. [DllImport("advapi32.dll", SetLastError = true)]
  265. static extern int RegUnLoadKey(IntPtr hKey, string lpSubKey);
  266. [DllImport("ntdll.dll", SetLastError = true)]
  267. static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
  268. [DllImport("advapi32.dll")]
  269. static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref UInt64 lpLuid);
  270. [DllImport("advapi32.dll")]
  271. static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid);
  272. public static void LoadDefaultUserHive()
  273. {
  274. var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  275. IntPtr parentHandle = parentKey.Handle.DangerousGetHandle();
  276. AcquirePrivileges();
  277. RegLoadKey(parentHandle, "DefaultUserHive", Environment.ExpandEnvironmentVariables(@"%SYSTEMDRIVE%\Users\Default\NTUSER.dat"));
  278. parentKey.Close();
  279. }
  280. public static void UnloadDefaultUserHive()
  281. {
  282. var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  283. AcquirePrivileges();
  284. RegUnLoadKey(parentKey.Handle.DangerousGetHandle(), "DefaultUserHive");
  285. parentKey.Close();
  286. }
  287. public static void AcquirePrivileges()
  288. {
  289. ulong luid = 0;
  290. bool throwaway;
  291. LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
  292. RtlAdjustPrivilege((int)luid, true, true, out throwaway);
  293. LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
  294. RtlAdjustPrivilege((int)luid, true, true, out throwaway);
  295. }
  296. public static void ReturnPrivileges()
  297. {
  298. ulong luid = 0;
  299. bool throwaway;
  300. LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
  301. RtlAdjustPrivilege((int)luid, false, true, out throwaway);
  302. LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
  303. RtlAdjustPrivilege((int)luid, false, true, out throwaway);
  304. }
  305. }
  306. }