CLI tool for running Playbooks
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.

251 lines
11 KiB

1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using System.Security;
  7. using System.Security.Principal;
  8. using System.Text.RegularExpressions;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. using Microsoft.Win32;
  12. using TrustedUninstaller.Shared.Exceptions;
  13. using TrustedUninstaller.Shared.Tasks;
  14. using YamlDotNet.Serialization;
  15. namespace TrustedUninstaller.Shared.Actions
  16. {
  17. public enum RegistryKeyOperation
  18. {
  19. Delete = 0,
  20. Add = 1
  21. }
  22. public class RegistryKeyAction : TaskAction, ITaskAction
  23. {
  24. public void RunTaskOnMainThread() { throw new NotImplementedException(); }
  25. [YamlMember(typeof(string), Alias = "path")]
  26. public string KeyName { get; set; }
  27. [YamlMember(typeof(Scope), Alias = "scope")]
  28. public Scope Scope { get; set; } = Scope.AllUsers;
  29. [YamlMember(typeof(RegistryKeyOperation), Alias = "operation")]
  30. public RegistryKeyOperation Operation { get; set; } = RegistryKeyOperation.Delete;
  31. [YamlMember(typeof(string), Alias = "weight")]
  32. public int ProgressWeight { get; set; } = 1;
  33. public int GetProgressWeight() => ProgressWeight;
  34. static Dictionary<RegistryHive, UIntPtr> HiveKeys = new Dictionary<RegistryHive, UIntPtr> {
  35. { RegistryHive.ClassesRoot, new UIntPtr(0x80000000u) },
  36. { RegistryHive.CurrentConfig, new UIntPtr(0x80000005u) },
  37. { RegistryHive.CurrentUser, new UIntPtr(0x80000001u) },
  38. { RegistryHive.DynData, new UIntPtr(0x80000006u) },
  39. { RegistryHive.LocalMachine, new UIntPtr(0x80000002u) },
  40. { RegistryHive.PerformanceData, new UIntPtr(0x80000004u) },
  41. { RegistryHive.Users, new UIntPtr(0x80000003u) }
  42. };
  43. [DllImport("advapi32.dll", SetLastError = true)]
  44. private static extern int RegOpenKeyEx(UIntPtr hKey, string subKey, int ulOptions, int samDesired, out UIntPtr hkResult);
  45. [DllImport("advapi32.dll", EntryPoint = "RegDeleteKeyEx", SetLastError = true)]
  46. private static extern int RegDeleteKeyEx(
  47. UIntPtr hKey,
  48. string lpSubKey,
  49. uint samDesired, // see Notes below
  50. uint Reserved);
  51. private static void DeleteKeyTreeWin32(string key, RegistryHive hive)
  52. {
  53. var openedKey = RegistryKey.OpenBaseKey(hive, RegistryView.Default).OpenSubKey(key);
  54. if (openedKey == null)
  55. return;
  56. openedKey.GetSubKeyNames().ToList().ForEach(subKey => DeleteKeyTreeWin32(key + "\\" + subKey, hive));
  57. openedKey.Close();
  58. RegDeleteKeyEx(HiveKeys[hive], key, 0x0100, 0);
  59. }
  60. private bool InProgress { get; set; }
  61. public void ResetProgress() => InProgress = false;
  62. public string ErrorString() => $"RegistryKeyAction failed to {Operation.ToString().ToLower()} key '{KeyName}'.";
  63. private List<RegistryKey> GetRoots()
  64. {
  65. var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper();
  66. var list = new List<RegistryKey>();
  67. if (hive.Equals("HKCU") || hive.Equals("HKEY_CURRENT_USER"))
  68. {
  69. RegistryKey usersKey;
  70. List<string> userKeys;
  71. switch (Scope)
  72. {
  73. case Scope.AllUsers:
  74. WinUtil.RegistryManager.HookUserHives();
  75. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  76. userKeys = usersKey.GetSubKeyNames().
  77. Where(x => x.StartsWith("S-") &&
  78. usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList();
  79. userKeys.AddRange(usersKey.GetSubKeyNames().Where(x => x.StartsWith("AME_UserHive_") && !x.EndsWith("_Classes")).ToList());
  80. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  81. return list;
  82. case Scope.ActiveUsers:
  83. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  84. userKeys = usersKey.GetSubKeyNames().
  85. Where(x => x.StartsWith("S-") &&
  86. usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList();
  87. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  88. return list;
  89. case Scope.DefaultUser:
  90. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  91. userKeys = usersKey.GetSubKeyNames().Where(x => x.Equals("AME_UserHive_Default") && !x.EndsWith("_Classes")).ToList();
  92. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  93. return list;
  94. }
  95. }
  96. list.Add(hive switch
  97. {
  98. "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  99. "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  100. "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  101. "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  102. "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  103. "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  104. "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  105. "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  106. _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.")
  107. });
  108. return list;
  109. }
  110. public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1);
  111. private RegistryKey? OpenSubKey(RegistryKey root)
  112. {
  113. var subKeyPath = GetSubKey();
  114. if (subKeyPath == null) throw new ArgumentException($"Key '{KeyName}' is invalid.");
  115. return root.OpenSubKey(subKeyPath, true);
  116. }
  117. public UninstallTaskStatus GetStatus()
  118. {
  119. try
  120. {
  121. var roots = GetRoots();
  122. foreach (var _root in roots)
  123. {
  124. var root = _root;
  125. var subKey = GetSubKey();
  126. if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase))
  127. {
  128. var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  129. root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true);
  130. subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase);
  131. if (root == null)
  132. {
  133. continue;
  134. }
  135. }
  136. var openedSubKey = root.OpenSubKey(subKey);
  137. if (Operation == RegistryKeyOperation.Delete && openedSubKey != null)
  138. {
  139. return UninstallTaskStatus.ToDo;
  140. }
  141. if (Operation == RegistryKeyOperation.Add && openedSubKey == null)
  142. {
  143. return UninstallTaskStatus.ToDo;
  144. }
  145. }
  146. }
  147. catch (Exception e)
  148. {
  149. ErrorLogger.WriteToErrorLog(e.Message,
  150. e.StackTrace, "RegistryKeyAction Error");
  151. return UninstallTaskStatus.ToDo;
  152. }
  153. return UninstallTaskStatus.Completed;
  154. }
  155. public async Task<bool> RunTask()
  156. {
  157. Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing registry key '{KeyName}'...");
  158. var roots = GetRoots();
  159. foreach (var _root in roots)
  160. {
  161. var root = _root;
  162. var subKey = GetSubKey();
  163. try
  164. {
  165. if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase))
  166. {
  167. var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  168. root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true);
  169. subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase);
  170. if (root == null)
  171. {
  172. ErrorLogger.WriteToErrorLog($"User classes hive not found for hive {_root.Name}.",
  173. Environment.StackTrace, "RegistryKeyAction Error");
  174. continue;
  175. }
  176. }
  177. if (Operation == RegistryKeyOperation.Add && root.OpenSubKey(subKey) == null)
  178. {
  179. root.CreateSubKey(subKey);
  180. }
  181. if (Operation == RegistryKeyOperation.Delete)
  182. {
  183. try
  184. {
  185. root.DeleteSubKeyTree(subKey, false);
  186. }
  187. catch (Exception e)
  188. {
  189. ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message,
  190. e.StackTrace, "RegistryKeyAction Warning", root?.Name + "\\" + subKey);
  191. var rootHive = root.Name.Split('\\').First() switch
  192. {
  193. "HKEY_CURRENT_USER" => RegistryHive.CurrentUser,
  194. "HKEY_LOCAL_MACHINE" => RegistryHive.LocalMachine,
  195. "HKEY_CLASSES_ROOT" => RegistryHive.ClassesRoot,
  196. "HKEY_USERS" => RegistryHive.Users,
  197. _ => throw new ArgumentException($"Unable to parse: " + root.Name.Split('\\').First())
  198. };
  199. DeleteKeyTreeWin32(root.Name.StartsWith("HKEY_USERS") ? root.Name.Split('\\')[1] + "\\" + subKey: subKey, rootHive);
  200. }
  201. }
  202. }
  203. catch (Exception e)
  204. {
  205. ErrorLogger.WriteToErrorLog(e.Message,
  206. e.StackTrace, "RegistryKeyAction Error", root?.Name + "\\" + subKey);
  207. }
  208. }
  209. return true;
  210. }
  211. }
  212. }