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.

513 lines
20 KiB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32;
using TrustedUninstaller.Shared;
using TrustedUninstaller.Shared.Actions;
using TrustedUninstaller.Shared.Tasks;
namespace TrustedUninstaller.CLI
{
public class CLI
{
private static async System.Threading.Tasks.Task<int> Main(string[] args)
{
//Needed after defender removal's reboot, the "current directory" will be set to System32
//After the auto start up.
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
if (args.Length == 1 && args[0] == "-DisableDefender")
{
return DisableDefender();
}
DualOut.Init();
if (!WinUtil.IsAdministrator())
{
System.Console.Error.WriteLine("This program must be launched as an Administrator!");
return -1;
}
#if !DEBUG
/*
if (!WinUtil.IsGenuineWindows())
{
System.Console.Error.WriteLine("This program only works on genuine Windows copies!");
return -1;
}
*/
#endif
if (args.Length < 1 || !Directory.Exists(args[0]))
{
Console.WriteLine("No Playbook selected.");
return -1;
}
AmeliorationUtil.Playbook = await AmeliorationUtil.DeserializePlaybook(args[0]);
if (!Directory.Exists($"{AmeliorationUtil.Playbook.Path}\\Configuration") || Directory.GetFiles($"{AmeliorationUtil.Playbook.Path}\\Configuration").Length == 0)
{
Console.WriteLine("Configuration folder is empty, put YAML files in it and restart the application.");
Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
return -1;
}
ExtractResourceFolder("resources", Directory.GetCurrentDirectory());
if (!WinUtil.IsTrustedInstaller())
{
Console.WriteLine("Checking requirements...\r\n");
if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.Internet) && !await (new Requirements.Internet()).IsMet())
{
Console.WriteLine("Internet must be connected to run this Playbook.");
}
if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.DefenderDisabled) && Process.GetProcessesByName("MsMpEng").Any())
{
bool first = true;
while ((await GetDefenderToggles()).Any(x => x))
{
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...");
Console.ReadKey();
}
bool remnantsOnly = Requirements.DefenderDisabled.RemnantsOnly();
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...");
Console.ReadKey();
try
{
Console.WriteLine("\r\nPreparing system...");
PrepareSystemCLI();
Console.WriteLine("Preparation Complete");
if (!remnantsOnly)
{
Console.WriteLine("\r\nRestarting system...");
CmdAction reboot = new CmdAction()
{
Command = "timeout /t 1 & shutdown /r /t 0",
Wait = false
};
AmeliorationUtil.SafeRunAction(reboot).Wait();
Environment.Exit(0);
}
} catch (Exception e)
{
Console.WriteLine("Error preparing system: " + e.Message);
Environment.Exit(-1);
}
}
if (AmeliorationUtil.Playbook.Requirements.Contains(Requirements.Requirement.Internet) && !await (new Requirements.Internet()).IsMet())
{
Console.WriteLine("Internet must be connected to run this Playbook.");
}
}
if (!File.Exists($"{AmeliorationUtil.Playbook.Path}\\options.txt"))
{
List<string> defaultOptions = new List<string>();
foreach (var page in AmeliorationUtil.Playbook.FeaturePages)
{
if (page.DependsOn != null && !defaultOptions.Contains(page.DependsOn))
continue;
if (page.GetType() == typeof(Playbook.CheckboxPage))
{
foreach (var option in ((Playbook.CheckboxPage)page).Options.Where(x => ((Playbook.CheckboxPage.CheckboxOption)x).IsChecked))
{
defaultOptions.Add(option.Name);
}
}
if (page.GetType() == typeof(Playbook.RadioPage))
defaultOptions.Add(((Playbook.RadioPage)page).DefaultOption);
if (page.GetType() == typeof(Playbook.RadioImagePage))
defaultOptions.Add(((Playbook.RadioImagePage)page).DefaultOption);
}
AmeliorationUtil.Playbook.Options = defaultOptions;
}
if (!AmeliorationUtil.Playbook.UseKernelDriver.HasValue)
{
if (new RegistryValueAction()
{
KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity",
Value = "Enabled",
Data = 1,
}.GetStatus()
!= UninstallTaskStatus.Completed
&&
new RegistryValueAction()
{
KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config",
Value = "VulnerableDriverBlocklistEnable",
Data = 0,
}.GetStatus()
== UninstallTaskStatus.Completed && (await GetDefenderToggles()).All(toggleOn => !toggleOn))
{
AmeliorationUtil.UseKernelDriver = true;
}
}
else
AmeliorationUtil.UseKernelDriver = AmeliorationUtil.Playbook.UseKernelDriver.Value;
try
{
if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ame-assassin")))
{
Console.WriteLine(":AME-STATUS: Extracting resources");
ExtractResourceFolder("resources", Directory.GetCurrentDirectory());
ExtractArchive(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CLI-Resources.7z"), AppDomain.CurrentDomain.BaseDirectory);
if (AmeliorationUtil.UseKernelDriver) ExtractArchive(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProcessInformer.7z"), AppDomain.CurrentDomain.BaseDirectory);
try
{
File.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CLI-Resources.7z"));
File.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProcessInformer.7z"));
} catch (Exception e)
{
}
}
} catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.Message,
e.StackTrace, "Error extracting resources.");
Console.WriteLine($":AME-Fatal Error: Error extracting resources.");
return -1;
}
await AmeliorationUtil.StartAmelioration();
return 0;
}
public static void ExtractArchive(string file, string targetDir)
{
RunCommand($"x \"{file}\" -o\"{targetDir}\" -p\"wizard\" -y -aos");
}
private static void RunCommand(string command)
{
var proc = new Process();
var startInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Normal,
Arguments = command,
FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7za.exe"),
RedirectStandardError = true,
};
proc.StartInfo = startInfo;
proc.Start();
StringBuilder errorOutput = new StringBuilder("");
proc.ErrorDataReceived += (sender, args) => { errorOutput.Append("\r\n" + args.Data); };
proc.BeginErrorReadLine();
proc.WaitForExit();
proc.CancelErrorRead();
if (proc.ExitCode == 1)
ErrorLogger.WriteToErrorLog(errorOutput.ToString(), Environment.StackTrace, "Warning while running 7zip.", command);
if (proc.ExitCode > 1)
throw new ArgumentOutOfRangeException("Error running 7zip: " + errorOutput.ToString());
}
public static void ExtractResourceFolder(string resource, string dir, bool overwrite = false)
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
Assembly assembly = Assembly.GetExecutingAssembly();
var resources = assembly.GetManifestResourceNames().Where(res => res.StartsWith($"TrustedUninstaller.CLI.Properties"));
foreach (var obj in resources)
{
using (UnmanagedMemoryStream stream = (UnmanagedMemoryStream)assembly.GetManifestResourceStream(obj))
{
int MB = 1024 * 1024;
int offset = -MB;
var file = dir + $"\\{obj.Substring($"TrustedUninstaller.CLI.Properties.{resource}.".Length).Replace("---", "\\")}";
if (file.EndsWith(".gitkeep")) continue;
var fileDir = Path.GetDirectoryName(file);
if (fileDir != null && !Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
if (File.Exists(file) && !overwrite) continue;
if (File.Exists(file) && overwrite)
{
try
{
File.Delete(file);
} catch (Exception e)
{
if (!Directory.Exists(Directory.GetCurrentDirectory() + "\\Logs"))
Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Logs");
using (var writer = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "Logs\\ErrorLog.txt"), true))
{
writer.WriteLine($"Title: Could not delete existing resource file {file}.\r\nMessage: {e.Message}\r\n\r\nStackTrace: {e.StackTrace}");
writer.WriteLine("\r\nDate/Time: " + DateTime.Now);
writer.WriteLine("============================================");
}
continue;
}
}
using (FileStream fsDlst = new FileStream(file, FileMode.CreateNew, FileAccess.Write))
{
while (offset + MB < stream.Length)
{
var buffer = new byte[MB];
offset += MB;
if (offset + MB > stream.Length)
{
var bytesLeft = stream.Length - offset;
buffer = new byte[bytesLeft];
}
stream.Seek(offset, SeekOrigin.Begin);
stream.Read(buffer, 0, buffer.Length);
fsDlst.Seek(offset, SeekOrigin.Begin);
fsDlst.Write(buffer, 0, buffer.Length);
}
}
}
}
}
public static async Task<List<bool>> GetDefenderToggles()
{
var result = new List<bool>();
await Task.Run(() =>
{
var defenderKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows Defender");
var policiesKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Policies\Microsoft\Windows Defender");
RegistryKey realtimePolicy = null;
RegistryKey realtimeKey = null;
try
{
try
{
realtimePolicy = policiesKey.OpenSubKey("Real-Time Protection");
} catch (Exception e)
{
}
if (realtimePolicy != null)
realtimeKey = realtimePolicy;
else
realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
} catch
{
result.Add(false);
}
if (realtimeKey != null)
{
try
{
result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
} catch (Exception exception)
{
try
{
realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
} catch (Exception e)
{
result.Add(true);
}
}
}
try
{
RegistryKey spynetPolicy = null;
RegistryKey spynetKey = null;
try
{
spynetPolicy = policiesKey.OpenSubKey("SpyNet");
} catch (Exception e)
{
}
if (spynetPolicy != null)
spynetKey = spynetPolicy;
else
spynetKey = defenderKey.OpenSubKey("SpyNet");
int reporting = 0;
int consent = 0;
try
{
reporting = (int)spynetKey.GetValue("SpyNetReporting");
} catch (Exception e)
{
if (spynetPolicy != null)
{
reporting = (int)defenderKey.OpenSubKey("SpyNet").GetValue("SpyNetReporting");
}
}
try
{
consent = (int)spynetKey.GetValue("SubmitSamplesConsent");
} catch (Exception e)
{
if (spynetPolicy != null)
{
consent = (int)defenderKey.OpenSubKey("SpyNet").GetValue("SubmitSamplesConsent");
}
}
result.Add(reporting != 0);
result.Add(consent != 0 && consent != 2 && consent != 4);
} catch
{
result.Add(false);
result.Add(false);
}
try
{
int tamper = (int)defenderKey.OpenSubKey("Features").GetValue("TamperProtection");
result.Add(tamper != 4 && tamper != 0);
} catch
{
result.Add(false);
}
});
return result;
}
private static int DisableDefender()
{
try
{
Defender.Disable();
}
catch (Exception ex)
{
ErrorLogger.WriteToErrorLog(ex.GetType() + ": " + ex.Message, ex.StackTrace,
$"First Defender disable failed from second process.");
Defender.Kill();
try
{
Defender.Disable();
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace,
$"Could not disable Windows Defender from second process.");
return 1;
}
}
return 0;
}
public static void PrepareSystemCLI()
{
try
{
if (!Defender.Kill())
{
ErrorLogger.WriteToErrorLog("Unknown reason", null, "Could not kill Defender");
try
{
var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
var exitCode = Defender.WaitForProcessExit(process, 10000);
if (exitCode != 0)
throw new Exception("Exit code was nonzero.");
} catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "First Defender disable failed");
Thread.Sleep(1000);
if (!Defender.Kill())
{
Thread.Sleep(3000);
Defender.Kill();
}
try
{
var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
var exitCode = Defender.WaitForProcessExit(process, 10000);
if (exitCode != 0)
throw new Exception("Exit code was nonzero.");
} catch (Exception exception)
{
ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "Second Defender disable failed");
Defender.Disable();
}
}
}
else
{
try
{
var process = Defender.StartElevatedProcess(Assembly.GetExecutingAssembly().Location, $@"-DisableDefender");
var exitCode = Defender.WaitForProcessExit(process, 15000);
if (exitCode != 0)
throw new Exception("Exit code was nonzero.");
} catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.GetType() + ": " + e.Message, e.StackTrace, "First Defender disable failed");
Defender.Disable();
}
}
} catch
{
}
}
public static async Task UninstallDriver()
{
CmdAction cmdAction = new CmdAction();
try
{
Console.WriteLine("Removing driver...");
cmdAction.Command = Environment.Is64BitOperatingSystem
? $"ProcessHacker\\x64\\ProcessHacker.exe -s -uninstallkph"
: $"ProcessHacker\\x86\\ProcessHacker.exe -s -uninstallkph";
await cmdAction.RunTask();
} catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an Error while uninstalling the driver.");
throw;
}
}
}
}