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 = Win32.Process.OpenProcess(Win32.Process.ProcessAccessFlags.QueryLimitedInformation, false, Process.GetProcessesByName("lsass").First().Id); 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 = Win32.Process.OpenProcess(Win32.Process.ProcessAccessFlags.QueryLimitedInformation, false, Process.GetProcessesByName("winlogon").First().Id); 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; } } }