Gorstaks Enterprise EDR
unknown
powershell
3 months ago
343 kB
9
No Index
# Antivirus.ps1
# Author: Gorstak
#Requires -RunAsAdministrator
param(
[switch]$Uninstall
)
$Script:InstallDir = "C:\ProgramData\AntivirusProtection"
$Script:ScriptInstallPath = Join-Path $Script:InstallDir "Antivirus.ps1"
$Script:DataDir = Join-Path $Script:InstallDir "Data"
$Script:LogsDir = Join-Path $Script:InstallDir "Logs"
$Script:QuarantineDir = Join-Path $Script:InstallDir "Quarantine"
$Script:ReportsDir = Join-Path $Script:InstallDir "Reports"
if ($Uninstall) {
Write-Host "[UNINSTALL] Starting uninstallation process..." -ForegroundColor Yellow
# Stop any running instances
try {
$runningInstances = Get-Process -Name "powershell" -ErrorAction SilentlyContinue | Where-Object {
$_.MainModule.FileName -eq $Script:ScriptInstallPath
}
if ($runningInstances) {
Write-Host "[UNINSTALL] Stopping running instances..." -ForegroundColor Yellow
$runningInstances | Stop-Process -Force
}
} catch {
Write-Host "[WARNING] Could not stop running instances: $_" -ForegroundColor Yellow
}
# Remove scheduled tasks
try {
$tasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "Antivirus*" }
foreach ($task in $tasks) {
Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false -ErrorAction SilentlyContinue
Write-Host "[UNINSTALL] Removed scheduled task: $($task.TaskName)" -ForegroundColor Green
}
} catch {
Write-Host "[WARNING] Could not remove all scheduled tasks: $_" -ForegroundColor Yellow
}
# Remove registry entries
try {
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Remove-ItemProperty -Path $regPath -Name "MalwareDetector" -ErrorAction SilentlyContinue
Write-Host "[UNINSTALL] Removed registry startup entry" -ForegroundColor Green
} catch {
Write-Host "[WARNING] Could not remove registry entry: $_" -ForegroundColor Yellow
}
# Remove installation directory
try {
if (Test-Path $Script:InstallDir) {
Write-Host "[UNINSTALL] Removing installation directory: $Script:InstallDir" -ForegroundColor Yellow
Remove-Item -Path $Script:InstallDir -Recurse -Force -ErrorAction Stop
Write-Host "[UNINSTALL] Installation directory removed" -ForegroundColor Green
}
} catch {
Write-Host "[WARNING] Could not remove installation directory completely: $_" -ForegroundColor Yellow
}
Write-Host "[UNINSTALL] Uninstallation complete!" -ForegroundColor Green
exit 0
}
function Initialize-Installation {
try {
$currentScriptPath = $PSCommandPath
# Check if we're already running from the installation directory
if ($currentScriptPath -ne $Script:ScriptInstallPath) {
Write-Host "[INSTALL] First run detected - installing to: $Script:InstallDir" -ForegroundColor Cyan
# Create installation directories
@($Script:InstallDir, $Script:DataDir, $Script:LogsDir, $Script:QuarantineDir, $Script:ReportsDir) | ForEach-Object {
if (-not (Test-Path $_)) {
New-Item -Path $_ -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Host "[INSTALL] Created directory: $_" -ForegroundColor Green
}
}
# Copy script to installation directory
Copy-Item -Path $currentScriptPath -Destination $Script:ScriptInstallPath -Force -ErrorAction Stop
Write-Host "[INSTALL] Script copied to: $Script:ScriptInstallPath" -ForegroundColor Green
# Add to startup
Add-ToStartup
# Re-launch from installation directory
Write-Host "[INSTALL] Launching from installation directory..." -ForegroundColor Cyan
Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -NoProfile -File `"$Script:ScriptInstallPath`"" -WindowStyle Normal
Write-Host "[INSTALL] Installation complete. Original script can now be deleted." -ForegroundColor Green
Write-Host "[INSTALL] The antivirus is now running from: $Script:ScriptInstallPath" -ForegroundColor Green
Write-Host "[INSTALL] To uninstall, run: powershell -ExecutionPolicy Bypass -File `"$Script:ScriptInstallPath`" -Uninstall" -ForegroundColor Yellow
exit 0
}
Write-Host "[INFO] Running from installation directory" -ForegroundColor Green
return $true
} catch {
Write-Host "[ERROR] Installation failed: $_" -ForegroundColor Red
Write-Host "[ERROR] Continuing from current location..." -ForegroundColor Yellow
return $false
}
}
# ============================================
# LOGGING FUNCTION (MUST BE VERY EARLY)
# ============================================
function Write-Log {
param ([string]$message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] $message"
try {
# Use Script:LogsDir if available, otherwise use temp
$targetLogFile = if ($Script:LogsDir -and (Test-Path $Script:LogsDir)) {
Join-Path $Script:LogsDir "antivirus_log.txt"
} elseif ($logFile) {
$logFile
} else {
Join-Path $env:TEMP "antivirus_log.txt"
}
$logEntry | Out-File -FilePath $targetLogFile -Append -Encoding UTF8 -ErrorAction SilentlyContinue
} catch {
Write-Host "[WARNING] Failed to write log: $message" -ForegroundColor Yellow
}
}
function Get-ManagedJobsRunningCount {
try {
if (-not $script:ManagedJobs) {
return 0
}
return ($script:ManagedJobs.Values | Where-Object { $_.Enabled -and ($null -eq $_.DisabledUtc) }).Count
} catch {
return 0
}
}
function Test-ScriptConfiguration {
$errors = @()
Write-Log "[CONFIG] Validating script configuration..."
@($Script:InstallDir, $Script:DataDir, $Script:LogsDir, $quarantineFolder, $Script:ReportsDir) | ForEach-Object {
if (-not (Test-Path $_)) {
try {
New-Item $_ -ItemType Directory -Force | Out-Null
Write-Log "[CONFIG] Created directory: $_"
}
catch {
$errors += "Cannot create directory: $_"
Write-Log "[CONFIG ERROR] Cannot create directory: $_"
}
}
}
try {
$testFile = Join-Path $quarantineFolder "test_permissions.txt"
"test" | Out-File $testFile -ErrorAction Stop
Remove-Item $testFile -Force
Write-Log "[CONFIG] Write permissions verified for quarantine folder"
} catch {
$errors += "No write permission to quarantine folder"
Write-Log "[CONFIG ERROR] No write permission to quarantine folder"
}
try {
$testConnection = Test-NetConnection -ComputerName "www.google.com" -Port 443 -InformationLevel Quiet -WarningAction SilentlyContinue -ErrorAction Stop
if ($testConnection) {
Write-Log "[CONFIG] Network connectivity verified"
} else {
$errors += "No internet connectivity - hash lookups may fail"
Write-Log "[CONFIG WARNING] No internet connectivity detected"
}
} catch {
Write-Log "[CONFIG WARNING] Could not verify network connectivity"
}
try {
$drive = (Get-Item $quarantineFolder).PSDrive
$freeSpaceGB = [math]::Round($drive.Free / 1GB, 2)
if ($freeSpaceGB -lt 1) {
$errors += "Low disk space: Only ${freeSpaceGB}GB available"
Write-Log "[CONFIG WARNING] Low disk space: ${freeSpaceGB}GB"
} else {
Write-Log "[CONFIG] Disk space available: ${freeSpaceGB}GB"
}
} catch {
Write-Log "[CONFIG WARNING] Could not check disk space"
}
if ($errors.Count -eq 0) {
Write-Log "[CONFIG] All configuration checks passed"
} else {
Write-Log "[CONFIG] Configuration validation found $($errors.Count) issue(s)"
}
return $errors
}
function Invoke-MemoryCleanup {
param([bool]$Force = $false)
try {
$currentMemoryMB = [math]::Round((Get-Process -Id $PID).WorkingSet64 / 1MB, 2)
$memoryThresholdMB = 500 # Trigger cleanup above 500MB
if ($Force -or $currentMemoryMB -gt $memoryThresholdMB) {
Write-Log "[MEMORY] Current usage: ${currentMemoryMB}MB - Starting cleanup..."
# Clear old cache entries more aggressively
if ($Script:FileHashCache.Count -gt 1000) {
$keysToRemove = $Script:FileHashCache.Keys | Select-Object -First 500
foreach ($key in $keysToRemove) {
$dummy = $null
[void]$Script:FileHashCache.TryRemove($key, [ref]$dummy)
}
Write-Log "[MEMORY] Cleared 500 cache entries"
}
# Clear old scanned files from memory
if ($scannedFiles.Count -gt 1000) {
$keysToRemove = @($scannedFiles.Keys | Select-Object -First 500)
foreach ($key in $keysToRemove) {
$scannedFiles.Remove($key)
}
Write-Log "[MEMORY] Cleared 500 scanned file records"
}
# Force garbage collection
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
$newMemoryMB = [math]::Round((Get-Process -Id $PID).WorkingSet64 / 1MB, 2)
$saved = $currentMemoryMB - $newMemoryMB
Write-Log "[MEMORY] Cleanup complete: ${newMemoryMB}MB (saved ${saved}MB)"
}
} catch {
Write-ErrorLog -Message "Memory cleanup failed" -Severity "Low" -ErrorRecord $_
}
}
function New-SecurityReport {
param([string]$ReportType = "Daily")
try {
Write-Log "[REPORT] Generating $ReportType security report..."
$report = @{
Generated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ReportType = $ReportType
Statistics = @{
FilesScanned = $scannedFiles.Count
FilesQuarantined = (Get-ChildItem "$quarantineFolder\*.quarantined" -ErrorAction SilentlyContinue).Count
ProcessesKilled = (Get-Content $logFile -ErrorAction SilentlyContinue | Select-String "\[KILL\]" | Measure-Object).Count
CacheHitRate = if (($Script:CacheHits + $Script:CacheMisses) -gt 0) {
[math]::Round(($Script:CacheHits / ($Script:CacheHits + $Script:CacheMisses)) * 100, 2)
} else { 0 }
TotalCacheHits = $Script:CacheHits
TotalCacheMisses = $Script:CacheMisses
}
RecentDetections = Get-Content $logFile -Tail 50 -ErrorAction SilentlyContinue
SystemStatus = @{
ManagedJobsRunning = (Get-ManagedJobsRunningCount)
MemoryUsageMB = [math]::Round((Get-Process -Id $PID).WorkingSet64 / 1MB, 2)
UptimeHours = [math]::Round(((Get-Date) - (Get-Process -Id $PID).StartTime).TotalHours, 2)
ActiveMutex = if ($Script:SecurityMutex) { $true } else { $false }
}
TopQuarantinedFiles = Get-ChildItem "$quarantineFolder\*.quarantined" -ErrorAction SilentlyContinue |
Select-Object Name, Length, CreationTime -First 10
}
$reportPath = Join-Path $Script:ReportsDir "$ReportType-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"
$report | ConvertTo-Json -Depth 5 | Set-Content $reportPath
Write-Log "[REPORT] Report generated: $reportPath"
Write-SecurityEvent -EventType "ReportGenerated" -Details @{ ReportPath = $reportPath; Type = $ReportType } -Severity "Informational"
return $reportPath
} catch {
Write-ErrorLog -Message "Failed to generate security report" -Severity "Medium" -ErrorRecord $_
return $null
}
}
function Initialize-WhitelistDatabase {
try {
$whitelistPath = Join-Path $Script:DataDir "whitelist.json"
if (Test-Path $whitelistPath) {
$jsonContent = Get-Content $whitelistPath -Raw | ConvertFrom-Json
$Script:Whitelist = @{
Processes = @{}
Files = @{}
Certificates = @{}
LastUpdated = $jsonContent.LastUpdated
}
if ($jsonContent.Processes) {
foreach ($prop in $jsonContent.Processes.PSObject.Properties) {
$Script:Whitelist.Processes[$prop.Name] = $prop.Value
}
}
if ($jsonContent.Files) {
foreach ($prop in $jsonContent.Files.PSObject.Properties) {
$Script:Whitelist.Files[$prop.Name] = $prop.Value
}
}
if ($jsonContent.Certificates) {
foreach ($prop in $jsonContent.Certificates.PSObject.Properties) {
$Script:Whitelist.Certificates[$prop.Name] = $prop.Value
}
}
Write-Log "[WHITELIST] Loaded whitelist database with $($Script:Whitelist.Files.Count) files, $($Script:Whitelist.Processes.Count) processes"
} else {
$Script:Whitelist = @{
Processes = @{}
Files = @{}
Certificates = @{}
LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content $whitelistPath
Write-Log "[WHITELIST] Created new whitelist database"
}
} catch {
Write-ErrorLog -Message "Failed to initialize whitelist database" -Severity "Medium" -ErrorRecord $_
$Script:Whitelist = @{
Processes = @{}
Files = @{}
Certificates = @{}
LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}
}
function Add-ToWhitelist {
param(
[string]$FilePath = $null,
[string]$ProcessName = $null,
[string]$Reason,
[string]$Category = "Manual"
)
try {
if ($FilePath) {
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction Stop).Hash
$cert = Get-AuthenticodeSignature -FilePath $FilePath -ErrorAction SilentlyContinue
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$certificateSubject = if ($cert.SignerCertificate) { $cert.SignerCertificate.Subject } else { $null }
$Script:Whitelist.Files[$hash] = @{
Path = $FilePath
Reason = $Reason
Category = $Category
AddedBy = $env:USERNAME
Timestamp = $timestamp
Certificate = $certificateSubject
}
Write-Log "[WHITELIST] Added file to whitelist: $FilePath (Hash: $hash)"
}
if ($ProcessName) {
$Script:Whitelist.Processes[$ProcessName.ToLower()] = @{
Reason = $Reason
Category = $Category
AddedBy = $env:USERNAME
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
Write-Log "[WHITELIST] Added process to whitelist: $ProcessName"
}
$Script:Whitelist.LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$whitelistPath = Join-Path $Script:DataDir "whitelist.json"
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content $whitelistPath
Write-SecurityEvent -EventType "WhitelistUpdated" -Details @{
FilePath = $FilePath
ProcessName = $ProcessName
Reason = $Reason
} -Severity "Informational"
} catch {
Write-ErrorLog -Message "Failed to add to whitelist" -Severity "Medium" -ErrorRecord $_
}
}
function Remove-FromWhitelist {
param(
[string]$Identifier
)
try {
$removed = $false
if ($Script:Whitelist.Files.ContainsKey($Identifier)) {
$Script:Whitelist.Files.Remove($Identifier)
$removed = $true
} else {
$matchingHash = $Script:Whitelist.Files.GetEnumerator() | Where-Object { $_.Value.Path -eq $Identifier } | Select-Object -First 1
if ($matchingHash) {
$Script:Whitelist.Files.Remove($matchingHash.Key)
$removed = $true
}
}
if ($Script:Whitelist.Processes.ContainsKey($Identifier.ToLower())) {
$Script:Whitelist.Processes.Remove($Identifier.ToLower())
$removed = $true
}
if ($removed) {
$Script:Whitelist.LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$whitelistPath = Join-Path $Script:DataDir "whitelist.json"
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content $whitelistPath
Write-Log "[WHITELIST] Removed from whitelist: $Identifier"
return $true
} else {
Write-Log "[WHITELIST] Item not found in whitelist: $Identifier"
return $false
}
} catch {
Write-ErrorLog -Message "Failed to remove from whitelist" -Severity "Medium" -ErrorRecord $_
return $false
}
}
function Test-IsWhitelisted {
param(
[string]$FilePath = $null,
[string]$ProcessName = $null
)
try {
if ($FilePath) {
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction Stop).Hash
if ($Script:Whitelist.Files.ContainsKey($hash)) {
return $true
}
}
if ($ProcessName) {
if ($Script:Whitelist.Processes.ContainsKey($ProcessName.ToLower())) {
return $true
}
}
return $false
} catch {
return $false
}
}
function Add-ToStartup {
try {
$exePath = "powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$Script:ScriptInstallPath`""
$appName = "MalwareDetector"
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
$existing = Get-ItemProperty -Path $regPath -Name $appName -ErrorAction SilentlyContinue
if (!$existing -or $existing.$appName -ne $exePath) {
Set-ItemProperty -Path $regPath -Name $appName -Value $exePath -Force
Write-Log "[STARTUP] Added to registry startup: $exePath"
}
$taskExists = Get-ScheduledTask -TaskName "${taskName}_Watchdog" -ErrorAction SilentlyContinue
if (-not $taskExists) {
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$Script:ScriptInstallPath`""
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
Register-ScheduledTask -TaskName "${taskName}_Watchdog" -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Watchdog for antivirus protection" -ErrorAction Stop | Out-Null
Write-Log "[STARTUP] Registered scheduled task: ${taskName}_Watchdog"
}
} catch {
Write-Log "[ERROR] Failed to add to startup: $_"
throw
}
}
# Initialize installation before anything else
Initialize-Installation
# ============================================
# CRITICAL SECURITY FIX #1: SELF-PROTECTION WITH MUTEX
# ============================================
$Script:MutexName = "Global\AntivirusProtectionMutex_{F9A2E1C4-3B7D-4A8E-9C5F-1D6E2B7A8C3D}"
$Script:SecurityMutex = $null
# Termination protection variables
$Script:TerminationAttempts = 0
$Script:MaxTerminationAttempts = 5
$Script:AutoRestart = $false
$Script:SelfPID = $PID
$taskName = "Antivirus"
# Task description used in scheduled task registration
$Script:TaskDescription = "Runs the Production Hardened Antivirus script"
$scriptPath = $Script:ScriptInstallPath
$quarantineFolder = $Script:QuarantineDir
$logFile = Join-Path $Script:LogsDir "antivirus_log.txt"
$localDatabase = Join-Path $Script:DataDir "scanned_files.txt"
$hashIntegrityFile = Join-Path $Script:DataDir "db_integrity.hmac"
$scannedFiles = @{}
$Script:FileHashCache = [System.Collections.Concurrent.ConcurrentDictionary[string, object]]::new()
$Script:MaxCacheSize = 2000 # Reduced from 5000 to save RAM
$Script:CacheHits = 0
$Script:CacheMisses = 0
$Script:ApiRateLimiter = @{
LastCall = [System.Collections.Generic.Dictionary[string, [DateTime]]]::new()
MinimumDelay = [TimeSpan]::FromMilliseconds(500) # Changed from 2 seconds to 500ms
}
$Base = $quarantineFolder
$QuarantineDir = $quarantineFolder
# Check admin privileges
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (-not $isAdmin) {
Write-Host "[CRITICAL] Must run as Administrator" -ForegroundColor Red
exit 1
}
# ============================================
# LOGGING FUNCTIONS (MUST BE FIRST)
# ============================================
$Script:ErrorSeverity = @{
"Critical" = 1
"High" = 2
"Medium" = 3
"Low" = 4
}
$Script:FailSafeMode = $false
$Script:JobsInitialized = $false
function Write-ErrorLog {
param(
[string]$Message,
[string]$Severity = "Medium",
[System.Management.Automation.ErrorRecord]$ErrorRecord = $null
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [ERROR-$Severity] $Message"
if ($ErrorRecord) {
$logEntry += " | Exception: $($ErrorRecord.Exception.Message) | StackTrace: $($ErrorRecord.ScriptStackTrace)"
}
try {
$errorLogPath = Join-Path $Script:LogsDir "error_log.txt"
$logEntry | Out-File -FilePath $errorLogPath -Append -Encoding UTF8 -ErrorAction Stop
} catch {
Write-Host "[FATAL] Cannot write to error log: $_" -ForegroundColor Red
}
if ($Severity -eq "Critical" -and $Script:JobsInitialized) {
$Script:FailSafeMode = $true
Write-Host "[FAIL-SAFE] Entering fail-safe mode due to critical runtime error" -ForegroundColor Red
}
}
function Write-SecurityEvent {
param(
[string]$EventType,
[hashtable]$Details,
[string]$Severity = "Informational"
)
try {
$securityEvent = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"
EventType = $EventType
Severity = $Severity
User = $env:USERNAME
Machine = $env:COMPUTERNAME
PID = $PID
Details = $Details
}
$eventJson = $securityEvent | ConvertTo-Json -Compress
$securityLogPath = Join-Path $Script:LogsDir "security_events.jsonl"
$eventJson | Out-File $securityLogPath -Append -Encoding UTF8
try {
$sourceName = "AntivirusScript"
if (-not [System.Diagnostics.EventLog]::SourceExists($sourceName)) {
New-EventLog -LogName Application -Source $sourceName -ErrorAction SilentlyContinue
}
$eventId = switch ($Severity) {
"Critical" { 1001 }
"High" { 1002 }
"Medium" { 1003 }
default { 1000 }
}
Write-EventLog -LogName Application -Source $sourceName -EventId $eventId `
-EntryType Information -Message "${EventType}: $(ConvertTo-Json $Details -Compress)"
} catch {
# Silently fail if we can't write to Windows Event Log
}
} catch {
Write-ErrorLog -Message "Failed to write security event" -Severity "Medium" -ErrorRecord $_
}
}
# ============================================
# MANAGED JOB FRAMEWORK (MUST BE BEFORE STARTUP)
# ============================================
$Script:LoopCounter = 0
$script:ManagedJobs = @{}
function Register-ManagedJob {
param(
[Parameter(Mandatory=$true)][string]$Name,
[Parameter(Mandatory=$true)][scriptblock]$ScriptBlock,
[int]$IntervalSeconds = 30,
[bool]$Enabled = $true,
[bool]$Critical = $false,
[int]$MaxRestartAttempts = 3,
[int]$RestartDelaySeconds = 5
)
if (-not $script:ManagedJobs) {
$script:ManagedJobs = @{}
}
$minIntervalSeconds = 1
if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MinimumIntervalSeconds) {
$minIntervalSeconds = [int]$Script:ManagedJobConfig.MinimumIntervalSeconds
}
$IntervalSeconds = [Math]::Max([int]$IntervalSeconds, [int]$minIntervalSeconds)
$script:ManagedJobs[$Name] = [ordered]@{
Name = $Name
ScriptBlock = $ScriptBlock
IntervalSeconds = $IntervalSeconds
Enabled = $Enabled
Critical = $Critical
MaxRestartAttempts = $MaxRestartAttempts
RestartDelaySeconds = $RestartDelaySeconds
RestartAttempts = 0
LastStartUtc = $null
LastSuccessUtc = $null
LastError = $null
NextRunUtc = [DateTime]::UtcNow
DisabledUtc = $null
}
}
function Invoke-ManagedJobsTick {
param(
[Parameter(Mandatory=$true)][DateTime]$NowUtc
)
if (-not $script:ManagedJobs) {
return
}
foreach ($job in $script:ManagedJobs.Values) {
if (-not $job.Enabled) {
continue
}
if ($null -ne $job.DisabledUtc) {
continue
}
if ($job.NextRunUtc -gt $NowUtc) {
continue
}
$job.LastStartUtc = $NowUtc
try {
& $job.ScriptBlock
$job.LastSuccessUtc = [DateTime]::UtcNow
$job.RestartAttempts = 0
$job.LastError = $null
$job.NextRunUtc = $job.LastSuccessUtc.AddSeconds([Math]::Max(1, $job.IntervalSeconds))
} catch {
$job.LastError = $_
$job.RestartAttempts++
try {
Write-ErrorLog -Message "Managed job '$($job.Name)' failed (attempt $($job.RestartAttempts)/$($job.MaxRestartAttempts))" -Severity "High" -ErrorRecord $_
} catch {}
if ($job.RestartAttempts -ge $job.MaxRestartAttempts) {
$job.DisabledUtc = [DateTime]::UtcNow
try {
Write-ErrorLog -Message "Managed job '$($job.Name)' exceeded max restart attempts and has been disabled" -Severity "Critical" -ErrorRecord $_
} catch {}
continue
}
$job.NextRunUtc = [DateTime]::UtcNow.AddSeconds([Math]::Max(1, $job.RestartDelaySeconds))
}
}
}
try {
$Script:SecurityMutex = [System.Threading.Mutex]::new($false, $Script:MutexName)
if (-not $Script:SecurityMutex.WaitOne(0, $false)) {
Write-Host "[PROTECTION] Another instance is already running. Exiting." -ForegroundColor Yellow
exit 1
}
Write-Host "[PROTECTION] Global mutex acquired successfully." -ForegroundColor Green
} catch {
Write-Host "[WARNING] Global mutex failed (requires admin): $($_.Exception.Message)" -ForegroundColor Yellow
Write-Host "[PROTECTION] Falling back to local mutex (user-level protection)..." -ForegroundColor Cyan
try {
$Script:MutexName = "Local\AntivirusProtectionMutex_{F9A2E1C4-3B7D-4A8E-9C5F-1D6E2B7A8C3D}_$env:USERNAME"
$Script:SecurityMutex = [System.Threading.Mutex]::new($false, $Script:MutexName)
if (-not $Script:SecurityMutex.WaitOne(0, $false)) {
Write-Host "[PROTECTION] Another instance is already running for this user. Exiting." -ForegroundColor Yellow
exit 1
}
Write-Host "[PROTECTION] Local mutex acquired successfully." -ForegroundColor Green
} catch {
Write-Host "[ERROR] Failed to acquire any mutex: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[WARNING] Continuing without mutex protection (multiple instances may run)..." -ForegroundColor Yellow
$Script:SecurityMutex = $null
}
}
function Get-SecureHMACKey {
try {
# Load System.Security assembly for DPAPI support
try {
Add-Type -AssemblyName System.Security -ErrorAction Stop
} catch {
Write-Host "[WARNING] System.Security assembly not available, using fallback encryption" -ForegroundColor Yellow
}
$keyPath = "$env:APPDATA\AntivirusProtection\hmac.key"
if (Test-Path $keyPath) {
# Load existing protected key
try {
$protectedKeyBytes = Get-Content $keyPath -Encoding Byte -ErrorAction Stop
$keyBytes = [System.Security.Cryptography.ProtectedData]::Unprotect(
$protectedKeyBytes,
$null,
[System.Security.Cryptography.DataProtectionScope]::CurrentUser
)
Write-Host "[SECURITY] Loaded protected HMAC key from user profile" -ForegroundColor Green
return $keyBytes
} catch {
Write-Host "[WARNING] Failed to load existing HMAC key: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# Generate and store new key
Write-Host "[SECURITY] Generating new HMAC key with DPAPI protection" -ForegroundColor Yellow
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$key = New-Object byte[] 32
$rng.GetBytes($key)
try {
$protectedKey = [System.Security.Cryptography.ProtectedData]::Protect(
$key,
$null,
[System.Security.Cryptography.DataProtectionScope]::CurrentUser
)
$keyDir = Split-Path $keyPath
New-Item -Path $keyDir -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
$protectedKey | Set-Content $keyPath -Encoding Byte
Write-Host "[SECURITY] HMAC key generated and protected with DPAPI" -ForegroundColor Green
} catch {
Write-Host "[WARNING] Could not protect HMAC key with DPAPI: $($_.Exception.Message)" -ForegroundColor Yellow
}
return $key
} catch {
Write-Host "[ERROR] Failed to load/generate secure HMAC key: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[WARNING] Using fallback HMAC key - NOT SECURE FOR PRODUCTION" -ForegroundColor Yellow
# Generate a random fallback key instead of hardcoded
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$fallbackKey = New-Object byte[] 32
$rng.GetBytes($fallbackKey)
return $fallbackKey
}
}
# Initialize HMAC key
$Script:HMACKey = Get-SecureHMACKey
Write-Log "[+] Antivirus starting up..."
# ============================================
# SECURITY ENHANCEMENT: CENTRALIZED ERROR HANDLING
# ============================================
# Moved trap handler after all functions are defined and add ISE detection for Ctrl+C protection
# The trap handler is now at the top, after logging functions are defined.
# ============================================
# SECURITY ENHANCEMENT: HMAC Key with DPAPI Protection
# ============================================
# The Get-SecureHMACKey function is now defined early, and the key is initialized after logging functions.
# ============================================
# SECURITY ENHANCEMENT: Event Logging
# ============================================
# This function is now defined at the top.
# ============================================
# OPERATIONAL ENHANCEMENT: Configuration Validation
# ============================================
function Test-ScriptConfiguration {
$errors = @()
Write-Log "[CONFIG] Validating script configuration..."
# Check required folders
@($quarantineFolder, "$quarantineFolder\reports") | ForEach-Object {
if (-not (Test-Path $_)) {
try {
New-Item $_ -ItemType Directory -Force | Out-Null
Write-Log "[CONFIG] Created directory: $_"
}
catch {
$errors += "Cannot create directory: $_"
Write-Log "[CONFIG ERROR] Cannot create directory: $_"
}
}
}
# Check file permissions
try {
$testFile = "$quarantineFolder\test_permissions.txt"
"test" | Out-File $testFile -ErrorAction Stop
Remove-Item $testFile -Force
Write-Log "[CONFIG] Write permissions verified for quarantine folder"
} catch {
$errors += "No write permission to quarantine folder"
Write-Log "[CONFIG ERROR] No write permission to quarantine folder"
}
# Check network connectivity for APIs
try {
$testConnection = Test-NetConnection -ComputerName "www.google.com" -Port 443 -InformationLevel Quiet -WarningAction SilentlyContinue -ErrorAction Stop
if ($testConnection) {
Write-Log "[CONFIG] Network connectivity verified"
} else {
$errors += "No internet connectivity - hash lookups may fail"
Write-Log "[CONFIG WARNING] No internet connectivity detected"
}
} catch {
Write-Log "[CONFIG WARNING] Could not verify network connectivity"
}
# Check available disk space
try {
$drive = (Get-Item $quarantineFolder).PSDrive
$freeSpaceGB = [math]::Round($drive.Free / 1GB, 2)
if ($freeSpaceGB -lt 1) {
$errors += "Low disk space: Only ${freeSpaceGB}GB available"
Write-Log "[CONFIG WARNING] Low disk space: ${freeSpaceGB}GB"
} else {
Write-Log "[CONFIG] Disk space available: ${freeSpaceGB}GB"
}
} catch {
Write-Log "[CONFIG WARNING] Could not check disk space"
}
if ($errors.Count -eq 0) {
Write-Log "[CONFIG] All configuration checks passed"
} else {
Write-Log "[CONFIG] Configuration validation found $($errors.Count) issue(s)"
}
return $errors
}
# ============================================
# PERFORMANCE ENHANCEMENT: Rate-Limited API Calls
# ============================================
function Invoke-RateLimitedRestMethod {
param(
[string]$Uri,
[string]$Method = "GET",
[object]$Body = $null,
[string]$ContentType = "application/json",
[int]$TimeoutSec = 3 # Reduced from 5 to 3 seconds
)
try {
$hostName = ([System.Uri]$Uri).Host
# Check if we need to wait
if ($Script:ApiRateLimiter.LastCall.ContainsKey($hostName)) {
$timeSinceLastCall = [DateTime]::Now - $Script:ApiRateLimiter.LastCall[$hostName]
if ($timeSinceLastCall -lt $Script:ApiRateLimiter.MinimumDelay) {
$sleepMs = ($Script:ApiRateLimiter.MinimumDelay - $timeSinceLastCall).TotalMilliseconds
Start-Sleep -Milliseconds $sleepMs
}
}
# Make the API call
$params = @{
Uri = $Uri
Method = $Method
TimeoutSec = $TimeoutSec
ErrorAction = 'Stop'
UseBasicParsing = $true # Faster parsing
}
if ($Body) { $params['Body'] = $Body }
if ($ContentType) { $params['ContentType'] = $ContentType }
$response = Invoke-RestMethod @params
# Update rate limiter
$Script:ApiRateLimiter.LastCall[$hostName] = [DateTime]::Now
return $response
} catch {
Write-ErrorLog -Message "Rate-limited API call failed: $Uri" -Severity "Low" -ErrorRecord $_
return $null
}
}
function Add-ToWhitelist {
param(
[string]$FilePath = $null,
[string]$ProcessName = $null,
[string]$Reason,
[string]$Category = "Manual"
)
try {
if ($FilePath) {
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction Stop).Hash
$cert = Get-AuthenticodeSignature -FilePath $FilePath -ErrorAction SilentlyContinue
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$certificateSubject = if ($cert.SignerCertificate) { $cert.SignerCertificate.Subject } else { $null }
$Script:Whitelist.Files[$hash] = @{
Path = $FilePath
Reason = $Reason
Category = $Category
AddedBy = $env:USERNAME
Timestamp = $timestamp
Certificate = $certificateSubject
}
Write-Log "[WHITELIST] Added file to whitelist: $FilePath (Hash: $hash)"
}
if ($ProcessName) {
$Script:Whitelist.Processes[$ProcessName.ToLower()] = @{
Reason = $Reason
Category = $Category
AddedBy = $env:USERNAME
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
Write-Log "[WHITELIST] Added process to whitelist: $ProcessName"
}
# Save to disk
$Script:Whitelist.LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content "$quarantineFolder\whitelist.json"
Write-SecurityEvent -EventType "WhitelistUpdated" -Details @{
FilePath = $FilePath
ProcessName = $ProcessName
Reason = $Reason
} -Severity "Informational"
} catch {
Write-ErrorLog -Message "Failed to add to whitelist" -Severity "Medium" -ErrorRecord $_
}
}
function Remove-FromWhitelist {
param(
[string]$Identifier
)
try {
$removed = $false
# Try to remove from files (by hash or path)
if ($Script:Whitelist.Files.ContainsKey($Identifier)) {
$Script:Whitelist.Files.Remove($Identifier)
$removed = $true
} else {
# Search by path
$matchingHash = $Script:Whitelist.Files.GetEnumerator() | Where-Object { $_.Value.Path -eq $Identifier } | Select-Object -First 1
if ($matchingHash) {
$Script:Whitelist.Files.Remove($matchingHash.Key)
$removed = $true
}
}
# Try to remove from processes
if ($Script:Whitelist.Processes.ContainsKey($Identifier.ToLower())) {
$Script:Whitelist.Processes.Remove($Identifier.ToLower())
$removed = $true
}
if ($removed) {
$Script:Whitelist.LastUpdated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content "$quarantineFolder\whitelist.json"
Write-Log "[WHITELIST] Removed from whitelist: $Identifier"
return $true
} else {
Write-Log "[WHITELIST] Item not found in whitelist: $Identifier"
return $false
}
} catch {
Write-ErrorLog -Message "Failed to remove from whitelist" -Severity "Medium" -ErrorRecord $_
return $false
}
}
function Test-IsWhitelisted {
param(
[string]$FilePath = $null,
[string]$ProcessName = $null
)
try {
if ($FilePath) {
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction Stop).Hash
if ($Script:Whitelist.Files.ContainsKey($hash)) {
return $true
}
}
if ($ProcessName) {
if ($Script:Whitelist.Processes.ContainsKey($ProcessName.ToLower())) {
return $true
}
}
return $false
} catch {
return $false
}
}
# ============================================
# SECURITY ENHANCEMENT: Memory Protection
# ============================================
function Protect-SensitiveData {
try {
# Clear sensitive strings from memory
if ($Script:HMACKey) {
[Array]::Clear($Script:HMACKey, 0, $Script:HMACKey.Length)
$Script:HMACKey = $null
}
# Force garbage collection
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[System.GC]::Collect()
Write-Log "[SECURITY] Sensitive data cleared from memory"
} catch {
Write-ErrorLog -Message "Failed to protect sensitive data" -Severity "Low" -ErrorRecord $_
}
}
# Register cleanup on exit
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
Protect-SensitiveData
if ($Script:SecurityMutex) {
try {
$Script:SecurityMutex.ReleaseMutex()
$Script:SecurityMutex.Dispose()
} catch {}
}
} | Out-Null
# ============================================
# PERFORMANCE ENHANCEMENT: Parallel Scanning
# ============================================
function Invoke-ParallelScan {
param(
[string[]]$Paths,
[int]$MaxThreads = 8 # Increased from 4 to 8 for faster scanning
)
try {
Write-Log "[SCAN] Starting parallel scan of $($Paths.Count) paths with $MaxThreads threads"
# Process in batches to avoid memory bloat
$batchSize = 100
$allResults = @()
for ($i = 0; $i -lt $Paths.Count; $i += $batchSize) {
$batch = $Paths[$i..[math]::Min($i + $batchSize - 1, $Paths.Count - 1)]
$batchResults = $batch | ForEach-Object -ThrottleLimit $MaxThreads -Parallel {
$path = $_
try {
if (Test-Path $path -ErrorAction SilentlyContinue) {
$hash = (Get-FileHash -Path $path -Algorithm SHA256 -ErrorAction Stop).Hash
[PSCustomObject]@{
Path = $path
Hash = $hash
Success = $true
Error = $null
}
} else {
[PSCustomObject]@{
Path = $path
Hash = $null
Success = $false
Error = "File not found"
}
}
} catch {
[PSCustomObject]@{
Path = $path
Hash = $null
Success = $false
Error = $_.Exception.Message
}
}
}
$allResults += $batchResults
# Cleanup between batches
if (($i + $batchSize) -lt $Paths.Count) {
[System.GC]::Collect()
}
}
$successCount = ($allResults | Where-Object Success).Count
Write-Log "[SCAN] Parallel scan complete: $successCount/$($Paths.Count) files processed successfully"
return $allResults
} catch {
Write-ErrorLog -Message "Parallel scan failed" -Severity "Medium" -ErrorRecord $_
return @()
}
}
# ============================================
# OPERATIONAL ENHANCEMENT: Interactive Exclusion Manager
# ============================================
function Show-ExclusionManager {
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " EXCLUSION MANAGER" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
while ($true) {
Write-Host "[1] Add Process Exclusion" -ForegroundColor Green
Write-Host "[2] Add File/Path Exclusion" -ForegroundColor Green
Write-Host "[3] View Current Exclusions" -ForegroundColor Yellow
Write-Host "[4] Remove Exclusion" -ForegroundColor Red
Write-Host "[5] Export Whitelist" -ForegroundColor Cyan
Write-Host "[6] Return to Monitoring" -ForegroundColor Gray
Write-Host ""
$choice = Read-Host "Select an option (1-6)"
switch ($choice) {
"1" {
$procName = Read-Host "Enter process name (e.g., myapp.exe)"
$reason = Read-Host "Enter reason for exclusion"
if ($procName) {
Add-ToWhitelist -ProcessName $procName -Reason $reason -Category "UserDefined"
Write-Host "[+] Process added to whitelist: $procName" -ForegroundColor Green
}
}
"2" {
$filePath = Read-Host "Enter full file path or directory pattern"
$reason = Read-Host "Enter reason for exclusion"
if ($filePath -and (Test-Path $filePath)) {
Add-ToWhitelist -FilePath $filePath -Reason $reason -Category "UserDefined"
Write-Host "[+] File/path added to whitelist: $filePath" -ForegroundColor Green
} else {
Write-Host "[-] Invalid path or file not found" -ForegroundColor Red
}
}
"3" {
Write-Host "`nCurrent Whitelisted Processes:" -ForegroundColor Cyan
$Script:Whitelist.Processes.GetEnumerator() | ForEach-Object {
Write-Host " - $($_.Key): $($_.Value.Reason) (Added: $($_.Value.Timestamp))" -ForegroundColor Gray
}
Write-Host "`nCurrent Whitelisted Files:" -ForegroundColor Cyan
$Script:Whitelist.Files.GetEnumerator() | ForEach-Object {
Write-Host " - $($_.Value.Path)" -ForegroundColor Gray
Write-Host " Hash: $($_.Key)" -ForegroundColor DarkGray
Write-Host " Reason: $($_.Value.Reason) (Added: $($_.Value.Timestamp))" -ForegroundColor DarkGray
}
Read-Host "`nPress Enter to continue"
}
"4" {
$identifier = Read-Host "Enter process name, file path, or hash to remove"
if ($identifier) {
if (Remove-FromWhitelist -Identifier $identifier) {
Write-Host "[+] Successfully removed from whitelist" -ForegroundColor Green
} else {
Write-Host "[-] Item not found in whitelist" -ForegroundColor Red
}
}
}
"5" {
$exportPath = "$quarantineFolder\whitelist_export_$(Get-Date -Format 'yyyyMMdd_HHmmss').json"
$Script:Whitelist | ConvertTo-Json -Depth 5 | Set-Content $exportPath
Write-Host "[+] Whitelist exported to: $exportPath" -ForegroundColor Green
Read-Host "Press Enter to continue"
}
"6" {
Write-Host "[*] Returning to monitoring..." -ForegroundColor Yellow
return
}
default {
Write-Host "[-] Invalid option. Please select 1-6." -ForegroundColor Red
}
}
Write-Host ""
}
}
# ============================================
# SECURITY ENHANCEMENT: Self-Defense Against Termination
# ============================================
function Register-TerminationProtection {
try {
# Monitor for unexpected termination attempts
$Script:UnhandledExceptionHandler = Register-ObjectEvent -InputObject ([AppDomain]::CurrentDomain) `
-EventName UnhandledException -Action {
param($src, $evtArgs)
$errorMsg = "Unhandled exception: $($evtArgs.Exception.ToString())"
$errorMsg | Out-File "$using:quarantineFolder\crash_log.txt" -Append
try {
# Log to security events
$securityEvent = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"
EventType = "UnexpectedTermination"
Severity = "Critical"
Exception = $evtArgs.Exception.ToString()
IsTerminating = $evtArgs.IsTerminating
}
$securityEvent | ConvertTo-Json -Compress | Out-File "$using:quarantineFolder\security_events.jsonl" -Append
} catch {}
# Attempt auto-restart if configured
if ($using:Script:AutoRestart -and $evtArgs.IsTerminating) {
try {
Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -File `"$using:Script:SelfPath`"" `
-WindowStyle Hidden -ErrorAction SilentlyContinue
} catch {}
}
}
Write-Log "[PROTECTION] Termination protection registered"
} catch {
Write-ErrorLog -Message "Failed to register termination protection" -Severity "Medium" -ErrorRecord $_
}
}
function Enable-CtrlCProtection {
try {
# Detect if running in ISE or console
if ($host.Name -eq "Windows PowerShell ISE Host") {
Write-Host "[PROTECTION] ISE detected - using trap-based Ctrl+C protection" -ForegroundColor Cyan
Write-Host "[PROTECTION] Ctrl+C protection enabled (requires $Script:MaxTerminationAttempts attempts to stop)" -ForegroundColor Green
return $true
}
[Console]::TreatControlCAsInput = $false
# Create scriptblock for the event handler
$cancelHandler = {
param($src, $evtArgs)
$Script:TerminationAttempts++
Write-Host "`n[PROTECTION] Termination attempt detected ($Script:TerminationAttempts/$Script:MaxTerminationAttempts)" -ForegroundColor Red
try {
Write-SecurityEvent -EventType "TerminationAttemptBlocked" -Details @{
PID = $PID
AttemptNumber = $Script:TerminationAttempts
} -Severity "Critical"
} catch {}
if ($Script:TerminationAttempts -ge $Script:MaxTerminationAttempts) {
Write-Host "[PROTECTION] Maximum termination attempts reached. Allowing graceful shutdown..." -ForegroundColor Yellow
$evtArgs.Cancel = $false
} else {
Write-Host "[PROTECTION] Termination blocked. Press Ctrl+C $($Script:MaxTerminationAttempts - $Script:TerminationAttempts) more times to force stop." -ForegroundColor Yellow
$evtArgs.Cancel = $true
}
}
# Register the event handler
[Console]::add_CancelKeyPress($cancelHandler)
Write-Host "[PROTECTION] Ctrl+C protection enabled (requires $Script:MaxTerminationAttempts attempts to stop)" -ForegroundColor Green
return $true
} catch {
Write-Host "[WARNING] Could not enable Ctrl+C protection: $($_.Exception.Message)" -ForegroundColor Yellow
return $false
}
}
function Enable-AutoRestart {
try {
$taskName = "AntivirusAutoRestart_$PID"
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$Script:SelfPath`""
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
-StartWhenAvailable -RunOnlyIfNetworkAvailable:$false
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger `
-Settings $settings -Force -ErrorAction Stop | Out-Null
Write-Host "[PROTECTION] Auto-restart scheduled task registered" -ForegroundColor Green
} catch {
Write-Host "[WARNING] Could not enable auto-restart: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
function Start-ProcessWatchdog {
try {
$watchdogJob = Start-Job -ScriptBlock {
param($parentPID, $scriptPath, $autoRestart)
while ($true) {
Start-Sleep -Seconds 30
# Check if parent process is still alive
$process = Get-Process -Id $parentPID -ErrorAction SilentlyContinue
if (-not $process) {
# Parent died - restart if configured
if ($autoRestart) {
Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$scriptPath`"" `
-WindowStyle Hidden -ErrorAction SilentlyContinue
}
break
}
}
} -ArgumentList $PID, $Script:SelfPath, $Script:AutoRestart
Write-Host "[PROTECTION] Process watchdog started (Job ID: $($watchdogJob.Id))" -ForegroundColor Green
} catch {
Write-Host "[WARNING] Could not start process watchdog: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
# ============================================
# PERFORMANCE ENHANCEMENT: Advanced Cache Management
# ============================================
function Get-CachedFileHash {
param([string]$FilePath)
try {
$cacheKey = $FilePath.ToLower()
$result = $null
# Check cache first
if ($Script:FileHashCache.TryGetValue($cacheKey, [ref]$result)) {
$Script:CacheHits++
return $result
}
# Cache miss - calculate hash
$Script:CacheMisses++
$hash = (Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction Stop).Hash
if ($hash) {
# Implement more aggressive cache size management
if ($Script:FileHashCache.Count -ge $Script:MaxCacheSize) {
# Remove oldest 25% of entries
$removeCount = [math]::Floor($Script:MaxCacheSize * 0.25)
$keysToRemove = $Script:FileHashCache.Keys | Select-Object -First $removeCount
foreach ($key in $keysToRemove) {
$dummy = $null
[void]$Script:FileHashCache.TryRemove($key, [ref]$dummy)
}
Write-Log "[CACHE] Evicted $removeCount entries (cache at max size)"
}
# Add to cache
[void]$Script:FileHashCache.TryAdd($cacheKey, $hash)
}
return $hash
} catch {
Write-ErrorLog -Message "Failed to get cached file hash: $FilePath" -Severity "Low" -ErrorRecord $_
return $null
}
}
$Script:SelfProcessName = $PID
$Script:SelfPath = $PSCommandPath
$Script:SelfHash = (Get-FileHash -Path $Script:SelfPath -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
$Script:SelfDirectory = Split-Path $Script:SelfPath -Parent
$Script:QuarantineDir = $QuarantineDir
Write-Host "[PROTECTION] Self-protection enabled. PID: $Script:SelfProcessName, Path: $Script:SelfPath"
$Script:ProtectedProcessNames = @(
"system", "csrss", "wininit", "services", "lsass", "svchost",
"smss", "winlogon", "explorer", "dwm", "taskmgr", "spoolsv",
"conhost", "fontdrvhost", "dllhost", "runtimebroker", "sihost",
"taskhostw", "searchindexer", "searchprotocolhost", "searchfilterhost",
"registry", "memory compression", "idle", "wudfhost", "dashost", "mscorsvw"
)
# Microsoft Store and UWP Apps Whitelist
$Script:MicrosoftStoreProcesses = @(
"windowsstore", "wsappx", "wwahost", "applicationframehost", "runtimebroker",
"microsoftedge", "microsoftedgecp", "msedge", "winstore.app",
"microsoft.windowsstore", "winstore.mobile.exe", "microsoft.paint",
"paintstudio.view", "mspaint", "microsoft.screensketch", "clipchamp",
"microsoft.photos", "microsoft.windowscalculator", "microsoft.windowscamera"
)
# Gaming Platform Processes Whitelist
$Script:GamingPlatformProcesses = @(
# Steam and dependencies
"steam", "steamservice", "steamwebhelper", "gameoverlayui", "steamerrorreporter",
"streaming_client", "steamclient", "steamcmd",
# Epic Games
"epicgameslauncher", "epicwebhelper", "epiconlineservices", "easyanticheat",
"easyanticheat_eos", "battleye", "eac_launcher",
# GOG Galaxy
"galaxyclient", "galaxyclientservice", "gogalaxy", "gogalaxy",
# Origin / EA Desktop
"origin", "originwebhelperservice", "originclientservice", "eadesktop",
"eabackgroundservice", "eaapp", "link2ea",
# Ubisoft Connect
"ubisoftgamelauncher", "upc", "ubisoft game launcher", "ubiorbitapi_r2_loader",
"uplayservice", "uplaywebcore",
# Battle.net
"battle.net", "blizzard", "agent", "blizzardbrowser",
# Xbox and Microsoft Gaming
"gamebar", "gamebarftwizard", "gamebarpresencewriter", "xboxapp",
"gamingservices", "gamingservicesnet", "xboxgamingoverlay", "xboxpcapp",
"microsoftgaming", "gamepass", "xbox", "xboxstat",
# Rockstar Games
"rockstargameslauncher", "launcherpatc her", "rockstarservice", "socialclubhelper",
# Riot Games
"riotclientservices", "riotclientux", "riotclientcrashhandler", "valorant",
"leagueclient", "leagueclientux",
# Discord (gaming communication)
"discord", "discordptb", "discordcanary", "discordoverlay",
# NVIDIA GeForce Experience
"nvcontainer", "nvidia web helper", "nvstreamservice", "geforcenow", "gfexperience",
# Anti-cheat systems
"vanguard", "riot vanguard", "faceit", "punkbuster", "nprotect",
"xigncode", "vac", "gameguard", "xtrap", "hackshield"
)
# Gaming Platform Directories Whitelist
$Script:GamingPlatformPaths = @(
"*\steam\*", "*\steamapps\*", "*\epic games\*", "*\epicgames\*",
"*\gog galaxy\*", "*\goggalaxy\*", "*\origin\*", "*\origin games\*",
"*\electronic arts\*", "*\ea games\*", "*\ea desktop\*",
"*\ubisoft\*", "*\ubisoft game launcher\*", "*\uplay\*",
"*\battle.net\*", "*\blizzard\*", "*\blizzard entertainment\*",
"*\riot games\*", "*\valorant\*", "*\league of legends\*",
"*\rockstar games\*", "*\xbox games\*", "*\xbox live\*",
"*\program files\modifiablewindowsapps\*",
"*\program files (x86)\microsoft\windowsapps\*",
"*\program files\microsoft\windowsapps\*",
"*\nvidia corporation\*", "*\geforce experience\*"
)
# Common Hardware Driver Processes Whitelist
$Script:CommonDriverProcesses = @(
# NVIDIA drivers
"nvdisplay.container", "nvcontainer", "nvidia web helper", "nvstreamservice",
"nvstreamsvc", "nvwmi64", "nvspcaps64", "nvtray", "nvcplui", "nvbackend",
"nvprofileupdater", "nvtmru", "nvdisplaycontainer", "nvtelemetrycontainer",
# AMD drivers
"amdow", "amddvr", "amdrsserv", "ccc", "mom", "atiesrxx", "atieclxx",
"amddvrserver", "amdlvrproxy", "radeonsoft", "rsservices", "amdrsserv",
# Intel drivers
"igfxtray", "hkcmd", "persistence", "igfxem", "igfxhk", "igfxpers",
"intelhaxm", "intelcphecisvs", "intelcphdcpsvc",
# Audio drivers (Realtek, Creative, etc)
"rthdvcpl", "rtkngui64", "rthdbpl", "realtekservice", "nahimicsvc",
"nahimicmsi", "creative", "audiodeviceservice",
# Network drivers
"intelmewservice", "lghub", "lghub_agent", "lghub_updater", "lcore",
"icue", "corsair", "lightingservice",
# Razer peripherals
"razer*", "rzchromasdk", "rzsynapse", "rzudd",
# Logitech peripherals
"logibolt", "logioptions", "logioverlay", "logitechgamingregistry",
# General device helpers
"synaptics", "touchpad", "wacom", "tablet", "pen"
)
# Microsoft Office Processes Whitelist
$Script:MicrosoftOfficeProcesses = @(
# Core Office applications
"winword", "excel", "powerpnt", "outlook", "msaccess", "mspub", "onenote",
"visio", "project", "teams", "lync", "skype", "skypeforbusiness",
# Office services and helpers
"officeclick2run", "officeclicktorun", "officec2rclient", "appvshnotify",
"msoasb", "msouc", "msoidsvc", "msosync", "officehub",
"msoia", "msoyb", "officeclicktorun", "integrator",
# OneDrive
"onedrive", "onedrivesync", "filecoauth",
# Office telemetry and updates
"msoia", "officeupdatemonitor", "officebackgroundtaskhandler"
)
# Adobe Products Whitelist
$Script:AdobeProcesses = @(
# Creative Cloud and core services
"creative cloud", "adobegcclient", "ccxprocess", "cclibrary", "adobenotificationclient",
"adobeipcbroker", "adobegcctray", "adobeupdateservice", "adobearmservice",
"adobe desktop service", "coresynch", "ccxwelcome",
# Photoshop
"photoshop", "photoshoplightroom", "lightroomclassic",
# Illustrator
"illustrator", "ai",
# Premiere Pro / After Effects
"premiere pro", "afterfx", "premiere", "ame", "adobe media encoder",
# Acrobat
"acrobat", "acrord32", "acrodist", "acrotray", "adobecollabsync",
# Other Adobe apps
"indesign", "audition", "animate", "dreamweaver", "bridge",
"xd", "dimension", "character animator", "prelude", "incopy",
# Shared Adobe components
"node", "cef", "adobegenuineservice", "adobeupdateservice"
)
# Common Productivity Software Whitelist
$Script:ProductivitySoftwareProcesses = @(
# Browsers (common legitimate ones)
"chrome", "firefox", "brave", "opera", "vivaldi", "edge",
# Communication
"slack", "zoom", "zoomopener", "whatsapp", "telegram", "signal",
# Cloud storage
"dropbox", "googledrivesync", "googledrivefs", "box", "icloudservices",
# Note-taking
"notion", "evernote", "obsidian", "notepad++",
# Development tools
"code", "devenv", "rider", "pycharm", "webstorm", "intellij",
"git", "github desktop", "sourcetree", "gitkraken",
# Compression tools
"7z", "7zfm", "7zg", "winrar", "winzip",
# Media players
"vlc", "spotify", "foobar2000", "musicbee", "aimp"
)
# Common Software Installation Paths Whitelist
$Script:CommonSoftwarePaths = @(
"*\microsoft office\*", "*\office*\*", "*\microsoft\office*\*",
"*\adobe\*", "*\adobe creative cloud\*", "*\adobe photoshop*\*",
"*\adobe illustrator*\*", "*\adobe premiere*\*", "*\adobe acrobat*\*",
"*\google\chrome\*", "*\mozilla firefox\*", "*\microsoft\edge\*",
"*\nvidia\*", "*\amd\*", "*\intel\*", "*\realtek\*",
"*\program files\common files\microsoft shared\*",
"*\program files (x86)\common files\microsoft shared\*",
"*\slack\*", "*\zoom\*", "*\dropbox\*", "*\onedrive\*"
)
# SECURITY FIX #6: Critical system services that must NEVER be killed
$Script:CriticalSystemProcesses = @(
"registry", "csrss", "smss", "services", "lsass", "wininit", "winlogon", "svchost"
)
# SECURITY FIX #6: Windows networking services allowlist
$Script:WindowsNetworkingServices = @(
"RpcSs", "DHCP", "Dhcp", "Dnscache", "DNS Cache", "LanmanServer", "LanmanWorkstation", "WinHttpAutoProxySvc",
"iphlpsvc", "netprofm", "NlaSvc", "Netman", "TermService", "SessionEnv", "UmRdpService"
)
$Script:EvilStrings = @(
"mimikatz", "sekurlsa::", "kerberos::", "lsadump::", "wdigest", "tspkg",
"http-beacon", "https-beacon", "cobaltstrike", "sleepmask", "reflective",
"amsi.dll", "AmsiScanBuffer", "EtwEventWrite", "MiniDumpWriteDump",
"VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
"ReflectiveLoader", "sharpchrome", "rubeus", "safetykatz", "sharphound"
)
# SECURITY FIX #4: LOLBin detection patterns for signed malware abuse
$Script:LOLBinPatterns = @{
"powershell.exe" = @("-encodedcommand", "-enc", "downloadstring", "invoke-expression", "iex", "bypass")
"cmd.exe" = @("powershell", "wscript", "mshta", "regsvr32", "rundll32")
"mshta.exe" = @("javascript:", "vbscript:", "http://", "https://")
"regsvr32.exe" = @("scrobj.dll", "http://", "https://", "/i:")
"rundll32.exe" = @("javascript:", "http://", "https://", ".cpl,")
"wmic.exe" = @("process call create", "shadowcopy delete")
"certutil.exe" = @("-urlcache", "-decode", "-split", "http://", "https://")
"bitsadmin.exe" = @("/transfer", "/download", "/upload")
"msbuild.exe" = @(".csproj", ".proj", ".xml")
"cscript.exe" = @(".vbs", ".js", ".jse")
"wscript.exe" = @(".vbs", ".js", ".jse")
}
# ============================================
# SECURITY FIX #3: DATABASE INTEGRITY WITH HMAC
# ============================================
function Get-HMACSignature {
param([string]$FilePath)
try {
if (-not (Test-Path $FilePath)) { return $null }
if ($null -eq $Script:HMACKey -or $Script:HMACKey.Length -eq 0) {
Write-ErrorLog -Message "HMAC key is not initialized" -Severity "Critical"
return $null
}
$hmac = New-Object System.Security.Cryptography.HMACSHA256
$hmac.Key = $Script:HMACKey
$fileContent = Get-Content $FilePath -Raw -Encoding UTF8
if ([string]::IsNullOrEmpty($fileContent)) {
Write-Log "[WARN] File $FilePath is empty, skipping HMAC computation"
return $null
}
$hashBytes = $hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($fileContent))
return [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLower()
} catch {
Write-ErrorLog -Message "HMAC computation failed for $FilePath" -Severity "High" -ErrorRecord $_
return $null
} finally {
if ($hmac) { $hmac.Dispose() }
}
}
function Test-DatabaseIntegrity {
if (-not (Test-Path $localDatabase)) { return $true }
if (-not (Test-Path $hashIntegrityFile)) {
Write-Log "[WARNING] No integrity file found for database. Creating one..."
$hmac = Get-HMACSignature -FilePath $localDatabase
if ($hmac) {
$hmac | Out-File -FilePath $hashIntegrityFile -Encoding UTF8
}
return $true
}
try {
$storedHMAC = Get-Content $hashIntegrityFile -Raw -ErrorAction Stop
$currentHMAC = Get-HMACSignature -FilePath $localDatabase
if ($storedHMAC.Trim() -ne $currentHMAC) {
Write-Log "[CRITICAL] Database integrity violation! Database has been tampered with!"
Write-ErrorLog -Message "Hash database HMAC mismatch - possible tampering detected" -Severity "Critical"
Write-SecurityEvent -EventType "DatabaseTampering" -Details @{ DatabasePath = $localDatabase } -Severity "Critical"
# Backup and reset
$backupPath = "$localDatabase.corrupted_$(Get-Date -Format 'yyyyMMddHHmmss')"
Copy-Item $localDatabase $backupPath -ErrorAction SilentlyContinue
Remove-Item $localDatabase -Force -ErrorAction Stop
Remove-Item $hashIntegrityFile -Force -ErrorAction Stop
Write-Log "[ACTION] Corrupted database backed up and reset"
return $false
}
return $true
} catch {
Write-ErrorLog -Message "Database integrity check failed" -Severity "High" -ErrorRecord $_
return $false
}
}
function Update-DatabaseIntegrity {
try {
$hmac = Get-HMACSignature -FilePath $localDatabase
if ($hmac) {
$hmac | Out-File -FilePath $hashIntegrityFile -Force -Encoding UTF8
}
} catch {
Write-ErrorLog -Message "Failed to update database integrity" -Severity "Medium" -ErrorRecord $_
}
}
# ============================================
# DATABASE LOADING WITH INTEGRITY CHECK
# ============================================
if (Test-Path $localDatabase) {
if (Test-DatabaseIntegrity) {
try {
$scannedFiles.Clear()
$lines = Get-Content $localDatabase -ErrorAction Stop
foreach ($line in $lines) {
if ($line -match "^([0-9a-f]{64}),(true|false)$") {
$scannedFiles[$matches[1]] = [bool]::Parse($matches[2])
}
}
Write-Log "[DATABASE] Loaded $($scannedFiles.Count) verified entries"
} catch {
Write-ErrorLog -Message "Failed to load database" -Severity "High" -ErrorRecord $_
$scannedFiles.Clear()
}
} else {
Write-Log "[DATABASE] Integrity check failed. Starting with empty database."
$scannedFiles.Clear()
}
} else {
$scannedFiles.Clear()
New-Item -Path $localDatabase -ItemType File -Force -ErrorAction Stop | Out-Null
Write-Log "[DATABASE] Created new database file"
}
# Ensure execution policy allows script
if ((Get-ExecutionPolicy) -eq "Restricted") {
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -Force -ErrorAction SilentlyContinue
Write-Log "[POLICY] Set execution policy to Bypass"
}
# ============================================
# SECURITY FIX #5: STRICT SELF-EXCLUSION
# ============================================
function Test-IsSelfOrRelated {
param([string]$FilePath)
try {
$normalizedPath = [System.IO.Path]::GetFullPath($FilePath).ToLower()
$selfNormalized = [System.IO.Path]::GetFullPath($Script:SelfPath).ToLower()
$quarantineNormalized = [System.IO.Path]::GetFullPath($Script:QuarantineDir).ToLower()
# Exclude self
if ($normalizedPath -eq $selfNormalized) {
return $true
}
# Exclude quarantine directory
if ($normalizedPath.StartsWith($quarantineNormalized)) {
return $true
}
# Exclude loaded PowerShell modules
$loadedModules = Get-Module | Select-Object -ExpandModuleBase
foreach ($moduleBase in $loadedModules) {
if ($moduleBase -and ([System.IO.Path]::GetFullPath($moduleBase).ToLower() -eq $normalizedPath)) {
return $true
}
}
# Exclude script directory
if ($normalizedPath.StartsWith($Script:SelfDirectory.ToLower())) {
return $true
}
return $false
} catch {
return $true # Fail-safe: if we can't determine, exclude it
}
}
function Test-CriticalSystemProcess {
param($Process)
try {
if (-not $Process) { return $true }
$procName = $Process.ProcessName.ToLower()
# Check against critical process list
if ($Script:CriticalSystemProcesses -contains $procName) {
return $true
}
# SECURITY FIX #4: Verify Microsoft signatures for System32/SysWOW64 binaries
$path = $Process.Path
if ($path -match "\\windows\\system32\\" -or $path -match "\\windows\\syswow64\\") {
try {
$signature = Get-AuthenticodeSignature -FilePath $path -ErrorAction Stop
if ($signature.Status -eq "Valid" -and $signature.SignerCertificate.Subject -match "CN=Microsoft") {
return $true
} else {
Write-Log "[SUSPICIOUS] Unsigned or non-Microsoft binary in System32: $path (Signature: $($signature.Status))"
return $false
}
} catch {
Write-Log "[ERROR] Could not verify signature for System32 file: $path"
return $true # Fail-safe
}
}
return $false
} catch {
return $true # Fail-safe
}
}
function Test-ProtectedOrSelf {
param($Process)
try {
if (-not $Process) { return $true }
$procName = $Process.ProcessName.ToLower()
$procId = $Process.Id
# SECURITY FIX #5: Self-protection
if ($procId -eq $Script:SelfProcessName) {
return $true
}
# Check protected process names
foreach ($protected in $Script:ProtectedProcessNames) {
if ($procName -eq $protected -or $procName -like "$protected*") {
return $true
}
}
foreach ($storeProc in $Script:MicrosoftStoreProcesses) {
if ($procName -eq $storeProc.ToLower() -or $procName -like "$($storeProc.ToLower())*") {
return $true
}
}
foreach ($gamingProc in $Script:GamingPlatformProcesses) {
if ($procName -eq $gamingProc.ToLower() -or $procName -like "$($gamingProc.ToLower())*") {
return $true
}
}
foreach ($driverProc in $Script:CommonDriverProcesses) {
if ($procName -eq $driverProc.ToLower() -or $procName -like "$($driverProc.ToLower())*") {
return $true
}
}
foreach ($officeProc in $Script:MicrosoftOfficeProcesses) {
if ($procName -eq $officeProc.ToLower() -or $procName -like "$($officeProc.ToLower())*") {
return $true
}
}
foreach ($adobeProc in $Script:AdobeProcesses) {
if ($procName -eq $adobeProc.ToLower() -or $procName -like "$($adobeProc.ToLower())*") {
return $true
}
}
foreach ($prodProc in $Script:ProductivitySoftwareProcesses) {
if ($procName -eq $prodProc.ToLower() -or $procName -like "$($prodProc.ToLower())*") {
return $true
}
}
# Check if path is self
if ($Process.Path -and (Test-IsSelfOrRelated -FilePath $Process.Path)) {
return $true
}
return $false
} catch {
return $true # Fail-safe
}
}
# ============================================
# SECURITY FIX #6: SERVICE-AWARE PROCESS KILLING
# ============================================
function Test-IsWindowsNetworkingService {
param([int]$ProcessId)
try {
$service = Get-CimInstance -ClassName Win32_Service -Filter "ProcessId = $ProcessId" -ErrorAction SilentlyContinue
if ($service) {
if ($Script:WindowsNetworkingServices -contains $service.Name) {
Write-Log "[PROTECTION] Process PID $ProcessId is Windows networking service: $($service.Name)"
return $true
}
}
return $false
} catch {
return $false
}
}
# ============================================
# SECURITY FIX #2: RACE-CONDITION-FREE FILE OPERATIONS
# ============================================
function Get-FileWithLock {
param([string]$FilePath)
try {
# Open with exclusive lock to prevent TOCTOU attacks
$fileStream = [System.IO.File]::Open($FilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::None)
# Get file attributes while locked
$fileInfo = New-Object System.IO.FileInfo($FilePath)
$length = $fileInfo.Length
$lastWrite = $fileInfo.LastWriteTime
# Calculate hash while file is locked
$sha256 = [System.Security.Cryptography.SHA256]::Create()
$hashBytes = $sha256.ComputeHash($fileStream)
$hash = [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLower()
$fileStream.Close()
return [PSCustomObject]@{
Hash = $hash
Length = $length
LastWriteTime = $lastWrite
Locked = $true
}
} catch {
return $null
}
}
function Get-SafeFileHash {
param ([string]$filePath)
try {
# SECURITY FIX #5: Self-exclusion check
if (Test-IsSelfOrRelated -FilePath $filePath) {
Write-Log "[EXCLUDED] Skipping self/related file: $filePath"
return $null
}
# PERFORMANCE FIX #8: Check cache first with timestamp validation
$fileInfo = Get-Item $filePath -Force -ErrorAction Stop
$cacheKey = "$filePath|$($fileInfo.LastWriteTime.Ticks)"
$cachedResult = $null
if ($Script:FileHashCache.TryGetValue($cacheKey, [ref]$cachedResult)) {
Write-Log "[CACHE HIT] Using cached hash for: $filePath"
$Script:CacheHits++
return $cachedResult
}
$Script:CacheMisses++
# SECURITY FIX #2: Lock file during scan
$lockedFile = Get-FileWithLock -FilePath $filePath
if (-not $lockedFile) {
Write-Log "[ERROR] Could not lock file for scanning: $filePath"
return $null
}
$signature = Get-AuthenticodeSignature -FilePath $filePath -ErrorAction Stop
$result = [PSCustomObject]@{
Hash = $lockedFile.Hash
Status = $signature.Status
StatusMessage = $signature.StatusMessage
SignerCertificate = $signature.SignerCertificate
LastWriteTime = $lockedFile.LastWriteTime
}
$Script:FileHashCache[$cacheKey] = $result
# Limit cache size to prevent memory exhaustion
if ($Script:FileHashCache.Count -gt 5000) {
# Remove oldest 1000 entries
$keysToRemove = $Script:FileHashCache.Keys | Select-Object -First 1000
foreach ($key in $keysToRemove) {
$null = $Script:FileHashCache.TryRemove($key, [ref]$null)
}
Write-Log "[CACHE] Cleared 1000 oldest entries (cache size was $($Script:FileHashCache.Count))"
}
return $result
} catch {
Write-ErrorLog -Message "Error processing file hash for $filePath" -Severity "Low" -ErrorRecord $_
return $null
}
}
function Get-SHA256Hash {
param([string]$FilePath)
try {
if (Test-IsSelfOrRelated -FilePath $FilePath) {
return $null
}
$lockedFile = Get-FileWithLock -FilePath $FilePath
return $lockedFile.Hash
} catch {
return $null
}
}
# ============================================
# SECURITY FIX #3: HARDENED QUARANTINE
# ============================================
function Move-FileToQuarantine {
param ([string]$filePath)
try {
# SECURITY FIX #5: Never quarantine self
if (Test-IsSelfOrRelated -FilePath $filePath) {
Write-Log "[PROTECTION] Refusing to quarantine self/related file: $filePath"
return $false
}
if (-not (Test-Path $filePath)) {
Write-Log "[QUARANTINE] File not found: $filePath"
return $false
}
# SECURITY FIX #2: Re-verify hash before quarantine
$finalHash = Get-FileWithLock -FilePath $filePath
if (-not $finalHash) {
Write-Log "[QUARANTINE] Could not lock file for final verification: $filePath"
return $false
}
# SECURITY FIX #3: Use GUID for unique quarantine naming
$guid = [System.Guid]::NewGuid().ToString()
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$fileName = Split-Path $filePath -Leaf
$quarantinePath = Join-Path $quarantineFolder "${timestamp}_${guid}_${fileName}.quarantined"
# SECURITY FIX #3: Strip execution permissions and rename
Copy-Item -Path $filePath -Destination $quarantinePath -Force -ErrorAction Stop
# SECURITY FIX #3: Remove execute permissions using icacls
icacls $quarantinePath /deny "*S-1-1-0:(X)" /inheritance:r | Out-Null
# Store metadata
$metadata = @{
OriginalPath = $filePath
QuarantinePath = $quarantinePath
Timestamp = $timestamp
GUID = $guid
Hash = $finalHash.Hash
Reason = "Unsigned or malicious"
} | ConvertTo-Json -Compress
Add-Content -Path "$quarantineFolder\quarantine_metadata.jsonl" -Value $metadata -Encoding UTF8
Write-Log "[QUARANTINE] File quarantined: $filePath -> $quarantinePath"
Write-SecurityEvent -EventType "FileQuarantined" -Details @{ OriginalPath = $filePath; QuarantinePath = $quarantinePath; Hash = $finalHash.Hash } -Severity "High"
# Attempt to delete original
try {
Remove-Item -Path $filePath -Force -ErrorAction Stop
Write-Log "[QUARANTINE] Original file deleted: $filePath"
} catch {
Write-Log "[QUARANTINE] Could not delete original (may be in use): $filePath"
}
return $true
} catch {
Write-ErrorLog -Message "Failed to quarantine file: $filePath" -Severity "High" -ErrorRecord $_
return $false
}
}
function Invoke-QuarantineFile {
param ([string]$filePath)
return Move-FileToQuarantine -filePath $filePath
}
function Set-FileOwnershipAndPermissions {
param ([string]$filePath)
try {
# Ensure SYSTEM has full control and deny execute for others
$acl = Get-Acl $filePath
$acl.SetAccessRuleProtection($true, $false) # Remove inheritance
$SYSTEM_SID = [System.Security.Principal.SecurityIdentifier]::new([System.Security.Principal.WellKnownSidType]::LocalSystemSid, $null)
$SYSTEM_Identity = $SYSTEM_SID.Translate([System.Security.Principal.NTAccount]).Value
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule($SYSTEM_Identity, "FullControl", "None", "Allow")
$acl.SetAccessRule($rule)
$Everyone_SID = [System.Security.Principal.SecurityIdentifier]::new([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
$Everyone_Identity = $Everyone_SID.Translate([System.Security.Principal.NTAccount]).Value
$denyRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Everyone_Identity, "ExecuteFile", "None", "Deny")
$acl.SetAccessRule($denyRule)
Set-Acl -Path $filePath -AclObject $acl -ErrorAction Stop
Write-Log "[ACL] Set SYSTEM FullControl and denied Execute for $filePath"
return $true
} catch {
Write-ErrorLog -Message "Failed to set file ownership/permissions for $filePath" -Severity "Low" -ErrorRecord $_
return $false
}
}
function Stop-ProcessUsingDLL {
param ([string]$filePath)
try {
if (Test-IsSelfOrRelated -FilePath $filePath) {
return
}
$processes = Get-Process | Where-Object {
try {
($_.Modules | Where-Object { $_.FileName -eq $filePath }) -and
-not (Test-ProtectedOrSelf $_) -and
-not (Test-CriticalSystemProcess $_)
} catch { $false }
}
foreach ($process in $processes) {
try {
Stop-ThreatProcess -ProcessId $process.Id -ProcessName $process.Name -Reason "Malicious Activity" -ErrorAction Stop
Write-Log "[KILL] Stopped process $($process.Name) (PID: $($process.Id)) using $filePath"
Write-SecurityEvent -EventType "ProcessKilled" -Details @{ ProcessName = $process.Name; PID = $process.Id; Reason = "Using quarantined DLL: $filePath" } -Severity "Medium"
} catch {
Write-ErrorLog -Message "Failed to stop process $($process.Name) using $filePath" -Severity "Medium" -ErrorRecord $_
}
}
} catch {
Write-ErrorLog -Message "Error stopping processes for $filePath" -Severity "Medium" -ErrorRecord $_
}
}
function Stop-ProcessesUsingFile {
param([string]$FilePath)
try {
if (Test-IsSelfOrRelated -FilePath $FilePath) {
return
}
Get-Process | Where-Object {
try {
$_.Path -eq $FilePath -and
-not (Test-ProtectedOrSelf $_) -and
-not (Test-CriticalSystemProcess $_) -and
-not (Test-IsWindowsNetworkingService -ProcessId $_.Id)
} catch { $false }
} | ForEach-Object {
try {
$_.Kill()
Write-Log "[KILL] Terminated process using file: $($_.ProcessName) (PID: $($_.Id))"
Write-SecurityEvent -EventType "ProcessKilled" -Details @{ ProcessName = $_.ProcessName; PID = $_.Id; Reason = "Using malicious file: $FilePath" } -Severity "Medium"
Start-Sleep -Milliseconds 100
} catch {
Write-ErrorLog -Message "Failed to kill process $($_.ProcessName)" -Severity "Low" -ErrorRecord $_
}
}
} catch {
Write-ErrorLog -Message "Error in Stop-ProcessesUsingFile" -Severity "Low" -ErrorRecord $_
}
}
function Test-ExcludeFile {
param ([string]$filePath)
if (Test-IsWhitelisted -FilePath $filePath) {
return $true
}
$lowerPath = $filePath.ToLower()
# SECURITY FIX #5: Exclude self and related files
if (Test-IsSelfOrRelated -FilePath $filePath) {
return $true
}
# Exclude assembly folders
if ($lowerPath -like "*\assembly\*") {
return $true
}
# Exclude ctfmon-related files
if ($lowerPath -like "*ctfmon*" -or $lowerPath -like "*msctf.dll" -or $lowerPath -like "*msutb.dll") {
return $true
}
foreach ($gamingPath in $Script:GamingPlatformPaths) {
if ($lowerPath -like $gamingPath) {
return $true
}
}
foreach ($softwarePath in $Script:CommonSoftwarePaths) {
if ($lowerPath -like $softwarePath) {
return $true
}
}
return $false
}
# ============================================
# SECURITY FIX #8: PERFORMANCE-OPTIMIZED SCANNING
# ============================================
function Remove-UnsignedDLLs {
Write-Log "[SCAN] Starting unsigned DLL/WINMD scan"
$drives = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DriveType -in (2, 3, 4) }
$Script:ScanThrottle = 0
$protectedDirs = @(
"C:\Windows\System32\wbem",
"C:\Windows\System32\WinSxS",
"C:\Windows\System32\LogFiles",
"C:\Windows\servicing",
"C:\Windows\WinSxS"
)
foreach ($drive in $drives) {
$root = $drive.DeviceID + "\"
Write-Log "[SCAN] Scanning drive: $root"
try {
$dllFiles = Get-ChildItem -Path $root -Include *.dll,*.winmd -Recurse -File -ErrorAction SilentlyContinue |
Where-Object {
$filePath = $_.FullName
$isProtected = $false
foreach ($protectedDir in $protectedDirs) {
if ($filePath -like "$protectedDir*") {
$isProtected = $true
break
}
}
-not $isProtected
}
foreach ($dll in $dllFiles) {
# PERFORMANCE FIX #8: Throttle scanning to prevent system slowdown
$Script:ScanThrottle++
if ($Script:ScanThrottle % 100 -eq 0) {
Start-Sleep -Milliseconds 50
}
try {
if (Test-ExcludeFile -filePath $dll.FullName) {
continue
}
$fileHash = Get-SafeFileHash -filePath $dll.FullName
if ($fileHash) {
if ($scannedFiles.ContainsKey($fileHash.Hash)) {
# Already scanned, take action if needed
if (-not $scannedFiles[$fileHash.Hash]) {
if (Set-FileOwnershipAndPermissions -filePath $dll.FullName) {
Stop-ProcessUsingDLL -filePath $dll.FullName
Invoke-QuarantineFile -filePath $dll.FullName
}
}
} else {
# New file
$isValid = $fileHash.Status -eq "Valid"
$scannedFiles[$fileHash.Hash] = $isValid
"$($fileHash.Hash),$isValid" | Out-File -FilePath $localDatabase -Append -Encoding UTF8 -ErrorAction Stop
Update-DatabaseIntegrity # SECURITY FIX #3: Update HMAC
Write-Log "[SCAN] New file: $($dll.FullName) (Valid: $isValid)"
Write-SecurityEvent -EventType "FileScanned" -Details @{ Path = $dll.FullName; Hash = $fileHash.Hash; IsValid = $isValid } -Severity "Informational"
if (-not $isValid) {
if (Set-FileOwnershipAndPermissions -filePath $dll.FullName) {
Stop-ProcessUsingDLL -filePath $dll.FullName
Invoke-QuarantineFile -filePath $dll.FullName
}
}
}
}
} catch {
Write-ErrorLog -Message "Error processing file $($dll.FullName)" -Severity "Low" -ErrorRecord $_
}
}
} catch {
Write-ErrorLog -Message "Scan failed for drive $root" -Severity "Medium" -ErrorRecord $_
}
}
}
# ============================================
# FILE SYSTEM WATCHER (Throttled)
# ============================================
$drives = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DriveType -in (2, 3, 4) }
foreach ($drive in $drives) {
$monitorPath = $drive.DeviceID + "\"
try {
$fileWatcher = New-Object System.IO.FileSystemWatcher
$fileWatcher.Path = $monitorPath
$fileWatcher.Filter = "*.*"
$fileWatcher.IncludeSubdirectories = $true
$fileWatcher.EnableRaisingEvents = $true
$fileWatcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor [System.IO.NotifyFilters]::LastWrite
$action = {
param($src, $evtArgs)
try {
$localScannedFiles = $using:scannedFiles
$localQuarantineFolder = $using:quarantineFolder
$localDatabase = $using:localDatabase
if ($evtArgs.ChangeType -in "Created", "Changed" -and
$evtArgs.FullPath -notlike "$localQuarantineFolder*" -and
($evtArgs.FullPath -like "*.dll" -or $evtArgs.FullPath -like "*.winmd")) {
if (Test-ExcludeFile -filePath $evtArgs.FullPath) {
return
}
Start-Sleep -Milliseconds 500 # Throttle
$fileHash = Get-SafeFileHash -filePath $evtArgs.FullPath
if ($fileHash) {
if ($localScannedFiles.ContainsKey($fileHash.Hash)) {
if (-not $localScannedFiles[$fileHash.Hash]) {
if (Set-FileOwnershipAndPermissions -filePath $evtArgs.FullPath) {
Stop-ProcessUsingDLL -filePath $evtArgs.FullPath
Invoke-QuarantineFile -filePath $evtArgs.FullPath
}
}
} else {
$isValid = $fileHash.Status -eq "Valid"
$localScannedFiles[$fileHash.Hash] = $isValid
"$($fileHash.Hash),$isValid" | Out-File -FilePath $localDatabase -Append -Encoding UTF8
Update-DatabaseIntegrity
Write-Log "[FSWATCHER] New file: $($evtArgs.FullPath) (Valid: $isValid)"
Write-SecurityEvent -EventType "FileScanned" -Details @{ Path = $evtArgs.FullPath; Hash = $fileHash.Hash; IsValid = $isValid } -Severity "Informational"
if (-not $isValid) {
if (Set-FileOwnershipAndPermissions -filePath $evtArgs.FullPath) {
Stop-ProcessUsingDLL -filePath $evtArgs.FullPath
Invoke-QuarantineFile -filePath $evtArgs.FullPath
}
}
}
}
}
} catch {
# Silently continue
}
}
Register-ObjectEvent -InputObject $fileWatcher -EventName Created -Action $action -ErrorAction Stop
Register-ObjectEvent -InputObject $fileWatcher -EventName Changed -Action $action -ErrorAction Stop
Write-Log "[WATCHER] FileSystemWatcher set up for $monitorPath"
} catch {
Write-ErrorLog -Message "Failed to set up watcher for $monitorPath" -Severity "Medium" -ErrorRecord $_
}
}
$ApiConfig = @{
CirclHashLookupUrl = "https://hashlookup.circl.lu/lookup/sha256"
CymruApiUrl = "https://api.malwarehash.cymru.com/v1/hash"
MalwareBazaarApiUrl = "https://mb-api.abuse.ch/api/v1/"
}
# ============================================
# HASH-BASED THREAT DETECTION
# ============================================
function Test-HashReputation {
param(
[string]$hash,
[string]$ProcessName
)
if (-not $hash) { return $false }
$isMalicious = $false
$jobs = @()
# CIRCL Hash Lookup
$jobs += Start-Job -ScriptBlock {
param($h, $url)
try {
$circlUrl = "$url/$h"
$response = Invoke-RestMethod -Uri $circlUrl -Method Get -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
if ($response -and $response.KnownMalicious) {
return @{ Source = "CIRCL"; Malicious = $true }
}
} catch {}
return $null
} -ArgumentList $hash, $ApiConfig.CirclHashLookupUrl
# Team Cymru
$jobs += Start-Job -ScriptBlock {
param($h, $url)
try {
$cymruUrl = "$url/$h"
$response = Invoke-RestMethod -Uri $cymruUrl -Method Get -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
if ($response -and $response.malicious -eq $true) {
return @{ Source = "Cymru"; Malicious = $true }
}
} catch {}
return $null
} -ArgumentList $hash, $ApiConfig.CymruApiUrl
$jobs += Start-Job -ScriptBlock {
param($h, $url)
try {
$mbBody = @{ query = "get_info"; hash = $h } | ConvertTo-Json
$response = Invoke-RestMethod -Uri $url -Method Post -Body $mbBody -ContentType "application/json" -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
if ($response -and $response.query_status -eq "ok") {
return @{ Source = "MalwareBazaar"; Malicious = $true }
}
} catch {}
return $null
} -ArgumentList $hash, $ApiConfig.MalwareBazaarApiUrl
# Wait for jobs with timeout
$jobs | Wait-Job -Timeout 5 | Out-Null
# Check results
foreach ($job in $jobs) {
try {
$result = Receive-Job -Job $job -ErrorAction SilentlyContinue
if ($result -and $result.Malicious) {
Write-Log "[HASH] $($result.Source) reports malicious: $ProcessName"
Write-SecurityEvent -EventType "MaliciousHashDetected" -Details @{
Source = $result.Source; Hash = $hash; Process = $ProcessName
} -Severity "High"
$isMalicious = $true
}
} catch {}
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
}
return $isMalicious
}
function Test-FileHash {
param(
[string]$FilePath,
[string]$ProcessName,
[int]$ProcessId
)
try {
if (-not (Test-Path $FilePath)) {
return $false
}
if (Test-IsSelfOrRelated -FilePath $FilePath) {
return $false
}
$hashInfo = Get-SafeFileHash -filePath $FilePath
if (-not $hashInfo) {
return $false
}
$hash = $hashInfo.Hash
if (Test-HashReputation -hash $hash -ProcessName $ProcessName) {
return $true
}
return $false
} catch {
Write-ErrorLog -Message "Error during file hash check for $FilePath" -Severity "Low" -ErrorRecord $_
return $false
}
}
# ============================================
# THREAT KILLING WITH SAFETY CHECKS
# ============================================
function Stop-ThreatProcess {
param(
[int]$ProcessId,
[string]$ProcessName,
[string]$Reason
)
try {
$proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
if (-not $proc) {
return $false
}
# SECURITY FIX #5 & #6: Comprehensive protection checks
if (Test-ProtectedOrSelf $proc) {
Write-Log "[PROTECTION] Refusing to kill protected process: $ProcessName (PID: $ProcessId)"
return $false
}
if (Test-CriticalSystemProcess $proc) {
Write-Log "[PROTECTION] Refusing to kill critical system process: $ProcessName (PID: $ProcessId)"
return $false
}
if (Test-IsWindowsNetworkingService -ProcessId $ProcessId) {
Write-Log "[PROTECTION] Refusing to kill Windows networking service: $ProcessName (PID: $ProcessId)"
return $false
}
Write-Log "[KILL] Terminating process: $ProcessName (PID: $ProcessId) - Reason: $Reason"
Write-SecurityEvent -EventType "ProcessKilled" -Details @{ ProcessName = $ProcessName; PID = $ProcessId; Reason = $Reason } -Severity "High"
Stop-Process -Id $ProcessId -Force -ErrorAction Stop
Write-Log "[KILL] Successfully terminated PID: $ProcessId"
return $true
} catch {
Write-ErrorLog -Message "Failed to terminate PID $ProcessId" -Severity "Low" -ErrorRecord $_
return $false
}
}
function Stop-ThreatProcess {
param(
[int]$ProcessId,
[string]$ProcessName,
[string]$Reason
)
return Stop-ThreatProcess -ProcessId $ProcessId -ProcessName $ProcessName -Reason $Reason
}
function Invoke-ProcessKill {
param(
[int]$ProcessId,
[string]$ProcessName,
[string]$Reason
)
return Stop-ThreatProcess -ProcessId $ProcessId -ProcessName $ProcessName -Reason $Reason
}
function Invoke-QuarantineProcess {
param(
$Process,
[string]$Reason
)
try {
if (Test-ProtectedOrSelf $Process) {
Write-Log "[PROTECTION] Refusing to quarantine protected process: $($Process.ProcessName)"
return $false
}
Write-Log "[QUARANTINE] Quarantining process $($Process.ProcessName) (PID: $($Process.Id)) due to: $Reason"
Write-SecurityEvent -EventType "ProcessQuarantineInitiated" -Details @{ ProcessName = $Process.ProcessName; PID = $Process.Id; Reason = $Reason } -Severity "High"
if ($Process.Path -and (Test-Path $Process.Path)) {
Invoke-QuarantineFile -FilePath $Process.Path
}
$Process.Kill()
Write-Log "[KILL] Killed malicious process: $($Process.ProcessName) (PID: $($Process.Id))"
Write-SecurityEvent -EventType "ProcessKilled" -Details @{ ProcessName = $Process.ProcessName; PID = $Process.Id; Reason = "Quarantined file: $Reason" } -Severity "High"
return $true
} catch {
Write-ErrorLog -Message "Failed to quarantine process $($Process.ProcessName)" -Severity "Medium" -ErrorRecord $_
return $false
}
}
# ============================================
# SECURITY FIX #4: LOLBin DETECTION
# ============================================
function Test-LOLBinAbuse {
param(
[string]$ProcessName,
[string]$CommandLine
)
$procLower = $ProcessName.ToLower()
if ($Script:LOLBinPatterns.ContainsKey($procLower)) {
$patterns = $Script:LOLBinPatterns[$procLower]
foreach ($pattern in $patterns) {
if ($CommandLine -match [regex]::Escape($pattern)) {
Write-Log "[LOLBIN] Detected LOLBin abuse: $ProcessName with pattern '$pattern'"
Write-SecurityEvent -EventType "LOLBinAbuse" -Details @{ ProcessName = $ProcessName; CommandLine = $CommandLine; Pattern = $pattern } -Severity "High"
return $true
}
}
}
return $false
}
# ============================================
# FILELESS MALWARE DETECTION
# ============================================
function Find-FilelessMalware {
$detections = @()
# PowerShell without file
try {
Get-Process -Name powershell -ErrorAction SilentlyContinue | Where-Object {
try {
-not (Test-ProtectedOrSelf $_) -and
($_.MainWindowTitle -match "encodedcommand|enc|iex|invoke-expression" -or
($_.Modules | Where-Object { $_.ModuleName -eq "" -or $_.FileName -eq "" }))
} catch { $false }
} | ForEach-Object {
$proc = $_
Write-Log "[FILELESS] Detected suspicious PowerShell: PID $($proc.Id)"
if ($proc.Path) {
$hashMalicious = Test-FileHash -FilePath $proc.Path -ProcessName $proc.Name -ProcessId $proc.Id
if ($hashMalicious) {
Invoke-QuarantineFile -FilePath $proc.Path
}
}
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Fileless PowerShell execution"
}
} catch {
Write-ErrorLog -Message "Error in PowerShell fileless detection" -Severity "Low" -ErrorRecord $_
}
# WMI Event Subscriptions
try {
Get-WmiObject -Namespace root\Subscription -Class __EventFilter -ErrorAction SilentlyContinue | Where-Object {
$_.Query -match "powershell|vbscript|javascript"
} | ForEach-Object {
$subscription = $_
try {
Write-Log "[FILELESS] Removing malicious WMI event filter: $($subscription.Name)"
Remove-WmiObject -InputObject $subscription -ErrorAction Stop
Write-SecurityEvent -EventType "WMIEventFilterRemoved" -Details @{ FilterName = $subscription.Name } -Severity "High"
$consumers = Get-WmiObject -Namespace root\Subscription -Class __EventConsumer -Filter "Name='$($subscription.Name)'" -ErrorAction SilentlyContinue
$bindings = Get-WmiObject -Namespace root\Subscription -Class __FilterToConsumerBinding -Filter "Filter='$($subscription.__RELPATH)'" -ErrorAction SilentlyContinue
foreach ($consumer in $consumers) {
Remove-WmiObject -InputObject $consumer -ErrorAction SilentlyContinue
}
foreach ($binding in $bindings) {
Remove-WmiObject -InputObject $binding -ErrorAction SilentlyContinue
}
} catch {
Write-ErrorLog -Message "Failed to remove WMI subscription" -Severity "Medium" -ErrorRecord $_
}
}
} catch {
Write-ErrorLog -Message "Error in WMI fileless detection" -Severity "Low" -ErrorRecord $_
}
# Registry Scripts
try {
$suspiciousKeys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
)
foreach ($key in $suspiciousKeys) {
if (Test-Path $key) {
Get-ItemProperty -Path $key -ErrorAction SilentlyContinue | ForEach-Object {
$_.PSObject.Properties | Where-Object {
$_.Value -match "powershell.*-enc|mshta|regsvr32.*scrobj|wscript|cscript" -and $_.Name -ne "PSGuid"
} | ForEach-Object {
try {
Write-Log "[FILELESS] Removing malicious registry entry: $($key)\$($_.Name)"
Remove-ItemProperty -Path $key -Name $_.Name -Force -ErrorAction Stop
Write-SecurityEvent -EventType "RegistryMalwareEntryRemoved" -Details @{ Path = "$key\$($_.Name)"; Value = $_.Value } -Severity "High"
} catch {
Write-ErrorLog -Message "Failed to remove registry entry" -Severity "Medium" -ErrorRecord $_
}
}
}
}
}
} catch {
Write-ErrorLog -Message "Error in registry fileless detection" -Severity "Low" -ErrorRecord $_
}
return $detections
}
# ============================================
# MEMORY SCANNER
# ============================================
function Start-MemoryMonitors {
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
$intervalHighMem = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.HighMemoryCheckIntervalSeconds) { [int]$Script:ManagedJobConfig.HighMemoryCheckIntervalSeconds } else { 300 }
Register-ManagedJob -Name "HighMemoryMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalHighMem -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-HighMemoryMonitorTick
}
$intervalPSMem = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.PowerShellMemoryScanIntervalSeconds) { [int]$Script:ManagedJobConfig.PowerShellMemoryScanIntervalSeconds } else { 10 }
Register-ManagedJob -Name "PowerShellMemoryScanner" -Enabled $true -Critical $true -IntervalSeconds $intervalPSMem -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-PowerShellMemoryScannerTick
}
Write-Log "[+] Memory monitors (managed jobs) registered"
}
function Invoke-HighMemoryMonitorTick {
try {
$parentProcess = Get-Process -Id $PID -ErrorAction SilentlyContinue
if ($parentProcess) {
$memoryMB = [math]::Round($parentProcess.WorkingSet64 / 1MB, 2)
if ($memoryMB -gt 500) {
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"[$timestamp] [MEMORY] High memory usage detected: ${memoryMB}MB" | Out-File (Join-Path $Script:LogsDir "memory_alerts.txt") -Append
}
}
} catch {}
}
function Invoke-PowerShellMemoryScannerTick {
try {
if (-not $script:PowerShellMemoryScannerEvilStrings) {
$script:PowerShellMemoryScannerEvilStrings = @(
'mimikatz','sekurlsa::','kerberos::','lsadump::','wdigest','tspkg',
'http-beacon','https-beacon','cobaltstrike','sleepmask','reflective',
'amsi.dll','AmsiScanBuffer','EtwEventWrite','MiniDumpWriteDump',
'VirtualAllocEx','WriteProcessMemory','CreateRemoteThread',
'ReflectiveLoader','sharpchrome','rubeus','safetykatz','sharphound'
)
}
$log = "$Base\ps_memory_hits.log"
$evil = $script:PowerShellMemoryScannerEvilStrings
$selfId = $PID
Get-Process | Where-Object {
try {
$_.Id -ne $selfId -and ($_.WorkingSet64 -gt 100MB -or $_.Name -match 'powershell|wscript|cscript|mshta|rundll32|regsvr32|msbuild|cmstp')
} catch { $false }
} | ForEach-Object {
$hit = $false
$proc = $_
try {
if (Test-ProtectedOrSelf $proc) { return }
if (Test-CriticalSystemProcess $proc) { return }
if (Test-IsWindowsNetworkingService -ProcessId $proc.Id) { return }
foreach ($m in $proc.Modules) {
foreach ($s in $evil) {
if ($m.ModuleName -match $s -or $m.FileName -match $s) {
$hit = $true
break
}
}
if ($hit) { break }
}
} catch {}
if ($hit) {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | PS MEMORY HIT -> $($proc.Name) ($($proc.Id))" | Out-File $log -Append
Write-SecurityEvent -EventType "MemoryScanHit" -Details @{ ProcessName = $proc.Name; PID = $proc.Id; Reason = "Suspicious strings in memory" } -Severity "High"
if ($proc.Path) {
$hashMalicious = Test-FileHash -FilePath $proc.Path -ProcessName $proc.Name -ProcessId $proc.Id
if ($hashMalicious) {
Invoke-QuarantineFile -FilePath $proc.Path
}
}
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Malicious strings in memory"
}
}
} catch {
try { Write-ErrorLog -Message "PowerShell memory scanner tick failed" -Severity "Low" -ErrorRecord $_ } catch {}
}
}
# ============================================
# REFLECTIVE PAYLOAD DETECTOR
# ============================================
Write-Log "[+] Starting reflective payload detector"
function Start-ReflectivePayloadDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ReflectivePayloadIntervalSeconds) { [int]$Script:ManagedJobConfig.ReflectivePayloadIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "ReflectivePayloadDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ReflectivePayloadDetectorTick
}
Write-Log "[+] Reflective payload detector (managed job) registered"
}
function Invoke-ReflectivePayloadDetectorTick {
try {
$log = "$Base\manual_map_hits.log"
$selfId = $PID
Get-Process | Where-Object {
try { $_.Id -ne $selfId -and $_.WorkingSet64 -gt 40MB } catch { $false }
} | ForEach-Object {
$p = $_
$sus = $false
try {
if (Test-ProtectedOrSelf $p) { return }
if (Test-CriticalSystemProcess $p) { return }
if (-not $p.Path -or $p.Path -eq '' -or $p.Path -match '$$Unknown$$') { $sus = $true }
if ($p.Modules | Where-Object { $_.FileName -eq '' -or $_.ModuleName -eq '' }) { $sus = $true }
} catch {}
if ($sus) {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | REFLECTIVE PAYLOAD -> $($p.Name) ($($p.Id)) Path='$($p.Path)'" | Out-File $log -Append
Write-SecurityEvent -EventType "ReflectivePayloadDetected" -Details @{ ProcessName = $p.Name; PID = $p.Id; Path = $p.Path } -Severity "Critical"
if ($p.Path) {
$hashMalicious = Test-FileHash -FilePath $p.Path -ProcessName $p.Name -ProcessId $p.Id
if ($hashMalicious) {
Invoke-QuarantineFile -FilePath $p.Path
}
}
Stop-ThreatProcess -ProcessId $p.Id -ProcessName $p.Name -Reason "Reflective payload detected"
}
}
} catch {
try { Write-ErrorLog -Message "Reflective payload detector tick failed" -Severity "Low" -ErrorRecord $_ } catch {}
}
}
# ============================================
# BEHAVIOR MONITOR
# ============================================
function Start-BehaviorMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.BehaviorMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.BehaviorMonitorIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "BehaviorMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-BehaviorMonitorTick
}
Write-Log "[+] Behavior monitor (managed job) registered"
}
function Invoke-BehaviorMonitorTick {
try {
if (-not $script:BehaviorMonitorBehaviors) {
$script:BehaviorMonitorBehaviors = @{
"ProcessHollowing" = {
param($Process)
try {
$procPath = $Process.Path
$modules = Get-Process -Id $Process.Id -Module -ErrorAction SilentlyContinue
return ($modules -and $procPath -and ($modules[0].FileName -ne $procPath))
} catch { return $false }
}
"CredentialAccess" = {
param($Process)
try {
$cmdline = (Get-CimInstance Win32_Process -Filter "ProcessId=$($Process.Id)" -ErrorAction SilentlyContinue).CommandLine
return ($cmdline -match "mimikatz|procdump|sekurlsa|lsadump" -or $Process.ProcessName -match "vaultcmd|cred")
} catch { return $false }
}
"LateralMovement" = {
param($Process)
try {
$connections = Get-NetTCPConnection -OwningProcess $Process.Id -ErrorAction SilentlyContinue
$remoteIPs = $connections | Where-Object {
$_.RemoteAddress -notmatch "^(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" -and
$_.RemoteAddress -ne "0.0.0.0" -and
$_.RemoteAddress -ne "::"
}
return ($remoteIPs.Count -gt 5)
} catch { return $false }
}
}
}
$behaviors = $script:BehaviorMonitorBehaviors
$logFilePath = "$Base\behavior_detections.log"
$selfId = $PID
Get-Process | Where-Object {
try { $_.Id -ne $selfId } catch { $false }
} | ForEach-Object {
try {
$process = $_
if (Test-ProtectedOrSelf $process) { return }
if (Test-CriticalSystemProcess $process) { return }
foreach ($behavior in $behaviors.Keys) {
try {
if (& $behaviors[$behavior] $process) {
$msg = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | BEHAVIOR DETECTED: $behavior | Process: $($process.Name) PID: $($process.Id) Path: $($process.Path)"
$msg | Out-File $logFilePath -Append
Write-SecurityEvent -EventType "SuspiciousBehaviorDetected" -Details @{ Behavior = $behavior; ProcessName = $process.Name; PID = $process.Id; Path = $process.Path } -Severity "High"
if ($behavior -in @("ProcessHollowing", "CredentialAccess")) {
if ($process.Path) {
$hashMalicious = Test-FileHash -FilePath $process.Path -ProcessName $process.Name -ProcessId $process.Id
if ($hashMalicious) {
Invoke-QuarantineFile -FilePath $process.Path
}
}
Stop-ThreatProcess -ProcessId $process.Id -ProcessName $process.Name -Reason "Suspicious behavior: $behavior"
}
}
} catch {}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Behavior monitor tick failed" -Severity "Medium" -ErrorRecord $_ } catch {}
}
}
function Start-EnhancedBehaviorMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.EnhancedBehaviorIntervalSeconds) { [int]$Script:ManagedJobConfig.EnhancedBehaviorIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "EnhancedBehaviorMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-EnhancedBehaviorMonitorTick
}
Write-Log "[+] Enhanced behavior monitor (managed job) registered"
}
function Invoke-EnhancedBehaviorMonitorTick {
try {
$logPath = "$Base\enhanced_behavior.log"
$protected = $Script:ProtectedProcessNames
$selfId = $PID
Get-Process | Where-Object {
try {
if ($_.Id -eq $selfId) { return $false }
if ($protected -and ($protected -contains $_.ProcessName.ToLower())) { return $false }
$true
} catch { $false }
} | ForEach-Object {
try {
$proc = $_
if (Test-ProtectedOrSelf $proc) { return }
if (Test-CriticalSystemProcess $proc) { return }
# LOLBin detection
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($commandLine -and (Test-LOLBinAbuse -ProcessName $proc.ProcessName -CommandLine $commandLine)) {
Add-Content -Path $logPath -Value "[LOLBIN ABUSE] Detected in $($proc.ProcessName) (PID: $($proc.Id)): $commandLine" -Encoding UTF8
Write-SecurityEvent -EventType "LOLBinAbuse" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; CommandLine = $commandLine } -Severity "High"
if ($proc.Path) {
Invoke-QuarantineFile -FilePath $proc.Path
}
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.ProcessName -Reason "LOLBin abuse detected"
}
# High thread/handle count
$threadCount = $proc.Threads.Count
$handleCount = $proc.HandleCount
if ($threadCount -gt 100 -or $handleCount -gt 10000) {
Add-Content -Path $logPath -Value "SUSPICIOUS BEHAVIOR: High thread/handle count in $($proc.ProcessName) (Threads: $threadCount, Handles: $handleCount)" -Encoding UTF8
Write-SecurityEvent -EventType "SuspiciousProcessBehavior" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; Behavior = "HighThreadHandleCount"; ThreadCount = $threadCount; HandleCount = $handleCount } -Severity "Medium"
}
# Random process name
if ($proc.ProcessName -match "^[a-z0-9]{32}$") {
Add-Content -Path $logPath -Value "SUSPICIOUS BEHAVIOR: Random process name: $($proc.ProcessName)" -Encoding UTF8
Write-SecurityEvent -EventType "SuspiciousProcessBehavior" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; Behavior = "RandomProcessName" } -Severity "Medium"
}
} catch {}
}
} catch {
try {
Write-ErrorLog -Message "Enhanced behavior monitor tick failed" -Severity "Medium" -ErrorRecord $_
} catch {}
}
}
function Start-AntiTamperMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.AntiTamperIntervalSeconds) { [int]$Script:ManagedJobConfig.AntiTamperIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "AntiTamperMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-AntiTamperMonitorTick
}
Write-Log "[+] Anti-tamper monitor (managed job) registered"
}
function Invoke-AntiTamperMonitorTick {
try {
if (-not $script:AntiTamperOriginalScriptHash) {
$scriptPathToCheck = $Script:SelfPath
if (-not $scriptPathToCheck) { $scriptPathToCheck = $PSCommandPath }
if (-not $scriptPathToCheck) { $scriptPathToCheck = $Script:ScriptInstallPath }
if ($scriptPathToCheck) {
$script:AntiTamperScriptPath = $scriptPathToCheck
$script:AntiTamperOriginalScriptHash = (Get-FileHash -Path $scriptPathToCheck -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
}
}
$logPath = "$Base\anti_tamper.log"
$selfPid = $PID
if ($script:AntiTamperScriptPath -and $script:AntiTamperOriginalScriptHash) {
$currentHash = (Get-FileHash -Path $script:AntiTamperScriptPath -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
if ($currentHash -and $currentHash -ne $script:AntiTamperOriginalScriptHash) {
$msg = "CRITICAL: Script file has been modified! Original: $($script:AntiTamperOriginalScriptHash), Current: $currentHash"
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ScriptTampering" -Details @{ OriginalHash = $script:AntiTamperOriginalScriptHash; CurrentHash = $currentHash } -Severity "Critical"
Write-Host "`n[!!! CRITICAL !!!] EDR script has been tampered with!" -ForegroundColor Red
}
}
$ourProcess = Get-Process -Id $selfPid -ErrorAction SilentlyContinue
if ($ourProcess) {
$threads = $ourProcess.Threads
$suspendedCount = ($threads | Where-Object { $_.ThreadState -eq 'Wait' -and $_.WaitReason -eq 'Suspended' }).Count
if ($suspendedCount -gt 5) {
$msg = "CRITICAL: Debugger attachment suspected - $suspendedCount suspended threads detected"
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "DebuggerDetected" -Details @{ SuspendedThreads = $suspendedCount } -Severity "Critical"
}
}
$currentModules = Get-Process -Id $selfPid -Module -ErrorAction SilentlyContinue
if ($currentModules -and $script:AntiTamperScriptPath) {
$unexpectedModules = $currentModules | Where-Object {
$_.FileName -notmatch "\\Windows\\|\\PowerShell\\" -and
$_.FileName -notmatch [regex]::Escape($script:AntiTamperScriptPath)
}
if ($unexpectedModules) {
foreach ($mod in $unexpectedModules) {
$msg = "CRITICAL: Unexpected module loaded into EDR process: $($mod.FileName)"
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ProcessInjection" -Details @{ Module = $mod.FileName } -Severity "Critical"
}
}
}
$dumpTools = Get-Process | Where-Object { $_.ProcessName -match "procdump|processhacker|processdumper|memorydumper|mimikatz" }
if ($dumpTools) {
foreach ($tool in $dumpTools) {
$msg = "CRITICAL: Memory dumping tool detected: $($tool.ProcessName) (PID: $($tool.Id))"
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "MemoryDumpingTool" -Details @{ ProcessName = $tool.ProcessName; PID = $tool.Id; Path = $tool.Path } -Severity "Critical"
try {
Stop-Process -Id $tool.Id -Force
$msg = "KILLED: Terminated memory dumping tool: $($tool.ProcessName)"
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
} catch {}
}
}
} catch {
try {
Write-ErrorLog -Message "Anti-tamper monitor tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
# ============================================
# START OF NEWLY ADDED CODE BLOCK
# ============================================
function Start-NetworkAnomalyDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.NetworkAnomalyIntervalSeconds) { [int]$Script:ManagedJobConfig.NetworkAnomalyIntervalSeconds } else { 15 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "NetworkAnomalyDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-NetworkAnomalyDetectorTick
}
Write-Log "[+] Network anomaly detector (managed job) registered"
}
function Invoke-NetworkAnomalyDetectorTick {
try {
if (-not $script:NetworkAnomalyConnectionBaseline) {
$script:NetworkAnomalyConnectionBaseline = @{}
}
if ($script:NetworkAnomalyConnectionBaseline.Count -gt 5000) {
$script:NetworkAnomalyConnectionBaseline.Clear()
}
$logFile = "$Base\network_anomalies.log"
$connections = Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue
foreach ($conn in $connections) {
try {
$remoteIP = $conn.RemoteAddress
$remotePort = $conn.RemotePort
$owningPID = $conn.OwningProcess
# Skip localhost and private IPs
if ($remoteIP -match "^(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)" -or $remoteIP -eq "0.0.0.0" -or $remoteIP -eq "::") {
continue
}
# Detect data exfiltration patterns (unusual ports)
if ($remotePort -in @(4444, 5555, 6666, 7777, 8888, 9999, 31337, 12345)) {
$process = Get-Process -Id $owningPID -ErrorAction SilentlyContinue
$msg = "SUSPICIOUS: Connection to known malicious port $remotePort from $($process.ProcessName) (PID: $owningPID) to $remoteIP"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousNetworkConnection" -Details @{
ProcessName = $process.ProcessName
PID = $owningPID
RemoteIP = $remoteIP
RemotePort = $remotePort
} -Severity "High"
try {
Stop-Process -Id $owningPID -Force
$msg = "KILLED: Terminated process with suspicious network activity: $($process.ProcessName)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
} catch {}
}
# Track connection frequency for each process/IP
$key = "$owningPID-$remoteIP"
if (-not $script:NetworkAnomalyConnectionBaseline.ContainsKey($key)) {
$script:NetworkAnomalyConnectionBaseline[$key] = 1
} else {
$script:NetworkAnomalyConnectionBaseline[$key]++
if ($script:NetworkAnomalyConnectionBaseline[$key] -gt 50) {
$process = Get-Process -Id $owningPID -ErrorAction SilentlyContinue
$msg = "SUSPICIOUS: Possible C2 beaconing from $($process.ProcessName) (PID: $owningPID) to $remoteIP (Count: $($script:NetworkAnomalyConnectionBaseline[$key]))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "PossibleC2Beaconing" -Details @{
ProcessName = $process.ProcessName
PID = $owningPID
RemoteIP = $remoteIP
ConnectionCount = $script:NetworkAnomalyConnectionBaseline[$key]
} -Severity "Critical"
}
}
} catch {}
}
$monitoringConnections = $connections | Where-Object { $_.RemoteAddress -match "monitoring|telemetry|analytics" }
if ($monitoringConnections) {
$msg = "SUSPICIOUS: Process attempting to access monitoring infrastructure"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
}
} catch {
try {
Write-ErrorLog -Message "Network anomaly detector tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
function Start-RootkitDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RootkitScanIntervalSeconds) { [int]$Script:ManagedJobConfig.RootkitScanIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "RootkitDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-RootkitDetectorTick
}
Write-Log "[+] Rootkit detector (managed job) registered"
}
function Invoke-RootkitDetectorTick {
try {
$logFile = "$Base\rootkit_detections.log"
# Check for hidden processes (compare Task Manager vs Get-Process)
$cimProcesses = Get-CimInstance Win32_Process | Select-Object -ExpandProperty ProcessId
$psProcesses = Get-Process | Select-Object -ExpandProperty Id
$hiddenPIDs = $cimProcesses | Where-Object { $_ -notin $psProcesses }
if ($hiddenPIDs) {
foreach ($processId in $hiddenPIDs) {
try {
$process = Get-CimInstance Win32_Process -Filter "ProcessId = $processId"
$msg = "CRITICAL: Hidden process detected: $($process.Name) (PID: $processId) - Possible rootkit"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "HiddenProcess" -Details @{ ProcessName = $process.Name; PID = $processId } -Severity "Critical"
} catch {}
}
}
# Check for suspicious drivers
$drivers = Get-CimInstance Win32_SystemDriver | Where-Object { $_.State -eq "Running" }
foreach ($driver in $drivers) {
try {
# Check if driver file exists (some rootkits use phantom drivers)
$driverPath = $driver.PathName -replace '"', ''
if ($driverPath -and -not (Test-Path $driverPath)) {
$msg = "CRITICAL: Phantom driver detected: $($driver.Name) - Path does not exist: $driverPath"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "PhantomDriver" -Details @{ DriverName = $driver.Name; Path = $driverPath } -Severity "Critical"
}
# Check if driver is unsigned
if ($driverPath -and (Test-Path $driverPath)) {
$sig = Get-AuthenticodeSignature -FilePath $driverPath -ErrorAction SilentlyContinue
if ($sig -and $sig.Status -ne "Valid") {
$msg = "WARNING: Unsigned/invalid driver: $($driver.Name) at $driverPath"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "UnsignedDriver" -Details @{ DriverName = $driver.Name; Path = $driverPath; SignatureStatus = $sig.Status } -Severity "High"
}
}
} catch {}
}
# Check for SSDT hooks (System Service Descriptor Table) - advanced rootkit detection
# This is a simplified check - would need kernel access for full SSDT inspection
$systemProcesses = Get-Process | Where-Object { $_.ProcessName -match "^(system|smss|csrss|wininit|services|lsass|svchost)$" }
foreach ($proc in $systemProcesses) {
try {
$modules = Get-Process -Id $proc.Id -Module -ErrorAction SilentlyContinue
$suspiciousModules = $modules | Where-Object {
$_.FileName -notmatch "\\Windows\\System32\\" -and
$_.FileName -notmatch "\\Windows\\SysWOW64\\"
}
if ($suspiciousModules) {
$msg = "CRITICAL: Suspicious module in system process $($proc.ProcessName): $($suspiciousModules[0].FileName)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SystemProcessHook" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
SuspiciousModule = $suspiciousModules[0].FileName
} -Severity "Critical"
}
} catch {}
}
} catch {
try {
Write-ErrorLog -Message "Rootkit detector tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
function Start-FileSystemIntegrityMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.FileSystemIntegrityIntervalSeconds) { [int]$Script:ManagedJobConfig.FileSystemIntegrityIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "FileSystemIntegrityMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-FileSystemIntegrityMonitorTick
}
Write-Log "[+] Filesystem integrity monitor (managed job) registered"
}
function Invoke-FileSystemIntegrityMonitorTick {
try {
if (-not $script:FileSystemIntegrityBaseline) {
$script:FileSystemIntegrityBaseline = @{}
}
if (-not $script:FileSystemIntegrityCriticalPaths) {
$script:FileSystemIntegrityCriticalPaths = @(
"$env:SystemRoot\System32\drivers\etc\hosts",
"$env:SystemRoot\System32\config\SAM",
"$env:SystemRoot\System32\config\SYSTEM",
"$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
)
}
$logFile = "$Base\filesystem_integrity.log"
# One-time baseline initialization
if (-not $script:FileSystemIntegrityBaselineInitialized) {
foreach ($path in $script:FileSystemIntegrityCriticalPaths) {
try {
if (Test-Path $path) {
if ((Get-Item $path).PSIsContainer) {
$files = Get-ChildItem -Path $path -File -ErrorAction SilentlyContinue
foreach ($file in $files) {
try {
$script:FileSystemIntegrityBaseline[$file.FullName] = (Get-FileHash -Path $file.FullName -Algorithm SHA256).Hash
} catch {}
}
} else {
$script:FileSystemIntegrityBaseline[$path] = (Get-FileHash -Path $path -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
}
}
} catch {}
}
$script:FileSystemIntegrityBaselineInitialized = $true
return
}
foreach ($path in @($script:FileSystemIntegrityBaseline.Keys)) {
try {
if (Test-Path $path) {
$currentHash = (Get-FileHash -Path $path -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
if ($currentHash -ne $script:FileSystemIntegrityBaseline[$path]) {
$msg = "CRITICAL: Critical system file modified: $path"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "CriticalFileModified" -Details @{
FilePath = $path
OriginalHash = $script:FileSystemIntegrityBaseline[$path]
CurrentHash = $currentHash
} -Severity "Critical"
# Update baseline
$script:FileSystemIntegrityBaseline[$path] = $currentHash
}
}
} catch {}
}
} catch {
try {
Write-ErrorLog -Message "Filesystem integrity monitor tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
function Start-COMControlMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.COMControlIntervalSeconds) { [int]$Script:ManagedJobConfig.COMControlIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "COMControlMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-COMControlMonitorTick
}
Write-Log "[+] COM control monitor (managed job) registered"
}
function Invoke-COMControlMonitorTick {
try {
# Whitelist for legitimate Windows system paths
$whitelistedPaths = @(
"C:\Windows\System32\",
"C:\Windows\SysWOW64\",
"C:\Windows\WinSxS\",
"C:\Program Files\",
"C:\Program Files (x86)\"
)
$basePaths = @(
"HKLM:\SOFTWARE\WOW6432Node\Classes\CLSID",
"HKLM:\SOFTWARE\Classes\CLSID"
)
foreach ($basePath in $basePaths) {
try {
if (Test-Path $basePath) {
Get-ChildItem -Path $basePath | Where-Object {
$_.PSChildName -match "\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"
} | ForEach-Object {
try {
$clsid = $_.PSChildName
$clsidPath = Join-Path $basePath $clsid
@("InProcServer32", "InprocHandler32") | ForEach-Object {
try {
$subKeyPath = Join-Path $clsidPath $_
if (Test-Path $subKeyPath) {
$dllPath = (Get-ItemProperty -Path $subKeyPath -Name "(Default)" -ErrorAction SilentlyContinue)."(Default)"
if ($dllPath -and (Test-Path $dllPath)) {
# Check if DLL is in a whitelisted path
$isWhitelisted = $false
foreach ($whitePath in $whitelistedPaths) {
if ($dllPath -like "$whitePath*") {
$isWhitelisted = $true
break
}
}
# Only flag if NOT whitelisted AND matches suspicious patterns
if (-not $isWhitelisted -and ($dllPath -match "\\temp\\|\\downloads\\|\\public\\" -or (Get-Item $dllPath).Length -lt 100KB)) {
Add-Content -Path $Script:logFile -Value "REMOVING malicious COM control: $dllPath" -Encoding UTF8
Write-SecurityEvent -EventType "MaliciousCOMControlRemoved" -Details @{ Path = $dllPath } -Severity "High"
Remove-Item -Path $clsidPath -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path $dllPath -Force -ErrorAction SilentlyContinue
}
}
}
} catch {}
}
} catch {}
}
}
} catch {}
}
} catch {
try {
Write-ErrorLog -Message "COM control monitor tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
function Invoke-MalwareScan {
try {
Get-Process | Where-Object {
try {
-not (Test-ProtectedOrSelf $_) -and
-not (Test-CriticalSystemProcess $_) -and
-not (Test-IsWindowsNetworkingService -ProcessId $_.Id)
} catch { $false }
} | ForEach-Object {
try {
$proc = $_
$procName = $proc.ProcessName.ToLower()
# Fileless malware detection
if ($procName -match "powershell|cmd") {
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($commandLine -and ($commandLine -match "-encodedcommand|-enc|invoke-expression|downloadstring|iex|-executionpolicy bypass")) {
Write-Log "[DETECTED] Fileless malware: $($proc.ProcessName) (PID: $($proc.Id))"
Write-SecurityEvent -EventType "FilelessMalwareDetected" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; CommandLine = $commandLine } -Severity "Critical"
Invoke-QuarantineProcess -Process $proc -Reason "Fileless malware execution"
}
}
} catch {}
}
} catch {}
}
function Invoke-LogRotation {
try {
if ((Get-Item $Script:logFile -ErrorAction SilentlyContinue).Length -gt 50MB) {
$backupLog = "$($Script:logFile).old"
if (Test-Path $backupLog) {
Remove-Item $backupLog -Force
}
Move-Item $Script:logFile $backupLog -Force
Write-Log "[*] Log rotated due to size"
}
} catch {}
}
# ============================================
# ENTERPRISE EDR: AMSI Bypass Detection
# ============================================
function Start-AMSIBypassDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.AMSIBypassIntervalSeconds) { [int]$Script:ManagedJobConfig.AMSIBypassIntervalSeconds } else { 15 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "AMSIBypassDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-AMSIBypassDetectorTick
}
Write-Log "[+] AMSI bypass detector (managed job) registered"
}
function Invoke-AMSIBypassDetectorTick {
try {
$logFile = "$Base\amsi_bypass_detections.log"
# Check for AMSI.dll being unloaded or tampered
$amsiDllPath = "$env:SystemRoot\System32\amsi.dll"
if (-not (Test-Path $amsiDllPath)) {
$msg = "CRITICAL: AMSI.dll missing from System32 - possible AMSI bypass"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "AMSIDllMissing" -Details @{ ExpectedPath = $amsiDllPath } -Severity "Critical"
}
# Check PowerShell processes for AMSI bypass indicators
$psProcesses = Get-Process -Name "powershell", "pwsh" -ErrorAction SilentlyContinue
foreach ($proc in $psProcesses) {
try {
# Skip our own process
if ($proc.Id -eq $PID) { continue }
# Check if amsi.dll is loaded in the PowerShell process
$modules = $proc.Modules | Select-Object -ExpandProperty ModuleName -ErrorAction SilentlyContinue
$amsiLoaded = $modules -contains "amsi.dll"
if (-not $amsiLoaded) {
$msg = "CRITICAL: PowerShell process (PID: $($proc.Id)) running without AMSI - likely bypass attack"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "AMSIBypassDetected" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
StartTime = $proc.StartTime
} -Severity "Critical"
# Terminate the suspicious process
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
$msg = "KILLED: Terminated PowerShell process with AMSI bypass (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
} catch {}
}
# Check for common AMSI bypass patterns in command line
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine) {
$bypassPatterns = @(
'AmsiScanBuffer',
'AmsiInitFailed',
'amsiContext',
'amsiSession',
'\[Ref\]\.Assembly\.GetType',
'System\.Management\.Automation\.AmsiUtils',
'amsi\.dll.*VirtualProtect',
'SetValue\(.*NonPublic.*amsi'
)
foreach ($pattern in $bypassPatterns) {
if ($cmdLine -match $pattern) {
$msg = "CRITICAL: AMSI bypass attempt detected in PowerShell (PID: $($proc.Id)) - Pattern: $pattern"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "AMSIBypassAttempt" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Pattern = $pattern
CommandLine = $cmdLine.Substring(0, [Math]::Min(500, $cmdLine.Length))
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
} catch {}
break
}
}
}
} catch {}
}
# Check for ETW tampering (often done alongside AMSI bypass)
$etwPatterns = @(
'EtwEventWrite',
'NtTraceEvent',
'EventWrite.*patch'
)
foreach ($proc in $psProcesses) {
try {
if ($proc.Id -eq $PID) { continue }
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine) {
foreach ($pattern in $etwPatterns) {
if ($cmdLine -match $pattern) {
$msg = "CRITICAL: ETW tampering detected in PowerShell (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ETWTamperingDetected" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Pattern = $pattern
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
} catch {}
break
}
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "AMSI bypass detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Credential Dumping Detection
# ============================================
function Start-CredentialDumpingDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.CredentialDumpingIntervalSeconds) { [int]$Script:ManagedJobConfig.CredentialDumpingIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "CredentialDumpingDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-CredentialDumpingDetectorTick
}
Write-Log "[+] Credential dumping detector (managed job) registered"
}
function Invoke-CredentialDumpingDetectorTick {
try {
$logFile = "$Base\credential_dumping_detections.log"
# Get LSASS process
$lsass = Get-Process -Name "lsass" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $lsass) { return }
# Known credential dumping tools
$credDumpTools = @(
'mimikatz',
'procdump',
'sqldumper',
'comsvcs',
'rundll32.*comsvcs.*MiniDump',
'sekurlsa',
'lsadump',
'wce\.exe',
'gsecdump',
'pwdump',
'fgdump',
'lazagne',
'crackmapexec',
'pypykatz',
'nanodump',
'handlekatz',
'physmem2profit'
)
# Check all running processes for credential dumping indicators
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
# Skip system processes and self
if ($proc.Id -le 4 -or $proc.Id -eq $PID) { continue }
$procNameLower = $proc.ProcessName.ToLower()
$cmdLine = $null
# Check process name against known tools
foreach ($tool in $credDumpTools) {
if ($procNameLower -match $tool) {
$msg = "CRITICAL: Known credential dumping tool detected: $($proc.ProcessName) (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "CredentialDumpingToolDetected" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Tool = $tool
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: $($proc.ProcessName)"
} catch {}
break
}
}
# Check command line for credential dumping patterns
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine) {
$suspiciousPatterns = @(
'sekurlsa::logonpasswords',
'lsadump::sam',
'lsadump::dcsync',
'privilege::debug',
'token::elevate',
'-ma lsass',
'MiniDump.*lsass',
'comsvcs\.dll.*MiniDump',
'Out-Minidump',
'Get-Process.*lsass.*\.DumpFile',
'procdump.*-ma.*lsass',
'rundll32.*comsvcs.*24'
)
foreach ($pattern in $suspiciousPatterns) {
if ($cmdLine -match $pattern) {
$msg = "CRITICAL: Credential dumping command detected: $($proc.ProcessName) (PID: $($proc.Id)) - Pattern: $pattern"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "CredentialDumpingCommand" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Pattern = $pattern
CommandLine = $cmdLine.Substring(0, [Math]::Min(500, $cmdLine.Length))
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
} catch {}
break
}
}
}
# Check for processes with handles to LSASS (potential credential access)
# This is a simplified check - full implementation would use NtQuerySystemInformation
if ($procNameLower -notin @('csrss', 'wininit', 'services', 'svchost', 'mrt', 'taskmgr', 'procexp', 'procexp64', 'msmpeng')) {
try {
$procPath = $proc.Path
if ($procPath -and (Test-Path $procPath)) {
$sig = Get-AuthenticodeSignature -FilePath $procPath -ErrorAction SilentlyContinue
$isUnsigned = (-not $sig) -or ($sig.Status -ne "Valid")
# Check if this unsigned process has debug privileges (common for credential dumping)
if ($isUnsigned) {
# This is a heuristic - unsigned process with suspicious name patterns
if ($procNameLower -match '^[a-f0-9]{8,}$|^tmp|^temp|\.tmp$') {
$msg = "WARNING: Unsigned process with suspicious name detected: $($proc.ProcessName) (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousUnsignedProcess" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Path = $procPath
} -Severity "High"
}
}
}
} catch {}
}
} catch {}
}
# Check for SAM/SYSTEM/SECURITY hive access attempts
$hiveAccessPatterns = @(
'reg.*save.*sam',
'reg.*save.*system',
'reg.*save.*security',
'vssadmin.*shadow',
'wmic.*shadowcopy',
'ntdsutil.*ifm',
'esentutl.*ntds'
)
foreach ($proc in $processes) {
try {
if ($proc.Id -eq $PID) { continue }
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine) {
foreach ($pattern in $hiveAccessPatterns) {
if ($cmdLine -match $pattern) {
$msg = "CRITICAL: Registry hive extraction attempt: $($proc.ProcessName) (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "HiveExtractionAttempt" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Pattern = $pattern
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
} catch {}
break
}
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Credential dumping detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Parent-Child Process Anomaly Detection
# ============================================
function Start-ProcessAnomalyDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ProcessAnomalyIntervalSeconds) { [int]$Script:ManagedJobConfig.ProcessAnomalyIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize known-good parent-child relationships
if (-not $script:ProcessAnomalyAllowedChains) {
$script:ProcessAnomalyAllowedChains = @{
'explorer.exe' = @('cmd.exe', 'powershell.exe', 'pwsh.exe', 'notepad.exe', 'mspaint.exe', 'calc.exe', 'chrome.exe', 'msedge.exe', 'firefox.exe', 'code.exe', 'devenv.exe')
'services.exe' = @('svchost.exe', 'spoolsv.exe', 'msdtc.exe', 'vds.exe', 'vmtoolsd.exe', 'vmwaretray.exe')
'svchost.exe' = @('wuauclt.exe', 'wermgr.exe', 'taskhostw.exe', 'sihost.exe', 'ctfmon.exe', 'RuntimeBroker.exe', 'dllhost.exe')
'cmd.exe' = @('conhost.exe', 'find.exe', 'findstr.exe', 'sort.exe', 'more.com', 'tree.com')
'powershell.exe' = @('conhost.exe')
'pwsh.exe' = @('conhost.exe')
'winword.exe' = @() # Word should NOT spawn cmd/powershell
'excel.exe' = @() # Excel should NOT spawn cmd/powershell
'outlook.exe' = @() # Outlook should NOT spawn cmd/powershell
'acrord32.exe' = @() # Adobe Reader should NOT spawn cmd/powershell
}
}
# Suspicious parent-child combinations (high confidence malicious)
if (-not $script:ProcessAnomalySuspiciousChains) {
$script:ProcessAnomalySuspiciousChains = @(
@{ Parent = 'winword.exe'; Child = 'cmd.exe' },
@{ Parent = 'winword.exe'; Child = 'powershell.exe' },
@{ Parent = 'winword.exe'; Child = 'pwsh.exe' },
@{ Parent = 'winword.exe'; Child = 'wscript.exe' },
@{ Parent = 'winword.exe'; Child = 'cscript.exe' },
@{ Parent = 'winword.exe'; Child = 'mshta.exe' },
@{ Parent = 'excel.exe'; Child = 'cmd.exe' },
@{ Parent = 'excel.exe'; Child = 'powershell.exe' },
@{ Parent = 'excel.exe'; Child = 'pwsh.exe' },
@{ Parent = 'excel.exe'; Child = 'wscript.exe' },
@{ Parent = 'excel.exe'; Child = 'mshta.exe' },
@{ Parent = 'outlook.exe'; Child = 'cmd.exe' },
@{ Parent = 'outlook.exe'; Child = 'powershell.exe' },
@{ Parent = 'outlook.exe'; Child = 'pwsh.exe' },
@{ Parent = 'acrord32.exe'; Child = 'cmd.exe' },
@{ Parent = 'acrord32.exe'; Child = 'powershell.exe' },
@{ Parent = 'wmiprvse.exe'; Child = 'powershell.exe' },
@{ Parent = 'wmiprvse.exe'; Child = 'cmd.exe' },
@{ Parent = 'mshta.exe'; Child = 'powershell.exe' },
@{ Parent = 'mshta.exe'; Child = 'cmd.exe' },
@{ Parent = 'wscript.exe'; Child = 'powershell.exe' },
@{ Parent = 'wscript.exe'; Child = 'cmd.exe' },
@{ Parent = 'cscript.exe'; Child = 'powershell.exe' },
@{ Parent = 'cscript.exe'; Child = 'cmd.exe' },
@{ Parent = 'rundll32.exe'; Child = 'powershell.exe' },
@{ Parent = 'rundll32.exe'; Child = 'cmd.exe' },
@{ Parent = 'regsvr32.exe'; Child = 'powershell.exe' },
@{ Parent = 'regsvr32.exe'; Child = 'cmd.exe' }
)
}
Register-ManagedJob -Name "ProcessAnomalyDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ProcessAnomalyDetectorTick
}
Write-Log "[+] Process anomaly detector (managed job) registered"
}
function Invoke-ProcessAnomalyDetectorTick {
try {
$logFile = "$Base\process_anomaly_detections.log"
# Get all processes with parent info
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Select-Object ProcessId, Name, ParentProcessId, CommandLine, CreationDate
foreach ($proc in $processes) {
try {
# Skip system processes
if ($proc.ProcessId -le 4) { continue }
$childName = $proc.Name.ToLower()
$parentProc = $processes | Where-Object { $_.ProcessId -eq $proc.ParentProcessId } | Select-Object -First 1
if (-not $parentProc) { continue }
$parentName = $parentProc.Name.ToLower()
# Check against suspicious chains
foreach ($chain in $script:ProcessAnomalySuspiciousChains) {
if ($parentName -eq $chain.Parent -and $childName -eq $chain.Child) {
$msg = "CRITICAL: Suspicious process chain detected: $parentName -> $childName (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousProcessChain" -Details @{
ParentProcess = $parentName
ChildProcess = $childName
ChildPID = $proc.ProcessId
ParentPID = $proc.ParentProcessId
CommandLine = if ($proc.CommandLine) { $proc.CommandLine.Substring(0, [Math]::Min(500, $proc.CommandLine.Length)) } else { "N/A" }
} -Severity "Critical"
# Terminate the suspicious child process
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: $childName (PID: $($proc.ProcessId))"
} catch {}
break
}
}
# Check for LOLBin abuse patterns
$lolbins = @('certutil.exe', 'bitsadmin.exe', 'mshta.exe', 'regsvr32.exe', 'rundll32.exe',
'wmic.exe', 'cmstp.exe', 'msiexec.exe', 'installutil.exe', 'regasm.exe',
'regsvcs.exe', 'msconfig.exe', 'msbuild.exe', 'csc.exe', 'vbc.exe')
if ($childName -in $lolbins -and $proc.CommandLine) {
$cmdLine = $proc.CommandLine.ToLower()
# Suspicious LOLBin usage patterns
$suspiciousUsage = $false
$reason = ""
if ($childName -eq 'certutil.exe' -and $cmdLine -match '-urlcache|-decode|-encode|http://|https://') {
$suspiciousUsage = $true
$reason = "CertUtil used for download/decode"
}
elseif ($childName -eq 'bitsadmin.exe' -and $cmdLine -match '/transfer|/create|http://|https://') {
$suspiciousUsage = $true
$reason = "BitsAdmin used for download"
}
elseif ($childName -eq 'mshta.exe' -and $cmdLine -match 'javascript:|vbscript:|http://|https://') {
$suspiciousUsage = $true
$reason = "MSHTA executing remote/script content"
}
elseif ($childName -eq 'regsvr32.exe' -and $cmdLine -match '/s /n /u /i:|scrobj\.dll|http://|https://') {
$suspiciousUsage = $true
$reason = "RegSvr32 Squiblydoo attack"
}
elseif ($childName -eq 'rundll32.exe' -and $cmdLine -match 'javascript:|http://|https://|shell32\.dll.*shellexec') {
$suspiciousUsage = $true
$reason = "RunDLL32 abuse"
}
elseif ($childName -eq 'wmic.exe' -and $cmdLine -match 'process call create|/node:|http://|https://') {
$suspiciousUsage = $true
$reason = "WMIC remote execution"
}
elseif ($childName -eq 'msbuild.exe' -and $cmdLine -match '\.xml|\.csproj' -and $parentName -notin @('devenv.exe', 'msbuild.exe')) {
$suspiciousUsage = $true
$reason = "MSBuild inline task execution"
}
if ($suspiciousUsage) {
$msg = "CRITICAL: LOLBin abuse detected: $childName - $reason (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "LOLBinAbuse" -Details @{
Process = $childName
PID = $proc.ProcessId
Reason = $reason
CommandLine = $proc.CommandLine.Substring(0, [Math]::Min(500, $proc.CommandLine.Length))
} -Severity "Critical"
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
} catch {}
}
}
# Detect unusual svchost.exe spawning (should only be spawned by services.exe)
if ($childName -eq 'svchost.exe' -and $parentName -ne 'services.exe') {
$msg = "CRITICAL: svchost.exe spawned by non-services.exe parent: $parentName (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SvchostAnomalousParent" -Details @{
ParentProcess = $parentName
ChildPID = $proc.ProcessId
ParentPID = $proc.ParentProcessId
} -Severity "Critical"
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
} catch {}
}
# Detect cmd/powershell spawned from unusual locations
if ($childName -in @('cmd.exe', 'powershell.exe', 'pwsh.exe')) {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
if ($procObj -and $procObj.Path) {
$expectedPaths = @(
"$env:SystemRoot\System32\cmd.exe",
"$env:SystemRoot\SysWOW64\cmd.exe",
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe",
"$env:SystemRoot\SysWOW64\WindowsPowerShell\v1.0\powershell.exe",
"$env:ProgramFiles\PowerShell\7\pwsh.exe",
"${env:ProgramFiles(x86)}\PowerShell\7\pwsh.exe"
)
if ($procObj.Path -notin $expectedPaths) {
$msg = "CRITICAL: Shell running from unexpected location: $($procObj.Path) (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ShellUnexpectedLocation" -Details @{
Process = $childName
PID = $proc.ProcessId
Path = $procObj.Path
} -Severity "Critical"
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
} catch {}
}
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Process anomaly detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: WMI Persistence Detection
# ============================================
function Start-WMIPersistenceDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.WMIPersistenceIntervalSeconds) { [int]$Script:ManagedJobConfig.WMIPersistenceIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "WMIPersistenceDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-WMIPersistenceDetectorTick
}
Write-Log "[+] WMI persistence detector (managed job) registered"
}
function Invoke-WMIPersistenceDetectorTick {
try {
$logFile = "$Base\wmi_persistence_detections.log"
# Check for WMI Event Subscriptions (common APT persistence mechanism)
# These are used by malware like APT29, APT32, and many ransomware families
# Check __EventFilter (triggers)
$eventFilters = Get-CimInstance -Namespace "root\subscription" -ClassName "__EventFilter" -ErrorAction SilentlyContinue
foreach ($filter in $eventFilters) {
try {
$filterName = $filter.Name
$query = $filter.Query
# Known legitimate filters to skip
$legitimateFilters = @(
'SCM Event Log Filter',
'BVTFilter',
'TSLogonFilter',
'TSLogonEvents'
)
if ($filterName -in $legitimateFilters) { continue }
# Check for suspicious queries
$suspicious = $false
$reason = ""
if ($query -match 'Win32_ProcessStartTrace|Win32_ProcessStopTrace') {
$suspicious = $true
$reason = "Process monitoring filter (potential keylogger/spy)"
}
elseif ($query -match '__InstanceCreationEvent|__InstanceModificationEvent|__InstanceDeletionEvent') {
$suspicious = $true
$reason = "Instance event filter (potential persistence)"
}
elseif ($query -match 'Win32_LogonSession|Win32_LoggedOnUser') {
$suspicious = $true
$reason = "Logon monitoring filter"
}
elseif ($query -match 'SELECT.*FROM.*WHERE.*TargetInstance') {
$suspicious = $true
$reason = "Generic WMI event subscription"
}
if ($suspicious) {
$msg = "WARNING: Suspicious WMI EventFilter detected: $filterName - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousWMIEventFilter" -Details @{
FilterName = $filterName
Query = $query
Reason = $reason
} -Severity "High"
}
} catch {}
}
# Check __EventConsumer (actions)
$consumers = @()
$consumers += Get-CimInstance -Namespace "root\subscription" -ClassName "CommandLineEventConsumer" -ErrorAction SilentlyContinue
$consumers += Get-CimInstance -Namespace "root\subscription" -ClassName "ActiveScriptEventConsumer" -ErrorAction SilentlyContinue
foreach ($consumer in $consumers) {
try {
$consumerName = $consumer.Name
$consumerType = $consumer.CimClass.CimClassName
# CommandLineEventConsumer - executes commands
if ($consumerType -eq "CommandLineEventConsumer") {
$commandLine = $consumer.CommandLineTemplate
$executable = $consumer.ExecutablePath
$msg = "CRITICAL: WMI CommandLineEventConsumer detected: $consumerName"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " CommandLine: $commandLine"
Add-Content -Path $logFile -Value " Executable: $executable"
Write-SecurityEvent -EventType "WMICommandLineConsumer" -Details @{
ConsumerName = $consumerName
CommandLine = $commandLine
Executable = $executable
} -Severity "Critical"
# Remove the malicious consumer
try {
$consumer | Remove-CimInstance -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | REMOVED: $consumerName"
} catch {}
}
# ActiveScriptEventConsumer - executes scripts
if ($consumerType -eq "ActiveScriptEventConsumer") {
$scriptText = $consumer.ScriptText
$scriptFile = $consumer.ScriptFileName
$msg = "CRITICAL: WMI ActiveScriptEventConsumer detected: $consumerName"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "WMIActiveScriptConsumer" -Details @{
ConsumerName = $consumerName
ScriptFile = $scriptFile
ScriptTextLength = if ($scriptText) { $scriptText.Length } else { 0 }
} -Severity "Critical"
# Remove the malicious consumer
try {
$consumer | Remove-CimInstance -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | REMOVED: $consumerName"
} catch {}
}
} catch {}
}
# Check __FilterToConsumerBinding (links filters to consumers)
$bindings = Get-CimInstance -Namespace "root\subscription" -ClassName "__FilterToConsumerBinding" -ErrorAction SilentlyContinue
foreach ($binding in $bindings) {
try {
$filterPath = $binding.Filter
$consumerPath = $binding.Consumer
$msg = "WARNING: WMI FilterToConsumerBinding detected"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Filter: $filterPath"
Add-Content -Path $logFile -Value " Consumer: $consumerPath"
Write-SecurityEvent -EventType "WMIBinding" -Details @{
Filter = "$filterPath"
Consumer = "$consumerPath"
} -Severity "High"
# Remove suspicious bindings (be careful - some may be legitimate)
# Only remove if consumer is CommandLine or ActiveScript type
if ($consumerPath -match 'CommandLineEventConsumer|ActiveScriptEventConsumer') {
try {
$binding | Remove-CimInstance -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | REMOVED binding"
} catch {}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "WMI persistence detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Scheduled Task Abuse Detection
# ============================================
function Start-ScheduledTaskDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ScheduledTaskIntervalSeconds) { [int]$Script:ManagedJobConfig.ScheduledTaskIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize baseline of known tasks
if (-not $script:ScheduledTaskBaseline) {
$script:ScheduledTaskBaseline = @{}
}
Register-ManagedJob -Name "ScheduledTaskDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ScheduledTaskDetectorTick
}
Write-Log "[+] Scheduled task detector (managed job) registered"
}
function Invoke-ScheduledTaskDetectorTick {
try {
$logFile = "$Base\scheduled_task_detections.log"
# Get all scheduled tasks
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.State -ne 'Disabled' }
foreach ($task in $tasks) {
try {
$taskName = $task.TaskName
$taskPath = $task.TaskPath
$fullPath = "$taskPath$taskName"
# Skip Microsoft tasks (usually legitimate)
if ($taskPath -match '^\\Microsoft\\') { continue }
# Get task actions
$actions = $task.Actions
foreach ($action in $actions) {
try {
$execute = $action.Execute
$arguments = $action.Arguments
if (-not $execute) { continue }
$suspicious = $false
$reason = ""
# Check for suspicious executables
if ($execute -match 'powershell|pwsh|cmd\.exe|wscript|cscript|mshta|rundll32|regsvr32|certutil|bitsadmin') {
$suspicious = $true
$reason = "Task executes script interpreter or LOLBin: $execute"
}
# Check for suspicious arguments
if ($arguments) {
if ($arguments -match '-enc|-encodedcommand|downloadstring|invoke-expression|iex|http://|https://|bypass') {
$suspicious = $true
$reason = "Task has suspicious arguments"
}
if ($arguments -match '\\temp\\|\\tmp\\|\\appdata\\local\\temp') {
$suspicious = $true
$reason = "Task executes from temp directory"
}
}
# Check for tasks running from user-writable locations
if ($execute -match '\\Users\\|\\AppData\\|\\Temp\\|\\Downloads\\') {
$suspicious = $true
$reason = "Task executes from user-writable location"
}
# Check for hidden/encoded PowerShell
if ($arguments -match '-w hidden|-windowstyle hidden|hidden -') {
$suspicious = $true
$reason = "Task runs hidden PowerShell"
}
# Check if this is a new task (not in baseline)
$isNew = $false
if (-not $script:ScheduledTaskBaseline.ContainsKey($fullPath)) {
$script:ScheduledTaskBaseline[$fullPath] = @{
Execute = $execute
Arguments = $arguments
FirstSeen = Get-Date
}
$isNew = $true
# New tasks are suspicious if they match any pattern
if ($execute -match 'powershell|cmd|wscript|mshta') {
$suspicious = $true
$reason = "Newly created task with script interpreter"
}
}
if ($suspicious) {
$msg = "CRITICAL: Suspicious scheduled task detected: $fullPath - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Execute: $execute"
Add-Content -Path $logFile -Value " Arguments: $arguments"
Add-Content -Path $logFile -Value " IsNew: $isNew"
Write-SecurityEvent -EventType "SuspiciousScheduledTask" -Details @{
TaskName = $taskName
TaskPath = $taskPath
Execute = $execute
Arguments = if ($arguments) { $arguments.Substring(0, [Math]::Min(500, $arguments.Length)) } else { "N/A" }
Reason = $reason
IsNew = $isNew
} -Severity "Critical"
# Disable the suspicious task
try {
Disable-ScheduledTask -TaskName $taskName -TaskPath $taskPath -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | DISABLED: $fullPath"
} catch {}
}
} catch {}
}
} catch {}
}
# Cap baseline size
if ($script:ScheduledTaskBaseline.Count -gt 1000) {
$keysToRemove = @($script:ScheduledTaskBaseline.Keys | Select-Object -First 200)
foreach ($k in $keysToRemove) {
$script:ScheduledTaskBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Scheduled task detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: DNS Exfiltration Detection
# ============================================
function Start-DNSExfiltrationDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.DNSExfiltrationIntervalSeconds) { [int]$Script:ManagedJobConfig.DNSExfiltrationIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize DNS query tracking
if (-not $script:DNSQueryTracking) {
$script:DNSQueryTracking = @{}
}
Register-ManagedJob -Name "DNSExfiltrationDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-DNSExfiltrationDetectorTick
}
Write-Log "[+] DNS exfiltration detector (managed job) registered"
}
function Invoke-DNSExfiltrationDetectorTick {
try {
$logFile = "$Base\dns_exfiltration_detections.log"
# Get DNS client cache - this shows recent DNS queries
$dnsCache = Get-DnsClientCache -ErrorAction SilentlyContinue
foreach ($entry in $dnsCache) {
try {
$name = $entry.Entry
$recordType = $entry.Type
if (-not $name) { continue }
# Skip common legitimate domains
$legitimateDomains = @(
'microsoft.com', 'windows.com', 'windowsupdate.com', 'msftconnecttest.com',
'google.com', 'googleapis.com', 'gstatic.com',
'cloudflare.com', 'cloudflare-dns.com',
'amazon.com', 'amazonaws.com',
'apple.com', 'icloud.com',
'facebook.com', 'fbcdn.net',
'akamai.net', 'akamaiedge.net',
'office.com', 'office365.com', 'outlook.com',
'live.com', 'msn.com', 'bing.com',
'github.com', 'githubusercontent.com'
)
$isLegitimate = $false
foreach ($legit in $legitimateDomains) {
if ($name -match [regex]::Escape($legit) + '$') {
$isLegitimate = $true
break
}
}
if ($isLegitimate) { continue }
$suspicious = $false
$reason = ""
# Check for unusually long subdomain (potential data exfiltration)
$parts = $name.Split('.')
if ($parts.Count -gt 2) {
$subdomain = $parts[0..($parts.Count - 3)] -join '.'
if ($subdomain.Length -gt 50) {
$suspicious = $true
$reason = "Unusually long subdomain (potential DNS tunneling/exfiltration)"
}
# Check for base64-like patterns in subdomain
if ($subdomain -match '^[A-Za-z0-9+/=]{20,}$') {
$suspicious = $true
$reason = "Base64-like subdomain pattern (potential encoded data)"
}
# Check for hex-encoded data
if ($subdomain -match '^[0-9a-fA-F]{32,}$') {
$suspicious = $true
$reason = "Hex-encoded subdomain (potential data exfiltration)"
}
}
# Track query frequency per domain
$baseDomain = if ($parts.Count -ge 2) { "$($parts[-2]).$($parts[-1])" } else { $name }
if (-not $script:DNSQueryTracking.ContainsKey($baseDomain)) {
$script:DNSQueryTracking[$baseDomain] = @{
Count = 1
FirstSeen = Get-Date
LastSeen = Get-Date
UniqueSubdomains = @($name)
}
} else {
$script:DNSQueryTracking[$baseDomain].Count++
$script:DNSQueryTracking[$baseDomain].LastSeen = Get-Date
if ($name -notin $script:DNSQueryTracking[$baseDomain].UniqueSubdomains) {
if ($script:DNSQueryTracking[$baseDomain].UniqueSubdomains.Count -lt 100) {
$script:DNSQueryTracking[$baseDomain].UniqueSubdomains += $name
}
}
# High frequency to single domain with many unique subdomains = suspicious
$tracking = $script:DNSQueryTracking[$baseDomain]
$timeSpan = (Get-Date) - $tracking.FirstSeen
if ($timeSpan.TotalMinutes -gt 0) {
$queriesPerMinute = $tracking.Count / $timeSpan.TotalMinutes
$uniqueSubdomainCount = $tracking.UniqueSubdomains.Count
if ($queriesPerMinute -gt 10 -and $uniqueSubdomainCount -gt 20) {
$suspicious = $true
$reason = "High DNS query rate with many unique subdomains (potential DNS tunneling)"
}
}
}
# Check for TXT record queries (often used for C2)
if ($recordType -eq 'TXT' -or $recordType -eq 16) {
$suspicious = $true
$reason = "TXT record query (commonly used for DNS C2)"
}
if ($suspicious) {
$msg = "WARNING: Suspicious DNS activity detected: $name - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousDNSActivity" -Details @{
DomainName = $name
RecordType = $recordType
Reason = $reason
} -Severity "High"
}
} catch {}
}
# Cap tracking dictionary size
if ($script:DNSQueryTracking.Count -gt 500) {
$keysToRemove = @($script:DNSQueryTracking.Keys | Select-Object -First 100)
foreach ($k in $keysToRemove) {
$script:DNSQueryTracking.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "DNS exfiltration detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Named Pipe Monitoring (Lateral Movement Detection)
# ============================================
function Start-NamedPipeMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.NamedPipeIntervalSeconds) { [int]$Script:ManagedJobConfig.NamedPipeIntervalSeconds } else { 15 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize known malicious pipe patterns
if (-not $script:NamedPipeBaseline) {
$script:NamedPipeBaseline = @{}
}
Register-ManagedJob -Name "NamedPipeMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-NamedPipeMonitorTick
}
Write-Log "[+] Named pipe monitor (managed job) registered"
}
function Invoke-NamedPipeMonitorTick {
try {
$logFile = "$Base\named_pipe_detections.log"
# Known malicious named pipes used by attack tools
$maliciousPipePatterns = @(
# Cobalt Strike
'msagent_*',
'MSSE-*',
'postex_*',
'status_*',
'mojo.*',
'interprocess.*',
# Metasploit
'meterpreter*',
# PsExec and similar
'psexec*',
'paexec*',
'remcom*',
'csexec*',
# Mimikatz
'mimikatz*',
# Empire
'empire*',
# Generic C2
'*-stdin',
'*-stdout',
'*-stderr',
# Covenant
'covenant*',
# Sliver
'sliver*',
# Other common malware pipes
'isapi_*',
'sdp_*',
'winsock*',
'ntsvcs_*'
)
# Get all named pipes
$pipes = Get-ChildItem -Path "\\.\pipe\" -ErrorAction SilentlyContinue
foreach ($pipe in $pipes) {
try {
$pipeName = $pipe.Name
# Check against malicious patterns
foreach ($pattern in $maliciousPipePatterns) {
if ($pipeName -like $pattern) {
$msg = "CRITICAL: Suspicious named pipe detected: $pipeName (matches pattern: $pattern)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousNamedPipe" -Details @{
PipeName = $pipeName
Pattern = $pattern
} -Severity "Critical"
# Try to identify the owning process
try {
$handleOutput = & handle.exe -a -p $pipeName 2>$null
if ($handleOutput) {
Add-Content -Path $logFile -Value " Handle info: $handleOutput"
}
} catch {}
break
}
}
# Check for new pipes with suspicious characteristics
if (-not $script:NamedPipeBaseline.ContainsKey($pipeName)) {
$script:NamedPipeBaseline[$pipeName] = @{
FirstSeen = Get-Date
Alerted = $false
}
# Alert on pipes with random-looking names (potential C2)
if ($pipeName -match '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$') {
$msg = "WARNING: New GUID-named pipe detected (potential C2): $pipeName"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "GUIDNamedPipe" -Details @{ PipeName = $pipeName } -Severity "High"
}
elseif ($pipeName -match '^[a-zA-Z0-9]{32,}$') {
$msg = "WARNING: New long random-named pipe detected: $pipeName"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "RandomNamedPipe" -Details @{ PipeName = $pipeName } -Severity "Medium"
}
}
} catch {}
}
# Cap baseline size
if ($script:NamedPipeBaseline.Count -gt 500) {
$keysToRemove = @($script:NamedPipeBaseline.Keys | Select-Object -First 100)
foreach ($k in $keysToRemove) {
$script:NamedPipeBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Named pipe monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Registry Run Key Persistence Detection
# ============================================
function Start-RegistryPersistenceDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RegistryPersistenceIntervalSeconds) { [int]$Script:ManagedJobConfig.RegistryPersistenceIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize baseline
if (-not $script:RegistryPersistenceBaseline) {
$script:RegistryPersistenceBaseline = @{}
}
Register-ManagedJob -Name "RegistryPersistenceDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-RegistryPersistenceDetectorTick
}
Write-Log "[+] Registry persistence detector (managed job) registered"
}
function Invoke-RegistryPersistenceDetectorTick {
try {
$logFile = "$Base\registry_persistence_detections.log"
# Common persistence registry locations
$persistenceKeys = @(
# Run keys (most common)
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce",
# RunServices (legacy but still used)
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce",
# Winlogon
"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",
# Explorer Run
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
# Active Setup
"HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components",
# Shell folders
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
foreach ($keyPath in $persistenceKeys) {
try {
if (-not (Test-Path $keyPath)) { continue }
$values = Get-ItemProperty -Path $keyPath -ErrorAction SilentlyContinue
if (-not $values) { continue }
foreach ($prop in $values.PSObject.Properties) {
try {
# Skip PowerShell metadata properties
if ($prop.Name -in @('PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider')) { continue }
$valueName = $prop.Name
$valueData = $prop.Value
if (-not $valueData -or $valueData -isnot [string]) { continue }
$fullKey = "$keyPath\$valueName"
# Check if this is a new entry
$isNew = $false
if (-not $script:RegistryPersistenceBaseline.ContainsKey($fullKey)) {
$script:RegistryPersistenceBaseline[$fullKey] = @{
Value = $valueData
FirstSeen = Get-Date
}
$isNew = $true
} elseif ($script:RegistryPersistenceBaseline[$fullKey].Value -ne $valueData) {
# Value changed
$isNew = $true
$script:RegistryPersistenceBaseline[$fullKey].Value = $valueData
$script:RegistryPersistenceBaseline[$fullKey].Changed = Get-Date
}
if (-not $isNew) { continue }
# Analyze the value for suspicious patterns
$suspicious = $false
$reason = ""
# Check for suspicious executables
if ($valueData -match 'powershell|pwsh|cmd\.exe|wscript|cscript|mshta|rundll32|regsvr32') {
$suspicious = $true
$reason = "Script interpreter in persistence key"
}
# Check for encoded commands
if ($valueData -match '-enc|-encodedcommand|frombase64') {
$suspicious = $true
$reason = "Encoded command in persistence key"
}
# Check for download cradles
if ($valueData -match 'downloadstring|downloadfile|invoke-webrequest|wget|curl|http://|https://') {
$suspicious = $true
$reason = "Download cradle in persistence key"
}
# Check for temp/appdata paths
if ($valueData -match '\\temp\\|\\tmp\\|\\appdata\\local\\temp|\\downloads\\') {
$suspicious = $true
$reason = "Execution from temp/user directory"
}
# Check for hidden window
if ($valueData -match '-w hidden|-windowstyle hidden') {
$suspicious = $true
$reason = "Hidden window execution"
}
# Check for random-looking executable names
if ($valueData -match '\\[a-f0-9]{8,}\.exe') {
$suspicious = $true
$reason = "Random hex-named executable"
}
if ($suspicious) {
$msg = "CRITICAL: Suspicious registry persistence detected: $fullKey - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Value: $valueData"
Write-SecurityEvent -EventType "SuspiciousRegistryPersistence" -Details @{
KeyPath = $keyPath
ValueName = $valueName
ValueData = $valueData.Substring(0, [Math]::Min(500, $valueData.Length))
Reason = $reason
IsNew = $isNew
} -Severity "Critical"
# Remove the malicious entry
try {
Remove-ItemProperty -Path $keyPath -Name $valueName -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | REMOVED: $valueName from $keyPath"
} catch {}
}
} catch {}
}
} catch {}
}
# Cap baseline size
if ($script:RegistryPersistenceBaseline.Count -gt 1000) {
$keysToRemove = @($script:RegistryPersistenceBaseline.Keys | Select-Object -First 200)
foreach ($k in $keysToRemove) {
$script:RegistryPersistenceBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Registry persistence detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: PowerShell Script Block Logging Analysis
# ============================================
function Start-ScriptBlockAnalyzer {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ScriptBlockAnalyzerIntervalSeconds) { [int]$Script:ManagedJobConfig.ScriptBlockAnalyzerIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Track last event time to avoid re-processing
if (-not $script:ScriptBlockLastEventTime) {
$script:ScriptBlockLastEventTime = (Get-Date).AddMinutes(-5)
}
Register-ManagedJob -Name "ScriptBlockAnalyzer" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ScriptBlockAnalyzerTick
}
Write-Log "[+] Script block analyzer (managed job) registered"
}
function Invoke-ScriptBlockAnalyzerTick {
try {
$logFile = "$Base\scriptblock_analysis.log"
# Get recent PowerShell script block logging events (Event ID 4104)
$events = Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational'
Id = 4104
StartTime = $script:ScriptBlockLastEventTime
} -MaxEvents 50 -ErrorAction SilentlyContinue
if (-not $events) { return }
# Update last event time
$script:ScriptBlockLastEventTime = Get-Date
# Suspicious patterns to detect
$suspiciousPatterns = @(
@{ Pattern = 'Invoke-Mimikatz|sekurlsa|kerberos::'; Severity = 'Critical'; Reason = 'Mimikatz detected' },
@{ Pattern = 'Invoke-Empire|Invoke-PSInject'; Severity = 'Critical'; Reason = 'Empire framework detected' },
@{ Pattern = 'Invoke-Shellcode|Invoke-ReflectivePEInjection'; Severity = 'Critical'; Reason = 'Shellcode injection detected' },
@{ Pattern = 'Get-GPPPassword|Get-GPPAutologon'; Severity = 'Critical'; Reason = 'GPP password extraction' },
@{ Pattern = 'Invoke-Kerberoast|Invoke-ASREPRoast'; Severity = 'Critical'; Reason = 'Kerberos attack detected' },
@{ Pattern = 'Invoke-DCSync|lsadump::dcsync'; Severity = 'Critical'; Reason = 'DCSync attack detected' },
@{ Pattern = 'Invoke-TokenManipulation|Invoke-CredentialInjection'; Severity = 'Critical'; Reason = 'Token manipulation detected' },
@{ Pattern = 'AmsiScanBuffer|AmsiInitFailed|amsiContext'; Severity = 'Critical'; Reason = 'AMSI bypass attempt' },
@{ Pattern = 'Net\.WebClient.*DownloadString|Invoke-Expression.*http'; Severity = 'High'; Reason = 'Download and execute pattern' },
@{ Pattern = 'IEX.*\(.*New-Object.*Net\.WebClient'; Severity = 'High'; Reason = 'Classic download cradle' },
@{ Pattern = '\[System\.Reflection\.Assembly\]::Load'; Severity = 'High'; Reason = 'Reflective assembly loading' },
@{ Pattern = 'VirtualAlloc|VirtualProtect|CreateThread'; Severity = 'High'; Reason = 'Memory manipulation APIs' },
@{ Pattern = 'Add-MpPreference.*-ExclusionPath'; Severity = 'High'; Reason = 'Defender exclusion modification' },
@{ Pattern = 'Set-MpPreference.*-DisableRealtimeMonitoring'; Severity = 'Critical'; Reason = 'Defender disable attempt' },
@{ Pattern = 'Stop-Service.*WinDefend|sc.*stop.*WinDefend'; Severity = 'Critical'; Reason = 'Defender service stop attempt' },
@{ Pattern = '-bxor|-band.*0x|char\]\s*\d+'; Severity = 'Medium'; Reason = 'Obfuscation detected' },
@{ Pattern = 'FromBase64String|ToBase64String.*\|.*iex'; Severity = 'High'; Reason = 'Base64 decode and execute' },
@{ Pattern = 'Invoke-WMIMethod.*Create|Win32_Process.*Create'; Severity = 'High'; Reason = 'WMI process creation' },
@{ Pattern = 'Enter-PSSession|Invoke-Command.*-ComputerName'; Severity = 'Medium'; Reason = 'Remote PowerShell execution' },
@{ Pattern = 'reg.*add.*\\Run|New-ItemProperty.*\\Run'; Severity = 'High'; Reason = 'Registry persistence' }
)
foreach ($logEvent in $events) {
try {
$scriptBlock = $logEvent.Properties[2].Value
if (-not $scriptBlock) { continue }
# Skip our own script
if ($scriptBlock -match 'Antivirus\.ps1|Invoke-ScriptBlockAnalyzerTick') { continue }
foreach ($check in $suspiciousPatterns) {
if ($scriptBlock -match $check.Pattern) {
$msg = "$($check.Severity.ToUpper()): Suspicious PowerShell detected - $($check.Reason)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Pattern: $($check.Pattern)"
Add-Content -Path $logFile -Value " Script (truncated): $($scriptBlock.Substring(0, [Math]::Min(500, $scriptBlock.Length)))"
Write-SecurityEvent -EventType "SuspiciousScriptBlock" -Details @{
Pattern = $check.Pattern
Reason = $check.Reason
ScriptLength = $scriptBlock.Length
TimeCreated = $logEvent.TimeCreated
} -Severity $check.Severity
# For critical detections, try to find and kill the process
if ($check.Severity -eq 'Critical') {
try {
$psProcesses = Get-Process -Name "powershell", "pwsh" -ErrorAction SilentlyContinue |
Where-Object { $_.Id -ne $PID }
foreach ($proc in $psProcesses) {
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine -match $check.Pattern) {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: PowerShell PID $($proc.Id)"
}
}
} catch {}
}
break # Only report first match per script block
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Script block analyzer tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Service Creation/Modification Detection
# ============================================
function Start-ServiceMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ServiceMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.ServiceMonitorIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize service baseline
if (-not $script:ServiceBaseline) {
$script:ServiceBaseline = @{}
}
Register-ManagedJob -Name "ServiceMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ServiceMonitorTick
}
Write-Log "[+] Service monitor (managed job) registered"
}
function Invoke-ServiceMonitorTick {
try {
$logFile = "$Base\service_detections.log"
# Get all services
$services = Get-CimInstance Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
try {
$svcName = $svc.Name
$svcPath = $svc.PathName
$svcState = $svc.State
$svcStartMode = $svc.StartMode
if (-not $svcPath) { continue }
$svcKey = $svcName
# Check if this is a new or modified service
$isNew = $false
$isModified = $false
if (-not $script:ServiceBaseline.ContainsKey($svcKey)) {
$script:ServiceBaseline[$svcKey] = @{
PathName = $svcPath
StartMode = $svcStartMode
FirstSeen = Get-Date
}
$isNew = $true
} elseif ($script:ServiceBaseline[$svcKey].PathName -ne $svcPath) {
$isModified = $true
$script:ServiceBaseline[$svcKey].PathName = $svcPath
$script:ServiceBaseline[$svcKey].Modified = Get-Date
}
if (-not $isNew -and -not $isModified) { continue }
# Analyze for suspicious patterns
$suspicious = $false
$reason = ""
# Check for script interpreters in service path
if ($svcPath -match 'powershell|pwsh|cmd\.exe|wscript|cscript|mshta') {
$suspicious = $true
$reason = "Service uses script interpreter"
}
# Check for temp/user directories
if ($svcPath -match '\\temp\\|\\tmp\\|\\appdata\\|\\users\\.*\\downloads\\') {
$suspicious = $true
$reason = "Service binary in temp/user directory"
}
# Check for encoded commands
if ($svcPath -match '-enc|-encodedcommand|frombase64') {
$suspicious = $true
$reason = "Service uses encoded command"
}
# Check for suspicious service names
if ($svcName -match '^[a-f0-9]{8,}$|^svc[0-9]+$|^service[0-9]+$') {
$suspicious = $true
$reason = "Suspicious service name pattern"
}
# Check for unquoted service paths with spaces (vulnerability)
if ($svcPath -notmatch '^"' -and $svcPath -match ' ' -and $svcPath -match '\.exe') {
$suspicious = $true
$reason = "Unquoted service path with spaces (potential hijack)"
}
# Check for services running as SYSTEM from user directories
if ($svc.StartName -match 'LocalSystem|SYSTEM' -and $svcPath -match '\\Users\\') {
$suspicious = $true
$reason = "SYSTEM service running from user directory"
}
# Check for unsigned binaries
$exePath = $svcPath -replace '^"([^"]+)".*', '$1' -replace '\s+-.*$', ''
if ($exePath -and (Test-Path $exePath)) {
$sig = Get-AuthenticodeSignature -FilePath $exePath -ErrorAction SilentlyContinue
if ($sig -and $sig.Status -ne "Valid") {
if ($isNew) {
$suspicious = $true
$reason = "New service with unsigned/invalid binary"
}
}
}
if ($suspicious) {
$action = if ($isNew) { "NEW" } else { "MODIFIED" }
$msg = "CRITICAL: Suspicious service detected ($action): $svcName - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Path: $svcPath"
Add-Content -Path $logFile -Value " StartMode: $svcStartMode"
Write-SecurityEvent -EventType "SuspiciousService" -Details @{
ServiceName = $svcName
PathName = $svcPath
StartMode = $svcStartMode
Reason = $reason
IsNew = $isNew
IsModified = $isModified
} -Severity "Critical"
# Stop and disable suspicious new services
if ($isNew -and $svcState -eq 'Running') {
try {
Stop-Service -Name $svcName -Force -ErrorAction SilentlyContinue
Set-Service -Name $svcName -StartupType Disabled -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | STOPPED and DISABLED: $svcName"
} catch {}
}
}
} catch {}
}
# Cap baseline size
if ($script:ServiceBaseline.Count -gt 500) {
$keysToRemove = @($script:ServiceBaseline.Keys | Select-Object -First 100)
foreach ($k in $keysToRemove) {
$script:ServiceBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Service monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: DLL Search Order Hijacking Detection
# ============================================
function Start-DLLHijackDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.DLLHijackIntervalSeconds) { [int]$Script:ManagedJobConfig.DLLHijackIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "DLLHijackDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-DLLHijackDetectorTick
}
Write-Log "[+] DLL hijack detector (managed job) registered"
}
function Invoke-DLLHijackDetectorTick {
try {
$logFile = "$Base\dll_hijack_detections.log"
# Known DLLs that are commonly hijacked
$hijackableDLLs = @(
'version.dll', 'winmm.dll', 'wsock32.dll', 'wtsapi32.dll',
'dbghelp.dll', 'dbgcore.dll', 'faultrep.dll', 'msimg32.dll',
'oleacc.dll', 'profapi.dll', 'secur32.dll', 'userenv.dll',
'uxtheme.dll', 'wer.dll', 'winhttp.dll', 'winspool.drv',
'wow64log.dll', 'CRYPTSP.dll', 'CRYPTBASE.dll', 'dwmapi.dll',
'netapi32.dll', 'propsys.dll', 'WindowsCodecs.dll'
)
# Check common application directories for suspicious DLLs
$appDirs = @(
"$env:ProgramFiles",
"${env:ProgramFiles(x86)}",
"$env:LOCALAPPDATA",
"$env:APPDATA"
)
foreach ($appDir in $appDirs) {
if (-not (Test-Path $appDir)) { continue }
# Get subdirectories (application folders)
$subDirs = Get-ChildItem -Path $appDir -Directory -ErrorAction SilentlyContinue | Select-Object -First 50
foreach ($dir in $subDirs) {
try {
foreach ($dllName in $hijackableDLLs) {
$dllPath = Join-Path $dir.FullName $dllName
if (Test-Path $dllPath) {
# Check if this DLL should be here (compare with System32)
$systemDll = Join-Path "$env:SystemRoot\System32" $dllName
if (Test-Path $systemDll) {
# Compare signatures
$appSig = Get-AuthenticodeSignature -FilePath $dllPath -ErrorAction SilentlyContinue
$sysSig = Get-AuthenticodeSignature -FilePath $systemDll -ErrorAction SilentlyContinue
$suspicious = $false
$reason = ""
# Unsigned DLL in app directory
if (-not $appSig -or $appSig.Status -ne "Valid") {
$suspicious = $true
$reason = "Unsigned DLL in application directory"
}
# Different signer than system DLL
elseif ($sysSig -and $sysSig.Status -eq "Valid" -and
$appSig.SignerCertificate.Subject -ne $sysSig.SignerCertificate.Subject) {
$suspicious = $true
$reason = "DLL signed by different entity than system version"
}
if ($suspicious) {
$msg = "WARNING: Potential DLL hijack detected: $dllPath - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "PotentialDLLHijack" -Details @{
DLLPath = $dllPath
DLLName = $dllName
AppDirectory = $dir.FullName
Reason = $reason
SignatureStatus = $appSig.Status
} -Severity "High"
# Quarantine the suspicious DLL
try {
$quarantinePath = "$quarantineFolder\$($dllName)_$(Get-Date -Format 'yyyyMMddHHmmss').quarantined"
Move-Item -Path $dllPath -Destination $quarantinePath -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | QUARANTINED: $dllPath -> $quarantinePath"
} catch {}
}
}
}
}
} catch {}
}
}
# Check for DLLs in PATH directories that shouldn't have them
$pathDirs = $env:PATH -split ';' | Where-Object { $_ -and (Test-Path $_) }
foreach ($pathDir in $pathDirs) {
try {
# Skip system directories
if ($pathDir -match "$([regex]::Escape($env:SystemRoot))|$([regex]::Escape($env:ProgramFiles))") { continue }
foreach ($dllName in $hijackableDLLs) {
$dllPath = Join-Path $pathDir $dllName
if (Test-Path $dllPath) {
$sig = Get-AuthenticodeSignature -FilePath $dllPath -ErrorAction SilentlyContinue
if (-not $sig -or $sig.Status -ne "Valid") {
$msg = "CRITICAL: Unsigned system DLL in PATH directory: $dllPath"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "DLLInPATH" -Details @{
DLLPath = $dllPath
DLLName = $dllName
PathDirectory = $pathDir
} -Severity "Critical"
# Quarantine
try {
$quarantinePath = "$quarantineFolder\$($dllName)_$(Get-Date -Format 'yyyyMMddHHmmss').quarantined"
Move-Item -Path $dllPath -Destination $quarantinePath -Force -ErrorAction SilentlyContinue
} catch {}
}
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "DLL hijack detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Token/Privilege Manipulation Detection
# ============================================
function Start-TokenManipulationDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.TokenManipulationIntervalSeconds) { [int]$Script:ManagedJobConfig.TokenManipulationIntervalSeconds } else { 15 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "TokenManipulationDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-TokenManipulationDetectorTick
}
Write-Log "[+] Token manipulation detector (managed job) registered"
}
function Invoke-TokenManipulationDetectorTick {
try {
$logFile = "$Base\token_manipulation_detections.log"
# Get all processes
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
# Skip system processes and self
if ($proc.Id -le 4 -or $proc.Id -eq $PID) { continue }
$procName = $proc.ProcessName.ToLower()
# Skip known safe processes
if ($procName -in @('system', 'smss', 'csrss', 'wininit', 'services', 'lsass', 'svchost', 'explorer', 'dwm', 'taskhostw')) { continue }
# Get process token information via command line analysis
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine) {
$suspicious = $false
$reason = ""
# Check for token manipulation patterns
$tokenPatterns = @(
@{ Pattern = 'Invoke-TokenManipulation'; Reason = 'PowerShell token manipulation' },
@{ Pattern = 'Invoke-CredentialInjection'; Reason = 'Credential injection' },
@{ Pattern = 'Invoke-RevertToSelf'; Reason = 'Token revert manipulation' },
@{ Pattern = 'ImpersonateLoggedOnUser|ImpersonateNamedPipeClient'; Reason = 'Impersonation API usage' },
@{ Pattern = 'DuplicateToken|DuplicateTokenEx'; Reason = 'Token duplication' },
@{ Pattern = 'SetThreadToken|AdjustTokenPrivileges'; Reason = 'Token privilege adjustment' },
@{ Pattern = 'CreateProcessWithToken|CreateProcessAsUser'; Reason = 'Process creation with alternate token' },
@{ Pattern = 'LogonUser.*LOGON32_LOGON_NEW_CREDENTIALS'; Reason = 'Pass-the-hash style logon' },
@{ Pattern = 'runas\s+/netonly|runas\s+/savecred'; Reason = 'RunAs with credential options' },
@{ Pattern = 'incognito|list_tokens|impersonate_token'; Reason = 'Incognito/Meterpreter token commands' }
)
foreach ($check in $tokenPatterns) {
if ($cmdLine -match $check.Pattern) {
$suspicious = $true
$reason = $check.Reason
break
}
}
if ($suspicious) {
$msg = "CRITICAL: Token manipulation detected: $($proc.ProcessName) (PID: $($proc.Id)) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "TokenManipulation" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Reason = $reason
CommandLine = $cmdLine.Substring(0, [Math]::Min(500, $cmdLine.Length))
} -Severity "Critical"
# Kill the process
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: $($proc.ProcessName) (PID: $($proc.Id))"
} catch {}
}
}
# Check for processes running with unexpected privileges
# Look for non-admin processes that somehow have admin tokens
try {
$procPath = $proc.Path
if ($procPath -and (Test-Path $procPath)) {
# Check if process is from user directory but running elevated
if ($procPath -match '\\Users\\[^\\]+\\(AppData|Downloads|Desktop|Documents)\\') {
# This is a user-directory process - check if it's running as SYSTEM or admin
$owner = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue).GetOwner()
if ($owner -and $owner.User -eq 'SYSTEM') {
$msg = "CRITICAL: User-directory process running as SYSTEM: $procPath (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ElevatedUserProcess" -Details @{
ProcessName = $proc.ProcessName
PID = $proc.Id
Path = $procPath
Owner = 'SYSTEM'
} -Severity "Critical"
try {
Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior"
} catch {}
}
}
}
} catch {}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Token manipulation detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Startup/Autorun Location Monitor
# ============================================
function Start-StartupMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.StartupMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.StartupMonitorIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize startup baseline
if (-not $script:StartupBaseline) {
$script:StartupBaseline = @{}
}
Register-ManagedJob -Name "StartupMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-StartupMonitorTick
}
Write-Log "[+] Startup monitor (managed job) registered"
}
function Invoke-StartupMonitorTick {
try {
$logFile = "$Base\startup_detections.log"
# Startup folder locations
$startupFolders = @(
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup",
"$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
)
foreach ($folder in $startupFolders) {
if (-not (Test-Path $folder)) { continue }
$items = Get-ChildItem -Path $folder -File -ErrorAction SilentlyContinue
foreach ($item in $items) {
try {
$itemPath = $item.FullName
$itemKey = $itemPath.ToLower()
# Check if new
$isNew = $false
if (-not $script:StartupBaseline.ContainsKey($itemKey)) {
$script:StartupBaseline[$itemKey] = @{
Name = $item.Name
Hash = (Get-FileHash -Path $itemPath -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
FirstSeen = Get-Date
}
$isNew = $true
}
if (-not $isNew) { continue }
# Analyze new startup item
$suspicious = $false
$reason = ""
# Check file extension
$ext = $item.Extension.ToLower()
if ($ext -in @('.bat', '.cmd', '.vbs', '.vbe', '.js', '.jse', '.wsf', '.wsh', '.ps1', '.hta')) {
$suspicious = $true
$reason = "Script file in startup folder"
}
# Check for shortcuts pointing to suspicious locations
if ($ext -eq '.lnk') {
try {
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut($itemPath)
$targetPath = $shortcut.TargetPath
if ($targetPath -match 'powershell|pwsh|cmd\.exe|wscript|cscript|mshta') {
$suspicious = $true
$reason = "Shortcut to script interpreter"
}
elseif ($targetPath -match '\\temp\\|\\tmp\\|\\appdata\\local\\temp') {
$suspicious = $true
$reason = "Shortcut to temp directory"
}
elseif ($targetPath -and -not (Test-Path $targetPath)) {
$suspicious = $true
$reason = "Shortcut to non-existent target"
}
} catch {}
}
# Check for unsigned executables
if ($ext -eq '.exe') {
$sig = Get-AuthenticodeSignature -FilePath $itemPath -ErrorAction SilentlyContinue
if (-not $sig -or $sig.Status -ne "Valid") {
$suspicious = $true
$reason = "Unsigned executable in startup folder"
}
}
if ($suspicious) {
$msg = "CRITICAL: Suspicious startup item detected: $($item.Name) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousStartupItem" -Details @{
ItemPath = $itemPath
ItemName = $item.Name
Reason = $reason
Folder = $folder
} -Severity "Critical"
# Quarantine the item
try {
$quarantinePath = "$quarantineFolder\startup_$($item.Name)_$(Get-Date -Format 'yyyyMMddHHmmss').quarantined"
Move-Item -Path $itemPath -Destination $quarantinePath -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | QUARANTINED: $itemPath"
} catch {}
}
} catch {}
}
}
# Cap baseline
if ($script:StartupBaseline.Count -gt 200) {
$keysToRemove = @($script:StartupBaseline.Keys | Select-Object -First 50)
foreach ($k in $keysToRemove) {
$script:StartupBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Startup monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Browser Extension Monitor
# ============================================
function Start-BrowserExtensionMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.BrowserExtensionIntervalSeconds) { [int]$Script:ManagedJobConfig.BrowserExtensionIntervalSeconds } else { 120 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize extension baseline
if (-not $script:BrowserExtensionBaseline) {
$script:BrowserExtensionBaseline = @{}
}
Register-ManagedJob -Name "BrowserExtensionMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-BrowserExtensionMonitorTick
}
Write-Log "[+] Browser extension monitor (managed job) registered"
}
function Invoke-BrowserExtensionMonitorTick {
try {
$logFile = "$Base\browser_extension_detections.log"
# Chrome extensions
$chromeExtPaths = @(
"$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions",
"$env:LOCALAPPDATA\Google\Chrome\User Data\Profile 1\Extensions"
)
# Edge extensions
$edgeExtPaths = @(
"$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Extensions",
"$env:LOCALAPPDATA\Microsoft\Edge\User Data\Profile 1\Extensions"
)
# Firefox extensions
$firefoxProfiles = "$env:APPDATA\Mozilla\Firefox\Profiles"
$allExtPaths = $chromeExtPaths + $edgeExtPaths
foreach ($extPath in $allExtPaths) {
if (-not (Test-Path $extPath)) { continue }
$extensions = Get-ChildItem -Path $extPath -Directory -ErrorAction SilentlyContinue
foreach ($ext in $extensions) {
try {
$extId = $ext.Name
$extKey = "$extPath\$extId"
# Check if new
$isNew = $false
if (-not $script:BrowserExtensionBaseline.ContainsKey($extKey)) {
$script:BrowserExtensionBaseline[$extKey] = @{
ExtensionId = $extId
FirstSeen = Get-Date
}
$isNew = $true
}
if (-not $isNew) { continue }
# Read manifest to get extension info
$manifestPath = Get-ChildItem -Path $ext.FullName -Recurse -Filter "manifest.json" -ErrorAction SilentlyContinue | Select-Object -First 1
$extName = $extId
$permissions = @()
if ($manifestPath) {
try {
$manifest = Get-Content -Path $manifestPath.FullName -Raw | ConvertFrom-Json
$extName = if ($manifest.name) { $manifest.name } else { $extId }
$permissions = @()
if ($manifest.permissions) { $permissions += $manifest.permissions }
if ($manifest.optional_permissions) { $permissions += $manifest.optional_permissions }
} catch {}
}
# Check for suspicious permissions
$suspicious = $false
$reason = ""
$dangerousPermissions = @(
'webRequestBlocking',
'nativeMessaging',
'debugger',
'proxy',
'vpnProvider',
'management',
'privacy',
'browsingData'
)
foreach ($perm in $permissions) {
if ($perm -in $dangerousPermissions) {
$suspicious = $true
$reason = "Extension has dangerous permission: $perm"
break
}
if ($perm -eq '<all_urls>' -or $perm -match '^\*://\*/') {
$suspicious = $true
$reason = "Extension has access to all websites"
break
}
}
# Check for extensions with random-looking IDs (not from store)
if ($extId -notmatch '^[a-z]{32}$') {
# Chrome Web Store extensions have 32 lowercase letter IDs
if ($extId -match '^[a-f0-9]{32}$|^[A-Z0-9]{20,}$') {
$suspicious = $true
$reason = "Extension ID doesn't match store format (possibly sideloaded)"
}
}
if ($suspicious) {
$msg = "WARNING: Suspicious browser extension detected: $extName ($extId) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousBrowserExtension" -Details @{
ExtensionId = $extId
ExtensionName = $extName
Path = $extPath
Reason = $reason
Permissions = ($permissions -join ', ')
} -Severity "High"
}
} catch {}
}
}
# Check Firefox extensions
if (Test-Path $firefoxProfiles) {
$profiles = Get-ChildItem -Path $firefoxProfiles -Directory -ErrorAction SilentlyContinue
foreach ($ffProfile in $profiles) {
$extensionsJson = Join-Path $ffProfile.FullName "extensions.json"
if (Test-Path $extensionsJson) {
try {
$extData = Get-Content -Path $extensionsJson -Raw | ConvertFrom-Json
foreach ($addon in $extData.addons) {
$addonId = $addon.id
$addonKey = "firefox_$addonId"
if (-not $script:BrowserExtensionBaseline.ContainsKey($addonKey)) {
$script:BrowserExtensionBaseline[$addonKey] = @{
ExtensionId = $addonId
FirstSeen = Get-Date
}
# Check if sideloaded
if ($addon.location -ne 'app-profile' -and $addon.location -ne 'app-system') {
$msg = "WARNING: Sideloaded Firefox extension detected: $($addon.defaultLocale.name) ($addonId)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SideloadedFirefoxExtension" -Details @{
ExtensionId = $addonId
ExtensionName = $addon.defaultLocale.name
Location = $addon.location
} -Severity "Medium"
}
}
}
} catch {}
}
}
}
# Cap baseline
if ($script:BrowserExtensionBaseline.Count -gt 500) {
$keysToRemove = @($script:BrowserExtensionBaseline.Keys | Select-Object -First 100)
foreach ($k in $keysToRemove) {
$script:BrowserExtensionBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Browser extension monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Firewall Rule Monitor
# ============================================
function Start-FirewallRuleMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.FirewallRuleIntervalSeconds) { [int]$Script:ManagedJobConfig.FirewallRuleIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize firewall baseline
if (-not $script:FirewallRuleBaseline) {
$script:FirewallRuleBaseline = @{}
}
Register-ManagedJob -Name "FirewallRuleMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-FirewallRuleMonitorTick
}
Write-Log "[+] Firewall rule monitor (managed job) registered"
}
function Invoke-FirewallRuleMonitorTick {
try {
$logFile = "$Base\firewall_rule_detections.log"
# Get all firewall rules
$rules = Get-NetFirewallRule -ErrorAction SilentlyContinue | Where-Object { $_.Enabled -eq 'True' }
foreach ($rule in $rules) {
try {
$ruleName = $rule.DisplayName
$ruleKey = $rule.Name
# Check if new
$isNew = $false
if (-not $script:FirewallRuleBaseline.ContainsKey($ruleKey)) {
$script:FirewallRuleBaseline[$ruleKey] = @{
DisplayName = $ruleName
Direction = $rule.Direction
Action = $rule.Action
FirstSeen = Get-Date
}
$isNew = $true
}
if (-not $isNew) { continue }
# Get additional rule details
$portFilter = Get-NetFirewallPortFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
$appFilter = Get-NetFirewallApplicationFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
$addressFilter = Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
$suspicious = $false
$reason = ""
# Check for suspicious inbound allow rules
if ($rule.Direction -eq 'Inbound' -and $rule.Action -eq 'Allow') {
# Allow all ports
if ($portFilter -and $portFilter.LocalPort -eq 'Any') {
$suspicious = $true
$reason = "Inbound rule allows all ports"
}
# Allow from any address
if ($addressFilter -and $addressFilter.RemoteAddress -eq 'Any') {
if ($portFilter -and $portFilter.LocalPort -ne 'Any') {
# Specific port from anywhere - check if suspicious port
$port = $portFilter.LocalPort
$suspiciousPorts = @('4444', '5555', '6666', '7777', '8888', '9999', '1234', '31337', '12345')
if ($port -in $suspiciousPorts) {
$suspicious = $true
$reason = "Inbound rule allows suspicious port $port from anywhere"
}
}
}
}
# Check for rules pointing to suspicious applications
if ($appFilter -and $appFilter.Program) {
$program = $appFilter.Program
if ($program -match '\\temp\\|\\tmp\\|\\appdata\\local\\temp|\\downloads\\') {
$suspicious = $true
$reason = "Firewall rule for application in temp/user directory"
}
if ($program -match 'powershell|pwsh|cmd\.exe|wscript|cscript|mshta') {
$suspicious = $true
$reason = "Firewall rule for script interpreter"
}
if ($program -match '\\[a-f0-9]{8,}\.exe') {
$suspicious = $true
$reason = "Firewall rule for random-named executable"
}
}
# Check for rules with suspicious names
if ($ruleName -match '^[a-f0-9]{8,}$|^rule[0-9]+$|^allow[0-9]+$') {
$suspicious = $true
$reason = "Firewall rule with suspicious name pattern"
}
if ($suspicious) {
$msg = "CRITICAL: Suspicious firewall rule detected: $ruleName - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " Direction: $($rule.Direction), Action: $($rule.Action)"
if ($appFilter.Program) {
Add-Content -Path $logFile -Value " Program: $($appFilter.Program)"
}
Write-SecurityEvent -EventType "SuspiciousFirewallRule" -Details @{
RuleName = $ruleName
Direction = "$($rule.Direction)"
Action = "$($rule.Action)"
Program = if ($appFilter.Program) { $appFilter.Program } else { "N/A" }
Reason = $reason
} -Severity "Critical"
# Disable the suspicious rule
try {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | DISABLED: $ruleName"
} catch {}
}
} catch {}
}
# Cap baseline
if ($script:FirewallRuleBaseline.Count -gt 1000) {
$keysToRemove = @($script:FirewallRuleBaseline.Keys | Select-Object -First 200)
foreach ($k in $keysToRemove) {
$script:FirewallRuleBaseline.Remove($k)
}
}
} catch {
try { Write-ErrorLog -Message "Firewall rule monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Shadow Copy Tampering Detection (Ransomware)
# ============================================
function Start-ShadowCopyMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ShadowCopyIntervalSeconds) { [int]$Script:ManagedJobConfig.ShadowCopyIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize shadow copy baseline
if (-not $script:ShadowCopyBaseline) {
$script:ShadowCopyBaseline = @{
Count = 0
LastCheck = $null
}
}
Register-ManagedJob -Name "ShadowCopyMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ShadowCopyMonitorTick
}
Write-Log "[+] Shadow copy monitor (managed job) registered"
}
function Invoke-ShadowCopyMonitorTick {
try {
$logFile = "$Base\shadowcopy_detections.log"
# Get current shadow copy count
$shadowCopies = Get-CimInstance Win32_ShadowCopy -ErrorAction SilentlyContinue
$currentCount = if ($shadowCopies) { @($shadowCopies).Count } else { 0 }
# Initialize baseline on first run
if (-not $script:ShadowCopyBaseline.LastCheck) {
$script:ShadowCopyBaseline.Count = $currentCount
$script:ShadowCopyBaseline.LastCheck = Get-Date
return
}
# Check for mass deletion (ransomware indicator)
$previousCount = $script:ShadowCopyBaseline.Count
if ($previousCount -gt 0 -and $currentCount -eq 0) {
$msg = "CRITICAL: All shadow copies deleted! Possible ransomware attack!"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ShadowCopyMassDelete" -Details @{
PreviousCount = $previousCount
CurrentCount = $currentCount
} -Severity "Critical"
# Try to identify the culprit
$recentProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object { $_.CommandLine -match 'vssadmin|wmic.*shadowcopy|bcdedit|wbadmin' }
foreach ($proc in $recentProcesses) {
$msg = "SUSPECT: Process may have deleted shadow copies: $($proc.Name) (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Add-Content -Path $logFile -Value " CommandLine: $($proc.CommandLine)"
Write-SecurityEvent -EventType "ShadowCopyDeleteSuspect" -Details @{
ProcessName = $proc.Name
PID = $proc.ProcessId
CommandLine = $proc.CommandLine
} -Severity "Critical"
# Kill the suspect process
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: $($proc.Name)"
} catch {}
}
}
elseif ($currentCount -lt $previousCount -and ($previousCount - $currentCount) -ge 2) {
$msg = "WARNING: Multiple shadow copies deleted (from $previousCount to $currentCount)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ShadowCopyPartialDelete" -Details @{
PreviousCount = $previousCount
CurrentCount = $currentCount
Deleted = $previousCount - $currentCount
} -Severity "High"
}
# Check for processes attempting to delete shadow copies
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$cmdLine = $proc.CommandLine
if (-not $cmdLine) { continue }
$suspicious = $false
$reason = ""
# Ransomware shadow copy deletion patterns
if ($cmdLine -match 'vssadmin\s+(delete|resize)\s+shadows') {
$suspicious = $true
$reason = "vssadmin shadow copy deletion"
}
elseif ($cmdLine -match 'wmic\s+shadowcopy\s+delete') {
$suspicious = $true
$reason = "WMIC shadow copy deletion"
}
elseif ($cmdLine -match 'bcdedit.*recoveryenabled.*no') {
$suspicious = $true
$reason = "BCDEdit recovery disable"
}
elseif ($cmdLine -match 'wbadmin\s+delete\s+(catalog|systemstatebackup)') {
$suspicious = $true
$reason = "wbadmin backup deletion"
}
elseif ($cmdLine -match 'vssadmin\s+resize\s+shadowstorage.*maxsize=') {
$suspicious = $true
$reason = "vssadmin shadow storage resize (potential deletion)"
}
if ($suspicious) {
$msg = "CRITICAL: Shadow copy tampering detected: $($proc.Name) (PID: $($proc.ProcessId)) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ShadowCopyTampering" -Details @{
ProcessName = $proc.Name
PID = $proc.ProcessId
Reason = $reason
CommandLine = $cmdLine.Substring(0, [Math]::Min(500, $cmdLine.Length))
} -Severity "Critical"
# Kill the process immediately
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KILLED: $($proc.Name) (PID: $($proc.ProcessId))"
} catch {}
}
} catch {}
}
# Update baseline
$script:ShadowCopyBaseline.Count = $currentCount
$script:ShadowCopyBaseline.LastCheck = Get-Date
} catch {
try { Write-ErrorLog -Message "Shadow copy monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: USB/Removable Media Monitor
# ============================================
function Start-USBMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.USBMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.USBMonitorIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize USB baseline
if (-not $script:USBBaseline) {
$script:USBBaseline = @{}
}
Register-ManagedJob -Name "USBMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-USBMonitorTick
}
Write-Log "[+] USB monitor (managed job) registered"
}
function Invoke-USBMonitorTick {
try {
$logFile = "$Base\usb_detections.log"
# Get removable drives
$removableDrives = Get-CimInstance Win32_LogicalDisk -ErrorAction SilentlyContinue |
Where-Object { $_.DriveType -eq 2 } # DriveType 2 = Removable
foreach ($drive in $removableDrives) {
try {
$driveLetter = $drive.DeviceID
$driveKey = $driveLetter
# Check if new
$isNew = $false
if (-not $script:USBBaseline.ContainsKey($driveKey)) {
$script:USBBaseline[$driveKey] = @{
VolumeName = $drive.VolumeName
Size = $drive.Size
FirstSeen = Get-Date
}
$isNew = $true
$msg = "INFO: New removable drive detected: $driveLetter ($($drive.VolumeName))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "RemovableDriveConnected" -Details @{
DriveLetter = $driveLetter
VolumeName = $drive.VolumeName
SizeGB = [math]::Round($drive.Size / 1GB, 2)
} -Severity "Informational"
}
if (-not $isNew) { continue }
# Scan for suspicious files on new USB
$suspiciousExtensions = @('.exe', '.dll', '.scr', '.bat', '.cmd', '.vbs', '.vbe', '.js', '.jse', '.wsf', '.wsh', '.ps1', '.hta', '.lnk')
$autorunFile = Join-Path $driveLetter "autorun.inf"
# Check for autorun.inf (often used for USB malware)
if (Test-Path $autorunFile) {
$msg = "WARNING: autorun.inf found on removable drive: $driveLetter"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "USBAutorunDetected" -Details @{
DriveLetter = $driveLetter
AutorunPath = $autorunFile
} -Severity "High"
# Read and analyze autorun.inf
try {
$autorunContent = Get-Content -Path $autorunFile -Raw -ErrorAction SilentlyContinue
if ($autorunContent -match 'open=|shellexecute=|shell\\') {
$msg = "CRITICAL: autorun.inf contains executable reference!"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
# Quarantine the autorun.inf
try {
$quarantinePath = "$quarantineFolder\autorun_$(Get-Date -Format 'yyyyMMddHHmmss').quarantined"
Move-Item -Path $autorunFile -Destination $quarantinePath -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | QUARANTINED: $autorunFile"
} catch {}
}
} catch {}
}
# Scan root for suspicious executables
$rootFiles = Get-ChildItem -Path "$driveLetter\" -File -ErrorAction SilentlyContinue | Select-Object -First 50
foreach ($file in $rootFiles) {
try {
$ext = $file.Extension.ToLower()
if ($ext -in $suspiciousExtensions) {
$suspicious = $false
$reason = ""
# Check for hidden executables
if ($file.Attributes -band [System.IO.FileAttributes]::Hidden) {
$suspicious = $true
$reason = "Hidden executable on USB root"
}
# Check for unsigned executables
if ($ext -eq '.exe' -or $ext -eq '.dll') {
$sig = Get-AuthenticodeSignature -FilePath $file.FullName -ErrorAction SilentlyContinue
if (-not $sig -or $sig.Status -ne "Valid") {
$suspicious = $true
$reason = "Unsigned executable on USB"
}
}
# Scripts on USB root are always suspicious
if ($ext -in @('.bat', '.cmd', '.vbs', '.ps1', '.hta', '.js')) {
$suspicious = $true
$reason = "Script file on USB root"
}
# Suspicious shortcut
if ($ext -eq '.lnk') {
try {
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut($file.FullName)
if ($shortcut.TargetPath -match 'powershell|cmd|wscript|mshta') {
$suspicious = $true
$reason = "Shortcut to script interpreter"
}
} catch {}
}
if ($suspicious) {
$msg = "CRITICAL: Suspicious file on USB: $($file.FullName) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousUSBFile" -Details @{
FilePath = $file.FullName
FileName = $file.Name
Reason = $reason
DriveLetter = $driveLetter
} -Severity "Critical"
# Quarantine the file
try {
$quarantinePath = "$quarantineFolder\usb_$($file.Name)_$(Get-Date -Format 'yyyyMMddHHmmss').quarantined"
Move-Item -Path $file.FullName -Destination $quarantinePath -Force -ErrorAction SilentlyContinue
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | QUARANTINED: $($file.FullName)"
} catch {}
}
}
} catch {}
}
} catch {}
}
# Clean up baseline for removed drives
$currentDrives = @($removableDrives.DeviceID)
$keysToRemove = @($script:USBBaseline.Keys | Where-Object { $_ -notin $currentDrives })
foreach ($k in $keysToRemove) {
$script:USBBaseline.Remove($k)
}
} catch {
try { Write-ErrorLog -Message "USB monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Clipboard Monitoring (Data Theft Detection)
# ============================================
function Start-ClipboardMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ClipboardMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.ClipboardMonitorIntervalSeconds } else { 5 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
# Initialize clipboard tracking
if (-not $script:ClipboardTracking) {
$script:ClipboardTracking = @{
LastHash = $null
SensitiveAccessCount = 0
LastReset = Get-Date
}
}
Register-ManagedJob -Name "ClipboardMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ClipboardMonitorTick
}
Write-Log "[+] Clipboard monitor (managed job) registered"
}
function Invoke-ClipboardMonitorTick {
try {
$logFile = "$Base\clipboard_detections.log"
# Reset counter every hour
if ((Get-Date) - $script:ClipboardTracking.LastReset -gt [TimeSpan]::FromHours(1)) {
$script:ClipboardTracking.SensitiveAccessCount = 0
$script:ClipboardTracking.LastReset = Get-Date
}
# Get clipboard content
Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue
$clipboardText = $null
try {
if ([System.Windows.Forms.Clipboard]::ContainsText()) {
$clipboardText = [System.Windows.Forms.Clipboard]::GetText()
}
} catch {
return # Clipboard access failed, skip this tick
}
if (-not $clipboardText -or $clipboardText.Length -lt 10) { return }
# Hash to detect changes
$currentHash = [System.BitConverter]::ToString(
[System.Security.Cryptography.SHA256]::Create().ComputeHash(
[System.Text.Encoding]::UTF8.GetBytes($clipboardText)
)
).Replace("-", "").Substring(0, 16)
if ($currentHash -eq $script:ClipboardTracking.LastHash) { return }
$script:ClipboardTracking.LastHash = $currentHash
# Check for sensitive data patterns
$sensitivePatterns = @(
@{ Pattern = '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'; Type = 'Email'; Severity = 'Low' },
@{ Pattern = '\b(?:\d{4}[-\s]?){3}\d{4}\b'; Type = 'CreditCard'; Severity = 'Critical' },
@{ Pattern = '\b\d{3}-\d{2}-\d{4}\b'; Type = 'SSN'; Severity = 'Critical' },
@{ Pattern = '(?i)(password|passwd|pwd)\s*[:=]\s*\S+'; Type = 'Password'; Severity = 'Critical' },
@{ Pattern = '(?i)(api[_-]?key|apikey|secret[_-]?key)\s*[:=]\s*[A-Za-z0-9_-]{20,}'; Type = 'APIKey'; Severity = 'Critical' },
@{ Pattern = '(?i)bearer\s+[A-Za-z0-9_-]{20,}'; Type = 'BearerToken'; Severity = 'Critical' },
@{ Pattern = 'AKIA[0-9A-Z]{16}'; Type = 'AWSAccessKey'; Severity = 'Critical' },
@{ Pattern = '-----BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY-----'; Type = 'PrivateKey'; Severity = 'Critical' },
@{ Pattern = '(?i)(mysql|postgres|mongodb|redis)://[^\s]+'; Type = 'DatabaseConnectionString'; Severity = 'Critical' }
)
foreach ($check in $sensitivePatterns) {
if ($clipboardText -match $check.Pattern) {
$script:ClipboardTracking.SensitiveAccessCount++
# Only log if threshold exceeded (avoid noise from normal copy/paste)
if ($script:ClipboardTracking.SensitiveAccessCount -ge 3 -or $check.Severity -eq 'Critical') {
$msg = "$($check.Severity.ToUpper()): Sensitive data detected in clipboard: $($check.Type)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SensitiveClipboardData" -Details @{
DataType = $check.Type
ContentLength = $clipboardText.Length
AccessCount = $script:ClipboardTracking.SensitiveAccessCount
} -Severity $check.Severity
# For critical data, check what process might be accessing clipboard
if ($check.Severity -eq 'Critical') {
try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class ForegroundWindow {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
}
"@ -ErrorAction SilentlyContinue
$hwnd = [ForegroundWindow]::GetForegroundWindow()
$processId = 0
[void][ForegroundWindow]::GetWindowThreadProcessId($hwnd, [ref]$processId)
if ($processId -gt 0) {
$fgProcess = Get-Process -Id $processId -ErrorAction SilentlyContinue
if ($fgProcess) {
Add-Content -Path $logFile -Value " Foreground process: $($fgProcess.ProcessName) (PID: $processId)"
}
}
} catch {}
}
}
break # Only report first match
}
}
} catch {
try { Write-ErrorLog -Message "Clipboard monitor tick failed" -Severity "Low" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Event Log Tampering Detection
# ============================================
function Start-EventLogMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.EventLogMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.EventLogMonitorIntervalSeconds } else { 30 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
if (-not $script:EventLogBaseline) {
$script:EventLogBaseline = @{}
}
Register-ManagedJob -Name "EventLogMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-EventLogMonitorTick
}
Write-Log "[+] Event log monitor (managed job) registered"
}
function Invoke-EventLogMonitorTick {
try {
$logFile = "$Base\eventlog_tampering.log"
# Check for event log clearing (Security Event ID 1102, System Event ID 104)
$clearEvents = @()
try {
$clearEvents += Get-WinEvent -FilterHashtable @{LogName='Security';Id=1102} -MaxEvents 5 -ErrorAction SilentlyContinue
$clearEvents += Get-WinEvent -FilterHashtable @{LogName='System';Id=104} -MaxEvents 5 -ErrorAction SilentlyContinue
} catch {}
foreach ($evt in $clearEvents) {
$evtKey = "$($evt.LogName)_$($evt.TimeCreated.Ticks)"
if (-not $script:EventLogBaseline.ContainsKey($evtKey)) {
$script:EventLogBaseline[$evtKey] = $true
$msg = "CRITICAL: Event log cleared! Log: $($evt.LogName)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "EventLogCleared" -Details @{ LogName = $evt.LogName; TimeCreated = $evt.TimeCreated } -Severity "Critical"
}
}
# Check for processes attempting to clear logs
$procs = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue
foreach ($proc in $procs) {
$cmd = $proc.CommandLine
if (-not $cmd) { continue }
if ($cmd -match 'wevtutil\s+(cl|clear-log)|Clear-EventLog|Remove-EventLog') {
$msg = "CRITICAL: Event log tampering attempt: $($proc.Name) (PID: $($proc.ProcessId))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "EventLogTampering" -Details @{ ProcessName = $proc.Name; PID = $proc.ProcessId; CommandLine = $cmd } -Severity "Critical"
try { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue } catch {}
}
}
# Cap baseline
if ($script:EventLogBaseline.Count -gt 100) {
$keysToRemove = @($script:EventLogBaseline.Keys | Select-Object -First 50)
foreach ($k in $keysToRemove) { $script:EventLogBaseline.Remove($k) }
}
} catch {
try { Write-ErrorLog -Message "Event log monitor tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Process Hollowing Detection
# ============================================
function Start-ProcessHollowingDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ProcessHollowingIntervalSeconds) { [int]$Script:ManagedJobConfig.ProcessHollowingIntervalSeconds } else { 20 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "ProcessHollowingDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ProcessHollowingDetectorTick
}
Write-Log "[+] Process hollowing detector (managed job) registered"
}
function Invoke-ProcessHollowingDetectorTick {
try {
$logFile = "$Base\process_hollowing.log"
# Common targets for process hollowing
$hollowTargets = @('svchost.exe', 'explorer.exe', 'notepad.exe', 'calc.exe', 'mspaint.exe', 'RuntimeBroker.exe')
$procs = Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -in ($hollowTargets -replace '\.exe$','') }
foreach ($proc in $procs) {
try {
if ($proc.Id -le 4 -or $proc.Id -eq $PID) { continue }
$procPath = $proc.Path
if (-not $procPath) { continue }
# Check if process image doesn't match expected location
$expectedPaths = @("$env:SystemRoot\System32", "$env:SystemRoot\SysWOW64", "$env:SystemRoot")
$isExpectedPath = $false
foreach ($exp in $expectedPaths) {
if ($procPath -like "$exp\*") { $isExpectedPath = $true; break }
}
# svchost should only run from System32
if ($proc.ProcessName -eq 'svchost' -and -not $isExpectedPath) {
$msg = "CRITICAL: Potential process hollowing - svchost from unexpected path: $procPath"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "ProcessHollowing" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; Path = $procPath } -Severity "Critical"
try { Stop-ThreatProcess -ProcessId $proc.Id -ProcessName $proc.Name -Reason "Suspicious Behavior" } catch {}
}
# Check for suspended state with network connections (common in hollowing)
$threads = $proc.Threads | Where-Object { $_.ThreadState -eq 'Wait' -and $_.WaitReason -eq 'Suspended' }
if ($threads.Count -gt 0) {
$conns = Get-NetTCPConnection -OwningProcess $proc.Id -ErrorAction SilentlyContinue
if ($conns) {
$msg = "WARNING: Suspended process with network activity: $($proc.ProcessName) (PID: $($proc.Id))"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousSuspendedProcess" -Details @{ ProcessName = $proc.ProcessName; PID = $proc.Id; Connections = $conns.Count } -Severity "High"
}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Process hollowing detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Keylogger Detection
# ============================================
function Start-KeyloggerDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.KeyloggerDetectorIntervalSeconds) { [int]$Script:ManagedJobConfig.KeyloggerDetectorIntervalSeconds } else { 15 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "KeyloggerDetector" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-KeyloggerDetectorTick
}
Write-Log "[+] Keylogger detector (managed job) registered"
}
function Invoke-KeyloggerDetectorTick {
try {
$logFile = "$Base\keylogger_detections.log"
$procs = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue
foreach ($proc in $procs) {
try {
$cmd = $proc.CommandLine
$name = $proc.Name
if (-not $cmd -and -not $name) { continue }
$suspicious = $false
$reason = ""
# Check for known keylogger patterns
$keyloggerPatterns = @(
@{ Pattern = 'GetAsyncKeyState|GetKeyState|SetWindowsHookEx.*WH_KEYBOARD'; Reason = 'Keyboard hook API' },
@{ Pattern = 'keylog|keystroke|keypress|keycapture'; Reason = 'Keylogger keyword' },
@{ Pattern = 'RegisterRawInputDevices|GetRawInputData'; Reason = 'Raw input API (potential keylogger)' }
)
foreach ($check in $keyloggerPatterns) {
if ($cmd -match $check.Pattern -or $name -match $check.Pattern) {
$suspicious = $true
$reason = $check.Reason
break
}
}
# Check for processes with keyboard hooks by examining loaded modules
if (-not $suspicious -and $proc.ProcessId -gt 4 -and $proc.ProcessId -ne $PID) {
try {
$p = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
if ($p -and $p.Modules) {
$suspiciousModules = $p.Modules | Where-Object { $_.ModuleName -match 'hook|key|log|capture' }
if ($suspiciousModules) {
$suspicious = $true
$reason = "Suspicious module loaded: $($suspiciousModules[0].ModuleName)"
}
}
} catch {}
}
if ($suspicious) {
$msg = "CRITICAL: Potential keylogger detected: $name (PID: $($proc.ProcessId)) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "KeyloggerDetected" -Details @{ ProcessName = $name; PID = $proc.ProcessId; Reason = $reason } -Severity "Critical"
try { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue } catch {}
}
} catch {}
}
} catch {
try { Write-ErrorLog -Message "Keylogger detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# ENTERPRISE EDR: Ransomware Behavior Detection
# ============================================
function Start-RansomwareBehaviorDetector {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RansomwareBehaviorIntervalSeconds) { [int]$Script:ManagedJobConfig.RansomwareBehaviorIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
if (-not $script:RansomwareTracking) {
$script:RansomwareTracking = @{}
}
Register-ManagedJob -Name "RansomwareBehaviorDetector" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-RansomwareBehaviorDetectorTick
}
Write-Log "[+] Ransomware behavior detector (managed job) registered"
}
function Invoke-RansomwareBehaviorDetectorTick {
try {
$logFile = "$Base\ransomware_behavior.log"
# Check for ransomware behavior patterns
$procs = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue
foreach ($proc in $procs) {
try {
$cmd = $proc.CommandLine
if (-not $cmd) { continue }
$suspicious = $false
$reason = ""
# Check for ransomware command patterns
if ($cmd -match 'cipher\s+/w:|vssadmin.*delete.*shadows|bcdedit.*recoveryenabled.*no|wbadmin.*delete') {
$suspicious = $true
$reason = "Ransomware preparation command"
}
# Check for mass encryption patterns
if ($cmd -match '\.(encrypted|locked|crypto|crypt)' -or $cmd -match 'encrypt|ransom|bitcoin|btc|decrypt') {
$suspicious = $true
$reason = "Ransomware keyword detected"
}
if ($suspicious) {
$msg = "CRITICAL: Ransomware behavior detected: $($proc.Name) (PID: $($proc.ProcessId)) - $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "RansomwareBehavior" -Details @{ ProcessName = $proc.Name; PID = $proc.ProcessId; Reason = $reason } -Severity "Critical"
try { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue } catch {}
}
} catch {}
}
# Check for ransom notes
$ransomNotePatterns = @('*readme*ransom*', '*decrypt*instructions*', '*how*decrypt*', '*restore*files*', '*your*files*encrypted*', '*DECRYPT*README*', '*_readme.txt', '*-DECRYPT.txt')
$commonDirs = @("$env:USERPROFILE\Desktop", "$env:USERPROFILE\Documents", "$env:PUBLIC\Desktop")
foreach ($dir in $commonDirs) {
if (-not (Test-Path $dir)) { continue }
foreach ($pattern in $ransomNotePatterns) {
$notes = Get-ChildItem -Path $dir -Filter $pattern -ErrorAction SilentlyContinue | Select-Object -First 3
foreach ($note in $notes) {
$noteKey = $note.FullName
if (-not $script:RansomwareTracking.ContainsKey($noteKey)) {
$script:RansomwareTracking[$noteKey] = $true
$msg = "CRITICAL: Ransom note detected: $($note.FullName)"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "RansomNoteDetected" -Details @{ FilePath = $note.FullName } -Severity "Critical"
try { Remove-Item -Path $note.FullName -Force -ErrorAction SilentlyContinue } catch {}
}
}
}
}
# Cap tracking
if ($script:RansomwareTracking.Count -gt 100) {
$keysToRemove = @($script:RansomwareTracking.Keys | Select-Object -First 50)
foreach ($k in $keysToRemove) { $script:RansomwareTracking.Remove($k) }
}
} catch {
try { Write-ErrorLog -Message "Ransomware behavior detector tick failed" -Severity "High" -ErrorRecord $_ } catch {}
}
}
# ============================================
# SECURITY FIX #1: PERIODIC INTEGRITY CHECKS
# ============================================
function Start-IntegrityMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.IntegrityMonitorIntervalSeconds) { [int]$Script:ManagedJobConfig.IntegrityMonitorIntervalSeconds } else { 60 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Register-ManagedJob -Name "IntegrityMonitor" -Enabled $true -Critical $true -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-IntegrityMonitorTick
}
Write-Log "[+] Integrity monitor (managed job) registered"
}
function Invoke-IntegrityMonitorTick {
try {
if (-not (Test-ScriptIntegrity)) {
Write-Host "[CRITICAL] Script integrity compromised. Terminating." -ForegroundColor Red
Write-SecurityEvent -EventType "ScriptIntegrityFailure" -Details @{ PID = $PID } -Severity "Critical"
Stop-Process -Id $PID -Force
}
} catch {
try {
Write-ErrorLog -Message "Integrity monitor tick failed" -Severity "High" -ErrorRecord $_
} catch {}
}
}
# ============================================
# MAIN EXECUTION
# ============================================
# Run configuration validation at startup
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " ANTIVIRUS PROTECTION - STARTING UP" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
Initialize-WhitelistDatabase
$configErrors = Test-ScriptConfiguration
if ($configErrors.Count -gt 0) {
Write-Host "[WARNING] Configuration issues detected:" -ForegroundColor Yellow
$configErrors | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow }
Write-Host ""
}
Write-SecurityEvent -EventType "AntivirusStartup" -Details @{
PID = $PID
User = $env:USERNAME
ConfigErrors = $configErrors.Count
} -Severity "Informational"
# Initialize HMAC key
# Moved to top after logging functions
Write-Log "[+] Starting all detection modules"
# Initial scan
Remove-UnsignedDLLs
Write-Host "`n[*] Initializing background monitoring jobs..."
$jobNames = @()
Write-Host "[*] Starting Behavior Monitor..."
Start-BehaviorMonitor
$jobNames += "BehaviorMonitor"
Write-Host "[*] Starting Enhanced Behavior Monitor..."
Start-EnhancedBehaviorMonitor
$jobNames += "EnhancedBehaviorMonitor"
Write-Host "[*] Starting COM Control Monitor..."
Start-COMControlMonitor
$jobNames += "COMControlMonitor"
Write-Host "[*] Starting Anti-Tamper Monitor..." -ForegroundColor Yellow
Start-AntiTamperMonitor
$jobNames += "AntiTamperMonitor"
Write-Host "[*] Starting Network Anomaly Detector..." -ForegroundColor Yellow
Start-NetworkAnomalyDetector
$jobNames += "NetworkAnomalyDetector"
Write-Host "[*] Starting Rootkit Detector..." -ForegroundColor Yellow
Start-RootkitDetector
$jobNames += "RootkitDetector"
Write-Host "[*] Starting Filesystem Integrity Monitor..." -ForegroundColor Yellow
Start-FileSystemIntegrityMonitor
$jobNames += "FileSystemIntegrityMonitor"
Write-Host "[*] Starting Memory Monitors..." -ForegroundColor Yellow
Start-MemoryMonitors
$jobNames += "MemoryMonitors"
# Add termination protection AFTER all functions are defined
Register-TerminationProtection
Write-Host "`n[PROTECTION] Initializing anti-termination safeguards..." -ForegroundColor Cyan
if ($host.Name -eq "Windows PowerShell ISE Host") {
# In ISE, use trap handler which is already defined at the top
Write-Host "[PROTECTION] ISE detected - using trap-based Ctrl+C protection" -ForegroundColor Cyan
Write-Host "[PROTECTION] Ctrl+C protection enabled (requires $Script:MaxTerminationAttempts attempts to stop)" -ForegroundColor Green
} else {
# In regular console, use the Console.CancelKeyPress handler
Enable-CtrlCProtection
}
# Enable auto-restart if running as admin
try {
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($isAdmin) {
Enable-AutoRestart
Start-ProcessWatchdog
} else {
Write-Host "[INFO] Auto-restart requires administrator privileges (optional)" -ForegroundColor Gray
}
} catch {
Write-Host "[WARNING] Some protection features failed to initialize: $($_.Exception.Message)" -ForegroundColor Yellow
}
Write-Host "[PROTECTION] Anti-termination safeguards active" -ForegroundColor Green
Write-Host "[*] Starting Integrity Monitor..."
Start-IntegrityMonitor
$jobNames += "IntegrityMonitor"
Write-Host "[*] Starting Reflective Payload Detector..." -ForegroundColor Yellow
Start-ReflectivePayloadDetector
$jobNames += "ReflectivePayloadDetector"
Write-Host "[*] Starting AMSI Bypass Detector..." -ForegroundColor Yellow
Start-AMSIBypassDetector
$jobNames += "AMSIBypassDetector"
Write-Host "[*] Starting Credential Dumping Detector..." -ForegroundColor Yellow
Start-CredentialDumpingDetector
$jobNames += "CredentialDumpingDetector"
Write-Host "[*] Starting Process Anomaly Detector..." -ForegroundColor Yellow
Start-ProcessAnomalyDetector
$jobNames += "ProcessAnomalyDetector"
Write-Host "[*] Starting WMI Persistence Detector..." -ForegroundColor Yellow
Start-WMIPersistenceDetector
$jobNames += "WMIPersistenceDetector"
Write-Host "[*] Starting Scheduled Task Detector..." -ForegroundColor Yellow
Start-ScheduledTaskDetector
$jobNames += "ScheduledTaskDetector"
Write-Host "[*] Starting DNS Exfiltration Detector..." -ForegroundColor Yellow
Start-DNSExfiltrationDetector
$jobNames += "DNSExfiltrationDetector"
Write-Host "[*] Starting Named Pipe Monitor..." -ForegroundColor Yellow
Start-NamedPipeMonitor
$jobNames += "NamedPipeMonitor"
Write-Host "[*] Starting Registry Persistence Detector..." -ForegroundColor Yellow
Start-RegistryPersistenceDetector
$jobNames += "RegistryPersistenceDetector"
Write-Host "[*] Starting Script Block Analyzer..." -ForegroundColor Yellow
Start-ScriptBlockAnalyzer
$jobNames += "ScriptBlockAnalyzer"
Write-Host "[*] Starting Service Monitor..." -ForegroundColor Yellow
Start-ServiceMonitor
$jobNames += "ServiceMonitor"
Write-Host "[*] Starting DLL Hijack Detector..." -ForegroundColor Yellow
Start-DLLHijackDetector
$jobNames += "DLLHijackDetector"
Write-Host "[*] Starting Token Manipulation Detector..." -ForegroundColor Yellow
Start-TokenManipulationDetector
$jobNames += "TokenManipulationDetector"
Write-Host "[*] Starting Startup Monitor..." -ForegroundColor Yellow
Start-StartupMonitor
$jobNames += "StartupMonitor"
Write-Host "[*] Starting Browser Extension Monitor..." -ForegroundColor Yellow
Start-BrowserExtensionMonitor
$jobNames += "BrowserExtensionMonitor"
Write-Host "[*] Starting Firewall Rule Monitor..." -ForegroundColor Yellow
Start-FirewallRuleMonitor
$jobNames += "FirewallRuleMonitor"
Write-Host "[*] Starting Shadow Copy Monitor..." -ForegroundColor Yellow
Start-ShadowCopyMonitor
$jobNames += "ShadowCopyMonitor"
Write-Host "[*] Starting USB Monitor..." -ForegroundColor Yellow
Start-USBMonitor
$jobNames += "USBMonitor"
Write-Host "[*] Starting Clipboard Monitor..." -ForegroundColor Yellow
Start-ClipboardMonitor
$jobNames += "ClipboardMonitor"
Write-Host "[*] Starting Event Log Monitor..." -ForegroundColor Yellow
Start-EventLogMonitor
$jobNames += "EventLogMonitor"
Write-Host "[*] Starting Process Hollowing Detector..." -ForegroundColor Yellow
Start-ProcessHollowingDetector
$jobNames += "ProcessHollowingDetector"
Write-Host "[*] Starting Keylogger Detector..." -ForegroundColor Yellow
Start-KeyloggerDetector
$jobNames += "KeyloggerDetector"
Write-Host "[*] Starting Ransomware Behavior Detector..." -ForegroundColor Yellow
Start-RansomwareBehaviorDetector
$jobNames += "RansomwareBehaviorDetector"
Start-Sleep -Seconds 2
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class DLL {
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int access, bool inherit, int pid);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr mod, string name);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr proc, IntPtr attr, uint stack, IntPtr start, IntPtr param, uint flags, IntPtr id);
[DllImport("kernel32.dll")]
public static extern uint WaitForSingleObject(IntPtr handle, uint ms);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool MoveFileEx(string src, string dst, int flags);
}
"@
# ============================================
# START OF NEWLY ADDED CODE BLOCK
# ============================================
function Start-NetworkTrafficMonitor {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.NetworkTrafficIntervalSeconds) { [int]$Script:ManagedJobConfig.NetworkTrafficIntervalSeconds } else { 5 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Initialize-NetworkTrafficMonitorState
Register-ManagedJob -Name "NetworkTrafficMonitor" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-NetworkTrafficMonitorTick
}
Write-Log "[+] Network Traffic Monitor (managed job) registered"
}
function Initialize-NetworkTrafficMonitorState {
if ($script:NTMInitialized) { return }
$script:NTMAllowedDomains = @()
$script:NTMAllowedIPs = @()
$script:NTMBlockedConnections = @{}
$script:NTMCurrentBrowserConnections = @{}
$script:NTMSeenConnections = @{}
$script:NTMInitialized = $true
}
function Invoke-NetworkTrafficMonitorTick {
try {
if (-not $script:NTMInitialized) {
Initialize-NetworkTrafficMonitorState
}
$logFile = "$Base\ntm_monitor.log"
function Write-ColorOutput {
param([string]$Message)
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $Message"
}
function Test-BrowserConnection {
param([string]$RemoteAddress)
if ($RemoteAddress -match '^(127\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)') {
return $true
}
return ($script:NTMAllowedIPs -contains $RemoteAddress)
}
function Watch-BrowserActivity {
param([string]$RemoteAddress, [string]$ProcessName, [int]$RemotePort)
$BrowserProcesses = @('chrome', 'firefox', 'msedge', 'iexplore', 'opera', 'brave')
if ($BrowserProcesses -contains $ProcessName.ToLower()) {
if ($script:NTMAllowedIPs -notcontains $RemoteAddress) {
$script:NTMAllowedIPs += $RemoteAddress
$script:NTMCurrentBrowserConnections[$RemoteAddress] = Get-Date
}
return $true
}
$now = Get-Date
foreach ($browserIP in $script:NTMCurrentBrowserConnections.Keys) {
$connectionTime = $script:NTMCurrentBrowserConnections[$browserIP]
if (($now - $connectionTime).TotalSeconds -le 30) {
if ($script:NTMAllowedIPs -notcontains $RemoteAddress) {
$script:NTMAllowedIPs += $RemoteAddress
}
return $true
}
}
return $false
}
function New-BlockRule {
param([string]$RemoteAddress, [int]$RemotePort, [string]$ProcessName)
$script:NTMBlockedConnections[$RemoteAddress] = @{ Port = $RemotePort; Process = $ProcessName }
Write-ColorOutput "BLOCKED: ${RemoteAddress}:${RemotePort} (Process: $ProcessName)"
Write-SecurityEvent -EventType "UnauthorizedConnectionBlocked" -Details @{
RemoteAddress = $RemoteAddress
RemotePort = $RemotePort
ProcessName = $ProcessName
} -Severity "Medium"
$ruleName = "AV_Block_$RemoteAddress"
try {
netsh advfirewall firewall add rule name="$ruleName" dir=out action=block remoteip=$RemoteAddress | Out-Null
} catch {}
}
$connections = Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue | Where-Object { $_.RemoteAddress -ne '0.0.0.0' -and $_.RemoteAddress -ne '::' }
foreach ($conn in $connections) {
$key = "$($conn.RemoteAddress):$($conn.RemotePort):$($conn.OwningProcess)"
if ($script:NTMSeenConnections.ContainsKey($key)) { continue }
$script:NTMSeenConnections[$key] = $true
try {
$process = Get-Process -Id $conn.OwningProcess -ErrorAction Stop
$processName = $process.ProcessName
} catch {
$processName = "Unknown"
}
$isBrowserOrDependency = Watch-BrowserActivity -RemoteAddress $conn.RemoteAddress -ProcessName $processName -RemotePort $conn.RemotePort
if ($isBrowserOrDependency) { continue }
if (-not (Test-BrowserConnection -RemoteAddress $conn.RemoteAddress)) {
if (-not $script:NTMBlockedConnections.ContainsKey($conn.RemoteAddress)) {
New-BlockRule -RemoteAddress $conn.RemoteAddress -RemotePort $conn.RemotePort -ProcessName $processName
}
}
}
# Cap SeenConnections to avoid RAM growth
if ($script:NTMSeenConnections.Count -gt 10000) {
$script:NTMSeenConnections.Clear()
}
# Clean up old browser connection timestamps
$now = Get-Date
$toRemove = @()
foreach ($ip in $script:NTMCurrentBrowserConnections.Keys) {
if (($now - $script:NTMCurrentBrowserConnections[$ip]).TotalSeconds -gt 60) {
$toRemove += $ip
}
}
foreach ($ip in $toRemove) {
$script:NTMCurrentBrowserConnections.Remove($ip)
}
} catch {
try { Write-ErrorLog -Message "Network traffic monitor tick failed" -Severity "Low" -ErrorRecord $_ } catch {}
}
}
function Start-KeyScrambler {
Start-Job -ScriptBlock {
try {
$Base = $using:Base
$logFile = "$Base\keyscrambler.log"
${function:Write-SecurityEvent} = ${using:function:Write-SecurityEvent}
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Starting KeyScrambler anti-keylogger protection..."
# KeyScrambler C# code
$Source = @"
using System;
using System.Runtime.InteropServices;
public class KeyScrambler
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public uint type;
public INPUTUNION u;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUTUNION
{
[FieldOffset(0)] public KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
private const uint INPUT_KEYBOARD = 1;
private const uint KEYEVENTF_UNICODE = 0x0004;
private const uint KEYEVENTF_KEYUP = 0x0002;
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, IntPtr lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")] private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] private static extern bool GetMessage(out MSG msg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
[DllImport("user32.dll")] private static extern bool TranslateMessage(ref MSG msg);
[DllImport("user32.dll")] private static extern IntPtr DispatchMessage(ref MSG msg);
[DllImport("user32.dll")] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[DllImport("user32.dll")] private static extern IntPtr GetMessageExtraInfo();
[DllImport("user32.dll")] private static extern short GetKeyState(int nVirtKey);
[DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName);
[StructLayout(LayoutKind.Sequential)]
public struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam; public IntPtr lParam; public uint time; public POINT pt; }
[StructLayout(LayoutKind.Sequential)]
public struct POINT { public int x; public int y; }
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr _hookID = IntPtr.Zero;
private static LowLevelKeyboardProc _proc;
private static Random _rnd = new Random();
public static void Start()
{
if (_hookID != IntPtr.Zero) return;
_proc = HookCallback;
_hookID = SetWindowsHookEx(WH_KEYBOARD_LL,
Marshal.GetFunctionPointerForDelegate(_proc),
GetModuleHandle(null), 0);
if (_hookID == IntPtr.Zero)
throw new Exception("Hook failed: " + Marshal.GetLastWin32Error());
MSG msg;
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
}
private static bool ModifiersDown()
{
return (GetKeyState(0x10) & 0x8000) != 0 ||
(GetKeyState(0x11) & 0x8000) != 0 ||
(GetKeyState(0x12) & 0x8000) != 0;
}
private static void InjectFakeChar(char c)
{
var inputs = new INPUT[2];
inputs[0].type = INPUT_KEYBOARD;
inputs[0].u.ki.wVk = 0;
inputs[0].u.ki.wScan = (ushort)c;
inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
inputs[0].u.ki.dwExtraInfo = GetMessageExtraInfo();
inputs[1] = inputs[0];
inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
Thread.Sleep(_rnd.Next(1, 7));
}
private static void Flood()
{
if (_rnd.NextDouble() < 0.5) return;
int count = _rnd.Next(1, 7);
for (int i = 0; i < count; i++)
InjectFakeChar((char)_rnd.Next('A', 'Z' + 1));
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
KBDLLHOOKSTRUCT k = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
if ((k.flags & 0x10) != 0) return CallNextHookEx(_hookID, nCode, wParam, lParam);
if (ModifiersDown()) return CallNextHookEx(_hookID, nCode, wParam, lParam);
if (k.vkCode >= 65 && k.vkCode <= 90)
{
if (_rnd.NextDouble() < 0.75) Flood();
var ret = CallNextHookEx(_hookID, nCode, wParam, lParam);
if (_rnd.NextDouble() < 0.75) Flood();
return ret;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
"@
try {
Add-Type -TypeDefinition $Source -Language CSharp -ErrorAction Stop
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | KeyScrambler compiled successfully!"
Write-SecurityEvent -EventType "KeyScramblerStarted" -Details @{ Status = "Active" } -Severity "Info"
# Start KeyScrambler
[KeyScrambler]::Start()
}
catch {
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | ERROR: KeyScrambler compilation failed: $($_.Exception.Message)"
Write-SecurityEvent -EventType "KeyScramblerFailed" -Details @{ Error = $_.Exception.Message } -Severity "High"
}
} catch {
try {
Add-Content -Path "$using:Base\keyscrambler.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | CRITICAL: KeyScrambler job crashed: $_"
} catch {}
}
} | Out-Null
Write-Log "[+] KeyScrambler anti-keylogger started"
}
function Start-ElfCatcher {
$intervalSeconds = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.ElfCatcherIntervalSeconds) { [int]$Script:ManagedJobConfig.ElfCatcherIntervalSeconds } else { 10 }
$maxRestarts = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.MaxRestartAttempts) { [int]$Script:ManagedJobConfig.MaxRestartAttempts } else { 3 }
$restartDelay = if ($Script:ManagedJobConfig -and $Script:ManagedJobConfig.RestartDelaySeconds) { [int]$Script:ManagedJobConfig.RestartDelaySeconds } else { 5 }
Initialize-ElfCatcherState
Register-ManagedJob -Name "ElfCatcher" -Enabled $true -Critical $false -IntervalSeconds $intervalSeconds -MaxRestartAttempts $maxRestarts -RestartDelaySeconds $restartDelay -ScriptBlock {
Invoke-ElfCatcherTick
}
Write-Log "[ELF-CATCHER] DLL monitor (managed job) registered"
}
function Initialize-ElfCatcherState {
if ($script:ElfCatcherInitialized) { return }
$script:ElfCatcherWhitelist = @('ntdll.dll', 'kernel32.dll', 'kernelbase.dll', 'user32.dll',
'gdi32.dll', 'msvcrt.dll', 'advapi32.dll', 'ws2_32.dll',
'shell32.dll', 'ole32.dll', 'combase.dll', 'bcrypt.dll',
'crypt32.dll', 'sechost.dll', 'rpcrt4.dll', 'imm32.dll')
$script:ElfCatcherTargets = @('chrome', 'msedge', 'firefox', 'brave', 'opera', 'vivaldi', 'iexplore', 'microsoftedge')
$script:ElfCatcherProcessed = @{}
$script:ElfCatcherInitialized = $true
}
function Invoke-ElfCatcherTick {
try {
if (-not $script:ElfCatcherInitialized) {
Initialize-ElfCatcherState
}
$logFile = "$Base\elf_catcher.log"
$whitelist = $script:ElfCatcherWhitelist
$targets = $script:ElfCatcherTargets
$processed = $script:ElfCatcherProcessed
foreach ($procName in $targets) {
$procs = Get-Process -Name $procName -ErrorAction SilentlyContinue
foreach ($proc in $procs) {
try {
$hProc = [DLL]::OpenProcess(0x1F0FFF, $false, $proc.Id)
if ($hProc -eq [IntPtr]::Zero) { continue }
$freeLib = [DLL]::GetProcAddress([DLL]::GetModuleHandle("kernel32.dll"), "FreeLibrary")
foreach ($mod in $proc.Modules) {
try {
$name = [System.IO.Path]::GetFileName($mod.FileName).ToLower()
if ($whitelist -contains $name) { continue }
$key = "$($proc.Id):$($mod.FileName)"
if ($processed.ContainsKey($key)) { continue }
$suspicious = $false
$reason = ""
if ($name -like '*_elf.dll') { $suspicious = $true; $reason = "ELF pattern DLL" }
elseif ($name -like '*.winmd' -and $mod.FileName -notmatch "\\Windows\\") { $suspicious = $true; $reason = "Suspicious WINMD outside Windows directory" }
elseif ($name -match '^[a-f0-9]{8,}\.dll$') { $suspicious = $true; $reason = "Random hex-named DLL" }
elseif ($mod.FileName -match "\\AppData\\Local\\Temp\\" -and $name -notlike "chrome_*" -and $name -notlike "edge_*") { $suspicious = $true; $reason = "DLL loaded from TEMP directory" }
if ($suspicious) {
$msg = "Detected suspicious DLL: $name in $procName (PID $($proc.Id)) - Reason: $reason"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
Write-SecurityEvent -EventType "SuspiciousDLLDetected" -Details @{ DLLName = $name; DLLPath = $mod.FileName; ProcessName = $procName; PID = $proc.Id; Reason = $reason } -Severity "High"
$thread = [DLL]::CreateRemoteThread($hProc, [IntPtr]::Zero, 0, $freeLib, $mod.BaseAddress, 0, [IntPtr]::Zero)
if ($thread -ne [IntPtr]::Zero) {
[DLL]::WaitForSingleObject($thread, 5000) | Out-Null
[DLL]::CloseHandle($thread) | Out-Null
$msg = "Successfully unloaded DLL: $name from $procName"
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg"
if (Test-Path $mod.FileName) {
if ([DLL]::MoveFileEx($mod.FileName, $null, 4)) {
Write-SecurityEvent -EventType "SuspiciousDLLScheduledForDeletion" -Details @{ DLLPath = $mod.FileName } -Severity "Medium"
}
}
}
$processed[$key] = $true
}
} catch {}
}
[DLL]::CloseHandle($hProc) | Out-Null
} catch {
}
}
}
# Cap processed list to avoid RAM growth
if ($processed.Count -gt 5000) {
$processed.Clear()
}
} catch {
try { Write-ErrorLog -Message "ElfCatcher tick failed" -Severity "Low" -ErrorRecord $_ } catch {}
}
}
Write-Host "[*] Starting Network Traffic Monitor..." -ForegroundColor Yellow
Start-NetworkTrafficMonitor
$jobNames += "NetworkTrafficMonitor"
Write-Host "[*] Starting Elf Catcher..." -ForegroundColor Yellow
Start-ElfCatcher
$jobNames += "ElfCatcher"
Write-Log "[+] All monitoring modules started successfully"
Write-Log "[+] Self-protection: ACTIVE"
Write-Log "[+] Database integrity: VERIFIED"
Write-Log "[+] Watchdog persistence: CONFIGURED"
# Set the flag indicating jobs have been initialized
$Script:JobsInitialized = $true
$managedJobsCount = 0
try {
if ($script:ManagedJobs) {
$managedJobsCount = ($script:ManagedJobs.Values | Where-Object { $_.Enabled -and ($null -eq $_.DisabledUtc) }).Count
}
} catch {}
# Keep script running
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Production Hardened Antivirus RUNNING" -ForegroundColor Green
Write-Host "Managed Jobs Active: $managedJobsCount" -ForegroundColor Green
Write-Host "Press [Ctrl] + [C] to stop." -ForegroundColor Yellow
Write-Host "Press [H] for help." -ForegroundColor Yellow
Write-Host "========================================`n" -ForegroundColor Cyan
# SECURITY FIX #1: PERIODIC INTEGRITY CHECKS
function Test-ScriptIntegrity {
try {
# Verify script file still exists
if (-not (Test-Path $Script:SelfPath)) {
Write-Log "[CRITICAL] Script file has been deleted: $Script:SelfPath"
return $false
}
# Calculate current hash
$currentHash = (Get-FileHash -Path $Script:SelfPath -Algorithm SHA256 -ErrorAction Stop).Hash
# Compare with original hash
if ($currentHash -ne $Script:SelfHash) {
Write-Log "[CRITICAL] Script file has been modified! Original: $Script:SelfHash, Current: $currentHash"
return $false
}
return $true
} catch {
Write-ErrorLog -Message "Failed to check script integrity" -Severity "High" -ErrorRecord $_
# On error, assume integrity is OK to prevent false positives
return $true
}
}
$Script:ManagedJobConfig = @{
MinimumIntervalSeconds = 5
HealthCheckIntervalSeconds = 30
MaxRestartAttempts = 3
RestartDelaySeconds = 5
# OPTIMIZED INTERVALS - Reduced CPU/RAM usage while maintaining security
# Critical detections run faster, less critical run slower
MalwareScanIntervalSeconds = 15 # Increased from 10
LogRotationIntervalSeconds = 120 # Increased from 60
MemoryCleanupIntervalSeconds = 300 # Decreased from 600 for better RAM
SecurityReportIntervalSeconds = 600 # Increased from 300
CacheMaintenanceIntervalSeconds = 180 # Decreased from 300 for better RAM
ScannedFilesMaxCount = 5000 # Decreased from 10000 for RAM
BehaviorMonitorIntervalSeconds = 15 # Increased from 10
IntegrityMonitorIntervalSeconds = 120 # Increased from 60
EnhancedBehaviorIntervalSeconds = 45 # Increased from 30
AntiTamperIntervalSeconds = 15 # Increased from 10
HighMemoryCheckIntervalSeconds = 180 # Decreased from 300
PowerShellMemoryScanIntervalSeconds = 20 # Increased from 10
ReflectivePayloadIntervalSeconds = 20 # Increased from 10
NetworkTrafficIntervalSeconds = 10 # Increased from 5
ElfCatcherIntervalSeconds = 20 # Increased from 10
RootkitScanIntervalSeconds = 120 # Increased from 60
FileSystemIntegrityIntervalSeconds = 60 # Increased from 30
NetworkAnomalyIntervalSeconds = 30 # Increased from 15
COMControlIntervalSeconds = 120 # Increased from 60
# Enterprise EDR intervals - OPTIMIZED
AMSIBypassIntervalSeconds = 20 # Increased from 15
CredentialDumpingIntervalSeconds = 15 # Increased from 10 (critical)
ProcessAnomalyIntervalSeconds = 15 # Increased from 10
WMIPersistenceIntervalSeconds = 120 # Increased from 60
ScheduledTaskIntervalSeconds = 120 # Increased from 60
DNSExfiltrationIntervalSeconds = 60 # Increased from 30
NamedPipeIntervalSeconds = 30 # Increased from 15
RegistryPersistenceIntervalSeconds = 120 # Increased from 60
ScriptBlockAnalyzerIntervalSeconds = 60 # Increased from 30
ServiceMonitorIntervalSeconds = 60 # Increased from 30
DLLHijackIntervalSeconds = 180 # Increased from 60
TokenManipulationIntervalSeconds = 20 # Increased from 15
StartupMonitorIntervalSeconds = 120 # Increased from 60
BrowserExtensionIntervalSeconds = 300 # Increased from 120
FirewallRuleIntervalSeconds = 120 # Increased from 60
ShadowCopyIntervalSeconds = 30 # Keep at 30 (ransomware critical)
USBMonitorIntervalSeconds = 15 # Increased from 10
ClipboardMonitorIntervalSeconds = 10 # Increased from 5
EventLogMonitorIntervalSeconds = 60 # Increased from 30
ProcessHollowingIntervalSeconds = 30 # Increased from 20
KeyloggerDetectorIntervalSeconds = 30 # Increased from 15
RansomwareBehaviorIntervalSeconds = 15 # Increased from 10 (critical)
}
function Invoke-CacheMaintenanceTick {
try {
# Enforce FileHashCache max size (ConcurrentDictionary)
if ($Script:FileHashCache -and $Script:MaxCacheSize -and $Script:FileHashCache.Count -gt $Script:MaxCacheSize) {
$removeCount = [Math]::Min(($Script:FileHashCache.Count - $Script:MaxCacheSize), 500)
if ($removeCount -gt 0) {
$keysToRemove = $Script:FileHashCache.Keys | Select-Object -First $removeCount
foreach ($k in $keysToRemove) {
$dummy = $null
[void]$Script:FileHashCache.TryRemove($k, [ref]$dummy)
}
}
}
# Enforce scanned files max size (hashtable)
$scannedVar = Get-Variable -Name scannedFiles -Scope Script -ErrorAction SilentlyContinue
if ($scannedVar -and $script:scannedFiles -and $Script:ManagedJobConfig.ScannedFilesMaxCount) {
if ($script:scannedFiles.Count -gt [int]$Script:ManagedJobConfig.ScannedFilesMaxCount) {
$removeCount = [Math]::Min(($script:scannedFiles.Count - [int]$Script:ManagedJobConfig.ScannedFilesMaxCount), 2000)
if ($removeCount -gt 0) {
$keysToRemove = @($script:scannedFiles.Keys | Select-Object -First $removeCount)
foreach ($k in $keysToRemove) {
$script:scannedFiles.Remove($k)
}
}
}
}
} catch {
try {
Write-ErrorLog -Message "Cache maintenance tick failed" -Severity "Low" -ErrorRecord $_
} catch {}
}
}
$Script:ManagedJobDefinitions = @(
@{ Name = 'MalwareScan'; Enabled = $true; Critical = $true; IntervalSeconds = $Script:ManagedJobConfig.MalwareScanIntervalSeconds; ScriptBlock = { Invoke-MalwareScan } }
@{ Name = 'LogRotation'; Enabled = $true; Critical = $false; IntervalSeconds = $Script:ManagedJobConfig.LogRotationIntervalSeconds; ScriptBlock = { Invoke-LogRotation } }
@{ Name = 'MemoryCleanup'; Enabled = $true; Critical = $false; IntervalSeconds = $Script:ManagedJobConfig.MemoryCleanupIntervalSeconds; ScriptBlock = { Invoke-MemoryCleanup } }
@{ Name = 'CacheMaintenance'; Enabled = $true; Critical = $false; IntervalSeconds = $Script:ManagedJobConfig.CacheMaintenanceIntervalSeconds; ScriptBlock = { Invoke-CacheMaintenanceTick } }
@{ Name = 'SecurityReport'; Enabled = $true; Critical = $false; IntervalSeconds = $Script:ManagedJobConfig.SecurityReportIntervalSeconds; ScriptBlock = {
$reportPath = New-SecurityReport
if ($reportPath) {
Write-Host "[REPORT] Generated: $reportPath" -ForegroundColor Cyan
}
}
}
@{ Name = 'ManagedJobsHealth'; Enabled = $true; Critical = $false; IntervalSeconds = $Script:ManagedJobConfig.HealthCheckIntervalSeconds; ScriptBlock = {
try {
$runningManaged = Get-ManagedJobsRunningCount
if ($runningManaged -lt 4) {
Write-Host "[!] WARNING: Some managed jobs may be disabled/stopped! Check logs for details." -ForegroundColor Yellow
Write-Log "[WARNING] Detected fewer active managed jobs than expected. Active: $runningManaged"
}
} catch {}
}
}
)
function Initialize-ManagedJobs {
if (-not $script:ManagedJobs) {
$script:ManagedJobs = @{}
}
if (-not $Script:Base -and $Base) {
$Script:Base = $Base
}
foreach ($def in $Script:ManagedJobDefinitions) {
Register-ManagedJob `
-Name $def.Name `
-Enabled ([bool]$def.Enabled) `
-Critical ([bool]$def.Critical) `
-IntervalSeconds ([int]$def.IntervalSeconds) `
-MaxRestartAttempts ([int]$Script:ManagedJobConfig.MaxRestartAttempts) `
-RestartDelaySeconds ([int]$Script:ManagedJobConfig.RestartDelaySeconds) `
-ScriptBlock $def.ScriptBlock
}
}
Initialize-ManagedJobs
try {
while ($true) {
# Check for Ctrl+C via keyboard input instead of relying on trap
try {
if ([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true)
switch ($key.Key) {
'H' {
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " ANTIVIRUS KEYBOARD SHORTCUTS" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "[H] - Show this help menu" -ForegroundColor White
Write-Host "[M] - Open Exclusion Manager" -ForegroundColor White
Write-Host "[R] - Generate Security Report" -ForegroundColor White
Write-Host "[Ctrl+C] - Stop antivirus (requires 5 attempts)" -ForegroundColor White
Write-Host "========================================`n" -ForegroundColor Cyan
}
'M' {
Show-ExclusionManager
}
'R' {
$reportPath = New-SecurityReport
Write-Host "[+] Security report generated: $reportPath" -ForegroundColor Green
}
}
}
} catch [System.InvalidOperationException] {
# Console redirected or not available - skip keyboard handling
} catch {
# Ignore other keyboard handling errors
}
# Handle Ctrl+C press
try {
if ([Console]::KeyAvailable) {
$consoleKey = [Console]::ReadKey($true)
if ($consoleKey.Modifiers -band [ConsoleModifiers]::Control -and $consoleKey.Key -eq [ConsoleKey]::C) {
$Script:TerminationAttempts++
Write-Host "`n[PROTECTION] Termination attempt detected ($Script:TerminationAttempts/$Script:MaxTerminationAttempts)" -ForegroundColor Red
if ($Script:TerminationAttempts -ge $Script:MaxTerminationAttempts) {
Write-Host "[PROTECTION] Maximum termination attempts reached. Shutting down..." -ForegroundColor Yellow
Write-SecurityEvent -EventType "ScriptTerminated" -Details @{ PID = $PID; TotalAttempts = $Script:TerminationAttempts } -Severity "Critical"
break # Exit the loop
} else {
Write-Host "[PROTECTION] Termination blocked. Press Ctrl+C $($Script:MaxTerminationAttempts - $Script:TerminationAttempts) more times to force stop." -ForegroundColor Yellow
Start-Sleep -Milliseconds 500
continue # Skip the rest of the loop iteration
}
}
}
} catch [System.InvalidOperationException] {
# Console not available - Ctrl+C protection won't work but that's okay
} catch {
# Ignore other errors
}
# Check for script integrity
# DISABLED: This check causes immediate exit when script is edited
# The background integrity monitor job still runs every 60 seconds
# if (-not (Test-ScriptIntegrity)) {
# Write-Host "[CRITICAL] Script integrity check failed. Entering fail-safe mode." -ForegroundColor Red
# Write-ErrorLog -Message "Script integrity compromised. Exiting." -Severity "Critical"
# break # Exit the loop
# }
# Check for script integrity
if (-not (Test-ScriptIntegrity)) {
Write-Host "[CRITICAL] Script integrity check failed. Entering fail-safe mode." -ForegroundColor Red
# Assuming Enter-FailSafeMode is defined elsewhere or this is a placeholder for error handling
# If it's not defined, this line might cause an error. For now, we'll log and exit.
Write-ErrorLog -Message "Script integrity compromised. Exiting." -Severity "Critical"
break # Exit the loop
}
Invoke-ManagedJobsTick -NowUtc ([DateTime]::UtcNow)
$Script:LoopCounter++
Start-Sleep -Seconds 1
}
} finally {
# Cleanup code
Write-Host "`n[*] Shutting down antivirus..." -ForegroundColor Yellow
# Background jobs (KeyScrambler, ProcessWatchdog, hash reputation) will terminate when this process exits.
# Managed in-process jobs do not need explicit cleanup.
# Release mutex
if ($Script:SecurityMutex) {
try {
$Script:SecurityMutex.ReleaseMutex()
$Script:SecurityMutex.Dispose()
Write-Host "[PROTECTION] Mutex released successfully." -ForegroundColor Green
} catch {
Write-Log "Warning: Failed to release mutex: $($_.Exception.Message)"
}
}
# Clear sensitive data from memory
Protect-SensitiveData
Write-Host "[*] Antivirus stopped." -ForegroundColor Green
}
Editor is loading...
Leave a Comment