Adds a complete PowerShell-based malware scanning toolkit: - Invoke-GuruScan.ps1: main orchestrator running RKill, AdwCleaner, Emsisoft, HitmanPro, and ESET in sequence with pre/post cleanup, whitelist support, ForceRemove blacklist, and -Headless switch - Invoke-PostRebootCleanup.ps1: post-reboot temp-user session that shows a fullscreen splash, verifies boot-time cleanup completed, removes scanner files, and restores the original user login name - Download-Scanners.ps1: downloads/refreshes scanner EXEs - Get-ScanSummary.ps1: parses results.json with optional Ollama AI analysis - Invoke-Remediation.ps1: re-runs scanners in clean mode Key features: exit-code-based reboot detection, whoami-based user capture (SYSTEM-safe via quser fallback), domain\user and local MACHINE\user restore on login screen after cleanup reboot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
116 lines
4.5 KiB
PowerShell
116 lines
4.5 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Downloads or refreshes all scanner executables defined in scanners.json.
|
|
.DESCRIPTION
|
|
Reads scanner definitions from scanners.json and downloads each scanner EXE
|
|
to the downloads\ subdirectory. Skips scanners with null download URLs.
|
|
.PARAMETER Force
|
|
Re-download all scanners even if they already exist locally.
|
|
.EXAMPLE
|
|
.\Download-Scanners.ps1
|
|
.\Download-Scanners.ps1 -Force
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[switch]$Force
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
|
|
$ConfigPath = Join-Path $PSScriptRoot 'scanners.json'
|
|
$DownloadsDir = Join-Path $PSScriptRoot 'downloads'
|
|
|
|
if (-not (Test-Path $ConfigPath)) {
|
|
Write-Host "[ERROR] scanners.json not found at: $ConfigPath" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
$config = Get-Content $ConfigPath -Raw | ConvertFrom-Json
|
|
|
|
if (-not (Test-Path $DownloadsDir)) {
|
|
New-Item -ItemType Directory -Path $DownloadsDir -Force | Out-Null
|
|
Write-Host "[INFO] Created downloads directory: $DownloadsDir" -ForegroundColor Cyan
|
|
}
|
|
|
|
$results = [System.Collections.Generic.List[pscustomobject]]::new()
|
|
|
|
foreach ($scanner in $config.scanners) {
|
|
$urlToUse = $null
|
|
$fileToSave = $null
|
|
|
|
# Manual-download entries: print instructions and skip
|
|
if ($scanner.PSObject.Properties['manual_download'] -and $scanner.manual_download -eq $true) {
|
|
$note = if ($scanner.PSObject.Properties['manual_download_note']) { $scanner.manual_download_note } else { 'Place EXE manually in downloads\' }
|
|
Write-Host " [MANUAL] $($scanner.name) — $note" -ForegroundColor Yellow
|
|
$results.Add([pscustomobject]@{ Scanner = $scanner.name; Status = 'MANUAL'; File = ''; Size = 0 })
|
|
continue
|
|
}
|
|
|
|
if ($scanner.installer_exe -and $scanner.download_url) {
|
|
$urlToUse = $scanner.download_url
|
|
$fileToSave = Join-Path $DownloadsDir (Split-Path $scanner.installer_exe -Leaf)
|
|
}
|
|
elseif ($scanner.download_url) {
|
|
$urlToUse = $scanner.download_url
|
|
$leafName = Split-Path $scanner.exe -Leaf
|
|
$fileToSave = Join-Path $DownloadsDir $leafName
|
|
}
|
|
else {
|
|
Write-Host " [SKIP] $($scanner.name) — no download URL configured" -ForegroundColor Yellow
|
|
$results.Add([pscustomobject]@{ Scanner = $scanner.name; Status = 'SKIPPED'; File = ''; Size = 0 })
|
|
continue
|
|
}
|
|
|
|
if ((Test-Path $fileToSave) -and -not $Force) {
|
|
$existingSize = (Get-Item $fileToSave).Length
|
|
Write-Host " [OK] $($scanner.name) already exists ($([math]::Round($existingSize / 1MB, 1)) MB)" -ForegroundColor Green
|
|
$results.Add([pscustomobject]@{ Scanner = $scanner.name; Status = 'EXISTS'; File = $fileToSave; Size = $existingSize })
|
|
continue
|
|
}
|
|
|
|
Write-Host " [....] $($scanner.name) — downloading from $urlToUse" -ForegroundColor Cyan
|
|
try {
|
|
$wc = New-Object System.Net.WebClient
|
|
$wc.Headers.Add('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)')
|
|
$wc.DownloadFile($urlToUse, $fileToSave)
|
|
$wc.Dispose()
|
|
|
|
if (-not (Test-Path $fileToSave)) {
|
|
throw "File not found after download attempt"
|
|
}
|
|
$downloadedSize = (Get-Item $fileToSave).Length
|
|
if ($downloadedSize -eq 0) {
|
|
throw "Downloaded file is 0 bytes — URL may be invalid or redirected"
|
|
}
|
|
|
|
Write-Host " [OK] $($scanner.name) downloaded ($([math]::Round($downloadedSize / 1MB, 1)) MB) -> $fileToSave" -ForegroundColor Green
|
|
$results.Add([pscustomobject]@{ Scanner = $scanner.name; Status = 'DOWNLOADED'; File = $fileToSave; Size = $downloadedSize })
|
|
}
|
|
catch {
|
|
Write-Host " [ERROR] $($scanner.name) — $($_.Exception.Message)" -ForegroundColor Red
|
|
if (Test-Path $fileToSave) {
|
|
Remove-Item $fileToSave -Force -ErrorAction SilentlyContinue
|
|
}
|
|
$results.Add([pscustomobject]@{ Scanner = $scanner.name; Status = 'FAILED'; File = $fileToSave; Size = 0 })
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "=== Download Summary ===" -ForegroundColor Cyan
|
|
$results | Format-Table -AutoSize
|
|
|
|
$manual = $results | Where-Object { $_.Status -eq 'MANUAL' }
|
|
if ($manual) {
|
|
Write-Host "[INFO] $($manual.Count) scanner(s) require manual download — see notes above." -ForegroundColor Yellow
|
|
}
|
|
|
|
$failed = $results | Where-Object { $_.Status -eq 'FAILED' }
|
|
if ($failed) {
|
|
Write-Host "[WARNING] $($failed.Count) scanner(s) failed to download. Scan coverage will be incomplete." -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
exit 0
|