feat(guru-scan): fix exit code capture, add GURUSCAN_RESULT_JSON reporting, pre-scan hardening

Exit code fix: add $proc.Handle caching after Start-Process -PassThru to prevent
the handle from being released before ExitCode is readable (known PS5.1 bug).

GuruRMM reporting: launcher now finds results.json after each scan and emits
GURUSCAN_RESULT_JSON:<compressed> to stdout. Agent CommandResult captures it;
server stores it in commands.stdout for retrieval via GET /api/commands/:id.

Pre-scan hardening:
- Pre-flight EXE check: warns about missing scanner binaries before run starts
- Windows Defender exclusions added for scanner/log paths before scan, removed after

AdwCleaner: add /path {LOG_ROOT} arg so logs write directly to scan log root;
update log_src to {LOG_ROOT}\Logs to match.

HitmanPro: add /quiet to scan and clean args to suppress GUI in headless runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 00:13:16 -07:00
parent a8ee927db0
commit 40e090c95a
4 changed files with 113 additions and 64 deletions

View File

@@ -1,7 +1,7 @@
# #
# GuruScan.psm1 # GuruScan.psm1
# Multi-engine malware scan orchestrator for GuruRMM. # Multi-engine malware scan orchestrator for GuruRMM.
# PowerShell 5.1 compatible no ternary, no ??, no ?. operators. # PowerShell 5.1 compatible -- no ternary, no ??, no ?. operators.
# #
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
@@ -73,7 +73,7 @@ function Wait-ProcessWithTimeout {
.OUTPUTS .OUTPUTS
$true if the process exited cleanly within the timeout, $false if it was killed. $true if the process exited cleanly within the timeout, $false if it was killed.
.NOTES .NOTES
Calls WaitForExit(5000) before returning to flush the exit code required Calls WaitForExit(5000) before returning to flush the exit code -- required
before the ExitCode property is reliably readable on fast-exit processes. before the ExitCode property is reliably readable on fast-exit processes.
#> #>
param( param(
@@ -89,7 +89,7 @@ function Wait-ProcessWithTimeout {
} }
Start-Sleep -Seconds 5 Start-Sleep -Seconds 5
} }
# Flush exit code required before ExitCode property is readable # Flush exit code -- required before ExitCode property is readable
$Process.WaitForExit(5000) | Out-Null $Process.WaitForExit(5000) | Out-Null
return $true return $true
} }
@@ -184,7 +184,7 @@ function Get-ExitCodeThreats {
'MSERT' { if ($ExitCode -ne 0) { return 1 } } 'MSERT' { if ($ExitCode -ne 0) { return 1 } }
'TDSSKiller' { if ($ExitCode -eq 1) { return 1 } } 'TDSSKiller' { if ($ExitCode -eq 1) { return 1 } }
'Stinger' { if ($ExitCode -eq 13) { return 1 } } 'Stinger' { if ($ExitCode -eq 13) { return 1 } }
# RKill: exit 1 = processes were killed not a threat count # RKill: exit 1 = processes were killed -- not a threat count
} }
return 0 return 0
} }
@@ -399,7 +399,7 @@ function Invoke-ScanPass {
Write-Host " Scanner : $($s.Name)" -ForegroundColor Cyan Write-Host " Scanner : $($s.Name)" -ForegroundColor Cyan
Write-Host " Mode : $ScanMode" Write-Host " Mode : $ScanMode"
# Normalize property names JSON objects use snake_case, PS hashtables use PascalCase # Normalize property names -- JSON objects use snake_case, PS hashtables use PascalCase
$sName = if ($s.PSObject.Properties['Name']) { $s.Name } else { $s.name } $sName = if ($s.PSObject.Properties['Name']) { $s.Name } else { $s.name }
$sExe = if ($s.PSObject.Properties['Exe']) { $s.Exe } else { $s.exe } $sExe = if ($s.PSObject.Properties['Exe']) { $s.Exe } else { $s.exe }
$sInstallerExe = if ($s.PSObject.Properties['InstallerExe']) { $s.InstallerExe } else { $s.installer_exe } $sInstallerExe = if ($s.PSObject.Properties['InstallerExe']) { $s.InstallerExe } else { $s.installer_exe }
@@ -416,8 +416,10 @@ function Invoke-ScanPass {
$sWaitOnProcess = if ($s.PSObject.Properties['WaitOnProcess']) { $s.WaitOnProcess } else { $s.wait_on_process } $sWaitOnProcess = if ($s.PSObject.Properties['WaitOnProcess']) { $s.WaitOnProcess } else { $s.wait_on_process }
$sInstallerArgs = if ($s.PSObject.Properties['InstallerArgs']) { $s.InstallerArgs } else { $s.installer_args } $sInstallerArgs = if ($s.PSObject.Properties['InstallerArgs']) { $s.InstallerArgs } else { $s.installer_args }
$sRunUpdateAfterInstall = if ($s.PSObject.Properties['RunUpdateAfterInstall']) { $s.RunUpdateAfterInstall } else { $s.run_update_after_install } $sRunUpdateAfterInstall = if ($s.PSObject.Properties['RunUpdateAfterInstall']) { $s.RunUpdateAfterInstall } else { $s.run_update_after_install }
$sSession0Compatible = if ($s.PSObject.Properties['Session0Compatible']) { $s.Session0Compatible } else { $s.session0_compatible }
if ($null -eq $sInstallerArgs) { $sInstallerArgs = @('/S') } if ($null -eq $sInstallerArgs) { $sInstallerArgs = @('/S') }
if ($null -eq $sRunUpdateAfterInstall) { $sRunUpdateAfterInstall = $false } if ($null -eq $sRunUpdateAfterInstall) { $sRunUpdateAfterInstall = $false }
if ($null -eq $sSession0Compatible) { $sSession0Compatible = $true }
# Coerce null JSON values to empty arrays / nulls # Coerce null JSON values to empty arrays / nulls
if ($null -eq $sInstallerExe) { $sInstallerExe = $null } if ($null -eq $sInstallerExe) { $sInstallerExe = $null }
@@ -428,6 +430,20 @@ function Invoke-ScanPass {
if ($null -eq $sPostClean) { $sPostClean = @() } if ($null -eq $sPostClean) { $sPostClean = @() }
if ($null -eq $sServiceNames) { $sServiceNames = @() } if ($null -eq $sServiceNames) { $sServiceNames = @() }
# Skip GUI-only scanners when running as SYSTEM in Session 0 (no interactive desktop)
if (-not $sSession0Compatible -and (Test-RunningAsSystem)) {
Write-Host " [WARNING] $sName requires an interactive user session -- skipping (running as SYSTEM)" -ForegroundColor Yellow
$results.Add([pscustomobject]@{
Scanner = $sName
Status = 'SKIPPED (requires user session)'
ExitCode = $null
ThreatsFound = 0
Duration = '0 min'
LogPath = ''
})
continue
}
$mainExePath = $sExe $mainExePath = $sExe
$installerExePath = $sInstallerExe $installerExePath = $sInstallerExe
$exeToCheck = if ($installerExePath) { $installerExePath } else { $mainExePath } $exeToCheck = if ($installerExePath) { $installerExePath } else { $mainExePath }
@@ -524,6 +540,7 @@ function Invoke-ScanPass {
} }
$proc = Start-Process @startParams $proc = Start-Process @startParams
$null = $proc.Handle # force handle caching -- Start-Process -PassThru can lose the handle before exit, making ExitCode return null
$serviceArr = @() $serviceArr = @()
if ($sServiceNames -and $sServiceNames.Count -gt 0) { if ($sServiceNames -and $sServiceNames.Count -gt 0) {
@@ -539,6 +556,7 @@ function Invoke-ScanPass {
$status = 'TIMED OUT' $status = 'TIMED OUT'
Write-Host " [WARNING] $sName timed out after $effectiveTimeoutMin min" -ForegroundColor Yellow Write-Host " [WARNING] $sName timed out after $effectiveTimeoutMin min" -ForegroundColor Yellow
} else { } else {
$proc.WaitForExit() | Out-Null # no-arg form guarantees ExitCode is readable
$exitCode = $proc.ExitCode $exitCode = $proc.ExitCode
} }
} }
@@ -619,7 +637,7 @@ function Register-ScannerCleanupTask {
Write-Host '' Write-Host ''
Write-Host '------------------------------------------------------------' -ForegroundColor Yellow Write-Host '------------------------------------------------------------' -ForegroundColor Yellow
Write-Host ' Reboot recommended registering post-reboot cleanup task' -ForegroundColor Yellow Write-Host ' Reboot recommended -- registering post-reboot cleanup task' -ForegroundColor Yellow
Write-Host '------------------------------------------------------------' -ForegroundColor Yellow Write-Host '------------------------------------------------------------' -ForegroundColor Yellow
# Write state for the cleanup script to read # Write state for the cleanup script to read
@@ -629,45 +647,14 @@ function Register-ScannerCleanupTask {
created_at = (Get-Date).ToUniversalTime().ToString('o') created_at = (Get-Date).ToUniversalTime().ToString('o')
} | ConvertTo-Json | Set-Content "$script:Base\cleanup-state.json" -Encoding UTF8 } | ConvertTo-Json | Set-Content "$script:Base\cleanup-state.json" -Encoding UTF8
# Write the cleanup script that the scheduled task will run # Copy the static cleanup script from the module directory to C:\GuruScan\
$cleanupScript = @' $cleanupSrc = Join-Path $script:ModuleRoot 'Invoke-ScannerCleanup.ps1'
$Base = 'C:\GuruScan' if (Test-Path $cleanupSrc) {
$state = @{ scan_id = ''; log_root = '' } Copy-Item -Path $cleanupSrc -Destination "$script:Base\Invoke-ScannerCleanup.ps1" -Force
$stateFile = "$Base\cleanup-state.json" } else {
Write-Host " [WARNING] Invoke-ScannerCleanup.ps1 not found at $cleanupSrc -- cleanup task will not run." -ForegroundColor Yellow
if (Test-Path $stateFile) { return
try { $state = Get-Content $stateFile -Raw | ConvertFrom-Json } catch {} }
}
# Remove scanner installation files
$scannerPaths = @(
'C:\EmsisoftCmd',
'C:\AdwCleaner',
'C:\ProgramData\HitmanPro',
'C:\ProgramData\HitmanPro.Alert'
)
foreach ($p in $scannerPaths) {
Remove-Item -Path $p -Recurse -Force -ErrorAction SilentlyContinue
}
# Remove scanner download EXEs
Remove-Item -Path "$Base\downloads" -Recurse -Force -ErrorAction SilentlyContinue
# Flag logs for GuruRMM to pull
@{
scan_id = $state.scan_id
log_root = $state.log_root
zip_path = "$Base\reports\$($state.scan_id).zip"
cleaned_at = (Get-Date).ToUniversalTime().ToString('o')
} | ConvertTo-Json | Set-Content "$Base\logs-ready.json" -Encoding UTF8
# Remove state file
Remove-Item -Path $stateFile -Force -ErrorAction SilentlyContinue
# Unregister this task
Unregister-ScheduledTask -TaskName 'GuruRMM-ScannerCleanup' -Confirm:$false -ErrorAction SilentlyContinue
'@
$cleanupScript | Set-Content "$script:Base\Invoke-ScannerCleanup.ps1" -Encoding UTF8
# Register as SYSTEM logon task with 30-minute delay # Register as SYSTEM logon task with 30-minute delay
try { try {
@@ -776,11 +763,11 @@ function Invoke-GuruScan {
[string]$OutputSink = 'Disk' [string]$OutputSink = 'Disk'
) )
# Whitelist written to C:\GuruScan\whitelist.txt before any scanner runs. # Whitelist -- written to C:\GuruScan\whitelist.txt before any scanner runs.
# Emsisoft and HitmanPro honour this; RKill and AdwCleaner do not. # Emsisoft and HitmanPro honour this; RKill and AdwCleaner do not.
$whitelist = @('C:\GuruScan') $whitelist = @('C:\GuruScan')
# ForceRemove blacklist items removed after all scanners complete. # ForceRemove blacklist -- items removed after all scanners complete.
$forceRemove = @() $forceRemove = @()
$scanStamp = "$env:COMPUTERNAME-$(Get-Date -Format yyyyMMdd-HHmmss)" $scanStamp = "$env:COMPUTERNAME-$(Get-Date -Format yyyyMMdd-HHmmss)"
@@ -836,6 +823,31 @@ function Invoke-GuruScan {
} }
} }
# Pre-flight: warn about missing scanner EXEs so missing scanners are
# visible up front rather than silently skipped mid-run.
$missingExes = @()
foreach ($s in $scannerList) {
$sName = if ($s.PSObject.Properties['Name']) { $s.Name } else { $s.name }
$sExe = if ($s.PSObject.Properties['Exe']) { $s.Exe } else { $s.exe }
$sInstExe = if ($s.PSObject.Properties['InstallerExe']) { $s.InstallerExe } else { $s.installer_exe }
if (-not (Test-Path $sExe) -and (-not $sInstExe -or -not (Test-Path $sInstExe))) {
$missingExes += $sName
}
}
if ($missingExes.Count -gt 0) {
Write-Host "[WARNING] Missing scanner EXEs -- these will be skipped: $($missingExes -join ', ')" -ForegroundColor Yellow
Write-Host " Run .\Download-Scanners.ps1 to download them." -ForegroundColor Yellow
Write-Host ''
}
# Add Windows Defender exclusions for scanner paths so Defender does not
# quarantine scanner EXEs or log files mid-run.
$defenderExclusions = @($script:Base, $script:LogRoot, 'C:\EmsisoftCmd', 'C:\AdwCleaner')
try {
Add-MpPreference -ExclusionPath $defenderExclusions -ErrorAction SilentlyContinue
Write-Host "[INFO] Windows Defender exclusions added for scanner paths" -ForegroundColor Cyan
} catch {}
# First pass # First pass
$startedAt = Get-Date $startedAt = Get-Date
$passParams = @{ $passParams = @{
@@ -964,7 +976,14 @@ function Invoke-GuruScan {
Register-ScannerCleanupTask -ScanId $scanStamp -LogRoot $runLogRoot Register-ScannerCleanupTask -ScanId $scanStamp -LogRoot $runLogRoot
} }
# Return result object (always returned; caller uses it for RMM sink) # Remove the Defender exclusions added at scan start.
try {
Remove-MpPreference -ExclusionPath $defenderExclusions -ErrorAction SilentlyContinue
Write-Host "[INFO] Windows Defender exclusions removed" -ForegroundColor Cyan
} catch {}
# Return result object (RMM sink) or suppress it (Disk sink lets the
# launcher read results.json from disk for structured reporting).
$resultRecord = [pscustomobject]$resultsObj $resultRecord = [pscustomobject]$resultsObj
if ($OutputSink -eq 'RMM') { if ($OutputSink -eq 'RMM') {
return $resultRecord return $resultRecord
@@ -1093,10 +1112,10 @@ function Invoke-Remediation {
if ($totalThreats -gt 0) { if ($totalThreats -gt 0) {
Write-Host " [WARNING] $totalThreats threat indicator(s) still detected after clean pass." -ForegroundColor Yellow Write-Host " [WARNING] $totalThreats threat indicator(s) still detected after clean pass." -ForegroundColor Yellow
Write-Host " Review logs carefully some threats may require a reboot before" -ForegroundColor Yellow Write-Host " Review logs carefully -- some threats may require a reboot before" -ForegroundColor Yellow
Write-Host " they can be fully removed. Reboot and re-scan." -ForegroundColor Yellow Write-Host " they can be fully removed. Reboot and re-scan." -ForegroundColor Yellow
} else { } else {
Write-Host " [OK] Clean pass complete no threats detected in output." -ForegroundColor Green Write-Host " [OK] Clean pass complete -- no threats detected in output." -ForegroundColor Green
} }
Write-Host "" Write-Host ""
@@ -1271,7 +1290,7 @@ function Get-ScanSummary {
Write-Host " Full logs: $(Split-Path $ResultsFile)" -ForegroundColor Gray Write-Host " Full logs: $(Split-Path $ResultsFile)" -ForegroundColor Gray
Write-Host "" Write-Host ""
# Ollama AI analysis (optional requires -AI switch) # Ollama AI analysis (optional -- requires -AI switch)
if ($AI) { if ($AI) {
Write-Host "================================================================" -ForegroundColor Cyan Write-Host "================================================================" -ForegroundColor Cyan
Write-Host " AI Analysis (Ollama)" -ForegroundColor Cyan Write-Host " AI Analysis (Ollama)" -ForegroundColor Cyan
@@ -1311,7 +1330,7 @@ $logText
Task: Extract a concise list of specific threat names, file paths, registry keys, or other findings from the logs. Task: Extract a concise list of specific threat names, file paths, registry keys, or other findings from the logs.
Format your response as a plain numbered list. If no specific threats are named in the logs, say "No named threats found in logs." Format your response as a plain numbered list. If no specific threats are named in the logs, say "No named threats found in logs."
Do not add commentary only the list. Do not add commentary -- only the list.
"@ "@
$threatDetails = Invoke-Ollama -Prompt $threatPrompt -Model $OllamaModel -BaseUrl $OllamaUrl $threatDetails = Invoke-Ollama -Prompt $threatPrompt -Model $OllamaModel -BaseUrl $OllamaUrl
@@ -1320,7 +1339,7 @@ Do not add commentary — only the list.
Write-Host " Identified Threats / Findings:" -ForegroundColor Yellow Write-Host " Identified Threats / Findings:" -ForegroundColor Yellow
$threatDetails -split "`n" | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } $threatDetails -split "`n" | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
} else { } else {
Write-Host " [WARNING] Ollama unavailable skipping threat extraction." -ForegroundColor Yellow Write-Host " [WARNING] Ollama unavailable -- skipping threat extraction." -ForegroundColor Yellow
} }
# Step 2: Prioritized remediation recommendations # Step 2: Prioritized remediation recommendations
@@ -1345,7 +1364,7 @@ Plain numbered list, no markdown headers, no padding text. Be specific.
Write-Host " Remediation Checklist:" -ForegroundColor Cyan Write-Host " Remediation Checklist:" -ForegroundColor Cyan
$recommendations -split "`n" | ForEach-Object { Write-Host " $_" -ForegroundColor Cyan } $recommendations -split "`n" | ForEach-Object { Write-Host " $_" -ForegroundColor Cyan }
} else { } else {
Write-Host " [WARNING] Ollama unavailable skipping recommendations." -ForegroundColor Yellow Write-Host " [WARNING] Ollama unavailable -- skipping recommendations." -ForegroundColor Yellow
} }
Write-Host "" Write-Host ""
@@ -1363,7 +1382,7 @@ function Invoke-PostRebootCleanup {
Normally this runs automatically via the GuruRMM-ScannerCleanup scheduled task. Normally this runs automatically via the GuruRMM-ScannerCleanup scheduled task.
.PARAMETER StateFile .PARAMETER StateFile
Path to cleanup-state.json. Defaults to C:\GuruScan\cleanup-state.json. Path to cleanup-state.json. Defaults to C:\GuruScan\cleanup-state.json.
(Parameter kept for backward compatibility the cleanup script reads it directly.) (Parameter kept for backward compatibility -- the cleanup script reads it directly.)
#> #>
[CmdletBinding()] [CmdletBinding()]
param( param(

View File

@@ -52,4 +52,22 @@ if (-not (Test-Path $moduleManifest)) {
} }
Import-Module $moduleManifest -Force Import-Module $moduleManifest -Force
$scanStart = Get-Date
Invoke-GuruScan @PSBoundParameters -OutputSink Disk Invoke-GuruScan @PSBoundParameters -OutputSink Disk
# Emit structured JSON to stdout for GuruRMM CommandResult capture.
# Read from results.json written during this run (newer than $scanStart).
$resultsFile = Get-ChildItem -Path 'C:\ScanLogs' -Recurse -Filter 'results.json' -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt $scanStart } |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if ($resultsFile) {
$json = Get-Content -Path $resultsFile.FullName -Raw -Encoding UTF8 -ErrorAction SilentlyContinue
if ($json) {
# Compress to single line so the agent's stdout parser sees it as one line
$compressed = ($json | ConvertFrom-Json | ConvertTo-Json -Depth 10 -Compress)
Write-Output ''
Write-Output "GURUSCAN_RESULT_JSON:$compressed"
}
}

View File

@@ -28,7 +28,7 @@ Scanners run in this order. Each stage hands off to the next regardless of findi
| # | Scanner | Category | Notes | | # | Scanner | Category | Notes |
|---|---------|----------|-------| |---|---------|----------|-------|
| 1 | **RKill** | process-killer | Kills malware-related processes before scanners run. Exit 1 = processes were killed (not a threat indicator). | | 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. | | 2 | **AdwCleaner** | adware | Removes adware, PUPs, and browser hijackers. **Requires an interactive user session** (GUI app; no headless/SYSTEM mode). Skipped automatically when running as SYSTEM with no desktop. To include AdwCleaner, dispatch via GuruRMM with `context: user_session`. |
| 3 | **Emsisoft Command Line Scanner** | antimalware | Two-step: NSIS installer extracts to `C:\EmsisoftCmd\`, then `/update` fetches latest definitions, then scans. | | 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`. | | 4 | **HitmanPro** | antimalware | Cloud-assisted second-opinion scanner. Trial registry is reset before each run via `Invoke-HitmanProTrialReset`. |
@@ -75,6 +75,11 @@ To run cleanup immediately without waiting (e.g. if the task was missed):
- `-Headless` passes `NoNewWindow` to all scanner launches, suppressing UI windows. - `-Headless` passes `NoNewWindow` to all scanner launches, suppressing UI windows.
Use this when dispatching from an RMM agent that has no interactive desktop. Use this when dispatching from an RMM agent that has 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). Currently: **AdwCleaner**.
The result record shows `SKIPPED (requires user session)` rather than a failure.
- To run AdwCleaner via GuruRMM, dispatch with `context: user_session` so it runs in
the active user's desktop session (requires a logged-in user on the target machine).
--- ---
@@ -156,6 +161,7 @@ guru-scan\
Invoke-Remediation.ps1 # Thin launcher -> Invoke-Remediation Invoke-Remediation.ps1 # Thin launcher -> Invoke-Remediation
Get-ScanSummary.ps1 # Thin launcher -> Get-ScanSummary Get-ScanSummary.ps1 # Thin launcher -> Get-ScanSummary
Invoke-PostRebootCleanup.ps1 # Thin launcher -> Invoke-PostRebootCleanup (manual cleanup trigger) Invoke-PostRebootCleanup.ps1 # Thin launcher -> Invoke-PostRebootCleanup (manual cleanup trigger)
Invoke-ScannerCleanup.ps1 # Post-reboot cleanup script; copied to C:\GuruScan\ when reboot is needed
Download-Scanners.ps1 # Downloads scanner EXEs from scanners.json URLs Download-Scanners.ps1 # Downloads scanner EXEs from scanners.json URLs
downloads\ # Scanner EXEs (gitignored) downloads\ # Scanner EXEs (gitignored)
``` ```

View File

@@ -21,7 +21,8 @@
"service_names": [], "service_names": [],
"hitmanpro_trial_reset": false, "hitmanpro_trial_reset": false,
"whitelist_arg": null, "whitelist_arg": null,
"wait_on_process": null "wait_on_process": null,
"session0_compatible": true
}, },
{ {
"name": "AdwCleaner", "name": "AdwCleaner",
@@ -33,18 +34,19 @@
"download_url": "https://adwcleaner.malwarebytes.com/adwcleaner?channel=release", "download_url": "https://adwcleaner.malwarebytes.com/adwcleaner?channel=release",
"manual_download": true, "manual_download": true,
"manual_download_note": "Malwarebytes blocks automated downloads; download manually from https://www.malwarebytes.com/adwcleaner", "manual_download_note": "Malwarebytes blocks automated downloads; download manually from https://www.malwarebytes.com/adwcleaner",
"scan_args": ["/eula", "/scan", "/noreboot"], "scan_args": ["/eula", "/scan", "/noreboot", "/path", "{LOG_ROOT}"],
"clean_args": ["/eula", "/clean", "/noreboot"], "clean_args": ["/eula", "/clean", "/noreboot", "/path", "{LOG_ROOT}"],
"log_src": "C:\\AdwCleaner\\Logs", "log_src": "{LOG_ROOT}\\Logs",
"timeout_min": 60, "timeout_min": 60,
"randomize_exe": false, "randomize_exe": false,
"pre_close_processes": [], "pre_close_processes": [],
"pre_clean_paths": ["C:\\AdwCleaner"], "pre_clean_paths": ["C:\\AdwCleaner"],
"post_clean_paths": ["C:\\AdwCleaner"], "post_clean_paths": [],
"service_names": ["AdwCleanerSvc"], "service_names": ["AdwCleanerSvc"],
"hitmanpro_trial_reset": false, "hitmanpro_trial_reset": false,
"whitelist_arg": null, "whitelist_arg": null,
"wait_on_process": "AdwCleaner" "wait_on_process": "AdwCleaner",
"session0_compatible": false
}, },
{ {
"name": "Emsisoft", "name": "Emsisoft",
@@ -94,7 +96,8 @@
"service_names": [], "service_names": [],
"hitmanpro_trial_reset": false, "hitmanpro_trial_reset": false,
"whitelist_arg": "emsisoft", "whitelist_arg": "emsisoft",
"wait_on_process": "a2cmd" "wait_on_process": "a2cmd",
"session0_compatible": true
}, },
{ {
"name": "HitmanPro", "name": "HitmanPro",
@@ -109,12 +112,14 @@
"scan_args": [ "scan_args": [
"/noinstall", "/noinstall",
"/scan", "/scan",
"/quiet",
"/log=\"{LOG_ROOT}\\HitmanPro_Scan_Log.txt\"", "/log=\"{LOG_ROOT}\\HitmanPro_Scan_Log.txt\"",
"/excludelist=\"C:\\GuruScan\\whitelist.txt\"" "/excludelist=\"C:\\GuruScan\\whitelist.txt\""
], ],
"clean_args": [ "clean_args": [
"/noinstall", "/noinstall",
"/clean", "/clean",
"/quiet",
"/log=\"{LOG_ROOT}\\HitmanPro_Scan_Log.txt\"", "/log=\"{LOG_ROOT}\\HitmanPro_Scan_Log.txt\"",
"/excludelist=\"C:\\GuruScan\\whitelist.txt\"" "/excludelist=\"C:\\GuruScan\\whitelist.txt\""
], ],
@@ -137,7 +142,8 @@
"service_names": [], "service_names": [],
"hitmanpro_trial_reset": true, "hitmanpro_trial_reset": true,
"whitelist_arg": "hitmanpro", "whitelist_arg": "hitmanpro",
"wait_on_process": "HitmanPro_x64" "wait_on_process": "HitmanPro_x64",
"session0_compatible": true
} }
] ]
} }