6.0 KiB
GuruScan
Multi-engine malware scan orchestrator for GuruRMM. Runs a sequenced chain of portable security scanners, captures all logs, and writes structured JSON results for downstream processing by the RMM agent.
Deploy Layout
| Path | Purpose |
|---|---|
C:\GuruScan\ |
Base directory — module files, whitelist, state files |
C:\GuruScan\downloads\ |
Scanner EXEs (populated by Download-Scanners.ps1) |
C:\GuruScan\reports\ |
Per-scan zip archives |
C:\ScanLogs\ |
Per-scan log folders (<HOSTNAME>-<YYYYMMDD-HHmmss>\) |
The module files (GuruScan.psm1, GuruScan.psd1, scanners.json) live in the
same directory as the launcher scripts (Invoke-GuruScan.ps1, etc.). This is
typically C:\GuruScan\ or the RMM deployment path — wherever the scripts are placed.
Scanner Chain
Scanners run in this order. Each stage hands off to the next regardless of findings.
| # | Scanner | Category | Notes |
|---|---|---|---|
| 1 | RKill | process-killer | Kills malware-related processes before scanners run. Exit 1 = processes were killed (not a threat indicator). |
| 2 | AdwCleaner | adware | Removes adware, PUPs, and browser hijackers. |
| 3 | Emsisoft Command Line Scanner | antimalware | Two-step: NSIS installer extracts to C:\EmsisoftCmd\, then /update fetches latest definitions, then scans. |
| 4 | HitmanPro | antimalware | Cloud-assisted second-opinion scanner. Trial registry is reset before each run via Invoke-HitmanProTrialReset. |
MSERT (Microsoft Safety Scanner) is excluded from the default chain — it is too slow
for routine remediation runs. Add it back to scanners.json if needed.
Exit Code Semantics
| Scanner | Exit 0 | Exit 1 | Exit 2 | Exit 3 | Other |
|---|---|---|---|---|---|
| RKill | Clean run | Processes killed (not a threat) | — | — | — |
| AdwCleaner | Clean | Cleaned, no reboot needed | Cleaned, reboot required | Found but not cleaned (scan-only) | — |
| Emsisoft | Clean | Threats found/cleaned | Cleaned, reboot required | — | — |
| HitmanPro | Clean | Cleaned | Cleaned, reboot required | — | — |
| MSERT | Clean | Threats found/cleaned | — | — | Non-zero = threats |
| TDSSKiller | Clean | Threats found | — | — | — |
| Stinger | Clean | — | — | — | 13 = threats |
Reboot-required exit codes: AdwCleaner 2, HitmanPro 2, Emsisoft 2.
Post-Scan Cleanup Lifecycle
When any scanner exits with a reboot-required code (exit 2), the following sequence runs automatically — no forced reboot, no temp user account:
Register-ScannerCleanupTaskwritescleanup-state.json(scan ID + log path) toC:\GuruScan\.Invoke-ScannerCleanup.ps1is written toC:\GuruScan\.- A SYSTEM scheduled task (
GuruRMM-ScannerCleanup) is registered with an at-logon + 30-minute delay trigger. - The scan completes and prints a message to reboot at your convenience.
- After the next natural reboot and user login, the task fires 30 minutes later (silently, in the background as SYSTEM).
- The cleanup script removes all scanner installation paths (
C:\EmsisoftCmd,C:\AdwCleaner,C:\ProgramData\HitmanPro*,C:\GuruScan\downloads\), writeslogs-ready.jsonfor GuruRMM to pick up, and unregisters itself.
To run cleanup immediately without waiting (e.g. if the task was missed):
.\Invoke-PostRebootCleanup.ps1
Headless / SYSTEM Behavior
-HeadlesspassesNoNewWindowto all scanner launches, suppressing UI windows. Use this when dispatching from an RMM agent that has no interactive desktop.
Licensing
| Scanner | License for MSP use |
|---|---|
| RKill | Free (BleepingComputer) |
| AdwCleaner | Free for personal and commercial use |
| Emsisoft Command Line Scanner | Free for personal and MSP remediation use |
| HitmanPro | Commercial license required. Each scan uses trial mode; Invoke-HitmanProTrialReset resets the trial window. Verify current licensing terms at https://www.hitmanpro.com before deploying at scale. |
Always verify current licensing terms with each vendor before large-scale deployment.
Stand-alone Usage
# Run all scanners in clean mode (default)
.\Invoke-GuruScan.ps1
# Detect only, then auto-remediate if threats found
.\Invoke-GuruScan.ps1 -ScanOnly -AutoRemediate
# Suppress scanner windows (RMM dispatch)
.\Invoke-GuruScan.ps1 -Headless
# View the latest scan results
.\Get-ScanSummary.ps1
# View with AI analysis (requires Ollama)
.\Get-ScanSummary.ps1 -AI
# Re-run clean pass against a prior scan
.\Invoke-Remediation.ps1 -LogRoot "C:\ScanLogs\DESKTOP-20260523-143000"
# Download/refresh scanner EXEs
.\Download-Scanners.ps1
Module Usage
The launcher scripts are thin wrappers. Import the module directly for scripted/pipeline use:
Import-Module .\GuruScan.psd1
# Disk sink (default) — writes results.json + CSV + zip
Invoke-GuruScan
# RMM sink — returns result object to the pipeline, no disk writes
$result = Invoke-GuruScan -OutputSink RMM -Headless
if ($result.total_threats -gt 0) { ... }
# Remediation from a prior scan
Invoke-Remediation -LogRoot "C:\ScanLogs\DESKTOP-20260523-143000"
# Summary report
Get-ScanSummary -AI
# Manual scanner cleanup (normally runs via scheduled task)
Invoke-PostRebootCleanup
Module Structure
guru-scan\
GuruScan.psm1 # Core module — all helpers + exported cmdlets
GuruScan.psd1 # Module manifest
scanners.json # Scanner definitions (single source of truth)
Invoke-GuruScan.ps1 # Thin launcher -> Invoke-GuruScan
Invoke-Remediation.ps1 # Thin launcher -> Invoke-Remediation
Get-ScanSummary.ps1 # Thin launcher -> Get-ScanSummary
Invoke-PostRebootCleanup.ps1 # Thin launcher -> Invoke-PostRebootCleanup (manual cleanup trigger)
Download-Scanners.ps1 # Downloads scanner EXEs from scanners.json URLs
downloads\ # Scanner EXEs (gitignored)