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.

512 lines
20 KiB

1 year ago
1 year ago
9 months ago
1 year ago
1 year ago
10 months ago
1 year ago
9 months ago
1 year ago
10 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
6 months ago
6 months ago
6 months ago
9 months ago
9 months ago
9 months ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
1 year ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. using Microsoft.Win32;
  12. using TrustedUninstaller.Shared;
  13. using TrustedUninstaller.Shared.Actions;
  14. using TrustedUninstaller.Shared.Tasks;
  15. namespace TrustedUninstaller.CLI
  16. {
  17. public class CLI
  18. {
  19. private static async System.Threading.Tasks.Task<int> Main(string[] args)
  20. {
  21. //Needed after defender removal's reboot, the "current directory" will be set to System32
  22. //After the auto start up.
  23. Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
  24. if (args.Length == 1 && args[0] == "-DisableDefender")
  25. {
  26. return DisableDefender();
  27. }
  28. DualOut.Init();
  29. if (!WinUtil.IsAdministrator())
  30. {
  31. System.Console.Error.WriteLine("This program must be launched as an Administrator!");
  32. return -1;
  33. }
  34. #if !DEBUG
  35. /*
  36. if (!WinUtil.IsGenuineWindows())
  37. {
  38. System.Console.Error.WriteLine("This program only works on genuine Windows copies!");
  39. return -1;
  40. }
  41. */
  42. #endif
  43. if (args.Length < 1 || !Directory.Exists(args[0]))
  44. {
  45. Console.WriteLine("No Playbook selected.");
  46. return -1;
  47. }
  48. AmeliorationUtil.Playbook = await AmeliorationUtil.DeserializePlaybook(args[0]);
  49. if (!Directory.Exists($"{AmeliorationUtil.Playbook.Path}\\Configuration") || Directory.GetFiles($"{AmeliorationUtil.Playbook.Path}\\Configuration").Length == 0)
  50. {
  51. Console.WriteLine("Configuration folder is empty, put YAML files in it and restart the application.");
  52. Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
  53. return -1;
  54. }
  55. ExtractResourceFolder("resources", Directory.GetCurrentDirectory());
  56. if (!WinUtil.IsTrustedInstaller())
  57. {
  58. Console.WriteLine("Checking requirements...\r\n");
  59. if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.Internet) && !await (new Requirements.Internet()).IsMet())
  60. {
  61. Console.WriteLine("Internet must be connected to run this Playbook.");
  62. }
  63. if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.DefenderDisabled) && Process.GetProcessesByName("MsMpEng").Any())
  64. {
  65. bool first = true;
  66. while ((await GetDefenderToggles()).Any(x => x))
  67. {
  68. Console.WriteLine("All 4 windows security toggles must be set to off.\r\nNavigate to Windows Security > Virus & threat detection > manage settings.\r\nPress any key to continue...");
  69. Console.ReadKey();
  70. }
  71. bool remnantsOnly = Requirements.DefenderDisabled.RemnantsOnly();
  72. Console.WriteLine(remnantsOnly ? "The system must be prepared before continuing.\r\nPress any key to continue..." : "The system must be prepared before continuing. Your system will restart after preparation\r\nPress any key to continue...");
  73. Console.ReadKey();
  74. try
  75. {
  76. Console.WriteLine("\r\nPreparing system...");
  77. PrepareSystemCLI();
  78. Console.WriteLine("Preparation Complete");
  79. if (!remnantsOnly)
  80. {
  81. Console.WriteLine("\r\nRestarting system...");
  82. CmdAction reboot = new CmdAction()
  83. {
  84. Command = "timeout /t 1 & shutdown /r /t 0",
  85. Wait = false
  86. };
  87. AmeliorationUtil.SafeRunAction(reboot).Wait();
  88. Environment.Exit(0);
  89. }
  90. } catch (Exception e)
  91. {
  92. Console.WriteLine("Error preparing system: " + e.Message);
  93. Environment.Exit(-1);
  94. }
  95. }
  96. if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.Internet) && !await (new Requirements.Internet()).IsMet())
  97. {
  98. Console.WriteLine("Internet must be connected to run this Playbook.");
  99. }
  100. }
  101. if (!File.Exists($"{AmeliorationUtil.Playbook.Path}\\options.txt"))
  102. {
  103. List<string> defaultOptions = new List<string>();
  104. foreach (var page in AmeliorationUtil.Playbook.FeaturePages)
  105. {
  106. if (page.DependsOn != null && !defaultOptions.Contains(page.DependsOn))
  107. continue;
  108. if (page.GetType() == typeof(Playbook.CheckboxPage))
  109. {
  110. foreach (var option in ((Playbook.CheckboxPage)page).Options.Where(x => ((Playbook.CheckboxPage.CheckboxOption)x).IsChecked))
  111. {
  112. defaultOptions.Add(option.Name);
  113. }
  114. }
  115. if (page.GetType() == typeof(Playbook.RadioPage))
  116. defaultOptions.Add(((Playbook.RadioPage)page).DefaultOption);
  117. if (page.GetType() == typeof(Playbook.RadioImagePage))
  118. defaultOptions.Add(((Playbook.RadioImagePage)page).DefaultOption);
  119. }
  120. AmeliorationUtil.Playbook.Options = defaultOptions;
  121. }
  122. if (!AmeliorationUtil.Playbook.UseKernelDriver.HasValue)
  123. {
  124. if (new RegistryValueAction()
  125. {
  126. KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity",
  127. Value = "Enabled",
  128. Data = 1,
  129. }.GetStatus()
  130. != UninstallTaskStatus.Completed
  131. &&
  132. new RegistryValueAction()
  133. {
  134. KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config",
  135. Value = "VulnerableDriverBlocklistEnable",
  136. Data = 0,
  137. }.GetStatus()
  138. == UninstallTaskStatus.Completed && (await GetDefenderToggles()).All(toggleOn => !toggleOn))
  139. {
  140. AmeliorationUtil.UseKernelDriver = true;
  141. }
  142. }
  143. else
  144. AmeliorationUtil.UseKernelDriver = AmeliorationUtil.Playbook.UseKernelDriver.Value;
  145. try
  146. {
  147. if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ame-assassin")))
  148. {
  149. Console.WriteLine(":AME-STATUS: Extracting resources");
  150. ExtractResourceFolder("resources", Directory.GetCurrentDirectory());
  151. ExtractArchive(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CLI-Resources.7z"), AppDomain.CurrentDomain.BaseDirectory);
  152. if (AmeliorationUtil.UseKernelDriver) ExtractArchive(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProcessInformer.7z"), AppDomain.CurrentDomain.BaseDirectory);
  153. try
  154. {
  155. File.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CLI-Resources.7z"));
  156. File.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProcessInformer.7z"));
  157. } catch (Exception e)
  158. {
  159. }
  160. }
  161. } catch (Exception e)
  162. {
  163. ErrorLogger.WriteToErrorLog(e.Message,
  164. e.StackTrace, "Error extracting resources.");
  165. Console.WriteLine($":AME-Fatal Error: Error extracting resources.");
  166. return -1;
  167. }
  168. await AmeliorationUtil.StartAmelioration();
  169. return 0;
  170. }
  171. public static void ExtractArchive(string file, string targetDir)
  172. {
  173. RunCommand($"x \"{file}\" -o\"{targetDir}\" -p\"wizard\" -y -aos");
  174. }
  175. private static void RunCommand(string command)
  176. {
  177. var proc = new Process();
  178. var startInfo = new ProcessStartInfo
  179. {
  180. CreateNoWindow = true,
  181. UseShellExecute = false,
  182. WindowStyle = ProcessWindowStyle.Normal,
  183. Arguments = command,
  184. FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7za.exe"),
  185. RedirectStandardError = true,
  186. };
  187. proc.StartInfo = startInfo;
  188. proc.Start();
  189. StringBuilder errorOutput = new StringBuilder("");
  190. proc.ErrorDataReceived += (sender, args) => { errorOutput.Append("\r\n" + args.Data); };
  191. proc.BeginErrorReadLine();
  192. proc.WaitForExit();
  193. proc.CancelErrorRead();
  194. if (proc.ExitCode == 1)
  195. ErrorLogger.WriteToErrorLog(errorOutput.ToString(), Environment.StackTrace, "Warning while running 7zip.", command);
  196. if (proc.ExitCode > 1)
  197. throw new ArgumentOutOfRangeException("Error running 7zip: " + errorOutput.ToString());
  198. }
  199. public static void ExtractResourceFolder(string resource, string dir, bool overwrite = false)
  200. {
  201. if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
  202. Assembly assembly = Assembly.GetExecutingAssembly();
  203. var resources = assembly.GetManifestResourceNames().Where(res => res.StartsWith($"TrustedUninstaller.CLI.Properties"));
  204. foreach (var obj in resources)
  205. {
  206. using (UnmanagedMemoryStream stream = (UnmanagedMemoryStream)assembly.GetManifestResourceStream(obj))
  207. {
  208. int MB = 1024 * 1024;
  209. int offset = -MB;
  210. var file = dir + $"\\{obj.Substring($"TrustedUninstaller.CLI.Properties.{resource}.".Length).Replace("---", "\\")}";
  211. if (file.EndsWith(".gitkeep")) continue;
  212. var fileDir = Path.GetDirectoryName(file);
  213. if (fileDir != null && !Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
  214. if (File.Exists(file) && !overwrite) continue;
  215. if (File.Exists(file) && overwrite)
  216. {
  217. try
  218. {
  219. File.Delete(file);
  220. } catch (Exception e)
  221. {
  222. if (!Directory.Exists(Directory.GetCurrentDirectory() + "\\Logs"))
  223. Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Logs");
  224. using (var writer = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "Logs\\ErrorLog.txt"), true))
  225. {
  226. writer.WriteLine($"Title: Could not delete existing resource file {file}.\r\nMessage: {e.Message}\r\n\r\nStackTrace: {e.StackTrace}");
  227. writer.WriteLine("\r\nDate/Time: " + DateTime.Now);
  228. writer.WriteLine("============================================");
  229. }
  230. continue;
  231. }
  232. }
  233. using (FileStream fsDlst = new FileStream(file, FileMode.CreateNew, FileAccess.Write))
  234. {
  235. while (offset + MB < stream.Length)
  236. {
  237. var buffer = new byte[MB];
  238. offset += MB;
  239. if (offset + MB > stream.Length)
  240. {
  241. var bytesLeft = stream.Length - offset;
  242. buffer = new byte[bytesLeft];
  243. }
  244. stream.Seek(offset, SeekOrigin.Begin);
  245. stream.Read(buffer, 0, buffer.Length);
  246. fsDlst.Seek(offset, SeekOrigin.Begin);
  247. fsDlst.Write(buffer, 0, buffer.Length);
  248. }
  249. }
  250. }
  251. }
  252. }
  253. public static async Task<List<bool>> GetDefenderToggles()
  254. {
  255. var result = new List<bool>();
  256. await Task.Run(() =>
  257. {
  258. var defenderKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows Defender");
  259. var policiesKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Policies\Microsoft\Windows Defender");
  260. RegistryKey realtimePolicy = null;
  261. RegistryKey realtimeKey = null;
  262. try
  263. {
  264. try
  265. {
  266. realtimePolicy = policiesKey.OpenSubKey("Real-Time Protection");
  267. } catch (Exception e)
  268. {
  269. }
  270. if (realtimePolicy != null)
  271. realtimeKey = realtimePolicy;
  272. else
  273. realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
  274. } catch
  275. {
  276. result.Add(false);
  277. }
  278. if (realtimeKey != null)
  279. {
  280. try
  281. {
  282. result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
  283. } catch (Exception exception)
  284. {
  285. try
  286. {
  287. realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
  288. result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
  289. } catch (Exception e)
  290. {
  291. result.Add(true);
  292. }
  293. }
  294. }
  295. try
  296. {
  297. RegistryKey spynetPolicy = null;
  298. RegistryKey spynetKey = null;
  299. try
  300. {
  301. spynetPolicy = policiesKey.OpenSubKey("SpyNet");
  302. } catch (Exception e)
  303. {
  304. }
  305. if (spynetPolicy != null)
  306. spynetKey = spynetPolicy;
  307. else
  308. spynetKey = defenderKey.OpenSubKey("SpyNet");
  309. int reporting = 0;
  310. int consent = 0;
  311. try
  312. {
  313. reporting = (int)spynetKey.GetValue("SpyNetReporting");
  314. } catch (Exception e)
  315. {
  316. if (spynetPolicy != null)
  317. {
  318. reporting = (int)defenderKey.OpenSubKey("SpyNet").GetValue("SpyNetReporting");
  319. }
  320. }
  321. try
  322. {
  323. consent = (int)spynetKey.GetValue("SubmitSamplesConsent");
  324. } catch (Exception e)
  325. {
  326. if (spynetPolicy != null)
  327. {
  328. consent = (int)defenderKey.OpenSubKey("SpyNet").GetValue("SubmitSamplesConsent");
  329. }
  330. }
  331. result.Add(reporting != 0);
  332. result.Add(consent != 0 && consent != 2 && consent != 4);
  333. } catch
  334. {
  335. result.Add(false);
  336. result.Add(false);
  337. }
  338. try
  339. {
  340. int tamper = (int)defenderKey.OpenSubKey("Features").GetValue("TamperProtection");
  341. result.Add(tamper != 4 && tamper != 0);
  342. } catch
  343. {
  344. result.Add(false);
  345. }
  346. });
  347. return result;
  348. }
  349. private static int DisableDefender()
  350. {
  351. try
  352. {
  353. Defender.Disable();
  354. }
  355. catch (Exception ex)
  356. {
  357. ErrorLogger.WriteToErrorLog(ex.GetType() + ": " + ex.Message, ex.StackTrace,
  358. $"First Defender disable failed from second process.");
  359. Defender.Kill();
  360. try
  361. {
  362. Defender.Disable();
  363. }
  364. catch (Exception e)
  365. {
  366. ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace,
  367. $"Could not disable Windows Defender from second process.");
  368. return 1;
  369. }
  370. }
  371. return 0;
  372. }
  373. public static void PrepareSystemCLI()
  374. {
  375. try
  376. {
  377. if (!Defender.Kill())
  378. {
  379. ErrorLogger.WriteToErrorLog("Unknown reason", null, "Could not kill Defender");
  380. try
  381. {
  382. var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
  383. var exitCode = Defender.WaitForProcessExit(process, 10000);
  384. if (exitCode != 0)
  385. throw new Exception("Exit code was nonzero.");
  386. } catch (Exception e)
  387. {
  388. ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "First Defender disable failed");
  389. Thread.Sleep(1000);
  390. if (!Defender.Kill())
  391. {
  392. Thread.Sleep(3000);
  393. Defender.Kill();
  394. }
  395. try
  396. {
  397. var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
  398. var exitCode = Defender.WaitForProcessExit(process, 10000);
  399. if (exitCode != 0)
  400. throw new Exception("Exit code was nonzero.");
  401. } catch (Exception exception)
  402. {
  403. ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "Second Defender disable failed");
  404. Defender.Disable();
  405. }
  406. }
  407. }
  408. else
  409. {
  410. try
  411. {
  412. var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
  413. var exitCode = Defender.WaitForProcessExit(process, 15000);
  414. if (exitCode != 0)
  415. throw new Exception("Exit code was nonzero.");
  416. } catch (Exception e)
  417. {
  418. ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "First Defender disable failed");
  419. Defender.Disable();
  420. }
  421. }
  422. } catch
  423. {
  424. }
  425. }
  426. public static async Task UninstallDriver()
  427. {
  428. CmdAction cmdAction = new CmdAction();
  429. try
  430. {
  431. Console.WriteLine("Removing driver...");
  432. cmdAction.Command = Environment.Is64BitOperatingSystem
  433. ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -uninstallkph"
  434. : $"ProcessHacker\\x86\\ProcessHacker.exe -s -uninstallkph";
  435. await cmdAction.RunTask();
  436. } catch (Exception e)
  437. {
  438. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an Error while uninstalling the driver.");
  439. throw;
  440. }
  441. }
  442. }
  443. }