sync: auto-sync from HOWARD-HOME at 2026-05-26 21:58:00
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-05-26 21:58:00
This commit is contained in:
@@ -1,215 +1,13 @@
|
||||
#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.
|
||||
Manually triggers GuruScan post-scan cleanup (removes scanner files).
|
||||
Normally this runs automatically via the GuruRMM-ScannerCleanup scheduled task.
|
||||
#>
|
||||
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 {}
|
||||
$moduleManifest = Join-Path $PSScriptRoot 'GuruScan.psd1'
|
||||
if (-not (Test-Path $moduleManifest)) {
|
||||
Write-Host "[ERROR] GuruScan module not found: $moduleManifest" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$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
|
||||
Import-Module $moduleManifest -Force
|
||||
Invoke-PostRebootCleanup
|
||||
|
||||
Reference in New Issue
Block a user