#Requires -RunAsAdministrator <# .SYNOPSIS Post-reboot cleanup agent for GuruScan. Runs automatically as GuruRMM-Temp after a reboot triggered by scanner exit code 2 (pending removal required). Shows a full-screen splash, verifies cleanup completed, removes scanner files, restores the original user's login name, then logs off. #> Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $Base = 'C:\GuruScan' $StateFile = "$Base\cleanup-state.json" $TempUser = 'GuruRMM-Temp' $WlKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' $SpecialKey= 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList' # --------------------------------------------------------------------------- # Kill Explorer so we have a clean screen before showing splash # --------------------------------------------------------------------------- Get-Process -Name explorer -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 # --------------------------------------------------------------------------- # Full-screen splash in a STA runspace (non-blocking) # --------------------------------------------------------------------------- $sync = [hashtable]::Synchronized(@{ Close = $false }) $uiRS = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() $uiRS.ApartmentState = 'STA' $uiRS.ThreadOptions = 'ReuseThread' $uiRS.Open() $uiPS = [System.Management.Automation.PowerShell]::Create() $uiPS.Runspace = $uiRS [void]$uiPS.AddScript({ param($s) Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase $win = New-Object System.Windows.Window $win.WindowStyle = 'None' $win.WindowState = 'Maximized' $win.Background = [System.Windows.Media.Brushes]::Black $win.Topmost = $true $win.Title = 'GuruRMM' $win.Cursor = [System.Windows.Input.Cursors]::None $panel = New-Object System.Windows.Controls.StackPanel $panel.VerticalAlignment = 'Center' $panel.HorizontalAlignment = 'Center' $title = New-Object System.Windows.Controls.TextBlock $title.Text = 'GuruRMM' $title.Foreground = [System.Windows.Media.Brushes]::White $title.FontSize = 42 $title.FontFamily = New-Object System.Windows.Media.FontFamily('Segoe UI') $title.FontWeight = [System.Windows.FontWeights]::Light $title.TextAlignment = 'Center' $title.Margin = New-Object System.Windows.Thickness(0,0,0,24) $body = New-Object System.Windows.Controls.TextBlock $body.Text = "Security cleanup is being completed on this machine.`n`nPlease do not power off this computer.`n`nThis process will finish shortly." $body.Foreground = [System.Windows.Media.SolidColorBrush][System.Windows.Media.Color]::FromRgb(180,180,180) $body.FontSize = 22 $body.FontFamily = New-Object System.Windows.Media.FontFamily('Segoe UI') $body.TextAlignment = 'Center' $body.LineHeight = 38 [void]$panel.Children.Add($title) [void]$panel.Children.Add($body) $win.Content = $panel $timer = New-Object System.Windows.Threading.DispatcherTimer $timer.Interval = [TimeSpan]::FromMilliseconds(500) $timer.Add_Tick({ if ($s.Close) { $win.Close(); $timer.Stop() } }) $timer.Start() [void]$win.ShowDialog() }).AddArgument($sync) $uiHandle = $uiPS.BeginInvoke() # --------------------------------------------------------------------------- # Read state file # --------------------------------------------------------------------------- $state = @{ original_user = ''; scan_id = ''; log_root = '' } if (Test-Path $StateFile) { try { $state = Get-Content $StateFile -Raw | ConvertFrom-Json } catch {} } $logRoot = $state.log_root $scanId = $state.scan_id $origUser = $state.original_user # --------------------------------------------------------------------------- # Wait for boot-time cleanup to settle # --------------------------------------------------------------------------- Start-Sleep -Seconds 60 # --------------------------------------------------------------------------- # Verify: check PendingFileRenameOperations (empty = boot cleanup completed) # --------------------------------------------------------------------------- $pendingKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' $pendingOps = (Get-ItemProperty -Path $pendingKey -Name PendingFileRenameOperations -ErrorAction SilentlyContinue).PendingFileRenameOperations $pendingDone = (-not $pendingOps -or $pendingOps.Count -eq 0) # Check scanner post-reboot logs $adwLog = Get-ChildItem 'C:\AdwCleaner\Logs' -Filter '*.txt' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 $hitLog = if ($logRoot -and (Test-Path "$logRoot\HitmanPro_Scan_Log.txt")) { Get-Item "$logRoot\HitmanPro_Scan_Log.txt" } else { $null } $verification = [ordered]@{ checked_at = (Get-Date).ToUniversalTime().ToString('o') pending_ops_cleared = $pendingDone adwcleaner_log = if ($adwLog) { $adwLog.FullName } else { 'not found' } hitmanpro_log = if ($hitLog) { $hitLog.FullName } else { 'not found' } result = if ($pendingDone) { 'clean' } else { 'pending_items_remain' } } # Write verification result alongside original results.json if ($logRoot -and (Test-Path $logRoot)) { $verification | ConvertTo-Json | Set-Content "$logRoot\post_reboot_verification.json" -Encoding UTF8 } # --------------------------------------------------------------------------- # Remove scanner installation files from this machine # --------------------------------------------------------------------------- $scannerPaths = @( 'C:\EmsisoftCmd', 'C:\AdwCleaner', 'C:\ProgramData\HitmanPro', 'C:\ProgramData\HitmanPro.Alert' ) foreach ($p in $scannerPaths) { Remove-Item -Path $p -Recurse -Force -ErrorAction SilentlyContinue } # --------------------------------------------------------------------------- # Flag logs for GuruRMM to pull (agent reads this file) # --------------------------------------------------------------------------- @{ scan_id = $scanId log_root = $logRoot zip_path = "$Base\reports\$scanId.zip" verified = $verification.result flagged_at = (Get-Date).ToUniversalTime().ToString('o') } | ConvertTo-Json | Set-Content "$Base\logs-ready.json" -Encoding UTF8 # --------------------------------------------------------------------------- # Restore original user's name in the login screen. # Win32_ComputerSystem.UserName returns "DOMAIN\user" or "MACHINE\user". # Split so the login screen pre-fills both fields correctly. # --------------------------------------------------------------------------- if ($origUser) { try { if ($origUser -match '^(.+)\\(.+)$') { $restoreDomain = $Matches[1] $restoreUser = $Matches[2] } else { $restoreDomain = $env:COMPUTERNAME $restoreUser = $origUser } Set-ItemProperty -Path $WlKey -Name 'DefaultUserName' -Value $restoreUser Set-ItemProperty -Path $WlKey -Name 'DefaultDomainName' -Value $restoreDomain } catch {} } # --------------------------------------------------------------------------- # Clear autologon settings # --------------------------------------------------------------------------- try { Set-ItemProperty -Path $WlKey -Name 'AutoAdminLogon' -Value '0' Remove-ItemProperty -Path $WlKey -Name 'DefaultPassword' -ErrorAction SilentlyContinue } catch {} # --------------------------------------------------------------------------- # Remove our scheduled logon task # --------------------------------------------------------------------------- Unregister-ScheduledTask -TaskName 'GuruRMM-PostRebootCleanup' -Confirm:$false -ErrorAction SilentlyContinue # --------------------------------------------------------------------------- # Schedule SYSTEM task to delete GuruRMM-Temp 2 minutes from now # (can't delete the account we're currently logged into) # --------------------------------------------------------------------------- try { $deleteScript = @" Remove-LocalUser -Name '$TempUser' -ErrorAction SilentlyContinue Remove-ItemProperty -Path '$SpecialKey' -Name '$TempUser' -ErrorAction SilentlyContinue Remove-Item -Path 'C:\Users\$TempUser' -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path '$StateFile' -Force -ErrorAction SilentlyContinue Unregister-ScheduledTask -TaskName 'GuruRMM-TempUserDelete' -Confirm:`$false -ErrorAction SilentlyContinue "@ $deleteScript | Set-Content "$Base\delete-temp-user.ps1" -Encoding UTF8 $action = New-ScheduledTaskAction -Execute 'powershell.exe' ` -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$Base\delete-temp-user.ps1`"" $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(2) $principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 5) Register-ScheduledTask -TaskName 'GuruRMM-TempUserDelete' -Action $action ` -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null } catch {} # --------------------------------------------------------------------------- # Close splash and log off # --------------------------------------------------------------------------- $sync.Close = $true Start-Sleep -Seconds 3 $uiPS.Stop() $uiRS.Close() & logoff