$ErrorActionPreference = "SilentlyContinue" $HOST_ADDR = "remote.iconic.io.vn" $PUB_KEY = "0MqAcRTYZxmnVw6pJs3v1WnjdDvMx0O0mDOERGBb0+c=" $CFG_STR = "host=$HOST_ADDR,key=$PUB_KEY" # Ensure Administrator privileges if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "Requesting administrative privileges..." -ForegroundColor Yellow Start-Process powershell.exe -Verb RunAs -ArgumentList "-NoExit -NoProfile -ExecutionPolicy Bypass -Command `"irm https://remote.iconic.io.vn | iex`"" exit } Write-Host "Checking RustDesk installation..." -ForegroundColor Cyan # 1. Check current installed version $installedVer = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RustDesk\" -ErrorAction SilentlyContinue).DisplayVersion $InstallPath = "$env:ProgramFiles\RustDesk\rustdesk.exe" # 2. Query latest release from GitHub $needsInstallOrUpdate = $false $downloadUrl = $null $latestVer = $null try { $rel = Invoke-RestMethod -Uri "https://api.github.com/repos/rustdesk/rustdesk/releases/latest" -Headers @{"User-Agent"="RustDeskInstaller"} -UseBasicParsing -TimeoutSec 10 $latestVer = $rel.tag_name $asset = $rel.assets | Where-Object { $_.name -match "x86_64\.exe$" } | Select-Object -First 1 if ($asset) { $downloadUrl = $asset.browser_download_url } } catch {} if (-not (Test-Path $InstallPath)) { $needsInstallOrUpdate = $true } elseif ($latestVer -and $installedVer -and ($installedVer -ne $latestVer)) { Write-Host "Update available: $installedVer -> $latestVer" -ForegroundColor Yellow $needsInstallOrUpdate = $true } # 3. Perform install or update if needed if ($needsInstallOrUpdate) { if (-not $downloadUrl) { $downloadUrl = "https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-x86_64.exe" } Write-Host "Downloading RustDesk $latestVer..." $Installer = "$env:TEMP\rustdesk-setup.exe" Invoke-WebRequest -Uri $downloadUrl -OutFile $Installer -UseBasicParsing Write-Host "Stopping running instances..." Stop-Process -Name "rustdesk" -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 Write-Host "Installing RustDesk..." $proc = Start-Process $Installer -ArgumentList "--silent-install" -PassThru if ($proc) { $proc.WaitForExit(30000) | Out-Null } Start-Sleep -Seconds 3 Remove-Item $Installer -Force -ErrorAction SilentlyContinue } else { Write-Host "RustDesk is already installed ($installedVer)." -ForegroundColor Green } # 4. Ensure system service is running, set Automatic, and configure recovery $ServiceName = "Rustdesk" $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($null -eq $service) { if (Test-Path $InstallPath) { Write-Host "Registering RustDesk system service..." Start-Process $InstallPath -ArgumentList "--install-service" -Wait Start-Sleep -Seconds 3 $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue } } # Set service to Automatic startup and auto-restart on crash Set-Service -Name $ServiceName -StartupType Automatic -ErrorAction SilentlyContinue & sc.exe failure $ServiceName reset= 0 actions= restart/5000/restart/5000/restart/5000 2>&1 | Out-Null if ($service -and $service.Status -ne "Running") { Start-Service -Name $ServiceName -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 } # Ensure single, clean auto-start entry (prevent duplicate startup entries in Task Manager) Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "RustDesk" -ErrorAction SilentlyContinue Remove-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "RustDesk" -ErrorAction SilentlyContinue $startupFolder = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Startup" $startupLnk = "$startupFolder\RustDesk Tray.lnk" if (-not (Test-Path $startupLnk)) { if (-not (Test-Path $startupFolder)) { New-Item -ItemType Directory -Force -Path $startupFolder | Out-Null } $wsh = New-Object -ComObject WScript.Shell $shortcut = $wsh.CreateShortcut($startupLnk) $shortcut.TargetPath = $InstallPath $shortcut.Arguments = "--tray" $shortcut.Save() } # 5. Fix Lock Screen, Sleep & UAC Isolation (Unattended 24/7 Access) Write-Host "Configuring 24/7 unattended access & power settings..." powercfg /change standby-timeout-ac 0 2>$null | Out-Null powercfg /change hibernate-timeout-ac 0 2>$null | Out-Null powercfg /change monitor-timeout-ac 0 2>$null | Out-Null # Disable Secure Desktop switch on UAC prompt (prevents black screen / elevation deadlock when locked) Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "PromptOnSecureDesktop" -Value 0 -Force -ErrorAction SilentlyContinue # 6. Apply server configuration Write-Host "Configuring server and maximum quality settings..." if (Test-Path $InstallPath) { & $InstallPath --config $CFG_STR } # 6b. Preserve and Synchronize Permanent Password, ID & Keypair across all profiles Write-Host "Preserving permanent password and user credentials..." $localServiceConfig = "$env:WINDIR\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config" if (-not (Test-Path $localServiceConfig)) { New-Item -ItemType Directory -Force -Path $localServiceConfig | Out-Null } $allTomls = @("$localServiceConfig\RustDesk.toml") Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue | ForEach-Object { $allTomls += "$($_.FullName)\AppData\Roaming\RustDesk\config\RustDesk.toml" } if (-not ($allTomls -contains "$env:APPDATA\RustDesk\config\RustDesk.toml")) { $allTomls += "$env:APPDATA\RustDesk\config\RustDesk.toml" } $masterPassToml = $null foreach ($f in $allTomls) { if (Test-Path $f) { $content = Get-Content $f -Raw -ErrorAction SilentlyContinue if ($content -match "password\s*=\s*'[^']+'") { $masterPassToml = $f break } elseif (-not $masterPassToml -and $content -match "key_pair\s*=") { $masterPassToml = $f } } } if ($masterPassToml) { foreach ($f in $allTomls) { if ($f -ne $masterPassToml) { $parent = Split-Path $f -Parent if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Force -Path $parent | Out-Null } Copy-Item -Path $masterPassToml -Destination $f -Force -ErrorAction SilentlyContinue } } } # 7. Inject Maximum Quality & Performance Settings (YUV444, 60FPS, GPU Encode, Direct3D, Best Quality) $qualitySettings = @{ "custom-rendezvous-server" = "'$HOST_ADDR'" "relay-server" = "'$HOST_ADDR'" "key" = "'$PUB_KEY'" "i444" = "'Y'" "image_quality" = "'custom'" "custom_image_quality" = "'200'" "custom-fps" = "'60'" "enable-hwcodec" = "'Y'" "enable-directx-capture" = "'Y'" "allow-d3d-render" = "'Y'" "view_style" = "'original'" "codec-preference" = "'vp9'" "access-mode" = "'full'" "enable-keyboard" = "'Y'" "enable-clipboard" = "'Y'" "enable-file-transfer" = "'Y'" } $cfgDirs = @( "$env:WINDIR\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config" ) Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue | ForEach-Object { $p = "$($_.FullName)\AppData\Roaming\RustDesk\config" if (-not ($cfgDirs -contains $p)) { $cfgDirs += $p } } if (-not ($cfgDirs -contains "$env:APPDATA\RustDesk\config")) { $cfgDirs += "$env:APPDATA\RustDesk\config" } foreach ($dir in $cfgDirs) { if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null } # 6a. Force server & performance settings into RustDesk2.toml $cfgPath = "$dir\RustDesk2.toml" $lines = @() if (Test-Path $cfgPath) { $lines = Get-Content $cfgPath | Where-Object { $l = $_ $match = $false foreach ($k in $qualitySettings.Keys) { if ($l -match "^\s*$k\s*=") { $match = $true; break } } -not $match } } if (-not ($lines -match "^\s*\[options\]")) { $lines += "[options]" } $final = @() foreach ($line in $lines) { $final += $line if ($line -match "^\s*\[options\]") { foreach ($k in $qualitySettings.Keys) { $v = $qualitySettings[$k] $final += "$k = $v" } if (-not ($lines -match "^\s*verification-method\s*=")) { $final += "verification-method = 'both'" } } } $final | Set-Content -Path $cfgPath -Encoding UTF8 -Force # 6b. Force default settings for all future peer connections $defaultToml = "$dir\RustDesk_default.toml" $defaultContent = "[options]`nimage_quality = 'custom'`ncodec-preference = 'vp9'`ncustom-fps = '60'`ni444 = 'Y'`nview_style = 'original'`ncustom_image_quality = '200'`nscroll_style = 'scrollauto'" Set-Content -Path $defaultToml -Value $defaultContent -Encoding UTF8 -Force # 6c. Force update all existing saved peers in peers/ $peersDir = "$dir\peers" if (Test-Path $peersDir) { Get-ChildItem -Path $peersDir -Filter "*.toml" | ForEach-Object { $p = $_.FullName $plines = Get-Content $p | Where-Object { $_ -notmatch '^\s*view_only\s*=' } $pnew = @("view_only = false") foreach ($l in $plines) { if ($l -match "^\s*view_style\s*=") { $pnew += "view_style = 'original'" } elseif ($l -match "^\s*custom_image_quality\s*=") { $pnew += "custom_image_quality = [200]" } elseif ($l -match "^\s*image_quality\s*=") { $pnew += "image_quality = 'custom'" } elseif ($l -match "^\s*custom-fps\s*=") { $pnew += "custom-fps = '60'" } elseif ($l -match "^\s*i444\s*=") { $pnew += "i444 = 'Y'" } elseif ($l -match "^\s*codec-preference\s*=") { $pnew += "codec-preference = 'vp9'" } else { $pnew += $l } } $pnew | Set-Content -Path $p -Encoding UTF8 -Force } } } # 8. Query permanent machine ID cleanly $machineId = $null if (Test-Path $InstallPath) { $idFile = "$env:TEMP\rd_id_$PID.txt" $procId = Start-Process -FilePath $InstallPath -ArgumentList "--get-id" -NoNewWindow -RedirectStandardOutput $idFile -PassThru -ErrorAction SilentlyContinue if ($procId) { $procId.WaitForExit(5000) | Out-Null } if (Test-Path $idFile) { $raw = Get-Content $idFile -ErrorAction SilentlyContinue | Select-Object -First 1 if ($raw) { $machineId = $raw.Trim() } Remove-Item $idFile -Force -ErrorAction SilentlyContinue } } # 9. Ensure RustDesk GUI application is active and visible on screen Write-Host "Opening RustDesk interface..." -ForegroundColor Cyan if (Test-Path $InstallPath) { # Launch detached via Windows Explorer under interactive user session Start-Process "explorer.exe" -ArgumentList "`"$InstallPath`"" Start-Sleep -Seconds 3 # Correctly target main GUI process (must have MainWindowTitle matching 'RustDesk') # Note: `--tray` and `--cm` have non-zero handles but empty titles! $proc = Get-Process -Name "rustdesk" -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -match "RustDesk" } | Select-Object -First 1 if ($proc -and $proc.MainWindowHandle -ne 0) { Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class RustDeskWinUtil { [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); } "@ -ErrorAction SilentlyContinue [RustDeskWinUtil]::ShowWindowAsync($proc.MainWindowHandle, 9) | Out-Null [RustDeskWinUtil]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null } } Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host " RustDesk 24/7 Unattended Access Ready!" -ForegroundColor Green if ($machineId) { Write-Host " Machine ID: $machineId (Permanent / Co dinh)" -ForegroundColor Yellow } Write-Host " Server: $HOST_ADDR" -ForegroundColor White Write-Host " Visual: VP9 4:4:4 | 60 FPS | Bitrate 200%" -ForegroundColor White Write-Host " Auto-start: Enabled (SYSTEM Service + Tray Run)" -ForegroundColor White Write-Host " Power & Lock: Sleep / Screen-off Disabled, UAC Prompt Fixed" -ForegroundColor White Write-Host "============================================================" -ForegroundColor Cyan