gists/kill_reboot/elevate.ps1
2018-10-11 16:45:31 +02:00

314 lines
11 KiB
PowerShell

# 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