How can I manipulate token privileges in .Net?

后端 未结 2 434
谎友^
谎友^ 2021-01-12 03:59

I\'d like to use C# to determine which privileges are assigned to my process/thread token, and adjust them as necessary. For example, in order for my program to restart the

2条回答
  •  天涯浪人
    2021-01-12 04:38

    Here's what I use. It is based off of the Mark Novak article, but with less paranoia for untrusted stack frames, CER's, or reentrance (since I assume you are not writing internet explorer or a SQL Server Add-in).

    Privilege.cs:

    using System;
    using Microsoft.Win32.SafeHandles;
    using System.Collections.Specialized;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.ConstrainedExecution;
    using System.Threading;
    using Luid = Esatto.Win32.Processes.NativeMethods.LUID;
    using Win32Exception = System.ComponentModel.Win32Exception;
    using PrivilegeNotHeldException = System.Security.AccessControl.PrivilegeNotHeldException;
    using static Esatto.Win32.Processes.NativeMethods;
    using System.Linq;
    using System.Security.Principal;
    
    // http://msdn.microsoft.com/en-us/magazine/cc163823.aspx
    namespace Esatto.Win32.Processes
    {
        public static class Privilege
        {
            #region Privilege names
    
            public const string 
                CreateToken = "SeCreateTokenPrivilege",
                AssignPrimaryToken = "SeAssignPrimaryTokenPrivilege",
                LockMemory = "SeLockMemoryPrivilege",
                IncreaseQuota = "SeIncreaseQuotaPrivilege",
                UnsolicitedInput = "SeUnsolicitedInputPrivilege",
                MachineAccount = "SeMachineAccountPrivilege",
                TrustedComputingBase = "SeTcbPrivilege",
                Security = "SeSecurityPrivilege",
                TakeOwnership = "SeTakeOwnershipPrivilege",
                LoadDriver = "SeLoadDriverPrivilege",
                SystemProfile = "SeSystemProfilePrivilege",
                SystemTime = "SeSystemtimePrivilege",
                ProfileSingleProcess = "SeProfileSingleProcessPrivilege",
                IncreaseBasePriority = "SeIncreaseBasePriorityPrivilege",
                CreatePageFile = "SeCreatePagefilePrivilege",
                CreatePermanent = "SeCreatePermanentPrivilege",
                Backup = "SeBackupPrivilege",
                Restore = "SeRestorePrivilege",
                Shutdown = "SeShutdownPrivilege",
                Debug = "SeDebugPrivilege",
                Audit = "SeAuditPrivilege",
                SystemEnvironment = "SeSystemEnvironmentPrivilege",
                ChangeNotify = "SeChangeNotifyPrivilege",
                RemoteShutdown = "SeRemoteShutdownPrivilege",
                Undock = "SeUndockPrivilege",
                SyncAgent = "SeSyncAgentPrivilege",
                EnableDelegation = "SeEnableDelegationPrivilege",
                ManageVolume = "SeManageVolumePrivilege",
                Impersonate = "SeImpersonatePrivilege",
                CreateGlobal = "SeCreateGlobalPrivilege",
                TrustedCredentialManagerAccess = "SeTrustedCredManAccessPrivilege",
                ReserveProcessor = "SeReserveProcessorPrivilege";
    
            #endregion
    
            public static void RunWithPrivileges(Action action, params string[] privs)
            {
                if (privs == null || privs.Length == 0)
                {
                    throw new ArgumentNullException(nameof(privs));
                }
                var luids = privs
                    .Select(e => new LUID_AND_ATTRIBUTES { Luid = GetLuidForName(e), Attributes = SE_PRIVILEGE_ENABLED })
                    .ToArray();
    
                RuntimeHelpers.PrepareConstrainedRegions();
                try { /* CER */ }
                finally
                {
                    using (var threadToken = new ThreadTokenScope())
                    using (new ThreadPrivilegeScope(threadToken, luids))
                    {
                        action();
                    }
                }
            }
    
            private static LUID_AND_ATTRIBUTES[] AdjustTokenPrivileges2(SafeTokenHandle token, LUID_AND_ATTRIBUTES[] attrs)
            {
                var sizeofAttr = Marshal.SizeOf();
                var pDesired = Marshal.AllocHGlobal(4 /* count */ + attrs.Length * sizeofAttr);
                try
                {
                    // Fill pStruct
                    {
                        Marshal.WriteInt32(pDesired, attrs.Length);
                        var pAttr = pDesired + 4;
                        for (int i = 0; i < attrs.Length; i++)
                        {
                            Marshal.StructureToPtr(attrs[i], pAttr, false);
                            pAttr += sizeofAttr;
                        }
                    }
    
                    // Call Adjust
                    const int cbPrevious = 16384 /* some arbitrarily high number */;
                    var pPrevious = Marshal.AllocHGlobal(cbPrevious);
                    try
                    {
                        if (!AdjustTokenPrivileges(token, false, pDesired, cbPrevious, pPrevious, out var retLen))
                        {
                            throw new Win32Exception();
                        }
    
                        // Parse result
                        {
                            var result = new LUID_AND_ATTRIBUTES[Marshal.ReadInt32(pPrevious)];
                            var pAttr = pPrevious + 4;
                            for (int i = 0; i < result.Length; i++)
                            {
                                result[i] = Marshal.PtrToStructure(pAttr);
                            }
                            return result;
                        }
                    }
                    finally { Marshal.FreeHGlobal(pPrevious); }
                }
                finally { Marshal.FreeHGlobal(pDesired); }
            }
    
            private static Luid GetLuidForName(string priv)
            {
                if (!LookupPrivilegeValue(null, priv, out var result))
                {
                    throw new Win32Exception();
                }
    
                return result;
            }
    
            private class ThreadPrivilegeScope : IDisposable
            {
                private LUID_AND_ATTRIBUTES[] RevertTo;
                private Thread OwnerThread;
                private readonly ThreadTokenScope Token;
    
                public ThreadPrivilegeScope(ThreadTokenScope token, LUID_AND_ATTRIBUTES[] setTo)
                {
                    this.OwnerThread = Thread.CurrentThread;
                    this.Token = token ?? throw new ArgumentNullException(nameof(token));
    
                    this.RevertTo = AdjustTokenPrivileges2(token.Handle, setTo);
                }
    
                public void Dispose()
                {
                    if (OwnerThread != Thread.CurrentThread)
                    {
                        throw new InvalidOperationException("Wrong thread");
                    }
                    if (RevertTo == null)
                    {
                        return;
                    }
    
                    AdjustTokenPrivileges2(Token.Handle, RevertTo);
                }
            }
    
            private class ThreadTokenScope : IDisposable
            {
                private bool IsImpersonating;
                private readonly Thread OwnerThread;
                public readonly SafeTokenHandle Handle;
    
                [ThreadStatic]
                private static ThreadTokenScope Current;
    
                public ThreadTokenScope()
                {
                    if (Current != null)
                    {
                        throw new InvalidOperationException("Reentrance to ThreadTokenScope");
                    }
    
                    this.OwnerThread = Thread.CurrentThread;
    
                    if (!OpenThreadToken(GetCurrentThread(), TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges, true, out var token))
                    {
                        var error = Marshal.GetLastWin32Error();
                        if (error != ERROR_NO_TOKEN)
                        {
                            throw new Win32Exception(error);
                        }
    
                        // No token is on the thread, copy from process
                        if (!OpenProcessToken(GetCurrentProcess(), TokenAccessLevels.Duplicate, out var processToken))
                        {
                            throw new Win32Exception();
                        }
    
                        if (!DuplicateTokenEx(processToken, TokenAccessLevels.Impersonate | TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
                            IntPtr.Zero, SecurityImpersonationLevel.Impersonation, TokenType.Impersonation, out token))
                        {
                            throw new Win32Exception();
                        }
    
                        if (!SetThreadToken(IntPtr.Zero, token))
                        {
                            throw new Win32Exception();
                        }
                        this.IsImpersonating = true;
                    }
    
                    this.Handle = token;
                    Current = this;
                }
    
                public void Dispose()
                {
                    if (OwnerThread != Thread.CurrentThread)
                    {
                        throw new InvalidOperationException("Wrong thread");
                    }
                    if (Current != this)
                    {
                        throw new ObjectDisposedException(nameof(ThreadTokenScope));
                    }
                    Current = null;
    
                    if (IsImpersonating)
                    {
                        RevertToSelf();
                    }
                    IsImpersonating = false;
                }
            }
        }
    }
    

    NativeMethods.cs:

    using Microsoft.Win32.SafeHandles;
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Text;
    
    namespace Esatto.Win32.Processes
    {
        internal static class NativeMethods
        {
            const string Advapi32 = "advapi32.dll";
            const string Kernel32 = "kernel32.dll";
            const string Wtsapi32 = "wtsapi32.dll";
            const string Userenv = "userenv.dll";
    
            #region constants
    
            public const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000;
            public const uint SE_PRIVILEGE_DISABLED = 0x00000000;
            public const uint SE_PRIVILEGE_ENABLED = 0x00000002;
            public const int ERROR_SUCCESS = 0x0;
            public const int ERROR_ACCESS_DENIED = 0x5;
            public const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
            public const int ERROR_NO_TOKEN = 0x3f0;
            public const int ERROR_NOT_ALL_ASSIGNED = 0x514;
            public const int ERROR_NO_SUCH_PRIVILEGE = 0x521;
            public const int ERROR_CANT_OPEN_ANONYMOUS = 0x543;
            public const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
            public const uint STANDARD_RIGHTS_READ = 0x00020000;
            public const uint NORMAL_PRIORITY_CLASS = 0x0020;
            public const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
            public const uint MAX_PATH = 260;
            public const uint CREATE_NO_WINDOW = 0x08000000;
            public const uint INFINITE = 0xFFFFFFFF;
    
            #endregion
    
            #region Advapi32
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool AdjustTokenPrivileges(SafeTokenHandle TokenHandle, bool DisableAllPrivileges,
                IntPtr NewState, uint BufferLength, IntPtr PreviousState, out uint ReturnLength);
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool RevertToSelf();
    
            [DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID Luid);
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool OpenProcessToken(IntPtr ProcessToken, TokenAccessLevels DesiredAccess, out SafeTokenHandle TokenHandle);
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool OpenThreadToken(IntPtr ThreadToken, TokenAccessLevels DesiredAccess, bool OpenAsSelf, out SafeTokenHandle TokenHandle);
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool DuplicateTokenEx(SafeTokenHandle ExistingToken, TokenAccessLevels DesiredAccess,
                IntPtr TokenAttributes, SecurityImpersonationLevel ImpersonationLevel, TokenType TokenType, out SafeTokenHandle NewToken);
    
            [DllImport(Advapi32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool SetThreadToken(IntPtr Thread, SafeTokenHandle Token);
    
            [DllImport(Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern bool CreateProcessAsUser(SafeTokenHandle hToken,
                StringBuilder appExeName, StringBuilder commandLine, IntPtr processAttributes,
                IntPtr threadAttributes, bool inheritHandles, uint dwCreationFlags,
                EnvironmentBlockSafeHandle environment, string currentDirectory, ref STARTUPINFO startupInfo,
                out PROCESS_INFORMATION startupInformation);
    
            [DllImport(Advapi32, CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool GetTokenInformation(IntPtr TokenHandle,
                TokenInformationClass TokenInformationClass, out int TokenInformation,
                uint TokenInformationLength, out uint ReturnLength);
    
            #endregion
    
            #region Kernel32
    
            [DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern bool CloseHandle(IntPtr handle);
    
            [DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern IntPtr GetCurrentProcess();
    
            [DllImport(Kernel32, ExactSpelling = true, SetLastError = true)]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
            public static extern IntPtr GetCurrentThread();
    
            [DllImport(Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
            public static extern SafeJobHandle CreateJobObject(IntPtr lpJobAttributes, string lpName);
    
            [DllImport(Kernel32, SetLastError = true)]
            public static extern bool SetInformationJobObject(SafeJobHandle hJob, JobObjectInfoType infoType,
                ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, int cbJobObjectInfoLength);
    
            [DllImport(Kernel32, SetLastError = true)]
            public static extern bool AssignProcessToJobObject(SafeJobHandle job, IntPtr process);
    
            #endregion
    
            #region Wtsapi
    
            [DllImport(Wtsapi32, ExactSpelling = true, SetLastError = true)]
            public static extern bool WTSQueryUserToken(int sessionid, out SafeTokenHandle handle);
    
            #endregion
    
            #region Userenv
    
            [DllImport(Userenv, CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern bool CreateEnvironmentBlock(out EnvironmentBlockSafeHandle lpEnvironment, SafeTokenHandle hToken, bool bInherit);
    
            [DllImport(Userenv, ExactSpelling = true, SetLastError = true)]
            public extern static bool DestroyEnvironmentBlock(IntPtr hEnvironment);
    
            #endregion
    
            #region Structs
    
            [StructLayout(LayoutKind.Sequential)]
            public struct IO_COUNTERS
            {
                public ulong ReadOperationCount;
                public ulong WriteOperationCount;
                public ulong OtherOperationCount;
                public ulong ReadTransferCount;
                public ulong WriteTransferCount;
                public ulong OtherTransferCount;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct JOBOBJECT_BASIC_LIMIT_INFORMATION
            {
                public long PerProcessUserTimeLimit;
                public long PerJobUserTimeLimit;
                public uint LimitFlags;
                public UIntPtr MinimumWorkingSetSize;
                public UIntPtr MaximumWorkingSetSize;
                public uint ActiveProcessLimit;
                public UIntPtr Affinity;
                public uint PriorityClass;
                public uint SchedulingClass;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct SECURITY_ATTRIBUTES
            {
                public uint nLength;
                public IntPtr lpSecurityDescriptor;
                public int bInheritHandle;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
            {
                public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
                public IO_COUNTERS IoInfo;
                public UIntPtr ProcessMemoryLimit;
                public UIntPtr JobMemoryLimit;
                public UIntPtr PeakProcessMemoryUsed;
                public UIntPtr PeakJobMemoryUsed;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            internal struct PROCESS_INFORMATION
            {
                public IntPtr hProcess;
                public IntPtr hThread;
                public uint dwProcessId;
                public uint dwThreadId;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            internal 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;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct LUID
            {
                public uint LowPart;
                public uint HighPart;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct LUID_AND_ATTRIBUTES
            {
                public LUID Luid;
                public uint Attributes;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct TOKEN_PRIVILEGE
            {
                public uint PrivilegeCount;
                public LUID_AND_ATTRIBUTES Privilege;
            }
    
            #endregion
    
            #region Enums
    
            public enum JobObjectInfoType
            {
                AssociateCompletionPortInformation = 7,
                BasicLimitInformation = 2,
                BasicUIRestrictions = 4,
                EndOfJobTimeInformation = 6,
                ExtendedLimitInformation = 9,
                SecurityLimitInformation = 5,
                GroupInformation = 11
            }
    
            public enum SecurityImpersonationLevel
            {
                Anonymous = 0,
                Identification = 1,
                Impersonation = 2,
                Delegation = 3,
            }
    
            public enum TokenType
            {
                Primary = 1,
                Impersonation = 2,
            }
    
            public enum TokenInformationClass
            {
                TokenUser = 1,
                TokenGroups,
                TokenPrivileges,
                TokenOwner,
                TokenPrimaryGroup,
                TokenDefaultDacl,
                TokenSource,
                TokenType,
                TokenImpersonationLevel,
                TokenStatistics,
                TokenRestrictedSids,
                TokenSessionId,
                TokenGroupsAndPrivileges,
                TokenSessionReference,
                TokenSandBoxInert,
                TokenAuditPolicy,
                TokenOrigin,
                TokenElevationType,
                TokenLinkedToken,
                TokenElevation,
                TokenHasRestrictions,
                TokenAccessInformation,
                TokenVirtualizationAllowed,
                TokenVirtualizationEnabled,
                TokenIntegrityLevel,
                TokenUIAccess,
                TokenMandatoryPolicy,
                TokenLogonSid,
                TokenIsAppContainer,
                TokenCapabilities,
                TokenAppContainerSid,
                TokenAppContainerNumber,
                TokenUserClaimAttributes,
                TokenDeviceClaimAttributes,
                TokenRestrictedUserClaimAttributes,
                TokenRestrictedDeviceClaimAttributes,
                TokenDeviceGroups,
                TokenRestrictedDeviceGroups,
                // MaxTokenInfoClass should always be the last enum
                MaxTokenInfoClass
            }
    
            #endregion
    
            #region SafeHandles
    
            public sealed class EnvironmentBlockSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
            {
                public EnvironmentBlockSafeHandle()
                    : base(true)
                {
                }
    
                protected override bool ReleaseHandle()
                {
                    return DestroyEnvironmentBlock(handle);
                }
            }
    
            public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
            {
                public SafeTokenHandle()
                    : base(true)
                {
                }
    
                override protected bool ReleaseHandle()
                {
                    return CloseHandle(handle);
                }
            }
    
            internal sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid
            {
                public SafeJobHandle()
                    : base(true)
                {
                }
    
                protected override bool ReleaseHandle()
                {
                    return CloseHandle(this.handle);
                }
            }
    
            #endregion
        }
    }
    

    Example Usage to create a process in another user's session:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Esatto.Win32.Processes
    {
        using System.ComponentModel;
        using System.Runtime.InteropServices;
        using System.Security.Principal;
        using static NativeMethods;
    
    #if ESATTO_WIN32
        public
    #else
        internal
    #endif
            static class ProcessInterop
        {
            public static void CreateProcessForSession(int sessionId, string exePath, string commandLine)
            {
                var privs = new[] { Privilege.TrustedComputingBase, Privilege.AssignPrimaryToken, Privilege.IncreaseQuota };
                Privilege.RunWithPrivileges(() => CreateProcessForSessionInternal(sessionId, exePath, commandLine), privs);
            }
    
            private static void CreateProcessForSessionInternal(int sessionId, string exePath, string commandLine)
            {
                SafeTokenHandle hDupToken;
                {
                    SafeTokenHandle hToken;
                    if (!WTSQueryUserToken(sessionId, out hToken))
                    {
                        throw new Win32Exception();
                    }
    
                    using (hToken)
                    {
                        if (!DuplicateTokenEx(hToken, TokenAccessLevels.AllAccess, IntPtr.Zero, SecurityImpersonationLevel.Impersonation, TokenType.Primary, out hDupToken))
                        {
                            throw new Win32Exception();
                        }
                    }
                }
    
                using (hDupToken)
                {
                    EnvironmentBlockSafeHandle env;
                    if (!CreateEnvironmentBlock(out env, hDupToken, false))
                    {
                        throw new Win32Exception();
                    }
    
                    using (env)
                    {
                        STARTUPINFO si = new STARTUPINFO();
                        si.cb = Marshal.SizeOf(si);
    
                        PROCESS_INFORMATION procInfo;
                        if (!CreateProcessAsUser(hDupToken, new StringBuilder(exePath), new StringBuilder(commandLine),
                            IntPtr.Zero, IntPtr.Zero, false, NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, env,
                            null, ref si, out procInfo))
                        {
                            throw new Win32Exception();
                        }
    
                        CloseHandle(procInfo.hProcess);
                        CloseHandle(procInfo.hThread);
                    }
                }
            }
    
            public static int GetSessionId(this WindowsIdentity ident)
            {
                if (ident == null)
                {
                    throw new ArgumentNullException();
                }
    
                int sessionId;
                uint unused;
                if (!GetTokenInformation(ident.Token, TokenInformationClass.TokenSessionId, out sessionId, 4, out unused))
                {
                    throw new Win32Exception();
                }
                // since we are not passing a SafeHandle, we need to keep our reference on the SafeHandle
                // contained in the WindowsIdentity
                GC.KeepAlive(ident);
    
                return sessionId;
            }
        }
    }
    

提交回复
热议问题