mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
308 lines
12 KiB
C#
308 lines
12 KiB
C#
|
|
// Copyright (c) Microsoft Corporation
|
|||
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|||
|
|
// See the LICENSE file in the project root for more information.
|
|||
|
|
|
|||
|
|
using System;
|
|||
|
|
using System.ComponentModel;
|
|||
|
|
using System.Diagnostics;
|
|||
|
|
using System.Diagnostics.CodeAnalysis;
|
|||
|
|
using System.Globalization;
|
|||
|
|
using System.Runtime.InteropServices;
|
|||
|
|
using System.Security.Principal;
|
|||
|
|
|
|||
|
|
// <summary>
|
|||
|
|
// Impersonation.
|
|||
|
|
// </summary>
|
|||
|
|
// <history>
|
|||
|
|
// 2008 created by Truong Do (ductdo).
|
|||
|
|
// 2009-... modified by Truong Do (TruongDo).
|
|||
|
|
// 2023- Included in PowerToys.
|
|||
|
|
// </history>
|
|||
|
|
using MouseWithoutBorders.Class;
|
|||
|
|
|
|||
|
|
namespace MouseWithoutBorders
|
|||
|
|
{
|
|||
|
|
internal partial class Common
|
|||
|
|
{
|
|||
|
|
internal static bool RunElevated()
|
|||
|
|
{
|
|||
|
|
return WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
internal static bool ImpersonateLoggedOnUserAndDoSomething(Action targetFunc)
|
|||
|
|
{
|
|||
|
|
if (Common.RunWithNoAdminRight)
|
|||
|
|
{
|
|||
|
|
targetFunc();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
uint dwSessionId;
|
|||
|
|
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
dwSessionId = (uint)Process.GetCurrentProcess().SessionId;
|
|||
|
|
uint rv = NativeMethods.WTSQueryUserToken(dwSessionId, ref hUserToken);
|
|||
|
|
LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture));
|
|||
|
|
|
|||
|
|
if (rv == 0)
|
|||
|
|
{
|
|||
|
|
LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}.");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!NativeMethods.DuplicateToken(hUserToken, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref hUserTokenDup))
|
|||
|
|
{
|
|||
|
|
TelemetryLogTrace($"DuplicateToken Failed! {GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (NativeMethods.ImpersonateLoggedOnUser(hUserTokenDup))
|
|||
|
|
{
|
|||
|
|
targetFunc();
|
|||
|
|
_ = NativeMethods.RevertToSelf();
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Log("ImpersonateLoggedOnUser Failed!");
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
Common.Log(e);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")]
|
|||
|
|
internal static int CreateProcessInInputDesktopSession(string commandLine, string arg, string desktop, short wShowWindow, bool lowIntegrity = false)
|
|||
|
|
|
|||
|
|
// As user who runs explorer.exe
|
|||
|
|
{
|
|||
|
|
if (!Program.User.ToLower(CultureInfo.InvariantCulture).Contains("system"))
|
|||
|
|
{
|
|||
|
|
ProcessStartInfo s = new(commandLine, arg);
|
|||
|
|
s.WindowStyle = wShowWindow != 0 ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
|
|||
|
|
Process p = Process.Start(s);
|
|||
|
|
|
|||
|
|
return p == null ? 0 : p.Id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string commandLineWithArg = commandLine + " " + arg;
|
|||
|
|
int lastError;
|
|||
|
|
int dwSessionId;
|
|||
|
|
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
|
|||
|
|
|
|||
|
|
Common.LogDebug("CreateProcessInInputDesktopSession called, launching " + commandLineWithArg + " on " + desktop);
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
dwSessionId = Process.GetCurrentProcess().SessionId;
|
|||
|
|
|
|||
|
|
// Get the user token used by DuplicateTokenEx
|
|||
|
|
lastError = (int)NativeMethods.WTSQueryUserToken((uint)dwSessionId, ref hUserToken);
|
|||
|
|
|
|||
|
|
NativeMethods.STARTUPINFO si = default;
|
|||
|
|
si.cb = Marshal.SizeOf(si);
|
|||
|
|
si.lpDesktop = "winsta0\\" + desktop;
|
|||
|
|
si.wShowWindow = wShowWindow;
|
|||
|
|
|
|||
|
|
NativeMethods.SECURITY_ATTRIBUTES sa = default;
|
|||
|
|
sa.Length = Marshal.SizeOf(sa);
|
|||
|
|
|
|||
|
|
if (!NativeMethods.DuplicateTokenEx(hUserToken, NativeMethods.MAXIMUM_ALLOWED, ref sa, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)NativeMethods.TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
|
|||
|
|
{
|
|||
|
|
lastError = Marshal.GetLastWin32Error();
|
|||
|
|
Common.Log(string.Format(CultureInfo.CurrentCulture, "DuplicateTokenEx error: {0} Token does not have the privilege.", lastError));
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (lowIntegrity)
|
|||
|
|
{
|
|||
|
|
NativeMethods.TOKEN_MANDATORY_LABEL tIL;
|
|||
|
|
|
|||
|
|
// Low
|
|||
|
|
string sIntegritySid = "S-1-16-4096";
|
|||
|
|
|
|||
|
|
bool rv = NativeMethods.ConvertStringSidToSid(sIntegritySid, out IntPtr pIntegritySid);
|
|||
|
|
|
|||
|
|
if (!rv)
|
|||
|
|
{
|
|||
|
|
Log("ConvertStringSidToSid failed");
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tIL.Label.Attributes = NativeMethods.SE_GROUP_INTEGRITY;
|
|||
|
|
tIL.Label.Sid = pIntegritySid;
|
|||
|
|
|
|||
|
|
rv = NativeMethods.SetTokenInformation(hUserTokenDup, NativeMethods.TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, ref tIL, (uint)Marshal.SizeOf(tIL) + (uint)IntPtr.Size);
|
|||
|
|
|
|||
|
|
if (!rv)
|
|||
|
|
{
|
|||
|
|
Log("SetTokenInformation failed");
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
uint dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS | NativeMethods.CREATE_NEW_CONSOLE;
|
|||
|
|
IntPtr pEnv = IntPtr.Zero;
|
|||
|
|
|
|||
|
|
if (NativeMethods.CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
|
|||
|
|
{
|
|||
|
|
dwCreationFlags |= NativeMethods.CREATE_UNICODE_ENVIRONMENT;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
pEnv = IntPtr.Zero;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_ = NativeMethods.CreateProcessAsUser(
|
|||
|
|
hUserTokenDup, // client's access token
|
|||
|
|
null, // file to execute
|
|||
|
|
commandLineWithArg, // command line
|
|||
|
|
ref sa, // pointer to process SECURITY_ATTRIBUTES
|
|||
|
|
ref sa, // pointer to thread SECURITY_ATTRIBUTES
|
|||
|
|
false, // handles are not inheritable
|
|||
|
|
(int)dwCreationFlags, // creation flags
|
|||
|
|
pEnv, // pointer to new environment block
|
|||
|
|
null, // name of current directory
|
|||
|
|
ref si, // pointer to STARTUPINFO structure
|
|||
|
|
out NativeMethods.PROCESS_INFORMATION pi); // receives information about new process
|
|||
|
|
|
|||
|
|
// GetLastError should be 0
|
|||
|
|
int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
|
|||
|
|
LogDebug("CreateProcessAsUser returned " + iResultOfCreateProcessAsUser.ToString(CultureInfo.CurrentCulture));
|
|||
|
|
|
|||
|
|
// Close handles task
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserToken);
|
|||
|
|
_ = NativeMethods.CloseHandle(hUserTokenDup);
|
|||
|
|
|
|||
|
|
return (iResultOfCreateProcessAsUser == 0) ? (int)pi.dwProcessId : 0;
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
Common.Log(e);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if CUSTOMIZE_LOGON_SCREEN
|
|||
|
|
internal static bool CreateLowIntegrityProcess(string commandLine, string args, int wait, bool killIfTimedOut, long limitedMem, short wShowWindow = 0)
|
|||
|
|
{
|
|||
|
|
int processId = CreateProcessInInputDesktopSession(commandLine, args, "default", wShowWindow, true);
|
|||
|
|
|
|||
|
|
if (processId <= 0)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (wait > 0)
|
|||
|
|
{
|
|||
|
|
if (limitedMem > 0)
|
|||
|
|
{
|
|||
|
|
int sec = 0;
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
Process p;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if ((p = Process.GetProcessById(processId)) == null)
|
|||
|
|
{
|
|||
|
|
Log("Process exited!");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (ArgumentException)
|
|||
|
|
{
|
|||
|
|
Log("GetProcessById.ArgumentException");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((!p.HasExited && p.PrivateMemorySize64 > limitedMem) || (++sec > (wait / 1000)))
|
|||
|
|
{
|
|||
|
|
Log(string.Format(CultureInfo.CurrentCulture, "Process log (mem): {0}, {1}", sec, p.PrivateMemorySize64));
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Thread.Sleep(1000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Process p;
|
|||
|
|
|
|||
|
|
if ((p = Process.GetProcessById(processId)) == null)
|
|||
|
|
{
|
|||
|
|
Log("Process exited!");
|
|||
|
|
}
|
|||
|
|
else if (NativeMethods.WaitForSingleObject(p.Handle, wait) != NativeMethods.WAIT_OBJECT_0 && killIfTimedOut)
|
|||
|
|
{
|
|||
|
|
Log("Process log (time).");
|
|||
|
|
TerminateProcessTree(p.Handle, (uint)processId, -1);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
internal static void TerminateProcessTree(IntPtr hProcess, uint processID, int exitCode)
|
|||
|
|
{
|
|||
|
|
if (processID > 0 && hProcess.ToInt32() > 0)
|
|||
|
|
{
|
|||
|
|
Process[] processes = Process.GetProcesses();
|
|||
|
|
int dwSessionId = Process.GetCurrentProcess().SessionId;
|
|||
|
|
|
|||
|
|
foreach (Process p in processes)
|
|||
|
|
{
|
|||
|
|
if (p.SessionId == dwSessionId)
|
|||
|
|
{
|
|||
|
|
NativeMethods.PROCESS_BASIC_INFORMATION processBasicInformation = default;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (NativeMethods.NtQueryInformationProcess(p.Handle, 0, ref processBasicInformation, (uint)Marshal.SizeOf(processBasicInformation), out uint bytesWritten) >= 0)
|
|||
|
|
{// NT_SUCCESS(...)
|
|||
|
|
if (processBasicInformation.InheritedFromUniqueProcessId == processID)
|
|||
|
|
{
|
|||
|
|
TerminateProcessTree(p.Handle, processBasicInformation.UniqueProcessId, exitCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (InvalidOperationException e)
|
|||
|
|
{
|
|||
|
|
Log(e);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
catch (Win32Exception e)
|
|||
|
|
{
|
|||
|
|
Log(e);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_ = NativeMethods.TerminateProcess(hProcess, (IntPtr)exitCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
}
|