在我的应用程序中,最初我使用了
user3122201的解决方案,它非常有效。但是,我希望提升的应用程序可以使用匿名管道与低权限应用程序通信。为了实现这一点,我需要一个允许管道句柄被继承的解决方案。
我提出了以下解决方案,允许句柄继承。
请注意,此解决方案和user3122201的另一个小差异:下面的方法以相同用户的进程运行,但具有受限访问权限,而user3122201的方法则以桌面用户的进程运行。
public static class ProcessHelper
{
public static Process? RunAsRestrictedUser(string fileName, string? args = null)
{
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
if (!GetRestrictedSessionUserToken(out var hRestrictedToken))
{
return null;
}
try
{
var si = new STARTUPINFO();
var pi = new PROCESS_INFORMATION();
var cmd = new StringBuilder();
cmd.Append('"').Append(fileName).Append('"');
if (!string.IsNullOrWhiteSpace(args))
{
cmd.Append(' ').Append(args);
}
if (!CreateProcessAsUser(
hRestrictedToken,
null,
cmd,
IntPtr.Zero,
IntPtr.Zero,
true,
0,
IntPtr.Zero,
Path.GetDirectoryName(fileName),
ref si,
out pi))
{
return null;
}
return Process.GetProcessById(pi.dwProcessId);
}
finally
{
CloseHandle(hRestrictedToken);
}
}
private static bool GetRestrictedSessionUserToken(out IntPtr token)
{
token = IntPtr.Zero;
if (!SaferCreateLevel(SaferScope.User, SaferLevel.NormalUser, SaferOpenFlags.Open, out var hLevel, IntPtr.Zero))
{
return false;
}
IntPtr hRestrictedToken = IntPtr.Zero;
TOKEN_MANDATORY_LABEL tml = default;
tml.Label.Sid = IntPtr.Zero;
IntPtr tmlPtr = IntPtr.Zero;
try
{
if (!SaferComputeTokenFromLevel(hLevel, IntPtr.Zero, out hRestrictedToken, 0, IntPtr.Zero))
{
return false;
}
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = IntPtr.Zero;
if (!ConvertStringSidToSid("S-1-16-8192", out tml.Label.Sid))
{
return false;
}
tmlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tml));
Marshal.StructureToPtr(tml, tmlPtr, false);
if (!SetTokenInformation(hRestrictedToken,
TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
tmlPtr, (uint)Marshal.SizeOf(tml)))
{
return false;
}
token = hRestrictedToken;
hRestrictedToken = IntPtr.Zero;
}
finally
{
SaferCloseLevel(hLevel);
SafeCloseHandle(hRestrictedToken);
if (tml.Label.Sid != IntPtr.Zero)
{
LocalFree(tml.Label.Sid);
}
if (tmlPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(tmlPtr);
}
}
return true;
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private 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)]
private struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct TOKEN_MANDATORY_LABEL
{
public SID_AND_ATTRIBUTES Label;
}
public enum SaferLevel : uint
{
Disallowed = 0,
Untrusted = 0x1000,
Constrained = 0x10000,
NormalUser = 0x20000,
FullyTrusted = 0x40000
}
public enum SaferScope : uint
{
Machine = 1,
User = 2
}
[Flags]
public enum SaferOpenFlags : uint
{
Open = 1
}
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool SaferCreateLevel(SaferScope scope, SaferLevel level, SaferOpenFlags openFlags, out IntPtr pLevelHandle, IntPtr lpReserved);
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved);
[DllImport("advapi32", SetLastError = true)]
private static extern bool SaferCloseLevel(IntPtr hLevelHandle);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private static bool SafeCloseHandle(IntPtr hObject)
{
return (hObject == IntPtr.Zero) ? true : CloseHandle(hObject);
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalFree(IntPtr hMem);
enum TOKEN_INFORMATION_CLASS
{
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,
MaxTokenInfoClass
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean SetTokenInformation(
IntPtr TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
UInt32 TokenInformationLength);
const uint SE_GROUP_INTEGRITY = 0x00000020;
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string? lpApplicationName,
StringBuilder? lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string? lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
}
SetTokenInformation
删除高完整性SID,然后使用修改后的令牌使用CreateProcessAsUser
启动新进程。 - Simon MᶜKenzie