diff --git a/TrustedUninstaller.CLI/CLI.cs b/TrustedUninstaller.CLI/CLI.cs
index d2ecef6..96368ea 100644
--- a/TrustedUninstaller.CLI/CLI.cs
+++ b/TrustedUninstaller.CLI/CLI.cs
@@ -142,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")))
@@ -150,9 +174,11 @@ 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)
{
}
diff --git a/TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z b/TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z
index ad024ba..d82b2f3 100644
Binary files a/TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z and b/TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z differ
diff --git a/TrustedUninstaller.CLI/Properties/resources/ProcessInformer.7z b/TrustedUninstaller.CLI/Properties/resources/ProcessInformer.7z
new file mode 100644
index 0000000..e829bbd
Binary files /dev/null and b/TrustedUninstaller.CLI/Properties/resources/ProcessInformer.7z differ
diff --git a/TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj b/TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj
index 33ab5ac..41cd477 100644
--- a/TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj
+++ b/TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj
@@ -70,6 +70,13 @@
embedded
SINGLE
+
+ bin\x64\Release\
+ x64
+ true
+ embedded
+ SINGLE;DEBUG
+
B8F0A67800B779C5CEF49BEAB6E5247E373F9452
@@ -91,21 +98,19 @@
app.manifest
-
+
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" & cmd /c "exit /b 0")
- cmd /c "echo Compiled> "$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\Compiled.txt""
-
-
- 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"
-
+ PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'CLI','""""x64\$(Configuration)""""' -NoNewWindow -Wait"
+
+
+
+ .allowedextension
+
-
+
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" & cmd /c "exit /b 0")
-
-
@@ -113,16 +118,6 @@
False
False
-
-
-
-
-
-
-
-
-
-
@@ -150,18 +145,12 @@
-
-
-
-
-
-
@@ -171,7 +160,7 @@
-
+
\ No newline at end of file
diff --git a/TrustedUninstaller.Shared/Actions/AppxAction.cs b/TrustedUninstaller.Shared/Actions/AppxAction.cs
index 7951ca2..725ff9a 100644
--- a/TrustedUninstaller.Shared/Actions/AppxAction.cs
+++ b/TrustedUninstaller.Shared/Actions/AppxAction.cs
@@ -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)
{
diff --git a/TrustedUninstaller.Shared/Actions/CmdAction.cs b/TrustedUninstaller.Shared/Actions/CmdAction.cs
index 145d6b7..739190a 100644
--- a/TrustedUninstaller.Shared/Actions/CmdAction.cs
+++ b/TrustedUninstaller.Shared/Actions/CmdAction.cs
@@ -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,25 @@ 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;
+
+ Console.WriteLine($"Running cmd command '{Command}'...");
+
+ 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 +69,20 @@ namespace TrustedUninstaller.Shared.Actions
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed;
}
- public async Task RunTask()
+
+ public Task 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 +103,7 @@ namespace TrustedUninstaller.Shared.Actions
if (!Wait)
{
process.Dispose();
- return true;
+ return;
}
var error = new StringBuilder();
@@ -97,6 +117,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)
+ {
+ 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 " + $"\"{Environment.ExpandEnvironmentVariables(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 +255,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;
diff --git a/TrustedUninstaller.Shared/Actions/FileAction.cs b/TrustedUninstaller.Shared/Actions/FileAction.cs
index a5dd9be..706f0bd 100644
--- a/TrustedUninstaller.Shared/Actions/FileAction.cs
+++ b/TrustedUninstaller.Shared/Actions/FileAction.cs
@@ -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,6 +381,11 @@ 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");
+
+ if (Path.GetExtension(file).Equals(".exe", StringComparison.OrdinalIgnoreCase))
+ {
+ await new TaskKillAction() { ProcessName = Path.GetFileNameWithoutExtension(file) }.RunTask();
+ }
await DeleteFile(file, true);
@@ -419,7 +453,7 @@ namespace TrustedUninstaller.Shared.Actions
};
try
{
- await permAction.RunTask();
+ permAction.RunTaskOnMainThread();
}
catch (Exception e)
{
@@ -466,7 +500,7 @@ namespace TrustedUninstaller.Shared.Actions
}
}
}
- else if (isFile)
+ if (isFile)
{
try
{
@@ -486,7 +520,7 @@ namespace TrustedUninstaller.Shared.Actions
};
try
{
- await permAction.RunTask();
+ permAction.RunTaskOnMainThread();
}
catch (Exception e)
{
@@ -504,15 +538,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 +607,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 +697,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);
}
}
diff --git a/TrustedUninstaller.Shared/Actions/LanguageAction.cs b/TrustedUninstaller.Shared/Actions/LanguageAction.cs
index 034c77a..2d82594 100644
--- a/TrustedUninstaller.Shared/Actions/LanguageAction.cs
+++ b/TrustedUninstaller.Shared/Actions/LanguageAction.cs
@@ -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;
diff --git a/TrustedUninstaller.Shared/Actions/LineInFileAction.cs b/TrustedUninstaller.Shared/Actions/LineInFileAction.cs
index fd4c053..5eee5ba 100644
--- a/TrustedUninstaller.Shared/Actions/LineInFileAction.cs
+++ b/TrustedUninstaller.Shared/Actions/LineInFileAction.cs
@@ -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; }
diff --git a/TrustedUninstaller.Shared/Actions/PowershellAction.cs b/TrustedUninstaller.Shared/Actions/PowershellAction.cs
index 4f91867..3c47f0f 100644
--- a/TrustedUninstaller.Shared/Actions/PowershellAction.cs
+++ b/TrustedUninstaller.Shared/Actions/PowershellAction.cs
@@ -14,6 +14,26 @@ 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;
+
+ Console.WriteLine($"Running PowerShell command '{Command}'...");
+
+ 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 +70,13 @@ namespace TrustedUninstaller.Shared.Actions
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed;
}
- public async Task RunTask()
+ public Task 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 +103,7 @@ namespace TrustedUninstaller.Shared.Actions
if (!Wait)
{
process.Dispose();
- return true;
+ return;
}
var error = new StringBuilder();
@@ -98,6 +118,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.IsNullOrEmpty(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.IsNullOrEmpty(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);
@@ -129,16 +257,50 @@ namespace TrustedUninstaller.Shared.Actions
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
{
diff --git a/TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs b/TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs
index bfc943c..f4bb6a8 100644
--- a/TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs
+++ b/TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs
@@ -23,6 +23,7 @@ namespace TrustedUninstaller.Shared.Actions
}
public class RegistryKeyAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "path")]
public string KeyName { get; set; }
diff --git a/TrustedUninstaller.Shared/Actions/RegistryValueAction.cs b/TrustedUninstaller.Shared/Actions/RegistryValueAction.cs
index 3aa3ed5..9adbd15 100644
--- a/TrustedUninstaller.Shared/Actions/RegistryValueAction.cs
+++ b/TrustedUninstaller.Shared/Actions/RegistryValueAction.cs
@@ -38,6 +38,7 @@ namespace TrustedUninstaller.Shared.Actions
public class RegistryValueAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "path")]
public string KeyName { get; set; }
diff --git a/TrustedUninstaller.Shared/Actions/RunAction.cs b/TrustedUninstaller.Shared/Actions/RunAction.cs
index 8b9e630..6744e55 100644
--- a/TrustedUninstaller.Shared/Actions/RunAction.cs
+++ b/TrustedUninstaller.Shared/Actions/RunAction.cs
@@ -2,14 +2,59 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Documents;
using TrustedUninstaller.Shared.Tasks;
using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
+ public enum Privilege
+ {
+ TrustedInstaller,
+ System,
+ CurrentUserElevated,
+ CurrentUser,
+ }
+
public class RunAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread()
+ {
+ if (RawPath != null) RawPath = Environment.ExpandEnvironmentVariables(RawPath);
+ InProgress = true;
+
+ if (Arguments == null) Console.WriteLine($"Running '{Exe}'...");
+ else Console.WriteLine($"Running '{Exe}' with arguments '{Arguments}'...");
+
+ WinUtil.CheckKph();
+
+ var currentDir = Directory.GetCurrentDirectory();
+
+ if (ExeDir) RawPath = AmeliorationUtil.Playbook.Path + "\\Executables";
+ if (BaseDir) RawPath = currentDir;
+
+ string file = null;
+ if (RawPath != null && File.Exists(Path.Combine(Environment.ExpandEnvironmentVariables(RawPath), Exe)))
+ file = Path.Combine(Environment.ExpandEnvironmentVariables(RawPath), Exe);
+ else if (ExistsInPath(Exe) || File.Exists(Environment.ExpandEnvironmentVariables(Exe)))
+ file = Environment.ExpandEnvironmentVariables(Exe);
+
+ if (file == null)
+ throw new FileNotFoundException($"Executable not found.");
+
+ if (RunAs == Privilege.TrustedInstaller)
+ RunAsProcess(file);
+ else
+ RunAsPrivilegedProcess(file);
+
+ InProgress = false;
+ return;
+ }
+ [YamlMember(typeof(Privilege), Alias = "runas")]
+ public Privilege RunAs { get; set; } = Privilege.TrustedInstaller;
+
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; } = null;
@@ -80,28 +125,13 @@ namespace TrustedUninstaller.Shared.Actions
return HasExited || !Wait ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo;
}
- public async Task RunTask()
+ public Task RunTask()
{
- if (RawPath != null) RawPath = Environment.ExpandEnvironmentVariables(RawPath);
- InProgress = true;
-
- if (Arguments == null) Console.WriteLine($"Running '{Exe}'...");
- else Console.WriteLine($"Running '{Exe}' with arguments '{Arguments}'...");
-
- var currentDir = Directory.GetCurrentDirectory();
-
- if (ExeDir) RawPath = AmeliorationUtil.Playbook.Path + "\\Executables";
- if (BaseDir) RawPath = currentDir;
-
- string file = null;
- if (RawPath != null && File.Exists(Path.Combine(RawPath, Exe)))
- file = Path.Combine(RawPath, Exe);
- else if (ExistsInPath(Exe) || File.Exists(Environment.ExpandEnvironmentVariables(Exe)))
- file = Exe;
-
- if (file == null)
- throw new FileNotFoundException($"Executable not found.");
+ return null;
+ }
+ private void RunAsProcess(string file)
+ {
var startInfo = new ProcessStartInfo
{
CreateNoWindow = !this.CreateWindow,
@@ -138,7 +168,7 @@ namespace TrustedUninstaller.Shared.Actions
if (!Wait)
{
exeProcess.Dispose();
- return true;
+ return;
}
if (ShowOutput)
@@ -165,15 +195,24 @@ namespace TrustedUninstaller.Shared.Actions
bool exited = exeProcess.WaitForExit(30000);
// WaitForExit alone seems to not be entirely reliable
- while (!exited && ExeRunning(exeProcess))
+ while (!exited && ExeRunning(exeProcess.ProcessName, exeProcess.Id))
{
exited = exeProcess.WaitForExit(30000);
}
}
- if (exeProcess.ExitCode != 0)
+ int exitCode = 0;
+ try
+ {
+ exitCode = exeProcess.ExitCode;
+ }
+ catch (Exception ex)
{
- ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments);
+ ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "RunAction Error", Exe + " " + Arguments);
+ }
+ if (exitCode != 0)
+ {
+ ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exitCode, null, "RunAction Error", Exe + " " + Arguments);
}
HasExited = true;
@@ -182,15 +221,116 @@ namespace TrustedUninstaller.Shared.Actions
exeProcess.CancelOutputRead();
if (ShowError)
exeProcess.CancelErrorRead();
+ }
+ private void RunAsPrivilegedProcess(string file)
+ {
+ var startInfo = new AugmentedProcess.ProcessStartInfo
+ {
+ CreateNoWindow = !this.CreateWindow,
+ UseShellExecute = false,
+ WindowStyle = ProcessWindowStyle.Normal,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ FileName = file,
+ };
+ if (Arguments != null) startInfo.Arguments = Arguments;
- InProgress = false;
- return true;
+ if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
+ if (!Wait)
+ {
+ startInfo.RedirectStandardError = false;
+ startInfo.RedirectStandardOutput = false;
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ startInfo.UseShellExecute = true;
+ }
+
+ if (!ShowOutput)
+ startInfo.RedirectStandardOutput = false;
+ if (!ShowError)
+ startInfo.RedirectStandardError = false;
+
+ var exeProcess = new AugmentedProcess.Process
+ {
+ StartInfo = startInfo,
+ EnableRaisingEvents = true
+ };
+
+ ProcessPrivilege.StartPrivilegedTask(exeProcess, RunAs);
+
+ if (!Wait)
+ {
+ exeProcess.Dispose();
+ return;
+ }
+
+ if (ShowOutput)
+ exeProcess.OutputDataReceived += PrivilegedProcOutputHandler;
+ if (ShowError)
+ exeProcess.ErrorDataReceived += PrivilegedProcOutputHandler;
+
+ if (ShowOutput)
+ exeProcess.BeginOutputReadLine();
+ if (ShowError)
+ exeProcess.BeginErrorReadLine();
+
+ if (Timeout.HasValue)
+ {
+ var exited = exeProcess.WaitForExit(Timeout.Value);
+ if (!exited)
+ {
+ exeProcess.Kill();
+ throw new TimeoutException($"Executable run timeout exceeded.");
+ }
+ }
+ else
+ {
+ bool exited = exeProcess.WaitForExit(30000);
+
+ // WaitForExit alone seems to not be entirely reliable
+ while (!exited && ExeRunning(exeProcess.ProcessName, exeProcess.Id))
+ {
+ exited = exeProcess.WaitForExit(30000);
+ }
+ }
+
+ try
+ {
+ if (exeProcess.ExitCode != 0)
+ {
+ ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "RunAction Error", Exe + " " + Arguments);
+
+ Thread.Sleep(500);
+ try
+ {
+ if (exeProcess.ExitCode != 0)
+ {
+ ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments);
+ }
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Error fetching process exit code. (2)", null, "RunAction Error", Exe + " " + Arguments);
+ }
+ }
+
+ HasExited = true;
+
+ if (ShowOutput)
+ exeProcess.CancelOutputRead();
+ if (ShowError)
+ exeProcess.CancelErrorRead();
}
- 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)
{
@@ -198,6 +338,25 @@ namespace TrustedUninstaller.Shared.Actions
}
}
+ private void PrivilegedProcOutputHandler(object sendingProcess, AugmentedProcess.DataReceivedEventArgs outLine)
+ {
+ // Collect the sort command output.
+ if (!String.IsNullOrEmpty(outLine.Data))
+ {
+ var outputString = outLine.Data;
+
+ if (outputString.Contains("\\AME"))
+ {
+ outputString = outputString.Substring(outputString.IndexOf('>') + 1);
+ }
+ Console.WriteLine(outputString);
+ Output += outputString + Environment.NewLine;
+ }
+ else
+ {
+ Console.WriteLine();
+ }
+ }
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
// Collect the sort command output.
diff --git a/TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs b/TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs
index 0847619..db74817 100644
--- a/TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs
+++ b/TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs
@@ -21,6 +21,7 @@ namespace TrustedUninstaller.Shared.Actions
internal class ScheduledTaskAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(ScheduledTaskOperation), Alias = "operation")]
public ScheduledTaskOperation Operation { get; set; } = ScheduledTaskOperation.Delete;
[YamlMember(Alias = "data")]
diff --git a/TrustedUninstaller.Shared/Actions/ServiceAction.cs b/TrustedUninstaller.Shared/Actions/ServiceAction.cs
index 2ae6c07..a9d5105 100644
--- a/TrustedUninstaller.Shared/Actions/ServiceAction.cs
+++ b/TrustedUninstaller.Shared/Actions/ServiceAction.cs
@@ -1,5 +1,7 @@
#nullable enable
using System;
+using System.Collections.Specialized;
+using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -7,6 +9,7 @@ using System.Management;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using System.Windows;
using Microsoft.Win32;
using TrustedUninstaller.Shared.Exceptions;
using TrustedUninstaller.Shared.Tasks;
@@ -25,6 +28,7 @@ namespace TrustedUninstaller.Shared.Actions
}
internal class ServiceAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(ServiceOperation), Alias = "operation")]
public ServiceOperation Operation { get; set; } = ServiceOperation.Delete;
@@ -94,7 +98,7 @@ namespace TrustedUninstaller.Shared.Actions
return (int)value == Startup.Value ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo;
}
- ServiceController serviceController;
+ ServiceController? serviceController;
if (Device) serviceController = GetDevice();
else serviceController = GetService();
@@ -129,12 +133,10 @@ namespace TrustedUninstaller.Shared.Actions
|| serviceController?.Status == ServiceControllerStatus.PausePending ?
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo,
ServiceOperation.Delete =>
- serviceController == null ?
+ serviceController == null || Win32.ServiceEx.IsPendingDeleteOrDeleted(serviceController.ServiceName) ?
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo,
_ => throw new ArgumentOutOfRangeException("Argument out of Range", new ArgumentOutOfRangeException())
};
-
-
}
private readonly string[] RegexNoKill = { "DcomLaunch" };
@@ -191,44 +193,46 @@ namespace TrustedUninstaller.Shared.Actions
foreach (ServiceController dependentService in service.DependentServices)
{
Console.WriteLine($"Killing dependent service {dependentService.ServiceName}...");
+
+ try
+ {
+ dependentService.Stop();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Service stop failed: " + e.Message, e.StackTrace, "ServiceAction Warning", ServiceName);
+ }
+
cmdAction.Command = Environment.Is64BitOperatingSystem ?
- $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {dependentService.ServiceName} -caction stop" :
+ $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {dependentService.ServiceName} -caction stop" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {dependentService.ServiceName} -caction stop";
- await cmdAction.RunTask();
+ if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
Console.WriteLine("Waiting for the service to stop...");
int delay = 100;
- while (service.Status != ServiceControllerStatus.Stopped && delay <= 1000)
+ while (dependentService.Status != ServiceControllerStatus.Stopped && delay <= 1000)
{
- service.Refresh();
+ dependentService.Refresh();
//Wait for the service to stop
Task.Delay(delay).Wait();
delay += 100;
}
if (delay >= 1000)
{
- Console.WriteLine("\r\nService stop timeout exceeded. Trying second method...");
-
- try
- {
- using var search = new ManagementObjectSearcher($"SELECT * FROM Win32_Service WHERE Name='{service.ServiceName}'");
-
- foreach (ManagementObject queryObj in search.Get())
- {
- var serviceId = (UInt32)queryObj["ProcessId"]; // Access service name
-
- var killServ = new TaskKillAction()
- {
- ProcessID = (int)serviceId
- };
- await killServ.RunTask();
- }
- }
- catch (Exception e)
+ Console.WriteLine("\r\nService stop timeout exceeded.");
+ }
+ try
+ {
+ var killServ = new TaskKillAction()
{
- ErrorLogger.WriteToErrorLog($"Could not kill dependent service {dependentService.ServiceName}.",
- e.StackTrace, "ServiceAction Error");
- }
+ ProcessID = Win32.ServiceEx.GetServiceProcessId(dependentService.ServiceName)
+ };
+ await killServ.RunTask();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog($"Could not kill dependent service {dependentService.ServiceName}.",
+ e.StackTrace, "ServiceAction Error");
}
}
@@ -243,11 +247,19 @@ namespace TrustedUninstaller.Shared.Actions
if (DeleteStop && service.Status != ServiceControllerStatus.StopPending && service.Status != ServiceControllerStatus.Stopped)
{
+ try
+ {
+ service.Stop();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Service stop failed: " + e.Message, e.StackTrace, "ServiceAction Warning", ServiceName);
+ }
+
cmdAction.Command = Environment.Is64BitOperatingSystem ?
- $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction stop" :
+ $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction stop" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction stop";
- await cmdAction.RunTask();
-
+ if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
Console.WriteLine("Waiting for the service to stop...");
@@ -259,30 +271,21 @@ namespace TrustedUninstaller.Shared.Actions
await Task.Delay(delay);
delay += 100;
}
- if (delay >= 1500)
+ if (delay >= 1000)
{
- Console.WriteLine("\r\nService stop timeout exceeded. Trying second method...");
-
- try
- {
- using var search = new ManagementObjectSearcher($"SELECT * FROM Win32_Service WHERE Name='{service.ServiceName}'");
-
- foreach (ManagementObject queryObj in search.Get())
- {
- var serviceId = (UInt32)queryObj["ProcessId"]; // Access service name
-
- var killServ = new TaskKillAction()
- {
- ProcessID = (int)serviceId
- };
- await killServ.RunTask();
- }
- }
- catch (Exception e)
+ Console.WriteLine("\r\nService stop timeout exceeded.");
+ }
+ try
+ {
+ var killServ = new TaskKillAction()
{
- ErrorLogger.WriteToErrorLog($"Could not kill service {service.ServiceName}.",
- e.StackTrace, "ServiceAction Error");
- }
+ ProcessID = Win32.ServiceEx.GetServiceProcessId(service.ServiceName)
+ };
+ await killServ.RunTask();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog($"Could not kill service {service.ServiceName}.", e.StackTrace, "ServiceAction Error");
}
if (RegistryDelete)
@@ -296,18 +299,39 @@ namespace TrustedUninstaller.Shared.Actions
}
else
{
+ try
+ {
+ ServiceInstaller ServiceInstallerObj = new ServiceInstaller();
+ ServiceInstallerObj.Context = new InstallContext();
+ ServiceInstallerObj.ServiceName = service.ServiceName;
+ ServiceInstallerObj.Uninstall(null);
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Service uninstall failed: " + e.Message, e.StackTrace, "ServiceAction Warning", ServiceName);
+ }
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction delete" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction delete";
- await cmdAction.RunTask();
+ if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
+
}
else
{
+ try
+ {
+ service.Stop();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Service stop failed: " + e.Message, e.StackTrace, "ServiceAction Warning", ServiceName);
+ }
+
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction {Operation.ToString().ToLower()}" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction {Operation.ToString().ToLower()}";
- await cmdAction.RunTask();
+ if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
service?.Dispose();
diff --git a/TrustedUninstaller.Shared/Actions/ShortcutAction.cs b/TrustedUninstaller.Shared/Actions/ShortcutAction.cs
index 7a727b4..9c5912d 100644
--- a/TrustedUninstaller.Shared/Actions/ShortcutAction.cs
+++ b/TrustedUninstaller.Shared/Actions/ShortcutAction.cs
@@ -10,6 +10,7 @@ namespace TrustedUninstaller.Shared.Actions
{
class ShortcutAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; }
diff --git a/TrustedUninstaller.Shared/Actions/SystemPackageAction.cs b/TrustedUninstaller.Shared/Actions/SystemPackageAction.cs
index aeaa65c..4b5d865 100644
--- a/TrustedUninstaller.Shared/Actions/SystemPackageAction.cs
+++ b/TrustedUninstaller.Shared/Actions/SystemPackageAction.cs
@@ -14,6 +14,7 @@ namespace TrustedUninstaller.Shared.Actions
// Integrate ame-assassin later
internal class SystemPackageAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
public enum Architecture
{
amd64 = 0,
@@ -77,11 +78,13 @@ namespace TrustedUninstaller.Shared.Actions
}
}
+ string kernelDriverArg = AmeliorationUtil.UseKernelDriver ? " -UseKernelDriver" : "";
+
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
CreateNoWindow = true,
- Arguments = $@"-SystemPackage ""{Name}"" -Arch {Arch.ToString()} -Language ""{Language}""" + excludeArgs + excludeDependsArgs,
+ Arguments = $@"-SystemPackage ""{Name}"" -Arch {Arch.ToString()} -Language ""{Language}""" + excludeArgs + excludeDependsArgs + kernelDriverArg,
FileName = Directory.GetCurrentDirectory() + "\\ame-assassin\\ame-assassin.exe",
RedirectStandardOutput = true,
RedirectStandardError = true
diff --git a/TrustedUninstaller.Shared/Actions/TaskKillAction.cs b/TrustedUninstaller.Shared/Actions/TaskKillAction.cs
index 99bd1ea..33e316c 100644
--- a/TrustedUninstaller.Shared/Actions/TaskKillAction.cs
+++ b/TrustedUninstaller.Shared/Actions/TaskKillAction.cs
@@ -13,9 +13,22 @@ namespace TrustedUninstaller.Shared.Actions
{
class TaskKillAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess,
+ bool bInheritHandle, int dwProcessId);
+
+ public enum ProcessAccessFlags : uint
+ {
+ QueryLimitedInformation = 0x1000
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool CloseHandle(IntPtr hObject);
[YamlMember(typeof(string), Alias = "name")]
public string? ProcessName { get; set; }
@@ -73,18 +86,38 @@ namespace TrustedUninstaller.Shared.Actions
return processToTerminate.Any() ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed;
}
- private IEnumerable GetProcess()
+ private List GetProcess()
{
- if (ProcessName == null) return new List();
+ if (ProcessID.HasValue)
+ {
+ var list = new List();
+ try
+ {
+ var process = Process.GetProcessById(ProcessID.Value);
+ if (ProcessName == null || process.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase))
+ list.Add(process);
+ else
+ return list;
+ }
+ catch (Exception e)
+ {
+ return list;
+ }
+ }
- if (ProcessName.EndsWith("*") && ProcessName.StartsWith("*")) return Process.GetProcesses()
- .Where(process => process.ProcessName.IndexOf(ProcessName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0);
+ if (ProcessName == null)
+ {
+ return new List();
+ }
+
+ if (ProcessName.EndsWith("*") && ProcessName.StartsWith("*")) return Process.GetProcesses().ToList()
+ .Where(process => process.ProcessName.IndexOf(ProcessName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0).ToList();
if (ProcessName.EndsWith("*")) return Process.GetProcesses()
- .Where(process => process.ProcessName.StartsWith(ProcessName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase));
+ .Where(process => process.ProcessName.StartsWith(ProcessName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase)).ToList();
if (ProcessName.StartsWith("*")) return Process.GetProcesses()
- .Where(process => process.ProcessName.EndsWith(ProcessName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase));
+ .Where(process => process.ProcessName.EndsWith(ProcessName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase)).ToList();
- return Process.GetProcessesByName(ProcessName);
+ return Process.GetProcessesByName(ProcessName).ToList();
}
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool IsProcessCritical(IntPtr hProcess, ref bool Critical);
@@ -116,85 +149,76 @@ namespace TrustedUninstaller.Shared.Actions
if (ProcessName != null)
{
//If the service is svchost, we stop the service instead of killing it.
- if (ProcessName.Contains("svchost"))
+ if (ProcessName.Equals("svchost", StringComparison.OrdinalIgnoreCase))
{
- // bool serviceFound = false;
try
{
- using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{ProcessID}'");
-
- foreach (ManagementObject queryObj in search.Get())
+ if (ProcessID.HasValue)
+ {
+ foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(ProcessID.Value))
+ {
+ try
+ {
+ var stopServ = new ServiceAction()
+ {
+ ServiceName = serviceName,
+ Operation = ServiceOperation.Stop
+ };
+ await stopServ.RunTask();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Could not kill service " + serviceName + ": " + e.Message);
+ ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Could not kill service " + serviceName + ": " + e.Message);
+ }
+ }
+ }
+ else
{
- var serviceName = (string)queryObj["Name"]; // Access service name
-
- var stopServ = new ServiceAction()
+ foreach (var process in GetProcess())
{
- ServiceName = serviceName,
- Operation = ServiceOperation.Stop
+ foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(process.Id))
+ {
+ try
+ {
+ var stopServ = new ServiceAction()
+ {
+ ServiceName = serviceName,
+ Operation = ServiceOperation.Stop
- };
- await stopServ.RunTask();
+ };
+ await stopServ.RunTask();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Could not kill service " + serviceName + ": " + e.Message);
+ ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Could not kill service " + serviceName + ": " + e.Message);
+ }
+ }
+ }
}
}
catch (NullReferenceException e)
{
- Console.WriteLine($"A service with PID: {ProcessID} could not be found.");
- ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Could not find service with PID {ProcessID}.");
+ Console.WriteLine($"A service with PID: {ProcessID.Value} could not be found.");
+ ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Could not find service with PID {ProcessID.Value}.");
}
-
-/* foreach (var serv in servicesToDelete)
+ int i;
+ for (i = 0; i <= 6 && GetProcess().Any(); i++)
{
- //The ID can only be associated with one of the services, there's no need to loop through
- //them all if we already found the service.
- if (serviceFound)
- {
- break;
- }
-
- try
- {
- using var search = new ManagementObjectSearcher($"select ProcessId from Win32_Service where Name = '{serv}'").Get();
- var servID = (uint)search.OfType().FirstOrDefault()["ProcessID"];
-
- if (servID == ProcessID)
- {
- serviceFound = true;
-
-
-
- }
- search.Dispose();
- }
- catch (Exception e)
- {
- var search = new ManagementObjectSearcher($"select Name from Win32_Service where ProcessID = '{ProcessID}'").Get();
- var servName = search.OfType().FirstOrDefault()["Name"];
- Console.WriteLine($"Could not find {servName} but PID {ProcessID} still exists.");
- ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Exception Type: {e.GetType()}");
- return false;
- }
- }*/
- //None of the services listed, we shouldn't kill svchost.
-/* if (!serviceFound)
+ await Task.Delay(100 * i);
+ }
+ if (i < 6)
{
- var search = new ManagementObjectSearcher($"select Name from Win32_Service where ProcessID = '{ProcessID}'").Get();
- var servName = search.OfType().FirstOrDefault()["Name"];
- Console.WriteLine($"A critical system process \"{servName}\" with PID {ProcessID} caused the Wizard to fail.");
- await WinUtil.UninstallDriver();
- Environment.Exit(-1);
- return false;
- }*/
-
- await Task.Delay(100);
-
- InProgress = false;
- return true;
+ InProgress = false;
+ return true;
+ }
}
if (PathContains != null && !ProcessID.HasValue)
{
- var processes = GetProcess().ToList();
+ var processes = GetProcess();
if (processes.Count > 0) Console.WriteLine("Processes:");
foreach (var process in processes.Where(x => x.MainModule.FileName.Contains(PathContains)))
@@ -203,58 +227,74 @@ namespace TrustedUninstaller.Shared.Actions
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success)) {
bool isCritical = false;
- IsProcessCritical(process.Handle, ref isCritical);
+ IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
+ IsProcessCritical(hprocess, ref isCritical);
+ CloseHandle(hprocess);
if (isCritical)
{
Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
continue;
}
}
-
+
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate";
- await cmdAction.RunTask();
+
+ if (AmeliorationUtil.UseKernelDriver)
+ cmdAction.RunTaskOnMainThread();
+ else
+ TerminateProcess(process.Handle, 1);
int i = 0;
- while (i <= 15 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
+ while (i <= 5 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
{
await Task.Delay(300);
+ if (AmeliorationUtil.UseKernelDriver)
+ cmdAction.RunTaskOnMainThread();
+ else
+ TerminateProcess(process.Handle, 1);
i++;
}
- if (i >= 15) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
+ try {
+ if (!TerminateProcess(process.Handle, 1))
+ ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Could not open process handle.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
+ }
+
+ if (i >= 5) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
}
InProgress = false;
return true;
}
}
-
if (ProcessID.HasValue)
{
+ var process = Process.GetProcessById(ProcessID.Value);
if (ProcessName != null && ProcessName.Equals("explorer", StringComparison.OrdinalIgnoreCase))
{
- try
- {
- var process = Process.GetProcessById(ProcessID.Value);
- TerminateProcess(process.Handle, 1);
- } catch (Exception)
+ try {
+ if (!TerminateProcess(process.Handle, 1))
+ ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
+ }
+ catch (Exception e)
{
- cmdAction.Command = Environment.Is64BitOperatingSystem ?
- $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate" :
- $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate";
- await cmdAction.RunTask();
+ ErrorLogger.WriteToErrorLog("Could not open process handle.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
}
}
else
{
- var process = Process.GetProcessById(ProcessID.Value);
-
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
{
bool isCritical = false;
try
{
- IsProcessCritical(process.Handle, ref isCritical);
+ IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
+ IsProcessCritical(hprocess, ref isCritical);
+ CloseHandle(hprocess);
}
catch (InvalidOperationException e)
{
@@ -267,59 +307,81 @@ namespace TrustedUninstaller.Shared.Actions
return false;
}
}
+ try {
+ if (!TerminateProcess(process.Handle, 1))
+ ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Could not open process handle.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
+ }
+
cmdAction.Command = Environment.Is64BitOperatingSystem ?
- $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate" :
- $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate";
- await cmdAction.RunTask();
+ $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID.Value} -caction terminate" :
+ $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID.Value} -caction terminate";
+ if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
}
await Task.Delay(100);
}
else
{
- var processes = GetProcess().ToList();
+ var processes = GetProcess();
if (processes.Count > 0) Console.WriteLine("Processes:");
foreach (var process in processes)
{
Console.WriteLine(process.ProcessName + " - " + process.Id);
-
- cmdAction.Command = Environment.Is64BitOperatingSystem ?
- $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.ProcessName}.exe -caction terminate" :
- $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.ProcessName}.exe -caction terminate";
- if (process.ProcessName == "explorer") TerminateProcess(process.Handle, 1);
- else
+
+ if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
{
- if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
+ bool isCritical = false;
+ try
{
- bool isCritical = false;
- try
- {
- IsProcessCritical(process.Handle, ref isCritical);
- }
- catch (InvalidOperationException e)
- {
- ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
- continue;
- }
- if (isCritical)
- {
- Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
- continue;
- }
+ IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id);
+ IsProcessCritical(hprocess, ref isCritical);
+ CloseHandle(hprocess);
}
-
- await cmdAction.RunTask();
+ catch (InvalidOperationException e)
+ {
+ ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
+ continue;
+ }
+ if (isCritical)
+ {
+ Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
+ continue;
+ }
+ }
+ try
+ {
+ if (!TerminateProcess(process.Handle, 1))
+ ErrorLogger.WriteToErrorLog("TerminateProcess failed with error code: " + Marshal.GetLastWin32Error(), Environment.StackTrace, "TaskKill Error", ProcessName);
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog("Could not open process handle.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
}
+
+ if (process.ProcessName == "explorer") continue;
+
+ cmdAction.Command = Environment.Is64BitOperatingSystem ?
+ $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate" :
+ $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate";
+ if (AmeliorationUtil.UseKernelDriver && process.ProcessName != "explorer") cmdAction.RunTaskOnMainThread();
int i = 0;
- while (i <= 15 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
+ while (i <= 5 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName))
{
+ if (AmeliorationUtil.UseKernelDriver)
+ cmdAction.RunTaskOnMainThread();
+ else
+ TerminateProcess(process.Handle, 1);
await Task.Delay(300);
i++;
}
- if (i >= 15) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
+ if (i >= 5) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error");
}
}
diff --git a/TrustedUninstaller.Shared/Actions/UpdateAction.cs b/TrustedUninstaller.Shared/Actions/UpdateAction.cs
index ab1edc5..582ed4b 100644
--- a/TrustedUninstaller.Shared/Actions/UpdateAction.cs
+++ b/TrustedUninstaller.Shared/Actions/UpdateAction.cs
@@ -10,6 +10,7 @@ namespace TrustedUninstaller.Shared.Actions
{
internal class UpdateAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "name")]
public string PackageName { get; set; }
diff --git a/TrustedUninstaller.Shared/Actions/UserAction.cs b/TrustedUninstaller.Shared/Actions/UserAction.cs
index 2a51c86..9004954 100644
--- a/TrustedUninstaller.Shared/Actions/UserAction.cs
+++ b/TrustedUninstaller.Shared/Actions/UserAction.cs
@@ -10,6 +10,7 @@ namespace TrustedUninstaller.Shared.Actions
{
public class UserAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "name")]
public string Username { get; set; } = "";
[YamlMember(typeof(bool), Alias = "admin")]
diff --git a/TrustedUninstaller.Shared/Actions/WriteStatusAction.cs b/TrustedUninstaller.Shared/Actions/WriteStatusAction.cs
index ef166ea..5b686c6 100644
--- a/TrustedUninstaller.Shared/Actions/WriteStatusAction.cs
+++ b/TrustedUninstaller.Shared/Actions/WriteStatusAction.cs
@@ -7,6 +7,7 @@ namespace TrustedUninstaller.Shared.Actions
{
public class WriteStatusAction : TaskAction, ITaskAction
{
+ public void RunTaskOnMainThread() { throw new NotImplementedException(); }
[YamlMember(typeof(string), Alias = "status")]
public string Status { get; set; }
private bool InProgress { get; set; }
diff --git a/TrustedUninstaller.Shared/AmeliorationUtil.cs b/TrustedUninstaller.Shared/AmeliorationUtil.cs
index 9d48253..531b1f3 100644
--- a/TrustedUninstaller.Shared/AmeliorationUtil.cs
+++ b/TrustedUninstaller.Shared/AmeliorationUtil.cs
@@ -27,8 +27,10 @@ namespace TrustedUninstaller.Shared
private static readonly ConfigParser Parser = new ConfigParser();
private static readonly HttpClient Client = new HttpClient();
-
- public static Playbook Playbook { set; get; }
+
+ public static Playbook Playbook { set; get; } = new Playbook();
+
+ public static bool UseKernelDriver = false;
public static readonly List ErrorDisplayList = new List();
@@ -37,16 +39,19 @@ namespace TrustedUninstaller.Shared
return Parser.Tasks.Sum(task => task.Actions.Sum(action =>
{
var taskAction = (TaskAction)action;
- if (!String.IsNullOrEmpty(taskAction.Option) &&
- (taskAction.Option.StartsWith("!") && options != null && options.Contains(taskAction.Option.Substring(1), StringComparer.OrdinalIgnoreCase) ||
- !taskAction.Option.StartsWith("!") && options != null && !options.Contains(taskAction.Option, StringComparer.OrdinalIgnoreCase)))
+ if ((!IsApplicableOption(taskAction.Option, options) || !IsApplicableArch(taskAction.Arch)) ||
+ (taskAction.Builds != null && (
+ !taskAction.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
+ ||
+ taskAction.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
+ (taskAction.Options != null && (
+ !taskAction.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
+ ||
+ taskAction.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
+ {
return 0;
+ }
- if (!String.IsNullOrEmpty(taskAction.Arch) &&
- (taskAction.Arch.StartsWith("!") && String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase) ||
- !taskAction.Arch.StartsWith("!") && !String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase)))
- return 0;
-
return action.GetProgressWeight();
}));
}
@@ -75,6 +80,21 @@ namespace TrustedUninstaller.Shared
}
}
+ if ((!IsApplicableOption(currentTask.Option, Playbook.Options) || !IsApplicableArch(currentTask.Arch)) ||
+ (currentTask.Builds != null && (
+ !currentTask.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
+ ||
+ currentTask.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
+ (currentTask.Options != null && (
+ !currentTask.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
+ ||
+ currentTask.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
+ {
+ Parser.Tasks.Remove(currentTask);
+ return true;
+ }
+
+
//Get the features of the last added task (the task that was just added from the config file)
var features = currentTask.Features;
@@ -203,15 +223,18 @@ namespace TrustedUninstaller.Shared
{
var taskAction = (TaskAction)action;
- if (!String.IsNullOrEmpty(taskAction.Option) &&
- (taskAction.Option.StartsWith("!") && Playbook.Options != null && Playbook.Options.Contains(taskAction.Option.Substring(1), StringComparer.OrdinalIgnoreCase) ||
- !taskAction.Option.StartsWith("!") && Playbook.Options != null && !Playbook.Options.Contains(taskAction.Option, StringComparer.OrdinalIgnoreCase)))
- continue;
-
- if (!String.IsNullOrEmpty(taskAction.Arch) &&
- (taskAction.Arch.StartsWith("!") && String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase) ||
- !taskAction.Arch.StartsWith("!") && !String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase)))
+ if ((!IsApplicableOption(taskAction.Option, Playbook.Options) || !IsApplicableArch(taskAction.Arch)) ||
+ (taskAction.Builds != null && (
+ !taskAction.Builds.Where(build => !build.StartsWith("!")).Any(build => IsApplicableWindowsVersion(build))
+ ||
+ taskAction.Builds.Where(build => build.StartsWith("!")).Any(build => !IsApplicableWindowsVersion(build)))) ||
+ (taskAction.Options != null && (
+ !taskAction.Options.Where(option => !option.StartsWith("!")).Any(option => IsApplicableOption(option, Playbook.Options))
+ ||
+ taskAction.Options.Where(option => option.StartsWith("!")).Any(option => !IsApplicableOption(option, Playbook.Options)))))
+ {
continue;
+ }
int i = 0;
@@ -225,7 +248,10 @@ namespace TrustedUninstaller.Shared
Console.WriteLine();
try
{
- await action.RunTask();
+ var actionTask = action.RunTask();
+ if (actionTask == null)
+ action.RunTaskOnMainThread();
+ else await actionTask;
action.ResetProgress();
}
catch (Exception e)
@@ -275,6 +301,8 @@ namespace TrustedUninstaller.Shared
}
Console.WriteLine("Task completed.");
+ ProcessPrivilege.ResetTokens();
+
File.AppendAllText("TasksAdded.txt", task.Title + Environment.NewLine);
}
catch (Exception e)
@@ -434,20 +462,20 @@ namespace TrustedUninstaller.Shared
Console.WriteLine($"{deletedItemsCount} files were deleted successfully. " +
$"{failedDeletedItemsCount} files couldn't be deleted.");
-
//Check if the kernel driver is installed.
//service = ServiceController.GetDevices()
//.FirstOrDefault(s => s.DisplayName == "KProcessHacker2");
- if (true)
+ if (UseKernelDriver)
{
//Remove Process Hacker's kernel driver.
await WinUtil.UninstallDriver();
+
+ await AmeliorationUtil.SafeRunAction(new RegistryKeyAction()
+ {
+ KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2",
+ });
}
- await AmeliorationUtil.SafeRunAction(new RegistryKeyAction()
- {
- KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2",
- });
-
+
File.Delete("TasksAdded.txt");
Console.WriteLine();
@@ -625,6 +653,95 @@ namespace TrustedUninstaller.Shared
}
}
}
+
+ private static bool IsApplicableWindowsVersion(string version)
+ {
+ bool negative = false;
+ if (version.StartsWith("!"))
+ {
+ version = version.TrimStart('!');
+ negative = true;
+ }
+
+ bool compareUpdateBuild = version.Contains(".");
+ var currentBuild = decimal.Parse(compareUpdateBuild ? Globals.WinVer + "." + Globals.WinUpdateVer : Globals.WinVer.ToString());
+
+ bool result = false;
+ if (version.StartsWith(">="))
+ {
+ var parsed = decimal.Parse(version.Substring(2));
+ if (currentBuild >= parsed)
+ result = true;
+ } else if (version.StartsWith("<="))
+ {
+ var parsed = decimal.Parse(version.Substring(2));
+ if (currentBuild <= parsed)
+ result = true;
+ } else if (version.StartsWith(">"))
+ {
+ var parsed = decimal.Parse(version.Substring(1));
+ if (currentBuild > parsed)
+ result = true;
+ } else if (version.StartsWith("<"))
+ {
+ var parsed = decimal.Parse(version.Substring(1));
+ if (currentBuild < parsed)
+ result = true;
+ }
+ else
+ {
+ var parsed = decimal.Parse(version);
+ if (currentBuild == parsed)
+ result = true;
+ }
+
+ return negative ? !result : result;
+ }
+
+ private static bool IsApplicableOption(string option, List options)
+ {
+ if (String.IsNullOrEmpty(option))
+ return true;
+
+ if (option.Contains("&"))
+ {
+ if (option.Contains("!"))
+ throw new ArgumentException("YAML options item must not contain both & and !", "options");
+
+ return option.Split('&').All(splitOption => IsApplicableOption(splitOption, options));
+ }
+
+ bool negative = false;
+ if (option.StartsWith("!"))
+ {
+ option = option.TrimStart('!');
+ negative = true;
+ }
+
+ if (options == null)
+ return negative ? true : false;
+
+ var result = options.Contains(option, StringComparer.OrdinalIgnoreCase);
+
+ return negative ? !result : result;
+ }
+
+ private static bool IsApplicableArch(string arch)
+ {
+ if (String.IsNullOrEmpty(arch))
+ return true;
+
+ bool negative = false;
+ if (arch.StartsWith("!"))
+ {
+ arch = arch.TrimStart('!');
+ negative = true;
+ }
+
+ var result = String.Equals(arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase);
+
+ return negative ? !result : result;
+ }
public static async Task SafeRunAction(ITaskAction action)
{
diff --git a/TrustedUninstaller.Shared/AugmentedProcess.cs b/TrustedUninstaller.Shared/AugmentedProcess.cs
new file mode 100644
index 0000000..f54d48b
--- /dev/null
+++ b/TrustedUninstaller.Shared/AugmentedProcess.cs
@@ -0,0 +1,3340 @@
+using System.Text;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Runtime.CompilerServices;
+using System.Diagnostics;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.Win32.SafeHandles;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.Versioning;
+using System.Runtime.ConstrainedExecution;
+using Microsoft.Win32;
+
+namespace TrustedUninstaller.Shared
+{
+ [DefaultEvent("Exited"), DefaultProperty("StartInfo"), HostProtection(SharedState = true, Synchronization = true, ExternalProcessMgmt = true, SelfAffectingProcessMgmt = true)]
+ public static class AugmentedProcess
+ {
+ public class Process : Component
+ {
+ public enum CreateType {
+ UserToken,
+ RawToken
+ }
+ //
+ // FIELDS
+ //
+ bool haveProcessId;
+ int processId;
+ bool haveProcessHandle;
+ SafeProcessHandle m_processHandle;
+ bool isRemoteMachine;
+ string machineName;
+ ProcessInfo processInfo;
+ Int32 m_processAccess;
+ ProcessThreadCollection threads;
+ ProcessModuleCollection modules;
+ bool haveMainWindow;
+ IntPtr mainWindowHandle; // no need to use SafeHandle for window
+ string mainWindowTitle;
+ bool haveWorkingSetLimits;
+ bool haveProcessorAffinity;
+ IntPtr processorAffinity;
+ bool havePriorityClass;
+ ProcessPriorityClass priorityClass;
+ ProcessStartInfo startInfo;
+ bool watchForExit;
+ bool watchingForExit;
+ EventHandler onExited;
+ bool exited;
+ int exitCode;
+ bool signaled;
+ DateTime exitTime;
+ bool haveExitTime;
+ bool responding;
+ bool haveResponding;
+ bool priorityBoostEnabled;
+ bool havePriorityBoostEnabled;
+ bool raisedOnExited;
+ bool expandEnvironmentVariables;
+ RegisteredWaitHandle registeredWaitHandle;
+ WaitHandle waitHandle;
+ ISynchronizeInvoke synchronizingObject;
+ StreamReader standardOutput;
+ StreamWriter standardInput;
+ StreamReader standardError;
+ OperatingSystem operatingSystem;
+ bool disposed;
+ static object s_CreateProcessLock = new object();
+
+ // This enum defines the operation mode for redirected process stream.
+ // We don't support switching between synchronous mode and asynchronous mode.
+ private enum StreamReadMode
+ {
+ undefined,
+ syncMode,
+ asyncMode
+ }
+
+ StreamReadMode outputStreamReadMode;
+ StreamReadMode errorStreamReadMode;
+ public event DataReceivedEventHandler OutputDataReceived;
+ public event DataReceivedEventHandler ErrorDataReceived;
+ // Abstract the stream details
+ internal AsyncStreamReader output;
+ internal AsyncStreamReader error;
+ internal bool pendingOutputRead;
+ internal bool pendingErrorRead;
+ internal static TraceSwitch processTracing = null;
+ public Process()
+ {
+ this.machineName = ".";
+ this.outputStreamReadMode = StreamReadMode.undefined;
+ this.errorStreamReadMode = StreamReadMode.undefined;
+ this.m_processAccess = NativeMethods.PROCESS_ALL_ACCESS;
+ }
+ [ResourceExposure(ResourceScope.Machine)]
+ Process(string machineName, bool isRemoteMachine, int processId, ProcessInfo processInfo) : base()
+ {
+ this.processInfo = processInfo;
+ this.machineName = machineName;
+ this.isRemoteMachine = isRemoteMachine;
+ this.processId = processId;
+ this.haveProcessId = true;
+ this.outputStreamReadMode = StreamReadMode.undefined;
+ this.errorStreamReadMode = StreamReadMode.undefined;
+ this.m_processAccess = NativeMethods.PROCESS_ALL_ACCESS;
+ }
+
+ //
+ // PROPERTIES
+ //
+ bool Associated
+ {
+ get
+ {
+ return haveProcessId || haveProcessHandle;
+ }
+ }
+
+ public string ProcessName
+ {
+ get
+ {
+ this.EnsureState(Process.State.HaveProcessInfo);
+ return this.processInfo.processName;
+ }
+ }
+
+ public int ExitCode
+ {
+ get
+ {
+ EnsureState(State.Exited);
+ return exitCode;
+ }
+ }
+ public bool HasExited
+ {
+ get
+ {
+ if (!exited)
+ {
+ EnsureState(State.Associated);
+ SafeProcessHandle handle = null;
+ try
+ {
+ handle = GetProcessHandle(NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.SYNCHRONIZE, false);
+ if (handle.IsInvalid)
+ {
+ exited = true;
+ }
+ else
+ {
+ int exitCode;
+
+ // Although this is the wrong way to check whether the process has exited,
+ // it was historically the way we checked for it, and a lot of code then took a dependency on
+ // the fact that this would always be set before the pipes were closed, so they would read
+ // the exit code out after calling ReadToEnd() or standard output or standard error. In order
+ // to allow 259 to function as a valid exit code and to break as few people as possible that
+ // took the ReadToEnd dependency, we check for an exit code before doing the more correct
+ // check to see if we have been signalled.
+ if (NativeMethods.GetExitCodeProcess(handle, out exitCode) && exitCode != NativeMethods.STILL_ACTIVE)
+ {
+ this.exited = true;
+ this.exitCode = exitCode;
+ }
+ else
+ {
+ // The best check for exit is that the kernel process object handle is invalid,
+ // or that it is valid and signaled. Checking if the exit code != STILL_ACTIVE
+ // does not guarantee the process is closed,
+ // since some process could return an actual STILL_ACTIVE exit code (259).
+ if (!signaled) // if we just came from WaitForExit, don't repeat
+ {
+ ProcessWaitHandle wh = null;
+ try
+ {
+ wh = new ProcessWaitHandle(handle);
+ this.signaled = wh.WaitOne(0, false);
+ }
+ finally
+ {
+ if (wh != null) wh.Close();
+ }
+ }
+ if (signaled)
+ {
+ if (!NativeMethods.GetExitCodeProcess(handle, out exitCode)) throw new Win32Exception();
+ this.exited = true;
+ this.exitCode = exitCode;
+ }
+ }
+ }
+ }
+ finally
+ {
+ ReleaseProcessHandle(handle);
+ }
+ if (exited)
+ {
+ RaiseOnExited();
+ }
+ }
+ return exited;
+ }
+ }
+ public IntPtr Handle
+ {
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ get
+ {
+ EnsureState(State.Associated);
+ return OpenProcessHandle(this.m_processAccess).DangerousGetHandle();
+ }
+ }
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public SafeProcessHandle SafeHandle
+ {
+ get
+ {
+ EnsureState(State.Associated);
+ return OpenProcessHandle(this.m_processAccess);
+ }
+ }
+ public int Id
+ {
+ get
+ {
+ EnsureState(State.HaveId);
+ return processId;
+ }
+ }
+ public string MachineName
+ {
+ get
+ {
+ EnsureState(State.Associated);
+ return machineName;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long NonpagedSystemMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.poolNonpagedBytes;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PagedMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.pageFileBytes;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PagedSystemMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.poolPagedBytes;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PeakPagedMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.pageFileBytesPeak;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PeakWorkingSet64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.workingSetPeak;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PeakVirtualMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.virtualBytesPeak;
+ }
+ }
+ private OperatingSystem OperatingSystem
+ {
+ get
+ {
+ if (operatingSystem == null)
+ {
+ operatingSystem = Environment.OSVersion;
+ }
+ return operatingSystem;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long PrivateMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.privateBytes;
+ }
+ }
+ public int SessionId
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.sessionId;
+ }
+ }
+ public ProcessStartInfo StartInfo
+ {
+ get
+ {
+ return startInfo;
+ }
+ [ResourceExposure(ResourceScope.Machine)]
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("value");
+ }
+ startInfo = value;
+ }
+ }
+
+ public bool ExpandEnvironmentVariables
+ {
+ get
+ {
+ return expandEnvironmentVariables;
+ }
+ set
+ {
+ expandEnvironmentVariables = value;
+ }
+ }
+
+ public ISynchronizeInvoke SynchronizingObject
+ {
+ get
+ {
+ if (this.synchronizingObject == null && DesignMode)
+ {
+ IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
+ if (host != null)
+ {
+ object baseComponent = host.RootComponent;
+ if (baseComponent != null && baseComponent is ISynchronizeInvoke) this.synchronizingObject = (ISynchronizeInvoke)baseComponent;
+ }
+ }
+ return this.synchronizingObject;
+ }
+ set
+ {
+ this.synchronizingObject = value;
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long VirtualMemorySize64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.virtualBytes;
+ }
+ }
+ public bool EnableRaisingEvents
+ {
+ get
+ {
+ return watchForExit;
+ }
+ set
+ {
+ if (value != watchForExit)
+ {
+ if (Associated)
+ {
+ if (value)
+ {
+ OpenProcessHandle();
+ EnsureWatchingForExit();
+ }
+ else
+ {
+ StopWatchingForExit();
+ }
+ }
+ watchForExit = value;
+ }
+ }
+ }
+ public StreamWriter StandardInput
+ {
+ get
+ {
+ if (standardInput == null)
+ {
+ throw new InvalidOperationException("CantGetStandardIn");
+ }
+ return standardInput;
+ }
+ }
+ public StreamReader StandardOutput
+ {
+ get
+ {
+ if (standardOutput == null)
+ {
+ throw new InvalidOperationException("CantGetStandardOut");
+ }
+ if (outputStreamReadMode == StreamReadMode.undefined)
+ {
+ outputStreamReadMode = StreamReadMode.syncMode;
+ }
+ else if (outputStreamReadMode != StreamReadMode.syncMode)
+ {
+ throw new InvalidOperationException("CantMixSyncAsyncOperation");
+ }
+ return standardOutput;
+ }
+ }
+ public StreamReader StandardError
+ {
+ get
+ {
+ if (standardError == null)
+ {
+ throw new InvalidOperationException("CantGetStandardError");
+ }
+ if (errorStreamReadMode == StreamReadMode.undefined)
+ {
+ errorStreamReadMode = StreamReadMode.syncMode;
+ }
+ else if (errorStreamReadMode != StreamReadMode.syncMode)
+ {
+ throw new InvalidOperationException("CantMixSyncAsyncOperation");
+ }
+ return standardError;
+ }
+ }
+ public int WorkingSet
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return unchecked((int)processInfo.workingSet);
+ }
+ }
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public long WorkingSet64
+ {
+ get
+ {
+ EnsureState(State.HaveNtProcessInfo);
+ return processInfo.workingSet;
+ }
+ }
+ public event EventHandler Exited
+ {
+ add
+ {
+ onExited += value;
+ }
+ remove
+ {
+ onExited -= value;
+ }
+ }
+ ///
+ /// Release the temporary handle we used to get process information.
+ /// If we used the process handle stored in the process object (we have all access to the handle,) don't release it.
+ ///
+ ///
+ void ReleaseProcessHandle(SafeProcessHandle handle)
+ {
+ if (handle == null)
+ {
+ return;
+ }
+ if (haveProcessHandle && handle == m_processHandle)
+ {
+ return;
+ }
+ handle.Close();
+ }
+ ///
+ /// This is called from the threadpool when a proces exits.
+ ///
+ ///
+ private void CompletionCallback(object context, bool wasSignaled)
+ {
+ StopWatchingForExit();
+ RaiseOnExited();
+ }
+ ///
+ ///
+ ///
+ /// Free any resources associated with this component.
+ ///
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ //Dispose managed and unmanaged resources
+ Close();
+ }
+ this.disposed = true;
+ base.Dispose(disposing);
+ }
+ }
+ ///
+ ///
+ /// Frees any resources associated with this component.
+ ///
+ ///
+ public void Close()
+ {
+ if (Associated)
+ {
+ if (haveProcessHandle)
+ {
+ StopWatchingForExit();
+ m_processHandle.Close();
+ m_processHandle = null;
+ haveProcessHandle = false;
+ }
+ haveProcessId = false;
+ isRemoteMachine = false;
+ machineName = ".";
+ raisedOnExited = false;
+
+ //Don't call close on the Readers and writers
+ //since they might be referenced by somebody else while the
+ //process is still alive but this method called.
+ standardOutput = null;
+ standardInput = null;
+ standardError = null;
+ output = null;
+ error = null;
+ Refresh();
+ }
+ }
+ ///
+ /// Helper method for checking preconditions when accessing properties.
+ ///
+ ///
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ void EnsureState(State state)
+ {
+ if ((state & State.Associated) != (State)0)
+ if (!Associated)
+ throw new InvalidOperationException("NoAssociatedProcess");
+ if ((state & State.IsLocal) != (State)0 && isRemoteMachine)
+ {
+ throw new NotSupportedException("NotSupportedRemote");
+ }
+ if ((state & Process.State.HaveProcessInfo) != (Process.State) 0 && this.processInfo == null)
+ {
+ if ((state & Process.State.HaveId) == (Process.State) 0)
+ this.EnsureState(Process.State.HaveId);
+ this.processInfo = GetProcessInfo(this.processId, this.machineName);
+ if (this.processInfo == null)
+ throw new InvalidOperationException("NoProcessInfo");
+ }
+ if ((state & State.Exited) != (State)0)
+ {
+ if (!HasExited)
+ {
+ throw new InvalidOperationException("WaitTillExit");
+ }
+ if (!haveProcessHandle)
+ {
+ throw new InvalidOperationException("NoProcessHandle");
+ }
+ }
+ }
+ void EnsureWatchingForExit()
+ {
+ if (!watchingForExit)
+ {
+ lock (this)
+ {
+ if (!watchingForExit)
+ {
+ watchingForExit = true;
+ try
+ {
+ this.waitHandle = new ProcessWaitHandle(m_processHandle);
+ this.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(this.waitHandle, new WaitOrTimerCallback(this.CompletionCallback), null, -1, true);
+ }
+ catch
+ {
+ watchingForExit = false;
+ throw;
+ }
+ }
+ }
+ }
+ }
+ protected void OnExited()
+ {
+ EventHandler exited = onExited;
+ if (exited != null)
+ {
+ if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
+ this.SynchronizingObject.BeginInvoke(exited, new object[]
+ {
+ this, EventArgs.Empty
+ });
+ else exited(this, EventArgs.Empty);
+ }
+ }
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ SafeProcessHandle GetProcessHandle(int access, bool throwIfExited)
+ {
+ if (haveProcessHandle)
+ {
+ if (throwIfExited)
+ {
+ // Since haveProcessHandle is true, we know we have the process handle
+ // open with at least SYNCHRONIZE access, so we can wait on it with
+ // zero timeout to see if the process has exited.
+ ProcessWaitHandle waitHandle = null;
+ try
+ {
+ waitHandle = new ProcessWaitHandle(m_processHandle);
+ if (waitHandle.WaitOne(0, false))
+ {
+ if (haveProcessId) throw new InvalidOperationException("Process has exited: " + processId);
+ else throw new InvalidOperationException("ProcessHasExitedNoId");
+ }
+ }
+ finally
+ {
+ if (waitHandle != null)
+ {
+ waitHandle.Close();
+ }
+ }
+ }
+ return m_processHandle;
+ }
+ else
+ {
+ throw new Exception("(AME) Process handle not available.");
+ }
+ }
+ ///
+ /// Gets a short-term handle to the process, with the given access. If a handle exists,
+ /// then it is reused. If the process has exited, it throws an exception.
+ ///
+ ///
+ SafeProcessHandle GetProcessHandle(int access)
+ {
+ return GetProcessHandle(access, true);
+ }
+ ///
+ /// Opens a long-term handle to the process, with all access. If a handle exists,
+ /// then it is reused. If the process has exited, it throws an exception.
+ ///
+ ///
+ SafeProcessHandle OpenProcessHandle()
+ {
+ return OpenProcessHandle(NativeMethods.PROCESS_ALL_ACCESS);
+ }
+ SafeProcessHandle OpenProcessHandle(Int32 access)
+ {
+ if (!haveProcessHandle)
+ {
+ //Cannot open a new process handle if the object has been disposed, since finalization has been suppressed.
+ if (this.disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+ SetProcessHandle(GetProcessHandle(access));
+ }
+ return m_processHandle;
+ }
+ ///
+ /// Raise the Exited event, but make sure we don't do it more than once.
+ ///
+ ///
+ void RaiseOnExited()
+ {
+ if (!raisedOnExited)
+ {
+ lock (this)
+ {
+ if (!raisedOnExited)
+ {
+ raisedOnExited = true;
+ OnExited();
+ }
+ }
+ }
+ }
+ ///
+ ///
+ /// Discards any information about the associated process
+ /// that has been cached inside the process component. After is called, the
+ /// first request for information for each property causes the process component
+ /// to obtain a new value from the associated process.
+ ///
+ ///
+ public void Refresh()
+ {
+ processInfo = null;
+ threads = null;
+ modules = null;
+ mainWindowTitle = null;
+ exited = false;
+ signaled = false;
+ haveMainWindow = false;
+ haveWorkingSetLimits = false;
+ haveProcessorAffinity = false;
+ havePriorityClass = false;
+ haveExitTime = false;
+ haveResponding = false;
+ havePriorityBoostEnabled = false;
+ }
+ ///
+ /// Helper to associate a process handle with this component.
+ ///
+ ///
+ void SetProcessHandle(SafeProcessHandle processHandle)
+ {
+ this.m_processHandle = processHandle;
+ this.haveProcessHandle = true;
+ if (watchForExit)
+ {
+ EnsureWatchingForExit();
+ }
+ }
+ ///
+ /// Helper to associate a process id with this component.
+ ///
+ ///
+ [ResourceExposure(ResourceScope.Machine)]
+ void SetProcessId(int processId)
+ {
+ this.processId = processId;
+ this.haveProcessId = true;
+ }
+ ///
+ ///
+ /// Starts a process specified by the property of this
+ /// component and associates it with the
+ /// . If a process resource is reused
+ /// rather than started, the reused process is associated with this
+ /// component.
+ ///
+ ///
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ public bool Start(CreateType type, ref Win32.TokensEx.SafeTokenHandle token)
+ {
+ Close();
+ ProcessStartInfo startInfo = StartInfo;
+ if (startInfo.FileName.Length == 0) throw new InvalidOperationException("FileNameMissing");
+
+ return StartWithCreateProcess(startInfo, type, ref token);
+ }
+ [ResourceExposure(ResourceScope.Process)]
+ [ResourceConsumption(ResourceScope.Process)]
+ private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, NativeMethods.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize)
+ {
+ bool ret = NativeMethods.CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize);
+ if (!ret || hReadPipe.IsInvalid || hWritePipe.IsInvalid)
+ {
+ throw new Win32Exception();
+ }
+ }
+
+ // Using synchronous Anonymous pipes for process input/output redirection means we would end up
+ // wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since
+ // it will take advantage of the NT IO completion port infrastructure. But we can't really use
+ // Overlapped I/O for process input/output as it would break Console apps (managed Console class
+ // methods such as WriteLine as well as native CRT functions like printf) which are making an
+ // assumption that the console standard handles (obtained via GetStdHandle()) are opened
+ // for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchrnously!
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ private void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
+ {
+ NativeMethods.SECURITY_ATTRIBUTES securityAttributesParent = new NativeMethods.SECURITY_ATTRIBUTES();
+ securityAttributesParent.bInheritHandle = true;
+ SafeFileHandle hTmp = null;
+ try
+ {
+ if (parentInputs)
+ {
+ CreatePipeWithSecurityAttributes(out childHandle, out hTmp, securityAttributesParent, 0);
+ }
+ else
+ {
+ CreatePipeWithSecurityAttributes(out hTmp, out childHandle, securityAttributesParent, 0);
+ }
+ // Duplicate the parent handle to be non-inheritable so that the child process
+ // doesn't have access. This is done for correctness sake, exact reason is unclear.
+ // One potential theory is that child process can do something brain dead like
+ // closing the parent end of the pipe and there by getting into a blocking situation
+ // as parent will not be draining the pipe at the other end anymore.
+ if (!NativeMethods.DuplicateHandle(new HandleRef(this, NativeMethods.GetCurrentProcess()), hTmp, new HandleRef(this, NativeMethods.GetCurrentProcess()), out parentHandle, 0, false,
+ NativeMethods.DUPLICATE_SAME_ACCESS))
+ {
+ throw new Win32Exception();
+ }
+ }
+ finally
+ {
+ if (hTmp != null && !hTmp.IsInvalid)
+ {
+ hTmp.Close();
+ }
+ }
+ }
+ private static StringBuilder BuildCommandLine(string executableFileName, string arguments)
+ {
+ // Construct a StringBuilder with the appropriate command line
+ // to pass to CreateProcess. If the filename isn't already
+ // in quotes, we quote it here. This prevents some security
+ // problems (it specifies exactly which part of the string
+ // is the file to execute).
+ StringBuilder commandLine = new StringBuilder();
+ string fileName = executableFileName.Trim();
+ bool fileNameIsQuoted = (fileName.StartsWith("\"", StringComparison.Ordinal) && fileName.EndsWith("\"", StringComparison.Ordinal));
+ if (!fileNameIsQuoted)
+ {
+ commandLine.Append("\"");
+ }
+ commandLine.Append(fileName);
+ if (!fileNameIsQuoted)
+ {
+ commandLine.Append("\"");
+ }
+ if (!String.IsNullOrEmpty(arguments))
+ {
+ commandLine.Append(" ");
+ commandLine.Append(arguments);
+ }
+ return commandLine;
+ }
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ private bool StartWithCreateProcess(ProcessStartInfo startInfo, CreateType type, ref Win32.TokensEx.SafeTokenHandle token)
+ {
+ // See knowledge base article Q190351 for an explanation of the following code. Noteworthy tricky points:
+ // * The handles are duplicated as non-inheritable before they are passed to CreateProcess so
+ // that the child process can not close them
+ // * CreateProcess allows you to redirect all or none of the standard IO handles, so we use
+ // GetStdHandle for the handles that are not being redirected
+
+ //Cannot start a new process and store its handle if the object has been disposed, since finalization has been suppressed.
+ if (this.disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+ StringBuilder commandLine = BuildCommandLine(startInfo.FileName, startInfo.Arguments);
+ NativeMethods.STARTUPINFO startupInfo = new NativeMethods.STARTUPINFO();
+ NativeMethods.PROCESS_INFORMATION processInfo = new NativeMethods.PROCESS_INFORMATION();
+ SafeProcessHandle procSH = new SafeProcessHandle();
+ SafeThreadHandle threadSH = new SafeThreadHandle();
+ bool retVal;
+ int errorCode = 0;
+ // handles used in parent process
+ SafeFileHandle standardInputWritePipeHandle = null;
+ SafeFileHandle standardOutputReadPipeHandle = null;
+ SafeFileHandle standardErrorReadPipeHandle = null;
+ IntPtr environmentPtr = (IntPtr)0;
+ //GCHandle environmentHandle = new GCHandle();
+ lock (s_CreateProcessLock)
+ {
+ try
+ {
+ // set up the streams
+ if (startInfo.CreateNoWindow && (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError))
+ {
+ if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
+ {
+ throw new InvalidOperationException("StandardOutputEncodingNotAllowed");
+ }
+ if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
+ {
+ throw new InvalidOperationException("StandardErrorEncodingNotAllowed");
+ }
+
+ if (startInfo.RedirectStandardInput)
+ {
+ CreatePipe(out standardInputWritePipeHandle, out startupInfo.hStdInput, true);
+ }
+ else
+ {
+ startupInfo.hStdInput = new SafeFileHandle(NativeMethods.GetStdHandle(NativeMethods.STD_INPUT_HANDLE), false);
+ }
+ if (startInfo.RedirectStandardOutput)
+ {
+ CreatePipe(out standardOutputReadPipeHandle, out startupInfo.hStdOutput, false);
+ }
+ else
+ {
+ startupInfo.hStdOutput = new SafeFileHandle(NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE), false);
+ }
+ if (startInfo.RedirectStandardError)
+ {
+ CreatePipe(out standardErrorReadPipeHandle, out startupInfo.hStdError, false);
+ }
+ else
+ {
+ startupInfo.hStdError = new SafeFileHandle(NativeMethods.GetStdHandle(NativeMethods.STD_ERROR_HANDLE), false);
+ }
+ startupInfo.dwFlags = NativeMethods.STARTF_USESTDHANDLES;
+ }
+
+ // set up the creation flags paramater
+ int creationFlags = 0;
+ if (startInfo.CreateNoWindow) creationFlags |= NativeMethods.CREATE_NO_WINDOW;
+
+ // set up the environment block parameterhttps://www.beyondtrust.com/assets/documents/BeyondTrust-Microsoft-Vulnerabilities-Report-2021.pdf
+
+ //if (startInfo.environmentVariables != null)
+ if (true)
+ {
+ creationFlags |= NativeMethods.CREATE_UNICODE_ENVIRONMENT;
+ //byte[] environmentBytes = EnvironmentBlock.ToByteArray(startInfo.environmentVariables, true);
+ //environmentHandle = GCHandle.Alloc(environmentBytes, GCHandleType.Pinned);
+ //environmentPtr = environmentHandle.AddrOfPinnedObject();
+ Win32.Process.CreateEnvironmentBlock(out environmentPtr, token, false);
+ }
+
+ /*
+ if (ExpandEnvironmentVariables && startInfo.Arguments.Contains("%"))
+ {
+ Environment.ExpandEnvironmentVariables()
+ var envVars = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ IntPtr next = environmentPtr;
+ while (Marshal.ReadByte(next) != 0)
+ {
+ var str = Marshal.PtrToStringUni(next);
+ // skip first character because windows allows env vars to begin with equal sign
+ var splitPoint = str.IndexOf('=', 1);
+ var envVarName = str.Substring(0, splitPoint);
+ var envVarVal = str.Substring(splitPoint + 1);
+ envVars.Add(envVarName, envVarVal);
+ next = (IntPtr)((Int64)next + (str.Length * 2) + 2);
+ }
+ return envVars;
+ }
+ */
+
+ if (!startInfo.CreateNoWindow)
+ {
+ creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_DEFAULT_ERROR_MODE;
+ creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_NEW_CONSOLE;
+ creationFlags |= (int)Win32.Process.ProcessCreationFlags.CREATE_NEW_PROCESS_GROUP;
+ //startupInfo.lpDesktop = "Winsta0\\Default";
+ }
+
+ string workingDirectory = startInfo.WorkingDirectory;
+ if (workingDirectory == string.Empty) workingDirectory = Environment.CurrentDirectory;
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try { }
+ finally
+ {
+ retVal = false;
+ if (type == CreateType.UserToken)
+ {
+ retVal = NativeMethods.CreateProcessAsUser(token,
+ null, // we don't need this since all the info is in commandLine
+ commandLine, // pointer to the command line string
+ null, // pointer to process security attributes, we don't need to inheriat the handle
+ null, // pointer to thread security attributes
+ true, // handle inheritance flag
+ creationFlags, // creation flags
+ environmentPtr, // pointer to new environment block
+ workingDirectory, // pointer to current directory name
+ startupInfo, // pointer to STARTUPINFO
+ processInfo // pointer to PROCESS_INFORMATION
+ );
+ } else if (type == CreateType.RawToken)
+ {
+ retVal = NativeMethods.CreateProcessWithToken(token,
+ NativeMethods.LogonFlags.LOGON_WITH_PROFILE,
+ null, // we don't need this since all the info is in commandLine
+ commandLine, // pointer to the command line string
+ creationFlags, // creation flags
+ environmentPtr, // pointer to new environment block
+ workingDirectory, // pointer to current directory name
+ startupInfo, // pointer to STARTUPINFO
+ processInfo // pointer to PROCESS_INFORMATION
+ );
+ }
+ /*
+ retVal = NativeMethods.CreateProcess(null, // we don't need this since all the info is in commandLine
+ commandLine, // pointer to the command line string
+ null, // pointer to process security attributes, we don't need to inheriat the handle
+ null, // pointer to thread security attributes
+ true, // handle inheritance flag
+ creationFlags, // creation flags
+ environmentPtr, // pointer to new environment block
+ workingDirectory, // pointer to current directory name
+ startupInfo, // pointer to STARTUPINFO
+ processInfo // pointer to PROCESS_INFORMATION
+ );
+ */
+ if (!retVal) errorCode = Marshal.GetLastWin32Error();
+ if (processInfo.hProcess != (IntPtr)0 && processInfo.hProcess != (IntPtr)NativeMethods.INVALID_HANDLE_VALUE) procSH.InitialSetHandle(processInfo.hProcess);
+ if (processInfo.hThread != (IntPtr)0 && processInfo.hThread != (IntPtr)NativeMethods.INVALID_HANDLE_VALUE) threadSH.InitialSetHandle(processInfo.hThread);
+ }
+ if (!retVal)
+ {
+ if (errorCode == NativeMethods.ERROR_BAD_EXE_FORMAT || errorCode == NativeMethods.ERROR_EXE_MACHINE_TYPE_MISMATCH)
+ {
+ throw new Win32Exception(errorCode, "InvalidApplication");
+ }
+ throw new Win32Exception(errorCode);
+ }
+ }
+ finally
+ {
+ // free environment block
+ //if (environmentHandle.IsAllocated)
+ //{
+ // environmentHandle.Free();
+ //}
+ Win32.Process.DestroyEnvironmentBlock(environmentPtr);
+ startupInfo.Dispose();
+ }
+ }
+ if (startInfo.RedirectStandardInput)
+ {
+ standardInput = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), Console.InputEncoding, 4096);
+ standardInput.AutoFlush = true;
+ }
+ if (startInfo.RedirectStandardOutput)
+ {
+ Encoding enc = (startInfo.StandardOutputEncoding != null) ? startInfo.StandardOutputEncoding : Console.OutputEncoding;
+ standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
+ }
+ if (startInfo.RedirectStandardError)
+ {
+ Encoding enc = (startInfo.StandardErrorEncoding != null) ? startInfo.StandardErrorEncoding : Console.OutputEncoding;
+ standardError = new StreamReader(new FileStream(standardErrorReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
+ }
+ bool ret = false;
+ if (!procSH.IsInvalid)
+ {
+ SetProcessHandle(procSH);
+ SetProcessId(processInfo.dwProcessId);
+ threadSH.Close();
+ ret = true;
+ }
+ return ret;
+ }
+
+/*
+ private static string ExpandEnvironmentVariables(string name, Dictionary environment)
+ {
+
+ switch (name)
+ {
+ case null:
+ case "":
+ return name;
+ default:
+ environment = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ {"SuSSY", "gussys"},
+ {"AMoGus", "gussys"},
+ {"Sussex", "gussys"}
+ };
+
+ StringBuilder result = new StringBuilder();
+
+ int index = name.IndexOf('%');
+ if (index > 0)
+ result.Append(name.Substring(index));
+
+ int lastValidIndex = -1;
+ while (index != -1)
+ {
+ lastValidIndex = index;
+
+ var end = name.IndexOf('%', index + 1);
+ if (end == -1)
+ {
+ result.Append('%');
+ break;
+ }
+
+ // Double % escape
+ if (end == index + 1)
+ {
+ index = name.IndexOf('%', index + 2);
+ result.Append(index == -1 ? "%" : "%" + name.Substring(end + 1, index - (end + 1)));
+ continue;
+ }
+
+ lastValidIndex = end;
+
+ if (environment.TryGetValue(name.Substring(index + 1, end - (index + 1)), out string varValue))
+ result.Append(varValue);
+
+ index = name.IndexOf('%', end + 1);
+
+ if (index != -1)
+ {
+ result.Append(name.Substring(end + 1, index - (end + 1)));
+ }
+ }
+
+ result.Append(lastValidIndex != -1 ? name.Substring(index + 1, name.Length - (index + 1)) : name);
+
+ return result.ToString();
+ }
+
+ }
+ */
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ private bool StartWithShellExecuteEx(ProcessStartInfo startInfo)
+ {
+ //Cannot start a new process and store its handle if the object has been disposed, since finalization has been suppressed.
+ if (this.disposed) throw new ObjectDisposedException(GetType().Name);
+ if (!String.IsNullOrEmpty(startInfo.UserName) || (startInfo.Password != null))
+ {
+ throw new InvalidOperationException("CantStartAsUser");
+ }
+ if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
+ {
+ throw new InvalidOperationException("CantRedirectStreams");
+ }
+ if (startInfo.StandardErrorEncoding != null)
+ {
+ throw new InvalidOperationException("StandardErrorEncodingNotAllowed");
+ }
+ if (startInfo.StandardOutputEncoding != null)
+ {
+ throw new InvalidOperationException("StandardOutputEncodingNotAllowed");
+ }
+
+ // can't set env vars with ShellExecuteEx...
+ if (startInfo.environmentVariables != null)
+ {
+ throw new InvalidOperationException("CantUseEnvVars");
+ }
+ NativeMethods.ShellExecuteInfo shellExecuteInfo = new NativeMethods.ShellExecuteInfo();
+ shellExecuteInfo.fMask = NativeMethods.SEE_MASK_NOCLOSEPROCESS;
+ if (startInfo.ErrorDialog)
+ {
+ shellExecuteInfo.hwnd = startInfo.ErrorDialogParentHandle;
+ }
+ else
+ {
+ shellExecuteInfo.fMask |= NativeMethods.SEE_MASK_FLAG_NO_UI;
+ }
+ switch (startInfo.WindowStyle)
+ {
+ case ProcessWindowStyle.Hidden:
+ shellExecuteInfo.nShow = NativeMethods.SW_HIDE;
+ break;
+ case ProcessWindowStyle.Minimized:
+ shellExecuteInfo.nShow = NativeMethods.SW_SHOWMINIMIZED;
+ break;
+ case ProcessWindowStyle.Maximized:
+ shellExecuteInfo.nShow = NativeMethods.SW_SHOWMAXIMIZED;
+ break;
+ default:
+ shellExecuteInfo.nShow = NativeMethods.SW_SHOWNORMAL;
+ break;
+ }
+ try
+ {
+ if (startInfo.FileName.Length != 0) shellExecuteInfo.lpFile = Marshal.StringToHGlobalAuto(startInfo.FileName);
+ if (startInfo.Verb.Length != 0) shellExecuteInfo.lpVerb = Marshal.StringToHGlobalAuto(startInfo.Verb);
+ if (startInfo.Arguments.Length != 0) shellExecuteInfo.lpParameters = Marshal.StringToHGlobalAuto(startInfo.Arguments);
+ if (startInfo.WorkingDirectory.Length != 0) shellExecuteInfo.lpDirectory = Marshal.StringToHGlobalAuto(startInfo.WorkingDirectory);
+ shellExecuteInfo.fMask |= NativeMethods.SEE_MASK_FLAG_DDEWAIT;
+ ShellExecuteHelper executeHelper = new ShellExecuteHelper(shellExecuteInfo);
+ if (!executeHelper.ShellExecuteOnSTAThread())
+ {
+ int error = executeHelper.ErrorCode;
+ if (error == 0)
+ {
+ switch ((long)shellExecuteInfo.hInstApp)
+ {
+ case NativeMethods.SE_ERR_FNF:
+ error = NativeMethods.ERROR_FILE_NOT_FOUND;
+ break;
+ case NativeMethods.SE_ERR_PNF:
+ error = NativeMethods.ERROR_PATH_NOT_FOUND;
+ break;
+ case NativeMethods.SE_ERR_ACCESSDENIED:
+ error = NativeMethods.ERROR_ACCESS_DENIED;
+ break;
+ case NativeMethods.SE_ERR_OOM:
+ error = NativeMethods.ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ case NativeMethods.SE_ERR_DDEFAIL:
+ case NativeMethods.SE_ERR_DDEBUSY:
+ case NativeMethods.SE_ERR_DDETIMEOUT:
+ error = NativeMethods.ERROR_DDE_FAIL;
+ break;
+ case NativeMethods.SE_ERR_SHARE:
+ error = NativeMethods.ERROR_SHARING_VIOLATION;
+ break;
+ case NativeMethods.SE_ERR_NOASSOC:
+ error = NativeMethods.ERROR_NO_ASSOCIATION;
+ break;
+ case NativeMethods.SE_ERR_DLLNOTFOUND:
+ error = NativeMethods.ERROR_DLL_NOT_FOUND;
+ break;
+ default:
+ error = (int)shellExecuteInfo.hInstApp;
+ break;
+ }
+ }
+ if (error == NativeMethods.ERROR_BAD_EXE_FORMAT || error == NativeMethods.ERROR_EXE_MACHINE_TYPE_MISMATCH)
+ {
+ throw new Win32Exception(error, "InvalidApplication");
+ }
+ throw new Win32Exception(error);
+ }
+ }
+ finally
+ {
+ if (shellExecuteInfo.lpFile != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpFile);
+ if (shellExecuteInfo.lpVerb != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpVerb);
+ if (shellExecuteInfo.lpParameters != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpParameters);
+ if (shellExecuteInfo.lpDirectory != (IntPtr)0) Marshal.FreeHGlobal(shellExecuteInfo.lpDirectory);
+ }
+ if (shellExecuteInfo.hProcess != (IntPtr)0)
+ {
+ SafeProcessHandle handle = new SafeProcessHandle(shellExecuteInfo.hProcess);
+ SetProcessHandle(handle);
+ return true;
+ }
+ return false;
+ }
+ ///
+ ///
+ /// Starts a process resource specified by the process start
+ /// information passed in, for example the file name of the process to start.
+ /// Associates the process resource with a new
+ /// component.
+ ///
+ ///
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ public static Process Start(CreateType type, ProcessStartInfo startInfo, Win32.TokensEx.SafeTokenHandle token)
+ {
+ Process process = new Process();
+ if (startInfo == null) throw new ArgumentNullException("startInfo");
+ process.StartInfo = startInfo;
+ if (process.Start(type, ref token))
+ {
+ return process;
+ }
+ return null;
+ }
+ ///
+ ///
+ /// Stops the
+ /// associated process immediately.
+ ///
+ ///
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ public void Kill()
+ {
+ SafeProcessHandle handle = null;
+ try
+ {
+ handle = GetProcessHandle(NativeMethods.PROCESS_TERMINATE);
+ if (!NativeMethods.TerminateProcess(handle, -1)) throw new Win32Exception();
+ }
+ finally
+ {
+ ReleaseProcessHandle(handle);
+ }
+ }
+ ///
+ /// Make sure we are not watching for process exit.
+ ///
+ ///
+ void StopWatchingForExit()
+ {
+ if (watchingForExit)
+ {
+ lock (this)
+ {
+ if (watchingForExit)
+ {
+ watchingForExit = false;
+ registeredWaitHandle.Unregister(null);
+ waitHandle.Close();
+ waitHandle = null;
+ registeredWaitHandle = null;
+ }
+ }
+ }
+ }
+ ///
+ ///
+ /// Instructs the component to wait the specified number of milliseconds for the associated process to exit.
+ ///
+ ///
+ public bool WaitForExit(int milliseconds)
+ {
+ SafeProcessHandle handle = null;
+ bool exited;
+ ProcessWaitHandle processWaitHandle = null;
+ try
+ {
+ handle = GetProcessHandle(NativeMethods.SYNCHRONIZE, false);
+ if (handle.IsInvalid)
+ {
+ exited = true;
+ }
+ else
+ {
+ processWaitHandle = new ProcessWaitHandle(handle);
+ if (processWaitHandle.WaitOne(milliseconds, false))
+ {
+ exited = true;
+ signaled = true;
+ }
+ else
+ {
+ exited = false;
+ signaled = false;
+ }
+ }
+ }
+ finally
+ {
+ if (processWaitHandle != null)
+ {
+ processWaitHandle.Close();
+ }
+
+ // If we have a hard timeout, we cannot wait for the streams
+ if (output != null && milliseconds == -1)
+ {
+ output.WaitUtilEOF();
+ }
+ if (error != null && milliseconds == -1)
+ {
+ error.WaitUtilEOF();
+ }
+ ReleaseProcessHandle(handle);
+ }
+ if (exited && watchForExit)
+ {
+ RaiseOnExited();
+ }
+ return exited;
+ }
+ ///
+ ///
+ /// Instructs the component to wait
+ /// indefinitely for the associated process to exit.
+ ///
+ ///
+ public void WaitForExit()
+ {
+ WaitForExit(-1);
+ }
+ ///
+ ///
+ /// Causes the component to wait the
+ /// specified number of milliseconds for the associated process to enter an
+ /// idle state.
+ /// This is only applicable for processes with a user interface,
+ /// therefore a message loop.
+ ///
+ ///
+ public bool WaitForInputIdle(int milliseconds)
+ {
+ SafeProcessHandle handle = null;
+ bool idle;
+ try
+ {
+ handle = GetProcessHandle(NativeMethods.SYNCHRONIZE | NativeMethods.PROCESS_QUERY_INFORMATION);
+ int ret = NativeMethods.WaitForInputIdle(handle, milliseconds);
+ switch (ret)
+ {
+ case NativeMethods.WAIT_OBJECT_0:
+ idle = true;
+ break;
+ case NativeMethods.WAIT_TIMEOUT:
+ idle = false;
+ break;
+ case NativeMethods.WAIT_FAILED:
+ default:
+ throw new InvalidOperationException("InputIdleUnkownError");
+ }
+ }
+ finally
+ {
+ ReleaseProcessHandle(handle);
+ }
+ return idle;
+ }
+ ///
+ ///
+ /// Instructs the component to wait
+ /// indefinitely for the associated process to enter an idle state. This
+ /// is only applicable for processes with a user interface, therefore a message loop.
+ ///
+ ///
+ public bool WaitForInputIdle()
+ {
+ return WaitForInputIdle(Int32.MaxValue);
+ }
+
+ // Support for working asynchronously with streams
+ ///
+ ///
+ /// Instructs the component to start
+ /// reading the StandardOutput stream asynchronously. The user can register a callback
+ /// that will be called when a line of data terminated by \n,\r or \r\n is reached, or the end of stream is reached
+ /// then the remaining information is returned. The user can add an event handler to OutputDataReceived.
+ ///
+ ///
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public void BeginOutputReadLine()
+ {
+ if (outputStreamReadMode == StreamReadMode.undefined)
+ {
+ outputStreamReadMode = StreamReadMode.asyncMode;
+ }
+ else if (outputStreamReadMode != StreamReadMode.asyncMode)
+ {
+ throw new InvalidOperationException("CantMixSyncAsyncOperation");
+ }
+ if (pendingOutputRead) throw new InvalidOperationException("PendingAsyncOperation");
+ pendingOutputRead = true;
+ // We can't detect if there's a pending sychronous read, tream also doesn't.
+ if (output == null)
+ {
+ if (standardOutput == null)
+ {
+ throw new InvalidOperationException("CantGetStandardOut");
+ }
+ Stream s = standardOutput.BaseStream;
+ output = new AsyncStreamReader(this, s, new UserCallBack(this.OutputReadNotifyUser), standardOutput.CurrentEncoding);
+ }
+ output.BeginReadLine();
+ }
+ ///
+ ///
+ /// Instructs the component to start
+ /// reading the StandardError stream asynchronously. The user can register a callback
+ /// that will be called when a line of data terminated by \n,\r or \r\n is reached, or the end of stream is reached
+ /// then the remaining information is returned. The user can add an event handler to ErrorDataReceived.
+ ///
+ ///
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public void BeginErrorReadLine()
+ {
+ if (errorStreamReadMode == StreamReadMode.undefined)
+ {
+ errorStreamReadMode = StreamReadMode.asyncMode;
+ }
+ else if (errorStreamReadMode != StreamReadMode.asyncMode)
+ {
+ throw new InvalidOperationException("CantMixSyncAsyncOperation");
+ }
+ if (pendingErrorRead)
+ {
+ throw new InvalidOperationException("PendingAsyncOperation");
+ }
+ pendingErrorRead = true;
+ // We can't detect if there's a pending sychronous read, stream also doesn't.
+ if (error == null)
+ {
+ if (standardError == null)
+ {
+ throw new InvalidOperationException("CantGetStandardError");
+ }
+ Stream s = standardError.BaseStream;
+ error = new AsyncStreamReader(this, s, new UserCallBack(this.ErrorReadNotifyUser), standardError.CurrentEncoding);
+ }
+ error.BeginReadLine();
+ }
+ ///
+ ///
+ /// Instructs the component to cancel the asynchronous operation
+ /// specified by BeginOutputReadLine().
+ ///
+ ///
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public void CancelOutputRead()
+ {
+ if (output != null)
+ {
+ output.CancelOperation();
+ }
+ else
+ {
+ throw new InvalidOperationException("NoAsyncOperation");
+ }
+ pendingOutputRead = false;
+ }
+ ///
+ ///
+ /// Instructs the component to cancel the asynchronous operation
+ /// specified by BeginErrorReadLine().
+ ///
+ ///
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public void CancelErrorRead()
+ {
+ if (error != null)
+ {
+ error.CancelOperation();
+ }
+ else
+ {
+ throw new InvalidOperationException("No async operation.");
+ }
+ pendingErrorRead = false;
+ }
+ internal void OutputReadNotifyUser(String data)
+ {
+ // To avoid ---- between remove handler and raising the event
+ DataReceivedEventHandler outputDataReceived = OutputDataReceived;
+ if (outputDataReceived != null)
+ {
+ DataReceivedEventArgs e = new DataReceivedEventArgs(data);
+ if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
+ {
+ SynchronizingObject.Invoke(outputDataReceived, new object[]
+ {
+ this, e
+ });
+ }
+ else
+ {
+ outputDataReceived(this, e); // Call back to user informing data is available.
+ }
+ }
+ }
+ internal void ErrorReadNotifyUser(String data)
+ {
+ // To avoid ---- between remove handler and raising the event
+ DataReceivedEventHandler errorDataReceived = ErrorDataReceived;
+ if (errorDataReceived != null)
+ {
+ DataReceivedEventArgs e = new DataReceivedEventArgs(data);
+ if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
+ {
+ SynchronizingObject.Invoke(errorDataReceived, new object[]
+ {
+ this, e
+ });
+ }
+ else
+ {
+ errorDataReceived(this, e); // Call back to user informing data is available.
+ }
+ }
+ }
+
+ ///
+ /// A desired internal state.
+ ///
+ ///
+ enum State
+ {
+ HaveId = 0x1,
+ IsLocal = 0x2,
+ IsNt = 0x4,
+ HaveProcessInfo = 0x8,
+ Exited = 0x10,
+ Associated = 0x20,
+ IsWin2k = 0x40,
+ HaveNtProcessInfo = HaveProcessInfo | IsNt
+ }
+ }
+
+ ///
+ /// This data structure contains information about a process that is collected
+ /// in bulk by querying the operating system. The reason to make this a separate
+ /// structure from the process component is so that we can throw it away all at once
+ /// when Refresh is called on the component.
+ ///
+ ///
+ internal class ProcessInfo
+ {
+ public ArrayList threadInfoList = new ArrayList();
+ public int basePriority;
+ public string processName;
+ public int processId;
+ public int handleCount;
+ public long poolPagedBytes;
+ public long poolNonpagedBytes;
+ public long virtualBytes;
+ public long virtualBytesPeak;
+ public long workingSetPeak;
+ public long workingSet;
+ public long pageFileBytesPeak;
+ public long pageFileBytes;
+ public long privateBytes;
+ public int mainModuleId; // used only for win9x - id is only for use with CreateToolHelp32
+ public int sessionId;
+ }
+
+ ///
+ /// This data structure contains information about a thread in a process that
+ /// is collected in bulk by querying the operating system. The reason to
+ /// make this a separate structure from the ProcessThread component is so that we
+ /// can throw it away all at once when Refresh is called on the component.
+ ///
+ ///
+ internal class ThreadInfo
+ {
+ public int threadId;
+ public int processId;
+ public int basePriority;
+ public int currentPriority;
+ public IntPtr startAddress;
+ public System.Diagnostics.ThreadState threadState;
+ public ThreadWaitReason threadWaitReason;
+ }
+
+ ///
+ /// This data structure contains information about a module in a process that
+ /// is collected in bulk by querying the operating system. The reason to
+ /// make this a separate structure from the ProcessModule component is so that we
+ /// can throw it away all at once when Refresh is called on the component.
+ ///
+ ///
+ internal class ModuleInfo
+ {
+ public string baseName;
+ public string fileName;
+ public IntPtr baseOfDll;
+ public IntPtr entryPoint;
+ public int sizeOfImage;
+ public int Id; // used only on win9x - for matching up with ProcessInfo.mainModuleId
+ }
+
+ internal static class EnvironmentBlock
+ {
+ public static byte[] ToByteArray(StringDictionary sd, bool unicode)
+ {
+ // get the keys
+ string[] keys = new string[sd.Count];
+ byte[] envBlock = null;
+ sd.Keys.CopyTo(keys, 0);
+
+ // get the values
+ string[] values = new string[sd.Count];
+ sd.Values.CopyTo(values, 0);
+
+ // sort both by the keys
+ // Windows 2000 requires the environment block to be sorted by the key
+ // It will first converting the case the strings and do ordinal comparison.
+ Array.Sort(keys, values, OrdinalCaseInsensitiveComparer.Default);
+
+ // create a list of null terminated "key=val" strings
+ StringBuilder stringBuff = new StringBuilder();
+ for (int i = 0; i < sd.Count; ++i)
+ {
+ stringBuff.Append(keys[i]);
+ stringBuff.Append('=');
+ stringBuff.Append(values[i]);
+ stringBuff.Append('\0');
+ }
+ // an extra null at the end indicates end of list.
+ stringBuff.Append('\0');
+ if (unicode)
+ {
+ envBlock = Encoding.Unicode.GetBytes(stringBuff.ToString());
+ }
+ else
+ {
+ envBlock = Encoding.Default.GetBytes(stringBuff.ToString());
+ if (envBlock.Length > UInt16.MaxValue) throw new InvalidOperationException("Environment block is too long.");
+ }
+ return envBlock;
+ }
+ }
+
+ internal class OrdinalCaseInsensitiveComparer : IComparer
+ {
+ internal static readonly OrdinalCaseInsensitiveComparer Default = new OrdinalCaseInsensitiveComparer();
+ public int Compare(Object a, Object b)
+ {
+ String sa = a as String;
+ String sb = b as String;
+ if (sa != null && sb != null)
+ {
+ return String.Compare(sa, sb, StringComparison.OrdinalIgnoreCase);
+ }
+ return Comparer.Default.Compare(a, b);
+ }
+ }
+
+ internal class ShellExecuteHelper
+ {
+ private NativeMethods.ShellExecuteInfo _executeInfo;
+ private int _errorCode;
+ private bool _succeeded;
+ public ShellExecuteHelper(NativeMethods.ShellExecuteInfo executeInfo)
+ {
+ _executeInfo = executeInfo;
+ }
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ public void ShellExecuteFunction()
+ {
+ if (!(_succeeded = NativeMethods.ShellExecuteEx(_executeInfo)))
+ {
+ _errorCode = Marshal.GetLastWin32Error();
+ }
+ }
+ public bool ShellExecuteOnSTAThread()
+ {
+ //
+ // SHELL API ShellExecute() requires STA in order to work correctly.
+ // If current thread is not a STA thread, we need to call ShellExecute on a new thread.
+ //
+ if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
+ {
+ ThreadStart threadStart = new ThreadStart(this.ShellExecuteFunction);
+ Thread executionThread = new Thread(threadStart);
+ executionThread.SetApartmentState(ApartmentState.STA);
+ executionThread.Start();
+ executionThread.Join();
+ }
+ else
+ {
+ ShellExecuteFunction();
+ }
+ return _succeeded;
+ }
+ public int ErrorCode
+ {
+ get
+ {
+ return _errorCode;
+ }
+ }
+ }
+
+ private static long[] CachedBuffer;
+
+ private static int GetNewBufferSize(int existingBufferSize, int requiredSize)
+ {
+ if (requiredSize == 0)
+ {
+ int num = existingBufferSize * 2;
+ return num >= existingBufferSize ? num : throw new OutOfMemoryException();
+ }
+ int num1 = requiredSize + 10240;
+ return num1 >= requiredSize ? num1 : throw new OutOfMemoryException();
+ }
+
+ internal static ProcessInfo GetProcessInfo(int processId, string machineName)
+ {
+ ProcessInfo[] processInfos = GetProcessInfos((Predicate)(pid => pid == processId));
+ if (processInfos.Length == 1) return processInfos[0];
+ return (ProcessInfo)null;
+ }
+ internal static ProcessInfo[] GetProcessInfos(Predicate processIdFilter = null)
+ {
+ int returnedSize = 0;
+ GCHandle gcHandle = new GCHandle();
+ int num = 131072;
+ long[] numArray = Interlocked.Exchange(ref CachedBuffer, (long[])null);
+ try
+ {
+ int error;
+ do
+ {
+ if (numArray == null) numArray = new long[(num + 7) / 8];
+ else num = numArray.Length * 8;
+ gcHandle = GCHandle.Alloc((object)numArray, GCHandleType.Pinned);
+ error = NativeMethods.NtQuerySystemInformation(5, gcHandle.AddrOfPinnedObject(), num, out returnedSize);
+ if (error == -1073741820)
+ {
+ if (gcHandle.IsAllocated) gcHandle.Free();
+ numArray = (long[])null;
+ num = GetNewBufferSize(num, returnedSize);
+ }
+ } while (error == -1073741820);
+ if (error < 0) throw new InvalidOperationException("CouldntGetProcessInfos", (Exception)new Win32Exception(error));
+ return GetProcessInfos(gcHandle.AddrOfPinnedObject(), processIdFilter);
+ }
+ finally
+ {
+ Interlocked.Exchange(ref CachedBuffer, numArray);
+ if (gcHandle.IsAllocated) gcHandle.Free();
+ }
+ }
+ private static ProcessInfo[] GetProcessInfos(IntPtr dataPtr, Predicate processIdFilter)
+ {
+ Hashtable hashtable = new Hashtable(60);
+ long num = 0;
+ while (true)
+ {
+ IntPtr ptr1 = (IntPtr)((long)dataPtr + num);
+ NativeMethods.SystemProcessInformation structure1 = new NativeMethods.SystemProcessInformation();
+ Marshal.PtrToStructure(ptr1, (object)structure1);
+ int int32 = structure1.UniqueProcessId.ToInt32();
+ if (processIdFilter == null || processIdFilter(int32))
+ {
+ ProcessInfo processInfo = new ProcessInfo();
+ processInfo.processId = int32;
+ processInfo.handleCount = (int)structure1.HandleCount;
+ processInfo.sessionId = (int)structure1.SessionId;
+ processInfo.poolPagedBytes = (long)(ulong)structure1.QuotaPagedPoolUsage;
+ processInfo.poolNonpagedBytes = (long)(ulong)structure1.QuotaNonPagedPoolUsage;
+ processInfo.virtualBytes = (long)(ulong)structure1.VirtualSize;
+ processInfo.virtualBytesPeak = (long)(ulong)structure1.PeakVirtualSize;
+ processInfo.workingSetPeak = (long)(ulong)structure1.PeakWorkingSetSize;
+ processInfo.workingSet = (long)(ulong)structure1.WorkingSetSize;
+ processInfo.pageFileBytesPeak = (long)(ulong)structure1.PeakPagefileUsage;
+ processInfo.pageFileBytes = (long)(ulong)structure1.PagefileUsage;
+ processInfo.privateBytes = (long)(ulong)structure1.PrivatePageCount;
+ processInfo.basePriority = structure1.BasePriority;
+ if (structure1.NamePtr == IntPtr.Zero)
+ {
+ processInfo.processName = processInfo.processId != 4 ?
+ (processInfo.processId != 0 ? processInfo.processId.ToString((IFormatProvider)CultureInfo.InvariantCulture) : "Idle") : "System";
+ }
+ else
+ {
+ string str = GetProcessShortName(Marshal.PtrToStringUni(structure1.NamePtr, (int)structure1.NameLength / 2));
+ processInfo.processName = str;
+ }
+ hashtable[(object)processInfo.processId] = (object)processInfo;
+ IntPtr ptr2 = (IntPtr)((long)ptr1 + (long)Marshal.SizeOf((object)structure1));
+ for (int index = 0; (long)index < (long)structure1.NumberOfThreads; ++index)
+ {
+ NativeMethods.SystemThreadInformation structure2 = new NativeMethods.SystemThreadInformation();
+ Marshal.PtrToStructure(ptr2, (object)structure2);
+ processInfo.threadInfoList.Add((object)new ThreadInfo()
+ {
+ processId = (int)structure2.UniqueProcess,
+ threadId = (int)structure2.UniqueThread,
+ basePriority = structure2.BasePriority,
+ currentPriority = structure2.Priority,
+ startAddress = structure2.StartAddress,
+ threadState = (System.Diagnostics.ThreadState)structure2.ThreadState,
+ threadWaitReason = GetThreadWaitReason((int)structure2.WaitReason)
+ });
+ ptr2 = (IntPtr)((long)ptr2 + (long)Marshal.SizeOf((object)structure2));
+ }
+ }
+ if (structure1.NextEntryOffset != 0U) num += (long)structure1.NextEntryOffset;
+ else break;
+ }
+ ProcessInfo[] processInfos = new ProcessInfo[hashtable.Values.Count];
+ hashtable.Values.CopyTo((Array)processInfos, 0);
+ return processInfos;
+ }
+
+ internal static ThreadWaitReason GetThreadWaitReason(int value)
+ {
+ switch (value)
+ {
+ case 0:
+ case 7:
+ return ThreadWaitReason.Executive;
+ case 1:
+ case 8:
+ return ThreadWaitReason.FreePage;
+ case 2:
+ case 9:
+ return ThreadWaitReason.PageIn;
+ case 3:
+ case 10:
+ return ThreadWaitReason.SystemAllocation;
+ case 4:
+ case 11:
+ return ThreadWaitReason.ExecutionDelay;
+ case 5:
+ case 12:
+ return ThreadWaitReason.Suspended;
+ case 6:
+ case 13:
+ return ThreadWaitReason.UserRequest;
+ case 14:
+ return ThreadWaitReason.EventPairHigh;
+ case 15:
+ return ThreadWaitReason.EventPairLow;
+ case 16:
+ return ThreadWaitReason.LpcReceive;
+ case 17:
+ return ThreadWaitReason.LpcReply;
+ case 18:
+ return ThreadWaitReason.VirtualMemory;
+ case 19:
+ return ThreadWaitReason.PageOut;
+ default:
+ return ThreadWaitReason.Unknown;
+ }
+ }
+
+ internal static string GetProcessShortName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ return string.Empty;
+ int num1 = -1;
+ int startIndex1 = -1;
+ for (int index = 0; index < name.Length; ++index)
+ {
+ if (name[index] == '\\')
+ num1 = index;
+ else if (name[index] == '.')
+ startIndex1 = index;
+ }
+ int num2 = startIndex1 != -1 ? (!string.Equals(".exe", name.Substring(startIndex1), StringComparison.OrdinalIgnoreCase) ? name.Length - 1 : startIndex1 - 1) : name.Length - 1;
+ int startIndex2 = num1 != -1 ? num1 + 1 : 0;
+ return name.Substring(startIndex2, num2 - startIndex2 + 1);
+ }
+
+ [HostProtection(MayLeakOnAbort = true)]
+ internal static class NativeMethods
+ {
+ public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+ public const int STARTF_USESTDHANDLES = 0x00000100;
+ public const int STD_INPUT_HANDLE = -10;
+ public const int STD_OUTPUT_HANDLE = -11;
+ public const int STD_ERROR_HANDLE = -12;
+ public const int STILL_ACTIVE = 0x00000103;
+ public const int SW_HIDE = 0;
+ public const int WAIT_OBJECT_0 = 0x00000000;
+ public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF);
+ public const int WAIT_TIMEOUT = 0x00000102;
+ public const int WAIT_ABANDONED = 0x00000080;
+ public const int ERROR_BAD_EXE_FORMAT = 193;
+ public const int ERROR_EXE_MACHINE_TYPE_MISMATCH = 216;
+
+ [DllImport("ntdll.dll", CharSet = CharSet.Auto)]
+ public static extern int NtQuerySystemInformation(
+ int query,
+ IntPtr dataPtr,
+ int size,
+ out int returnedSize);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class SystemProcessInformation
+ {
+ internal uint NextEntryOffset;
+ internal uint NumberOfThreads;
+ private long SpareLi1;
+ private long SpareLi2;
+ private long SpareLi3;
+ private long CreateTime;
+ private long UserTime;
+ private long KernelTime;
+ internal ushort NameLength;
+ internal ushort MaximumNameLength;
+ internal IntPtr NamePtr;
+ internal int BasePriority;
+ internal IntPtr UniqueProcessId;
+ internal IntPtr InheritedFromUniqueProcessId;
+ internal uint HandleCount;
+ internal uint SessionId;
+ internal UIntPtr PageDirectoryBase;
+ internal UIntPtr PeakVirtualSize;
+ internal UIntPtr VirtualSize;
+ internal uint PageFaultCount;
+ internal UIntPtr PeakWorkingSetSize;
+ internal UIntPtr WorkingSetSize;
+ internal UIntPtr QuotaPeakPagedPoolUsage;
+ internal UIntPtr QuotaPagedPoolUsage;
+ internal UIntPtr QuotaPeakNonPagedPoolUsage;
+ internal UIntPtr QuotaNonPagedPoolUsage;
+ internal UIntPtr PagefileUsage;
+ internal UIntPtr PeakPagefileUsage;
+ internal UIntPtr PrivatePageCount;
+ private long ReadOperationCount;
+ private long WriteOperationCount;
+ private long OtherOperationCount;
+ private long ReadTransferCount;
+ private long WriteTransferCount;
+ private long OtherTransferCount;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class SystemThreadInformation
+ {
+ private long KernelTime;
+ private long UserTime;
+ private long CreateTime;
+ private uint WaitTime;
+ internal IntPtr StartAddress;
+ internal IntPtr UniqueProcess;
+ internal IntPtr UniqueThread;
+ internal int Priority;
+ internal int BasePriority;
+ internal uint ContextSwitches;
+ internal uint ThreadState;
+ internal uint WaitReason;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class STARTUPINFO
+ {
+ public int cb;
+ public IntPtr lpReserved = IntPtr.Zero;
+ public string lpDesktop = null;
+ public IntPtr lpTitle = IntPtr.Zero;
+ public int dwX = 0;
+ public int dwY = 0;
+ public int dwXSize = 0;
+ public int dwYSize = 0;
+ public int dwXCountChars = 0;
+ public int dwYCountChars = 0;
+ public int dwFillAttribute = 0;
+ public int dwFlags;
+ public short wShowWindow = 0;
+ public short cbReserved2 = 0;
+ public IntPtr lpReserved2 = IntPtr.Zero;
+ public SafeFileHandle hStdInput = new SafeFileHandle(IntPtr.Zero, false);
+ public SafeFileHandle hStdOutput = new SafeFileHandle(IntPtr.Zero, false);
+ public SafeFileHandle hStdError = new SafeFileHandle(IntPtr.Zero, false);
+ public STARTUPINFO()
+ {
+ cb = Marshal.SizeOf(this);
+ }
+ public void Dispose()
+ {
+ // close the handles created for child process
+ if (hStdInput != null && !hStdInput.IsInvalid)
+ {
+ hStdInput.Close();
+ hStdInput = null;
+ }
+ if (hStdOutput != null && !hStdOutput.IsInvalid)
+ {
+ hStdOutput.Close();
+ hStdOutput = null;
+ }
+ if (hStdError != null && !hStdError.IsInvalid)
+ {
+ hStdError.Close();
+ hStdError = null;
+ }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class SECURITY_ATTRIBUTES
+ {
+ public int nLength = 12;
+ public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
+ public bool bInheritHandle;
+ }
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern bool GetExitCodeProcess(SafeProcessHandle processHandle, out int exitCode);
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern bool GetProcessTimes(SafeProcessHandle handle, out long creation, out long exit, out long kernel, out long user);
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Process)]
+ public static extern IntPtr GetStdHandle(int whichHandle);
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Process)]
+ public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [ResourceExposure(ResourceScope.Process)]
+ public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, // LPCTSTR
+ StringBuilder lpCommandLine, // LPTSTR - note: CreateProcess might insert a null somewhere in this string
+ SECURITY_ATTRIBUTES lpProcessAttributes, // LPSECURITY_ATTRIBUTES
+ SECURITY_ATTRIBUTES lpThreadAttributes, // LPSECURITY_ATTRIBUTES
+ bool bInheritHandles, // BOOL
+ int dwCreationFlags, // DWORD
+ IntPtr lpEnvironment, // LPVOID
+ [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, // LPCTSTR
+ STARTUPINFO lpStartupInfo, // LPSTARTUPINFO
+ PROCESS_INFORMATION lpProcessInformation // LPPROCESS_INFORMATION
+ );
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Machine)]
+ public static extern bool TerminateProcess(SafeProcessHandle processHandle, int exitCode);
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Process)]
+ public static extern IntPtr GetCurrentProcess();
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [SuppressUnmanagedCodeSecurityAttribute]
+ [ResourceExposure(ResourceScope.Machine)]
+ public static extern bool CreateProcessAsUser(Win32.TokensEx.SafeTokenHandle hToken, string lpApplicationName, StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes,
+ bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation);
+
+ [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Auto)]
+ internal static extern bool CreateProcessWithToken(
+ Win32.TokensEx.SafeTokenHandle hToken,
+ LogonFlags dwLogonFlags,
+ string lpApplicationName,
+ StringBuilder lpCommandLine,
+ int dwCreationFlags,
+ IntPtr lpEnvironment,
+ string lpCurrentDirectory,
+ STARTUPINFO lpStartupInfo,
+ PROCESS_INFORMATION lpProcessInformation);
+
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true, BestFitMapping = false)]
+ [ResourceExposure(ResourceScope.Machine)]
+ internal static extern bool CreateProcessWithLogonW(string userName, string domain, IntPtr password, LogonFlags logonFlags, [MarshalAs(UnmanagedType.LPTStr)] string appName,
+ StringBuilder cmdLine, int creationFlags, IntPtr environmentBlock, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, // LPCTSTR
+ STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation);
+
+ //TODO: TOKEN
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class PROCESS_INFORMATION
+ {
+ public IntPtr hProcess = IntPtr.Zero;
+ public IntPtr hThread = IntPtr.Zero;
+ public int dwProcessId = 0;
+ public int dwThreadId = 0;
+ }
+
+ [Flags]
+ internal enum LogonFlags
+ {
+ LOGON_WITH_PROFILE = 0x00000001,
+ LOGON_NETCREDENTIALS_ONLY = 0x00000002
+ }
+
+ public const int QS_KEY = 0x0001,
+ QS_MOUSEMOVE = 0x0002,
+ QS_MOUSEBUTTON = 0x0004,
+ QS_POSTMESSAGE = 0x0008,
+ QS_TIMER = 0x0010,
+ QS_PAINT = 0x0020,
+ QS_SENDMESSAGE = 0x0040,
+ QS_HOTKEY = 0x0080,
+ QS_ALLPOSTMESSAGE = 0x0100,
+ QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON,
+ QS_INPUT = QS_MOUSE | QS_KEY,
+ QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY,
+ QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE;
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int WaitForInputIdle(SafeProcessHandle handle, int milliseconds);
+ [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Machine)]
+ public static extern bool ShellExecuteEx(ShellExecuteInfo info);
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, BestFitMapping = false)]
+ [ResourceExposure(ResourceScope.Machine)]
+ public static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
+ bool bInheritHandle, int dwOptions);
+ [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true, BestFitMapping = false)]
+ [ResourceExposure(ResourceScope.Machine)]
+ public static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeWaitHandle targetHandle, int dwDesiredAccess,
+ bool bInheritHandle, int dwOptions);
+ [DllImport("user32.dll", CharSet = CharSet.Auto, BestFitMapping = true)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int GetWindowText(HandleRef hWnd, StringBuilder lpString, int nMaxCount);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int GetWindowTextLength(HandleRef hWnd);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern IntPtr SendMessageTimeout(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int GetWindowLong(HandleRef hWnd, int nIndex);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ [ResourceExposure(ResourceScope.None)]
+ public static extern int PostMessage(HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal class ShellExecuteInfo
+ {
+ public int cbSize;
+ public int fMask;
+ public IntPtr hwnd = (IntPtr)0;
+ public IntPtr lpVerb = (IntPtr)0;
+ public IntPtr lpFile = (IntPtr)0;
+ public IntPtr lpParameters = (IntPtr)0;
+ public IntPtr lpDirectory = (IntPtr)0;
+ public int nShow;
+ public IntPtr hInstApp = (IntPtr)0;
+ public IntPtr lpIDList = (IntPtr)0;
+ public IntPtr lpClass = (IntPtr)0;
+ public IntPtr hkeyClass = (IntPtr)0;
+ public int dwHotKey = 0;
+ public IntPtr hIcon = (IntPtr)0;
+ public IntPtr hProcess = (IntPtr)0;
+ [ResourceExposure(ResourceScope.Machine)]
+ public ShellExecuteInfo()
+ {
+ cbSize = Marshal.SizeOf(this);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct LUID
+ {
+ public int LowPart;
+ public int HighPart;
+ }
+
+ public const int SEE_MASK_NOCLOSEPROCESS = 0x00000040;
+ public const int SEE_MASK_CONNECTNETDRV = 0x00000080;
+ public const int SEE_MASK_FLAG_DDEWAIT = 0x00000100;
+ public const int SEE_MASK_DOENVSUBST = 0x00000200;
+ public const int SEE_MASK_FLAG_NO_UI = 0x00000400;
+ public const int PROCESS_TERMINATE = 0x0001;
+ public const int PROCESS_QUERY_INFORMATION = 0x0400;
+ public const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
+ public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
+ public const int SYNCHRONIZE = 0x00100000;
+ public const int PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF;
+ public const int READ_CONTROL = 0x00020000;
+ public const int STANDARD_RIGHTS_READ = READ_CONTROL;
+ public const int KEY_QUERY_VALUE = 0x0001;
+ public const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
+ public const int KEY_NOTIFY = 0x0010;
+ public const int ERROR_BROKEN_PIPE = 109;
+ public const int ERROR_NO_DATA = 232;
+ public const int ERROR_HANDLE_EOF = 38;
+ public const int ERROR_IO_INCOMPLETE = 996;
+ public const int ERROR_IO_PENDING = 997;
+ public const int ERROR_FILE_EXISTS = 0x50;
+ public const int ERROR_FILENAME_EXCED_RANGE = 0xCE; // filename too long.
+ public const int ERROR_MORE_DATA = 234;
+ public const int ERROR_CANCELLED = 1223;
+ public const int ERROR_FILE_NOT_FOUND = 2;
+ public const int ERROR_PATH_NOT_FOUND = 3;
+ public const int ERROR_ACCESS_DENIED = 5;
+ public const int ERROR_INVALID_HANDLE = 6;
+ public const int ERROR_NOT_ENOUGH_MEMORY = 8;
+ public const int ERROR_BAD_COMMAND = 22;
+ public const int ERROR_SHARING_VIOLATION = 32;
+ public const int ERROR_OPERATION_ABORTED = 995;
+ public const int ERROR_NO_ASSOCIATION = 1155;
+ public const int ERROR_DLL_NOT_FOUND = 1157;
+ public const int ERROR_DDE_FAIL = 1156;
+ public const int ERROR_INVALID_PARAMETER = 87;
+ public const int ERROR_PARTIAL_COPY = 299;
+ public const int ERROR_SUCCESS = 0;
+ public const int ERROR_ALREADY_EXISTS = 183;
+ public const int ERROR_COUNTER_TIMEOUT = 1121;
+ public const int DUPLICATE_CLOSE_SOURCE = 1;
+ public const int DUPLICATE_SAME_ACCESS = 2;
+ public const int SE_ERR_FNF = 2;
+ public const int SE_ERR_PNF = 3;
+ public const int SE_ERR_ACCESSDENIED = 5;
+ public const int SE_ERR_OOM = 8;
+ public const int SE_ERR_DLLNOTFOUND = 32;
+ public const int SE_ERR_SHARE = 26;
+ public const int SE_ERR_ASSOCINCOMPLETE = 27;
+ public const int SE_ERR_DDETIMEOUT = 28;
+ public const int SE_ERR_DDEFAIL = 29;
+ public const int SE_ERR_DDEBUSY = 30;
+ public const int SE_ERR_NOASSOC = 31;
+ public const int CREATE_NO_WINDOW = 0x08000000;
+ public const int CREATE_SUSPENDED = 0x00000004;
+ public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
+ public const int SMTO_ABORTIFHUNG = 0x0002;
+ public const int GWL_STYLE = -16;
+ public const int GCL_WNDPROC = -24;
+ public const int GWL_WNDPROC = -4;
+ public const int WS_DISABLED = 0x08000000;
+ public const int WM_NULL = 0x0000;
+ public const int WM_CLOSE = 0x0010;
+ public const int SW_SHOWNORMAL = 1;
+ public const int SW_NORMAL = 1;
+ public const int SW_SHOWMINIMIZED = 2;
+ public const int SW_SHOWMAXIMIZED = 3;
+ public const int SW_MAXIMIZE = 3;
+ public const int SW_SHOWNOACTIVATE = 4;
+ public const int SW_SHOW = 5;
+ public const int SW_MINIMIZE = 6;
+ public const int SW_SHOWMINNOACTIVE = 7;
+ public const int SW_SHOWNA = 8;
+ public const int SW_RESTORE = 9;
+ public const int SW_SHOWDEFAULT = 10;
+ public const int SW_MAX = 10;
+ public const int GW_OWNER = 4;
+ public const int WHITENESS = 0x00FF0062;
+ }
+
+ internal delegate void UserCallBack(String data);
+
+ internal class AsyncStreamReader : IDisposable
+ {
+ internal const int DefaultBufferSize = 1024; // Byte buffer size
+ private const int MinBufferSize = 128;
+ private Stream stream;
+ private Encoding encoding;
+ private Decoder decoder;
+ private byte[] byteBuffer;
+ private char[] charBuffer;
+ // Record the number of valid bytes in the byteBuffer, for a few checks.
+
+ // This is the maximum number of chars we can get from one call to
+ // ReadBuffer. Used so ReadBuffer can tell when to copy data into
+ // a user's char[] directly, instead of our internal char[].
+ private int _maxCharsPerBuffer;
+
+ // Store a backpointer to the process class, to check for user callbacks
+ private Process process;
+
+ // Delegate to call user function.
+ private UserCallBack userCallBack;
+
+ // Internal Cancel operation
+ private bool cancelOperation;
+ private ManualResetEvent eofEvent;
+ private Queue messageQueue;
+ private StringBuilder sb;
+ private bool bLastCarriageReturn;
+
+ // Cache the last position scanned in sb when searching for lines.
+ private int currentLinePos;
+ internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding) : this(process, stream, callback, encoding, DefaultBufferSize) { }
+
+ // Creates a new AsyncStreamReader for the given stream. The
+ // character encoding is set by encoding and the buffer size,
+ // in number of 16-bit characters, is set by bufferSize.
+ //
+ internal AsyncStreamReader(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
+ {
+ Init(process, stream, callback, encoding, bufferSize);
+ messageQueue = new Queue();
+ }
+ private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
+ {
+ this.process = process;
+ this.stream = stream;
+ this.encoding = encoding;
+ this.userCallBack = callback;
+ decoder = encoding.GetDecoder();
+ if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
+ byteBuffer = new byte[bufferSize];
+ _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
+ charBuffer = new char[_maxCharsPerBuffer];
+ cancelOperation = false;
+ eofEvent = new ManualResetEvent(false);
+ sb = null;
+ this.bLastCarriageReturn = false;
+ }
+ public virtual void Close()
+ {
+ Dispose(true);
+ }
+ void IDisposable.Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (stream != null) stream.Close();
+ }
+ if (stream != null)
+ {
+ stream = null;
+ encoding = null;
+ decoder = null;
+ byteBuffer = null;
+ charBuffer = null;
+ }
+ if (eofEvent != null)
+ {
+ eofEvent.Close();
+ eofEvent = null;
+ }
+ }
+ public virtual Encoding CurrentEncoding
+ {
+ get
+ {
+ return encoding;
+ }
+ }
+ public virtual Stream BaseStream
+ {
+ get
+ {
+ return stream;
+ }
+ }
+
+ // User calls BeginRead to start the asynchronous read
+ internal void BeginReadLine()
+ {
+ if (cancelOperation)
+ {
+ cancelOperation = false;
+ }
+ if (sb == null)
+ {
+ sb = new StringBuilder(DefaultBufferSize);
+ stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null);
+ }
+ else
+ {
+ FlushMessageQueue();
+ }
+ }
+ internal void CancelOperation()
+ {
+ cancelOperation = true;
+ }
+
+ // This is the async callback function. Only one thread could/should call this.
+ private void ReadBuffer(IAsyncResult ar)
+ {
+ int byteLen;
+ try
+ {
+ byteLen = stream.EndRead(ar);
+ }
+ catch (IOException)
+ {
+ // We should ideally consume errors from operations getting cancelled
+ // so that we don't crash the unsuspecting parent with an unhandled exc.
+ // This seems to come in 2 forms of exceptions (depending on platform and scenario),
+ // namely OperationCanceledException and IOException (for errorcode that we don't
+ // map explicitly).
+ byteLen = 0; // Treat this as EOF
+ }
+ catch (OperationCanceledException)
+ {
+ // We should consume any OperationCanceledException from child read here
+ // so that we don't crash the parent with an unhandled exc
+ byteLen = 0; // Treat this as EOF
+ }
+ if (byteLen == 0)
+ {
+ // We're at EOF, we won't call this function again from here on.
+ lock (messageQueue)
+ {
+ if (sb.Length != 0)
+ {
+ messageQueue.Enqueue(sb.ToString());
+ sb.Length = 0;
+ }
+ messageQueue.Enqueue(null);
+ }
+ try
+ {
+ // UserCallback could throw, we should still set the eofEvent
+ FlushMessageQueue();
+ }
+ finally
+ {
+ eofEvent.Set();
+ }
+ }
+ else
+ {
+ int charLen = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, 0);
+ sb.Append(charBuffer, 0, charLen);
+ GetLinesFromStringBuilder();
+ stream.BeginRead(byteBuffer, 0, byteBuffer.Length, new AsyncCallback(ReadBuffer), null);
+ }
+ }
+
+ // Read lines stored in StringBuilder and the buffer we just read into.
+ // A line is defined as a sequence of characters followed by
+ // a carriage return ('\r'), a line feed ('\n'), or a carriage return
+ // immediately followed by a line feed. The resulting string does not
+ // contain the terminating carriage return and/or line feed. The returned
+ // value is null if the end of the input stream has been reached.
+ //
+ private void GetLinesFromStringBuilder()
+ {
+ int currentIndex = currentLinePos;
+ int lineStart = 0;
+ int len = sb.Length;
+
+ // skip a beginning '\n' character of new block if last block ended
+ // with '\r'
+ if (bLastCarriageReturn && (len > 0) && sb[0] == '\n')
+ {
+ currentIndex = 1;
+ lineStart = 1;
+ bLastCarriageReturn = false;
+ }
+ while (currentIndex < len)
+ {
+ char ch = sb[currentIndex];
+ // Note the following common line feed chars:
+ // \n - UNIX \r\n - DOS \r - Mac
+ if (ch == '\r' || ch == '\n')
+ {
+ string s = sb.ToString(lineStart, currentIndex - lineStart);
+ lineStart = currentIndex + 1;
+ // skip the "\n" character following "\r" character
+ if ((ch == '\r') && (lineStart < len) && (sb[lineStart] == '\n'))
+ {
+ lineStart++;
+ currentIndex++;
+ }
+ lock (messageQueue)
+ {
+ messageQueue.Enqueue(s);
+ }
+ }
+ currentIndex++;
+ }
+ // Protect length as IndexOutOfRangeException was being thrown when less than a
+ // character's worth of bytes was read at the beginning of a line.
+ if (len > 0 && sb[len - 1] == '\r')
+ {
+ bLastCarriageReturn = true;
+ }
+ // Keep the rest characaters which can't form a new line in string builder.
+ if (lineStart < len)
+ {
+ if (lineStart == 0)
+ {
+ // we found no breaklines, in this case we cache the position
+ // so next time we don't have to restart from the beginning
+ currentLinePos = currentIndex;
+ }
+ else
+ {
+ sb.Remove(0, lineStart);
+ currentLinePos = 0;
+ }
+ }
+ else
+ {
+ sb.Length = 0;
+ currentLinePos = 0;
+ }
+ FlushMessageQueue();
+ }
+ private void FlushMessageQueue()
+ {
+ while (true)
+ {
+ // When we call BeginReadLine, we also need to flush the queue
+ // So there could be a ---- between the ReadBuffer and BeginReadLine
+ // We need to take lock before DeQueue.
+ if (messageQueue.Count > 0)
+ {
+ lock (messageQueue)
+ {
+ if (messageQueue.Count > 0)
+ {
+ string s = (string)messageQueue.Dequeue();
+ // skip if the read is the read is cancelled
+ // this might happen inside UserCallBack
+ // However, continue to drain the queue
+ if (!cancelOperation)
+ {
+ userCallBack(s);
+ }
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ // Wait until we hit EOF. This is called from Process.WaitForExit
+ // We will lose some information if we don't do this.
+ internal void WaitUtilEOF()
+ {
+ if (eofEvent != null)
+ {
+ eofEvent.WaitOne();
+ eofEvent.Close();
+ eofEvent = null;
+ }
+ }
+ }
+
+ public delegate void DataReceivedEventHandler(Object sender, DataReceivedEventArgs e);
+
+ public class DataReceivedEventArgs : EventArgs
+ {
+ internal string _data;
+ internal DataReceivedEventArgs(string data) => this._data = data;
+ public string Data => this._data;
+ }
+
+ internal class ProcessWaitHandle : WaitHandle
+ {
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ internal ProcessWaitHandle(SafeProcessHandle processHandle) : base()
+ {
+ SafeWaitHandle waitHandle = null;
+ bool succeeded = NativeMethods.DuplicateHandle(new HandleRef(this, NativeMethods.GetCurrentProcess()), processHandle, new HandleRef(this, NativeMethods.GetCurrentProcess()), out waitHandle,
+ 0, false, NativeMethods.DUPLICATE_SAME_ACCESS);
+ if (!succeeded)
+ {
+ Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
+ }
+ this.SafeWaitHandle = waitHandle;
+ }
+ }
+
+ [SuppressUnmanagedCodeSecurityAttribute]
+ internal sealed class SafeThreadHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ internal SafeThreadHandle() : base(true) { }
+ internal void InitialSetHandle(IntPtr h)
+ {
+ Debug.Assert(base.IsInvalid, "Safe handle should only be set once");
+ base.SetHandle(h);
+ }
+ override protected bool ReleaseHandle()
+ {
+ return CloseHandle(handle);
+ }
+ [DllImport("kernel32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
+ public static extern bool CloseHandle(IntPtr handle);
+ }
+
+ [HostProtectionAttribute(MayLeakOnAbort = true)]
+ [SuppressUnmanagedCodeSecurityAttribute]
+ internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
+ internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle)
+ {
+ SetHandle(existingHandle);
+ }
+ [DllImport("kernel32.dll")]
+ [ResourceExposure(ResourceScope.None)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private static extern IntPtr LocalFree(IntPtr hMem);
+ override protected bool ReleaseHandle()
+ {
+ return LocalFree(handle) == IntPtr.Zero;
+ }
+ }
+
+ [SuppressUnmanagedCodeSecurityAttribute]
+ public sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ internal static SafeProcessHandle InvalidHandle = new SafeProcessHandle(IntPtr.Zero);
+
+ // Note that OpenProcess returns 0 on failure
+ internal SafeProcessHandle() : base(true) { }
+ internal SafeProcessHandle(IntPtr handle) : base(true)
+ {
+ SetHandle(handle);
+ }
+ [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
+ public SafeProcessHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle)
+ {
+ SetHandle(existingHandle);
+ }
+ [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
+ [ResourceExposure(ResourceScope.Machine)]
+ internal static extern SafeProcessHandle OpenProcess(int access, bool inherit, int processId);
+ internal void InitialSetHandle(IntPtr h)
+ {
+ Debug.Assert(base.IsInvalid, "Safe handle should only be set once");
+ base.handle = h;
+ }
+ override protected bool ReleaseHandle()
+ {
+ return CloseHandle(handle);
+ }
+ [DllImport("kernel32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
+ public static extern bool CloseHandle(IntPtr handle);
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter)),
+ // Disabling partial trust scenarios
+ PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust"), HostProtection(SharedState = true, SelfAffectingProcessMgmt = true)]
+ public sealed class ProcessStartInfo
+ {
+ string fileName;
+ string arguments;
+ string directory;
+ string verb;
+ ProcessWindowStyle windowStyle;
+ bool errorDialog;
+ IntPtr errorDialogParentHandle;
+ bool useShellExecute = false;
+ string userName;
+ string domain;
+ SecureString password;
+ string passwordInClearText;
+ bool loadUserProfile;
+ bool redirectStandardInput = false;
+ bool redirectStandardOutput = false;
+ bool redirectStandardError = false;
+ Encoding standardOutputEncoding;
+ Encoding standardErrorEncoding;
+ bool createNoWindow = false;
+ WeakReference weakParentProcess;
+ internal StringDictionary environmentVariables;
+ ///
+ /// Default constructor. At least the
+ /// property must be set before starting the process.
+ ///
+ public ProcessStartInfo() { }
+ internal ProcessStartInfo(Process parent)
+ {
+ this.weakParentProcess = new WeakReference(parent);
+ }
+ ///
+ /// Specifies the name of the application or document that is to be started.
+ ///
+ [ResourceExposure(ResourceScope.Machine)]
+ public ProcessStartInfo(string fileName)
+ {
+ this.fileName = fileName;
+ }
+ ///
+ /// Specifies the name of the application that is to be started, as well as a set
+ /// of command line arguments to pass to the application.
+ ///
+ [ResourceExposure(ResourceScope.Machine)]
+ public ProcessStartInfo(string fileName, string arguments)
+ {
+ this.fileName = fileName;
+ this.arguments = arguments;
+ }
+ ///
+ ///
+ /// Specifies the verb to use when opening the filename. For example, the "print"
+ /// verb will print a document specified using .
+ /// Each file extension has it's own set of verbs, which can be obtained using the
+ /// property.
+ /// The default verb can be specified using "".
+ ///
+ ///
+ /// Discuss 'opening' vs. 'starting.' I think the part about the
+ /// default verb was a dev comment.
+ /// Find out what
+ /// that means.
+ ///
+ ///
+ public string Verb
+ {
+ get
+ {
+ if (verb == null) return string.Empty;
+ return verb;
+ }
+ set
+ {
+ verb = value;
+ }
+ }
+ public string Arguments
+ {
+ get
+ {
+ if (arguments == null) return string.Empty;
+ return arguments;
+ }
+ set
+ {
+ arguments = value;
+ }
+ }
+ public bool CreateNoWindow
+ {
+ get
+ {
+ return createNoWindow;
+ }
+ set
+ {
+ createNoWindow = value;
+ }
+ }
+ public StringDictionary EnvironmentVariables
+ {
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ get
+ {
+ // Note:
+ // Creating a detached ProcessStartInfo will pre-populate the environment
+ // with current environmental variables.
+
+ // When used with an existing Process.ProcessStartInfo the following behavior
+ // * Desktop - Populates with current Environment (rather than that of the process)
+ if (environmentVariables == null)
+ {
+ environmentVariables = new StringDictionaryWithComparer();
+
+ // if not in design mode, initialize the child environment block with all the parent variables
+ if (!(this.weakParentProcess != null && this.weakParentProcess.IsAlive && ((Component)this.weakParentProcess.Target).Site != null &&
+ ((Component)this.weakParentProcess.Target).Site.DesignMode))
+ {
+ foreach (DictionaryEntry entry in System.Environment.GetEnvironmentVariables()) environmentVariables.Add((string)entry.Key, (string)entry.Value);
+ }
+ }
+ return environmentVariables;
+ }
+ }
+ private IDictionary environment;
+ public IDictionary Environment
+ {
+ get
+ {
+ if (environment == null)
+ {
+ environment = this.EnvironmentVariables.AsGenericDictionary();
+ }
+ return environment;
+ }
+ }
+ public bool RedirectStandardInput
+ {
+ get
+ {
+ return redirectStandardInput;
+ }
+ set
+ {
+ redirectStandardInput = value;
+ }
+ }
+ public bool RedirectStandardOutput
+ {
+ get
+ {
+ return redirectStandardOutput;
+ }
+ set
+ {
+ redirectStandardOutput = value;
+ }
+ }
+ public bool RedirectStandardError
+ {
+ get
+ {
+ return redirectStandardError;
+ }
+ set
+ {
+ redirectStandardError = value;
+ }
+ }
+ public Encoding StandardErrorEncoding
+ {
+ get
+ {
+ return standardErrorEncoding;
+ }
+ set
+ {
+ standardErrorEncoding = value;
+ }
+ }
+ public Encoding StandardOutputEncoding
+ {
+ get
+ {
+ return standardOutputEncoding;
+ }
+ set
+ {
+ standardOutputEncoding = value;
+ }
+ }
+ public bool UseShellExecute
+ {
+ get
+ {
+ return useShellExecute;
+ }
+ set
+ {
+ useShellExecute = value;
+ }
+ }
+ ///
+ /// Returns the set of verbs associated with the file specified by the
+ /// property.
+ ///
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string[] Verbs
+ {
+ [ResourceExposure(ResourceScope.None)]
+ [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
+ get
+ {
+ ArrayList verbs = new ArrayList();
+ RegistryKey key = null;
+ string extension = Path.GetExtension(FileName);
+ try
+ {
+ if (extension != null && extension.Length > 0)
+ {
+ key = Registry.ClassesRoot.OpenSubKey(extension);
+ if (key != null)
+ {
+ string value = (string)key.GetValue(String.Empty);
+ key.Close();
+ key = Registry.ClassesRoot.OpenSubKey(value + "\\shell");
+ if (key != null)
+ {
+ string[] names = key.GetSubKeyNames();
+ for (int i = 0; i < names.Length; i++)
+ if (string.Compare(names[i], "new", StringComparison.OrdinalIgnoreCase) != 0)
+ verbs.Add(names[i]);
+ key.Close();
+ key = null;
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (key != null) key.Close();
+ }
+ string[] temp = new string[verbs.Count];
+ verbs.CopyTo(temp, 0);
+ return temp;
+ }
+ }
+ public string UserName
+ {
+ get
+ {
+ if (userName == null)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return userName;
+ }
+ }
+ set
+ {
+ userName = value;
+ }
+ }
+ public SecureString Password
+ {
+ get
+ {
+ return password;
+ }
+ set
+ {
+ password = value;
+ }
+ }
+ public string PasswordInClearText
+ {
+ get
+ {
+ return passwordInClearText;
+ }
+ set
+ {
+ passwordInClearText = value;
+ }
+ }
+ public string Domain
+ {
+ get
+ {
+ if (domain == null)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return domain;
+ }
+ }
+ set
+ {
+ domain = value;
+ }
+ }
+ public bool LoadUserProfile
+ {
+ get
+ {
+ return loadUserProfile;
+ }
+ set
+ {
+ loadUserProfile = value;
+ }
+ }
+ public string FileName
+ {
+ [ResourceExposure(ResourceScope.Machine)]
+ get
+ {
+ if (fileName == null) return string.Empty;
+ return fileName;
+ }
+ [ResourceExposure(ResourceScope.Machine)]
+ set
+ {
+ fileName = value;
+ }
+ }
+ public string WorkingDirectory
+ {
+ [ResourceExposure(ResourceScope.Machine)]
+ get
+ {
+ if (directory == null) return string.Empty;
+ return directory;
+ }
+ [ResourceExposure(ResourceScope.Machine)]
+ set
+ {
+ directory = value;
+ }
+ }
+ public bool ErrorDialog
+ {
+ get
+ {
+ return errorDialog;
+ }
+ set
+ {
+ errorDialog = value;
+ }
+ }
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public IntPtr ErrorDialogParentHandle
+ {
+ get
+ {
+ return errorDialogParentHandle;
+ }
+ set
+ {
+ errorDialogParentHandle = value;
+ }
+ }
+ public ProcessWindowStyle WindowStyle
+ {
+ get
+ {
+ return windowStyle;
+ }
+ set
+ {
+ if (!Enum.IsDefined(typeof(ProcessWindowStyle), value)) throw new InvalidEnumArgumentException("value", (int)value, typeof(ProcessWindowStyle));
+ windowStyle = value;
+ }
+ }
+ }
+
+ [Serializable]
+ internal class StringDictionaryWithComparer : StringDictionary
+ {
+ public StringDictionaryWithComparer() : this((IEqualityComparer)StringComparer.OrdinalIgnoreCase) { }
+ public StringDictionaryWithComparer(IEqualityComparer comparer) => this.ReplaceHashtable(new Hashtable(comparer));
+ public override string this[string key]
+ {
+ get => key != null ? (string)this.contents[(object)key] : throw new ArgumentNullException(nameof(key));
+ set
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents[(object)key] = (object)value;
+ }
+ }
+ public override void Add(string key, string value)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents.Add((object)key, (object)value);
+ }
+ public override bool ContainsKey(string key) => key != null ? this.contents.ContainsKey((object)key) : throw new ArgumentNullException(nameof(key));
+ public override void Remove(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents.Remove((object)key);
+ }
+ }
+
+ [Serializable]
+ public class StringDictionary : IEnumerable
+ {
+ internal Hashtable contents = new Hashtable();
+ /// Gets the number of key/value pairs in the .
+ /// The number of key/value pairs in the .
+ /// Retrieving the value of this property is an O(1) operation.
+ public virtual int Count => this.contents.Count;
+ /// Gets a value indicating whether access to the is synchronized (thread safe).
+ ///
+ /// if access to the is synchronized (thread safe); otherwise, .
+ public virtual bool IsSynchronized => this.contents.IsSynchronized;
+ /// Gets or sets the value associated with the specified key.
+ /// The key whose value to get or set.
+ /// The value associated with the specified key. If the specified key is not found, Get returns , and Set creates a new entry with the specified key.
+ ///
+ /// is .
+ public virtual string this[string key]
+ {
+ get
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return (string)this.contents[(object)key.ToLower(CultureInfo.InvariantCulture)];
+ }
+ set
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents[(object)key.ToLower(CultureInfo.InvariantCulture)] = (object)value;
+ }
+ }
+ /// Gets a collection of keys in the .
+ /// An that provides the keys in the .
+ public virtual ICollection Keys => this.contents.Keys;
+ /// Gets an object that can be used to synchronize access to the .
+ /// An that can be used to synchronize access to the .
+ public virtual object SyncRoot => this.contents.SyncRoot;
+ /// Gets a collection of values in the .
+ /// An that provides the values in the .
+ public virtual ICollection Values => this.contents.Values;
+ /// Adds an entry with the specified key and value into the .
+ /// The key of the entry to add.
+ /// The value of the entry to add. The value can be .
+ ///
+ /// is .
+ /// An entry with the same key already exists in the .
+ /// The is read-only.
+ public virtual void Add(string key, string value)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents.Add((object)key.ToLower(CultureInfo.InvariantCulture), (object)value);
+ }
+ /// Removes all entries from the .
+ /// The is read-only.
+ public virtual void Clear() => this.contents.Clear();
+ /// Determines if the contains a specific key.
+ /// The key to locate in the .
+ ///
+ /// if the contains an entry with the specified key; otherwise, .
+ /// The key is .
+ public virtual bool ContainsKey(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return this.contents.ContainsKey((object)key.ToLower(CultureInfo.InvariantCulture));
+ }
+ /// Determines if the contains a specific value.
+ /// The value to locate in the . The value can be .
+ ///
+ /// if the contains an element with the specified value; otherwise, .
+ public virtual bool ContainsValue(string value) => this.contents.ContainsValue((object)value);
+ /// Copies the string dictionary values to a one-dimensional instance at the specified index.
+ /// The one-dimensional that is the destination of the values copied from the .
+ /// The index in the array where copying begins.
+ ///
+ /// is multidimensional.
+ /// -or-
+ /// The number of elements in the is greater than the available space from to the end of .
+ ///
+ /// is .
+ ///
+ /// is less than the lower bound of .
+ public virtual void CopyTo(Array array, int index) => this.contents.CopyTo(array, index);
+ /// Returns an enumerator that iterates through the string dictionary.
+ /// An that iterates through the string dictionary.
+ public virtual IEnumerator GetEnumerator() => (IEnumerator)this.contents.GetEnumerator();
+ /// Removes the entry with the specified key from the string dictionary.
+ /// The key of the entry to remove.
+ /// The key is .
+ /// The is read-only.
+ public virtual void Remove(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.contents.Remove((object)key.ToLower(CultureInfo.InvariantCulture));
+ }
+ internal void ReplaceHashtable(Hashtable useThisHashtableInstead) => this.contents = useThisHashtableInstead;
+ internal IDictionary AsGenericDictionary() => (IDictionary)new StringDictionary.GenericAdapter(this);
+
+ private class GenericAdapter : IDictionary, ICollection>, IEnumerable>, IEnumerable
+ {
+ private StringDictionary m_stringDictionary;
+ private StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter _values;
+ private StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter _keys;
+ internal GenericAdapter(StringDictionary stringDictionary) => this.m_stringDictionary = stringDictionary;
+ public void Add(string key, string value) => this[key] = value;
+ public bool ContainsKey(string key) => this.m_stringDictionary.ContainsKey(key);
+ public void Clear() => this.m_stringDictionary.Clear();
+ public int Count => this.m_stringDictionary.Count;
+ public string this[string key]
+ {
+ get
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return this.m_stringDictionary.ContainsKey(key) ? this.m_stringDictionary[key] : throw new KeyNotFoundException();
+ }
+ set
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ this.m_stringDictionary[key] = value;
+ }
+ }
+ public ICollection Keys
+ {
+ get
+ {
+ if (this._keys == null)
+ this._keys = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Key);
+ return (ICollection)this._keys;
+ }
+ }
+ public ICollection Values
+ {
+ get
+ {
+ if (this._values == null)
+ this._values = new StringDictionary.GenericAdapter.ICollectionToGenericCollectionAdapter(this.m_stringDictionary, StringDictionary.GenericAdapter.KeyOrValue.Value);
+ return (ICollection)this._values;
+ }
+ }
+ public bool Remove(string key)
+ {
+ if (!this.m_stringDictionary.ContainsKey(key)) return false;
+ this.m_stringDictionary.Remove(key);
+ return true;
+ }
+ public bool TryGetValue(string key, out string value)
+ {
+ if (!this.m_stringDictionary.ContainsKey(key))
+ {
+ value = (string)null;
+ return false;
+ }
+ value = this.m_stringDictionary[key];
+ return true;
+ }
+ void ICollection>.Add(KeyValuePair item) => this.m_stringDictionary.Add(item.Key, item.Value);
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ string str;
+ return this.TryGetValue(item.Key, out str) && str.Equals(item.Value);
+ }
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ if (array == null) throw new ArgumentNullException(nameof(array), "ArgumentNull_Array");
+ if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), "ArgumentOutOfRange_NeedNonNegNum");
+ if (array.Length - arrayIndex < this.Count) throw new ArgumentException("Arg_ArrayPlusOffTooSmall");
+ int num = arrayIndex;
+ foreach (DictionaryEntry dictionaryEntry in this.m_stringDictionary) array[num++] = new KeyValuePair((string)dictionaryEntry.Key, (string)dictionaryEntry.Value);
+ }
+ bool ICollection>.IsReadOnly => false;
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ if (!((ICollection>)this).Contains(item)) return false;
+ this.m_stringDictionary.Remove(item.Key);
+ return true;
+ }
+ IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)this.GetEnumerator();
+ public IEnumerator> GetEnumerator()
+ {
+ foreach (DictionaryEntry dictionaryEntry in this.m_stringDictionary) yield return new KeyValuePair((string)dictionaryEntry.Key, (string)dictionaryEntry.Value);
+ }
+
+ internal enum KeyOrValue
+ {
+ Key,
+ Value,
+ }
+
+ private class ICollectionToGenericCollectionAdapter : ICollection, IEnumerable, IEnumerable
+ {
+ private StringDictionary _internal;
+ private StringDictionary.GenericAdapter.KeyOrValue _keyOrValue;
+ public ICollectionToGenericCollectionAdapter(StringDictionary source, StringDictionary.GenericAdapter.KeyOrValue keyOrValue)
+ {
+ this._internal = source != null ? source : throw new ArgumentNullException(nameof(source));
+ this._keyOrValue = keyOrValue;
+ }
+ public void Add(string item) => this.ThrowNotSupportedException();
+ public void Clear() => this.ThrowNotSupportedException();
+ public void ThrowNotSupportedException()
+ {
+ if (this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key) throw new NotSupportedException("NotSupported_KeyCollectionSet");
+ throw new NotSupportedException("NotSupported_ValueCollectionSet");
+ }
+ public bool Contains(string item) => this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key ? this._internal.ContainsKey(item) : this._internal.ContainsValue(item);
+ public void CopyTo(string[] array, int arrayIndex) => this.GetUnderlyingCollection().CopyTo((Array)array, arrayIndex);
+ public int Count => this._internal.Count;
+ public bool IsReadOnly => true;
+ public bool Remove(string item)
+ {
+ this.ThrowNotSupportedException();
+ return false;
+ }
+ private ICollection GetUnderlyingCollection() => this._keyOrValue == StringDictionary.GenericAdapter.KeyOrValue.Key ? this._internal.Keys : this._internal.Values;
+ public IEnumerator GetEnumerator()
+ {
+ foreach (string underlying in (IEnumerable)this.GetUnderlyingCollection()) yield return underlying;
+ }
+ IEnumerator IEnumerable.GetEnumerator() => this.GetUnderlyingCollection().GetEnumerator();
+ }
+ }
+ }
+ }
+}
diff --git a/TrustedUninstaller.Shared/Debug.cs b/TrustedUninstaller.Shared/Debug.cs
new file mode 100644
index 0000000..b633fda
--- /dev/null
+++ b/TrustedUninstaller.Shared/Debug.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Diagnostics;
+
+namespace TrustedUninstaller.Shared
+{
+ public static class Testing
+ {
+ [Conditional("DEBUG")]
+ public static void WriteLine(object text)
+ {
+ Console.WriteLine(text.ToString());
+ }
+ public static void WriteLine(Exception exception, string shortTrace)
+ {
+ Console.WriteLine(exception.GetType() + " at " + shortTrace + ":" + exception.Message);
+ }
+ public static void WriteLine(Exception exception, string shortTrace, string item)
+ {
+ Console.WriteLine(exception.GetType() + " at " + shortTrace + $" ({item}):" + exception.Message);
+ }
+ }
+}
diff --git a/TrustedUninstaller.Shared/Defender.cs b/TrustedUninstaller.Shared/Defender.cs
index 1de627e..cadf45e 100644
--- a/TrustedUninstaller.Shared/Defender.cs
+++ b/TrustedUninstaller.Shared/Defender.cs
@@ -132,7 +132,7 @@ namespace TrustedUninstaller.Shared
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
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryValueAction() { KeyName = "HKLM\\SOFTWARE\\Microsoft\\Windows Defender", Value = "InstallLocation", Operation = RegistryValueOperation.Delete }.GetStatus() !=
UninstallTaskStatus.Completed)
@@ -157,7 +157,7 @@ namespace TrustedUninstaller.Shared
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
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryKeyAction() { KeyName = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend" }.GetStatus() !=
UninstallTaskStatus.Completed)
@@ -183,7 +183,7 @@ namespace TrustedUninstaller.Shared
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
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\WinDefend", Value = "Start", Data = 4, Type = RegistryValueType.REG_DWORD }.GetStatus() !=
UninstallTaskStatus.Completed)
@@ -211,7 +211,7 @@ namespace TrustedUninstaller.Shared
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
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryKeyAction() { KeyName = @"HKCR\CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32" }.GetStatus() !=
UninstallTaskStatus.Completed)
@@ -247,7 +247,7 @@ namespace TrustedUninstaller.Shared
"reg delete \"HKLM\\SOFTWARE\\Microsoft\\WindowsRuntime\\ActivatableClassId\\Windows.Internal.Security.SmartScreen.EventLogger\" /f &" +
"reg delete \"HKLM\\SOFTWARE\\Microsoft\\WindowsRuntime\\ActivatableClassId\\Windows.Internal.Security.SmartScreen.UriReputationService\" /f\"",
CreateWindow = false
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryKeyAction() { KeyName = @"HKCR\CLSID\{a463fcb9-6b1c-4e0d-a80b-a2ca7999e25d}\InprocServer32" }.GetStatus() !=
UninstallTaskStatus.Completed)
@@ -275,7 +275,7 @@ namespace TrustedUninstaller.Shared
Arguments =
@"-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg add ""HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity"" /v Enabled /d 0 /f",
CreateWindow = false
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity", Value = "Enabled", Data = 0, }.GetStatus()
!= UninstallTaskStatus.Completed)
@@ -302,19 +302,39 @@ namespace TrustedUninstaller.Shared
Arguments =
@"-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg add ""HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity"" /v Enabled /d 0 /f",
CreateWindow = false
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
if (new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity", Value = "Enabled", Data = 0, }.GetStatus()
!= UninstallTaskStatus.Completed)
ErrorLogger.WriteToErrorLog("Could not disable memory integrity.", null, "Defender disable warning");
}
- AmeliorationUtil.SafeRunAction(new RegistryValueAction()
+ try
+ {
+ // Can cause ProcessHacker driver warnings without this
+ new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config", Value = "VulnerableDriverBlocklistEnable", Data = 0, }.RunTask().Wait();
+
+ if (new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config", Value = "VulnerableDriverBlocklistEnable", Data = 0, }.GetStatus()
+ != UninstallTaskStatus.Completed)
+ throw new Exception("Unknown error");
+ }
+ catch (Exception e)
{
- KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config",
- Value = "VulnerableDriverBlocklistEnable",
- Data = 0,
- }).Wait();
+ ErrorLogger.WriteToErrorLog("First blocklist disable failed: " + e.GetType() + " " + e.Message, null, "Kernel driver preparation warning");
+
+ new RunAction()
+ {
+ RawPath = Directory.GetCurrentDirectory(),
+ Exe = $"NSudoLC.exe",
+ Arguments =
+ @"-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg add ""HKLM\SYSTEM\CurrentControlSet\Control\CI\Config"" /v VulnerableDriverBlocklistEnable /d 0 /f",
+ CreateWindow = false
+ }.RunTaskOnMainThread();
+
+ if (new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config", Value = "VulnerableDriverBlocklistEnable", Data = 0, }.GetStatus()
+ != UninstallTaskStatus.Completed)
+ ErrorLogger.WriteToErrorLog("Could not disable blocklist.", null, "Kernel driver preparation error");
+ }
return restartRequired;
}
@@ -491,7 +511,7 @@ namespace TrustedUninstaller.Shared
"net stop WinDefend\"",
CreateWindow = false,
Timeout = 7500,
- }.RunTask().Wait();
+ }.RunTaskOnMainThread();
}
return !Process.GetProcessesByName("MsMpEng").Any();
@@ -597,7 +617,7 @@ namespace TrustedUninstaller.Shared
tokenGroups.Groups[idx].Attributes &= ~(uint)PInvoke.SE_GROUP_ATTRIBUTES.SE_GROUP_OWNER;
}
}
- Console.WriteLine(tokenGroups.GroupCount);
+
tokenGroups.Groups[tokenGroups.GroupCount].Sid = tiSid;
tokenGroups.Groups[tokenGroups.GroupCount].Attributes = (uint)PInvoke.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED | (uint)PInvoke.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED_BY_DEFAULT;
tokenGroups.GroupCount++;
diff --git a/TrustedUninstaller.Shared/Globals.cs b/TrustedUninstaller.Shared/Globals.cs
index 9ee744c..fc98d44 100644
--- a/TrustedUninstaller.Shared/Globals.cs
+++ b/TrustedUninstaller.Shared/Globals.cs
@@ -9,12 +9,27 @@ namespace TrustedUninstaller.Shared
{
public class Globals
{
- public const string CurrentVersion = "0.7.2";
- public const double CurrentVersionNumber = 0.72;
-#if DEBUG
- public static readonly int WinVer = 19045;
-#else
+ public const string CurrentVersion = "0.7.3";
+ public const double CurrentVersionNumber = 0.73;
+
public static readonly int WinVer = Int32.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("CurrentBuildNumber").ToString());
-#endif
+
+ private static int _winUpdateVer = -1;
+ public static int WinUpdateVer
+ {
+ get
+ {
+ if (_winUpdateVer != -1)
+ return _winUpdateVer;
+
+ try
+ {
+ _winUpdateVer = (int)Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("UBR");
+ }
+ catch { _winUpdateVer = 0; }
+
+ return _winUpdateVer;
+ }
+ }
}
}
diff --git a/TrustedUninstaller.Shared/Playbook.cs b/TrustedUninstaller.Shared/Playbook.cs
index 2f05939..181e34d 100644
--- a/TrustedUninstaller.Shared/Playbook.cs
+++ b/TrustedUninstaller.Shared/Playbook.cs
@@ -5,7 +5,6 @@ using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Management.Automation.Runspaces;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -53,6 +52,8 @@ namespace TrustedUninstaller.Shared
public bool Overhaul { get; set; } = false;
public string Path { get; set; }
+
+ public bool? UseKernelDriver { get; set; } = null;
public List Options { get; set; } = null;
diff --git a/TrustedUninstaller.Shared/ProcessPrivilege.cs b/TrustedUninstaller.Shared/ProcessPrivilege.cs
new file mode 100644
index 0000000..6a75791
--- /dev/null
+++ b/TrustedUninstaller.Shared/ProcessPrivilege.cs
@@ -0,0 +1,507 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+using TrustedUninstaller.Shared.Actions;
+
+namespace TrustedUninstaller.Shared
+{
+ public class ProcessPrivilege
+ {
+ private static Win32.TokensEx.SafeTokenHandle userToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ private static Win32.TokensEx.SafeTokenHandle elevatedUserToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ private static Win32.TokensEx.SafeTokenHandle systemToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ private static Win32.TokensEx.SafeTokenHandle impsersonatedSystemToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ private static Win32.TokensEx.SafeTokenHandle lsassToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+
+ internal static void ResetTokens()
+ {
+ elevatedUserToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ lsassToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ systemToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ impsersonatedSystemToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ userToken = new Win32.TokensEx.SafeTokenHandle(IntPtr.Zero);
+ }
+
+ public static void StartPrivilegedTask(AugmentedProcess.Process process, Privilege privilege)
+ {
+ var tcs = StartThread(process, privilege);
+ tcs.Task.Wait();
+ for (int i = 0; tcs.Task.Result != null && i <= 3; i++)
+ {
+ ErrorLogger.WriteToErrorLog("Error launching privileged process: " + tcs.Task.Result.Message, tcs.Task.Result.StackTrace, "PrivilegedProcess Warning",
+ Path.GetFileName(process.StartInfo.FileName));
+ ResetTokens();
+ Thread.Sleep(500 * i);
+ tcs = StartThread(process, privilege);
+ tcs.Task.Wait();
+ }
+
+ if (tcs.Task.Result != null)
+ throw new SecurityException("Error launching privileged process.", tcs.Task.Result);
+ }
+
+ private static TaskCompletionSource StartThread(AugmentedProcess.Process process, Privilege privilege)
+ {
+ var tcs = new TaskCompletionSource();
+ var thread = new Thread(() =>
+ {
+ try
+ {
+ switch (privilege)
+ {
+ case (Privilege.System):
+ GetSystemToken();
+ process.Start(AugmentedProcess.Process.CreateType.RawToken, ref systemToken);
+ break;
+ case (Privilege.CurrentUser):
+ GetUserToken(true);
+ process.Start(AugmentedProcess.Process.CreateType.UserToken, ref userToken);
+ break;
+ case (Privilege.CurrentUserElevated):
+ GetElevatedUserToken();
+ process.Start(AugmentedProcess.Process.CreateType.RawToken, ref elevatedUserToken);
+ break;
+ default:
+ throw new ArgumentException("Unexpected.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.SetResult(e);
+ return;
+ }
+
+ tcs.SetResult(null);
+ });
+ thread.Start();
+ return tcs;
+ }
+
+ private static uint GetUserSession()
+ {
+ var sessionId = Win32.WTS.WTSGetActiveConsoleSessionId();
+ if (sessionId != 0xFFFFFFFF) return sessionId;
+ IntPtr pSessionInfo = IntPtr.Zero;
+ Int32 count = 0;
+ if (Win32.WTS.WTSEnumerateSessions((IntPtr)null, 0, 1, ref pSessionInfo, ref count) == 0)
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error enumerating user sessions.");
+ Int32 dataSize = Marshal.SizeOf(typeof(Win32.WTS.WTS_SESSION_INFO));
+ Int64 current = (Int64)pSessionInfo;
+ for (int i = 0; i < count; i++)
+ {
+ Win32.WTS.WTS_SESSION_INFO si =
+ (Win32.WTS.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current,
+ typeof(Win32.WTS.WTS_SESSION_INFO));
+ current += dataSize;
+ if (si.State == Win32.WTS.WTS_CONNECTSTATE_CLASS.WTSActive)
+ {
+ sessionId = (uint)si.SessionID;
+ break;
+ }
+ }
+ Win32.WTS.WTSFreeMemory(pSessionInfo);
+ return sessionId;
+ }
+
+ private static void GetUserToken(bool getPrivileges)
+ {
+ if (getPrivileges)
+ {
+ GetSystemToken();
+ var result = Win32.Tokens.ImpersonateLoggedOnUser(systemToken);
+ if (!result)
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error impersonating system process token.");
+
+ Win32.TokensEx.AdjustCurrentPrivilege(Win32.Tokens.SE_ASSIGNPRIMARYTOKEN_NAME);
+ Win32.TokensEx.AdjustCurrentPrivilege(Win32.Tokens.SE_INCREASE_QUOTA_NAME);
+ }
+
+ if (userToken.DangerousGetHandle() != IntPtr.Zero)
+ return;
+
+ var sessionId = GetUserSession();
+
+ if (Win32.WTS.WTSQueryUserToken(sessionId, out Win32.TokensEx.SafeTokenHandle wtsToken))
+ {
+ if (!Win32.Tokens.DuplicateTokenEx(wtsToken, Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS,
+ IntPtr.Zero,
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
+ Win32.Tokens.TOKEN_TYPE.TokenPrimary, out userToken))
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(),
+ "Failed to duplicate process token for lsass.");
+ }
+ return;
+ }
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error fetching active user session token.");
+ }
+
+ private static void GetElevatedUserToken()
+ {
+ GetSystemToken();
+ var result = Win32.Tokens.ImpersonateLoggedOnUser(systemToken);
+
+ if (!result)
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error impersonating system process token.");
+
+ if (lsassToken.DangerousGetHandle() == IntPtr.Zero)
+ {
+
+ var processHandle = Process.GetProcessesByName("lsass").First().Handle;
+ if (!Win32.Tokens.OpenProcessToken(processHandle,
+ Win32.Tokens.TokenAccessFlags.TOKEN_DUPLICATE |
+ Win32.Tokens.TokenAccessFlags.TOKEN_ASSIGN_PRIMARY |
+ Win32.Tokens.TokenAccessFlags.TOKEN_QUERY | Win32.Tokens.TokenAccessFlags.TOKEN_IMPERSONATE,
+ out var tokenHandle))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to open process token for lsass.");
+ }
+
+ if (!Win32.Tokens.DuplicateTokenEx(tokenHandle, Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS,
+ IntPtr.Zero,
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
+ Win32.Tokens.TOKEN_TYPE.TokenImpersonation, out lsassToken))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(),
+ "Failed to duplicate process token for lsass.");
+ }
+
+ Win32.CloseHandle(processHandle);
+ }
+
+
+ result = Win32.Tokens.ImpersonateLoggedOnUser(lsassToken);
+ if (!result)
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error impersonating lsass process token.");
+
+
+ Win32.TokensEx.AdjustCurrentPrivilege(Win32.Tokens.SE_ASSIGNPRIMARYTOKEN_NAME);
+ Win32.TokensEx.AdjustCurrentPrivilege(Win32.Tokens.SE_INCREASE_QUOTA_NAME);
+
+
+ if (elevatedUserToken.DangerousGetHandle() != IntPtr.Zero)
+ return;
+
+
+ var privileges = new[]
+ {
+ Win32.Tokens.SE_INCREASE_QUOTA_NAME,
+ Win32.Tokens.SE_MACHINE_ACCOUNT_NAME, Win32.Tokens.SE_SECURITY_NAME,
+ Win32.Tokens.SE_TAKE_OWNERSHIP_NAME, Win32.Tokens.SE_LOAD_DRIVER_NAME,
+ Win32.Tokens.SE_SYSTEM_PROFILE_NAME, Win32.Tokens.SE_SYSTEMTIME_NAME,
+ Win32.Tokens.SE_PROFILE_SINGLE_PROCESS_NAME, Win32.Tokens.SE_INCREASE_BASE_PRIORITY_NAME,
+ Win32.Tokens.SE_CREATE_PERMANENT_NAME,
+ Win32.Tokens.SE_BACKUP_NAME, Win32.Tokens.SE_RESTORE_NAME, Win32.Tokens.SE_SHUTDOWN_NAME,
+ Win32.Tokens.SE_DEBUG_NAME, Win32.Tokens.SE_AUDIT_NAME, Win32.Tokens.SE_SYSTEM_ENVIRONMENT_NAME,
+ Win32.Tokens.SE_CHANGE_NOTIFY_NAME,
+ Win32.Tokens.SE_UNDOCK_NAME, Win32.Tokens.SE_SYNC_AGENT_NAME,
+ Win32.Tokens.SE_ENABLE_DELEGATION_NAME, Win32.Tokens.SE_MANAGE_VOLUME_NAME,
+ Win32.Tokens.SE_IMPERSONATE_NAME, Win32.Tokens.SE_CREATE_GLOBAL_NAME,
+ Win32.Tokens.SE_TRUSTED_CREDMAN_ACCESS_NAME, Win32.Tokens.SE_RELABEL_NAME,
+ Win32.Tokens.SE_TIME_ZONE_NAME,
+ Win32.Tokens.SE_CREATE_SYMBOLIC_LINK_NAME, Win32.Tokens.SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME
+ };
+ var authId = Win32.Tokens.SYSTEM_LUID;
+
+ GetUserToken(false);
+
+ Win32.Tokens.DuplicateTokenEx(userToken,
+ Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS, IntPtr.Zero,
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, Win32.Tokens.TOKEN_TYPE.TokenPrimary,
+ out Win32.TokensEx.SafeTokenHandle dupedUserToken);
+
+ Win32.SID.AllocateAndInitializeSid(
+ ref Win32.SID.SECURITY_MANDATORY_LABEL_AUTHORITY,
+ 1,
+ (int)Win32.SID.SECURITY_MANDATORY_LABEL.High,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ out IntPtr integritySid);
+
+ var tokenMandatoryLabel = new Win32.Tokens.TOKEN_MANDATORY_LABEL() {
+ Label = default(Win32.SID.SID_AND_ATTRIBUTES)
+ };
+
+ tokenMandatoryLabel.Label.Attributes = (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_INTEGRITY;
+ tokenMandatoryLabel.Label.Sid = integritySid;
+
+ var integritySize = Marshal.SizeOf(tokenMandatoryLabel);
+ var tokenInfo = Marshal.AllocHGlobal(integritySize);
+
+ Marshal.StructureToPtr(tokenMandatoryLabel, tokenInfo, false);
+
+ Win32.Tokens.SetTokenInformation(
+ dupedUserToken,
+ Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
+ tokenInfo,
+ integritySize + Win32.SID.GetLengthSid(integritySid));
+
+
+ var pTokenUser = Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenUser);
+ var pTokenOwner =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenOwner);
+ var pTokenPrivileges =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenPrivileges);
+ var pTokenGroups =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenGroups);
+ var pTokenPrimaryGroup =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenPrimaryGroup);
+ var pTokenDefaultDacl =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenDefaultDacl);
+ var pTokenSource =
+ Win32.TokensEx.GetInfoFromToken(dupedUserToken, Win32.Tokens.TOKEN_INFORMATION_CLASS.TokenSource);
+
+ var tokenUser =
+ (Win32.Tokens.TOKEN_USER)Marshal.PtrToStructure(pTokenUser, typeof(Win32.Tokens.TOKEN_USER));
+ if (!Win32.TokensEx.CreateTokenPrivileges(privileges, out var tokenPrivileges))
+ tokenPrivileges =
+ (Win32.Tokens.TOKEN_PRIVILEGES)Marshal.PtrToStructure(pTokenPrivileges,
+ typeof(Win32.Tokens.TOKEN_PRIVILEGES));
+ var tokenGroups = (Win32.Tokens.TOKEN_GROUPS)Marshal.PtrToStructure(
+ pTokenGroups, typeof(Win32.Tokens.TOKEN_GROUPS));
+ var tokenOwner =
+ (Win32.Tokens.TOKEN_OWNER)Marshal.PtrToStructure(pTokenOwner, typeof(Win32.Tokens.TOKEN_OWNER));
+ var tokenPrimaryGroup =
+ (Win32.Tokens.TOKEN_PRIMARY_GROUP)Marshal.PtrToStructure(pTokenPrimaryGroup,
+ typeof(Win32.Tokens.TOKEN_PRIMARY_GROUP));
+ var tokenDefaultDacl = (Win32.Tokens.TOKEN_DEFAULT_DACL)Marshal.PtrToStructure(
+ pTokenDefaultDacl, typeof(Win32.Tokens.TOKEN_DEFAULT_DACL));
+ var tokenSource = (Win32.Tokens.TOKEN_SOURCE)Marshal.PtrToStructure(
+ pTokenSource, typeof(Win32.Tokens.TOKEN_SOURCE));
+ /*
+ for (var idx = 0; idx < tokenPrivileges.PrivilegeCount - 1; idx++)
+ {
+ if ((tokenPrivileges.Privileges[idx].Attributes &
+ (uint)Win32.Tokens.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED) != 0)
+ {
+ }
+
+ if ((tokenPrivileges.Privileges[idx].Attributes &
+ (uint)Win32.Tokens.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED_BY_DEFAULT) != 0)
+ {
+ }
+ }
+ */
+
+ IntPtr adminsSid = IntPtr.Zero;
+ IntPtr localAndAdminSid = IntPtr.Zero;
+
+ bool adminsFound = false;
+ bool localAndAdminFound = false;
+
+ for (var idx = 0; idx < tokenGroups.GroupCount - 1; idx++)
+ {
+ Win32.SID.ConvertSidToStringSid(tokenGroups.Groups[idx].Sid, out string strSid);
+ if (string.Compare(strSid, Win32.SID.DOMAIN_ALIAS_RID_ADMINS, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ adminsFound = true;
+ tokenGroups.Groups[idx].Attributes = (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED |
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES
+ .SE_GROUP_ENABLED_BY_DEFAULT | (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_MANDATORY | (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_OWNER;
+ } else if (string.Compare(strSid, Win32.SID.DOMAIN_ALIAS_RID_LOCAL_AND_ADMIN_GROUP, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ localAndAdminFound = true;
+ tokenGroups.Groups[idx].Attributes = (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED |
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES
+ .SE_GROUP_ENABLED_BY_DEFAULT | (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_MANDATORY;
+ }
+ }
+
+ if (!adminsFound)
+ {
+ Win32.SID.ConvertStringSidToSid(Win32.SID.DOMAIN_ALIAS_RID_ADMINS, out adminsSid);
+ tokenGroups.Groups[tokenGroups.GroupCount].Sid = adminsSid;
+ tokenGroups.Groups[tokenGroups.GroupCount].Attributes =
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED |
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED_BY_DEFAULT;
+ tokenGroups.GroupCount++;
+ }
+ if (!localAndAdminFound)
+ {
+ Win32.SID.ConvertStringSidToSid(Win32.SID.DOMAIN_ALIAS_RID_LOCAL_AND_ADMIN_GROUP, out localAndAdminSid);
+ tokenGroups.Groups[tokenGroups.GroupCount].Sid = localAndAdminSid;
+ tokenGroups.Groups[tokenGroups.GroupCount].Attributes =
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED |
+ (uint)Win32.Tokens.SE_GROUP_ATTRIBUTES.SE_GROUP_ENABLED_BY_DEFAULT;
+ tokenGroups.GroupCount++;
+ }
+
+ var expirationTime = new Win32.LARGE_INTEGER() { QuadPart = -1L };
+ var sqos = new Win32.Tokens.SECURITY_QUALITY_OF_SERVICE(
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, Win32.Tokens.SECURITY_STATIC_TRACKING,
+ 0);
+ var oa = new Win32.Tokens.OBJECT_ATTRIBUTES(string.Empty, 0) { };
+ var pSqos = Marshal.AllocHGlobal(Marshal.SizeOf(sqos));
+ Marshal.StructureToPtr(sqos, pSqos, true);
+ oa.SecurityQualityOfService = pSqos;
+
+ var status = Win32.Tokens.ZwCreateToken(out elevatedUserToken,
+ Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS, ref oa, Win32.Tokens.TOKEN_TYPE.TokenPrimary,
+ ref authId, ref expirationTime, ref tokenUser, ref tokenGroups, ref tokenPrivileges, ref tokenOwner,
+ ref tokenPrimaryGroup, ref tokenDefaultDacl, ref tokenSource);
+
+ Win32.LocalFree(pTokenUser);
+ Win32.LocalFree(pTokenOwner);
+ Win32.LocalFree(pTokenGroups);
+ Win32.LocalFree(pTokenDefaultDacl);
+ Win32.LocalFree(pTokenPrivileges);
+ Win32.LocalFree(pTokenPrimaryGroup);
+
+ if (adminsSid != IntPtr.Zero)
+ Win32.SID.FreeSid(adminsSid);
+ if (localAndAdminSid != IntPtr.Zero)
+ Win32.SID.FreeSid(localAndAdminSid);
+ if (integritySid != IntPtr.Zero)
+ Win32.SID.FreeSid(integritySid);
+
+ if (status != 0)
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating elevated user token: " + status);
+ }
+
+ public static void GetSystemToken()
+ {
+ if (systemToken.DangerousGetHandle() != IntPtr.Zero)
+ return;
+
+ try
+ {
+ var processHandle = Process.GetProcessesByName("winlogon").First().Handle;
+ if (!Win32.Tokens.OpenProcessToken(processHandle,
+ Win32.Tokens.TokenAccessFlags.TOKEN_DUPLICATE | Win32.Tokens.TokenAccessFlags.TOKEN_ASSIGN_PRIMARY |
+ Win32.Tokens.TokenAccessFlags.TOKEN_QUERY | Win32.Tokens.TokenAccessFlags.TOKEN_IMPERSONATE,
+ out var tokenHandle))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to open process token for winlogon.");
+ }
+
+ if (!Win32.Tokens.DuplicateTokenEx(tokenHandle, Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS, IntPtr.Zero,
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
+ Win32.Tokens.TOKEN_TYPE.TokenPrimary, out systemToken))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(),
+ "Failed to duplicate process token for winlogon.");
+ }
+
+ Win32.CloseHandle(processHandle);
+ }
+ catch (Exception e)
+ {
+ var sessionId = GetUserSession();
+ int dwLsassPID = -1;
+ int dwWinLogonPID = -1;
+ Win32.WTS.WTS_PROCESS_INFO[] pProcesses;
+ IntPtr pProcessInfo = IntPtr.Zero;
+ int dwProcessCount = 0;
+ if (Win32.WTS.WTSEnumerateProcesses((IntPtr)null, 0, 1, ref pProcessInfo, ref dwProcessCount))
+ {
+ IntPtr pMemory = pProcessInfo;
+ pProcesses = new Win32.WTS.WTS_PROCESS_INFO[dwProcessCount];
+ for (int i = 0; i < dwProcessCount; i++)
+ {
+ pProcesses[i] =
+ (Win32.WTS.WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo,
+ typeof(Win32.WTS.WTS_PROCESS_INFO));
+ pProcessInfo = (IntPtr)((long)pProcessInfo + Marshal.SizeOf(pProcesses[i]));
+ var processName = Marshal.PtrToStringAnsi(pProcesses[i].ProcessName);
+ Win32.SID.ConvertSidToStringSid(pProcesses[i].UserSid, out string sid);
+ string strSid;
+ if (processName == null || pProcesses[i].UserSid == default || sid != "S-1-5-18") continue;
+ if ((-1 == dwLsassPID) && (0 == pProcesses[i].SessionID) && (processName == "lsass.exe"))
+ {
+ dwLsassPID = pProcesses[i].ProcessID;
+ continue;
+ }
+
+ if ((-1 == dwWinLogonPID) && (sessionId == pProcesses[i].SessionID) &&
+ (processName == "winlogon.exe"))
+ {
+ dwWinLogonPID = pProcesses[i].ProcessID;
+ continue;
+ }
+ }
+
+ Win32.WTS.WTSFreeMemory(pMemory);
+ }
+
+ IntPtr systemProcessHandle = IntPtr.Zero;
+ try
+ {
+ systemProcessHandle = Process.GetProcessById(dwLsassPID).Handle;
+ }
+ catch
+ {
+ systemProcessHandle = Process.GetProcessById(dwWinLogonPID).Handle;
+ }
+
+ if (!Win32.Tokens.OpenProcessToken(systemProcessHandle, Win32.Tokens.TokenAccessFlags.TOKEN_DUPLICATE,
+ out Win32.TokensEx.SafeTokenHandle token))
+ {
+ Win32.CloseHandle(systemProcessHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to open process token.");
+ }
+
+ if (!Win32.Tokens.DuplicateTokenEx(token, Win32.Tokens.TokenAccessFlags.MAXIMUM_ALLOWED, IntPtr.Zero,
+ Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
+ Win32.Tokens.TOKEN_TYPE.TokenPrimary, out systemToken))
+ {
+ Win32.CloseHandle(systemProcessHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to duplicate process token.");
+ }
+
+ Win32.CloseHandle(systemProcessHandle);
+ }
+ }
+
+ public static Win32.TokensEx.SafeTokenHandle GetCurrentProcessToken()
+ {
+ if (!Win32.Tokens.OpenProcessToken(Win32.Process.GetCurrentProcess(),
+ Win32.Tokens.TokenAccessFlags.TOKEN_READ, out Win32.TokensEx.SafeTokenHandle token))
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Error opening token for current process.");
+ return token;
+ }
+
+ private static Win32.TokensEx.SafeTokenHandle GetProcessTokenByName(string name, bool impersonation)
+ {
+ var processHandle = Process.GetProcessesByName(name).First().Handle;
+ if (!Win32.Tokens.OpenProcessToken(processHandle,
+ Win32.Tokens.TokenAccessFlags.TOKEN_DUPLICATE | Win32.Tokens.TokenAccessFlags.TOKEN_ASSIGN_PRIMARY |
+ Win32.Tokens.TokenAccessFlags.TOKEN_QUERY | Win32.Tokens.TokenAccessFlags.TOKEN_IMPERSONATE,
+ out var tokenHandle))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to open process token for " + name + ".");
+ }
+
+ if (!Win32.Tokens.DuplicateTokenEx(tokenHandle, Win32.Tokens.TokenAccessFlags.TOKEN_ALL_ACCESS, IntPtr.Zero,
+ impersonation ? Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation : Win32.Tokens.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
+ impersonation ? Win32.Tokens.TOKEN_TYPE.TokenImpersonation : Win32.Tokens.TOKEN_TYPE.TokenPrimary, out Win32.TokensEx.SafeTokenHandle handle))
+ {
+ Win32.CloseHandle(processHandle);
+ throw new Win32Exception(Marshal.GetLastWin32Error(),
+ "Failed to duplicate process token for " + name + ".");
+ }
+
+ Win32.CloseHandle(processHandle);
+ return handle;
+ } }
+}
\ No newline at end of file
diff --git a/TrustedUninstaller.Shared/Tasks/ITaskAction.cs b/TrustedUninstaller.Shared/Tasks/ITaskAction.cs
index dff1cd6..adf3423 100644
--- a/TrustedUninstaller.Shared/Tasks/ITaskAction.cs
+++ b/TrustedUninstaller.Shared/Tasks/ITaskAction.cs
@@ -20,5 +20,6 @@ namespace TrustedUninstaller.Shared.Tasks
public string ErrorString();
public UninstallTaskStatus GetStatus();
public Task RunTask();
+ public void RunTaskOnMainThread();
}
}
\ No newline at end of file
diff --git a/TrustedUninstaller.Shared/Tasks/TaskAction.cs b/TrustedUninstaller.Shared/Tasks/TaskAction.cs
index e720b40..d380e1b 100644
--- a/TrustedUninstaller.Shared/Tasks/TaskAction.cs
+++ b/TrustedUninstaller.Shared/Tasks/TaskAction.cs
@@ -6,8 +6,16 @@ namespace TrustedUninstaller.Shared.Tasks
{
[YamlMember(typeof(bool), Alias = "ignoreErrors")]
public bool IgnoreErrors { get; set; } = false;
+
[YamlMember(typeof(string), Alias = "option")]
public string Option { get; set; } = null;
+
+ [YamlMember(typeof(string[]), Alias = "options")]
+ public string[] Options { get; set; } = null;
+
+ [YamlMember(typeof(string[]), Alias = "builds")]
+ public string[] Builds { get; set; } = null;
+
[YamlMember(typeof(string), Alias = "cpuArch")]
public string Arch { get; set; } = null;
}
diff --git a/TrustedUninstaller.Shared/Tasks/UninstallTask.cs b/TrustedUninstaller.Shared/Tasks/UninstallTask.cs
index fa2ae20..d43b128 100644
--- a/TrustedUninstaller.Shared/Tasks/UninstallTask.cs
+++ b/TrustedUninstaller.Shared/Tasks/UninstallTask.cs
@@ -24,6 +24,15 @@ namespace TrustedUninstaller.Shared.Tasks
public int Priority { get; set; } = 1;
public UninstallTaskPrivilege Privilege { get; set; } = UninstallTaskPrivilege.Admin;
+
+ [YamlMember(typeof(string), Alias = "option")]
+ public string Option { get; set; } = null;
+ [YamlMember(typeof(string[]), Alias = "options")]
+ public string[] Options { get; set; } = null;
+ [YamlMember(typeof(string[]), Alias = "builds")]
+ public string[] Builds { get; set; } = null;
+ [YamlMember(typeof(string), Alias = "cpuArch")]
+ public string Arch { get; set; } = null;
public List Features { get; set; } = new List();
diff --git a/TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj b/TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj
index 857980a..e294537 100644
--- a/TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj
+++ b/TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj
@@ -51,14 +51,21 @@
true
SINGLE
+
+ bin\x64\Release\
+ x64
+ true
+ embedded
+ true
+ SINGLE;DEBUG
+
-
+
-
False
True
@@ -67,10 +74,7 @@
-
-
-
..\TrustedUninstaller.GUI\bin\x64\Release\YamlDotNet.dll
@@ -95,10 +99,13 @@
+
+
+
@@ -113,6 +120,7 @@
+
@@ -131,11 +139,11 @@
-
+
@@ -156,16 +164,16 @@
true
true
-
+
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" & cmd /c "exit /b 0")
- cmd /c "echo Compiled> "$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\Compiled.txt""
+ PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'Shared','""""x64\$(Configuration)""""' -NoNewWindow -Wait"
+
+
+ .allowedextension
+
-
- 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 'Shared' -WindowStyle Hidden"
-
-
+
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" & cmd /c "exit /b 0")
\ No newline at end of file
diff --git a/TrustedUninstaller.Shared/Win32.cs b/TrustedUninstaller.Shared/Win32.cs
new file mode 100644
index 0000000..7dbc1ab
--- /dev/null
+++ b/TrustedUninstaller.Shared/Win32.cs
@@ -0,0 +1,1699 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Security.Permissions;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+using TrustedUninstaller.Shared;
+using TrustedUninstaller.Shared.Actions;
+using TrustedUninstaller.Shared.Tasks;
+
+namespace TrustedUninstaller.Shared
+{
+ public static class Win32
+ {
+ public static class Service
+ {
+ [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
+ public static extern IntPtr CreateService(
+ IntPtr hSCManager,
+ string lpServiceName,
+ string lpDisplayName,
+ uint dwDesiredAccess,
+ uint dwServiceType,
+ uint dwStartType,
+ uint dwErrorControl,
+ string lpBinaryPathName,
+ [Optional] string lpLoadOrderGroup,
+ [Optional] string lpdwTagId, // only string so we can pass null
+ [Optional] string lpDependencies,
+ [Optional] string lpServiceStartName,
+ [Optional] string lpPassword);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DeleteService(IntPtr hService);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, SERVICE_ACCESS dwDesiredAccess);
+
+ [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern IntPtr OpenSCManager(string machineName, string databaseName, SCM_ACCESS dwDesiredAccess);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool QueryServiceStatusEx(IntPtr Service, int InfoLevel,
+ ref SERVICE_STATUS_PROCESS ServiceStatus, int BufSize, out int BytesNeeded);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CloseServiceHandle(IntPtr hSCObject);
+
+ [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool EnumServicesStatusEx(
+ IntPtr hSCManager,
+ uint InfoLevel,
+ SERVICE_TYPE dwServiceType,
+ uint dwServiceState,
+ IntPtr lpServices,
+ int cbBufSize,
+ out int pcbBytesNeeded,
+ out int lpServicesReturned,
+ ref int lpResumeHandle,
+ string pszGroupName
+ );
+
+ public struct ENUM_SERVICE_STATUS_PROCESS {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string lpServiceName;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string lpDisplayName;
+ public SERVICE_STATUS_PROCESS ServiceStatusProcess;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SERVICE_STATUS_PROCESS
+ {
+ public SERVICE_TYPE ServiceType;
+ public SERVICE_STATE CurrentState;
+ public SERVICE_ACCEPT ControlsAccepted;
+ public int Win32ExitCode;
+ public int ServiceSpecificExitCode;
+ public int CheckPoint;
+ public int WaitHint;
+ public int ProcessID;
+ public SERVICE_FLAGS ServiceFlags;
+ }
+
+ public enum SERVICE_STATE : int
+ {
+ ContinuePending = 0x5,
+ PausePending = 0x6,
+ Paused = 0x7,
+ Running = 0x4,
+ StartPending = 0x2,
+ StopPending = 0x3,
+ Stopped = 0x1
+ }
+
+ public enum SERVICE_ACCEPT : int
+ {
+ NetBindChange = 0x10,
+ ParamChange = 0x8,
+ PauseContinue = 0x2,
+ PreShutdown = 0x100,
+ Shutdown = 0x4,
+ Stop = 0x1,
+ HardwareProfileChange = 0x20,
+ PowerEvent = 0x40,
+ SessionChange = 0x80
+ }
+
+ public enum SERVICE_FLAGS : int
+ {
+ None = 0,
+ RunsInSystemProcess = 0x1
+ }
+
+ ///
+ /// Service types.
+ ///
+ [Flags]
+ public enum SERVICE_TYPE : uint
+ {
+ ///
+ /// Driver service.
+ ///
+ SERVICE_KERNEL_DRIVER = 0x00000001,
+ ///
+ /// File system driver service.
+ ///
+ SERVICE_FILE_SYSTEM_DRIVER = 0x00000002,
+ ///
+ /// Service that runs in its own process.
+ ///
+ SERVICE_WIN32_OWN_PROCESS = 0x00000010,
+ ///
+ /// Service that shares a process with one or more other services.
+ ///
+ SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
+ ///
+ /// The service can interact with the desktop.
+ ///
+ SERVICE_INTERACTIVE_PROCESS = 0x00000100,
+ }
+
+ ///
+ /// Service start options
+ ///
+ public enum SERVICE_START : uint
+ {
+ ///
+ /// A device driver started by the system loader. This value is valid
+ /// only for driver services.
+ ///
+ SERVICE_BOOT_START = 0x00000000,
+ ///
+ /// A device driver started by the IoInitSystem function. This value
+ /// is valid only for driver services.
+ ///
+ SERVICE_SYSTEM_START = 0x00000001,
+ ///
+ /// A service started automatically by the service control manager
+ /// during system startup. For more information, see Automatically
+ /// Starting Services.
+ ///
+ SERVICE_AUTO_START = 0x00000002,
+ ///
+ /// A service started by the service control manager when a process
+ /// calls the StartService function. For more information, see
+ /// Starting Services on Demand.
+ ///
+ SERVICE_DEMAND_START = 0x00000003,
+ ///
+ /// A service that cannot be started. Attempts to start the service
+ /// result in the error code ERROR_SERVICE_DISABLED.
+ ///
+ SERVICE_DISABLED = 0x00000004,
+ }
+
+ ///
+ /// Severity of the error, and action taken, if this service fails
+ /// to start.
+ ///
+ public enum SERVICE_ERROR
+ {
+ ///
+ /// The startup program ignores the error and continues the startup
+ /// operation.
+ ///
+ SERVICE_ERROR_IGNORE = 0x00000000,
+ ///
+ /// The startup program logs the error in the event log but continues
+ /// the startup operation.
+ ///
+ SERVICE_ERROR_NORMAL = 0x00000001,
+ ///
+ /// The startup program logs the error in the event log. If the
+ /// last-known-good configuration is being started, the startup
+ /// operation continues. Otherwise, the system is restarted with
+ /// the last-known-good configuration.
+ ///
+ SERVICE_ERROR_SEVERE = 0x00000002,
+ ///
+ /// The startup program logs the error in the event log, if possible.
+ /// If the last-known-good configuration is being started, the startup
+ /// operation fails. Otherwise, the system is restarted with the
+ /// last-known good configuration.
+ ///
+ SERVICE_ERROR_CRITICAL = 0x00000003,
+ }
+
+ [Flags]
+ public enum SERVICE_ACCESS : uint
+ {
+ STANDARD_RIGHTS_REQUIRED = 0xF0000,
+ SERVICE_QUERY_CONFIG = 0x00001,
+ SERVICE_CHANGE_CONFIG = 0x00002,
+ SERVICE_QUERY_STATUS = 0x00004,
+ SERVICE_ENUMERATE_DEPENDENTS = 0x00008,
+ SERVICE_START = 0x00010,
+ SERVICE_STOP = 0x00020,
+ SERVICE_PAUSE_CONTINUE = 0x00040,
+ SERVICE_INTERROGATE = 0x00080,
+ SERVICE_USER_DEFINED_CONTROL = 0x00100,
+ SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP |
+ SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL)
+ }
+
+ [Flags]
+ public enum SCM_ACCESS : uint
+ {
+ STANDARD_RIGHTS_REQUIRED = 0xF0000,
+ SC_MANAGER_CONNECT = 0x00001,
+ SC_MANAGER_CREATE_SERVICE = 0x00002,
+ SC_MANAGER_ENUMERATE_SERVICE = 0x00004,
+ SC_MANAGER_LOCK = 0x00008,
+ SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,
+ SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,
+ SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS |
+ SC_MANAGER_MODIFY_BOOT_CONFIG
+ }
+ }
+ public static class ServiceEx {
+ public static bool IsPendingDeleteOrDeleted(string serviceName)
+ {
+ var manager = Service.OpenSCManager(null, null, Service.SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
+ if (manager == IntPtr.Zero)
+ return new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\" + serviceName, Value = "DeleteFlag", Type = RegistryValueType.REG_DWORD, Data = 1 }.GetStatus() == UninstallTaskStatus.Completed ||
+ new RegistryKeyAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\" + serviceName, Operation = RegistryKeyOperation.Delete }.GetStatus() == UninstallTaskStatus.Completed;
+
+ var handle = Service.CreateService(manager, serviceName, "AME Deletion Check",
+ (uint)Service.SERVICE_ACCESS.SERVICE_ALL_ACCESS,
+ (uint)Service.SERVICE_TYPE.SERVICE_WIN32_OWN_PROCESS, (uint)Service.SERVICE_START.SERVICE_DISABLED,
+ (uint)Service.SERVICE_ERROR.SERVICE_ERROR_IGNORE, @"ame-deletion-check");
+ if (handle == IntPtr.Zero)
+ {
+ if (Marshal.GetLastWin32Error() == 0x00000430)
+ {
+ return true;
+ }
+ return new RegistryValueAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\" + serviceName, Value = "DeleteFlag", Type = RegistryValueType.REG_DWORD, Data = 1 }.GetStatus() == UninstallTaskStatus.Completed ||
+ new RegistryKeyAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\" + serviceName, Operation = RegistryKeyOperation.Delete }.GetStatus() == UninstallTaskStatus.Completed;
+ }
+ Service.DeleteService(handle);
+ Service.CloseServiceHandle(handle);
+ Service.CloseServiceHandle(manager);
+ return true;
+ }
+ public static bool IsPendingStopOrStopped(string serviceName, out bool pending)
+ {
+ pending = false;
+ var manager = Service.OpenSCManager(null, null, Service.SCM_ACCESS.SC_MANAGER_ENUMERATE_SERVICE);
+ if (manager == IntPtr.Zero)
+ return false;
+
+ var handle = Service.OpenService(manager, serviceName,
+ Service.SERVICE_ACCESS.SERVICE_QUERY_STATUS);
+
+ if (handle == IntPtr.Zero)
+ return true;
+
+ Service.SERVICE_STATUS_PROCESS status = new Service.SERVICE_STATUS_PROCESS();
+
+ if (!Service.QueryServiceStatusEx(handle, 0, ref status, Marshal.SizeOf(status), out int retLen))
+ {
+ Service.CloseServiceHandle(handle);
+ Service.CloseServiceHandle(manager);
+ return false;
+ }
+
+ Service.CloseServiceHandle(handle);
+ Service.CloseServiceHandle(manager);
+
+ pending = status.CurrentState == Service.SERVICE_STATE.StopPending;
+ return pending || status.CurrentState == Service.SERVICE_STATE.Stopped;
+ }
+
+ public static int GetServiceProcessId(string serviceName)
+ {
+ var manager = Service.OpenSCManager(null, null, Service.SCM_ACCESS.SC_MANAGER_ENUMERATE_SERVICE);
+ if (manager == IntPtr.Zero)
+ throw new Exception("Error opening service manager.");
+
+ var handle = Service.OpenService(manager, serviceName,
+ Service.SERVICE_ACCESS.SERVICE_QUERY_STATUS);
+
+ Service.SERVICE_STATUS_PROCESS status = new Service.SERVICE_STATUS_PROCESS();
+
+ if (!Service.QueryServiceStatusEx(handle, 0, ref status, Marshal.SizeOf(status), out int retLen))
+ {
+ Service.CloseServiceHandle(handle);
+ Service.CloseServiceHandle(manager);
+ throw new Exception("Error querying service ProcessId: " + Marshal.GetLastWin32Error());
+ }
+ Service.CloseServiceHandle(handle);
+ Service.CloseServiceHandle(manager);
+
+ return status.ProcessID == 0 ? -1 : status.ProcessID;
+ }
+ public static IEnumerable GetServicesFromProcessId(int processId)
+ {
+ return GetServices(Service.SERVICE_TYPE.SERVICE_WIN32_OWN_PROCESS |
+ Service.SERVICE_TYPE.SERVICE_WIN32_SHARE_PROCESS).Where(x => x.ServiceStatusProcess.ProcessID == processId).Select(x => x.lpServiceName);
+ }
+ private static Service.ENUM_SERVICE_STATUS_PROCESS[] GetServices(Service.SERVICE_TYPE serviceTypes)
+ {
+ IntPtr handle = Service.OpenSCManager(null, null, Service.SCM_ACCESS.SC_MANAGER_ENUMERATE_SERVICE);
+ if (handle == IntPtr.Zero)
+ throw new Exception("Could not open service manager.");
+
+ int iBytesNeeded = 0;
+ int iServicesReturned = 0;
+ int iResumeHandle = 0;
+
+ Service.EnumServicesStatusEx(handle,
+ 0,
+ Service.SERVICE_TYPE.SERVICE_WIN32_OWN_PROCESS | Service.SERVICE_TYPE.SERVICE_WIN32_SHARE_PROCESS,
+ 1,
+ IntPtr.Zero,
+ 0,
+ out iBytesNeeded,
+ out iServicesReturned,
+ ref iResumeHandle,
+ null);
+
+ IntPtr buffer = Marshal.AllocHGlobal((int)iBytesNeeded);
+
+ Service.EnumServicesStatusEx(handle,
+ 0,
+ Service.SERVICE_TYPE.SERVICE_WIN32_OWN_PROCESS | Service.SERVICE_TYPE.SERVICE_WIN32_SHARE_PROCESS,
+ 1,
+ buffer,
+ iBytesNeeded,
+ out iBytesNeeded,
+ out iServicesReturned,
+ ref iResumeHandle,
+ null);
+
+ var services = new Service.ENUM_SERVICE_STATUS_PROCESS[iServicesReturned];
+ var serviceSize = Marshal.SizeOf(typeof(Service.ENUM_SERVICE_STATUS_PROCESS));
+ for (int i = 0; i < iServicesReturned; i++)
+ {
+ var servicePtr = new IntPtr(buffer.ToInt64() + i * serviceSize);
+ services[i] = Marshal.PtrToStructure(servicePtr);
+ }
+
+ Marshal.FreeHGlobal(buffer);
+
+ return services;
+ }
+ }
+
+ public static class Tokens
+ {
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool OpenProcessToken(IntPtr hProcess, TokenAccessFlags DesiredAccess,
+ out TokensEx.SafeTokenHandle hToken);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool GetTokenInformation(TokensEx.SafeTokenHandle TokenHandle,
+ TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength,
+ out int ReturnLength);
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetTokenInformation(
+ TokensEx.SafeTokenHandle hToken,
+ TOKEN_INFORMATION_CLASS tokenInfoClass,
+ IntPtr pTokenInfo,
+ int tokenInfoLength);
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetTokenInformation(
+ TokensEx.SafeTokenHandle hToken,
+ TOKEN_INFORMATION_CLASS tokenInfoClass,
+ ref uint pTokenInfo,
+ int tokenInfoLength);
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern bool DuplicateTokenEx(TokensEx.SafeTokenHandle hExistingToken, TokenAccessFlags dwDesiredAccess,
+ IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType,
+ out TokensEx.SafeTokenHandle phNewToken);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool ImpersonateLoggedOnUser(TokensEx.SafeTokenHandle hToken);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, out LUID lpLuid);
+
+ [DllImport("ntdll.dll", SetLastError = true)]
+ public static extern IntPtr RtlAdjustPrivilege(LUID privilege, bool bEnablePrivilege, bool isThreadPrivilege,
+ out bool previousValue);
+
+ [DllImport("ntdll.dll")]
+ public static extern int ZwCreateToken(out TokensEx.SafeTokenHandle TokenHandle, TokenAccessFlags DesiredAccess,
+ ref OBJECT_ATTRIBUTES ObjectAttributes, TOKEN_TYPE TokenType, ref LUID AuthenticationId,
+ ref LARGE_INTEGER ExpirationTime, ref TOKEN_USER TokenUser, ref TOKEN_GROUPS TokenGroups,
+ ref TOKEN_PRIVILEGES TokenPrivileges, ref TOKEN_OWNER TokenOwner,
+ ref TOKEN_PRIMARY_GROUP TokenPrimaryGroup, ref TOKEN_DEFAULT_DACL TokenDefaultDacl,
+ ref TOKEN_SOURCE TokenSource);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct OBJECT_ATTRIBUTES : IDisposable
+ {
+ public int Length;
+ public IntPtr RootDirectory;
+ private IntPtr objectName;
+ public uint Attributes;
+ public IntPtr SecurityDescriptor;
+ public IntPtr SecurityQualityOfService;
+
+ public OBJECT_ATTRIBUTES(string name, uint attrs)
+ {
+ Length = 0;
+ RootDirectory = IntPtr.Zero;
+ objectName = IntPtr.Zero;
+ Attributes = attrs;
+ SecurityDescriptor = IntPtr.Zero;
+ SecurityQualityOfService = IntPtr.Zero;
+ Length = Marshal.SizeOf(this);
+ ObjectName = new UNICODE_STRING(name);
+ }
+
+ public UNICODE_STRING ObjectName
+ {
+ get => (UNICODE_STRING)Marshal.PtrToStructure(objectName, typeof(UNICODE_STRING));
+ set
+ {
+ var fDeleteOld = objectName != IntPtr.Zero;
+ if (!fDeleteOld) objectName = Marshal.AllocHGlobal(Marshal.SizeOf(value));
+ Marshal.StructureToPtr(value, objectName, fDeleteOld);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (objectName != IntPtr.Zero)
+ {
+ Marshal.DestroyStructure(objectName, typeof(UNICODE_STRING));
+ Marshal.FreeHGlobal(objectName);
+ objectName = IntPtr.Zero;
+ }
+ }
+ }
+
+ [Flags]
+ public enum TokenAccessFlags : uint
+ {
+ TOKEN_ADJUST_DEFAULT = 0x0080,
+ TOKEN_ADJUST_GROUPS = 0x0040,
+ TOKEN_ADJUST_PRIVILEGES = 0x0020,
+ TOKEN_ADJUST_SESSIONID = 0x0100,
+ TOKEN_ASSIGN_PRIMARY = 0x0001,
+ TOKEN_DUPLICATE = 0x0002,
+ TOKEN_EXECUTE = 0x00020000,
+ TOKEN_IMPERSONATE = 0x0004,
+ TOKEN_QUERY = 0x0008,
+ TOKEN_QUERY_SOURCE = 0x0010,
+ TOKEN_READ = 0x00020008,
+ TOKEN_WRITE = 0x000200E0,
+ TOKEN_ALL_ACCESS = 0x000F01FF,
+ MAXIMUM_ALLOWED = 0x02000000
+ }
+
+ public enum TOKEN_TYPE
+ {
+ TokenPrimary = 1,
+ TokenImpersonation
+ }
+
+ public enum TOKEN_INFORMATION_CLASS
+ {
+ ///
+ /// The buffer receives a TOKEN_USER structure that contains the user account of the token.
+ ///
+ TokenUser = 1,
+
+ ///
+ /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
+ ///
+ TokenGroups,
+
+ ///
+ /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
+ ///
+ TokenPrivileges,
+
+ ///
+ /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
+ ///
+ TokenOwner,
+
+ ///
+ /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
+ ///
+ TokenPrimaryGroup,
+
+ ///
+ /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
+ ///
+ TokenDefaultDacl,
+
+ ///
+ /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
+ ///
+ TokenSource,
+
+ ///
+ /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
+ ///
+ TokenType,
+
+ ///
+ /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
+ ///
+ TokenImpersonationLevel,
+
+ ///
+ /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
+ ///
+ TokenStatistics,
+
+ ///
+ /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
+ ///
+ TokenRestrictedSids,
+
+ ///
+ /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token.
+ ///
+ TokenSessionId,
+
+ ///
+ /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
+ ///
+ TokenGroupsAndPrivileges,
+
+ ///
+ /// Reserved.
+ ///
+ TokenSessionReference,
+
+ ///
+ /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
+ ///
+ TokenSandBoxInert,
+
+ ///
+ /// Reserved.
+ ///
+ TokenAuditPolicy,
+
+ ///
+ /// The buffer receives a TOKEN_ORIGIN value.
+ ///
+ TokenOrigin,
+
+ ///
+ /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
+ ///
+ TokenElevationType,
+
+ ///
+ /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
+ ///
+ TokenLinkedToken,
+
+ ///
+ /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
+ ///
+ TokenElevation,
+
+ ///
+ /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
+ ///
+ TokenHasRestrictions,
+
+ ///
+ /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
+ ///
+ TokenAccessInformation,
+
+ ///
+ /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
+ ///
+ TokenVirtualizationAllowed,
+
+ ///
+ /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
+ ///
+ TokenVirtualizationEnabled,
+
+ ///
+ /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level.
+ ///
+ TokenIntegrityLevel,
+
+ ///
+ /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
+ ///
+ TokenUIAccess,
+
+ ///
+ /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
+ ///
+ TokenMandatoryPolicy,
+
+ ///
+ /// The buffer receives the token's logon security identifier (SID).
+ ///
+ TokenLogonSid,
+
+ ///
+ /// The maximum value for this enumeration
+ ///
+ MaxTokenInfoClass
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_GROUPS
+ {
+ public int GroupCount;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public SID.SID_AND_ATTRIBUTES[] Groups;
+
+ public TOKEN_GROUPS(int privilegeCount)
+ {
+ GroupCount = privilegeCount;
+ Groups = new SID.SID_AND_ATTRIBUTES[32];
+ }
+ }
+
+ public struct TOKEN_PRIMARY_GROUP
+ {
+ public IntPtr PrimaryGroup; // PSID
+
+ public TOKEN_PRIMARY_GROUP(IntPtr _sid)
+ {
+ PrimaryGroup = _sid;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_SOURCE
+ {
+ public TOKEN_SOURCE(string name)
+ {
+ SourceName = new byte[8];
+ Encoding.GetEncoding(1252).GetBytes(name, 0, name.Length, SourceName, 0);
+ if (!SID.AllocateLocallyUniqueId(out SourceIdentifier)) throw new Win32Exception();
+ }
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public byte[] SourceName;
+
+ public LUID SourceIdentifier;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_USER
+ {
+ public SID.SID_AND_ATTRIBUTES User;
+
+ public TOKEN_USER(IntPtr _sid)
+ {
+ User = new SID.SID_AND_ATTRIBUTES { Sid = _sid, Attributes = 0 };
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_OWNER
+ {
+ public IntPtr Owner; // PSID
+
+ public TOKEN_OWNER(IntPtr _owner)
+ {
+ Owner = _owner;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_DEFAULT_DACL
+ {
+ public IntPtr DefaultDacl; // PACL
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_PRIVILEGES
+ {
+ public int PrivilegeCount;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)]
+ public LUID_AND_ATTRIBUTES[] Privileges;
+
+ public TOKEN_PRIVILEGES(int privilegeCount)
+ {
+ PrivilegeCount = privilegeCount;
+ Privileges = new LUID_AND_ATTRIBUTES[36];
+ }
+ }
+
+ public enum SECURITY_IMPERSONATION_LEVEL
+ {
+ SecurityAnonymous,
+ SecurityIdentification,
+ SecurityImpersonation,
+ SecurityDelegation
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SECURITY_QUALITY_OF_SERVICE
+ {
+ public int Length;
+ public SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+ public byte ContextTrackingMode;
+ public byte EffectiveOnly;
+
+ public SECURITY_QUALITY_OF_SERVICE(SECURITY_IMPERSONATION_LEVEL _impersonationLevel,
+ byte _contextTrackingMode, byte _effectiveOnly)
+ {
+ Length = 0;
+ ImpersonationLevel = _impersonationLevel;
+ ContextTrackingMode = _contextTrackingMode;
+ EffectiveOnly = _effectiveOnly;
+ Length = Marshal.SizeOf(this);
+ }
+ }
+
+ [Flags]
+ public enum SE_PRIVILEGE_ATTRIBUTES : uint
+ {
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001,
+ SE_PRIVILEGE_ENABLED = 0x00000002,
+ SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
+ }
+
+ [Flags]
+ public enum SE_GROUP_ATTRIBUTES : uint
+ {
+ SE_GROUP_MANDATORY = 0x00000001,
+ SE_GROUP_ENABLED_BY_DEFAULT = 0x00000002,
+ SE_GROUP_ENABLED = 0x00000004,
+ SE_GROUP_OWNER = 0x00000008,
+ SE_GROUP_USE_FOR_DENY_ONLY = 0x00000010,
+ SE_GROUP_INTEGRITY = 0x00000020,
+ SE_GROUP_INTEGRITY_ENABLED = 0x00000040,
+ SE_GROUP_RESOURCE = 0x20000000,
+ SE_GROUP_LOGON_ID = 0xC0000000,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_MANDATORY_LABEL
+ {
+ public SID.SID_AND_ATTRIBUTES Label;
+ }
+
+ public const byte SECURITY_STATIC_TRACKING = 0;
+ public static readonly LUID ANONYMOUS_LOGON_LUID = new LUID { LowPart = 0x3e6, HighPart = 0 };
+ public static readonly LUID SYSTEM_LUID = new LUID { LowPart = 0x3e7, HighPart = 0 };
+ public const string SE_CREATE_TOKEN_NAME = "SeCreateTokenPrivilege";
+ public const string SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege";
+ public const string SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege";
+ public const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";
+ public const string SE_MACHINE_ACCOUNT_NAME = "SeMachineAccountPrivilege";
+ public const string SE_TCB_NAME = "SeTcbPrivilege";
+ public const string SE_SECURITY_NAME = "SeSecurityPrivilege";
+ public const string SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege";
+ public const string SE_LOAD_DRIVER_NAME = "SeLoadDriverPrivilege";
+ public const string SE_SYSTEM_PROFILE_NAME = "SeSystemProfilePrivilege";
+ public const string SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege";
+ public const string SE_PROFILE_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege";
+ public const string SE_INCREASE_BASE_PRIORITY_NAME = "SeIncreaseBasePriorityPrivilege";
+ public const string SE_CREATE_PAGEFILE_NAME = "SeCreatePagefilePrivilege";
+ public const string SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege";
+ public const string SE_BACKUP_NAME = "SeBackupPrivilege";
+ public const string SE_RESTORE_NAME = "SeRestorePrivilege";
+ public const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
+ public const string SE_DEBUG_NAME = "SeDebugPrivilege";
+ public const string SE_AUDIT_NAME = "SeAuditPrivilege";
+ public const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
+ public const string SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege";
+ public const string SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege";
+ public const string SE_UNDOCK_NAME = "SeUndockPrivilege";
+ public const string SE_SYNC_AGENT_NAME = "SeSyncAgentPrivilege";
+ public const string SE_ENABLE_DELEGATION_NAME = "SeEnableDelegationPrivilege";
+ public const string SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege";
+ public const string SE_IMPERSONATE_NAME = "SeImpersonatePrivilege";
+ public const string SE_CREATE_GLOBAL_NAME = "SeCreateGlobalPrivilege";
+ public const string SE_TRUSTED_CREDMAN_ACCESS_NAME = "SeTrustedCredManAccessPrivilege";
+ public const string SE_RELABEL_NAME = "SeRelabelPrivilege";
+ public const string SE_INCREASE_WORKING_SET_NAME = "SeIncreaseWorkingSetPrivilege";
+ public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege";
+ public const string SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege";
+
+ public const string SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME =
+ "SeDelegateSessionUserImpersonatePrivilege";
+ }
+
+ public static class TokensEx
+ {
+ public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ // A default constructor is required for P/Invoke to instantiate the class
+ public SafeTokenHandle(IntPtr preexistingHandle)
+ : base(ownsHandle: true)
+ {
+ this.SetHandle(preexistingHandle);
+ }
+ public SafeTokenHandle()
+ : base(ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ return Win32.CloseHandle(handle);
+ }
+ }
+
+ public static bool CreateTokenPrivileges(string[] privs, out Tokens.TOKEN_PRIVILEGES tokenPrivileges)
+ {
+ var sizeOfStruct = Marshal.SizeOf(typeof(Tokens.TOKEN_PRIVILEGES));
+ var pPrivileges = Marshal.AllocHGlobal(sizeOfStruct);
+ tokenPrivileges = (Tokens.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
+ pPrivileges, typeof(Tokens.TOKEN_PRIVILEGES));
+ tokenPrivileges.PrivilegeCount = privs.Length;
+ for (var idx = 0; idx < tokenPrivileges.PrivilegeCount; idx++)
+ {
+ if (!Win32.Tokens.LookupPrivilegeValue(IntPtr.Zero, privs[idx], out var luid)) return false;
+ tokenPrivileges.Privileges[idx].Attributes =
+ (uint)(Tokens.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED |
+ Tokens.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED_BY_DEFAULT);
+ tokenPrivileges.Privileges[idx].Luid = luid;
+ }
+
+ return true;
+ }
+
+ public static void AdjustCurrentPrivilege(string privilege)
+ {
+ Win32.Tokens.LookupPrivilegeValue(IntPtr.Zero, privilege, out LUID luid);
+ Win32.Tokens.RtlAdjustPrivilege(luid, true, true, out _);
+ }
+
+ public static IntPtr GetInfoFromToken(SafeTokenHandle token, Win32.Tokens.TOKEN_INFORMATION_CLASS information)
+ {
+ Win32.Tokens.GetTokenInformation(token, information, IntPtr.Zero, 0, out int length);
+ var result = Marshal.AllocHGlobal(length);
+ Win32.Tokens.GetTokenInformation(token, information, result, length, out length);
+ return result;
+ }
+ }
+
+ public static class Process
+ {
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr GetCurrentProcess();
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
+
+ [DllImport("userenv.dll", SetLastError = true)]
+ public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, TokensEx.SafeTokenHandle hToken, bool bInherit);
+ [DllImport("userenv.dll", SetLastError = true)]
+ public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool CreateProcessAsUser(Win32.TokensEx.SafeTokenHandle hToken, string lpApplicationName, StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes,
+ bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation);
+
+ [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern bool CreateProcessWithToken(TokensEx.SafeTokenHandle hToken, LogonFlags dwLogonFlags,
+ string lpApplicationName, string lpCommandLine, ProcessCreationFlags dwCreationFlags,
+ IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
+ out PROCESS_INFORMATION lpProcessInformation);
+
+ public enum LogonFlags
+ {
+ WithProfile = 1,
+ NetCredentialsOnly
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct PROCESS_INFORMATION
+ {
+ public IntPtr hProcess;
+ public IntPtr hThread;
+ public int dwProcessId;
+ public int dwThreadId;
+ }
+
+ [Flags]
+ public enum ProcessCreationFlags : uint
+ {
+ DEBUG_PROCESS = 0x00000001,
+ DEBUG_ONLY_THIS_PROCESS = 0x00000002,
+ CREATE_SUSPENDED = 0x00000004,
+ DETACHED_PROCESS = 0x00000008,
+ CREATE_NEW_CONSOLE = 0x00000010,
+ CREATE_NEW_PROCESS_GROUP = 0x00000200,
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400,
+ CREATE_SEPARATE_WOW_VDM = 0x00000800,
+ CREATE_SHARED_WOW_VDM = 0x00001000,
+ INHERIT_PARENT_AFFINITY = 0x00010000,
+ CREATE_PROTECTED_PROCESS = 0x00040000,
+ EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
+ CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
+ CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
+ CREATE_DEFAULT_ERROR_MODE = 0x04000000,
+ CREATE_NO_WINDOW = 0x08000000
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct STARTUPINFO
+ {
+ public int cb;
+ public string lpReserved;
+ public string lpDesktop;
+ public string lpTitle;
+ public int dwX;
+ public int dwY;
+ public int dwXSize;
+ public int dwYSize;
+ public int dwXCountChars;
+ public int dwYCountChars;
+ public int dwFillAttribute;
+ public int dwFlags;
+ public short wShowWindow;
+ public short cbReserved2;
+ public IntPtr lpReserved2;
+ public IntPtr hStdInput;
+ public IntPtr hStdOutput;
+ public IntPtr hStdError;
+ }
+ }
+
+ public static class IO
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename,
+ [MarshalAs(UnmanagedType.U4)] FileAccess access, [MarshalAs(UnmanagedType.U4)] FileShare share,
+ IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
+ [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
+ [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, IntPtr templateFile);
+
+ [Flags]
+ public enum FileAccess : uint
+ {
+ AccessSystemSecurity = 0x1000000,
+ MaximumAllowed = 0x2000000,
+ Delete = 0x10000,
+ ReadControl = 0x20000,
+ WriteDAC = 0x40000,
+ WriteOwner = 0x80000,
+ Synchronize = 0x100000,
+ StandardRightsRequired = 0xF0000,
+ StandardRightsRead = ReadControl,
+ StandardRightsWrite = ReadControl,
+ StandardRightsExecute = ReadControl,
+ StandardRightsAll = 0x1F0000,
+ SpecificRightsAll = 0xFFFF,
+ FILE_READ_DATA = 0x0001, // file & pipe
+ FILE_LIST_DIRECTORY = 0x0001, // directory
+ FILE_WRITE_DATA = 0x0002, // file & pipe
+ FILE_ADD_FILE = 0x0002, // directory
+ FILE_APPEND_DATA = 0x0004, // file
+ FILE_ADD_SUBDIRECTORY = 0x0004, // directory
+ FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
+ FILE_READ_EA = 0x0008, // file & directory
+ FILE_WRITE_EA = 0x0010, // file & directory
+ FILE_EXECUTE = 0x0020, // file
+ FILE_TRAVERSE = 0x0020, // directory
+ FILE_DELETE_CHILD = 0x0040, // directory
+ FILE_READ_ATTRIBUTES = 0x0080, // all
+ FILE_WRITE_ATTRIBUTES = 0x0100, // all
+
+ //
+ // Generic Section
+ //
+ GenericRead = 0x80000000,
+ GenericWrite = 0x40000000,
+ GenericExecute = 0x20000000,
+ GenericAll = 0x10000000,
+ SPECIFIC_RIGHTS_ALL = 0x00FFFF,
+ FILE_ALL_ACCESS = StandardRightsRequired | Synchronize | 0x1FF,
+
+ FILE_GENERIC_READ = StandardRightsRead | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA |
+ Synchronize,
+
+ FILE_GENERIC_WRITE = StandardRightsWrite | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA |
+ FILE_APPEND_DATA | Synchronize,
+ FILE_GENERIC_EXECUTE = StandardRightsExecute | FILE_READ_ATTRIBUTES | FILE_EXECUTE | Synchronize
+ }
+
+ [Flags]
+ public enum FileShare : uint
+ {
+ None = 0x00000000,
+ Read = 0x00000001,
+ Write = 0x00000002,
+ Delete = 0x00000004
+ }
+
+ public enum FileMode : uint
+ {
+ New = 1,
+ CreateAlways = 2,
+ OpenExisting = 3,
+ OpenAlways = 4,
+ TruncateExisting = 5
+ }
+
+ [Flags]
+ public enum FileAttributes : uint
+ {
+ Readonly = 0x00000001,
+ Hidden = 0x00000002,
+ System = 0x00000004,
+ Directory = 0x00000010,
+ Archive = 0x00000020,
+ Device = 0x00000040,
+ Normal = 0x00000080,
+ Temporary = 0x00000100,
+ SparseFile = 0x00000200,
+ ReparsePoint = 0x00000400,
+ Compressed = 0x00000800,
+ Offline = 0x00001000,
+ NotContentIndexed = 0x00002000,
+ Encrypted = 0x00004000,
+ Write_Through = 0x80000000,
+ Overlapped = 0x40000000,
+ NoBuffering = 0x20000000,
+ RandomAccess = 0x10000000,
+ SequentialScan = 0x08000000,
+ DeleteOnClose = 0x04000000,
+ BackupSemantics = 0x02000000,
+ PosixSemantics = 0x01000000,
+ OpenReparsePoint = 0x00200000,
+ OpenNoRecall = 0x00100000,
+ FirstPipeInstance = 0x00080000
+ }
+ }
+
+ public static class SID
+ {
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool AllocateAndInitializeSid(ref SID_IDENTIFIER_AUTHORITY pIdentifierAuthority,
+ byte nSubAuthorityCount, int dwSubAuthority0, int dwSubAuthority1, int dwSubAuthority2,
+ int dwSubAuthority3, int dwSubAuthority4, int dwSubAuthority5, int dwSubAuthority6, int dwSubAuthority7,
+ out IntPtr pSid);
+
+ [DllImport("advapi32.dll")]
+ public static extern bool AllocateLocallyUniqueId(out LUID allocated);
+
+ [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern int GetLengthSid(IntPtr pSid);
+
+ [DllImport("advapi32.dll")]
+ public static extern IntPtr FreeSid(IntPtr pSid);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SID_AND_ATTRIBUTES
+ {
+ public IntPtr Sid;
+ public uint Attributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SID_IDENTIFIER_AUTHORITY
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.I1)]
+ public byte[] Value;
+
+ public SID_IDENTIFIER_AUTHORITY(byte[] value)
+ {
+ this.Value = value;
+ }
+ }
+
+ public enum SECURITY_MANDATORY_LABEL
+ {
+ Untrusted = 0x00000000,
+ Low = 0x00001000,
+ Medium = 0x00002000,
+ MediumPlus = 0x00002100,
+ High = 0x00003000,
+ System = 0x00004000,
+ }
+
+ public static SID_IDENTIFIER_AUTHORITY SECURITY_MANDATORY_LABEL_AUTHORITY =
+ new SID_IDENTIFIER_AUTHORITY(new byte[] { 0, 0, 0, 0, 0, 16 });
+ public const int NtSecurityAuthority = 5;
+ public const int AuthenticatedUser = 11;
+
+ public const string DOMAIN_ALIAS_RID_ADMINS = "S-1-5-32-544";
+ public const string DOMAIN_ALIAS_RID_LOCAL_AND_ADMIN_GROUP = "S-1-5-114";
+ public const string TRUSTED_INSTALLER_RID =
+ "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464";
+
+ public const string INTEGRITY_UNTRUSTED_SID = "S-1-16-0";
+ public const string INTEGRITY_LOW_SID = "S-1-16-4096";
+ public const string INTEGRITY_MEDIUM_SID = "S-1-16-8192";
+ public const string INTEGRITY_MEDIUMPLUS_SID = "S-1-16-8448";
+ public const string INTEGRITY_HIGH_SID = "S-1-16-12288";
+ public const string INTEGRITY_SYSTEM_SID = "S-1-16-16384";
+ public const string INTEGRITY_PROTECTEDPROCESS_SID = "S-1-16-20480";
+ }
+
+ public static class WTS
+ {
+ [DllImport("wtsapi32.dll", SetLastError = true)]
+ public static extern int WTSEnumerateSessions(IntPtr hServer, int Reserved, int Version,
+ ref IntPtr ppSessionInfo, ref int pCount);
+
+ [DllImport("wtsapi32.dll", SetLastError = true)]
+ public static extern bool WTSEnumerateProcesses(IntPtr serverHandle, Int32 reserved, Int32 version,
+ ref IntPtr ppProcessInfo, ref Int32 pCount);
+
+ [DllImport("kernel32.dll")]
+ public static extern uint WTSGetActiveConsoleSessionId();
+
+ [DllImport("wtsapi32.dll", SetLastError = true)]
+ public static extern bool WTSQueryUserToken(UInt32 sessionId, out TokensEx.SafeTokenHandle Token);
+
+ [DllImport("wtsapi32.dll")]
+ public static extern void WTSFreeMemory(IntPtr pMemory);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WTS_SESSION_INFO
+ {
+ public Int32 SessionID;
+ [MarshalAs(UnmanagedType.LPStr)]
+ public String pWinStationName;
+ public WTS_CONNECTSTATE_CLASS State;
+ }
+
+ public enum WTS_CONNECTSTATE_CLASS
+ {
+ WTSActive,
+ WTSConnected,
+ WTSConnectQuery,
+ WTSShadow,
+ WTSDisconnected,
+ WTSIdle,
+ WTSListen,
+ WTSReset,
+ WTSDown,
+ WTSInit
+ }
+
+ public struct WTS_PROCESS_INFO
+ {
+ public int SessionID;
+ public int ProcessID;
+ public IntPtr ProcessName;
+ public IntPtr UserSid;
+ }
+ }
+
+ public static class LSA
+ {
+ [DllImport("secur32.dll", SetLastError = false)]
+ public static extern uint LsaFreeReturnBuffer(IntPtr buffer);
+
+ [DllImport("secur32.dll", SetLastError = false)]
+ public static extern uint LsaEnumerateLogonSessions(out ulong LogonSessionCount,
+ out IntPtr LogonSessionList);
+
+ [DllImport("secur32.dll", SetLastError = false)]
+ public static extern uint LsaGetLogonSessionData(IntPtr luid, out IntPtr ppLogonSessionData);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct LSA_UNICODE_STRING
+ {
+ public UInt16 Length;
+ public UInt16 MaximumLength;
+ public IntPtr buffer;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SECURITY_LOGON_SESSION_DATA
+ {
+ public UInt32 Size;
+ public LUID LoginID;
+ public LSA_UNICODE_STRING Username;
+ public LSA_UNICODE_STRING LoginDomain;
+ public LSA_UNICODE_STRING AuthenticationPackage;
+ public UInt32 LogonType;
+ public UInt32 Session;
+ public IntPtr PSiD;
+ public UInt64 LoginTime;
+ public LSA_UNICODE_STRING LogonServer;
+ public LSA_UNICODE_STRING DnsDomainName;
+ public LSA_UNICODE_STRING Upn;
+ }
+
+ public enum SECURITY_LOGON_TYPE : uint
+ {
+ Interactive = 2, //The security principal is logging on interactively.
+ Network, //The security principal is logging using a network.
+ Batch, //The logon is for a batch process.
+ Service, //The logon is for a service account.
+ Proxy, //Not supported.
+ Unlock, //The logon is an attempt to unlock a workstation.
+ NetworkCleartext, //The logon is a network logon with cleartext credentials.
+ NewCredentials, // Allows the caller to clone its current token and specify new credentials for outbound connections.
+ RemoteInteractive, // A terminal server session that is both remote and interactive.
+ CachedInteractive, // Attempt to use the cached credentials without going out across the network.
+ CachedRemoteInteractive, // Same as RemoteInteractive, except used publicly for auditing purposes.
+ CachedUnlock // The logon is an attempt to unlock a workstation.
+ }
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr LocalFree(IntPtr hMem);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [SuppressUnmanagedCodeSecurity]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct LUID
+ {
+ public UInt32 LowPart;
+ public UInt32 HighPart;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct LUID_AND_ATTRIBUTES
+ {
+ public LUID Luid;
+ public UInt32 Attributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public class SECURITY_ATTRIBUTES
+ {
+ public int nLength = 12;
+ public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
+ public bool bInheritHandle;
+ }
+
+ [HostProtection(MayLeakOnAbort = true)]
+ [SuppressUnmanagedCodeSecurityAttribute]
+ public sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
+ internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle)
+ {
+ SetHandle(existingHandle);
+ }
+ [DllImport("kernel32.dll")]
+ [ResourceExposure(ResourceScope.None)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ private static extern IntPtr LocalFree(IntPtr hMem);
+ override protected bool ReleaseHandle()
+ {
+ return LocalFree(handle) == IntPtr.Zero;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit, Size = 8)]
+ public struct LARGE_INTEGER
+ {
+ [FieldOffset(0)] public int Low;
+ [FieldOffset(4)] public int High;
+ [FieldOffset(0)] public long QuadPart;
+
+ public LARGE_INTEGER(long _quad)
+ {
+ Low = 0;
+ High = 0;
+ QuadPart = _quad;
+ }
+
+ public long ToInt64()
+ {
+ return ((long)High << 32) | (uint)Low;
+ }
+
+ public static LARGE_INTEGER FromInt64(long value)
+ {
+ return new LARGE_INTEGER { Low = (int)value, High = (int)(value >> 32) };
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct UNICODE_STRING : IDisposable
+ {
+ public ushort Length;
+ public ushort MaximumLength;
+ private IntPtr buffer;
+
+ public UNICODE_STRING(string s)
+ {
+ Length = (ushort)(s.Length * 2);
+ MaximumLength = (ushort)(Length + 2);
+ buffer = Marshal.StringToHGlobalUni(s);
+ }
+
+ public void Dispose()
+ {
+ Marshal.FreeHGlobal(buffer);
+ buffer = IntPtr.Zero;
+ }
+
+ public override string ToString()
+ {
+ return Marshal.PtrToStringUni(buffer);
+ }
+ }
+
+ public const int STATUS_SUCCESS = 0;
+ public static readonly int STATUS_INFO_LENGTH_MISMATCH = Convert.ToInt32("0xC0000004", 16);
+ public const int ERROR_BAD_LENGTH = 0x00000018;
+ public const int ERROR_INSUFFICIENT_BUFFER = 0x0000007A;
+ public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+
+ public enum NtStatus : uint
+ {
+ // Success
+ Success = 0x00000000,
+ Wait0 = 0x00000000,
+ Wait1 = 0x00000001,
+ Wait2 = 0x00000002,
+ Wait3 = 0x00000003,
+ Wait63 = 0x0000003f,
+ Abandoned = 0x00000080,
+ AbandonedWait0 = 0x00000080,
+ AbandonedWait1 = 0x00000081,
+ AbandonedWait2 = 0x00000082,
+ AbandonedWait3 = 0x00000083,
+ AbandonedWait63 = 0x000000bf,
+ UserApc = 0x000000c0,
+ KernelApc = 0x00000100,
+ Alerted = 0x00000101,
+ Timeout = 0x00000102,
+ Pending = 0x00000103,
+ Reparse = 0x00000104,
+ MoreEntries = 0x00000105,
+ NotAllAssigned = 0x00000106,
+ SomeNotMapped = 0x00000107,
+ OpLockBreakInProgress = 0x00000108,
+ VolumeMounted = 0x00000109,
+ RxActCommitted = 0x0000010a,
+ NotifyCleanup = 0x0000010b,
+ NotifyEnumDir = 0x0000010c,
+ NoQuotasForAccount = 0x0000010d,
+ PrimaryTransportConnectFailed = 0x0000010e,
+ PageFaultTransition = 0x00000110,
+ PageFaultDemandZero = 0x00000111,
+ PageFaultCopyOnWrite = 0x00000112,
+ PageFaultGuardPage = 0x00000113,
+ PageFaultPagingFile = 0x00000114,
+ CrashDump = 0x00000116,
+ ReparseObject = 0x00000118,
+ NothingToTerminate = 0x00000122,
+ ProcessNotInJob = 0x00000123,
+ ProcessInJob = 0x00000124,
+ ProcessCloned = 0x00000129,
+ FileLockedWithOnlyReaders = 0x0000012a,
+ FileLockedWithWriters = 0x0000012b,
+
+ // Informational
+ Informational = 0x40000000,
+ ObjectNameExists = 0x40000000,
+ ThreadWasSuspended = 0x40000001,
+ WorkingSetLimitRange = 0x40000002,
+ ImageNotAtBase = 0x40000003,
+ RegistryRecovered = 0x40000009,
+
+ // Warning
+ Warning = 0x80000000,
+ GuardPageViolation = 0x80000001,
+ DatatypeMisalignment = 0x80000002,
+ Breakpoint = 0x80000003,
+ SingleStep = 0x80000004,
+ BufferOverflow = 0x80000005,
+ NoMoreFiles = 0x80000006,
+ HandlesClosed = 0x8000000a,
+ PartialCopy = 0x8000000d,
+ DeviceBusy = 0x80000011,
+ InvalidEaName = 0x80000013,
+ EaListInconsistent = 0x80000014,
+ NoMoreEntries = 0x8000001a,
+ LongJump = 0x80000026,
+ DllMightBeInsecure = 0x8000002b,
+
+ // Error
+ Error = 0xc0000000,
+ Unsuccessful = 0xc0000001,
+ NotImplemented = 0xc0000002,
+ InvalidInfoClass = 0xc0000003,
+ InfoLengthMismatch = 0xc0000004,
+ AccessViolation = 0xc0000005,
+ InPageError = 0xc0000006,
+ PagefileQuota = 0xc0000007,
+ InvalidHandle = 0xc0000008,
+ BadInitialStack = 0xc0000009,
+ BadInitialPc = 0xc000000a,
+ InvalidCid = 0xc000000b,
+ TimerNotCanceled = 0xc000000c,
+ InvalidParameter = 0xc000000d,
+ NoSuchDevice = 0xc000000e,
+ NoSuchFile = 0xc000000f,
+ InvalidDeviceRequest = 0xc0000010,
+ EndOfFile = 0xc0000011,
+ WrongVolume = 0xc0000012,
+ NoMediaInDevice = 0xc0000013,
+ NoMemory = 0xc0000017,
+ NotMappedView = 0xc0000019,
+ UnableToFreeVm = 0xc000001a,
+ UnableToDeleteSection = 0xc000001b,
+ IllegalInstruction = 0xc000001d,
+ AlreadyCommitted = 0xc0000021,
+ AccessDenied = 0xc0000022,
+ BufferTooSmall = 0xc0000023,
+ ObjectTypeMismatch = 0xc0000024,
+ NonContinuableException = 0xc0000025,
+ BadStack = 0xc0000028,
+ NotLocked = 0xc000002a,
+ NotCommitted = 0xc000002d,
+ InvalidParameterMix = 0xc0000030,
+ ObjectNameInvalid = 0xc0000033,
+ ObjectNameNotFound = 0xc0000034,
+ ObjectNameCollision = 0xc0000035,
+ ObjectPathInvalid = 0xc0000039,
+ ObjectPathNotFound = 0xc000003a,
+ ObjectPathSyntaxBad = 0xc000003b,
+ DataOverrun = 0xc000003c,
+ DataLate = 0xc000003d,
+ DataError = 0xc000003e,
+ CrcError = 0xc000003f,
+ SectionTooBig = 0xc0000040,
+ PortConnectionRefused = 0xc0000041,
+ InvalidPortHandle = 0xc0000042,
+ SharingViolation = 0xc0000043,
+ QuotaExceeded = 0xc0000044,
+ InvalidPageProtection = 0xc0000045,
+ MutantNotOwned = 0xc0000046,
+ SemaphoreLimitExceeded = 0xc0000047,
+ PortAlreadySet = 0xc0000048,
+ SectionNotImage = 0xc0000049,
+ SuspendCountExceeded = 0xc000004a,
+ ThreadIsTerminating = 0xc000004b,
+ BadWorkingSetLimit = 0xc000004c,
+ IncompatibleFileMap = 0xc000004d,
+ SectionProtection = 0xc000004e,
+ EasNotSupported = 0xc000004f,
+ EaTooLarge = 0xc0000050,
+ NonExistentEaEntry = 0xc0000051,
+ NoEasOnFile = 0xc0000052,
+ EaCorruptError = 0xc0000053,
+ FileLockConflict = 0xc0000054,
+ LockNotGranted = 0xc0000055,
+ DeletePending = 0xc0000056,
+ CtlFileNotSupported = 0xc0000057,
+ UnknownRevision = 0xc0000058,
+ RevisionMismatch = 0xc0000059,
+ InvalidOwner = 0xc000005a,
+ InvalidPrimaryGroup = 0xc000005b,
+ NoImpersonationToken = 0xc000005c,
+ CantDisableMandatory = 0xc000005d,
+ NoLogonServers = 0xc000005e,
+ NoSuchLogonSession = 0xc000005f,
+ NoSuchPrivilege = 0xc0000060,
+ PrivilegeNotHeld = 0xc0000061,
+ InvalidAccountName = 0xc0000062,
+ UserExists = 0xc0000063,
+ NoSuchUser = 0xc0000064,
+ GroupExists = 0xc0000065,
+ NoSuchGroup = 0xc0000066,
+ MemberInGroup = 0xc0000067,
+ MemberNotInGroup = 0xc0000068,
+ LastAdmin = 0xc0000069,
+ WrongPassword = 0xc000006a,
+ IllFormedPassword = 0xc000006b,
+ PasswordRestriction = 0xc000006c,
+ LogonFailure = 0xc000006d,
+ AccountRestriction = 0xc000006e,
+ InvalidLogonHours = 0xc000006f,
+ InvalidWorkstation = 0xc0000070,
+ PasswordExpired = 0xc0000071,
+ AccountDisabled = 0xc0000072,
+ NoneMapped = 0xc0000073,
+ TooManyLuidsRequested = 0xc0000074,
+ LuidsExhausted = 0xc0000075,
+ InvalidSubAuthority = 0xc0000076,
+ InvalidAcl = 0xc0000077,
+ InvalidSid = 0xc0000078,
+ InvalidSecurityDescr = 0xc0000079,
+ ProcedureNotFound = 0xc000007a,
+ InvalidImageFormat = 0xc000007b,
+ NoToken = 0xc000007c,
+ BadInheritanceAcl = 0xc000007d,
+ RangeNotLocked = 0xc000007e,
+ DiskFull = 0xc000007f,
+ ServerDisabled = 0xc0000080,
+ ServerNotDisabled = 0xc0000081,
+ TooManyGuidsRequested = 0xc0000082,
+ GuidsExhausted = 0xc0000083,
+ InvalidIdAuthority = 0xc0000084,
+ AgentsExhausted = 0xc0000085,
+ InvalidVolumeLabel = 0xc0000086,
+ SectionNotExtended = 0xc0000087,
+ NotMappedData = 0xc0000088,
+ ResourceDataNotFound = 0xc0000089,
+ ResourceTypeNotFound = 0xc000008a,
+ ResourceNameNotFound = 0xc000008b,
+ ArrayBoundsExceeded = 0xc000008c,
+ FloatDenormalOperand = 0xc000008d,
+ FloatDivideByZero = 0xc000008e,
+ FloatInexactResult = 0xc000008f,
+ FloatInvalidOperation = 0xc0000090,
+ FloatOverflow = 0xc0000091,
+ FloatStackCheck = 0xc0000092,
+ FloatUnderflow = 0xc0000093,
+ IntegerDivideByZero = 0xc0000094,
+ IntegerOverflow = 0xc0000095,
+ PrivilegedInstruction = 0xc0000096,
+ TooManyPagingFiles = 0xc0000097,
+ FileInvalid = 0xc0000098,
+ InstanceNotAvailable = 0xc00000ab,
+ PipeNotAvailable = 0xc00000ac,
+ InvalidPipeState = 0xc00000ad,
+ PipeBusy = 0xc00000ae,
+ IllegalFunction = 0xc00000af,
+ PipeDisconnected = 0xc00000b0,
+ PipeClosing = 0xc00000b1,
+ PipeConnected = 0xc00000b2,
+ PipeListening = 0xc00000b3,
+ InvalidReadMode = 0xc00000b4,
+ IoTimeout = 0xc00000b5,
+ FileForcedClosed = 0xc00000b6,
+ ProfilingNotStarted = 0xc00000b7,
+ ProfilingNotStopped = 0xc00000b8,
+ NotSameDevice = 0xc00000d4,
+ FileRenamed = 0xc00000d5,
+ CantWait = 0xc00000d8,
+ PipeEmpty = 0xc00000d9,
+ CantTerminateSelf = 0xc00000db,
+ InternalError = 0xc00000e5,
+ InvalidParameter1 = 0xc00000ef,
+ InvalidParameter2 = 0xc00000f0,
+ InvalidParameter3 = 0xc00000f1,
+ InvalidParameter4 = 0xc00000f2,
+ InvalidParameter5 = 0xc00000f3,
+ InvalidParameter6 = 0xc00000f4,
+ InvalidParameter7 = 0xc00000f5,
+ InvalidParameter8 = 0xc00000f6,
+ InvalidParameter9 = 0xc00000f7,
+ InvalidParameter10 = 0xc00000f8,
+ InvalidParameter11 = 0xc00000f9,
+ InvalidParameter12 = 0xc00000fa,
+ MappedFileSizeZero = 0xc000011e,
+ TooManyOpenedFiles = 0xc000011f,
+ Cancelled = 0xc0000120,
+ CannotDelete = 0xc0000121,
+ InvalidComputerName = 0xc0000122,
+ FileDeleted = 0xc0000123,
+ SpecialAccount = 0xc0000124,
+ SpecialGroup = 0xc0000125,
+ SpecialUser = 0xc0000126,
+ MembersPrimaryGroup = 0xc0000127,
+ FileClosed = 0xc0000128,
+ TooManyThreads = 0xc0000129,
+ ThreadNotInProcess = 0xc000012a,
+ TokenAlreadyInUse = 0xc000012b,
+ PagefileQuotaExceeded = 0xc000012c,
+ CommitmentLimit = 0xc000012d,
+ InvalidImageLeFormat = 0xc000012e,
+ InvalidImageNotMz = 0xc000012f,
+ InvalidImageProtect = 0xc0000130,
+ InvalidImageWin16 = 0xc0000131,
+ LogonServer = 0xc0000132,
+ DifferenceAtDc = 0xc0000133,
+ SynchronizationRequired = 0xc0000134,
+ DllNotFound = 0xc0000135,
+ IoPrivilegeFailed = 0xc0000137,
+ OrdinalNotFound = 0xc0000138,
+ EntryPointNotFound = 0xc0000139,
+ ControlCExit = 0xc000013a,
+ PortNotSet = 0xc0000353,
+ DebuggerInactive = 0xc0000354,
+ CallbackBypass = 0xc0000503,
+ PortClosed = 0xc0000700,
+ MessageLost = 0xc0000701,
+ InvalidMessage = 0xc0000702,
+ RequestCanceled = 0xc0000703,
+ RecursiveDispatch = 0xc0000704,
+ LpcReceiveBufferExpected = 0xc0000705,
+ LpcInvalidConnectionUsage = 0xc0000706,
+ LpcRequestsNotAllowed = 0xc0000707,
+ ResourceInUse = 0xc0000708,
+ ProcessIsProtected = 0xc0000712,
+ VolumeDirty = 0xc0000806,
+ FileCheckedOut = 0xc0000901,
+ CheckOutRequired = 0xc0000902,
+ BadFileType = 0xc0000903,
+ FileTooLarge = 0xc0000904,
+ FormsAuthRequired = 0xc0000905,
+ VirusInfected = 0xc0000906,
+ VirusDeleted = 0xc0000907,
+ TransactionalConflict = 0xc0190001,
+ InvalidTransaction = 0xc0190002,
+ TransactionNotActive = 0xc0190003,
+ TmInitializationFailed = 0xc0190004,
+ RmNotActive = 0xc0190005,
+ RmMetadataCorrupt = 0xc0190006,
+ TransactionNotJoined = 0xc0190007,
+ DirectoryNotRm = 0xc0190008,
+ CouldNotResizeLog = 0xc0190009,
+ TransactionsUnsupportedRemote = 0xc019000a,
+ LogResizeInvalidSize = 0xc019000b,
+ RemoteFileVersionMismatch = 0xc019000c,
+ CrmProtocolAlreadyExists = 0xc019000f,
+ TransactionPropagationFailed = 0xc0190010,
+ CrmProtocolNotFound = 0xc0190011,
+ TransactionSuperiorExists = 0xc0190012,
+ TransactionRequestNotValid = 0xc0190013,
+ TransactionNotRequested = 0xc0190014,
+ TransactionAlreadyAborted = 0xc0190015,
+ TransactionAlreadyCommitted = 0xc0190016,
+ TransactionInvalidMarshallBuffer = 0xc0190017,
+ CurrentTransactionNotValid = 0xc0190018,
+ LogGrowthFailed = 0xc0190019,
+ ObjectNoLongerExists = 0xc0190021,
+ StreamMiniversionNotFound = 0xc0190022,
+ StreamMiniversionNotValid = 0xc0190023,
+ MiniversionInaccessibleFromSpecifiedTransaction = 0xc0190024,
+ CantOpenMiniversionWithModifyIntent = 0xc0190025,
+ CantCreateMoreStreamMiniversions = 0xc0190026,
+ HandleNoLongerValid = 0xc0190028,
+ NoTxfMetadata = 0xc0190029,
+ LogCorruptionDetected = 0xc0190030,
+ CantRecoverWithHandleOpen = 0xc0190031,
+ RmDisconnected = 0xc0190032,
+ EnlistmentNotSuperior = 0xc0190033,
+ RecoveryNotNeeded = 0xc0190034,
+ RmAlreadyStarted = 0xc0190035,
+ FileIdentityNotPersistent = 0xc0190036,
+ CantBreakTransactionalDependency = 0xc0190037,
+ CantCrossRmBoundary = 0xc0190038,
+ TxfDirNotEmpty = 0xc0190039,
+ IndoubtTransactionsExist = 0xc019003a,
+ TmVolatile = 0xc019003b,
+ RollbackTimerExpired = 0xc019003c,
+ TxfAttributeCorrupt = 0xc019003d,
+ EfsNotAllowedInTransaction = 0xc019003e,
+ TransactionalOpenNotAllowed = 0xc019003f,
+ TransactedMappingUnsupportedRemote = 0xc0190040,
+ TxfMetadataAlreadyPresent = 0xc0190041,
+ TransactionScopeCallbacksNotSet = 0xc0190042,
+ TransactionRequiredPromotion = 0xc0190043,
+ CannotExecuteFileInTransaction = 0xc0190044,
+ TransactionsNotFrozen = 0xc0190045,
+ MaximumNtStatus = 0xffffffff
+ }
+ }
+}
\ No newline at end of file
diff --git a/TrustedUninstaller.Shared/WinUtil.cs b/TrustedUninstaller.Shared/WinUtil.cs
index 4ff3927..162448a 100644
--- a/TrustedUninstaller.Shared/WinUtil.cs
+++ b/TrustedUninstaller.Shared/WinUtil.cs
@@ -5,7 +5,6 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
-using System.Management.Automation;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -18,6 +17,7 @@ using System.Windows;
using System.Windows.Interop;
using Microsoft.Win32;
using TrustedUninstaller.Shared.Actions;
+using TrustedUninstaller.Shared.Tasks;
namespace TrustedUninstaller.Shared
{
@@ -444,7 +444,7 @@ namespace TrustedUninstaller.Shared
if (res == ERROR_MORE_DATA)
{
// Create an array to store the process results
- RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
+ RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded + 3];
pnProcInfo = pnProcInfoNeeded;
// Get the list
@@ -465,7 +465,7 @@ namespace TrustedUninstaller.Shared
catch (ArgumentException) { }
}
}
- else throw new Exception("Could not list processes locking resource.");
+ else throw new Exception("Could not list processes locking resource: " + res);
}
else if (res != 0)
throw new Exception("Could not list processes locking resource. Could not get size of result." + $" Result value: {res}");
@@ -609,37 +609,60 @@ namespace TrustedUninstaller.Shared
public static async Task RemoveProtectionAsync()
{
var cmdAction = new CmdAction();
- if (!IsVCInstalled())
+
+ if (AmeliorationUtil.UseKernelDriver)
{
- Console.WriteLine(Environment.NewLine + "Installing VC 15...");
+ /*
+ if (!IsVCInstalled())
+ {
+ Console.WriteLine(Environment.NewLine + "Installing VC 15...");
+ try
+ {
+ //Install Visual C++ 2015 redistributable package silently
+ cmdAction.Command = "vc_redist.x64.exe /q /norestart";
+ cmdAction.RunTaskOnMainThread();
+ }
+ catch (Exception e)
+ {
+ ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Error while installing VC 15.");
+ throw;
+ }
+ }
+ */
+
try
{
- //Install Visual C++ 2015 redistributable package silently
- cmdAction.Command = "vc_redist.x64.exe /q /norestart";
- await cmdAction.RunTask();
+ Console.WriteLine(Environment.NewLine + "Installing driver...");
+ cmdAction.Command = Environment.Is64BitOperatingSystem
+ ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -installkph"
+ : $"ProcessHacker\\x86\\ProcessHacker.exe -s -installkph";
+ cmdAction.RunTaskOnMainThread();
+
+ await AmeliorationUtil.SafeRunAction(new RegistryValueAction()
+ { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2", Value = "DeleteFlag", Type = RegistryValueType.REG_DWORD, Data = 1 });
}
catch (Exception e)
{
- ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Error while installing VC 15.");
+ ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an error while installing its driver.");
throw;
}
}
+ }
- try
- {
- Console.WriteLine(Environment.NewLine + "Installing driver...");
- cmdAction.Command = Environment.Is64BitOperatingSystem
- ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -installkph"
- : $"ProcessHacker\\x86\\ProcessHacker.exe -s -installkph";
- var res = await cmdAction.RunTask();
-
- }
- catch (Exception e)
- {
- ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an error while installing its driver.");
- throw;
- }
-
+ public static async void CheckKph()
+ {
+ if (!AmeliorationUtil.UseKernelDriver || new RegistryKeyAction() { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2", Operation = RegistryKeyOperation.Add }.GetStatus() == UninstallTaskStatus.Completed)
+ return;
+
+ Console.WriteLine(Environment.NewLine + "Installing driver...");
+ var cmdAction = new CmdAction();
+ cmdAction.Command = Environment.Is64BitOperatingSystem
+ ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -installkph"
+ : $"ProcessHacker\\x86\\ProcessHacker.exe -s -installkph";
+ cmdAction.RunTaskOnMainThread();
+
+ await AmeliorationUtil.SafeRunAction(new RegistryValueAction()
+ { KeyName = @"HKLM\SYSTEM\CurrentControlSet\Services\KProcessHacker2", Value = "DeleteFlag", Type = RegistryValueType.REG_DWORD, Data = 1 });
}
private const int GWL_STYLE = -16;
@@ -686,6 +709,10 @@ namespace TrustedUninstaller.Shared
public static async Task UninstallDriver()
{
+ if (AmeliorationUtil.UseKernelDriver == false)
+ return;
+ AmeliorationUtil.UseKernelDriver = false;
+
CmdAction cmdAction = new CmdAction();
try
{
@@ -693,7 +720,7 @@ namespace TrustedUninstaller.Shared
cmdAction.Command = Environment.Is64BitOperatingSystem
? $"ProcessHacker\\x64\\ProcessHacker.exe -s -uninstallkph"
: $"ProcessHacker\\x86\\ProcessHacker.exe -s -uninstallkph";
- await cmdAction.RunTask();
+ cmdAction.RunTaskOnMainThread();
}
catch (Exception e)
{