# this is a very stripped-down C# port of NSudo: # https://github.com/M2Team/NSudo # you should already be running this script as an administrator. param([string]$command="cmd") function Execute-Elevated { param([string]$command="cmd") $Definition = @" using System; using System.Runtime.InteropServices; using System.Diagnostics; public class Dummy { const int CREATE_NO_WINDOW = 0x8000000; const int CREATE_UNICODE_ENVIRONMENT = 0x400; const int MAXIMUM_ALLOWED = 0x2000000; const int PROCESS_QUERY_INFORMATION = 0x400; const int SE_PRIVILEGE_ENABLED = 0x2; const int SecurityImpersonation = 2; const int TOKEN_QUERY = 0x8; const int TokenPrimary = 1; const int TokenImpersonation = 2; const int TokenUser = 1; [StructLayout(LayoutKind.Sequential)] public struct SID_AND_ATTRIBUTES { public IntPtr Sid; public int Attributes; } public struct TOKEN_USER { public SID_AND_ATTRIBUTES User; } [StructLayout(LayoutKind.Sequential)] public struct LUID { public UInt32 LowPart; public Int32 HighPart; } // hack to make a simple TOKEN_PRIVILEGES struct when Count=1. [StructLayout(LayoutKind.Sequential, Pack=1)] public struct TokenPrivilegeSingle { public int Count; public LUID Luid; public uint Attr; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct STARTUPINFOW { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public ushort wShowWindow; public ushort cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [DllImport("kernel32.dll")] static extern IntPtr LocalFree( IntPtr hMem); [DllImport("kernel32.dll", SetLastError=true)] static extern bool CloseHandle( IntPtr hHandle); [DllImport("userenv.dll", SetLastError=true)] static extern bool CreateEnvironmentBlock( out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); [DllImport("userenv.dll", SetLastError=true)] static extern bool DestroyEnvironmentBlock( IntPtr lpEnvironment); [DllImport("advapi32", CharSet=CharSet.Auto, SetLastError=true)] static extern bool ConvertSidToStringSid( IntPtr pSID, out IntPtr ptrSid); [DllImport("kernel32.dll", SetLastError=true)] static extern IntPtr OpenProcess( int dwDesiredAccess, bool bInheritHandle, long dwProcessId); [DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)] static extern bool OpenProcessToken( IntPtr h, int acc, out IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool SetThreadToken( IntPtr pHandle, IntPtr hToken); [DllImport("advapi32.dll", SetLastError=true)] static extern bool GetTokenInformation( IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, out int ReturnLength); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] static extern bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, int ImpersonationLevel, int TokenType, out IntPtr phNewToken); [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] static extern bool CreateProcessAsUserW( IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFOW lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", SetLastError = true)] static extern bool AdjustTokenPrivileges( IntPtr TokenHandle, bool DisableAllPrivileges, ref TokenPrivilegeSingle NewState, uint Zero, IntPtr Null1, IntPtr Null2); public static string ConvertSidToStringSid(IntPtr sid) { IntPtr pstr = IntPtr.Zero; bool ok = ConvertSidToStringSid(sid, out pstr); if (!ok) { LocalFree(pstr); throw new Exception("ConvertSidToStringSid: not ok"); } string sidstr = Marshal.PtrToStringAuto(pstr); LocalFree(pstr); return sidstr; } public static bool HasPrivileges(int pid) { bool ok = true; IntPtr hProc = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid); if (hProc == IntPtr.Zero) throw new Exception("OpenProcess: null"); IntPtr hToken = IntPtr.Zero; ok = OpenProcessToken(hProc, TOKEN_QUERY, out hToken); if (!ok || hToken == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null"); int tiLength = -1; GetTokenInformation(hToken, TokenUser, IntPtr.Zero, 0, out tiLength); IntPtr ti = Marshal.AllocHGlobal(tiLength); ok = GetTokenInformation(hToken, TokenUser, ti, tiLength, out tiLength); if (!ok) { Marshal.FreeHGlobal(ti); throw new Exception("GetTokenInformation: not ok"); } TOKEN_USER tokenUser = (TOKEN_USER)Marshal.PtrToStructure(ti, typeof(TOKEN_USER)); string sidstr = ConvertSidToStringSid(tokenUser.User.Sid); return sidstr == "S-1-5-18"; // described as "Local System" } public static int FindSystemPid(string processName) { Process currentProc = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(processName); int pid = -1; foreach (Process proc in processes) { if (currentProc.SessionId == proc.SessionId && HasPrivileges(proc.Id)) { pid = proc.Id; break; } } if (pid == -1) throw new Exception("FindSystemPid: couldn't find " + processName); return pid; } public static IntPtr DuplicateProcessToken(int pid, int impLevel, int tokenType) { // desiredAccess is always MAXIMUM_ALLOWED // lpTokenAttributes is always null (IntPtr.Zero) bool ok = true; IntPtr hProc = OpenProcess(MAXIMUM_ALLOWED, false, pid); if (hProc == IntPtr.Zero) throw new Exception("OpenProcess: null"); IntPtr hToken = IntPtr.Zero; ok = OpenProcessToken(hProc, MAXIMUM_ALLOWED, out hToken); if (!ok || hToken == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null"); IntPtr phToken = IntPtr.Zero; ok = DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, IntPtr.Zero, impLevel, tokenType, out phToken); if (!ok || phToken == IntPtr.Zero) throw new Exception("DuplicateTokenEx: not ok or null"); return phToken; } public static void CreateProcessW(IntPtr hToken, IntPtr environment, string directory, string comSpec, string command) { bool ok = true; STARTUPINFOW startupInfo = new STARTUPINFOW(); PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION(); startupInfo.cb = Marshal.SizeOf(startupInfo); startupInfo.lpDesktop = "WinSta0\\Default"; ok = CreateProcessAsUserW(hToken, comSpec, command, IntPtr.Zero, IntPtr.Zero, false, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, environment, directory, ref startupInfo, out processInfo); if (!ok) { int err = Marshal.GetLastWin32Error(); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); throw new Exception("CreateProcessAsUser failure: " + err); } } public static IntPtr CreateEnvironment(IntPtr hToken) { bool ok = true; IntPtr environment; ok = CreateEnvironmentBlock(out environment, hToken, true); if (!ok || environment == IntPtr.Zero) throw new Exception("CreateEnvironmentBlock: not ok or null"); return environment; } public static void SetTokenAllPrivileges(IntPtr hToken, bool enable) { // turns out the only one we need is 3: SE_ASSIGNPRIMARYTOKEN_PRIVILEGE bool ok = true; TokenPrivilegeSingle tp = new TokenPrivilegeSingle(); tp.Count = 1; tp.Attr = (uint)(enable ? SE_PRIVILEGE_ENABLED : 0); tp.Luid.LowPart = 3; ok = AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); if (!ok) throw new Exception("AdjustTokenPrivileges: not ok"); int err = Marshal.GetLastWin32Error(); if (err != 0) throw new Exception("AdjustTokenPrivileges failure: " + err); } public static void Execute(string comSpec, string command) { bool ok = true; IntPtr hTokenSelf; ok = OpenProcessToken((IntPtr)(-1), MAXIMUM_ALLOWED, out hTokenSelf); if (!ok || hTokenSelf == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null"); IntPtr environment = CreateEnvironment(hTokenSelf); int pid = FindSystemPid("winlogon"); Console.WriteLine("winlogon PID: " + pid); IntPtr hTokenSystemImpersonate = DuplicateProcessToken(pid, SecurityImpersonation, TokenImpersonation); SetTokenAllPrivileges(hTokenSystemImpersonate, true); ok = SetThreadToken(IntPtr.Zero, hTokenSystemImpersonate); if (!ok) throw new Exception("SetThreadToken: not ok"); IntPtr hTokenSystemPrimary = DuplicateProcessToken(pid, SecurityImpersonation, TokenPrimary); string fullCommand = "/c start \"" + comSpec + "\" " + command; Console.WriteLine(comSpec + " " + fullCommand); CreateProcessW(hTokenSystemPrimary, environment, null, comSpec, fullCommand); // TODO: always clean up regardless of exceptions. DestroyEnvironmentBlock(environment); } } "@ $type = Add-Type $definition -PassThru $type[0]::Execute((get-item env:"ComSpec").Value, $command) } Execute-Elevated $command exit