using System; using System.ComponentModel; using System.Globalization; using System.Runtime.InteropServices; namespace SweetPotato { internal class ImpersonationToken { // Constants that are going to be used during our procedure. public static uint SE_PRIVILEGE_ENABLED = 0x00000002; public static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000; public static uint STANDARD_RIGHTS_READ = 0x00020000; public static uint TOKEN_ASSIGN_PRIMARY = 0x00000001; public static uint TOKEN_DUPLICATE = 0x00000002; public static uint TOKEN_IMPERSONATE = 0x00000004; public static uint TOKEN_QUERY = 0x00000008; public static uint TOKEN_QUERY_SOURCE = 0x00000010; public static uint TOKEN_ADJUST_PRIVILEGES = 0x00000020; public static uint TOKEN_ADJUST_GROUPS = 0x00000040; public static uint TOKEN_ADJUST_DEFAULT = 0x00000080; public static uint TOKEN_ADJUST_SESSIONID = 0x00000100; public static uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY; public static uint TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; public static uint MAXIMUM_ALLOWED = 0x02000000; public static uint DETACHED_PROCESS = 0x00000008; public static uint CREATE_NEW_CONSOLE = 0x00000010; public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } [StructLayout(LayoutKind.Sequential)] internal struct LUID { internal Int32 LowPart; internal UInt32 HighPart; } [StructLayout(LayoutKind.Sequential)] internal struct TOKEN_PRIVILEGES { internal Int32 PrivilegeCount; internal LUID Luid; internal UInt32 Attributes; } // This also works with CharSet.Ansi as long as the calling function uses the same character set. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } public enum LogonFlags { WithProfile = 1, NetCredentialsOnly } public enum CreationFlags { DefaultErrorMode = 0x04000000, DetachedProcess = 0x00000008, NewConsole = 0x00000010, NewProcessGroup = 0x00000200, SeparateWOWVDM = 0x00000800, Suspended = 0x00000004, UnicodeEnvironment = 0x00000400, ExtendedStartupInfoPresent = 0x00080000 } public enum SecurityEntity { SE_CREATE_TOKEN_NAME, SE_ASSIGNPRIMARYTOKEN_NAME, SE_LOCK_MEMORY_NAME, SE_INCREASE_QUOTA_NAME, SE_UNSOLICITED_INPUT_NAME, SE_MACHINE_ACCOUNT_NAME, SE_TCB_NAME, SE_SECURITY_NAME, SE_TAKE_OWNERSHIP_NAME, SE_LOAD_DRIVER_NAME, SE_SYSTEM_PROFILE_NAME, SE_SYSTEMTIME_NAME, SE_PROF_SINGLE_PROCESS_NAME, SE_INC_BASE_PRIORITY_NAME, SE_CREATE_PAGEFILE_NAME, SE_CREATE_PERMANENT_NAME, SE_BACKUP_NAME, SE_RESTORE_NAME, SE_SHUTDOWN_NAME, SE_DEBUG_NAME, SE_AUDIT_NAME, SE_SYSTEM_ENVIRONMENT_NAME, SE_CHANGE_NOTIFY_NAME, SE_REMOTE_SHUTDOWN_NAME, SE_UNDOCK_NAME, SE_SYNC_AGENT_NAME, SE_ENABLE_DELEGATION_NAME, SE_MANAGE_VOLUME_NAME, SE_IMPERSONATE_NAME, SE_CREATE_GLOBAL_NAME, SE_CREATE_SYMBOLIC_LINK_NAME, SE_INC_WORKING_SET_NAME, SE_RELABEL_NAME, SE_TIME_ZONE_NAME, SE_TRUSTED_CREDMAN_ACCESS_NAME } [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags, string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool CreateProcessAsUserW(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll")] public static extern uint WTSGetActiveConsoleSessionId(); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool AdjustTokenPrivileges(IntPtr tokenhandle, [MarshalAs(UnmanagedType.Bool)] bool disableAllPrivileges, [MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES newstate, uint bufferlength, IntPtr previousState, IntPtr returnlength); // OpenProcessToken [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool OpenThreadToken(IntPtr ThreadHandle, uint DesiredAccess, bool OpenAsSelf, out IntPtr TokenHandle); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool SetThreadToken(IntPtr pHandle, IntPtr hToken); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll")] public static extern IntPtr GetCurrentThread(); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern Boolean CloseHandle(IntPtr hObject); [DllImport("userenv.dll", SetLastError = true)] public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); private static string GetSecurityEntityValue(SecurityEntity securityEntity) { switch (securityEntity) { case SecurityEntity.SE_ASSIGNPRIMARYTOKEN_NAME: return "SeAssignPrimaryTokenPrivilege"; case SecurityEntity.SE_AUDIT_NAME: return "SeAuditPrivilege"; case SecurityEntity.SE_BACKUP_NAME: return "SeBackupPrivilege"; case SecurityEntity.SE_CHANGE_NOTIFY_NAME: return "SeChangeNotifyPrivilege"; case SecurityEntity.SE_CREATE_GLOBAL_NAME: return "SeCreateGlobalPrivilege"; case SecurityEntity.SE_CREATE_PAGEFILE_NAME: return "SeCreatePagefilePrivilege"; case SecurityEntity.SE_CREATE_PERMANENT_NAME: return "SeCreatePermanentPrivilege"; case SecurityEntity.SE_CREATE_SYMBOLIC_LINK_NAME: return "SeCreateSymbolicLinkPrivilege"; case SecurityEntity.SE_CREATE_TOKEN_NAME: return "SeCreateTokenPrivilege"; case SecurityEntity.SE_DEBUG_NAME: return "SeDebugPrivilege"; case SecurityEntity.SE_ENABLE_DELEGATION_NAME: return "SeEnableDelegationPrivilege"; case SecurityEntity.SE_IMPERSONATE_NAME: return "SeImpersonatePrivilege"; case SecurityEntity.SE_INC_BASE_PRIORITY_NAME: return "SeIncreaseBasePriorityPrivilege"; case SecurityEntity.SE_INCREASE_QUOTA_NAME: return "SeIncreaseQuotaPrivilege"; case SecurityEntity.SE_INC_WORKING_SET_NAME: return "SeIncreaseWorkingSetPrivilege"; case SecurityEntity.SE_LOAD_DRIVER_NAME: return "SeLoadDriverPrivilege"; case SecurityEntity.SE_LOCK_MEMORY_NAME: return "SeLockMemoryPrivilege"; case SecurityEntity.SE_MACHINE_ACCOUNT_NAME: return "SeMachineAccountPrivilege"; case SecurityEntity.SE_MANAGE_VOLUME_NAME: return "SeManageVolumePrivilege"; case SecurityEntity.SE_PROF_SINGLE_PROCESS_NAME: return "SeProfileSingleProcessPrivilege"; case SecurityEntity.SE_RELABEL_NAME: return "SeRelabelPrivilege"; case SecurityEntity.SE_REMOTE_SHUTDOWN_NAME: return "SeRemoteShutdownPrivilege"; case SecurityEntity.SE_RESTORE_NAME: return "SeRestorePrivilege"; case SecurityEntity.SE_SECURITY_NAME: return "SeSecurityPrivilege"; case SecurityEntity.SE_SHUTDOWN_NAME: return "SeShutdownPrivilege"; case SecurityEntity.SE_SYNC_AGENT_NAME: return "SeSyncAgentPrivilege"; case SecurityEntity.SE_SYSTEM_ENVIRONMENT_NAME: return "SeSystemEnvironmentPrivilege"; case SecurityEntity.SE_SYSTEM_PROFILE_NAME: return "SeSystemProfilePrivilege"; case SecurityEntity.SE_SYSTEMTIME_NAME: return "SeSystemtimePrivilege"; case SecurityEntity.SE_TAKE_OWNERSHIP_NAME: return "SeTakeOwnershipPrivilege"; case SecurityEntity.SE_TCB_NAME: return "SeTcbPrivilege"; case SecurityEntity.SE_TIME_ZONE_NAME: return "SeTimeZonePrivilege"; case SecurityEntity.SE_TRUSTED_CREDMAN_ACCESS_NAME: return "SeTrustedCredManAccessPrivilege"; case SecurityEntity.SE_UNDOCK_NAME: return "SeUndockPrivilege"; default: throw new ArgumentOutOfRangeException(typeof(SecurityEntity).Name); } } public static bool EnablePrivilege(SecurityEntity securityEntity) { const int ERROR_NOT_ALL_ASSIGNED = 1300; if (!Enum.IsDefined(typeof(SecurityEntity), securityEntity)) throw new InvalidEnumArgumentException("securityEntity", (int)securityEntity, typeof(SecurityEntity)); var securityEntityValue = GetSecurityEntityValue(securityEntity); try { var locallyUniqueIdentifier = new LUID(); if (LookupPrivilegeValue(null, securityEntityValue, ref locallyUniqueIdentifier)) { var TOKEN_PRIVILEGES = new TOKEN_PRIVILEGES(); TOKEN_PRIVILEGES.PrivilegeCount = 1; TOKEN_PRIVILEGES.Attributes = SE_PRIVILEGE_ENABLED; TOKEN_PRIVILEGES.Luid = locallyUniqueIdentifier; var tokenHandle = IntPtr.Zero; try { var currentProcess = GetCurrentProcess(); if (OpenProcessToken(currentProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out tokenHandle)) { if (AdjustTokenPrivileges(tokenHandle, false, ref TOKEN_PRIVILEGES, 1024, IntPtr.Zero, IntPtr.Zero)) { var lastError = Marshal.GetLastWin32Error(); if (lastError == ERROR_NOT_ALL_ASSIGNED) { //Not likley to have the privilege return false; } else if (lastError != 0) { var win32Exception = new Win32Exception(); throw new InvalidOperationException("AdjustTokenPrivileges failed.", win32Exception); } else { return true; } } else { var win32Exception = new Win32Exception(); throw new InvalidOperationException("AdjustTokenPrivileges failed.", win32Exception); } } else { var win32Exception = new Win32Exception(); var exceptionMessage = string.Format(CultureInfo.InvariantCulture, "OpenProcessToken failed. CurrentProcess: {0}", currentProcess.ToInt32()); throw new InvalidOperationException(exceptionMessage, win32Exception); } } finally { if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle); } } else { var win32Exception = new Win32Exception(); var exceptionMessage = string.Format(CultureInfo.InvariantCulture, "LookupPrivilegeValue failed. SecurityEntityValue: {0}", securityEntityValue); throw new InvalidOperationException(exceptionMessage, win32Exception); } } catch (Exception e) { var exceptionMessage = string.Format(CultureInfo.InvariantCulture, "GrandPrivilege failed. SecurityEntity: {0}", securityEntityValue); throw new InvalidOperationException(exceptionMessage, e); } } } }