diff --git a/8x8_Work_Updater.ps1 b/8x8_Work_Updater.ps1 index c23b845..7285fb7 100644 --- a/8x8_Work_Updater.ps1 +++ b/8x8_Work_Updater.ps1 @@ -1,8 +1,8 @@ # ============================================================================== # Script Name: 8x8_Work_Updater.ps1 -# Description: Downloads 8x8 with a progress bar, uses Win32 CreateProcessAsUser -# token duplication to display a live countdown GUI directly on -# the active user's screen, silently updates 8x8, and relaunches it. +# Description: Downloads 8x8, uses ServiceUI to display a live countdown GUI +# directly inside the active user's desktop session, silently +# installs 8x8, and relaunches the application. # ============================================================================== # Ensure script is running elevated @@ -11,18 +11,19 @@ if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdenti exit 1 } -# Load required .NET assemblies Add-Type -AssemblyName System.Net.Http -$workDir = "C:\ProgramData\JCT600_8x8Update" -$flagFile = Join-Path $workDir "8x8_Update_Proceed.flag" -$guiScript = Join-Path $workDir "Show-8x8UserGui.ps1" +$workDir = "C:\ProgramData\JCT600_8x8Update" +$flagFile = Join-Path $workDir "8x8_Update_Proceed.flag" +$guiScript = Join-Path $workDir "Show-8x8UserGui.ps1" +$serviceUiPath = Join-Path $workDir "ServiceUI.exe" +$installerPath = Join-Path $workDir "8x8_Work_Installer.msi" if (-not (Test-Path $workDir)) { New-Item -Path $workDir -ItemType Directory -Force | Out-Null } -# Ensure standard users have full read/execute permissions to the working folder +# Ensure standard users have permissions to read/execute from this directory $acl = Get-Acl $workDir $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Users", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow") $acl.AddAccessRule($rule) @@ -30,109 +31,23 @@ Set-Acl $workDir $acl if (Test-Path $flagFile) { Remove-Item $flagFile -Force } -# ------------------------------------------------------------------------------ -# Embedded C# Win32 Process Launcher (Crosses Session 0 Isolation via Token Duplication) -# ------------------------------------------------------------------------------ -$TokenLauncherCode = @" -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -public class UserSessionLauncher -{ - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, int ImpersonationLevel, int TokenType, out IntPtr phNewToken); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle(IntPtr hObject); - - [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; - } - - [StructLayout(LayoutKind.Sequential)] - private struct PROCESS_INFORMATION - { - public IntPtr hProcess; - public IntPtr hThread; - public int dwProcessId; - public int dwThreadId; - } - - public static bool StartProcessInUserSession(string commandLine) - { - Process[] explorers = Process.GetProcessesByName("explorer"); - if (explorers.Length == 0) return false; - - IntPtr hProcess = explorers[0].Handle; - IntPtr hToken = IntPtr.Zero; - IntPtr hDupToken = IntPtr.Zero; - - if (!OpenProcessToken(hProcess, 0x0002 | 0x0004, out hToken)) return false; - - if (!DuplicateTokenEx(hToken, 0x02000000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080 | 0x0100, IntPtr.Zero, 2, 1, out hDupToken)) - { - CloseHandle(hToken); - return false; - } - - STARTUPINFO si = new STARTUPINFO(); - si.cb = Marshal.SizeOf(si); - si.lpDesktop = @"winsta0\default"; // Connect directly to interactive user display - - PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); - bool result = CreateProcessAsUser(hDupToken, null, commandLine, IntPtr.Zero, IntPtr.Zero, false, 0x00000010, IntPtr.Zero, null, ref si, out pi); - - if (pi.hProcess != IntPtr.Zero) CloseHandle(pi.hProcess); - if (pi.hThread != IntPtr.Zero) CloseHandle(pi.hThread); - if (hDupToken != IntPtr.Zero) CloseHandle(hDupToken); - if (hToken != IntPtr.Zero) CloseHandle(hToken); - - return result; - } -} -"@ - -Add-Type -TypeDefinition $TokenLauncherCode -ErrorAction SilentlyContinue - try { # -------------------------------------------------------------------------- - # Step 1: Download 8x8 Setup Installer with Console Progress Bar + # Step 1: Download 8x8 Installer & ServiceUI Tool # -------------------------------------------------------------------------- - $installerUrl = "https://work-desktop-assets.8x8.com/prod-publish/ga/work-64-msi-v8.35.2-6.msi" - $installerPath = Join-Path $workDir "8x8_Work_Installer.msi" + $installerUrl = "https://work-desktop-assets.8x8.com/prod-publish/ga/work-64-msi-v8.35.2-6.msi" + # Portable ServiceUI x64 direct binary + $serviceUiUrl = "https://raw.githubusercontent.com/asheroto/ServiceUI/main/ServiceUI_x64.exe" - Write-Host "[1/5] Downloading 8x8 Work installer..." -ForegroundColor Cyan + Write-Host "[1/5] Downloading 8x8 Work installer and helper tools..." -ForegroundColor Cyan try { + # Download ServiceUI + Invoke-WebRequest -Uri $serviceUiUrl -OutFile $serviceUiPath -UseBasicParsing + + # Download 8x8 Installer with stream progress bar $httpClient = New-Object System.Net.Http.HttpClient $response = $httpClient.GetAsync($installerUrl, [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead).Result - if (-not $response.IsSuccessStatusCode) { throw "HTTP Error $($response.StatusCode)" } $totalBytes = $response.Content.Headers.ContentLength @@ -159,12 +74,12 @@ try { $outputStream.Close(); $inputStream.Close(); $httpClient.Dispose() Write-Host "`nDownload complete: $installerPath" -ForegroundColor Green } catch { - Write-Error "`nFailed to download installer. Error: $_" + Write-Error "`nFailed to download installer or tools. Error: $_" exit 1 } # -------------------------------------------------------------------------- - # Step 2: Write User-Session GUI Script (BOM-Free UTF-8) + # Step 2: Write User-Session GUI Script # -------------------------------------------------------------------------- Write-Host "[2/5] Generating user-session GUI dialog script..." -ForegroundColor Cyan @@ -272,26 +187,22 @@ $form.Dispose() [System.IO.File]::WriteAllText($guiScript, $guiContent, $utf8NoBom) # -------------------------------------------------------------------------- - # Step 3: Trigger Interactive GUI on Active User's Desktop via Token Duplication + # Step 3: Launch GUI via ServiceUI Directly into Active User's Session # -------------------------------------------------------------------------- - Write-Host "[3/5] Launching interactive dialog on active user desktop..." -ForegroundColor Cyan + Write-Host "[3/5] Launching interactive dialog on active user desktop via ServiceUI..." -ForegroundColor Cyan $explorerProc = Get-Process -Name explorer -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $explorerProc) { - Write-Warning "No active user desktop session detected. Proceeding directly to update." + Write-Warning "No active desktop session detected. Proceeding directly to update." } else { - $launchCmd = "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$guiScript`"" - $launched = [UserSessionLauncher]::StartProcessInUserSession($launchCmd) + # ServiceUI attaches the process directly into explorer.exe's interactive window station + $serviceUiArgs = "-process:explorer.exe powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$guiScript`"" + Start-Process -FilePath $serviceUiPath -ArgumentList $serviceUiArgs - if ($launched) { - Write-Host "Dialog launched successfully on user's active screen." -ForegroundColor Green - Write-Host "Waiting for user response or timer expiration (1 hour max)..." -ForegroundColor Yellow - while (-not (Test-Path $flagFile)) { - Start-Sleep -Seconds 2 - } - } else { - Write-Warning "Failed to duplicate user token. Proceeding to silent installation." + Write-Host "Dialog displayed on user screen. Waiting for user response or 1-hour expiration..." -ForegroundColor Yellow + while (-not (Test-Path $flagFile)) { + Start-Sleep -Seconds 2 } } @@ -322,7 +233,7 @@ $form.Dispose() Write-Host "Installation completed successfully." -ForegroundColor Green # -------------------------------------------------------------------------- - # Step 5: Launch 8x8 Work via User Session Token + # Step 5: Launch 8x8 Work in User Session # -------------------------------------------------------------------------- Write-Host "[5/5] Launching 8x8 Work in user session..." -ForegroundColor Cyan @@ -337,7 +248,8 @@ $form.Dispose() if ($exePath) { if ($explorerProc) { - [UserSessionLauncher]::StartProcessInUserSession("`"$exePath`"") | Out-Null + $launchArgs = "-process:explorer.exe `"$exePath`"" + Start-Process -FilePath $serviceUiPath -ArgumentList $launchArgs Write-Host "8x8 Work launched successfully in user context." -ForegroundColor Green } else { Start-Process -FilePath $exePath