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.

199 lines
8.3 KiB

1 year ago
1 year 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.Text.RegularExpressions;
  8. using System.Threading.Tasks;
  9. using Microsoft.Win32;
  10. using TrustedUninstaller.Shared.Exceptions;
  11. using TrustedUninstaller.Shared.Tasks;
  12. using YamlDotNet.Serialization;
  13. namespace TrustedUninstaller.Shared.Actions
  14. {
  15. public enum RegistryKeyOperation
  16. {
  17. Delete = 0,
  18. Add = 1
  19. }
  20. public class RegistryKeyAction : ITaskAction
  21. {
  22. [YamlMember(typeof(string), Alias = "path")]
  23. public string KeyName { get; set; }
  24. [YamlMember(typeof(Scope), Alias = "scope")]
  25. public Scope Scope { get; set; } = Scope.AllUsers;
  26. [YamlMember(typeof(RegistryKeyOperation), Alias = "operation")]
  27. public RegistryKeyOperation Operation { get; set; } = RegistryKeyOperation.Delete;
  28. [YamlMember(typeof(string), Alias = "weight")]
  29. public int ProgressWeight { get; set; } = 1;
  30. public int GetProgressWeight() => ProgressWeight;
  31. private bool InProgress { get; set; }
  32. public void ResetProgress() => InProgress = false;
  33. public string ErrorString() => $"RegistryKeyAction failed to {Operation.ToString().ToLower()} key '{KeyName}'.";
  34. private List<RegistryKey> GetRoots()
  35. {
  36. var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper();
  37. var list = new List<RegistryKey>();
  38. if (hive.Equals("HKCU") || hive.Equals("HKEY_CURRENT_USER"))
  39. {
  40. RegistryKey usersKey;
  41. List<string> userKeys;
  42. switch (Scope)
  43. {
  44. case Scope.AllUsers:
  45. WinUtil.RegistryManager.HookUserHives();
  46. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  47. userKeys = usersKey.GetSubKeyNames().
  48. Where(x => x.StartsWith("S-") &&
  49. usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList();
  50. userKeys.AddRange(usersKey.GetSubKeyNames().Where(x => x.StartsWith("AME_UserHive_") && !x.EndsWith("_Classes")).ToList());
  51. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  52. return list;
  53. case Scope.ActiveUsers:
  54. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  55. userKeys = usersKey.GetSubKeyNames().
  56. Where(x => x.StartsWith("S-") &&
  57. usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList();
  58. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  59. return list;
  60. case Scope.DefaultUser:
  61. usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  62. userKeys = usersKey.GetSubKeyNames().Where(x => x.Equals("AME_UserHive_Default") && !x.EndsWith("_Classes")).ToList();
  63. userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true)));
  64. return list;
  65. }
  66. }
  67. list.Add(hive switch
  68. {
  69. "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  70. "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default),
  71. "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  72. "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default),
  73. "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  74. "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default),
  75. "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  76. "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default),
  77. _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.")
  78. });
  79. return list;
  80. }
  81. public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1);
  82. private RegistryKey? OpenSubKey(RegistryKey root)
  83. {
  84. var subKeyPath = GetSubKey();
  85. if (subKeyPath == null) throw new ArgumentException($"Key '{KeyName}' is invalid.");
  86. return root.OpenSubKey(subKeyPath, true);
  87. }
  88. public UninstallTaskStatus GetStatus()
  89. {
  90. try
  91. {
  92. var roots = GetRoots();
  93. foreach (var _root in roots)
  94. {
  95. var root = _root;
  96. var subKey = GetSubKey();
  97. if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase))
  98. {
  99. var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  100. root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true);
  101. subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase);
  102. if (root == null)
  103. {
  104. continue;
  105. }
  106. }
  107. var openedSubKey = root.OpenSubKey(subKey);
  108. if (Operation == RegistryKeyOperation.Delete && openedSubKey != null)
  109. {
  110. return UninstallTaskStatus.ToDo;
  111. }
  112. if (Operation == RegistryKeyOperation.Add && openedSubKey == null)
  113. {
  114. return UninstallTaskStatus.ToDo;
  115. }
  116. }
  117. }
  118. catch (Exception e)
  119. {
  120. ErrorLogger.WriteToErrorLog(e.Message,
  121. e.StackTrace, "RegistryKeyAction Error");
  122. return UninstallTaskStatus.ToDo;
  123. }
  124. return UninstallTaskStatus.Completed;
  125. }
  126. public async Task<bool> RunTask()
  127. {
  128. Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing registry key '{KeyName}'...");
  129. var roots = GetRoots();
  130. foreach (var _root in roots)
  131. {
  132. var root = _root;
  133. var subKey = GetSubKey();
  134. try
  135. {
  136. if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase))
  137. {
  138. var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
  139. root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true);
  140. subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase);
  141. if (root == null)
  142. {
  143. ErrorLogger.WriteToErrorLog($"User classes hive not found for hive {_root.Name}.",
  144. Environment.StackTrace, "RegistryKeyAction Error");
  145. continue;
  146. }
  147. }
  148. if (Operation == RegistryKeyOperation.Add && root.OpenSubKey(subKey) == null)
  149. {
  150. root.CreateSubKey(subKey);
  151. }
  152. if (Operation == RegistryKeyOperation.Delete)
  153. {
  154. root.DeleteSubKeyTree(subKey, false);
  155. }
  156. }
  157. catch (Exception e)
  158. {
  159. ErrorLogger.WriteToErrorLog(e.Message,
  160. e.StackTrace, "RegistryKeyAction Error", root?.Name + "\\" + subKey);
  161. }
  162. }
  163. return true;
  164. }
  165. }
  166. }