added support to run under SYSTEM account
This commit is contained in:
parent
0499544f5c
commit
80f07d401a
1 changed files with 352 additions and 44 deletions
|
|
@ -82,18 +82,18 @@
|
|||
** As well as added support for dynamic deadline retrieval for software updates **
|
||||
** Stuff has been rewritten to suit my understanding and thoughts of the script **
|
||||
|
||||
2.0.0 - Huge changes to how this script handles custom protocols
|
||||
Added Support for Custom Actions/Protocols within the script under user context removing the need for that to be run under SYSTEM/ADMIN
|
||||
- <Option Name="Action" Value="ToastRunUpdateID:" />
|
||||
- <Option Name="Action" Value="ToastRunPackageID:" />
|
||||
- <Option Name="Action" Value="ToastRunApplicationID:" />
|
||||
- <Option Name="Action" Value="ToastReboot:" />
|
||||
Added Support to dynamically create Custom Action Scripts to support Custom Protocols
|
||||
Added Support for Software (Feature) Updates : Searches for an update and will store in variable
|
||||
Added new XML Types for Software Updates:
|
||||
- <Option Name="RunUpdateID" Enabled="True" Value="3012973" />
|
||||
- <Option Name="RunUpdateTitle" Enabled="True" Value="Version 1909" />
|
||||
Added support for getting deadline date/time dynamically for software updates
|
||||
2.0.0 - Huge changes to how this script handles custom protocols
|
||||
Added Support for Custom Actions/Protocols within the script under user context removing the need for that to be run under SYSTEM/ADMIN
|
||||
- <Option Name="Action" Value="ToastRunUpdateID:" />
|
||||
- <Option Name="Action" Value="ToastRunPackageID:" />
|
||||
- <Option Name="Action" Value="ToastRunApplicationID:" />
|
||||
- <Option Name="Action" Value="ToastReboot:" />
|
||||
Added Support to dynamically create Custom Action Scripts to support Custom Protocols
|
||||
Added Support for Software (Feature) Updates : Searches for an update and will store in variable
|
||||
Added new XML Types for Software Updates:
|
||||
- <Option Name="RunUpdateID" Enabled="True" Value="3012973" />
|
||||
- <Option Name="RunUpdateTitle" Enabled="True" Value="Version 1909" />
|
||||
Added support for getting deadline date/time dynamically for software updates
|
||||
- Configure DynamicDeadline with the UpdateID
|
||||
|
||||
2.0.1 - Updated custom action scripts!
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
- If newer version is available from the script, new custom action scripts will be created
|
||||
- This allows me to make sure the relevant scripts are in place in case I change something along the way
|
||||
- Modified script output of custom script for RunPackageID to pick up Program ID dynamically
|
||||
Added support for getting deadline date/time dynamically for applications
|
||||
Added support for getting deadline date/time dynamically for applications
|
||||
- Configure DynamicDeadline with the Application ID
|
||||
|
||||
2.0.2 - Fixed an error in the custom protocols
|
||||
|
|
@ -267,21 +267,21 @@ function Get-GivenName() {
|
|||
if (Get-Service -Name ccmexec -ErrorAction SilentlyContinue) {
|
||||
Write-Log -Message "Looking for logged on user's SID in WMI with CCM client"
|
||||
$LoggedOnSID = Get-CimInstance -Namespace ROOT\CCM -Class CCM_UserLogonEvents -Filter "LogoffTime=null" | Select -ExpandProperty UserSID
|
||||
if ($LoggedOnSID.GetType().IsArray) {
|
||||
Write-Log -Message "Multiple SID's found logged on. Skipping"
|
||||
$GivenName = $null
|
||||
if ($LoggedOnSID.GetType().IsArray) {
|
||||
Write-Log -Message "Multiple SID's found logged on. Skipping"
|
||||
$GivenName = $null
|
||||
}
|
||||
else {
|
||||
$RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
|
||||
$DisplayName = (Get-ChildItem -Path $RegKey | Where-Object {$_.GetValue("LoggedOnUserSID") -eq $LoggedOnSID} | Select-Object -First 1).GetValue("LoggedOnDisplayName")
|
||||
if ($DisplayName) {
|
||||
$GivenName = $DisplayName.Split()[0].Trim()
|
||||
Write-Log -Message "Given name found matching logged on user SID: $GivenName"
|
||||
$GivenName
|
||||
}
|
||||
else {
|
||||
$GivenName = $null
|
||||
}
|
||||
else {
|
||||
$RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
|
||||
$DisplayName = (Get-ChildItem -Path $RegKey | Where-Object {$_.GetValue("LoggedOnUserSID") -eq $LoggedOnSID} | Select-Object -First 1).GetValue("LoggedOnDisplayName")
|
||||
if ($DisplayName) {
|
||||
$GivenName = $DisplayName.Split()[0].Trim()
|
||||
Write-Log -Message "Given name found matching logged on user SID: $GivenName"
|
||||
$GivenName
|
||||
}
|
||||
else {
|
||||
$GivenName = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -719,14 +719,21 @@ function Write-UpdateIDRegistry() {
|
|||
|
||||
# Create Display-ToastNotification function
|
||||
function Display-ToastNotification() {
|
||||
$Load = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
|
||||
$Load = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime]
|
||||
# Load the notification into the required format
|
||||
$ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
|
||||
$ToastXml.LoadXml($Toast.OuterXml)
|
||||
# Display the toast notification
|
||||
try {
|
||||
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml)
|
||||
if (Test-NTSystem) {
|
||||
# is running under SYSTEM context
|
||||
# show notification to all logged users
|
||||
& (Join-Path -Path $CustomScriptsPath -ChildPath "InvokePSScriptAsUser.ps1") "$PSCommandPath" "$Config"
|
||||
} else {
|
||||
# is running under user context
|
||||
$Load = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
|
||||
$Load = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime]
|
||||
# Load the notification into the required format
|
||||
$ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
|
||||
$ToastXml.LoadXml($Toast.OuterXml)
|
||||
# Display the toast notification
|
||||
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($App).Show($ToastXml)
|
||||
}
|
||||
Write-Log -Message "All good. Toast notification was displayed"
|
||||
# Using Write-Output for sending status to IME log when used with Endpoint Analytics in Intune
|
||||
Write-Output "All good. Toast notification was displayed"
|
||||
|
|
@ -752,11 +759,9 @@ function Display-ToastNotification() {
|
|||
}
|
||||
|
||||
# Create Test-NTSystem function
|
||||
# If the script is being run as SYSTEM, the toast notification won't display
|
||||
function Test-NTSystem() {
|
||||
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
if ($currentUser.IsSystem -eq $true) {
|
||||
Write-Log -Message "The script is being run as SYSTEM. This is not supported. The script needs the current user's context" -Level Error
|
||||
$true
|
||||
}
|
||||
elseif ($currentUser.IsSystem -eq $false) {
|
||||
|
|
@ -853,7 +858,7 @@ function Write-CustomActionScript() {
|
|||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Position="0")]
|
||||
[ValidateSet("ToastRunApplicationID","ToastRunPackageID","ToastRunUpdateID","ToastReboot")]
|
||||
[ValidateSet("ToastRunApplicationID", "ToastRunPackageID", "ToastRunUpdateID", "ToastReboot", "InvokePSScriptAsUser")]
|
||||
[string] $Type,
|
||||
[Parameter(Position="1")]
|
||||
[String] $Path = $global:CustomScriptsPath
|
||||
|
|
@ -1113,13 +1118,321 @@ exit 0
|
|||
# Do not run another type; break
|
||||
Break
|
||||
}
|
||||
InvokePSScriptAsUser {
|
||||
# create ps1 script that can invoke another script under all logged users (if started as SYSTEM)
|
||||
try {
|
||||
$PS1FileName = 'InvokePSScriptAsUser.ps1'
|
||||
try {
|
||||
New-Item -Path $Path -Name $PS1FileName -Force -OutVariable PathInfo | Out-Null
|
||||
} catch {
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Log -Level Error -Message "Error message: $ErrorMessage"
|
||||
}
|
||||
try {
|
||||
$GetCustomScriptPath = $PathInfo.FullName
|
||||
[String]$Script = @'
|
||||
param($File, $argument)
|
||||
|
||||
$Source = @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Runasuser
|
||||
{
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
#region Win32 Constants
|
||||
|
||||
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
|
||||
private const int CREATE_NO_WINDOW = 0x08000000;
|
||||
|
||||
private const int CREATE_NEW_CONSOLE = 0x00000010;
|
||||
|
||||
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
|
||||
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
|
||||
|
||||
#endregion
|
||||
|
||||
#region DllImports
|
||||
|
||||
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||||
private static extern bool CreateProcessAsUser(
|
||||
IntPtr hToken,
|
||||
String lpApplicationName,
|
||||
String lpCommandLine,
|
||||
IntPtr lpProcessAttributes,
|
||||
IntPtr lpThreadAttributes,
|
||||
bool bInheritHandle,
|
||||
uint dwCreationFlags,
|
||||
IntPtr lpEnvironment,
|
||||
String lpCurrentDirectory,
|
||||
ref STARTUPINFO lpStartupInfo,
|
||||
out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
|
||||
private static extern bool DuplicateTokenEx(
|
||||
IntPtr ExistingTokenHandle,
|
||||
uint dwDesiredAccess,
|
||||
IntPtr lpThreadAttributes,
|
||||
int TokenType,
|
||||
int ImpersonationLevel,
|
||||
ref IntPtr DuplicateTokenHandle);
|
||||
|
||||
[DllImport("userenv.dll", SetLastError = true)]
|
||||
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
|
||||
|
||||
[DllImport("userenv.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hSnapshot);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint WTSGetActiveConsoleSessionId();
|
||||
|
||||
[DllImport("Wtsapi32.dll")]
|
||||
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
|
||||
|
||||
[DllImport("wtsapi32.dll", SetLastError = true)]
|
||||
private static extern int WTSEnumerateSessions(
|
||||
IntPtr hServer,
|
||||
int Reserved,
|
||||
int Version,
|
||||
ref IntPtr ppSessionInfo,
|
||||
ref int pCount);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Win32 Structs
|
||||
|
||||
private enum SW
|
||||
{
|
||||
SW_HIDE = 0,
|
||||
SW_SHOWNORMAL = 1,
|
||||
SW_NORMAL = 1,
|
||||
SW_SHOWMINIMIZED = 2,
|
||||
SW_SHOWMAXIMIZED = 3,
|
||||
SW_MAXIMIZE = 3,
|
||||
SW_SHOWNOACTIVATE = 4,
|
||||
SW_SHOW = 5,
|
||||
SW_MINIMIZE = 6,
|
||||
SW_SHOWMINNOACTIVE = 7,
|
||||
SW_SHOWNA = 8,
|
||||
SW_RESTORE = 9,
|
||||
SW_SHOWDEFAULT = 10,
|
||||
SW_MAX = 10
|
||||
}
|
||||
|
||||
private enum WTS_CONNECTSTATE_CLASS
|
||||
{
|
||||
WTSActive,
|
||||
WTSConnected,
|
||||
WTSConnectQuery,
|
||||
WTSShadow,
|
||||
WTSDisconnected,
|
||||
WTSIdle,
|
||||
WTSListen,
|
||||
WTSReset,
|
||||
WTSDown,
|
||||
WTSInit
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public uint dwProcessId;
|
||||
public uint dwThreadId;
|
||||
}
|
||||
|
||||
private enum SECURITY_IMPERSONATION_LEVEL
|
||||
{
|
||||
SecurityAnonymous = 0,
|
||||
SecurityIdentification = 1,
|
||||
SecurityImpersonation = 2,
|
||||
SecurityDelegation = 3,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
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 short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
private enum TOKEN_TYPE
|
||||
{
|
||||
TokenPrimary = 1,
|
||||
TokenImpersonation = 2
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct WTS_SESSION_INFO
|
||||
{
|
||||
public readonly UInt32 SessionID;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public readonly String pWinStationName;
|
||||
|
||||
public readonly WTS_CONNECTSTATE_CLASS State;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Gets the user token from the currently active session
|
||||
private static bool GetSessionUserToken(ref IntPtr phUserToken)
|
||||
{
|
||||
var bResult = false;
|
||||
var hImpersonationToken = IntPtr.Zero;
|
||||
var activeSessionId = INVALID_SESSION_ID;
|
||||
var pSessionInfo = IntPtr.Zero;
|
||||
var sessionCount = 0;
|
||||
|
||||
// Get a handle to the user access token for the current active session.
|
||||
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
|
||||
{
|
||||
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
|
||||
var current = pSessionInfo;
|
||||
|
||||
for (var i = 0; i < sessionCount; i++)
|
||||
{
|
||||
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
|
||||
current += arrayElementSize;
|
||||
|
||||
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
|
||||
{
|
||||
activeSessionId = si.SessionID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If enumerating did not work, fall back to the old method
|
||||
if (activeSessionId == INVALID_SESSION_ID)
|
||||
{
|
||||
activeSessionId = WTSGetActiveConsoleSessionId();
|
||||
}
|
||||
|
||||
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
|
||||
{
|
||||
// Convert the impersonation token to a primary token
|
||||
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
|
||||
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
|
||||
ref phUserToken);
|
||||
|
||||
CloseHandle(hImpersonationToken);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)
|
||||
{
|
||||
var hUserToken = IntPtr.Zero;
|
||||
var startInfo = new STARTUPINFO();
|
||||
var procInfo = new PROCESS_INFORMATION();
|
||||
var pEnv = IntPtr.Zero;
|
||||
int iResultOfCreateProcessAsUser;
|
||||
|
||||
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
|
||||
|
||||
try
|
||||
{
|
||||
if (!GetSessionUserToken(ref hUserToken))
|
||||
{
|
||||
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
|
||||
}
|
||||
|
||||
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
|
||||
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
|
||||
startInfo.lpDesktop = "winsta0\\default";
|
||||
|
||||
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
|
||||
{
|
||||
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
|
||||
}
|
||||
|
||||
if (!CreateProcessAsUser(hUserToken,
|
||||
appPath, // Application Name
|
||||
cmdLine, // Command Line
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
false,
|
||||
dwCreationFlags,
|
||||
pEnv,
|
||||
workDir, // Working directory
|
||||
ref startInfo,
|
||||
out procInfo))
|
||||
{
|
||||
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
|
||||
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
|
||||
}
|
||||
|
||||
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(hUserToken);
|
||||
if (pEnv != IntPtr.Zero)
|
||||
{
|
||||
DestroyEnvironmentBlock(pEnv);
|
||||
}
|
||||
CloseHandle(procInfo.hThread);
|
||||
CloseHandle(procInfo.hProcess);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
# Load the custom type
|
||||
Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp -ErrorAction Stop
|
||||
|
||||
# Run PS as user to display the message box
|
||||
[Runasuser.ProcessExtensions]::StartProcessAsCurrentUser("$env:windir\System32\WindowsPowerShell\v1.0\Powershell.exe", " -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$File`" $argument") | Out-Null
|
||||
'@
|
||||
if (-NOT[string]::IsNullOrEmpty($Script)) {
|
||||
Out-File -FilePath $GetCustomScriptPath -InputObject $Script -Encoding ASCII -Force
|
||||
}
|
||||
} catch {
|
||||
Write-Log -Level Error "Failed to create the .ps1 script for $Type. Show notification if run under SYSTEM might not work"
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Log -Level Error -Message "Error message: $ErrorMessage"
|
||||
}
|
||||
|
||||
} catch {
|
||||
Write-Log -Level Error "Failed to create the .ps1 script for $Type. Show notification if run under SYSTEM might not work"
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
Write-Log -Level Error -Message "Error message: $ErrorMessage"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
######### GENERAL VARIABLES #########
|
||||
# Global variables
|
||||
# Setting global script version
|
||||
$global:ScriptVersion = "2.1.0"
|
||||
$global:ScriptVersion = "2.1.5"
|
||||
# Setting executing directory
|
||||
$global:ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
# Setting global custom action script location
|
||||
|
|
@ -1165,12 +1478,6 @@ if (-NOT(Test-Path -Path $global:CustomScriptsPath)) {
|
|||
}
|
||||
|
||||
# Testing for prerequisites
|
||||
# Testing if script is being run as SYSTEM. This is not supported as the toast notification needs the current user's context
|
||||
$isSystem = Test-NTSystem
|
||||
if ($isSystem -eq $True) {
|
||||
Write-Log -Message "Aborting script" -Level Error
|
||||
Exit 1
|
||||
}
|
||||
|
||||
# Test if the script is being run on a supported version of Windows. Windows 10 AND workstation OS is required
|
||||
$SupportedWindowsVersion = Get-WindowsVersion
|
||||
|
|
@ -1629,6 +1936,7 @@ if ($CreateScriptsProtocolsEnabled -eq "True") {
|
|||
Write-CustomActionScript -Type ToastRunApplicationID
|
||||
Write-CustomActionScript -Type ToastRunPackageID
|
||||
Write-CustomActionScript -Type ToastRunUpdateID
|
||||
Write-CustomActionScript -Type InvokePSScriptAsUser
|
||||
New-ItemProperty -Path $global:RegistryPath -Name $RegistryName -Value $global:ScriptVersion -PropertyType "String" -Force | Out-Null
|
||||
}
|
||||
catch {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue