4 Commits
0.7 ... master

Author SHA1 Message Date
  Styris 63739db83a Update to v0.7.4 11 months ago
  Styris ddfdcccc04 Update to v0.7.3 11 months ago
  Styris 4e275d776c Update to v0.7.2 1 year ago
  Styris 7147425b5d Update to v0.7.1 1 year ago
35 changed files with 9032 additions and 598 deletions
Split View
  1. +160
    -133
      TrustedUninstaller.CLI/CLI.cs
  2. BIN
      TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z
  3. BIN
      TrustedUninstaller.CLI/Properties/resources/ProcessInformer.7z
  4. +16
    -27
      TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj
  5. +13
    -4
      TrustedUninstaller.Shared/Actions/AppxAction.cs
  6. +160
    -14
      TrustedUninstaller.Shared/Actions/CmdAction.cs
  7. +92
    -46
      TrustedUninstaller.Shared/Actions/FileAction.cs
  8. +3
    -1
      TrustedUninstaller.Shared/Actions/LanguageAction.cs
  9. +1
    -0
      TrustedUninstaller.Shared/Actions/LineInFileAction.cs
  10. +179
    -14
      TrustedUninstaller.Shared/Actions/PowershellAction.cs
  11. +54
    -2
      TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs
  12. +1
    -0
      TrustedUninstaller.Shared/Actions/RegistryValueAction.cs
  13. +217
    -37
      TrustedUninstaller.Shared/Actions/RunAction.cs
  14. +1
    -0
      TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs
  15. +230
    -79
      TrustedUninstaller.Shared/Actions/ServiceAction.cs
  16. +1
    -0
      TrustedUninstaller.Shared/Actions/ShortcutAction.cs
  17. +4
    -1
      TrustedUninstaller.Shared/Actions/SystemPackageAction.cs
  18. +276
    -120
      TrustedUninstaller.Shared/Actions/TaskKillAction.cs
  19. +1
    -0
      TrustedUninstaller.Shared/Actions/UpdateAction.cs
  20. +1
    -0
      TrustedUninstaller.Shared/Actions/UserAction.cs
  21. +1
    -0
      TrustedUninstaller.Shared/Actions/WriteStatusAction.cs
  22. +139
    -71
      TrustedUninstaller.Shared/AmeliorationUtil.cs
  23. +3340
    -0
      TrustedUninstaller.Shared/AugmentedProcess.cs
  24. +22
    -0
      TrustedUninstaller.Shared/Debug.cs
  25. +1638
    -0
      TrustedUninstaller.Shared/Defender.cs
  26. +21
    -6
      TrustedUninstaller.Shared/Globals.cs
  27. +2
    -1
      TrustedUninstaller.Shared/Playbook.cs
  28. +506
    -0
      TrustedUninstaller.Shared/ProcessPrivilege.cs
  29. +129
    -10
      TrustedUninstaller.Shared/Requirements.cs
  30. +1
    -0
      TrustedUninstaller.Shared/Tasks/ITaskAction.cs
  31. +8
    -0
      TrustedUninstaller.Shared/Tasks/TaskAction.cs
  32. +9
    -0
      TrustedUninstaller.Shared/Tasks/UninstallTask.cs
  33. +31
    -13
      TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj
  34. +1721
    -0
      TrustedUninstaller.Shared/Win32.cs
  35. +54
    -19
      TrustedUninstaller.Shared/WinUtil.cs

+ 160
- 133
TrustedUninstaller.CLI/CLI.cs View File

@ -5,6 +5,7 @@ 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;
@ -22,6 +23,11 @@ namespace TrustedUninstaller.CLI
//After the auto start up.
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
if (args.Length == 1 && args[0] == "-DisableDefender")
{
return DisableDefender();
}
DualOut.Init();
if (!WinUtil.IsAdministrator())
@ -44,19 +50,19 @@ namespace TrustedUninstaller.CLI
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");
@ -64,7 +70,7 @@ namespace TrustedUninstaller.CLI
{
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;
@ -74,9 +80,9 @@ namespace TrustedUninstaller.CLI
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
@ -103,8 +109,8 @@ namespace TrustedUninstaller.CLI
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.");
@ -136,6 +142,30 @@ namespace TrustedUninstaller.CLI
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")))
@ -144,31 +174,34 @@ namespace TrustedUninstaller.CLI
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) { }
}
}
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();
@ -181,15 +214,15 @@ namespace TrustedUninstaller.CLI
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();
@ -199,7 +232,8 @@ namespace TrustedUninstaller.CLI
if (proc.ExitCode > 1)
throw new ArgumentOutOfRangeException("Error running 7zip: " + errorOutput.ToString());
}
public static void ExtractResourceFolder(string resource, string dir, bool overwrite = false)
public static void ExtractResourceFolder(string resource, string dir, bool overwrite = false)
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
@ -216,7 +250,7 @@ namespace TrustedUninstaller.CLI
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);
@ -226,8 +260,7 @@ namespace TrustedUninstaller.CLI
try
{
File.Delete(file);
}
catch (Exception e)
} catch (Exception e)
{
if (!Directory.Exists(Directory.GetCurrentDirectory() + "\\Logs"))
Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Logs");
@ -237,6 +270,7 @@ namespace TrustedUninstaller.CLI
writer.WriteLine("\r\nDate/Time: " + DateTime.Now);
writer.WriteLine("============================================");
}
continue;
}
}
@ -264,7 +298,7 @@ namespace TrustedUninstaller.CLI
}
}
}
public static async Task<List<bool>> GetDefenderToggles()
{
var result = new List<bool>();
@ -281,32 +315,31 @@ namespace TrustedUninstaller.CLI
try
{
realtimePolicy = policiesKey.OpenSubKey("Real-Time Protection");
} catch (Exception e)
{
}
catch (Exception e) { }
if (realtimePolicy != null)
realtimeKey = realtimePolicy;
else
realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
}
catch
} catch
{
result.Add(false);
}
if (realtimeKey != null)
{
try
{
result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
}
catch (Exception exception)
} catch (Exception exception)
{
try
{
realtimeKey = defenderKey.OpenSubKey("Real-Time Protection");
result.Add((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1);
}
catch (Exception e)
} catch (Exception e)
{
result.Add(true);
}
@ -317,12 +350,13 @@ namespace TrustedUninstaller.CLI
{
RegistryKey spynetPolicy = null;
RegistryKey spynetKey = null;
try
{
spynetPolicy = policiesKey.OpenSubKey("SpyNet");
} catch (Exception e)
{
}
catch (Exception e) { }
if (spynetPolicy != null)
spynetKey = spynetPolicy;
@ -334,19 +368,18 @@ namespace TrustedUninstaller.CLI
try
{
reporting = (int)spynetKey.GetValue("SpyNetReporting");
}
catch (Exception e)
} catch (Exception e)
{
if (spynetPolicy != null)
{
reporting = (int)defenderKey.OpenSubKey("SpyNet").GetValue("SpyNetReporting");
}
}
try
{
consent = (int)spynetKey.GetValue("SubmitSamplesConsent");
}
catch (Exception e)
} catch (Exception e)
{
if (spynetPolicy != null)
{
@ -356,131 +389,125 @@ namespace TrustedUninstaller.CLI
result.Add(reporting != 0);
result.Add(consent != 0 && consent != 2 && consent != 4);
}
catch
} catch
{
result.Add(false);
result.Add(false);
}
try
{
int tamper = (int)defenderKey.OpenSubKey("Features").GetValue("TamperProtection");
result.Add(tamper != 4 && tamper != 0);
}
catch
} catch
{
result.Add(false);
}
});
return result;
}
public static void PrepareSystemCLI()
private static int DisableDefender()
{
try
{
var defenderStop = new RunAction()
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait cmd /c \"" +
"sc sdset \"WinDefend\" \"D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWRPLOCRRC;;;BA)(A;;CCLCSWRPLOCRRC;;;BU)(A;;CCLCSWRPLOCRRC;;;IU)(A;;CCLCSWRPLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)\"&" +
"sc config WinDefend start=disabled&" +
"net stop WinDefend\"",
CreateWindow = false,
Timeout = 7500,
};
defenderStop.RunTask().Wait();
} catch (Exception e)
{
Defender.Disable();
}
var defenderValues = new RunAction()
catch (Exception ex)
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait cmd /c \"reg delete \"HKLM\\SOFTWARE\\Microsoft\\Windows Defender\" /v \"ProductAppDataPath\" /f &" +
" reg delete \"HKLM\\SOFTWARE\\Microsoft\\Windows Defender\" /v \"InstallLocation\" /f\"",
CreateWindow = false
};
defenderValues.RunTask().Wait();
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.");
var defenderKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows Defender");
if (defenderKey != null && defenderKey.GetValueNames().Contains("InstallLocation"))
{
throw new Exception("Could not remove defender install values.");
return 1;
}
}
var defenderService = new RunAction()
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg delete \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend\" /f",
CreateWindow = false
};
defenderService.RunTask().Wait();
if (Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\WinDefend") != null)
return 0;
}
public static void PrepareSystemCLI()
{
try
{
var disableDefender = new RunAction()
if (!Defender.Kill())
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg add \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend\" /v \"Start\" /t REG_DWORD /d 4 /f",
CreateWindow = false
};
disableDefender.RunTask().Wait();
if (new RegistryValueAction() {KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\WinDefend", Value = "Start", Data = 4, Type = RegistryValueType.REG_DWORD}.GetStatus() != UninstallTaskStatus.Completed)
throw new Exception("Could not disable WinDefend service.");
}
ErrorLogger.WriteToErrorLog("Unknown reason", null, "Could not kill Defender");
// MpOAV.dll normally in use by a lot of processes. This prevents that.
var MpOAVCLSID = new RunAction()
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = @"-U:T -P:E -M:S -Priority:RealTime -ShowWindowMode:Hide -Wait reg delete ""HKCR\CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32"" /f",
CreateWindow = false
};
MpOAVCLSID.RunTask().Wait();
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");
if (Registry.ClassesRoot.OpenSubKey(@"CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32") != null)
{
throw new Exception("Could not remove MpOAV mapping.");
}
Thread.Sleep(1000);
if (!Defender.Kill())
{
Thread.Sleep(3000);
Defender.Kill();
}
// Can cause ProcessHacker driver warnings without this
AmeliorationUtil.SafeRunAction(new RegistryValueAction()
{
KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity",
Value = "Enabled",
Data = 0,
}).Wait();
AmeliorationUtil.SafeRunAction(new RegistryValueAction()
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
{
KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config",
Value = "VulnerableDriverBlocklistEnable",
Data = 0,
}).Wait();
}
}
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;
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;
}
}
}
}
}
}

BIN
TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z View File


BIN
TrustedUninstaller.CLI/Properties/resources/ProcessInformer.7z View File


+ 16
- 27
TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj View File

@ -70,6 +70,13 @@
<DebugType>embedded</DebugType>
<DefineConstants>SINGLE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Single File|x64' ">
<OutputPath>bin\x64\Release\</OutputPath>
<PlatformTarget>x64</PlatformTarget>
<Optimize>true</Optimize>
<DebugType>embedded</DebugType>
<DefineConstants>SINGLE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>B8F0A67800B779C5CEF49BEAB6E5247E373F9452</ManifestCertificateThumbprint>
</PropertyGroup>
@ -91,21 +98,19 @@
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' Or '$(Configuration)|$(Platform)' == 'Debug Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")
cmd /c "echo Compiled&gt; "$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\Compiled.txt""</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PreBuildEvent>PowerShell -NoP -C "Get-Process 'ame-builder' -EA SilentlyContinue | Wait-Process -EA SilentlyContinue; EXIT 0"
PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'CLI' -WindowStyle Hidden"
</PreBuildEvent>
PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'CLI','""""x64\$(Configuration)""""' -NoNewWindow -Wait"
</PostBuildEvent>
<AllowedReferenceRelatedFileExtensions>
<!-- Prevent default XML files copied to output.-->
.allowedextension
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' != 'Single File|x64' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' != 'Single File|x64' And '$(Configuration)|$(Platform)' != 'Debug Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")</PostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
@ -113,16 +118,6 @@
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="System.Management" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CLI.cs" />
@ -150,18 +145,12 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.0" />
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SQLitePCLRaw.core" Version="2.0.7" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.CodeDom" Version="6.0.0" />
<PackageReference Include="System.IO.Compression" Version="4.0.0" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
@ -171,7 +160,7 @@
<EmbeddedResource Include="Properties\resources\CLI-Resources.7z" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\resources\NSudoLC.exe" />
<EmbeddedResource Include="Properties\resources\ProcessInformer.7z" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

+ 13
- 4
TrustedUninstaller.Shared/Actions/AppxAction.cs View File

@ -16,6 +16,8 @@ namespace TrustedUninstaller.Shared.Actions
// Integrate ame-assassin later
internal class AppxAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread() { throw new NotImplementedException(); }
public enum AppxOperation
{
Remove = 0,
@ -39,6 +41,9 @@ namespace TrustedUninstaller.Shared.Actions
[YamlMember(typeof(bool), Alias = "verboseOutput")]
public bool Verbose { get; set; } = false;
[YamlMember(typeof(bool), Alias = "unregister")]
public bool Unregister { get; set; } = false;
[YamlMember(typeof(string), Alias = "weight")]
public int ProgressWeight { get; set; } = 30;
public int GetProgressWeight() => ProgressWeight;
@ -69,14 +74,18 @@ namespace TrustedUninstaller.Shared.Actions
InProgress = true;
Console.WriteLine($"Removing APPX {Type.ToString().ToLower()} '{Name}'...");
WinUtil.CheckKph();
string verboseArg = Verbose ? " -Verbose" : "";
string unregisterArg = Unregister ? " -Verbose" : "";
string kernelDriverArg = AmeliorationUtil.UseKernelDriver ? " -UseKernelDriver" : "";
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
CreateNoWindow = true,
Arguments = $@"-{Type.ToString()} ""{Name}""" + verboseArg,
Arguments = $@"-{Type.ToString()} ""{Name}""" + verboseArg + unregisterArg + kernelDriverArg,
FileName = Directory.GetCurrentDirectory() + "\\ame-assassin\\ame-assassin.exe",
RedirectStandardOutput = true,
RedirectStandardError = true
@ -98,7 +107,7 @@ namespace TrustedUninstaller.Shared.Actions
bool exited = proc.WaitForExit(30000);
// WaitForExit alone seems to not be entirely reliable
while (!exited && ExeRunning(proc))
while (!exited && ExeRunning("ame-assassin", proc.Id))
{
exited = proc.WaitForExit(30000);
}
@ -121,11 +130,11 @@ namespace TrustedUninstaller.Shared.Actions
if (!write.Equals("Complete!")) Console.WriteLine(write);
}
private static bool ExeRunning(Process process)
private static bool ExeRunning(string name, int id)
{
try
{
return Process.GetProcessesByName(process.ProcessName).Any(x => x.Id == process.Id);
return Process.GetProcessesByName(name).Any(x => x.Id == id);
}
catch (Exception)
{


+ 160
- 14
TrustedUninstaller.Shared/Actions/CmdAction.cs View File

@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TrustedUninstaller.Shared.Exceptions;
using TrustedUninstaller.Shared.Tasks;
@ -11,6 +13,28 @@ namespace TrustedUninstaller.Shared.Actions
{
public class CmdAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread()
{
if (InProgress) throw new TaskInProgressException("Another Cmd action was called while one was in progress.");
InProgress = true;
var privilegeText = RunAs == Privilege.CurrentUser ? " as the current user" : RunAs == Privilege.CurrentUserElevated ? " as the current user elevated" : RunAs == Privilege.System ?
" as the system account" : "";
Console.WriteLine($"Running cmd command '{Command}'{privilegeText}...");
ExitCode = null;
if (RunAs == Privilege.TrustedInstaller)
RunAsProcess();
else
RunAsPrivilegedProcess();
InProgress = false;
}
[YamlMember(typeof(Privilege), Alias = "runas")]
public Privilege RunAs { get; set; } = Privilege.TrustedInstaller;
[YamlMember(typeof(string), Alias = "command")]
public string Command { get; set; }
@ -48,21 +72,20 @@ namespace TrustedUninstaller.Shared.Actions
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed;
}
public async Task<bool> RunTask()
public Task<bool> RunTask()
{
if (InProgress) throw new TaskInProgressException("Another Cmd action was called while one was in progress.");
InProgress = true;
Console.WriteLine($"Running cmd command '{Command}'...");
ExitCode = null;
return null;
}
private void RunAsProcess()
{
var process = new Process();
var startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Normal,
FileName = "cmd.exe",
Arguments = "/C " + $"\"{Environment.ExpandEnvironmentVariables(this.Command)}\"",
Arguments = "/C " + $"\"{this.Command}\"",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
@ -83,7 +106,7 @@ namespace TrustedUninstaller.Shared.Actions
if (!Wait)
{
process.Dispose();
return true;
return;
}
var error = new StringBuilder();
@ -97,6 +120,114 @@ namespace TrustedUninstaller.Shared.Actions
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (Timeout != null)
{
var exited = process.WaitForExit(Timeout.Value);
if (!exited)
{
process.Kill();
throw new TimeoutException($"Command '{Command}' timeout exceeded.");
}
}
else
{
bool exited = process.WaitForExit(30000);
// WaitForExit alone seems to not be entirely reliable
while (!exited && CmdRunning(process.Id))
{
exited = process.WaitForExit(30000);
}
}
int exitCode = 0;
try
{
exitCode = process.ExitCode;
}
catch (Exception ex)
{
ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "CmdAction Error", Command);
}
if (exitCode != 0 && !Command.Contains("ProcessHacker\\x64\\ProcessHacker.exe"))
{
StandardError = error.ToString();
Console.WriteLine($"cmd instance exited with error code: {exitCode}");
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error message: {StandardError}");
ErrorLogger.WriteToErrorLog("Cmd exited with a non-zero exit code: " + exitCode, null, "CmdAction Error", Command);
this.ExitCode = exitCode;
}
else
{
ExitCode = 0;
}
process.CancelOutputRead();
process.CancelErrorRead();
process.Dispose();
}
private static bool CmdRunning(int id)
{
try
{
return Process.GetProcessesByName("cmd").Any(x => x.Id == id);
}
catch (Exception)
{
return false;
}
}
private void RunAsPrivilegedProcess()
{
var process = new AugmentedProcess.Process();
var startInfo = new AugmentedProcess.ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Normal,
FileName = "cmd.exe",
Arguments = "/C " + $"{this.Command}",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true
};
if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
if (!Wait)
{
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = true;
}
process.StartInfo = startInfo;
ProcessPrivilege.StartPrivilegedTask(process, RunAs);
if (!Wait)
{
process.Dispose();
return;
}
var error = new StringBuilder();
process.OutputDataReceived += PrivilegedProcOutputHandler;
process.ErrorDataReceived += delegate(object sender, AugmentedProcess.DataReceivedEventArgs args)
{
if (!String.IsNullOrEmpty(args.Data))
error.AppendLine(args.Data);
else
error.AppendLine();
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (Timeout != null)
{
@ -127,14 +258,29 @@ namespace TrustedUninstaller.Shared.Actions
}
process.CancelOutputRead();
process.CancelErrorRead();
process.Dispose();
InProgress = false;
return true;
}
private void PrivilegedProcOutputHandler(object sendingProcess, AugmentedProcess.DataReceivedEventArgs outLine)
{
var outputString = outLine.Data;
private static void ProcOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
if (outputString.Contains("\\AME"))
{
outputString = outputString.Substring(outputString.IndexOf('>') + 1);
}
Console.WriteLine(outputString);
}
else
{
Console.WriteLine();
}
}
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
var outputString = outLine.Data;


+ 92
- 46
TrustedUninstaller.Shared/Actions/FileAction.cs View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -9,6 +10,7 @@ using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using TrustedUninstaller.Shared.Exceptions;
@ -19,6 +21,7 @@ namespace TrustedUninstaller.Shared.Actions
{
public class FileAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; }
@ -81,13 +84,14 @@ namespace TrustedUninstaller.Shared.Actions
{
if (!TrustedInstaller)
{
try {await Task.Run(() => File.Delete(file));} catch {}
try { File.Delete(file);} catch (Exception e) { }
if (File.Exists(file))
{
try
{
EzUnlockFileW(file);
var result = EzUnlockFileW(file);
Testing.WriteLine($"ExUnlock on ({file}) result: " + result);
}
catch (Exception e)
{
@ -95,20 +99,21 @@ namespace TrustedUninstaller.Shared.Actions
$"FileAction Error", file);
}
try {await Task.Run(() => File.Delete(file));} catch {}
try {await Task.Run(() => File.Delete(file));} catch (Exception e) {Testing.WriteLine(e, "DeleteFile > File.Delete(File)");}
CmdAction delAction = new CmdAction()
{
Command = $"del /q /f \"{file}\""
};
await delAction.RunTask();
delAction.RunTaskOnMainThread();
}
}
else if (File.Exists("NSudoLC.exe"))
{
try
{
EzUnlockFileW(file);
var result = EzUnlockFileW(file);
Testing.WriteLine($"ExUnlock on ({file}) result: " + result);
}
catch (Exception e)
{
@ -123,7 +128,7 @@ namespace TrustedUninstaller.Shared.Actions
CreateWindow = false
};
await tiDelAction.RunTask();
tiDelAction.RunTaskOnMainThread();
if (tiDelAction.Output != null)
{
if (log) ErrorLogger.WriteToErrorLog(tiDelAction.Output, Environment.StackTrace,
@ -149,7 +154,7 @@ namespace TrustedUninstaller.Shared.Actions
{
Command = $"rmdir /Q /S \"{dir}\""
};
await deleteDirCmd.RunTask();
deleteDirCmd.RunTaskOnMainThread();
if (deleteDirCmd.StandardError != null)
{
@ -171,7 +176,7 @@ namespace TrustedUninstaller.Shared.Actions
CreateWindow = false
};
await tiDelAction.RunTask();
tiDelAction.RunTaskOnMainThread();
if (tiDelAction.Output != null)
{
@ -216,15 +221,28 @@ namespace TrustedUninstaller.Shared.Actions
var cmdAction = new CmdAction();
Console.WriteLine($"Removing driver service {driverService}...");
// TODO: Replace with win32
try
{
ServiceInstaller ServiceInstallerObj = new ServiceInstaller();
ServiceInstallerObj.Context = new InstallContext();
ServiceInstallerObj.ServiceName = driverService;
ServiceInstallerObj.Uninstall(null);
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog("Service uninstall failed: " + e.Message, e.StackTrace, "FileAction Warning", RawPath);
}
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop";
await cmdAction.RunTask();
if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete";
await cmdAction.RunTask();
if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
catch (Exception servException)
{
@ -270,16 +288,19 @@ namespace TrustedUninstaller.Shared.Actions
{
try
{
using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{svchost.Id}'");
foreach (ManagementObject queryObj in search.Get())
foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(svchost.Id))
{
var serviceName = (string)queryObj["Name"]; // Access service name
var serv = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
if (serv == null) svcCount++;
else svcCount += serv.DependentServices.Length + 1;
svcCount++;
try
{
var serviceController = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
if (serviceController != null)
svcCount += serviceController.DependentServices.Length;
}
catch (Exception e)
{
Console.WriteLine($"\r\nError: Could not get amount of dependent services for {serviceName}.\r\nException: " + e.Message);
}
}
} catch (Exception e)
{
@ -328,7 +349,15 @@ namespace TrustedUninstaller.Shared.Actions
continue;
}
await taskKillAction.RunTask();
try
{
await taskKillAction.RunTask();
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
$"FileAction Error: Could not kill process {taskKillAction.ProcessName}.");
}
}
// This gives any obstinant processes some time to unlock the file on their own.
@ -352,14 +381,13 @@ namespace TrustedUninstaller.Shared.Actions
if (delay >= 800)
ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{file}'. Process termination loop exceeded max cycles (8).",
Environment.StackTrace, "FileAction Error");
await DeleteFile(file, true);
using (var writer = new StreamWriter("Logs\\FileChecklist.txt", true))
if (Path.GetExtension(file).Equals(".exe", StringComparison.OrdinalIgnoreCase))
{
writer.WriteLine($"File Path: {file}\r\nDeleted: {!File.Exists(file)}\r\n" +
$"======================");
await new TaskKillAction() { ProcessName = Path.GetFileNameWithoutExtension(file) }.RunTask();
}
await DeleteFile(file, true);
}
}
//Loop through any subdirectories
@ -419,7 +447,7 @@ namespace TrustedUninstaller.Shared.Actions
};
try
{
await permAction.RunTask();
permAction.RunTaskOnMainThread();
}
catch (Exception e)
{
@ -466,7 +494,7 @@ namespace TrustedUninstaller.Shared.Actions
}
}
}
else if (isFile)
if (isFile)
{
try
{
@ -486,7 +514,7 @@ namespace TrustedUninstaller.Shared.Actions
};
try
{
await permAction.RunTask();
permAction.RunTaskOnMainThread();
}
catch (Exception e)
{
@ -504,15 +532,30 @@ namespace TrustedUninstaller.Shared.Actions
var cmdAction = new CmdAction();
Console.WriteLine($"Removing driver service {driverService}...");
// TODO: Replace with win32
try
{
ServiceInstaller ServiceInstallerObj = new ServiceInstaller();
ServiceInstallerObj.Context = new InstallContext();
ServiceInstallerObj.ServiceName = driverService;
ServiceInstallerObj.Uninstall(null);
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog("Service uninstall failed: " + e.Message, e.StackTrace, "FileAction Warning", RawPath);
}
WinUtil.CheckKph();
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop";
await cmdAction.RunTask();
if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete";
await cmdAction.RunTask();
if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
catch (Exception servException)
{
@ -558,16 +601,19 @@ namespace TrustedUninstaller.Shared.Actions
{
try
{
using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{svchost.Id}'");
foreach (ManagementObject queryObj in search.Get())
foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(svchost.Id))
{
var serviceName = (string)queryObj["Name"]; // Access service name
var serv = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
if (serv == null) svcCount++;
else svcCount += serv.DependentServices.Length + 1;
svcCount++;
try
{
var serviceController = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
if (serviceController != null)
svcCount += serviceController.DependentServices.Length;
}
catch (Exception e)
{
Console.WriteLine($"\r\nError: Could not get amount of dependent services for {serviceName}.\r\nException: " + e.Message);
}
}
} catch (Exception e)
{
@ -645,6 +691,11 @@ namespace TrustedUninstaller.Shared.Actions
ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{realPath}'. Process termination loop exceeded max cycles (8).",
Environment.StackTrace, "FileAction Error");
if (Path.GetExtension(realPath).Equals(".exe", StringComparison.OrdinalIgnoreCase))
{
await new TaskKillAction() { ProcessName = Path.GetFileNameWithoutExtension(realPath) }.RunTask();
}
await DeleteFile(realPath, true);
}
}
@ -653,11 +704,6 @@ namespace TrustedUninstaller.Shared.Actions
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
$"FileAction Error: Error while trying to delete {realPath}.");
}
using (var writer = new StreamWriter("Logs\\FileChecklist.txt", true))
{
writer.WriteLine($"File Path: {realPath}\r\nDeleted: {!File.Exists(realPath)}\r\n" +
$"======================");
}
}
else
{


+ 3
- 1
TrustedUninstaller.Shared/Actions/LanguageAction.cs View File

@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -8,6 +9,7 @@ namespace TrustedUninstaller.Shared.Actions
{
class LanguageAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread() { throw new NotImplementedException(); }
public int ProgressWeight { get; set; } = 1;
public int GetProgressWeight() => ProgressWeight;


+ 1
- 0
TrustedUninstaller.Shared/Actions/LineInFileAction.cs View File

@ -16,6 +16,7 @@ namespace TrustedUninstaller.Shared.Actions
}
internal class LineInFileAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(Alias = "path")]
public string RawPath { get; set; }


+ 179
- 14
TrustedUninstaller.Shared/Actions/PowershellAction.cs View File

@ -14,6 +14,29 @@ namespace TrustedUninstaller.Shared.Actions
{
public class PowerShellAction : TaskAction, ITaskAction
{
public void RunTaskOnMainThread()
{
if (InProgress) throw new TaskInProgressException("Another Powershell action was called while one was in progress.");
InProgress = true;
var privilegeText = RunAs == Privilege.CurrentUser ? " as the current user" : RunAs == Privilege.CurrentUserElevated ? " as the current user elevated" : RunAs == Privilege.System ?
" as the system account" : "";
Console.WriteLine($"Running PowerShel command '{Command}'{privilegeText}...");
WinUtil.CheckKph();
if (RunAs == Privilege.TrustedInstaller)
RunAsProcess();
else
RunAsPrivilegedProcess();
InProgress = false;
return;
}
[YamlMember(typeof(Privilege), Alias = "runas")]
public Privilege RunAs { get; set; } = Privilege.TrustedInstaller;
[YamlMember(typeof(string), Alias = "command")]
public string Command { get; set; }
@ -50,13 +73,13 @@ namespace TrustedUninstaller.Shared.Actions
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed;
}
public async Task<bool> RunTask()
public Task<bool> RunTask()
{
if (InProgress) throw new TaskInProgressException("Another Powershell action was called while one was in progress.");
InProgress = true;
Console.WriteLine($"Running PowerShell command '{Command}'...");
return null;
}
private void RunAsProcess()
{
var process = new Process();
var startInfo = new ProcessStartInfo
{
@ -83,7 +106,7 @@ namespace TrustedUninstaller.Shared.Actions
if (!Wait)
{
process.Dispose();
return true;
return;
}
var error = new StringBuilder();
@ -98,6 +121,114 @@ namespace TrustedUninstaller.Shared.Actions
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (Timeout != null)
{
var exited = process.WaitForExit(Timeout.Value);
if (!exited)
{
process.Kill();
throw new TimeoutException($"Command '{Command}' timeout exceeded.");
}
}
else
{
bool exited = process.WaitForExit(30000);
// WaitForExit alone seems to not be entirely reliable
while (!exited && PowerShellRunning(process.Id))
{
exited = process.WaitForExit(30000);
}
}
StandardError = error.ToString();
int exitCode = 0;
try
{
exitCode = process.ExitCode;
}
catch (Exception ex)
{
ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "PowerShellAction Error", Command);
}
if (exitCode != 0)
{
Console.WriteLine($"PowerShell instance exited with error code: {exitCode}");
if (!String.IsNullOrWhiteSpace(StandardError)) Console.WriteLine($"Error message: {StandardError}");
ErrorLogger.WriteToErrorLog("PowerShell exited with a non-zero exit code: " + exitCode, null, "PowerShellAction Error", Command);
this.ExitCode = exitCode;
}
else
{
if (!String.IsNullOrWhiteSpace(StandardError)) Console.WriteLine($"Error output: {StandardError}");
ExitCode = 0;
}
process.CancelOutputRead();
process.CancelErrorRead();
process.Dispose();
}
private static bool PowerShellRunning(int id)
{
try
{
return Process.GetProcessesByName("powershell").Any(x => x.Id == id);
}
catch (Exception)
{
return false;
}
}
private void RunAsPrivilegedProcess()
{
var process = new AugmentedProcess.Process();
var startInfo = new AugmentedProcess.ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Normal,
FileName = "PowerShell.exe",
Arguments = $@"-NoP -ExecutionPolicy Bypass -NonInteractive -C ""{Command}""",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true
};
if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
if (!Wait)
{
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = true;
}
process.StartInfo = startInfo;
ProcessPrivilege.StartPrivilegedTask(process, RunAs);
if (!Wait)
{
process.Dispose();
return;
}
var error = new StringBuilder();
process.OutputDataReceived += PrivilegedProcOutputHandler;
process.ErrorDataReceived += delegate(object sender, AugmentedProcess.DataReceivedEventArgs args)
{
if (!String.IsNullOrEmpty(args.Data))
error.AppendLine(args.Data);
else
error.AppendLine();
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (Timeout != null)
{
var exited = process.WaitForExit(Timeout.Value);
@ -114,7 +245,7 @@ namespace TrustedUninstaller.Shared.Actions
if (process.ExitCode != 0)
{
Console.WriteLine($"PowerShell instance exited with error code: {process.ExitCode}");
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error message: {StandardError}");
if (!String.IsNullOrWhiteSpace(StandardError)) Console.WriteLine($"Error message: {StandardError}");
ErrorLogger.WriteToErrorLog("PowerShell exited with a non-zero exit code: " + process.ExitCode, null, "PowerShellAction Error", Command);
@ -122,23 +253,57 @@ namespace TrustedUninstaller.Shared.Actions
}
else
{
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error output: {StandardError}");
if (!String.IsNullOrWhiteSpace(StandardError)) Console.WriteLine($"Error output: {StandardError}");
ExitCode = 0;
}
process.CancelOutputRead();
process.CancelErrorRead();
process.Dispose();
InProgress = false;
return true;
}
private static bool ExeRunning(string exe, int id)
{
try
{
return Process.GetProcessesByName(Path.GetFileNameWithoutExtension(exe)).Any(x => x.Id == id);
}
catch (Exception)
{
return false;
}
}
private static void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
private void PrivilegedProcOutputHandler(object sendingProcess, AugmentedProcess.DataReceivedEventArgs outLine)
{
var outputString = outLine.Data;
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
Console.WriteLine(outLine.Data);
if (outputString.Contains("\\AME"))
{
outputString = outputString.Substring(outputString.IndexOf('>') + 1);
}
Console.WriteLine(outputString);
}
else
{
Console.WriteLine();
}
}
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
var outputString = outLine.Data;
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
if (outputString.Contains("\\AME"))
{
outputString = outputString.Substring(outputString.IndexOf('>') + 1);
}
Console.WriteLine(outputString);
}
else
{


+ 54
- 2
TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs View File