我想使用C#来确定为我的进程/线程令牌分配了哪些权限,并根据需要进行调整。例如,为了让我的程序重新启动计算机,它必须首先启用SeShutdownPrivilege
权限。
如何从托管代码安全地完成?
答案 0 :(得分:10)
这是非平凡的,因为它没有内置机制。不仅需要P / Invoke,而且您必须仔细编码,以确保您不会通过启用它们来“泄漏”权限,然后不会很快禁用它们(如果您重新启动计算机,则不会出现问题)。 / p>
有关描述的完整代码示例,请阅读Mark Novak撰写的2005年3月“Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently”的MSDN杂志文章。
这是P / Invoke声明:
using System;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
namespace PrivilegeClass
{
[Flags]
internal enum TokenAccessLevels
{
AssignPrimary = 0x00000001,
Duplicate = 0x00000002,
Impersonate = 0x00000004,
Query = 0x00000008,
QuerySource = 0x00000010,
AdjustPrivileges = 0x00000020,
AdjustGroups = 0x00000040,
AdjustDefault = 0x00000080,
AdjustSessionId = 0x00000100,
Read = 0x00020000 | Query,
Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault,
AllAccess = 0x000F0000 |
AssignPrimary |
Duplicate |
Impersonate |
Query |
QuerySource |
AdjustPrivileges |
AdjustGroups |
AdjustDefault |
AdjustSessionId,
MaximumAllowed = 0x02000000
}
internal enum SecurityImpersonationLevel
{
Anonymous = 0,
Identification = 1,
Impersonation = 2,
Delegation = 3,
}
internal enum TokenType
{
Primary = 1,
Impersonation = 2,
}
internal sealed class NativeMethods
{
internal const uint SE_PRIVILEGE_DISABLED = 0x00000000;
internal const uint SE_PRIVILEGE_ENABLED = 0x00000002;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct LUID
{
internal uint LowPart;
internal uint HighPart;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct LUID_AND_ATTRIBUTES
{
internal LUID Luid;
internal uint Attributes;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct TOKEN_PRIVILEGE
{
internal uint PrivilegeCount;
internal LUID_AND_ATTRIBUTES Privilege;
}
internal const string ADVAPI32 = "advapi32.dll";
internal const string KERNEL32 = "kernel32.dll";
internal const int ERROR_SUCCESS = 0x0;
internal const int ERROR_ACCESS_DENIED = 0x5;
internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
internal const int ERROR_NO_TOKEN = 0x3f0;
internal const int ERROR_NOT_ALL_ASSIGNED = 0x514;
internal const int ERROR_NO_SUCH_PRIVILEGE = 0x521;
internal const int ERROR_CANT_OPEN_ANONYMOUS = 0x543;
[DllImport(
KERNEL32,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool CloseHandle(IntPtr handle);
[DllImport(
ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool AdjustTokenPrivileges (
[In] SafeTokenHandle TokenHandle,
[In] bool DisableAllPrivileges,
[In] ref TOKEN_PRIVILEGE NewState,
[In] uint BufferLength,
[In,Out] ref TOKEN_PRIVILEGE PreviousState,
[In,Out] ref uint ReturnLength);
[DllImport(
ADVAPI32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool RevertToSelf();
[DllImport(
ADVAPI32,
EntryPoint="LookupPrivilegeValueW",
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool LookupPrivilegeValue (
[In] string lpSystemName,
[In] string lpName,
[In,Out] ref LUID Luid);
[DllImport(
KERNEL32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
IntPtr GetCurrentProcess();
[DllImport(
KERNEL32,
CharSet=CharSet.Auto,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
IntPtr GetCurrentThread ();
[DllImport(
ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool OpenProcessToken (
[In] IntPtr ProcessToken,
[In] TokenAccessLevels DesiredAccess,
[In,Out] ref SafeTokenHandle TokenHandle);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool OpenThreadToken(
[In] IntPtr ThreadToken,
[In] TokenAccessLevels DesiredAccess,
[In] bool OpenAsSelf,
[In,Out] ref SafeTokenHandle TokenHandle);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool DuplicateTokenEx(
[In] SafeTokenHandle ExistingToken,
[In] TokenAccessLevels DesiredAccess,
[In] IntPtr TokenAttributes,
[In] SecurityImpersonationLevel ImpersonationLevel,
[In] TokenType TokenType,
[In,Out] ref SafeTokenHandle NewToken);
[DllImport
(ADVAPI32,
CharSet=CharSet.Unicode,
SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern
bool SetThreadToken(
[In] IntPtr Thread,
[In] SafeTokenHandle Token);
static NativeMethods()
{
}
}
}
答案 1 :(得分:1)
这就是我用的。它基于Mark Novak的文章,但是对于不受信任的堆栈帧,CER或重入(由于我假设您不是在编写Internet Explorer或SQL Server加载项),偏执症较少。
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<LUID_AND_ATTRIBUTES>();
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<LUID_AND_ATTRIBUTES>(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
}
}
在另一个用户的会话中创建进程的示例用法:
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;
}
}
}