# 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.

---

## 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 | **Emsisoft Command Line Scanner** | antimalware | Two-step: NSIS installer extracts to `C:\EmsisoftCmd\`, then `/update` fetches latest definitions, then deep-scans `C:\`. |
| 3 | **HitmanPro** | antimalware | Cloud-assisted second-opinion scanner. Trial registry is reset before each run via `Invoke-HitmanProTrialReset`. Runs with `/quiet` (no GUI). |

**AdwCleaner** is not currently in the chain. It requires both elevated privileges
and an interactive desktop session simultaneously — SYSTEM context is elevated but
Session 0 (no desktop), user_session has a desktop but a non-elevated WTS token.
It will be re-added once a scheduled-task `InteractiveToken` dispatch is implemented.

MSERT (Microsoft Safety Scanner) is also excluded from the default chain — too slow
for routine runs. Add it 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) | — | — | — |
| 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: 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:

1. `Register-ScannerCleanupTask` writes `cleanup-state.json` (scan ID + log path) to `C:\GuruScan\`.
2. `Invoke-ScannerCleanup.ps1` is written to `C:\GuruScan\`.
3. A SYSTEM scheduled task (`GuruRMM-ScannerCleanup`) is registered with an **at-logon + 30-minute delay** trigger.
4. The scan completes and prints a message to reboot at your convenience.
5. After the next natural reboot and user login, the task fires 30 minutes later (silently, as SYSTEM).
6. The cleanup script removes all scanner installation paths (`C:\EmsisoftCmd`, `C:\ProgramData\HitmanPro*`, `C:\GuruScan\downloads\`), writes `logs-ready.json` for GuruRMM to pick up, and unregisters itself.

To run cleanup immediately without waiting:
```powershell
.\Invoke-PostRebootCleanup.ps1
```

---

## Headless / SYSTEM Behavior

- `-Headless` launches all scanner processes with `WindowStyle=Hidden`, suppressing
  UI windows and preventing child processes from inheriting the PowerShell pipe
  handles. Use this when dispatching from an RMM agent with no interactive desktop.
- Scanners with `session0_compatible: false` in `scanners.json` are automatically
  skipped when the module detects it is running as SYSTEM (Session 0). The result
  record shows `SKIPPED (requires user session)` rather than a failure.
- The whitelist (`C:\GuruScan\whitelist.txt`) is honoured by Emsisoft (`/wl=`) and
  HitmanPro (`/excludelist=`). RKill does not support a whitelist.

---

## GuruRMM Integration

When dispatched via GuruRMM in `system` context with `-Headless`, the launcher
emits a `GURUSCAN_RESULT_JSON:<compressed-json>` line to stdout at the end of the
run. The GuruRMM agent captures this in `command.stdout` for structured result
reporting on the dashboard.

```powershell
# GuruRMM dispatch command (system context, elevated):
C:\GuruScan\Invoke-GuruScan.ps1 -Headless
```

The JSON structure matches `results.json` written to disk:

```json
{
  "scan_id": "HOSTNAME-20260527-010203",
  "machine": "HOSTNAME",
  "started_at": "...",
  "completed_at": "...",
  "total_threats": 0,
  "reboot_required": false,
  "scan_mode": "clean",
  "scanners": [
    { "name": "RKill",    "status": "completed", "exit_code": 1, "threats_found": 0, "duration_min": 0.09 },
    { "name": "Emsisoft", "status": "completed", "exit_code": 0, "threats_found": 0, "duration_min": 4.8  },
    { "name": "HitmanPro","status": "completed", "exit_code": 0, "threats_found": 0, "duration_min": 2.2  }
  ]
}
```

---

## Licensing

| Scanner | License for MSP use |
|---------|---------------------|
| RKill | Free (BleepingComputer) |
| 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

```powershell
# 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:

```powershell
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
  Invoke-ScannerCleanup.ps1    # Post-reboot cleanup; copied to C:\GuruScan\ when reboot is needed
  Download-Scanners.ps1        # Downloads scanner EXEs from scanners.json URLs
  downloads\                   # Scanner EXEs (gitignored)
```
