Antivirus by Gorstak
unknown
powershell
3 months ago
418 kB
5
No Index
# Antivirus.ps1 - Single-file EDR (generated from current Bin logic)
# Author: Gorstak | Usage: .\Antivirus.ps1 | .\Antivirus.ps1 -RemoveRules
#Requires -RunAsAdministrator
param(
[Parameter(Mandatory=$false)][switch]$RemoveRules = $false,
[Parameter(Mandatory=$false)][string]$Module = $null
)
$script:ScriptRoot = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
# --- OptimizedConfig ---
# Optimized configuration for EDR modules
# Provides tick intervals, scan limits, batch settings, CPU throttling
#Requires -Version 5.1
$script:ModuleTickIntervals = @{
"HashDetection" = 90
"ResponseEngine" = 180
"MemoryScanning" = 90
"BeaconDetection" = 60
"NetworkTrafficMonitoring" = 45
"AMSIBypassDetection" = 90
"ProcessAnomalyDetection" = 90
"EventLogMonitoring" = 90
"FileEntropyDetection" = 120
"Initializer" = 300
"PrivacyForgeSpoofing" = 60
}
$script:ScanLimits = @{
"MaxFiles" = 500
"MaxProcesses" = 500
"MaxConnections" = 1000
"MaxEvents" = 500
"SampleSizeBytes" = 4096
}
function Get-TickInterval {
param([string]$ModuleName)
if ($script:ModuleTickIntervals.ContainsKey($ModuleName)) {
return $script:ModuleTickIntervals[$ModuleName]
}
return 90
}
function Get-ScanLimit {
param([string]$LimitName)
if ($script:ScanLimits.ContainsKey($LimitName)) {
return $script:ScanLimits[$LimitName]
}
return 500
}
function Get-BatchSettings {
return @{
BatchSize = 50
BatchDelayMs = 10
}
}
function Get-LoopSleep {
return 5
}
function Test-CPULoadThreshold {
try {
$cpu = Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue
if ($cpu -and $cpu.CounterSamples[0].CookedValue -gt 90) { return $true }
} catch { }
return $false
}
function Get-CPULoad {
try {
$cpu = Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue
if ($cpu) { return [int]$cpu.CounterSamples[0].CookedValue }
} catch { }
return 0
}
function Write-Log {
param(
[string]$Level,
[string]$Message,
[string]$Category = ""
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$output = "[$timestamp] [$Level] $Message"
if ($Category) {
$output = "[$timestamp] [$Level] [$Category] $Message"
}
Write-Output $output
}
# --- CacheManager ---
# Cache Manager Module
# Provides caching to reduce repeated expensive operations
#Requires -Version 5.1
$script:SignatureCache = @{}
$script:HashCache = @{}
$script:ProcessCache = @{}
$script:LastCacheClean = Get-Date
function Get-CachedSignature {
param([string]$FilePath)
$now = Get-Date
$ttl = 60 # minutes
if ($script:SignatureCache.ContainsKey($FilePath)) {
$cached = $script:SignatureCache[$FilePath]
if (($now - $cached.Timestamp).TotalMinutes -lt $ttl) {
return $cached.Value
}
}
try {
$sig = Get-AuthenticodeSignature -FilePath $FilePath -ErrorAction SilentlyContinue
$script:SignatureCache[$FilePath] = @{
Value = $sig
Timestamp = $now
}
return $sig
} catch {
return $null
}
}
function Get-CachedFileHash {
param(
[string]$FilePath,
[string]$Algorithm = "MD5"
)
$now = Get-Date
$ttl = 120 # minutes
$key = "$FilePath|$Algorithm"
if ($script:HashCache.ContainsKey($key)) {
$cached = $script:HashCache[$key]
$fileInfo = Get-Item $FilePath -ErrorAction SilentlyContinue
if ($fileInfo -and $cached.LastWrite -eq $fileInfo.LastWriteTime -and ($now - $cached.Timestamp).TotalMinutes -lt $ttl) {
return $cached.Value
}
}
try {
$fileInfo = Get-Item $FilePath -ErrorAction SilentlyContinue
if (-not $fileInfo) { return $null }
$hash = (Get-FileHash -Path $FilePath -Algorithm $Algorithm -ErrorAction SilentlyContinue).Hash
$script:HashCache[$key] = @{
Value = $hash
Timestamp = $now
LastWrite = $fileInfo.LastWriteTime
}
return $hash
} catch {
return $null
}
}
function Clear-ExpiredCache {
$now = Get-Date
if (($now - $script:LastCacheClean).TotalMinutes -lt 30) {
return
}
$script:LastCacheClean = $now
# Clean signature cache
$expiredSigs = $script:SignatureCache.Keys | Where-Object {
($now - $script:SignatureCache[$_].Timestamp).TotalMinutes -gt 60
}
foreach ($key in $expiredSigs) {
$script:SignatureCache.Remove($key)
}
# Clean hash cache
$expiredHashes = $script:HashCache.Keys | Where-Object {
($now - $script:HashCache[$_].Timestamp).TotalMinutes -gt 120
}
foreach ($key in $expiredHashes) {
$script:HashCache.Remove($key)
}
}
# --- Initializer ---
# Initializer Module
# Sets up the Antivirus EDR environment - runs once at startup - Optimized for low resource usage
$ModuleName = "Initializer"
$script:LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$Initialized = $false
function Invoke-Initialization {
try {
Write-Output "STATS:$ModuleName`:Starting environment initialization"
# Create required directories
$directories = @(
"$env:ProgramData\Antivirus",
"$env:ProgramData\Antivirus\Logs",
"$env:ProgramData\Antivirus\Data",
"$env:ProgramData\Antivirus\Modules",
"$env:ProgramData\Antivirus\Quarantine",
"$env:ProgramData\Antivirus\Reports"
)
foreach ($dir in $directories) {
if (-not (Test-Path $dir)) {
New-Item -Path $dir -ItemType Directory -Force | Out-Null
Write-Output "STATS:$ModuleName`:Created directory: $dir"
}
}
# Initialize log files with headers
$logFiles = @(
"$env:ProgramData\Antivirus\Logs\System_$(Get-Date -Format 'yyyy-MM-dd').log",
"$env:ProgramData\Antivirus\Logs\Threats_$(Get-Date -Format 'yyyy-MM-dd').log",
"$env:ProgramData\Antivirus\Logs\Responses_$(Get-Date -Format 'yyyy-MM-dd').log"
)
foreach ($logFile in $logFiles) {
if (-not (Test-Path $logFile)) {
$header = "# Antivirus EDR Log - Created $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n# Format: Timestamp|Module|Action|Details`n"
Set-Content -Path $logFile -Value $header
Write-Output "STATS:$ModuleName`:Initialized log: $logFile"
}
}
# Create Event Log source if it doesn't exist
try {
if (-not [System.Diagnostics.EventLog]::SourceExists("AntivirusEDR")) {
[System.Diagnostics.EventLog]::CreateEventSource("AntivirusEDR", "Application")
Write-Output "STATS:$ModuleName`:Created Event Log source: AntivirusEDR"
}
} catch {
Write-Output "ERROR:$ModuleName`:Failed to create Event Log source: $_"
}
# Initialize configuration file
$configFile = "$env:ProgramData\Antivirus\Data\config.json"
if (-not (Test-Path $configFile)) {
$defaultConfig = @{
Version = "1.0"
Initialized = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
LastUpdate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
Settings = @{
MaxLogSizeMB = 100
QuarantineRetentionDays = 30
EnableRealTimeResponse = $true
ResponseSeverity = "Medium"
}
}
$defaultConfig | ConvertTo-Json -Depth 3 | Set-Content -Path $configFile
Write-Output "STATS:$ModuleName`:Created configuration file"
}
# Create status tracking file
$statusFile = "$env:ProgramData\Antivirus\Data\agent_status.json"
if (-not (Test-Path $statusFile)) {
$statusTemplate = @{
LastCheck = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
ActiveAgents = @()
SystemHealth = "Healthy"
TotalDetections = 0
TotalResponses = 0
}
$statusTemplate | ConvertTo-Json -Depth 3 | Set-Content -Path $statusFile
Write-Output "STATS:$ModuleName`:Created status tracking file"
}
# Log initialization completion
$initLog = "$env:ProgramData\Antivirus\Logs\System_$(Get-Date -Format 'yyyy-MM-dd').log"
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|Initializer|Environment|Antivirus EDR environment initialized successfully" | Add-Content -Path $initLog
Write-Output "DETECTION:$ModuleName`:Environment initialization completed"
return 1
} catch {
Write-Output "ERROR:$ModuleName`:Initialization failed: $_"
return 0
}
}
function Start-Module {
$loopSleep = Get-LoopSleep
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high (only for maintenance, not init)
if ($Initialized -and (Test-CPULoadThreshold)) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping check"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
if (-not $Initialized) {
$count = Invoke-Initialization
if ($count -gt 0) {
$Initialized = $true
$TickInterval = Get-TickInterval -ModuleName $ModuleName # Use optimized interval
Write-Output "STATS:$ModuleName`:Initialization complete - switching to maintenance mode"
}
} else {
# Maintenance mode - just check system health
$statusFile = "$env:ProgramData\Antivirus\Data\agent_status.json"
if (Test-Path $statusFile) {
$status = Get-Content $statusFile | ConvertFrom-Json
$status.LastCheck = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
$status | ConvertTo-Json -Depth 3 | Set-Content -Path $statusFile
Write-Output "STATS:$ModuleName`:System health check completed"
} else {
Write-Output "STATS:$ModuleName`:Status file not found, system may need re-initialization"
}
}
$script:LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- HashDetection ---
# Optimized Hash-based Malware Detection Module
# Significantly reduced file I/O and CPU usage
$ModuleName = "HashDetection"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:ThreatHashes = @{}
$script:ScannedFiles = @{}
$script:Initialized = $false
function Initialize-HashDatabaseOptimized {
if ($script:Initialized) {
return
}
$threatPath = "$env:ProgramData\Antivirus\HashDatabase\threats.txt"
if (Test-Path $threatPath) {
Get-Content $threatPath -ErrorAction SilentlyContinue | ForEach-Object {
if ($_ -match '^([A-F0-9]{32,64})$') {
$script:ThreatHashes[$matches[1].ToUpper()] = $true
}
}
}
$script:Initialized = $true
}
function Invoke-HashScanOptimized {
$threatsFound = @()
$batchSettings = Get-BatchSettings
$maxFiles = Get-ScanLimit -LimitName "MaxFiles"
Initialize-HashDatabaseOptimized
$scanPaths = @("$env:APPDATA", "$env:LOCALAPPDATA\Temp", "$env:USERPROFILE\Downloads")
$scannedCount = 0
foreach ($scanPath in $scanPaths) {
if (-not (Test-Path $scanPath)) { continue }
if ($scannedCount -ge $maxFiles) { break }
try {
$cutoff = (Get-Date).AddHours(-1)
$files = Get-ChildItem -Path $scanPath -Include *.exe,*.dll -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt $cutoff } |
Select-Object -First ($maxFiles - $scannedCount)
$batchCount = 0
foreach ($file in $files) {
$scannedCount++
if ($script:ScannedFiles.ContainsKey($file.FullName)) {
$cached = $script:ScannedFiles[$file.FullName]
if ($cached.LastWrite -eq $file.LastWriteTime) {
continue
}
}
$batchCount++
if ($batchCount % $batchSettings.BatchSize -eq 0 -and $batchSettings.BatchDelayMs -gt 0) {
Start-Sleep -Milliseconds $batchSettings.BatchDelayMs
}
$hash = Get-CachedFileHash -FilePath $file.FullName -Algorithm "SHA256"
if (-not $hash) { continue }
# Mark as scanned
$script:ScannedFiles[$file.FullName] = @{
LastWrite = $file.LastWriteTime
Hash = $hash
}
# Check against threat database
if ($script:ThreatHashes.ContainsKey($hash.ToUpper())) {
$threatsFound += @{
File = $file.FullName
Hash = $hash
Threat = "Known Malware Hash"
}
}
}
} catch {
continue
}
}
if ($threatsFound.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\HashDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
$threatsFound | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|THREAT|$($_.File)|$($_.Hash)" | Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($threatsFound.Count) hash-based threats"
}
Write-Output "STATS:$ModuleName`:Scanned=$scannedCount,Threats=$($threatsFound.Count)"
$now = Get-Date
$oldKeys = $script:ScannedFiles.Keys | Where-Object {
-not (Test-Path $_)
}
foreach ($key in $oldKeys) {
$script:ScannedFiles.Remove($key)
}
Clear-ExpiredCache
return $threatsFound.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Start-Sleep -Seconds (Get-Random -Minimum 10 -Maximum 60)
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-HashScanOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 60
}
}
}
# --- AMSIBypassDetection ---
# Detects AMSI bypass techniques (amsi.dll patching, etc.)
# Converted from GEDR C# job: JobAMSIBypassDetection
# Enhanced with full implementation from C# version
#Requires -Version 5.1
$ModuleName = "AMSIBypassDetection"
$script:Initialized = $false
# AMSI bypass patterns
$script:BypassPatterns = @(
"AmsiUtils",
"AmsiScanBuffer",
"amsiInitFailed",
"Bypass",
"amsi.dll",
"amsiutils",
"PatchAmsi",
"DisableAmsi",
"Remove-Amsi",
"Invoke-AmsiBypass",
"AMSI.*bypass",
"bypass.*AMSI",
"-nop.*-w.*hidden.*-enc",
"amsi.*off",
"amsi.*disable",
"Set-Amsi",
"Override.*AMSI",
"System.Management.Automation.AmsiUtils",
"AmsiContext",
"AMSI_RESULT_CLEAN",
"ForceAmsi",
"Hacking",
"Context"
)
function Initialize-AMSIBypassDetection {
if ($script:Initialized) {
return
}
Write-Log "INFO" "Initializing AMSI Bypass Detection module"
$script:Initialized = $true
}
function Get-ProcessList {
# Get process list similar to GEDR's EdrProcess.GetProcesses
try {
$processes = Get-CimInstance -ClassName Win32_Process -ErrorAction SilentlyContinue |
Select-Object ProcessId, Name, CommandLine, ExecutablePath, ParentProcessId |
Where-Object { $_.ProcessId -ne $PID }
return $processes
}
catch {
Write-Log "ERROR" "Failed to get process list: $($_.Exception.Message)"
return @()
}
}
function Test-ScriptHostProcess {
param([string]$ProcessName)
if ([string]::IsNullOrEmpty($ProcessName)) {
return $false
}
$nameLower = $ProcessName.ToLowerInvariant()
return ($nameLower.Contains("powershell") -or
$nameLower.Contains("pwsh") -or
$nameLower.Contains("wscript") -or
$nameLower.Contains("cscript"))
}
function Test-AMSIBypassPattern {
param([string]$CommandLine)
if ([string]::IsNullOrEmpty($CommandLine)) {
return $false
}
$cmdLower = $CommandLine.ToLowerInvariant()
foreach ($pattern in $script:BypassPatterns) {
if ($cmdLower -match $pattern) {
return $true
}
}
return $false
}
function Test-ObfuscatedCommand {
param([string]$CommandLine)
if ([string]::IsNullOrEmpty($CommandLine)) {
return $false
}
$cmdLower = $CommandLine.ToLowerInvariant()
return ($cmdLower.Contains("-enc") -and
$cmdLower.Contains("-encodedcommand") -and
$CommandLine.Length -gt 500)
}
function Test-AMSIDisabledRegistry {
# Check if AMSI is disabled via registry
try {
# Check common AMSI bypass registry locations
$amsiPaths = @(
"HKLM:\SOFTWARE\Microsoft\AMSI",
"HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings",
"HKCU:\SOFTWARE\Microsoft\Windows Script Host\Settings"
)
foreach ($path in $amsiPaths) {
if (Test-Path $path) {
$properties = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
if ($properties) {
# Check for common AMSI disable values
foreach ($prop in $properties.PSObject.Properties) {
if ($prop.Name -like "*amsi*" -or $prop.Name -like "*AMSI*") {
if ($prop.Value -eq 0 -or $prop.Value -eq "Disabled" -or $prop.Value -eq $false) {
return $true
}
}
}
}
}
}
return $false
}
catch {
Write-Log "ERROR" "Error checking AMSI registry: $($_.Exception.Message)"
return $false
}
}
function Invoke-ThreatResponse {
param(
[int]$ProcessId,
[string]$ProcessName,
[string]$ExecutablePath,
[string]$ThreatLevel
)
try {
# Log threat response
Write-Log "ACTION" "THREAT RESPONSE: Triggering response for $ProcessName (PID: $ProcessId) - Level: $ThreatLevel" -Category "threat_responses"
# Implement response actions based on threat level
switch ($ThreatLevel) {
"High" {
# For high threats, consider terminating process
if ($ProcessId -gt 0) {
try {
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
Write-Log "ACTION" "THREAT RESPONSE: Terminated high-risk process $ProcessName (PID: $ProcessId)" -Category "threat_responses"
}
catch {
Write-Log "WARNING" "Failed to terminate process $ProcessName (PID: $ProcessId): $($_.Exception.Message)"
}
}
# Quarantine file if it exists
if ($ExecutablePath -and (Test-Path $ExecutablePath)) {
Invoke-QuarantineFile -FilePath $ExecutablePath -Reason "AMSI bypass detection"
}
}
"Critical" {
# For critical threats, always terminate and quarantine
if ($ProcessId -gt 0) {
try {
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
Write-Log "ACTION" "THREAT RESPONSE: Terminated critical process $ProcessName (PID: $ProcessId)" -Category "threat_responses"
}
catch {
Write-Log "WARNING" "Failed to terminate process $ProcessName (PID: $ProcessId): $($_.Exception.Message)"
}
}
if ($ExecutablePath -and (Test-Path $ExecutablePath)) {
Invoke-QuarantineFile -FilePath $ExecutablePath -Reason "Critical AMSI bypass detection"
}
}
}
}
catch {
Write-Log "ERROR" "Error in threat response: $($_.Exception.Message)"
}
}
function Invoke-QuarantineFile {
param([string]$FilePath, [string]$Reason)
try {
$quarantinePath = "$env:ProgramData\Antivirus\Quarantine"
if (-not (Test-Path $quarantinePath)) {
New-Item -Path $quarantinePath -ItemType Directory -Force | Out-Null
}
$fileName = Split-Path $FilePath -Leaf
$quarantineFile = Join-Path $quarantinePath "$fileName-$([Guid]::NewGuid()).quar"
Move-Item -Path $FilePath -Destination $quarantineFile -Force
Write-Log "ACTION" "QUARANTINE: $FilePath moved to $quarantineFile (Reason: $Reason)" -Category "quarantine"
}
catch {
Write-Log "ERROR" "Failed to quarantine file $FilePath`: $($_.Exception.Message)"
}
}
function Invoke-AMSIBypassDetection {
try {
Initialize-AMSIBypassDetection
$script:ThreatCount = 0
$processes = Get-ProcessList
Write-Log "INFO" "Scanning $($processes.Count) processes for AMSI bypass patterns"
foreach ($process in $processes) {
$processName = if ($process.Name) { $process.Name } else { "" }
$cmdLine = if ($process.CommandLine) { $process.CommandLine } else { "" }
# Only check script host processes
if (-not (Test-ScriptHostProcess -ProcessName $processName)) {
continue
}
if ([string]::IsNullOrEmpty($cmdLine)) {
continue
}
# Check for AMSI bypass patterns
if (Test-AMSIBypassPattern -CommandLine $cmdLine) {
$cmdLower = $cmdLine.ToLowerInvariant()
$pattern = $script:BypassPatterns | Where-Object { $cmdLower -match $_ } | Select-Object -First 1
$dedupeKey = "AMSIBypass_$($process.ProcessId)_$($pattern)"
if (Test-Deduplication -Key $dedupeKey -Category "amsi_bypass") {
Write-Log "THREAT" "AMSI BYPASS: $($process.Name) (PID: $($process.ProcessId)) - Pattern: $pattern" -Category "amsi_bypass_detections"
$script:ThreatCount++
}
# Trigger threat response
Invoke-ThreatResponse -ProcessId $process.ProcessId -ProcessName $process.Name -ExecutablePath $process.ExecutablePath -ThreatLevel "High"
}
# Check for obfuscated commands
if (Test-ObfuscatedCommand -CommandLine $cmdLine) {
$dedupeKey = "AMSIBypass_$($process.ProcessId)_obfuscated"
if (Test-Deduplication -Key $dedupeKey -Category "amsi_bypass") {
Write-Log "THREAT" "AMSI BYPASS (obfuscated): $($process.Name) (PID: $($process.ProcessId)) - Long encoded command" -Category "amsi_bypass_detections"
$script:ThreatCount++
}
# Trigger critical threat response for obfuscated commands
Invoke-ThreatResponse -ProcessId $process.ProcessId -ProcessName $process.Name -ExecutablePath $process.ExecutablePath -ThreatLevel "Critical"
}
}
# Check registry for AMSI disable
if (Test-AMSIDisabledRegistry) {
$dedupeKey = "AMSIBypass_Registry"
if (Test-Deduplication -Key $dedupeKey -Category "amsi_bypass") {
Write-Log "THREAT" "AMSI BYPASS: Registry tampering detected - AMSI disabled via registry" -Category "amsi_bypass_detections"
$script:ThreatCount++
}
}
Write-Log "INFO" "AMSI bypass detection completed. Threats found: $script:ThreatCount"
}
catch {
Write-Log "ERROR" "Error in AMSIBypassDetection: $($_.Exception.Message)"
}
}
# Module exports - scheduler will call Invoke-AMSIBypassDetection on appropriate interval
# Alias for backward compatibility
function Invoke-AMSIBypassScan {
Invoke-AMSIBypassDetection
}
# --- GFocus ---
# GFocus.ps1 - Network traffic monitor + firewall rule cleanup
# Single script: monitor mode (default) or -RemoveRules to clear NTM_Block_* / BlockedConnection_*
# Monitors IPs; blocks or allows or removes block as the user surfs. Address bar is inferred from
# browser 80/443 connections (no extension): first 80/443 = navigation, within 30s = dependencies.
# Browsers only: games and other apps are never monitored or blocked. Block rules are
# browser-specific (program= + remoteip). DNS IPs (8.8.8.8, 1.1.1.1, etc.) are never blocked.
# Requires Administrator privileges
function Write-ColorOutput {
param([string]$Message, [string]$Color = "White")
Write-Host $Message -ForegroundColor $Color
}
# --- Remove-rules mode: clear NTM_Block_* and BlockedConnection_*, then exit ---
function Remove-BlockedRules {
Write-ColorOutput "Removing all blocked connection rules..." "Yellow"
$totalRemoved = 0
$blockedRules = Get-NetFirewallRule -DisplayName "BlockedConnection_*" -ErrorAction SilentlyContinue
if ($blockedRules) {
$count = ($blockedRules | Measure-Object).Count
Write-ColorOutput "Found $count BlockedConnection_* rule(s)." "Cyan"
foreach ($Rule in $blockedRules) {
Remove-NetFirewallRule -DisplayName $Rule.DisplayName -ErrorAction SilentlyContinue
Write-ColorOutput " Removed: $($Rule.DisplayName)" "Green"
$totalRemoved++
}
} else {
Write-ColorOutput "No BlockedConnection_* rules found." "Gray"
}
$out = netsh advfirewall firewall show rule name=all 2>&1 | Out-String
$ruleMatches = [regex]::Matches($out, 'Rule Name:\s*(NTM_Block_[^\s\r\n]+)')
$ntmList = @($ruleMatches | ForEach-Object { $_.Groups[1].Value.Trim() } | Sort-Object -Unique)
if ($ntmList.Count -gt 0) {
Write-ColorOutput "Found $($ntmList.Count) NTM_Block_* rule(s)." "Cyan"
foreach ($name in $ntmList) {
$del = netsh advfirewall firewall delete rule name="$name" 2>&1 | Out-String
if ($del -notmatch 'No rules match') {
Write-ColorOutput " Removed: $name" "Green"
$totalRemoved++
}
}
} else {
Write-ColorOutput "No NTM_Block_* rules found." "Gray"
}
if ($totalRemoved -gt 0) {
Write-ColorOutput "`nDone. Removed $totalRemoved rule(s) in total." "Green"
} else {
Write-ColorOutput "`nNo blocked connection rules to remove." "Gray"
}
}
# --- Monitor mode (NTM) ---
$script:AllowedDomains = @()
$script:AllowedIPs = @()
$script:BlockedConnections = @{}
$script:MonitoringActive = $true
$script:CurrentBrowserConnections = @{}
# Browsers only: monitoring and blocking apply solely to these processes.
$BrowserProcesses = @(
'chrome', 'firefox', 'msedge', 'iexplore', 'opera', 'brave', 'vivaldi', 'waterfox', 'palemoon',
'seamonkey', 'librewolf', 'tor', 'dragon', 'iridium', 'chromium', 'maxthon', 'slimjet', 'citrio',
'blisk', 'sidekick', 'epic', 'ghostery', 'falkon', 'kinza', 'orbitum', 'coowon', 'coc_coc_browser',
'browser', 'qqbrowser', 'ucbrowser', '360chrome', '360se', 'sleipnir', 'k-meleon', 'basilisk',
'floorp', 'pulse', 'naver', 'whale', 'coccoc', 'yandex', 'avastbrowser', 'asb', 'avgbrowser',
'ccleanerbrowser', 'dcbrowser', 'edge', 'edgedev', 'edgebeta', 'edgecanary', 'operagx', 'operaneon',
'bravesoftware', 'browsex', 'browsec', 'comet', 'elements', 'flashpeak', 'surf'
)
# Gaming (and all non-browser apps) are never monitored or blocked — explicitly unhindered.
$GamingProcesses = @(
'steam', 'steamwebhelper', 'epicgameslauncher', 'origin', 'battle.net', 'eadesktop', 'ea app',
'ubisoft game launcher', 'gog galaxy', 'rungame', 'gamebar', 'gameservices', 'overwolf'
)
# Never block these IPs (common DNS). Blocking them would break resolution for everyone.
$NeverBlockIPs = @('8.8.8.8', '8.8.4.4', '1.1.1.1', '1.0.0.1')
function Remove-BlockRulesForIP {
param([string]$RemoteAddress)
$safeName = $RemoteAddress -replace '\.', '_' -replace ':', '_'
$prefix = "NTM_Block_${safeName}_"
$out = netsh advfirewall firewall show rule name=all 2>&1 | Out-String
$ruleMatches = [regex]::Matches($out, 'Rule Name:\s*(NTM_Block_[^\s\r\n]+)')
foreach ($m in $ruleMatches) {
$name = $m.Groups[1].Value.Trim()
if ($name -like "${prefix}*") {
$del = netsh advfirewall firewall delete rule name="$name" 2>&1 | Out-String
if ($del -notmatch 'No rules match') {
Write-ColorOutput "REMOVED BLOCK (user surfed): $RemoteAddress -> $name" "Green"
}
}
}
$toRemove = @($script:BlockedConnections.Keys | Where-Object { $_ -like "${RemoteAddress}|*" })
foreach ($k in $toRemove) { $script:BlockedConnections.Remove($k) }
}
function Add-AllowedDomain {
param([string]$Domain)
$Domain = $Domain -replace '^https?://', '' -replace '/$', ''
$Domain = ($Domain -split '/')[0]
if ($Domain -match '^[\d\.]+$' -or $Domain -match '^[\da-f:]+$') {
if ($script:AllowedIPs -notcontains $Domain) {
$script:AllowedIPs += $Domain
Write-ColorOutput "Added allowed IP: $Domain" "Green"
Remove-BlockRulesForIP -RemoteAddress $Domain
}
return
}
if ($script:AllowedDomains -notcontains $Domain) {
$script:AllowedDomains += $Domain
Write-ColorOutput "Added allowed domain: $Domain" "Green"
try {
$IPs = [System.Net.Dns]::GetHostAddresses($Domain) | ForEach-Object { $_.IPAddressToString }
foreach ($IP in $IPs) {
if ($script:AllowedIPs -notcontains $IP) {
$script:AllowedIPs += $IP
Write-ColorOutput " Resolved IP: $IP" "Gray"
Remove-BlockRulesForIP -RemoteAddress $IP
}
}
} catch {
Write-ColorOutput " Warning: Could not resolve domain to IP" "Yellow"
}
}
}
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 }
if ($script:AllowedIPs -contains $RemoteAddress) { return $true }
return $false
}
function Watch-BrowserActivity {
param([string]$RemoteAddress, [string]$ProcessName, [int]$RemotePort)
if ($BrowserProcesses -contains $ProcessName.ToLower()) {
$Now = Get-Date
$RecentNavigationTime = $null
foreach ($BrowserIP in $script:CurrentBrowserConnections.Keys) {
$ConnectionTime = $script:CurrentBrowserConnections[$BrowserIP]
$TimeDiff = ($Now - $ConnectionTime).TotalSeconds
if ($TimeDiff -le 30) {
if ($null -eq $RecentNavigationTime -or $ConnectionTime -gt $RecentNavigationTime) {
$RecentNavigationTime = $ConnectionTime
}
}
}
if ($null -ne $RecentNavigationTime) {
if ($script:AllowedIPs -notcontains $RemoteAddress) {
$script:AllowedIPs += $RemoteAddress
Write-ColorOutput "DEPENDENCY: Allowing $RemoteAddress (linked to browser navigation)" "Gray"
Remove-BlockRulesForIP -RemoteAddress $RemoteAddress
}
return $true
}
elseif ($RemotePort -eq 443 -or $RemotePort -eq 80) {
if ($script:AllowedIPs -notcontains $RemoteAddress) {
$script:AllowedIPs += $RemoteAddress
Write-ColorOutput "BROWSER NAVIGATION: Allowing $RemoteAddress and its dependencies" "Cyan"
Remove-BlockRulesForIP -RemoteAddress $RemoteAddress
}
$script:CurrentBrowserConnections[$RemoteAddress] = $Now
return $true
}
else {
if ($script:AllowedIPs -notcontains $RemoteAddress) {
$script:AllowedIPs += $RemoteAddress
Write-ColorOutput "BROWSER: Allowing $RemoteAddress" "DarkCyan"
Remove-BlockRulesForIP -RemoteAddress $RemoteAddress
}
return $true
}
}
$Now = Get-Date
foreach ($BrowserIP in $script:CurrentBrowserConnections.Keys) {
$ConnectionTime = $script:CurrentBrowserConnections[$BrowserIP]
$TimeDiff = ($Now - $ConnectionTime).TotalSeconds
if ($TimeDiff -le 30) {
if ($script:AllowedIPs -notcontains $RemoteAddress) {
$script:AllowedIPs += $RemoteAddress
Write-ColorOutput "DEPENDENCY: Allowing $RemoteAddress (linked to browser navigation)" "Gray"
Remove-BlockRulesForIP -RemoteAddress $RemoteAddress
}
return $true
}
}
return $false
}
function New-BlockRule {
param([string]$RemoteAddress, [int]$RemotePort, [string]$ProcessName, [string]$ProgramPath)
if ($RemoteAddress -in $NeverBlockIPs) { return }
$safeName = $RemoteAddress -replace '\.', '_' -replace ':', '_'
$procSafe = ($ProcessName -replace '\.exe$','').Trim().ToLower()
$ruleName = "NTM_Block_${safeName}_${procSafe}"
$key = "${RemoteAddress}|${ProcessName}"
if (-not $ProgramPath -or -not (Test-Path $ProgramPath)) {
Write-ColorOutput "Skip block (no program path): $RemoteAddress ($ProcessName)" "Yellow"
return
}
$progArg = "program=`"$ProgramPath`""
$out = netsh advfirewall firewall add rule name="$ruleName" dir=out action=block remoteip="$RemoteAddress" $progArg 2>&1 | Out-String
if ($out -match 'already exists') {
$script:BlockedConnections[$key] = @{ Port = $RemotePort; Process = $ProcessName }
return
}
if ($out -match 'Error|Failed|Unable') {
Write-ColorOutput "Failed to block $RemoteAddress for $ProcessName : $($out.Trim())" "Red"
return
}
$script:BlockedConnections[$key] = @{ Port = $RemotePort; Process = $ProcessName }
Write-ColorOutput "BLOCKED (browser only): $RemoteAddress`:$RemotePort ($ProcessName)" "Red"
}
function Start-ConnectionMonitoring {
Write-ColorOutput "" "Cyan"
Write-ColorOutput "=== GFocus / Network Traffic Monitor ===" "Cyan"
Write-ColorOutput "Browsers only: monitoring and blocking apply to browsers only." "Cyan"
Write-ColorOutput "Gaming and all other apps are unhindered (never monitored or blocked)." "Green"
Write-ColorOutput "Address bar inferred from browser 80/443 nav + 30s dependencies (no extension)." "Cyan"
Write-ColorOutput "Block/allow/remove block as user surfs. Press Ctrl+C to stop." "Yellow"
Write-ColorOutput "To clear block rules later: GFocus.ps1 -RemoveRules" "Gray"
Write-ColorOutput "" "Cyan"
$SeenConnections = @{}
while ($script:MonitoringActive) {
$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 ($SeenConnections.ContainsKey($Key)) { continue }
$SeenConnections[$Key] = $true
try {
$Process = Get-Process -Id $Conn.OwningProcess -ErrorAction Stop
$ProcessName = $Process.ProcessName
$ProcessPath = $Process.Path
} catch { $Process = $null; $ProcessName = "Unknown"; $ProcessPath = $null }
$procName = ($ProcessName -replace '\.exe$','').Trim().ToLower()
if ($procName -notin $BrowserProcesses) {
continue
}
$IsBrowserOrDependency = Watch-BrowserActivity -RemoteAddress $Conn.RemoteAddress -ProcessName $ProcessName -RemotePort $Conn.RemotePort
if ($IsBrowserOrDependency) { continue }
if (-not (Test-BrowserConnection -RemoteAddress $Conn.RemoteAddress)) {
$blockKey = "$($Conn.RemoteAddress)|$ProcessName"
if (-not $script:BlockedConnections.ContainsKey($blockKey)) {
New-BlockRule -RemoteAddress $Conn.RemoteAddress -RemotePort $Conn.RemotePort -ProcessName $ProcessName -ProgramPath $ProcessPath
}
} else {
Write-ColorOutput "ALLOWED: $($Conn.RemoteAddress):$($Conn.RemotePort) (Process: $ProcessName)" "Green"
}
}
$Now = Get-Date
$ToRemove = @()
foreach ($IP in $script:CurrentBrowserConnections.Keys) {
if (($Now - $script:CurrentBrowserConnections[$IP]).TotalSeconds -gt 60) { $ToRemove += $IP }
}
foreach ($IP in $ToRemove) { $script:CurrentBrowserConnections.Remove($IP) }
Start-Sleep -Seconds 2
}
}
function Stop-Monitoring {
Write-ColorOutput "" "Cyan"
Write-ColorOutput "=== Stopping GFocus ===" "Cyan"
Write-ColorOutput "Blocked connections:" "Yellow"
if ($script:BlockedConnections.Count -eq 0) {
Write-ColorOutput " None." "Green"
} else {
foreach ($k in $script:BlockedConnections.Keys) {
$Info = $script:BlockedConnections[$k]
$ip = ($k -split '\|', 2)[0]
Write-ColorOutput " - ${ip}:$($Info.Port) - $($Info.Process) (browser-only rule)" "Red"
}
}
Write-ColorOutput "`nTo remove block rules: GFocus.ps1 -RemoveRules" "Gray"
}
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Stop-Monitoring }
# One tick for scheduler: process new browser connections (allow/block). Uses script-level seen cache.
if (-not $script:GFocusSeenConnections) { $script:GFocusSeenConnections = @{} }
function Invoke-GFocusTick {
$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:GFocusSeenConnections.ContainsKey($Key)) { continue }
$script:GFocusSeenConnections[$Key] = $true
try {
$Process = Get-Process -Id $Conn.OwningProcess -ErrorAction Stop
$ProcessName = $Process.ProcessName
$ProcessPath = $Process.Path
} catch { $Process = $null; $ProcessName = "Unknown"; $ProcessPath = $null }
$procName = ($ProcessName -replace '\.exe$','').Trim().ToLower()
if ($procName -notin $BrowserProcesses) { continue }
$IsBrowserOrDependency = Watch-BrowserActivity -RemoteAddress $Conn.RemoteAddress -ProcessName $ProcessName -RemotePort $Conn.RemotePort
if ($IsBrowserOrDependency) { continue }
if (-not (Test-BrowserConnection -RemoteAddress $Conn.RemoteAddress)) {
$blockKey = "$($Conn.RemoteAddress)|$ProcessName"
if (-not $script:BlockedConnections.ContainsKey($blockKey)) {
New-BlockRule -RemoteAddress $Conn.RemoteAddress -RemotePort $Conn.RemotePort -ProcessName $ProcessName -ProgramPath $ProcessPath
}
}
}
$Now = Get-Date
$ToRemove = @($script:CurrentBrowserConnections.Keys | Where-Object { ($Now - $script:CurrentBrowserConnections[$_]).TotalSeconds -gt 60 })
foreach ($IP in $ToRemove) { $script:CurrentBrowserConnections.Remove($IP) }
}
# --- ResponseEngine ---
# Response Engine Module
# Centralized response system for all detection modules - Optimized for low resource usage
$ModuleName = "ResponseEngine"
$script:LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$ResponseQueue = @()
$ResponseActions = @{
"Critical" = @("Quarantine", "KillProcess", "BlockNetwork", "Log")
"High" = @("Quarantine", "Log", "Alert")
"Medium" = @("Log", "Alert")
"Low" = @("Log")
}
$ProcessedThreats = @{}
function Invoke-ResponseAction {
param(
[hashtable]$Detection,
[string]$Severity
)
$actions = $ResponseActions[$Severity]
if (-not $actions) {
$actions = @("Log")
}
$results = @()
foreach ($action in $actions) {
try {
switch ($action) {
"Quarantine" {
if ($Detection.FilePath -or $Detection.DllPath) {
$filePath = $Detection.FilePath -or $Detection.DllPath
if (Test-Path $filePath) {
# Import quarantine function if available
$quarantineModule = Get-Module -Name "QuarantineManagement" -ErrorAction SilentlyContinue
if (-not $quarantineModule) {
# Load quarantine module
$quarantinePath = Join-Path $PSScriptRoot "QuarantineManagement.ps1"
if (Test-Path $quarantinePath) {
. $quarantinePath
}
}
# Call quarantine function
if (Get-Command -Name "Invoke-QuarantineFile" -ErrorAction SilentlyContinue) {
Invoke-QuarantineFile -FilePath $filePath -Reason "Threat Detected: $($Detection.Type)" -Source $Detection.ModuleName
$results += "Quarantined: $filePath"
}
}
}
}
"KillProcess" {
if ($Detection.ProcessId) {
try {
$proc = Get-Process -Id $Detection.ProcessId -ErrorAction Stop
Stop-Process -Id $Detection.ProcessId -Force -ErrorAction Stop
$results += "Killed process: $($proc.ProcessName) (PID: $Detection.ProcessId)"
} catch {
$results += "Failed to kill process PID: $Detection.ProcessId"
}
}
}
"BlockNetwork" {
if ($Detection.RemoteAddress -or $Detection.RemotePort) {
try {
# Block network connection using firewall
$remoteIP = $Detection.RemoteAddress
$remotePort = $Detection.RemotePort
if ($remoteIP) {
$ruleName = "Block_Threat_$($remoteIP.Replace('.', '_'))"
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if (-not $existingRule) {
New-NetFirewallRule -DisplayName $ruleName -Direction Outbound -RemoteAddress $remoteIP -Action Block -ErrorAction SilentlyContinue | Out-Null
$results += "Blocked network to: $remoteIP"
}
}
} catch {
$results += "Failed to block network: $_"
}
}
}
"Alert" {
# Send alert (can be extended with email, SIEM, etc.)
$alertMsg = "ALERT: $($Detection.Type) - $($Detection.ProcessName -or $Detection.FilePath) - Severity: $Severity"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2100 `
-Message $alertMsg
$results += "Alert sent: $alertMsg"
}
"Log" {
# Already logged, but add to response log
$logPath = "$env:ProgramData\Antivirus\Logs\ResponseEngine_$(Get-Date -Format 'yyyy-MM-dd').log"
$logEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$Severity|$($Detection.Type)|$($Detection.ProcessName -or $Detection.FilePath)|$($Detection.ModuleName)"
Add-Content -Path $logPath -Value $logEntry -ErrorAction SilentlyContinue
$results += "Logged"
}
}
} catch {
$results += "Error in action $action`: $_"
}
}
return $results
}
function Invoke-ResponseEngine {
$responses = @()
try {
# Check all module detection logs for new threats
$logPath = "$env:ProgramData\Antivirus\Logs"
if (Test-Path $logPath) {
$today = Get-Date -Format 'yyyy-MM-dd'
$logFiles = Get-ChildItem -Path $logPath -Filter "*_$today.log" -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne "ResponseEngine_$today.log" -and $_.Name -ne "Antivirus_$today.log" }
foreach ($logFile in $logFiles) {
try {
$moduleName = $logFile.BaseName -replace "_$today", ""
$logEntries = Get-Content -Path $logFile.FullName -ErrorAction SilentlyContinue | Select-Object -Last 50
foreach ($entry in $logEntries) {
# Parse log entry (format: timestamp|type|risk|details)
if ($entry -match '\|') {
$parts = $entry -split '\|'
if ($parts.Length -ge 3) {
$timestamp = $parts[0]
$detectionType = $parts[1]
$risk = $parts[2]
$details = $parts[3..($parts.Length-1)] -join '|'
# Create detection hash
$detectionHash = ($moduleName + $timestamp + $detectionType + $details).GetHashCode()
# Skip if already processed
if ($ProcessedThreats.ContainsKey($detectionHash)) {
continue
}
# Determine severity from risk level
$severity = switch ($risk) {
"Critical" { "Critical" }
"High" { "High" }
"Medium" { "Medium" }
default { "Low" }
}
# Create detection object
$detection = @{
ModuleName = $moduleName
Timestamp = $timestamp
Type = $detectionType
Risk = $risk
Details = $details
Severity = $severity
}
# Extract ProcessId, FilePath, etc. from details
if ($details -match 'PID:(\d+)') {
$detection.ProcessId = [int]$matches[1]
}
if ($details -match '(.+\.exe|.+\.dll)') {
$detection.FilePath = $matches[1]
}
if ($details -match '(\d+\.\d+\.\d+\.\d+)') {
$detection.RemoteAddress = $matches[1]
}
# Execute response actions
$actionResults = Invoke-ResponseAction -Detection $detection -Severity $severity
# Mark as processed
$ProcessedThreats[$detectionHash] = Get-Date
$responses += @{
Detection = $detection
Actions = $actionResults
Timestamp = Get-Date
}
Write-Output "DETECTION:$ModuleName`:Processed $detectionType from $moduleName - Actions: $($actionResults -join ', ')"
}
}
}
} catch {
continue
}
}
}
# Cleanup old processed threats (older than 24 hours)
$oldKeys = $ProcessedThreats.Keys | Where-Object {
((Get-Date) - $ProcessedThreats[$_]).TotalHours -gt 24
}
foreach ($key in $oldKeys) {
$ProcessedThreats.Remove($key)
}
# Also check Event Log for detection events (2001-2100)
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Application'; Id=2001..2099} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object {
$_.Source -eq "AntivirusEDR" -and
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromMinutes(5)
}
foreach ($event in $events) {
$eventHash = ($event.Id.ToString() + $event.TimeCreated.ToString()).GetHashCode()
if (-not $ProcessedThreats.ContainsKey($eventHash)) {
# Parse event message for detection info
$message = $event.Message
$severity = if ($event.EntryType -eq "Error") { "Critical" }
elseif ($event.EntryType -eq "Warning") { "High" }
else { "Medium" }
$detection = @{
ModuleName = "EventLog"
Timestamp = $event.TimeCreated.ToString()
Type = $message
Severity = $severity
EventId = $event.Id
}
# Extract info from message
if ($message -match 'PID:\s*(\d+)') {
$detection.ProcessId = [int]$matches[1]
}
if ($message -match '(?:THREAT|DETECTED|FOUND):\s*(.+?)(?:\s*\||\s*-\s*|$)') {
$detection.Details = $matches[1]
}
# Execute response for critical/high severity
if ($severity -in @("Critical", "High")) {
$actionResults = Invoke-ResponseAction -Detection $detection -Severity $severity
$responses += @{
Detection = $detection
Actions = $actionResults
Timestamp = Get-Date
}
}
$ProcessedThreats[$eventHash] = Get-Date
}
}
} catch { }
if ($responses.Count -gt 0) {
Write-Output "STATS:$ModuleName`:Processed $($responses.Count) threats"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $responses.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high
if (Test-CPULoadThreshold) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping scan"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ResponseEngine
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- BeaconDetection ---
# Beacon Detection Module
# Detects C2 beaconing and command & control communication - Optimized for low resource usage
$ModuleName = "BeaconDetection"
$script:LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$ConnectionBaseline = @{}
function Initialize-BeaconBaseline {
try {
$maxConnections = Get-ScanLimit -LimitName "MaxConnections"
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" } | Select-Object -First $maxConnections
foreach ($conn in $connections) {
$key = "$($conn.RemoteAddress):$($conn.RemotePort)"
if (-not $ConnectionBaseline.ContainsKey($key)) {
$ConnectionBaseline[$key] = @{
Count = 0
FirstSeen = Get-Date
LastSeen = Get-Date
}
}
$ConnectionBaseline[$key].Count++
$ConnectionBaseline[$key].LastSeen = Get-Date
}
} catch { }
}
function Invoke-BeaconDetection {
$detections = @()
try {
# Monitor for periodic connections (beacon indicator)
$maxConnections = Get-ScanLimit -LimitName "MaxConnections"
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" } | Select-Object -First $maxConnections
# Group connections by process and remote address
$connGroups = $connections | Group-Object -Property @{Expression={$_.OwningProcess}}, @{Expression={$_.RemoteAddress}}
foreach ($group in $connGroups) {
$procId = $group.Name.Split(',')[0].Trim()
$remoteIP = $group.Name.Split(',')[1].Trim()
try {
$proc = Get-Process -Id $procId -ErrorAction SilentlyContinue
if (-not $proc) { continue }
# Check connection frequency (beacon pattern)
$connTimes = $group.Group | ForEach-Object { $_.CreationTime } | Sort-Object
if ($connTimes.Count -gt 3) {
# Calculate intervals between connections
$intervals = @()
for ($i = 1; $i -lt $connTimes.Count; $i++) {
$interval = ($connTimes[$i] - $connTimes[$i-1]).TotalSeconds
$intervals += $interval
}
# Check for regular intervals (beacon indicator)
if ($intervals.Count -gt 2) {
$avgInterval = ($intervals | Measure-Object -Average).Average
$variance = ($intervals | ForEach-Object { [Math]::Pow($_ - $avgInterval, 2) } | Measure-Object -Average).Average
$stdDev = [Math]::Sqrt($variance)
# Low variance = regular intervals = beacon
if ($stdDev -lt $avgInterval * 0.2 -and $avgInterval -gt 10 -and $avgInterval -lt 3600) {
$detections += @{
ProcessId = $procId
ProcessName = $proc.ProcessName
RemoteAddress = $remoteIP
ConnectionCount = $connTimes.Count
AverageInterval = [Math]::Round($avgInterval, 2)
Type = "Beacon Pattern Detected"
Risk = "High"
}
}
}
}
} catch {
continue
}
}
# Check for connections to suspicious TLDs
foreach ($conn in $connections) {
if ($conn.RemoteAddress -notmatch '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.)') {
try {
$dns = [System.Net.Dns]::GetHostEntry($conn.RemoteAddress).HostName
$suspiciousTLDs = @(".onion", ".bit", ".i2p", ".tk", ".ml", ".ga", ".cf")
foreach ($tld in $suspiciousTLDs) {
if ($dns -like "*$tld") {
$detections += @{
ProcessId = $conn.OwningProcess
RemoteAddress = $conn.RemoteAddress
RemoteHost = $dns
Type = "Connection to Suspicious TLD"
Risk = "Medium"
}
break
}
}
} catch { }
}
}
# Check for HTTP/HTTPS connections with small data transfer (beacon)
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$procConns = $connections | Where-Object { $_.OwningProcess -eq $proc.Id }
$httpConns = $procConns | Where-Object { $_.RemotePort -in @(80, 443, 8080, 8443) }
if ($httpConns.Count -gt 0) {
# Check network stats
$netStats = Get-Counter "\Process($($proc.ProcessName))\IO Data Bytes/sec" -ErrorAction SilentlyContinue
if ($netStats -and $netStats.CounterSamples[0].CookedValue -lt 1000 -and $netStats.CounterSamples[0].CookedValue -gt 0) {
# Small but consistent data transfer = beacon
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
DataRate = $netStats.CounterSamples[0].CookedValue
ConnectionCount = $httpConns.Count
Type = "Low Data Transfer Beacon Pattern"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
} catch { }
# Check for processes with connections to many different IPs (C2 rotation)
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
$procConns = $connections | Where-Object { $_.OwningProcess -eq $proc.Id }
$uniqueIPs = ($procConns | Select-Object -Unique RemoteAddress).RemoteAddress.Count
if ($uniqueIPs -gt 10) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
UniqueIPs = $uniqueIPs
ConnectionCount = $procConns.Count
Type = "Multiple C2 Connections (IP Rotation)"
Risk = "High"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2037 `
-Message "BEACON DETECTED: $($detection.Type) - $($detection.ProcessName) (PID: $($detection.ProcessId)) - $($detection.RemoteAddress -or $detection.RemoteHost)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\BeaconDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|PID:$($_.ProcessId)|$($_.ProcessName)|$($_.RemoteAddress -or $_.RemoteHost)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) beacon indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Initialize-BeaconBaseline
Start-Sleep -Seconds (Get-Random -Minimum 30 -Maximum 90) # Longer initial delay
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high
if (Test-CPULoadThreshold) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping scan"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-BeaconDetection
$script:LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- NetworkTrafficMonitoring ---
# Optimized Network Traffic Monitoring Module
# Reduced polling and caching of connection state
$ModuleName = "NetworkTrafficMonitoring"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:ConnectionCache = @{}
$script:KnownGoodConnections = @{}
function Invoke-NetworkTrafficMonitoringOptimized {
$detections = @()
$maxConnections = Get-ScanLimit -LimitName "MaxConnections"
$batchSettings = Get-BatchSettings
try {
$tcpConnections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" } |
Select-Object -First $maxConnections
$batchCount = 0
foreach ($conn in $tcpConnections) {
$key = "$($conn.LocalAddress):$($conn.LocalPort)-$($conn.RemoteAddress):$($conn.RemotePort)"
if ($script:KnownGoodConnections.ContainsKey($key)) {
continue
}
$batchCount++
if ($batchCount % $batchSettings.BatchSize -eq 0 -and $batchSettings.BatchDelayMs -gt 0) {
Start-Sleep -Milliseconds $batchSettings.BatchDelayMs
}
# Check for suspicious patterns (simplified checks)
$isPrivate = $conn.RemoteAddress -match '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.)'
if (-not $isPrivate) {
# Connection to public IP on ephemeral port
if ($conn.RemotePort -gt 49152) {
$detections += @{
RemoteAddress = $conn.RemoteAddress
RemotePort = $conn.RemotePort
Type = "Connection to Public Ephemeral Port"
Risk = "Low"
}
}
} else {
$script:KnownGoodConnections[$key] = $true
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\NetworkTraffic_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.RemoteAddress):$($_.RemotePort)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) traffic anomalies"
}
Write-Output "STATS:$ModuleName`:Active connections=$($tcpConnections.Count)"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Start-Sleep -Seconds (Get-Random -Minimum 30 -Maximum 90) # Longer initial delay
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high
if (Test-CPULoadThreshold) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping scan"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-NetworkTrafficMonitoringOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- ProcessAnomalyDetection ---
# Optimized Process Anomaly Detection Module
# Reduced resource usage with baseline caching
$ModuleName = "ProcessAnomalyDetection"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:BaselineProcesses = @{}
$script:KnownGoodProcesses = @{}
function Initialize-BaselineOptimized {
if ($script:BaselineProcesses.Count -gt 0) {
return
}
try {
$maxProcs = Get-ScanLimit -LimitName "MaxProcesses"
$processes = Get-Process -ErrorAction SilentlyContinue | Select-Object -First $maxProcs
foreach ($proc in $processes) {
$key = $proc.ProcessName
if (-not $script:BaselineProcesses.ContainsKey($key)) {
$script:BaselineProcesses[$key] = @{
Count = 0
FirstSeen = Get-Date
}
}
$script:BaselineProcesses[$key].Count++
}
} catch { }
}
function Test-ProcessAnomalyOptimized {
param($Process)
$anomalies = @()
if ($script:KnownGoodProcesses.ContainsKey($Process.ProcessName)) {
return $anomalies
}
if ($Process.Path) {
$systemPaths = @("$env:SystemRoot\System32", "$env:SystemRoot\SysWOW64")
foreach ($sysPath in $systemPaths) {
if ($Process.Path -like "$sysPath\*") {
try {
$sig = Get-CachedSignature -FilePath $Process.Path
if ($sig -and $sig.Status -ne "Valid") {
$anomalies += "Unsigned executable in system directory"
} elseif ($sig -and $sig.Status -eq "Valid") {
$script:KnownGoodProcesses[$Process.ProcessName] = $true
}
} catch { }
}
}
}
return $anomalies
}
function Invoke-ProcessAnomalyScanOptimized {
$detections = @()
$batchSettings = Get-BatchSettings
$maxProcs = Get-ScanLimit -LimitName "MaxProcesses"
try {
Initialize-BaselineOptimized
$processes = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.Path } |
Select-Object -First $maxProcs
$batchCount = 0
foreach ($proc in $processes) {
if ($script:KnownGoodProcesses.ContainsKey($proc.ProcessName)) {
continue
}
$batchCount++
if ($batchCount % $batchSettings.BatchSize -eq 0 -and $batchSettings.BatchDelayMs -gt 0) {
Start-Sleep -Milliseconds $batchSettings.BatchDelayMs
}
$anomalies = Test-ProcessAnomalyOptimized -Process $proc
if ($anomalies.Count -gt 0) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Path = $proc.Path
Anomalies = $anomalies
Risk = "High"
}
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\ProcessAnomaly_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|PID:$($_.ProcessId)|$($_.ProcessName)|$($_.Anomalies -join ';')" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) process anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
Clear-ExpiredCache
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Start-Sleep -Seconds (Get-Random -Minimum 30 -Maximum 90) # Longer initial delay
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high
if (Test-CPULoadThreshold) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping scan"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ProcessAnomalyScanOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- EventLogMonitoring ---
# Optimized Event Log Monitoring Module
# Reduced Event Log query overhead
$ModuleName = "EventLogMonitoring"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:LastEventTime = @{}
function Invoke-EventLogMonitoringOptimized {
$detections = @()
$maxEvents = Get-ScanLimit -LimitName "MaxEvents"
try {
$lastCheck = if ($script:LastEventTime.ContainsKey('Security')) {
$script:LastEventTime['Security']
} else {
(Get-Date).AddMinutes(-$TickInterval / 60)
}
try {
$securityEvents = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
StartTime = $lastCheck
} -ErrorAction SilentlyContinue -MaxEvents $maxEvents
$script:LastEventTime['Security'] = Get-Date
# Check for failed logons
$failedLogons = $securityEvents | Where-Object { $_.Id -eq 4625 }
if ($failedLogons.Count -gt 10) {
$detections += @{
EventCount = $failedLogons.Count
Type = "Excessive Failed Logon Attempts"
Risk = "High"
}
}
# Check for privilege escalation
$privilegeEvents = $securityEvents | Where-Object { $_.Id -in @(4672, 4673, 4674) }
if ($privilegeEvents.Count -gt 0) {
$detections += @{
EventCount = $privilegeEvents.Count
Type = "Privilege Escalation Events"
Risk = "High"
}
}
# Check for log clearing
$logClearing = $securityEvents | Where-Object { $_.Id -eq 1102 }
if ($logClearing.Count -gt 0) {
$detections += @{
EventCount = $logClearing.Count
Type = "Event Log Cleared"
Risk = "Critical"
}
}
} catch { }
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\EventLogMonitoring_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|Count:$($_.EventCount)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) event log anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Start-Sleep -Seconds (Get-Random -Minimum 5 -Maximum 20)
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-EventLogMonitoringOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 60
}
}
}
# --- FileEntropyDetection ---
# Optimized File Entropy Detection Module
# Reduced file I/O with smart sampling
$ModuleName = "FileEntropyDetection"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$HighEntropyThreshold = 7.2
$script:ScannedFiles = @{}
function Measure-FileEntropyOptimized {
param([string]$FilePath)
try {
if (-not (Test-Path $FilePath)) { return $null }
$fileInfo = Get-Item $FilePath -ErrorAction Stop
$sampleSize = Get-ScanLimit -LimitName "SampleSizeBytes"
$sampleSize = [Math]::Min($sampleSize, $fileInfo.Length)
if ($sampleSize -eq 0) { return $null }
$stream = [System.IO.File]::OpenRead($FilePath)
$bytes = New-Object byte[] $sampleSize
$stream.Read($bytes, 0, $sampleSize) | Out-Null
$stream.Close()
# Calculate byte frequency
$freq = @{}
foreach ($byte in $bytes) {
if ($freq.ContainsKey($byte)) {
$freq[$byte]++
} else {
$freq[$byte] = 1
}
}
# Calculate Shannon entropy
$entropy = 0
$total = $bytes.Count
foreach ($count in $freq.Values) {
$p = $count / $total
if ($p -gt 0) {
$entropy -= $p * [Math]::Log($p, 2)
}
}
return @{
Entropy = $entropy
FileSize = $fileInfo.Length
SampleSize = $sampleSize
}
} catch {
return $null
}
}
function Invoke-FileEntropyDetectionOptimized {
$detections = @()
$maxFiles = Get-ScanLimit -LimitName "MaxFiles"
$batchSettings = Get-BatchSettings
try {
$cutoff = (Get-Date).AddHours(-2)
$scanPaths = @("$env:APPDATA", "$env:LOCALAPPDATA\Temp", "$env:USERPROFILE\Downloads")
$scannedCount = 0
foreach ($scanPath in $scanPaths) {
if (-not (Test-Path $scanPath)) { continue }
if ($scannedCount -ge $maxFiles) { break }
try {
$files = Get-ChildItem -Path $scanPath -Include *.exe,*.dll,*.scr -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt $cutoff } |
Select-Object -First ($maxFiles - $scannedCount)
$batchCount = 0
foreach ($file in $files) {
$scannedCount++
if ($script:ScannedFiles.ContainsKey($file.FullName)) {
$cached = $script:ScannedFiles[$file.FullName]
if ($cached.LastWrite -eq $file.LastWriteTime) {
continue
}
}
$batchCount++
if ($batchCount % $batchSettings.BatchSize -eq 0 -and $batchSettings.BatchDelayMs -gt 0) {
Start-Sleep -Milliseconds $batchSettings.BatchDelayMs
}
$entropyResult = Measure-FileEntropyOptimized -FilePath $file.FullName
# Mark as scanned
$script:ScannedFiles[$file.FullName] = @{
LastWrite = $file.LastWriteTime
Entropy = if ($entropyResult) { $entropyResult.Entropy } else { 0 }
}
if ($entropyResult -and $entropyResult.Entropy -ge $HighEntropyThreshold) {
$detections += @{
FilePath = $file.FullName
FileName = $file.Name
Entropy = [Math]::Round($entropyResult.Entropy, 2)
Type = "High Entropy File"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\FileEntropy_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.FilePath)|Entropy:$($_.Entropy)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) high entropy files"
}
Write-Output "STATS:$ModuleName`:Scanned=$scannedCount"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
$toRemove = $script:ScannedFiles.Keys | Where-Object { -not (Test-Path $_) }
foreach ($key in $toRemove) {
$script:ScannedFiles.Remove($key)
}
Clear-ExpiredCache
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
Start-Sleep -Seconds (Get-Random -Minimum 30 -Maximum 90)
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-FileEntropyDetectionOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120
}
}
}
# --- YaraDetection ---
# YARA Detection
# Runs yara.exe against suspicious files (if present)
$ModuleName = "YaraDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 120 }
$YaraPaths = @("$env:ProgramFiles\Yara\yara64.exe", "$env:ProgramFiles (x86)\Yara\yara.exe", "yara.exe", "yara64.exe")
$RulesPaths = @("$env:ProgramData\Antivirus\Yara", "$env:ProgramData\Antivirus\Rules", "$PSScriptRoot\YaraRules")
$ScanPaths = @("$env:Temp", "$env:TEMP", "$env:SystemRoot\Temp")
function Get-YaraExe {
foreach ($p in $YaraPaths) {
if (Test-Path $p) { return $p }
}
return $null
}
function Get-RulesPath {
foreach ($p in $RulesPaths) {
if (Test-Path $p) {
$rules = Get-ChildItem -Path $p -Filter *.yar -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($rules) { return $rules.Directory.FullName }
}
}
return $null
}
function Invoke-YaraScan {
$yara = Get-YaraExe
if (-not $yara) {
return 0
}
$rulesDir = Get-RulesPath
if (-not $rulesDir) {
return 0
}
$detections = @()
foreach ($base in $ScanPaths) {
if (-not (Test-Path $base)) { continue }
try {
$files = Get-ChildItem -Path $base -Include *.exe, *.dll, *.ps1 -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 100
foreach ($f in $files) {
try {
$out = & $yara -r "$rulesDir\*.yar" $f.FullName 2>&1
if ($out -and $out -match '\S') {
$detections += @{
File = $f.FullName
Match = ($out | Select-Object -First 5) -join "; "
}
}
} catch { }
}
} catch { }
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2096 -Message "YARA: $($d.File) - $($d.Match)" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\yara_detection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object { "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.File)|$($_.Match)" | Add-Content -Path $logPath }
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) YARA matches"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-YaraScan | Out-Null
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- WMIPersistenceDetection ---
# WMI Persistence Detection Module
# Detects WMI-based persistence mechanisms
$ModuleName = "WMIPersistenceDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-WMIPersistenceScan {
$detections = @()
try {
# Check WMI Event Consumers
$eventConsumers = Get-CimInstance -Namespace root\subscription -ClassName __EventConsumer -ErrorAction SilentlyContinue
foreach ($consumer in $eventConsumers) {
# Check for suspicious consumer types
if ($consumer.__CLASS -match 'ActiveScript|CommandLine') {
$suspicious = $false
$details = @{}
# Check CommandLineEventConsumer
if ($consumer.__CLASS -eq '__EventConsumer') {
$cmdLine = $consumer.CommandLineTemplate
if ($cmdLine) {
$details.CommandLine = $cmdLine
# Check for suspicious commands
if ($cmdLine -match 'powershell|cmd|certutil|bitsadmin|wmic' -or
$cmdLine -match 'http|https|ftp' -or
$cmdLine -match '-encodedcommand|-nop|-w.*hidden') {
$suspicious = $true
}
}
}
# Check ActiveScriptEventConsumer
if ($consumer.__CLASS -match 'ActiveScript') {
$script = $consumer.ScriptText
if ($script -and ($script.Length -gt 1000 -or
$script -match 'wscript|eval|exec|shell')) {
$suspicious = $true
$details.ScriptLength = $script.Length
}
}
if ($suspicious) {
$detections += @{
ConsumerName = $consumer.Name
ConsumerClass = $consumer.__CLASS
Details = $details
Risk = "High"
}
}
}
}
# Check WMI Event Filters
$eventFilters = Get-CimInstance -Namespace root\subscription -ClassName __EventFilter -ErrorAction SilentlyContinue
foreach ($filter in $eventFilters) {
$query = $filter.Query
if ($query) {
# Check for suspicious event filters
if ($query -match 'SELECT.*FROM.*__InstanceModificationEvent' -or
$query -match 'SELECT.*FROM.*Win32_ProcessStartTrace') {
$filterName = $filter.Name
# Check if filter is bound to suspicious consumer
$bindings = Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding -ErrorAction SilentlyContinue |
Where-Object { $_.Filter -like "*$filterName*" }
if ($bindings) {
$detections += @{
FilterName = $filterName
Query = $query
Type = "Event Filter Binding"
Risk = "Medium"
}
}
}
}
}
# Check for suspicious WMI namespaces
try {
$namespaces = Get-CimInstance -Namespace root -ClassName __Namespace -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^[a-f0-9]{32}$' }
foreach ($ns in $namespaces) {
$detections += @{
Namespace = $ns.Name
Type = "Suspicious WMI Namespace"
Risk = "High"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2006 `
-Message "WMI PERSISTENCE DETECTED: $($detection.ConsumerName -or $detection.FilterName -or $detection.Namespace) - $($detection.Type)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\WMIPersistence_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ConsumerName -or $_.FilterName)|$($_.Type)|$($_.Risk)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) WMI persistence mechanisms"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-WMIPersistenceScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ScheduledTaskDetection ---
# Scheduled Task Detection Module
# Detects malicious scheduled tasks
$ModuleName = "ScheduledTaskDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-ScheduledTaskScan {
$detections = @()
try {
# Get all scheduled tasks
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue
foreach ($task in $tasks) {
$taskInfo = Get-ScheduledTaskInfo -TaskName $task.TaskName -ErrorAction SilentlyContinue
$taskActions = $task.Actions
$taskSettings = $task.Settings
$suspicious = $false
$reasons = @()
# Check task actions
foreach ($action in $taskActions) {
if ($action.Execute) {
$executeLower = $action.Execute.ToLower()
# Check for suspicious executables
if ($executeLower -match 'powershell|cmd|wscript|cscript|rundll32|mshta') {
if ($action.Arguments) {
$argsLower = $action.Arguments.ToLower()
# Check for suspicious arguments
if ($argsLower -match '-encodedcommand|-nop|-w.*hidden|-executionpolicy.*bypass' -or
$argsLower -match 'http|https|ftp' -or
$argsLower -match 'javascript:|\.hta|\.vbs') {
$suspicious = $true
$reasons += "Suspicious command line arguments"
}
}
# Check for tasks running as SYSTEM
if ($task.Principal.RunLevel -eq "Highest" -or
$task.Principal.UserId -eq "SYSTEM") {
$suspicious = $true
$reasons += "Runs as SYSTEM/High privilege"
}
}
}
}
# Check for hidden tasks
if ($taskSettings.Hidden) {
$suspicious = $true
$reasons += "Hidden task"
}
# Check for tasks that run when user is logged off
if ($taskSettings.RunOnlyIfNetworkAvailable -and
$taskSettings.StartWhenAvailable) {
$suspicious = $true
$reasons += "Runs when network available (exfiltration risk)"
}
# Check for tasks in suspicious locations
foreach ($action in $taskActions) {
if ($action.WorkingDirectory) {
$workDir = $action.WorkingDirectory
if ($workDir -match '\$env:|temp|appdata' -and
$workDir -notmatch '^[A-Z]:\\') {
$suspicious = $true
$reasons += "Suspicious working directory"
}
}
}
# Check for recently created tasks
$createdDate = $taskInfo | Select-Object -ExpandProperty 'CreationDate' -ErrorAction SilentlyContinue
if ($createdDate) {
$age = (Get-Date) - $createdDate
if ($age.TotalDays -lt 7) {
$suspicious = $true
$reasons += "Recently created (within 7 days)"
}
}
if ($suspicious) {
$detections += @{
TaskName = $task.TaskName
TaskPath = $task.TaskPath
Actions = $taskActions
Reasons = $reasons
State = $task.State
Risk = "High"
}
}
}
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2007 `
-Message "SUSPICIOUS TASK: $($detection.TaskName) - $($detection.Reasons -join ', ')"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ScheduledTask_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
$actionStr = ($_.Actions | ForEach-Object { "$($_.Execute) $($_.Arguments)" }) -join ' | '
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.TaskName)|$($_.Reasons -join ';')|$actionStr" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) suspicious scheduled tasks"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ScheduledTaskScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- RegistryPersistenceDetection ---
# Registry Persistence Detection Module
# Detects malicious registry-based persistence
$ModuleName = "RegistryPersistenceDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
$PersistenceKeys = @(
"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",
"HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
"HKLM:\SYSTEM\CurrentControlSet\Services"
)
function Test-SuspiciousRegistryValue {
param([string]$Value)
if ([string]::IsNullOrEmpty($Value)) { return $false }
$valueLower = $Value.ToLower()
# Check for suspicious patterns
$suspiciousPatterns = @(
'powershell.*-encodedcommand',
'powershell.*-nop.*-w.*hidden',
'cmd.*\/c.*powershell',
'wscript.*http',
'cscript.*http',
'rundll32.*javascript',
'mshta.*http',
'certutil.*urlcache',
'bitsadmin.*transfer',
'regsvr32.*http',
'\.exe.*http',
'\.dll.*http'
)
foreach ($pattern in $suspiciousPatterns) {
if ($valueLower -match $pattern) {
return $true
}
}
# Check for suspicious file locations
if ($valueLower -match '\$env:' -and
($valueLower -match 'temp|appdata|local' -or
$valueLower -notmatch '^[A-Z]:\\')) {
return $true
}
# Check for unsigned executables
if ($valueLower -like '*.exe' -or $valueLower -like '*.dll') {
$filePath = $valueLower -replace '"', '' -split ' ' | Select-Object -First 1
if ($filePath -and (Test-Path $filePath)) {
try {
$sig = Get-AuthenticodeSignature -FilePath $filePath -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
return $true
}
} catch { }
}
}
return $false
}
function Invoke-RegistryPersistenceScan {
$detections = @()
try {
foreach ($regPath in $PersistenceKeys) {
if (-not (Test-Path $regPath)) { continue }
try {
$values = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
if ($values) {
$valueProps = $values.PSObject.Properties | Where-Object {
$_.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider')
}
foreach ($prop in $valueProps) {
$regValue = $prop.Value
$regName = $prop.Name
if (Test-SuspiciousRegistryValue -Value $regValue) {
$detections += @{
RegistryPath = $regPath
ValueName = $regName
Value = $regValue
Risk = "High"
}
}
}
}
} catch { }
}
# Check for suspicious service entries
try {
$services = Get-CimInstance Win32_Service -ErrorAction SilentlyContinue |
Where-Object {
$_.PathName -match 'powershell|cmd|wscript|cscript|http' -or
$_.StartName -eq 'LocalSystem' -and $_.PathName -notmatch '^[A-Z]:\\Windows'
}
foreach ($svc in $services) {
if (Test-SuspiciousRegistryValue -Value $svc.PathName) {
$detections += @{
RegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$($svc.Name)"
ValueName = "ImagePath"
Value = $svc.PathName
ServiceName = $svc.Name
Risk = "Critical"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2008 `
-Message "REGISTRY PERSISTENCE: $($detection.RegistryPath)\$($detection.ValueName) - $($detection.Value)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\RegistryPersistence_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.RegistryPath)|$($_.ValueName)|$($_.Value)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) registry persistence mechanisms"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-RegistryPersistenceScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- DLLHijackingDetection ---
# DLL Hijacking Detection Module
# Detects DLL hijacking attempts
$ModuleName = "DLLHijackingDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Test-DLLHijacking {
param([string]$DllPath)
if (-not (Test-Path $DllPath)) { return $false }
# Check if DLL is in suspicious locations
$suspiciousPaths = @(
"$env:TEMP",
"$env:LOCALAPPDATA\Temp",
"$env:APPDATA",
"$env:USERPROFILE\Downloads",
"$env:USERPROFILE\Desktop"
)
foreach ($susPath in $suspiciousPaths) {
if ($DllPath -like "$susPath*") {
return $true
}
}
# Check if DLL is unsigned
try {
$sig = Get-AuthenticodeSignature -FilePath $DllPath -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
return $true
}
} catch { }
return $false
}
function Invoke-DLLHijackingScan {
$detections = @()
try {
# Check loaded DLLs in processes
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules | Where-Object { $_.FileName -like "*.dll" }
foreach ($module in $modules) {
if (Test-DLLHijacking -DllPath $module.FileName) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
DllPath = $module.FileName
DllName = $module.ModuleName
Risk = "High"
}
}
}
} catch {
# Access denied or process exited
continue
}
}
# Check for DLLs in application directories
$appPaths = @(
"$env:ProgramFiles",
"$env:ProgramFiles(x86)",
"$env:SystemRoot\System32",
"$env:SystemRoot\SysWOW64"
)
foreach ($appPath in $appPaths) {
if (-not (Test-Path $appPath)) { continue }
try {
$dlls = Get-ChildItem -Path $appPath -Filter "*.dll" -Recurse -ErrorAction SilentlyContinue |
Select-Object -First 100
foreach ($dll in $dlls) {
if ($dll.DirectoryName -ne "$appPath") {
# Check if DLL is signed
try {
$sig = Get-AuthenticodeSignature -FilePath $dll.FullName -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
$detections += @{
DllPath = $dll.FullName
Type = "Unsigned DLL in application directory"
Risk = "Medium"
}
}
} catch { }
}
}
} catch { }
}
# Check Event Log for DLL load failures
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'; Id=7} -ErrorAction SilentlyContinue -MaxEvents 100
foreach ($event in $events) {
if ($event.Message -match 'DLL.*not.*found|DLL.*load.*failed') {
$detections += @{
EventId = $event.Id
Message = $event.Message
TimeCreated = $event.TimeCreated
Type = "DLL Load Failure"
Risk = "Medium"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2009 `
-Message "DLL HIJACKING: $($detection.ProcessName -or $detection.Type) - $($detection.DllPath -or $detection.Message)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\DLLHijacking_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ProcessName -or $_.Type)|$($_.DllPath -or $_.DllName)|$($_.Risk)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) DLL hijacking indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-DLLHijackingScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- TokenManipulationDetection ---
# Token Manipulation Detection Module
# Detects token theft and impersonation
$ModuleName = "TokenManipulationDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-TokenManipulationScan {
$detections = @()
try {
# Check for processes with unusual token privileges
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath, ParentProcessId
foreach ($proc in $processes) {
try {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction Stop
# Check for SeDebugPrivilege (enables token access)
try {
$token = Get-CimInstance Win32_LogicalDisk -ErrorAction SilentlyContinue
# Indirect check - processes accessing LSASS often have this
if ($proc.Name -eq "lsass") {
# Check for processes accessing LSASS
$accessingProcs = Get-CimInstance Win32_Process |
Where-Object { $_.ParentProcessId -eq $proc.ProcessId -and
$_.Name -notin @("svchost.exe", "dwm.exe") }
foreach ($accProc in $accessingProcs) {
$detections += @{
ProcessId = $accProc.ProcessId
ProcessName = $accProc.Name
Type = "LSASS Access - Possible Token Theft"
Risk = "Critical"
}
}
}
} catch { }
# Check for processes running as SYSTEM but not in Windows directory
if ($procObj.StartInfo.UserName -eq "SYSTEM" -or
$proc.Name -eq "SYSTEM") {
if ($proc.ExecutablePath -and
$proc.ExecutablePath -notlike "$env:SystemRoot\*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ExecutablePath = $proc.ExecutablePath
Type = "SYSTEM token on non-Windows executable"
Risk = "High"
}
}
}
} catch {
continue
}
}
# Check Security Event Log for token operations
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4672} -ErrorAction SilentlyContinue -MaxEvents 100
foreach ($event in $events) {
$xml = [xml]$event.ToXml()
$subjectUserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'SubjectUserName'}).'#text'
$targetUserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TargetUserName'}).'#text'
# Check for unusual token impersonation
if ($subjectUserName -ne $targetUserName -and
$targetUserName -eq "SYSTEM") {
$detections += @{
EventId = $event.Id
SubjectUser = $subjectUserName
TargetUser = $targetUserName
Type = "Token Impersonation - SYSTEM"
TimeCreated = $event.TimeCreated
Risk = "High"
}
}
}
} catch { }
# Check for common token manipulation tools
$tokenTools = @("incognito", "mimikatz", "invoke-tokenmanipulation", "getsystem")
$runningProcs = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $runningProcs) {
foreach ($tool in $tokenTools) {
if ($proc.ProcessName -like "*$tool*" -or
$proc.Path -like "*$tool*") {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ProcessPath = $proc.Path
Type = "Token Manipulation Tool"
Tool = $tool
Risk = "Critical"
}
}
}
}
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2010 `
-Message "TOKEN MANIPULATION: $($detection.ProcessName -or $detection.Type) - $($detection.Tool -or $detection.TargetUser)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\TokenManipulation_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ProcessName -or $_.Type)|$($_.Risk)|$($_.Tool -or $_.TargetUser)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) token manipulation attempts"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-TokenManipulationScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ProcessHollowingDetection ---
# Process Hollowing Detection Module
# Detects process hollowing attacks
$ModuleName = "ProcessHollowingDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 20 }
function Invoke-ProcessHollowingScan {
$detections = @()
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath, CommandLine, ParentProcessId, CreationDate
foreach ($proc in $processes) {
try {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction Stop
$procPath = $procObj.Path
$imgPath = $proc.ExecutablePath
# Check for path mismatch (indicator of process hollowing)
if ($procPath -and $imgPath -and $procPath -ne $imgPath) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ProcessPath = $procPath
ImagePath = $imgPath
Type = "Path Mismatch - Process Hollowing"
Risk = "Critical"
}
}
# Check for processes with unusual parent relationships
if ($proc.ParentProcessId) {
try {
$parent = Get-CimInstance Win32_Process -Filter "ProcessId=$($proc.ParentProcessId)" -ErrorAction Stop
# Check for processes spawned from non-standard parents
$suspiciousParents = @{
"explorer.exe" = @("notepad.exe", "calc.exe", "cmd.exe", "powershell.exe")
"winlogon.exe" = @("cmd.exe", "powershell.exe", "wmic.exe")
"services.exe" = @("cmd.exe", "powershell.exe", "rundll32.exe")
}
if ($suspiciousParents.ContainsKey($parent.Name)) {
if ($proc.Name -in $suspiciousParents[$parent.Name]) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ParentProcess = $parent.Name
Type = "Suspicious Parent-Child Relationship"
Risk = "High"
}
}
}
} catch { }
}
# Check for processes with suspended threads
try {
$threads = Get-CimInstance Win32_Thread -Filter "ProcessHandle=$($proc.ProcessId)" -ErrorAction SilentlyContinue
$suspendedThreads = $threads | Where-Object { $_.ThreadState -eq 5 } # Suspended
if ($suspendedThreads.Count -gt 0 -and $suspendedThreads.Count -eq $threads.Count) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
SuspendedThreads = $suspendedThreads.Count
Type = "All Threads Suspended - Process Hollowing"
Risk = "High"
}
}
} catch { }
# Check for processes with unusual memory regions
try {
$memoryRegions = Get-Process -Id $proc.ProcessId -ErrorAction Stop |
Select-Object -ExpandProperty Modules -ErrorAction SilentlyContinue
if ($memoryRegions) {
$unknownModules = $memoryRegions | Where-Object {
$_.FileName -notlike "$env:SystemRoot\*" -and
$_.FileName -notlike "$env:ProgramFiles*" -and
$_.ModuleName -notin @("kernel32.dll", "ntdll.dll", "user32.dll")
}
if ($unknownModules.Count -gt 5) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
UnknownModules = $unknownModules.Count
Type = "Unusual Memory Modules"
Risk = "Medium"
}
}
}
} catch { }
} catch {
continue
}
}
# Check for processes with unusual PE structure
try {
$suspiciousProcs = $processes | Where-Object {
$_.ExecutablePath -and
(Test-Path $_.ExecutablePath) -and
$_.ExecutablePath -notlike "$env:SystemRoot\*"
}
foreach ($proc in $suspiciousProcs) {
try {
$peInfo = Get-Item $proc.ExecutablePath -ErrorAction Stop
# Check if executable is signed
$sig = Get-AuthenticodeSignature -FilePath $proc.ExecutablePath -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
# Check if it's impersonating a legitimate process
$legitNames = @("svchost.exe", "explorer.exe", "notepad.exe", "calc.exe", "dwm.exe")
if ($proc.Name -in $legitNames) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ExecutablePath = $proc.ExecutablePath
Type = "Unsigned Executable Impersonating Legitimate Process"
Risk = "Critical"
}
}
}
} catch { }
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2011 `
-Message "PROCESS HOLLOWING: $($detection.ProcessName) (PID: $($detection.ProcessId)) - $($detection.Type)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ProcessHollowing_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|PID:$($_.ProcessId)|$($_.ProcessName)|$($_.Type)|$($_.Risk)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) process hollowing indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ProcessHollowingScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- KeyloggerDetection ---
# Keylogger Detection Module
# Detects keylogging activity
$ModuleName = "KeyloggerDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-KeyloggerScan {
$detections = @()
try {
# Check for known keylogger processes
$keyloggerNames = @("keylog", "keylogger", "keyspy", "keycapture", "keystroke", "keyhook", "keymon", "kl")
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
$procNameLower = $proc.ProcessName.ToLower()
foreach ($keylogName in $keyloggerNames) {
if ($procNameLower -like "*$keylogName*") {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ProcessPath = $proc.Path
Type = "Known Keylogger Process"
Risk = "Critical"
}
break
}
}
}
# Check for processes with keyboard hooks
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath
foreach ($proc in $processes) {
try {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction Stop
# Check for SetWindowsHookEx usage (common in keyloggers)
$modules = $procObj.Modules | Where-Object {
$_.ModuleName -match "hook|keyboard|input"
}
if ($modules.Count -gt 0) {
# Check if process is signed
if ($proc.ExecutablePath -and (Test-Path $proc.ExecutablePath)) {
$sig = Get-AuthenticodeSignature -FilePath $proc.ExecutablePath -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ExecutablePath = $proc.ExecutablePath
HookModules = $modules.ModuleName -join ','
Type = "Unsigned Process with Keyboard Hooks"
Risk = "High"
}
}
}
}
} catch {
continue
}
}
} catch { }
# Check for processes accessing keyboard input devices
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$handles = Get-CimInstance Win32_ProcessHandle -Filter "ProcessId=$($proc.Id)" -ErrorAction SilentlyContinue
if ($handles) {
# Check for keyboard device access
$keyboardHandles = $handles | Where-Object {
$_.Name -match "keyboard|kbdclass|keybd"
}
if ($keyboardHandles.Count -gt 0) {
# Exclude legitimate processes
$legitProcesses = @("explorer.exe", "winlogon.exe", "csrss.exe")
if ($proc.ProcessName -notin $legitProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
KeyboardHandles = $keyboardHandles.Count
Type = "Direct Keyboard Device Access"
Risk = "High"
}
}
}
}
} catch {
continue
}
}
} catch { }
# Check for clipboard monitoring (often used with keyloggers)
try {
$clipboardProcs = Get-Process | Where-Object {
$_.Modules.ModuleName -like "*clipboard*" -or
$_.Path -like "*clipboard*"
}
foreach ($proc in $clipboardProcs) {
if ($proc.ProcessName -notin @("explorer.exe", "dwm.exe")) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Type = "Clipboard Monitoring Process"
Risk = "Medium"
}
}
}
} catch { }
# Check Event Log for suspicious keyboard events
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Security'} -ErrorAction SilentlyContinue -MaxEvents 500 |
Where-Object { $_.Message -match 'keyboard|keystroke|hook' }
if ($events.Count -gt 10) {
$detections += @{
EventCount = $events.Count
Type = "Excessive Keyboard Events"
Risk = "Medium"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2012 `
-Message "KEYLOGGER DETECTED: $($detection.ProcessName -or $detection.Type) - $($detection.HookModules -or $detection.Type)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\Keylogger_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ProcessName -or $_.Type)|$($_.Risk)|$($_.HookModules -or $_.KeyboardHandles)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) keylogger indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-KeyloggerScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ReflectiveDLLInjectionDetection ---
# Reflective DLL Injection Detection Module
# Detects reflective DLL injection and memory-only DLLs
$ModuleName = "ReflectiveDLLInjectionDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-ReflectiveDLLInjectionDetection {
$detections = @()
try {
# Check for processes with memory-only DLLs (reflective injection indicator)
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules
# Check for DLLs that don't exist on disk (reflective injection)
$memoryOnlyDlls = $modules | Where-Object {
$_.FileName -notlike "$env:SystemRoot\*" -and
$_.FileName -notlike "$env:ProgramFiles*" -and
-not (Test-Path $_.FileName)
}
if ($memoryOnlyDlls.Count -gt 0) {
foreach ($dll in $memoryOnlyDlls) {
# Exclude known legitimate cases
if ($dll.ModuleName -in @("kernel32.dll", "ntdll.dll", "user32.dll")) {
continue
}
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
DllName = $dll.ModuleName
BaseAddress = $dll.BaseAddress.ToString()
DllPath = $dll.FileName
Type = "Memory-Only DLL (Reflective Injection)"
Risk = "Critical"
}
}
}
# Check for unusual DLL base addresses (heap-based injection)
$unusualAddresses = $modules | Where-Object {
$addr = [Int64]$_.BaseAddress
# DLLs loaded at unusual addresses (not typical image base)
($addr -lt 0x400000 -or $addr -gt 0x7FFFFFFF0000) -and
$_.FileName -like "*.dll"
}
if ($unusualAddresses.Count -gt 3) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
UnusualAddressCount = $unusualAddresses.Count
Type = "DLLs at Unusual Memory Addresses"
Risk = "High"
}
}
# Check for processes with many unsigned DLLs in memory
$unsignedInMemory = 0
foreach ($mod in $modules) {
if ($mod.FileName -like "*.dll" -and (Test-Path $mod.FileName)) {
try {
$sig = Get-AuthenticodeSignature -FilePath $mod.FileName -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid" -and $mod.FileName -notlike "$env:SystemRoot\*") {
$unsignedInMemory++
}
} catch { }
}
}
if ($unsignedInMemory -gt 10) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
UnsignedDllCount = $unsignedInMemory
Type = "Many Unsigned DLLs in Memory"
Risk = "Medium"
}
}
} catch {
continue
}
}
# Check for processes using reflective loading APIs
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, CommandLine
foreach ($proc in $processes) {
if ($proc.CommandLine) {
# Check for reflective loading patterns in command line
if ($proc.CommandLine -match 'VirtualAlloc|WriteProcessMemory|CreateRemoteThread|LoadLibrary|GetProcAddress' -or
$proc.CommandLine -match 'reflective|manual.*map|pe.*injection') {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Type = "Process Using Reflective Loading APIs"
Risk = "High"
}
}
}
}
} catch { }
# Check for hollowed processes (related to reflective injection)
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath
foreach ($proc in $processes) {
try {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction Stop
$procPath = $procObj.Path
$imgPath = $proc.ExecutablePath
# Check for path mismatch (process hollowing often uses reflective injection)
if ($procPath -and $imgPath -and $procPath -ne $imgPath) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ProcessPath = $procPath
ImagePath = $imgPath
Type = "Process Hollowing with Reflective Injection"
Risk = "Critical"
}
}
} catch {
continue
}
}
} catch { }
# Check Event Log for DLL load failures (may indicate injection attempts)
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'; Id=7} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(1) -and
$_.Message -match 'DLL.*not.*found|DLL.*load.*failed|reflective'
}
if ($events.Count -gt 10) {
$detections += @{
EventCount = $events.Count
Type = "Excessive DLL Load Failures (Possible Injection Attempts)"
Risk = "Medium"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2036 `
-Message "REFLECTIVE DLL INJECTION: $($detection.Type) - $($detection.ProcessName) (PID: $($detection.ProcessId))"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ReflectiveDLL_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|PID:$($_.ProcessId)|$($_.ProcessName)|$($_.DllName -or $_.BaseAddress)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) reflective DLL injection indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ReflectiveDLLInjectionDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- RansomwareDetection ---
# Ransomware Detection Module
# Detects ransomware behavior patterns and ransom notes
#Requires -Version 5.1
# Converted from GEDR C# job: JobRansomwareDetection
# Enhanced with full implementation from C# version
$ModuleName = "RansomwareDetection"
$LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:Initialized = $false
# Ransomware command-line patterns
$script:RansomwarePatterns = @(
"vssadmin delete shadows",
"vssadmin.exe delete",
"wbadmin delete catalog",
"bcdedit",
"shadow copy",
"shadowcopy",
"cryptolocker",
"wannacry",
".encrypted",
".locked",
".crypto"
)
# Ransom note file names
$script:RansomNoteNames = @(
"readme.txt",
"decrypt.txt",
"how_to_decrypt.txt",
"recover.txt",
"restore.txt",
"!!!readme!!!.txt",
"readme_to_decrypt.txt",
"decrypt_instruction.txt"
)
function Initialize-RansomwareDetection {
if ($script:Initialized) {
return
}
Write-Log "INFO" "Initializing Ransomware Detection module"
$script:Initialized = $true
}
function Get-ProcessList {
# Get process list similar to GEDR's EdrProcess.GetProcesses
try {
$processes = Get-CimInstance -ClassName Win32_Process -ErrorAction SilentlyContinue |
Select-Object ProcessId, Name, CommandLine, ExecutablePath, ParentProcessId |
Where-Object { $_.ProcessId -ne $PID }
return $processes
}
catch {
Write-Log "ERROR" "Failed to get process list: $($_.Exception.Message)"
return @()
}
}
function Test-RansomwareCommand {
param([string]$CommandLine)
if ([string]::IsNullOrEmpty($CommandLine)) {
return $false
}
$cmdLower = $CommandLine.ToLowerInvariant()
foreach ($pattern in $script:RansomwarePatterns) {
if ($cmdLower.Contains($pattern.ToLowerInvariant())) {
return $true
}
}
return $false
}
function Find-RansomNotes {
# Check for ransom notes in user directories
try {
$userProfile = $env:USERPROFILE
if ([string]::IsNullOrEmpty($userProfile)) {
return
}
$directories = @(
Join-Path $userProfile "Documents",
Join-Path $userProfile "Desktop",
Join-Path $userProfile "Pictures"
)
foreach ($directory in $directories) {
if (-not (Test-Path $directory)) {
continue
}
foreach ($noteName in $script:RansomNoteNames) {
$fullPath = Join-Path $directory $noteName
if (Test-Path $fullPath) {
$dedupeKey = "Ransomware_Note_$($fullPath)"
if (Test-Deduplication -Key $dedupeKey -Category "ransomware") {
Write-Log "THREAT" "RANSOMWARE: Ransom note detected: $fullPath" -Category "ransomware_detections"
$script:ThreatCount++
# Trigger response
Invoke-ThreatResponse -ProcessId 0 -ProcessName "RansomNote" -ExecutablePath $fullPath -ThreatLevel "Critical"
}
}
}
}
}
catch {
Write-Log "ERROR" "Error checking for ransom notes: $($_.Exception.Message)"
}
}
function Test-ShadowCopyDeletion {
# Check if shadow copies exist (ransomware often deletes them)
try {
$shadowCopies = Get-CimInstance -ClassName Win32_ShadowCopy -ErrorAction SilentlyContinue
$count = if ($shadowCopies) { $shadowCopies.Count } else { 0 }
if ($count -eq 0) {
$dedupeKey = "Ransomware_ShadowCopyZero"
if (Test-Deduplication -Key $dedupeKey -Category "ransomware") {
Write-Log "THREAT" "RANSOMWARE: No shadow copies present (possible ransomware deletion)" -Category "ransomware_detections"
$script:ThreatCount++
}
}
}
catch {
Write-Log "ERROR" "Error checking shadow copies: $($_.Exception.Message)"
}
}
function Invoke-ThreatResponse {
param(
[int]$ProcessId,
[string]$ProcessName,
[string]$ExecutablePath,
[string]$ThreatLevel
)
try {
# Log threat response
Write-Log "ACTION" "THREAT RESPONSE: Triggering response for $ProcessName (PID: $ProcessId) - Level: $ThreatLevel" -Category "threat_responses"
# Implement response actions based on threat level
switch ($ThreatLevel) {
"Critical" {
# For critical threats, consider terminating process
if ($ProcessId -gt 0) {
try {
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
Write-Log "ACTION" "THREAT RESPONSE: Terminated critical process $ProcessName (PID: $ProcessId)" -Category "threat_responses"
}
catch {
Write-Log "WARNING" "Failed to terminate process $ProcessName (PID: $ProcessId): $($_.Exception.Message)"
}
}
# Quarantine file if it exists
if ($ExecutablePath -and (Test-Path $ExecutablePath)) {
Invoke-QuarantineFile -FilePath $ExecutablePath -Reason "Ransomware detection"
}
}
}
}
catch {
Write-Log "ERROR" "Error in threat response: $($_.Exception.Message)"
}
}
function Invoke-QuarantineFile {
param([string]$FilePath, [string]$Reason)
try {
$quarantinePath = "$env:ProgramData\Antivirus\Quarantine"
if (-not (Test-Path $quarantinePath)) {
New-Item -Path $quarantinePath -ItemType Directory -Force | Out-Null
}
$fileName = Split-Path $FilePath -Leaf
$quarantineFile = Join-Path $quarantinePath "$fileName-$([Guid]::NewGuid()).quar"
Move-Item -Path $FilePath -Destination $quarantineFile -Force
Write-Log "ACTION" "QUARANTINE: $FilePath moved to $quarantineFile (Reason: $Reason)" -Category "quarantine"
}
catch {
Write-Log "ERROR" "Failed to quarantine file $FilePath`: $($_.Exception.Message)"
}
}
function Invoke-RansomwareDetection {
try {
Initialize-RansomwareDetection
$script:ThreatCount = 0
$processes = Get-ProcessList
Write-Log "INFO" "Scanning $($processes.Count) processes for ransomware patterns"
foreach ($process in $processes) {
$cmdLine = if ($process.CommandLine) { $process.CommandLine } else { "" }
if (Test-RansomwareCommand -CommandLine $cmdLine) {
$pattern = $script:RansomwarePatterns | Where-Object { $cmdLine.ToLowerInvariant().Contains($_.ToLowerInvariant()) } | Select-Object -First 1
$dedupeKey = "Ransomware_$($process.ProcessId)_$($pattern)"
if (Test-Deduplication -Key $dedupeKey -Category "ransomware") {
Write-Log "THREAT" "RANSOMWARE: $($process.Name) (PID: $($process.ProcessId)) | Pattern: $pattern" -Category "ransomware_detections"
$script:ThreatCount++
}
# Trigger threat response
Invoke-ThreatResponse -ProcessId $process.ProcessId -ProcessName $process.Name -ExecutablePath $process.ExecutablePath -ThreatLevel "Critical"
}
}
# Check for ransom notes
Find-RansomNotes
# Check shadow copy status
Test-ShadowCopyDeletion
Write-Log "INFO" "Ransomware detection completed. Threats found: $script:ThreatCount"
}
catch {
Write-Log "ERROR" "Error in RansomwareDetection: $($_.Exception.Message)"
}
}
# Module exports - scheduler will call Invoke-RansomwareDetection on appropriate interval
# --- NetworkAnomalyDetection ---
# Network Anomaly Detection Module
# Detects unusual network activity
$ModuleName = "NetworkAnomalyDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
$BaselineConnections = @{}
function Initialize-NetworkBaseline {
try {
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" }
foreach ($conn in $connections) {
$key = "$($conn.LocalAddress):$($conn.LocalPort)-$($conn.RemoteAddress):$($conn.RemotePort)"
if (-not $BaselineConnections.ContainsKey($key)) {
$BaselineConnections[$key] = @{
Count = 0
FirstSeen = Get-Date
}
}
$BaselineConnections[$key].Count++
}
} catch { }
}
function Invoke-NetworkAnomalyScan {
$detections = @()
try {
# Get current network connections
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" }
# Check for unusual destinations
$suspiciousIPs = @()
$suspiciousPorts = @(4444, 5555, 6666, 7777, 8888, 9999, 1337, 31337, 8080, 8443)
foreach ($conn in $connections) {
# Check for suspicious ports
if ($conn.RemotePort -in $suspiciousPorts) {
$detections += @{
LocalAddress = $conn.LocalAddress
LocalPort = $conn.LocalPort
RemoteAddress = $conn.RemoteAddress
RemotePort = $conn.RemotePort
State = $conn.State
Type = "Suspicious Port"
Risk = "High"
}
}
# Check for connections to known bad IPs
if ($conn.RemoteAddress -match '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)') {
# Private IP - check if it's unusual
$key = "$($conn.RemoteAddress):$($conn.RemotePort)"
if (-not $BaselineConnections.ContainsKey($key)) {
$detections += @{
LocalAddress = $conn.LocalAddress
LocalPort = $conn.LocalPort
RemoteAddress = $conn.RemoteAddress
RemotePort = $conn.RemotePort
Type = "New Connection to Private IP"
Risk = "Medium"
}
}
}
}
# Check for processes with unusual network activity
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath
foreach ($proc in $processes) {
try {
$procConnections = $connections | Where-Object {
$owner = (Get-NetTCPConnection -OwningProcess $proc.ProcessId -ErrorAction SilentlyContinue)
$owner.Count -gt 0
}
if ($procConnections.Count -gt 50) {
# High number of connections
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ConnectionCount = $procConnections.Count
Type = "High Network Activity"
Risk = "Medium"
}
}
} catch {
continue
}
}
} catch { }
# Check for DNS queries to suspicious domains
try {
$dnsQueries = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-DNS-Client/Operational'; Id=3008} -ErrorAction SilentlyContinue -MaxEvents 100
$suspiciousDomains = @(".onion", ".bit", ".i2p", "pastebin.com", "githubusercontent.com")
foreach ($event in $dnsQueries) {
$message = $event.Message
foreach ($domain in $suspiciousDomains) {
if ($message -like "*$domain*") {
$detections += @{
EventId = $event.Id
Message = $message
Type = "Suspicious DNS Query"
Domain = $domain
Risk = "Medium"
}
}
}
}
} catch { }
# Check for unusual data transfer
try {
$netStats = Get-NetAdapterStatistics -ErrorAction SilentlyContinue
foreach ($adapter in $netStats) {
if ($adapter.SentBytes -gt 1GB -or $adapter.ReceivedBytes -gt 1GB) {
# High data transfer
$timeSpan = (Get-Date) - $LastTick
if ($timeSpan.TotalSeconds -gt 0) {
$bytesPerSec = $adapter.SentBytes / $timeSpan.TotalSeconds
if ($bytesPerSec -gt 10MB) {
$detections += @{
Adapter = $adapter.Name
SentBytes = $adapter.SentBytes
ReceivedBytes = $adapter.ReceivedBytes
Type = "High Data Transfer Rate"
Risk = "Medium"
}
}
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2015 `
-Message "NETWORK ANOMALY: $($detection.Type) - $($detection.ProcessName -or $detection.RemoteAddress -or $detection.Adapter)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\NetworkAnomaly_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.RemoteAddress)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) network anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
Initialize-NetworkBaseline
Start-Sleep -Seconds 10
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-NetworkAnomalyScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- DNSExfiltrationDetection ---
# DNS Exfiltration Detection Module
# Detects data exfiltration via DNS queries
$ModuleName = "DNSExfiltrationDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-DNSExfiltrationDetection {
$detections = @()
try {
# Monitor DNS queries
try {
$dnsEvents = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-DNS-Client/Operational'; Id=3008} -ErrorAction SilentlyContinue -MaxEvents 500
$suspiciousQueries = @()
foreach ($event in $dnsEvents) {
try {
$xml = [xml]$event.ToXml()
$queryName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'QueryName'}).'#text'
$queryType = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'QueryType'}).'#text'
if ($queryName) {
# Check for base64-encoded subdomains (common in DNS exfiltration)
$subdomain = $queryName.Split('.')[0]
if ($subdomain.Length -gt 20 -and
$subdomain -match '^[A-Za-z0-9+/=]+$' -and
$subdomain.Length -gt 50) {
$suspiciousQueries += @{
QueryName = $queryName
QueryType = $queryType
TimeCreated = $event.TimeCreated
Type = "Base64-Encoded DNS Query"
Risk = "High"
}
}
# Check for hex-encoded subdomains
if ($subdomain.Length -gt 20 -and
$subdomain -match '^[A-Fa-f0-9]+$' -and
$subdomain.Length -gt 50) {
$suspiciousQueries += @{
QueryName = $queryName
QueryType = $queryType
TimeCreated = $event.TimeCreated
Type = "Hex-Encoded DNS Query"
Risk = "High"
}
}
# Check for unusually long domain names
if ($queryName.Length -gt 253) {
$suspiciousQueries += @{
QueryName = $queryName
QueryType = $queryType
TimeCreated = $event.TimeCreated
Type = "Unusually Long DNS Query"
Risk = "Medium"
}
}
# Check for queries to suspicious TLDs
$suspiciousTLDs = @(".onion", ".bit", ".i2p", ".test", ".local")
foreach ($tld in $suspiciousTLDs) {
if ($queryName -like "*$tld") {
$suspiciousQueries += @{
QueryName = $queryName
QueryType = $queryType
TimeCreated = $event.TimeCreated
Type = "DNS Query to Suspicious TLD"
TLD = $tld
Risk = "Medium"
}
}
}
}
} catch {
continue
}
}
$detections += $suspiciousQueries
# Check for excessive DNS queries from single process
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$procEvents = $dnsEvents | Where-Object {
$_.ProcessId -eq $proc.Id
} -ErrorAction SilentlyContinue
if ($procEvents.Count -gt 100) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
DNSQueryCount = $procEvents.Count
Type = "Excessive DNS Queries from Process"
Risk = "High"
}
}
} catch {
continue
}
}
} catch { }
} catch { }
# Monitor DNS traffic volume
try {
$dnsConnections = Get-NetUDPEndpoint -ErrorAction SilentlyContinue |
Where-Object { $_.LocalPort -eq 53 }
if ($dnsConnections.Count -gt 100) {
$detections += @{
ConnectionCount = $dnsConnections.Count
Type = "High DNS Traffic Volume"
Risk = "Medium"
}
}
} catch { }
# Check for DNS tunneling tools
try {
$processes = Get-CimInstance Win32_Process |
Where-Object {
$_.Name -match 'dnscat|iodine|dns2tcp|dnsperf'
}
foreach ($proc in $processes) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Type = "DNS Tunneling Tool Detected"
Risk = "Critical"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2029 `
-Message "DNS EXFILTRATION: $($detection.Type) - $($detection.QueryName -or $detection.ProcessName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\DNSExfiltration_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.QueryName -or $_.ProcessName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) DNS exfiltration indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-DNSExfiltrationDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- RootkitDetection ---
# Rootkit Detection Module
# Detects rootkit installation and activity
$ModuleName = "RootkitDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-RootkitScan {
$detections = @()
try {
# Check for hidden processes (rootkit indicator)
$processes = Get-Process -ErrorAction SilentlyContinue
$cimProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Select-Object ProcessId, Name
$processIds = $processes | ForEach-Object { $_.Id } | Sort-Object -Unique
$cimProcessIds = $cimProcesses | ForEach-Object { $_.ProcessId } | Sort-Object -Unique
$hiddenProcesses = Compare-Object -ReferenceObject $processIds -DifferenceObject $cimProcessIds
if ($hiddenProcesses) {
foreach ($hidden in $hiddenProcesses) {
$detections += @{
ProcessId = $hidden.InputObject
Type = "Hidden Process Detected"
Risk = "Critical"
}
}
}
# Check for hidden files/directories
try {
$systemDirs = @("$env:SystemRoot\System32", "$env:SystemRoot\SysWOW64")
foreach ($dir in $systemDirs) {
if (Test-Path $dir) {
$files = Get-ChildItem -Path $dir -Force -ErrorAction SilentlyContinue |
Where-Object { $_.Attributes -match 'Hidden' -or $_.Attributes -match 'System' }
$suspiciousFiles = $files | Where-Object {
$_.Name -match '^(\.|\.\.|sys|drv)' -or
$_.Extension -match '^\.(sys|drv|dll)$'
}
if ($suspiciousFiles.Count -gt 10) {
$detections += @{
Directory = $dir
SuspiciousFiles = $suspiciousFiles.Count
Type = "Suspicious Hidden Files in System Directory"
Risk = "High"
}
}
}
}
} catch { }
# Check for kernel drivers
try {
$drivers = Get-CimInstance Win32_SystemDriver -ErrorAction SilentlyContinue |
Where-Object {
$_.State -eq "Running" -and
$_.PathName -notlike "$env:SystemRoot\*" -or
$_.Description -match 'rootkit|stealth|hook'
}
foreach ($driver in $drivers) {
$detections += @{
DriverName = $driver.Name
PathName = $driver.PathName
Description = $driver.Description
Type = "Suspicious Kernel Driver"
Risk = "Critical"
}
}
} catch { }
# Check for SSDT hooks (indirectly through Event Log)
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'; Id=6008} -ErrorAction SilentlyContinue -MaxEvents 50
$hookIndicators = $events | Where-Object {
$_.Message -match 'hook|SSDT|kernel|driver.*unexpected'
}
if ($hookIndicators.Count -gt 0) {
$detections += @{
EventCount = $hookIndicators.Count
Type = "SSDT Hook Indicators"
Risk = "Critical"
}
}
} catch { }
# Check for processes with unusual privileges
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath
foreach ($proc in $processes) {
try {
$procObj = Get-Process -Id $proc.ProcessId -ErrorAction Stop
# Check for SeDebugPrivilege (common in rootkits)
if ($procObj.PrivilegedProcessorTime.TotalSeconds -gt 0 -and
$proc.Name -notin @("csrss.exe", "winlogon.exe", "services.exe")) {
# Indirect check - process with unusual privileges
if ($proc.ExecutablePath -and (Test-Path $proc.ExecutablePath)) {
$sig = Get-AuthenticodeSignature -FilePath $proc.ExecutablePath -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid" -and $proc.ExecutablePath -like "$env:SystemRoot\*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ExecutablePath = $proc.ExecutablePath
Type = "Unsigned Process with System Privileges"
Risk = "High"
}
}
}
}
} catch {
continue
}
}
} catch { }
# Check for unusual boot entries
try {
$bootEntries = Get-CimInstance Win32_BootConfiguration -ErrorAction SilentlyContinue
foreach ($boot in $bootEntries) {
if ($boot.Description -match 'rootkit|stealth|hook' -or
$boot.Description -notlike '*Windows*') {
$detections += @{
BootEntry = $boot.Description
Type = "Suspicious Boot Entry"
Risk = "Critical"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2017 `
-Message "ROOTKIT DETECTED: $($detection.Type) - $($detection.ProcessName -or $detection.DriverName -or $detection.Directory)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\Rootkit_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.DriverName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) rootkit indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-RootkitScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ClipboardMonitoring ---
# Clipboard Monitoring Module
# Monitors clipboard for sensitive data
$ModuleName = "ClipboardMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 10 }
function Invoke-ClipboardMonitoring {
$detections = @()
try {
# Monitor clipboard content
Add-Type -AssemblyName System.Windows.Forms
if ([System.Windows.Forms.Clipboard]::ContainsText()) {
$clipboardText = [System.Windows.Forms.Clipboard]::GetText()
if (-not [string]::IsNullOrEmpty($clipboardText)) {
# Check for sensitive data patterns
$sensitivePatterns = @(
@{Pattern = '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'; Type = 'Email Address'},
@{Pattern = '\b\d{3}-\d{2}-\d{4}\b'; Type = 'SSN'},
@{Pattern = '\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b'; Type = 'Credit Card'},
@{Pattern = '(?i)(password|passwd|pwd|secret|api[_-]?key|token|bearer)'; Type = 'Password/Token'},
@{Pattern = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'; Type = 'IP Address'},
@{Pattern = '(?i)(https?://[^\s]+)'; Type = 'URL'}
)
foreach ($pattern in $sensitivePatterns) {
if ($clipboardText -match $pattern.Pattern) {
$matches = [regex]::Matches($clipboardText, $pattern.Pattern)
if ($matches.Count -gt 0) {
$detections += @{
Type = "Sensitive Data in Clipboard"
DataType = $pattern.Type
MatchCount = $matches.Count
Risk = if ($pattern.Type -match 'Password|SSN|Credit Card') { "High" } else { "Medium" }
}
}
}
}
# Check for large clipboard content (possible exfiltration)
if ($clipboardText.Length -gt 10000) {
$detections += @{
Type = "Large Clipboard Content"
ContentLength = $clipboardText.Length
Risk = "Medium"
}
}
# Check for base64 encoded content
if ($clipboardText -match '^[A-Za-z0-9+/]+={0,2}$' -and $clipboardText.Length -gt 100) {
try {
$decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($clipboardText))
if ($decoded.Length -gt 0) {
$detections += @{
Type = "Base64 Encoded Content in Clipboard"
EncodedLength = $clipboardText.Length
DecodedLength = $decoded.Length
Risk = "Medium"
}
}
} catch { }
}
}
}
# Check for processes accessing clipboard
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules | Where-Object {
$_.ModuleName -match 'clipboard|clip'
}
if ($modules.Count -gt 0) {
# Exclude legitimate processes
$legitProcesses = @("explorer.exe", "dwm.exe", "mstsc.exe")
if ($proc.ProcessName -notin $legitProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Type = "Process Accessing Clipboard"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2018 `
-Message "CLIPBOARD MONITORING: $($detection.Type) - $($detection.DataType -or $detection.ProcessName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\Clipboard_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.DataType -or $_.ProcessName)|$($_.Risk)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) clipboard anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ClipboardMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- COMMonitoring ---
# COM Object Monitoring Module
# Monitors COM object instantiation and usage
$ModuleName = "COMMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-COMMonitoring {
$detections = @()
try {
# Check for suspicious COM objects
$suspiciousCOMObjects = @(
"Shell.Application",
"WScript.Shell",
"Scripting.FileSystemObject",
"Excel.Application",
"Word.Application",
"InternetExplorer.Application"
)
# Check running processes for COM usage
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, CommandLine
foreach ($proc in $processes) {
try {
# Check command line for COM object creation
if ($proc.CommandLine) {
foreach ($comObj in $suspiciousCOMObjects) {
if ($proc.CommandLine -like "*$comObj*" -or
$proc.CommandLine -like "*New-Object*$comObj*" -or
$proc.CommandLine -like "*CreateObject*$comObj*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
COMObject = $comObj
Type = "Suspicious COM Object Usage"
Risk = "Medium"
}
}
}
}
} catch {
continue
}
}
# Check Event Log for COM object registration
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Application'} -ErrorAction SilentlyContinue -MaxEvents 500 |
Where-Object { $_.Message -match 'COM|Component Object Model' }
$registrationEvents = $events | Where-Object {
$_.Message -match 'registration|registration.*failed|COM.*error'
}
if ($registrationEvents.Count -gt 5) {
$detections += @{
EventCount = $registrationEvents.Count
Type = "Unusual COM Registration Activity"
Risk = "Medium"
}
}
} catch { }
# Check for COM hijacking
try {
$comKeys = @(
"HKCU:\SOFTWARE\Classes\CLSID",
"HKLM:\SOFTWARE\Classes\CLSID"
)
foreach ($comKey in $comKeys) {
if (Test-Path $comKey) {
$clsidKeys = Get-ChildItem -Path $comKey -ErrorAction SilentlyContinue | Select-Object -First 100
foreach ($clsid in $clsidKeys) {
$inprocServer = Join-Path $clsid.PSPath "InprocServer32"
if (Test-Path $inprocServer) {
$default = (Get-ItemProperty -Path $inprocServer -Name "(default)" -ErrorAction SilentlyContinue).'(default)'
if ($default -and $default -notlike "$env:SystemRoot\*") {
$detections += @{
CLSID = $clsid.Name
InprocServer = $default
Type = "COM Hijacking - Non-System InprocServer"
Risk = "High"
}
}
}
}
}
}
} catch { }
# Check for Excel/Word automation (common in malware)
try {
$excelProcs = Get-Process -Name "EXCEL" -ErrorAction SilentlyContinue
$wordProcs = Get-Process -Name "WINWORD" -ErrorAction SilentlyContinue
foreach ($proc in ($excelProcs + $wordProcs)) {
try {
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId=$($proc.Id)").CommandLine
if ($commandLine -match '\.vbs|\.js|\.ps1|\.bat|powershell|cmd') {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
CommandLine = $commandLine
Type = "Office Application with Script Execution"
Risk = "High"
}
}
} catch { }
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2019 `
-Message "COM MONITORING: $($detection.Type) - $($detection.ProcessName -or $detection.CLSID -or $detection.COMObject)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\COMMonitoring_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.COMObject -or $_.CLSID)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) COM object anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-COMMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- BrowserExtensionMonitoring ---
# Browser Extension Monitoring Module
# Monitors browser extensions for malicious activity
$ModuleName = "BrowserExtensionMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-BrowserExtensionMonitoring {
$detections = @()
try {
# Check Chrome extensions
$chromeExtensionsPath = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions"
if (Test-Path $chromeExtensionsPath) {
$chromeExts = Get-ChildItem -Path $chromeExtensionsPath -Directory -ErrorAction SilentlyContinue
foreach ($ext in $chromeExts) {
$manifestPath = Join-Path $ext.FullName "*\manifest.json"
$manifests = Get-ChildItem -Path $manifestPath -ErrorAction SilentlyContinue
foreach ($manifest in $manifests) {
try {
$manifestContent = Get-Content $manifest.FullName -Raw | ConvertFrom-Json -ErrorAction Stop
# Check for suspicious permissions
$suspiciousPermissions = @("all_urls", "tabs", "cookies", "history", "downloads", "webRequest", "webRequestBlocking")
$hasSuspiciousPerms = $false
if ($manifestContent.permissions) {
foreach ($perm in $manifestContent.permissions) {
if ($perm -in $suspiciousPermissions) {
$hasSuspiciousPerms = $true
break
}
}
}
# Check for unsigned extensions
$isSigned = $manifestContent.key -ne $null
if ($hasSuspiciousPerms -or -not $isSigned) {
$detections += @{
Browser = "Chrome"
ExtensionId = $ext.Name
ExtensionName = $manifestContent.name
ManifestPath = $manifest.FullName
HasSuspiciousPermissions = $hasSuspiciousPerms
IsSigned = $isSigned
Type = "Suspicious Chrome Extension"
Risk = if ($hasSuspiciousPerms) { "High" } else { "Medium" }
}
}
} catch {
continue
}
}
}
}
# Check Edge extensions
$edgeExtensionsPath = "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Extensions"
if (Test-Path $edgeExtensionsPath) {
$edgeExts = Get-ChildItem -Path $edgeExtensionsPath -Directory -ErrorAction SilentlyContinue
foreach ($ext in $edgeExts) {
$manifestPath = Join-Path $ext.FullName "*\manifest.json"
$manifests = Get-ChildItem -Path $manifestPath -ErrorAction SilentlyContinue
foreach ($manifest in $manifests) {
try {
$manifestContent = Get-Content $manifest.FullName -Raw | ConvertFrom-Json -ErrorAction Stop
if ($manifestContent.permissions) {
$suspiciousPerms = $manifestContent.permissions | Where-Object {
$_ -in @("all_urls", "tabs", "cookies", "webRequest")
}
if ($suspiciousPerms.Count -gt 0) {
$detections += @{
Browser = "Edge"
ExtensionId = $ext.Name
ExtensionName = $manifestContent.name
SuspiciousPermissions = $suspiciousPerms -join ','
Type = "Suspicious Edge Extension"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
}
}
# Check Firefox extensions
$firefoxProfilesPath = "$env:APPDATA\Mozilla\Firefox\Profiles"
if (Test-Path $firefoxProfilesPath) {
$profiles = Get-ChildItem -Path $firefoxProfilesPath -Directory -ErrorAction SilentlyContinue
foreach ($profile in $profiles) {
$extensionsPath = Join-Path $profile.FullName "extensions"
if (Test-Path $extensionsPath) {
$firefoxExts = Get-ChildItem -Path $extensionsPath -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -eq ".xpi" -or $_.Extension -eq "" }
foreach ($ext in $firefoxExts) {
$detections += @{
Browser = "Firefox"
ExtensionPath = $ext.FullName
Type = "Firefox Extension Detected"
Risk = "Low"
}
}
}
}
}
# Check for browser processes with unusual activity
try {
$browserProcs = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessName -match 'chrome|edge|firefox|msedge' }
foreach ($proc in $browserProcs) {
try {
$conns = Get-NetTCPConnection -OwningProcess $proc.Id -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" }
# Check for connections to suspicious domains
$remoteIPs = $conns.RemoteAddress | Select-Object -Unique
foreach ($ip in $remoteIPs) {
try {
$hostname = [System.Net.Dns]::GetHostEntry($ip).HostName
$suspiciousDomains = @(".onion", ".bit", ".i2p", "pastebin", "githubusercontent")
foreach ($domain in $suspiciousDomains) {
if ($hostname -like "*$domain*") {
$detections += @{
BrowserProcess = $proc.ProcessName
ProcessId = $proc.Id
ConnectedDomain = $hostname
Type = "Browser Connecting to Suspicious Domain"
Risk = "Medium"
}
}
}
} catch { }
}
} catch {
continue
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2020 `
-Message "BROWSER EXTENSION: $($detection.Type) - $($detection.ExtensionName -or $detection.BrowserProcess)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\BrowserExtension_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ExtensionName -or $_.BrowserProcess)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) browser extension anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-BrowserExtensionMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ShadowCopyMonitoring ---
# Shadow Copy Monitoring Module
# Monitors shadow copy deletion (ransomware indicator)
$ModuleName = "ShadowCopyMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-ShadowCopyMonitoring {
$detections = @()
try {
# Check for shadow copies
$shadowCopies = Get-CimInstance Win32_ShadowCopy -ErrorAction SilentlyContinue
# Check shadow copy count
if ($shadowCopies.Count -eq 0) {
$detections += @{
Type = "No Shadow Copies Found"
ShadowCopyCount = 0
Risk = "Medium"
}
}
# Monitor shadow copy deletion
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'} -ErrorAction SilentlyContinue -MaxEvents 200 |
Where-Object {
$_.Message -match 'shadow|vss|volume.*snapshot' -or
$_.Id -in @(8221, 8222, 8223, 8224)
}
$deletionEvents = $events | Where-Object {
$_.Message -match 'delete|deleted|remove|removed'
}
if ($deletionEvents.Count -gt 0) {
foreach ($event in $deletionEvents) {
$detections += @{
EventId = $event.Id
TimeCreated = $event.TimeCreated
Message = $event.Message
Type = "Shadow Copy Deletion Detected"
Risk = "High"
}
}
}
} catch { }
# Check for VSSAdmin usage
try {
$processes = Get-CimInstance Win32_Process |
Where-Object { $_.Name -eq "vssadmin.exe" -or $_.CommandLine -like "*vssadmin*" }
foreach ($proc in $processes) {
if ($proc.CommandLine -match 'delete.*shadows|resize.*shadowstorage') {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Type = "VSSAdmin Shadow Copy Manipulation"
Risk = "Critical"
}
}
}
} catch { }
# Check for Volume Shadow Copy Service status
try {
$vssService = Get-CimInstance Win32_Service -Filter "Name='VSS'" -ErrorAction SilentlyContinue
if ($vssService) {
if ($vssService.State -ne "Running") {
$detections += @{
ServiceState = $vssService.State
Type = "Volume Shadow Copy Service Not Running"
Risk = "High"
}
}
}
} catch { }
# Check shadow storage configuration
try {
$shadowStorage = Get-CimInstance Win32_ShadowStorage -ErrorAction SilentlyContinue
foreach ($storage in $shadowStorage) {
# Check if shadow storage is disabled or too small
$allocated = $storage.AllocatedSpace
$maxSize = $storage.MaxSpace
if ($maxSize -eq 0 -or $allocated -eq 0) {
$detections += @{
Volume = $storage.Volume
AllocatedSpace = $allocated
MaxSpace = $maxSize
Type = "Shadow Storage Disabled or Empty"
Risk = "Medium"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2021 `
-Message "SHADOW COPY MONITORING: $($detection.Type) - $($detection.ProcessName -or $detection.Volume -or $detection.Message)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ShadowCopy_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.Volume)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) shadow copy anomalies"
}
Write-Output "STATS:$ModuleName`:Shadow copies=$($shadowCopies.Count)"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ShadowCopyMonitoring
$LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- USBMonitoring ---
# USB Device Monitoring Module
# Monitors USB device connections and activity
$ModuleName = "USBMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-USBMonitoring {
$detections = @()
try {
# Get USB devices
$usbDevices = Get-CimInstance Win32_USBControllerDevice -ErrorAction SilentlyContinue
foreach ($usbDevice in $usbDevices) {
try {
$device = Get-CimInstance -InputObject $usbDevice.Dependent -ErrorAction SilentlyContinue
if ($device) {
# Check for suspicious USB device types
$suspiciousDevices = @{
"HID" = "Human Interface Device"
"USBSTOR" = "USB Mass Storage"
}
$deviceType = $device.DeviceID
# Check for HID devices (keyloggers, etc.)
if ($deviceType -match "HID") {
$detections += @{
DeviceId = $device.DeviceID
DeviceName = $device.Name
DeviceType = "HID Device"
Type = "USB HID Device Connected"
Risk = "Medium"
}
}
# Check for USB storage devices
if ($deviceType -match "USBSTOR") {
$detections += @{
DeviceId = $device.DeviceID
DeviceName = $device.Name
DeviceType = "USB Storage"
Type = "USB Storage Device Connected"
Risk = "Low"
}
}
}
} catch {
continue
}
}
# Check Event Log for USB device events
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'; Id=20001,20002,20003} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object { $_.Message -match 'USB|removable|storage' }
$recentEvents = $events | Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromMinutes(5)
}
if ($recentEvents.Count -gt 5) {
$detections += @{
EventCount = $recentEvents.Count
Type = "Excessive USB Device Activity"
Risk = "Medium"
}
}
} catch { }
# Check for USB device autorun
try {
$autorunKeys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"
)
foreach ($key in $autorunKeys) {
if (Test-Path $key) {
$noDriveAutorun = Get-ItemProperty -Path $key -Name "NoDriveTypeAutoRun" -ErrorAction SilentlyContinue
if ($noDriveAutorun -and $noDriveAutorun.NoDriveTypeAutoRun -eq 0) {
$detections += @{
RegistryKey = $key
Type = "USB Autorun Enabled"
Risk = "High"
}
}
}
}
} catch { }
# Check for processes accessing USB devices
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules | Where-Object {
$_.ModuleName -match 'usb|hid|storage'
}
if ($modules.Count -gt 0) {
# Exclude legitimate processes
$legitProcesses = @("explorer.exe", "svchost.exe", "services.exe")
if ($proc.ProcessName -notin $legitProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
USBModules = $modules.ModuleName -join ','
Type = "Process Accessing USB Devices"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
} catch { }
# Check for USB device driver modifications
try {
$usbDrivers = Get-CimInstance Win32_SystemDriver -ErrorAction SilentlyContinue |
Where-Object {
$_.PathName -match 'usb|hid' -and
$_.PathName -notlike "$env:SystemRoot\*"
}
foreach ($driver in $usbDrivers) {
$detections += @{
DriverName = $driver.Name
DriverPath = $driver.PathName
Type = "Non-Standard USB Driver"
Risk = "High"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Information -EventId 2022 `
-Message "USB MONITORING: $($detection.Type) - $($detection.DeviceName -or $detection.ProcessName -or $detection.DriverName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\USBMonitoring_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.DeviceName -or $_.ProcessName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) USB device anomalies"
}
Write-Output "STATS:$ModuleName`:USB devices=$($usbDevices.Count)"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-USBMonitoring
$LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- WebcamGuardian ---
# Webcam Guardian Module
# Monitors webcam access and protects privacy
$ModuleName = "WebcamGuardian"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 20 }
function Invoke-WebcamGuardian {
$detections = @()
try {
# Check for webcam devices
try {
$webcamDevices = Get-CimInstance Win32_PnPEntity -ErrorAction SilentlyContinue |
Where-Object {
$_.Name -match 'camera|webcam|video|imaging|usb.*video' -or
$_.PNPDeviceID -match 'VID_.*PID_.*.*CAMERA'
}
if ($webcamDevices.Count -gt 0) {
Write-Output "STATS:$ModuleName`:Webcam devices=$($webcamDevices.Count)"
}
} catch { }
# Check for processes accessing webcam
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
# Check for webcam-related modules
$modules = $proc.Modules | Where-Object {
$_.ModuleName -match 'ksuser|avicap32|msvfw32|amstream|qcap|vidcap'
}
if ($modules.Count -gt 0) {
# Check if process is authorized
$authorizedProcesses = @("explorer.exe", "dwm.exe", "chrome.exe", "firefox.exe", "msedge.exe", "teams.exe", "zoom.exe", "skype.exe")
if ($proc.ProcessName -notin $authorizedProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ProcessPath = $proc.Path
WebcamModules = $modules.ModuleName -join ','
Type = "Unauthorized Webcam Access"
Risk = "High"
}
}
}
# Check for video capture libraries
$videoModules = $proc.Modules | Where-Object {
$_.ModuleName -match 'video|capture|stream|directshow|media'
}
if ($videoModules.Count -gt 3 -and
$proc.ProcessName -notin $authorizedProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
VideoModules = $videoModules.ModuleName -join ','
Type = "Process with Many Video Capture Modules"
Risk = "Medium"
}
}
} catch {
continue
}
}
} catch { }
# Check for webcam-related registry keys
try {
$webcamRegKeys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam",
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam"
)
foreach ($regKey in $webcamRegKeys) {
if (Test-Path $regKey) {
$values = Get-ItemProperty -Path $regKey -ErrorAction SilentlyContinue
if ($values) {
# Check for applications with webcam access
$appAccess = $values.PSObject.Properties | Where-Object {
$_.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider') -and
$_.Value -ne "Deny"
}
foreach ($app in $appAccess) {
# Check if app is suspicious
if ($app.Name -notmatch 'microsoft|windows|explorer|chrome|firefox|edge|teams|zoom|skype') {
$detections += @{
RegistryKey = $regKey
AppName = $app.Name
Access = $app.Value
Type = "Suspicious App with Webcam Access"
Risk = "High"
}
}
}
}
}
}
} catch { }
# Check Event Log for webcam access
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Application'} -ErrorAction SilentlyContinue -MaxEvents 500 |
Where-Object {
$_.Message -match 'camera|webcam|video.*capture|imaging.*device'
}
$webcamEvents = $events | Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromMinutes(5)
}
if ($webcamEvents.Count -gt 10) {
$detections += @{
EventCount = $webcamEvents.Count
Type = "Excessive Webcam Access Activity"
Risk = "Medium"
}
}
} catch { }
# Check for webcam blocking/monitoring tools
try {
$processes = Get-CimInstance Win32_Process |
Where-Object {
$_.Name -match 'camtasia|obs|webcam|camera|guardian|privacy'
}
foreach ($proc in $processes) {
# These are usually legitimate, but log them
Write-Output "STATS:$ModuleName`:Webcam-related process=$($proc.Name)"
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2031 `
-Message "WEBCAM GUARDIAN: $($detection.Type) - $($detection.ProcessName -or $detection.AppName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\WebcamGuardian_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.AppName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) webcam access anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-WebcamGuardian
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- AttackToolsDetection ---
# Attack Tools Detection
# Detects Mimikatz, Cobalt Strike, Metasploit, etc.
$ModuleName = "AttackToolsDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 90 }
$AttackTools = @("mimikatz", "pwdump", "procdump", "wce", "gsecdump", "cain", "john", "hashcat", "hydra", "medusa", "nmap", "metasploit", "armitage", "cobalt")
function Invoke-AttackToolsScan {
$detections = @()
try {
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Select-Object ProcessId, Name, CommandLine
foreach ($proc in $processes) {
$name = if ($proc.Name) { $proc.Name.ToLower() } else { "" }
$cmd = if ($proc.CommandLine) { $proc.CommandLine.ToLower() } else { "" }
foreach ($tool in $AttackTools) {
if ($name -like "*$tool*" -or $cmd -like "*$tool*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Tool = $tool
Type = "Attack Tool Detected"
Risk = "Critical"
}
break
}
}
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2092 -Message "ATTACK TOOL: $($d.Tool) - $($d.ProcessName) (PID: $($d.ProcessId))" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\attack_tools_detection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object { "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Tool)|$($_.ProcessName)|PID:$($_.ProcessId)" | Add-Content -Path $logPath }
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) attack tools"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-AttackToolsScan | Out-Null
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- AdvancedThreatDetection ---
# Advanced Threat Detection
# High-entropy files in Windows\Temp, System32\Tasks
$ModuleName = "AdvancedThreatDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 90 }
$ScanPaths = @("$env:SystemRoot\Temp", "$env:SystemRoot\System32\Tasks")
$Extensions = @("*.exe", "*.dll", "*.ps1", "*.vbs")
function Measure-FileEntropy {
param([string]$FilePath)
try {
$bytes = [System.IO.File]::ReadAllBytes($FilePath)
if ($bytes.Length -eq 0) { return 0 }
$sample = if ($bytes.Length -gt 4096) { $bytes[0..4095] } else { $bytes }
$freq = @{}
foreach ($b in $sample) {
if (-not $freq.ContainsKey($b)) { $freq[$b] = 0 }
$freq[$b]++
}
$entropy = 0
foreach ($c in $freq.Values) {
$p = $c / $sample.Count
$entropy -= $p * [Math]::Log($p, 2)
}
return $entropy
} catch { return 0 }
}
function Invoke-AdvancedThreatScan {
$detections = @()
foreach ($basePath in $ScanPaths) {
if (-not (Test-Path $basePath)) { continue }
foreach ($ext in $Extensions) {
try {
Get-ChildItem -Path $basePath -Filter $ext -File -ErrorAction SilentlyContinue | ForEach-Object {
$ent = Measure-FileEntropy -FilePath $_.FullName
if ($ent -gt 7.5) {
$detections += @{
Path = $_.FullName
Entropy = [Math]::Round($ent, 2)
Type = "High-Entropy File"
Risk = "High"
}
}
}
} catch { }
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\advanced_threat_detection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Path)|Entropy:$($_.Entropy)" | Add-Content -Path $logPath
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2091 -Message "ADVANCED THREAT: $($_.Path)" -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) high-entropy files"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-AdvancedThreatScan | Out-Null
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- FirewallRuleMonitoring ---
# Firewall Rule Monitoring Module
# Monitors firewall rules for suspicious changes
$ModuleName = "FirewallRuleMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
$BaselineRules = @{}
function Initialize-FirewallBaseline {
try {
$rules = Get-NetFirewallRule -ErrorAction SilentlyContinue
foreach ($rule in $rules) {
$key = "$($rule.Name)|$($rule.Direction)|$($rule.Action)"
if (-not $BaselineRules.ContainsKey($key)) {
$BaselineRules[$key] = @{
Name = $rule.Name
Direction = $rule.Direction
Action = $rule.Action
Enabled = $rule.Enabled
FirstSeen = Get-Date
}
}
}
} catch { }
}
function Invoke-FirewallRuleMonitoring {
$detections = @()
try {
# Get current firewall rules
$rules = Get-NetFirewallRule -ErrorAction SilentlyContinue
# Check for new or modified rules
foreach ($rule in $rules) {
$key = "$($rule.Name)|$($rule.Direction)|$($rule.Action)"
if (-not $BaselineRules.ContainsKey($key)) {
# New rule detected
$detections += @{
RuleName = $rule.Name
Direction = $rule.Direction
Action = $rule.Action
Enabled = $rule.Enabled
Type = "New Firewall Rule"
Risk = "Medium"
}
# Update baseline
$BaselineRules[$key] = @{
Name = $rule.Name
Direction = $rule.Direction
Action = $rule.Action
Enabled = $rule.Enabled
FirstSeen = Get-Date
}
} else {
# Check if rule was modified
$baseline = $BaselineRules[$key]
if ($rule.Enabled -ne $baseline.Enabled) {
$detections += @{
RuleName = $rule.Name
OldState = $baseline.Enabled
NewState = $rule.Enabled
Type = "Firewall Rule State Changed"
Risk = "High"
}
$baseline.Enabled = $rule.Enabled
}
}
}
# Check for suspicious firewall rules
foreach ($rule in $rules) {
try {
$ruleFilters = Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
$ruleAppFilters = Get-NetFirewallApplicationFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
# Check for rules allowing all traffic
if ($ruleFilters) {
if ($ruleFilters.RemoteAddress -eq "*" -and $rule.Action -eq "Allow") {
$detections += @{
RuleName = $rule.Name
RemoteAddress = $ruleFilters.RemoteAddress
Type = "Firewall Rule Allows All Traffic"
Risk = "High"
}
}
}
# Check for rules allowing unsigned applications
if ($ruleAppFilters -and $ruleAppFilters.Program) {
foreach ($program in $ruleAppFilters.Program) {
if ($program -and (Test-Path $program)) {
$sig = Get-AuthenticodeSignature -FilePath $program -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid" -and $rule.Action -eq "Allow") {
$detections += @{
RuleName = $rule.Name
Program = $program
Type = "Firewall Rule Allows Unsigned Application"
Risk = "Medium"
}
}
}
}
}
# Check for rules with unusual port ranges
$portFilters = Get-NetFirewallPortFilter -AssociatedNetFirewallRule $rule -ErrorAction SilentlyContinue
if ($portFilters) {
if ($portFilters.LocalPort -eq "*" -or
($portFilters.LocalPort -is [Array] -and $portFilters.LocalPort.Count -gt 100)) {
$detections += @{
RuleName = $rule.Name
LocalPort = $portFilters.LocalPort
Type = "Firewall Rule with Unusual Port Range"
Risk = "Medium"
}
}
}
} catch {
continue
}
}
# Check for firewall service status
try {
$fwService = Get-CimInstance Win32_Service -Filter "Name='MpsSvc'" -ErrorAction SilentlyContinue
if ($fwService -and $fwService.State -ne "Running") {
$detections += @{
ServiceState = $fwService.State
Type = "Windows Firewall Service Not Running"
Risk = "Critical"
}
}
} catch { }
# Check for firewall profiles
try {
$profiles = Get-NetFirewallProfile -ErrorAction SilentlyContinue
foreach ($profile in $profiles) {
if ($profile.Enabled -eq $false) {
$detections += @{
ProfileName = $profile.Name
Type = "Firewall Profile Disabled"
Risk = "Critical"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2024 `
-Message "FIREWALL RULE MONITORING: $($detection.Type) - $($detection.RuleName -or $detection.ProfileName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\FirewallRule_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.RuleName -or $_.ProfileName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) firewall rule anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
Initialize-FirewallBaseline
Start-Sleep -Seconds 10
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-FirewallRuleMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ServiceMonitoring ---
# Service Monitoring Module
# Monitors Windows services for suspicious activity
$ModuleName = "ServiceMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
$BaselineServices = @{}
function Initialize-ServiceBaseline {
try {
$services = Get-CimInstance Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
$key = "$($svc.Name)|$($svc.PathName)"
if (-not $BaselineServices.ContainsKey($key)) {
$BaselineServices[$key] = @{
Name = $svc.Name
DisplayName = $svc.DisplayName
PathName = $svc.PathName
State = $svc.State
StartMode = $svc.StartMode
StartName = $svc.StartName
FirstSeen = Get-Date
}
}
}
} catch { }
}
function Invoke-ServiceMonitoring {
$detections = @()
try {
$services = Get-CimInstance Win32_Service -ErrorAction SilentlyContinue
foreach ($svc in $services) {
$key = "$($svc.Name)|$($svc.PathName)"
# Check for new services
if (-not $BaselineServices.ContainsKey($key)) {
$detections += @{
ServiceName = $svc.Name
DisplayName = $svc.DisplayName
PathName = $svc.PathName
State = $svc.State
StartMode = $svc.StartMode
Type = "New Service Detected"
Risk = "High"
}
# Update baseline
$BaselineServices[$key] = @{
Name = $svc.Name
DisplayName = $svc.DisplayName
PathName = $svc.PathName
State = $svc.State
StartMode = $svc.StartMode
StartName = $svc.StartName
FirstSeen = Get-Date
}
} else {
# Check for service state changes
$baseline = $BaselineServices[$key]
if ($svc.State -ne $baseline.State) {
$detections += @{
ServiceName = $svc.Name
OldState = $baseline.State
NewState = $svc.State
Type = "Service State Changed"
Risk = "Medium"
}
$baseline.State = $svc.State
}
}
# Check for suspicious service properties
if ($svc.PathName -and (Test-Path $svc.PathName)) {
# Check for unsigned service executables
$sig = Get-AuthenticodeSignature -FilePath $svc.PathName -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
$detections += @{
ServiceName = $svc.Name
PathName = $svc.PathName
Type = "Unsigned Service Executable"
Risk = "High"
}
}
# Check for services not in system directories
if ($svc.PathName -notlike "$env:SystemRoot\*" -and
$svc.PathName -notlike "$env:ProgramFiles*") {
$detections += @{
ServiceName = $svc.Name
PathName = $svc.PathName
Type = "Service Executable Outside System/Program Directories"
Risk = "Medium"
}
}
# Check for services with suspicious command line arguments
if ($svc.PathName -match 'powershell|cmd|wscript|cscript|http') {
$detections += @{
ServiceName = $svc.Name
PathName = $svc.PathName
Type = "Service with Suspicious Command Line"
Risk = "High"
}
}
}
# Check for services running as SYSTEM with suspicious paths
if ($svc.StartName -eq "LocalSystem" -or $svc.StartName -eq "NT AUTHORITY\SYSTEM") {
if ($svc.PathName -notlike "$env:SystemRoot\*") {
$detections += @{
ServiceName = $svc.Name
StartName = $svc.StartName
PathName = $svc.PathName
Type = "SYSTEM Service Outside System Directory"
Risk = "Critical"
}
}
}
# Check for services with unusual display names
if ($svc.DisplayName -match 'update|installer|system|security|windows' -and
$svc.Name -notmatch '^[A-Z][a-z]') {
# Suspicious display name pattern
$detections += @{
ServiceName = $svc.Name
DisplayName = $svc.DisplayName
Type = "Service with Suspicious Display Name"
Risk = "Medium"
}
}
}
# Check for stopped critical services
$criticalServices = @("WinDefend", "SecurityHealthService", "MpsSvc", "BITS")
foreach ($criticalSvc in $criticalServices) {
$svc = $services | Where-Object { $_.Name -eq $criticalSvc } | Select-Object -First 1
if ($svc -and $svc.State -ne "Running") {
$detections += @{
ServiceName = $svc.Name
State = $svc.State
Type = "Critical Service Stopped"
Risk = "High"
}
}
}
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2025 `
-Message "SERVICE MONITORING: $($detection.Type) - $($detection.ServiceName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ServiceMonitoring_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ServiceName)|$($_.PathName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) service anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
Initialize-ServiceBaseline
Start-Sleep -Seconds 10
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ServiceMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- FilelessDetection ---
# Fileless Malware Detection Module
# Detects fileless malware techniques
$ModuleName = "FilelessDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 20 }
function Invoke-FilelessDetection {
$detections = @()
try {
# Check for PowerShell in memory execution
$psProcesses = Get-Process -Name "powershell*","pwsh*" -ErrorAction SilentlyContinue
foreach ($psProc in $psProcesses) {
try {
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId=$($psProc.Id)").CommandLine
if ($commandLine) {
# Check for encoded commands
if ($commandLine -match '-encodedcommand|-enc|-e\s+[A-Za-z0-9+/=]{100,}') {
$detections += @{
ProcessId = $psProc.Id
ProcessName = $psProc.ProcessName
CommandLine = $commandLine
Type = "PowerShell Encoded Command Execution"
Risk = "High"
}
}
# Check for IEX/Invoke-Expression usage
if ($commandLine -match '(?i)(iex|invoke-expression|invoke-expression)') {
$detections += @{
ProcessId = $psProc.Id
ProcessName = $psProc.ProcessName
CommandLine = $commandLine
Type = "PowerShell IEX Execution"
Risk = "High"
}
}
# Check for download and execute
if ($commandLine -match '(?i)(downloadstring|downloadfile|webclient|invoke-webrequest).*(http|https|ftp)') {
$detections += @{
ProcessId = $psProc.Id
ProcessName = $psProc.ProcessName
CommandLine = $commandLine
Type = "PowerShell Download and Execute"
Risk = "Critical"
}
}
}
} catch {
continue
}
}
# Check for WMI event consumers (fileless persistence)
try {
$wmiConsumers = Get-CimInstance -Namespace root\subscription -ClassName __EventConsumer -ErrorAction SilentlyContinue
foreach ($consumer in $wmiConsumers) {
if ($consumer.__CLASS -match 'ActiveScript|CommandLine') {
$detections += @{
ConsumerName = $consumer.Name
ConsumerClass = $consumer.__CLASS
Type = "WMI Fileless Persistence"
Risk = "High"
}
}
}
} catch { }
# Check for Registry-based fileless execution
try {
$regKeys = @(
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($regKey in $regKeys) {
if (Test-Path $regKey) {
$values = Get-ItemProperty -Path $regKey -ErrorAction SilentlyContinue
if ($values) {
$valueProps = $values.PSObject.Properties | Where-Object {
$_.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider')
}
foreach ($prop in $valueProps) {
$value = $prop.Value
# Check for registry-based script execution
if ($value -match 'powershell.*-enc|wscript|javascript|vbscript' -and
-not (Test-Path $value)) {
$detections += @{
RegistryPath = $regKey
ValueName = $prop.Name
Value = $value
Type = "Registry-Based Fileless Execution"
Risk = "High"
}
}
}
}
}
}
} catch { }
# Check for .NET reflection-based execution
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules | Where-Object {
$_.ModuleName -match 'System\.Reflection|System\.CodeDom'
}
if ($modules.Count -gt 0) {
# Check for unsigned processes using reflection
if ($proc.Path -and (Test-Path $proc.Path)) {
$sig = Get-AuthenticodeSignature -FilePath $proc.Path -ErrorAction SilentlyContinue
if ($sig.Status -ne "Valid") {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ReflectionModules = $modules.ModuleName -join ','
Type = "Unsigned Process Using Reflection"
Risk = "Medium"
}
}
}
}
} catch {
continue
}
}
} catch { }
# Check for memory-only modules
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules | Where-Object {
$_.FileName -notlike "$env:SystemRoot\*" -and
$_.FileName -notlike "$env:ProgramFiles*" -and
-not (Test-Path $_.FileName)
}
if ($modules.Count -gt 5) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
MemoryModules = $modules.Count
Type = "Process with Many Memory-Only Modules"
Risk = "High"
}
}
} catch {
continue
}
}
} catch { }
# Check Event Log for fileless execution indicators
try {
$events = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'} -ErrorAction SilentlyContinue -MaxEvents 200 |
Where-Object {
$_.Message -match 'encodedcommand|iex|invoke-expression|downloadstring'
}
foreach ($event in $events) {
$detections += @{
EventId = $event.Id
TimeCreated = $event.TimeCreated
Message = $event.Message
Type = "Event Log Fileless Execution Indicator"
Risk = "Medium"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2026 `
-Message "FILELESS DETECTION: $($detection.Type) - $($detection.ProcessName -or $detection.ConsumerName -or $detection.ValueName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\FilelessDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.ConsumerName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) fileless malware indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-FilelessDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- MemoryScanning ---
# Optimized Memory Scanning Module
# Reduced CPU/RAM/Disk usage with caching and batching
$ModuleName = "MemoryScanning"
$script:LastTick = Get-Date
$TickInterval = Get-TickInterval -ModuleName $ModuleName
$script:ProcessBaseline = @{}
$script:LastBaselineUpdate = Get-Date
function Update-ProcessBaseline {
$now = Get-Date
if (($now - $script:LastBaselineUpdate).TotalMinutes -lt 5) {
return
}
$script:LastBaselineUpdate = $now
$maxProcs = Get-ScanLimit -LimitName "MaxProcesses"
try {
$processes = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.WorkingSet64 -gt 10MB } |
Select-Object -First $maxProcs
foreach ($proc in $processes) {
$key = "$($proc.ProcessName)|$($proc.Id)"
if (-not $script:ProcessBaseline.ContainsKey($key)) {
$script:ProcessBaseline[$key] = @{
FirstSeen = $now
Scanned = $false
}
}
}
$toRemove = @()
foreach ($key in $script:ProcessBaseline.Keys) {
$pid = $key.Split('|')[1]
try {
$null = Get-Process -Id $pid -ErrorAction Stop
} catch {
$toRemove += $key
}
}
foreach ($key in $toRemove) {
$script:ProcessBaseline.Remove($key)
}
} catch { }
}
function Invoke-MemoryScanningOptimized {
$detections = @()
$batchSettings = Get-BatchSettings
$maxProcs = Get-ScanLimit -LimitName "MaxProcesses"
try {
Update-ProcessBaseline
$processes = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.WorkingSet64 -gt 10MB } |
Select-Object -First $maxProcs
$batchCount = 0
foreach ($proc in $processes) {
try {
$key = "$($proc.ProcessName)|$($proc.Id)"
if ($script:ProcessBaseline.ContainsKey($key) -and $script:ProcessBaseline[$key].Scanned) {
continue
}
$batchCount++
if ($batchCount % $batchSettings.BatchSize -eq 0 -and $batchSettings.BatchDelayMs -gt 0) {
Start-Sleep -Milliseconds $batchSettings.BatchDelayMs
}
$modules = $proc.Modules | Where-Object {
$_.FileName -notlike "$env:SystemRoot\*" -and
$_.FileName -notlike "$env:ProgramFiles*"
}
if ($modules.Count -gt 5) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
SuspiciousModules = $modules.Count
Type = "Process with Many Non-System Modules"
Risk = "Medium"
}
}
if ($script:ProcessBaseline.ContainsKey($key)) {
$script:ProcessBaseline[$key].Scanned = $true
}
} catch {
continue
}
}
$now = Get-Date
foreach ($key in $script:ProcessBaseline.Keys) {
if (($now - $script:ProcessBaseline[$key].FirstSeen).TotalSeconds -gt $TickInterval) {
$script:ProcessBaseline[$key].Scanned = $false
$script:ProcessBaseline[$key].FirstSeen = $now
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\MemoryScanning_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName)|$($_.ProcessId)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) memory anomalies"
}
Write-Output "STATS:$ModuleName`:Scanned $($processes.Count) processes"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
Clear-ExpiredCache
return $detections.Count
}
function Start-Module {
$loopSleep = Get-LoopSleep
while ($true) {
try {
# CPU throttling - skip scan if CPU load is too high
if (Test-CPULoadThreshold) {
$cpuLoad = Get-CPULoad
Write-Output "STATS:$ModuleName`:CPU load too high ($cpuLoad%), skipping scan"
Start-Sleep -Seconds ($loopSleep * 2) # Sleep longer when CPU is high
continue
}
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-MemoryScanningOptimized
$script:LastTick = $now
}
Start-Sleep -Seconds $loopSleep
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 120 # Longer sleep on error
}
}
}
# --- NamedPipeMonitoring ---
# Named Pipe Monitoring Module
# Monitors named pipes for malicious activity
$ModuleName = "NamedPipeMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-NamedPipeMonitoring {
$detections = @()
try {
# Get named pipes using WMI
$pipes = Get-CimInstance Win32_NamedPipe -ErrorAction SilentlyContinue
# Check for suspicious named pipes
$suspiciousPatterns = @(
"paexec",
"svchost",
"lsass",
"spoolsv",
"psexec",
"mimikatz",
"impacket"
)
foreach ($pipe in $pipes) {
$pipeNameLower = $pipe.Name.ToLower()
foreach ($pattern in $suspiciousPatterns) {
if ($pipeNameLower -match $pattern) {
$detections += @{
PipeName = $pipe.Name
Pattern = $pattern
Type = "Suspicious Named Pipe Pattern"
Risk = "Medium"
}
break
}
}
# Check for pipes with unusual names (random characters)
if ($pipe.Name -match '^[A-Za-z0-9]{32,}$' -and
$pipe.Name -notmatch 'microsoft|windows|system') {
$detections += @{
PipeName = $pipe.Name
Type = "Named Pipe with Random-Like Name"
Risk = "Low"
}
}
}
# Check for processes creating named pipes
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$procPipes = $pipes | Where-Object {
$_.Instances -gt 0
}
# Check for processes creating many named pipes
$pipeCount = ($procPipes | Measure-Object).Count
if ($pipeCount -gt 10 -and
$proc.ProcessName -notin @("svchost.exe", "spoolsv.exe", "services.exe")) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
PipeCount = $pipeCount
Type = "Process Creating Many Named Pipes"
Risk = "Medium"
}
}
} catch {
continue
}
}
} catch { }
# Check Event Log for named pipe errors
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'} -ErrorAction SilentlyContinue -MaxEvents 200 |
Where-Object {
$_.Message -match 'named.*pipe|pipe.*error|pipe.*failed'
}
$pipeErrors = $events | Where-Object {
$_.LevelDisplayName -eq "Error"
}
if ($pipeErrors.Count -gt 5) {
$detections += @{
EventCount = $pipeErrors.Count
Type = "Excessive Named Pipe Errors"
Risk = "Low"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Information -EventId 2028 `
-Message "NAMED PIPE MONITORING: $($detection.Type) - $($detection.PipeName -or $detection.ProcessName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\NamedPipe_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.PipeName -or $_.ProcessName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) named pipe anomalies"
}
Write-Output "STATS:$ModuleName`:Active pipes=$($pipes.Count)"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-NamedPipeMonitoring
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- CodeInjectionDetection ---
# Code Injection Detection Module
# Detects various code injection techniques
$ModuleName = "CodeInjectionDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-CodeInjectionDetection {
$detections = @()
try {
# Check for processes with unusual memory regions (injection indicator)
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$modules = $proc.Modules
# Check for RWX memory regions (code injection indicator)
# We'll use heuristics since we can't directly query memory protection
# Check for processes with modules in unusual locations
$unusualModules = $modules | Where-Object {
$_.FileName -notlike "$env:SystemRoot\*" -and
$_.FileName -notlike "$env:ProgramFiles*" -and
-not (Test-Path $_.FileName) -and
$_.ModuleName -like "*.dll"
}
if ($unusualModules.Count -gt 5) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
UnusualModules = $unusualModules.Count
Type = "Many Unusual Memory Modules (Code Injection)"
Risk = "High"
}
}
# Check for processes with unusual thread counts (injection indicator)
if ($proc.Threads.Count -gt 50 -and $proc.ProcessName -notin @("chrome.exe", "msedge.exe", "firefox.exe")) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
ThreadCount = $proc.Threads.Count
Type = "Unusual Thread Count (Possible Injection)"
Risk = "Medium"
}
}
} catch {
continue
}
}
# Check for processes using injection APIs
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, CommandLine
foreach ($proc in $processes) {
if ($proc.CommandLine) {
# Check for code injection API usage
$injectionPatterns = @(
'VirtualAllocEx',
'WriteProcessMemory',
'CreateRemoteThread',
'NtCreateThreadEx',
'RtlCreateUserThread',
'SetThreadContext',
'QueueUserAPC',
'ProcessHollowing',
'DLL.*injection',
'code.*injection'
)
foreach ($pattern in $injectionPatterns) {
if ($proc.CommandLine -match $pattern) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
InjectionPattern = $pattern
Type = "Code Injection API Usage"
Risk = "High"
}
break
}
}
}
}
} catch { }
# Check for processes with unusual handle counts (injection indicator)
try {
$processes = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.HandleCount -gt 1000 }
foreach ($proc in $processes) {
# Exclude legitimate processes
$legitProcesses = @("chrome.exe", "msedge.exe", "firefox.exe", "explorer.exe", "svchost.exe")
if ($proc.ProcessName -notin $legitProcesses) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
HandleCount = $proc.HandleCount
Type = "Unusual Handle Count (Possible Injection)"
Risk = "Medium"
}
}
}
} catch { }
# Check for processes accessing other processes (injection indicator)
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ExecutablePath
foreach ($proc in $processes) {
try {
# Check if process has SeDebugPrivilege (enables injection)
# Indirect check through process properties
if ($proc.ExecutablePath -and (Test-Path $proc.ExecutablePath)) {
$sig = Get-AuthenticodeSignature -FilePath $proc.ExecutablePath -ErrorAction SilentlyContinue
# Unsigned processes accessing system processes
if ($sig.Status -ne "Valid" -and $proc.Name -match 'debug|inject|hollow') {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ExecutablePath = $proc.ExecutablePath
Type = "Suspicious Process Name (Injection Tool)"
Risk = "High"
}
}
}
} catch {
continue
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2038 `
-Message "CODE INJECTION: $($detection.Type) - $($detection.ProcessName) (PID: $($detection.ProcessId))"
}
$logPath = "$env:ProgramData\Antivirus\Logs\CodeInjection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|PID:$($_.ProcessId)|$($_.ProcessName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) code injection indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-CodeInjectionDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- DataExfiltrationDetection ---
# Data Exfiltration Detection Module
# Comprehensive data exfiltration detection beyond DNS
$ModuleName = "DataExfiltrationDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
$BaselineTransfer = @{}
function Invoke-DataExfiltrationDetection {
$detections = @()
try {
# Check for large outbound data transfers
try {
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" -and $_.RemoteAddress -notmatch '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.)' }
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$procConns = $connections | Where-Object { $_.OwningProcess -eq $proc.Id }
if ($procConns.Count -gt 0) {
# Check network statistics
$netStats = Get-Counter "\Process($($proc.ProcessName))\IO Data Bytes/sec" -ErrorAction SilentlyContinue
if ($netStats) {
$bytesPerSec = $netStats.CounterSamples[0].CookedValue
# Large outbound transfer
if ($bytesPerSec -gt 1MB) {
$baselineKey = $proc.ProcessName
$baseline = if ($BaselineTransfer.ContainsKey($baselineKey)) { $BaselineTransfer[$baselineKey] } else { 0 }
if ($bytesPerSec -gt $baseline * 2 -and $bytesPerSec -gt 1MB) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
BytesPerSecond = [Math]::Round($bytesPerSec / 1MB, 2)
ConnectionCount = $procConns.Count
Type = "Large Outbound Data Transfer"
Risk = "High"
}
}
$BaselineTransfer[$baselineKey] = $bytesPerSec
}
}
}
} catch {
continue
}
}
} catch { }
# Check for file uploads to cloud storage/suspicious domains
try {
$processes = Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessName -match 'curl|wget|powershell|certutil|bitsadmin' }
foreach ($proc in $processes) {
try {
$procObj = Get-CimInstance Win32_Process -Filter "ProcessId=$($proc.Id)" -ErrorAction SilentlyContinue
if ($procObj.CommandLine) {
$uploadPatterns = @(
'upload|PUT|POST',
'dropbox|google.*drive|onedrive|mega|wetransfer',
'pastebin|github|paste.*bin',
'http.*upload|ftp.*put',
'-OutFile.*http',
'Invoke-WebRequest.*-Method.*Put'
)
foreach ($pattern in $uploadPatterns) {
if ($procObj.CommandLine -match $pattern -and
$procObj.CommandLine -notmatch 'download|get|GET') {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
CommandLine = $procObj.CommandLine
Pattern = $pattern
Type = "File Upload to External Service"
Risk = "High"
}
break
}
}
}
} catch {
continue
}
}
} catch { }
# Check for clipboard with large data (possible exfiltration)
try {
Add-Type -AssemblyName System.Windows.Forms
if ([System.Windows.Forms.Clipboard]::ContainsText()) {
$clipboardText = [System.Windows.Forms.Clipboard]::GetText()
if ($clipboardText.Length -gt 50000) {
# Large clipboard content
$detections += @{
ClipboardSize = $clipboardText.Length
Type = "Large Clipboard Content (Possible Exfiltration)"
Risk = "Medium"
}
}
}
} catch { }
# Check for processes accessing many files then connecting externally
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$handles = Get-CimInstance Win32_ProcessHandle -Filter "ProcessId=$($proc.Id)" -ErrorAction SilentlyContinue
$fileHandles = $handles | Where-Object { $_.Name -like "*.txt" -or $_.Name -like "*.doc*" -or $_.Name -like "*.pdf" -or $_.Name -like "*.xls*" }
if ($fileHandles.Count -gt 20) {
$conns = Get-NetTCPConnection -OwningProcess $proc.Id -ErrorAction SilentlyContinue |
Where-Object {
$_.State -eq "Established" -and
$_.RemoteAddress -notmatch '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.)'
}
if ($conns.Count -gt 0) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
FileHandleCount = $fileHandles.Count
ExternalConnections = $conns.Count
Type = "File Access Followed by External Connection"
Risk = "High"
}
}
}
} catch {
continue
}
}
} catch { }
# Check for processes reading sensitive files then connecting externally
try {
$sensitivePaths = @(
"$env:USERPROFILE\Documents",
"$env:USERPROFILE\Desktop",
"$env:APPDATA\..\Local\Credentials"
)
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
foreach ($sensitivePath in $sensitivePaths) {
if (Test-Path $sensitivePath) {
$files = Get-ChildItem -Path $sensitivePath -File -ErrorAction SilentlyContinue |
Where-Object { (Get-Date) - $_.LastAccessTime -lt [TimeSpan]::FromMinutes(5) }
if ($files.Count -gt 5) {
$conns = Get-NetTCPConnection -OwningProcess $proc.Id -ErrorAction SilentlyContinue |
Where-Object { $_.State -eq "Established" -and $_.RemoteAddress -notmatch '^(10\.|192\.168\.)' }
if ($conns.Count -gt 0) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
SensitiveFilesAccessed = $files.Count
ExternalConnections = $conns.Count
Type = "Sensitive File Access Followed by External Connection"
Risk = "Critical"
}
break
}
}
}
}
} catch {
continue
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2040 `
-Message "DATA EXFILTRATION: $($detection.Type) - $($detection.ProcessName -or 'System')"
}
$logPath = "$env:ProgramData\Antivirus\Logs\DataExfiltration_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) data exfiltration indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-DataExfiltrationDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- HoneypotMonitoring ---
# Honeypot Monitoring
# Creates decoy files and processes to detect unauthorized access
$ModuleName = "HoneypotMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 300 }
$HoneypotFiles = @()
$HoneypotPaths = @(
"$env:USERPROFILE\Desktop\passwords.txt",
"$env:USERPROFILE\Documents\credentials.xlsx",
"$env:USERPROFILE\Documents\credit_cards.txt",
"$env:USERPROFILE\Desktop\private_keys.txt",
"$env:APPDATA\credentials.db",
"$env:USERPROFILE\Documents\financial_data.xlsx"
)
function Initialize-Honeypots {
try {
foreach ($honeypotPath in $HoneypotPaths) {
$dir = Split-Path -Parent $honeypotPath
if (-not (Test-Path $dir)) {
New-Item -Path $dir -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
}
if (-not (Test-Path $honeypotPath)) {
# Create honeypot file with fake but realistic content
$content = switch -Wildcard ([System.IO.Path]::GetFileName($honeypotPath)) {
"*password*" { "FAKE_PASSWORD_FILE_DO_NOT_USE`r`nusername: admin`r`npassword: fake_password_123`r`nLastModified: $(Get-Date -Format 'yyyy-MM-dd')" }
"*credential*" { "FAKE_CREDENTIAL_FILE_DO_NOT_USE`r`nAccount: fake_account`r`nPassword: fake_pass_123`r`nCreated: $(Get-Date -Format 'yyyy-MM-dd')" }
"*credit*" { "FAKE_CREDIT_CARD_FILE_DO_NOT_USE`r`nCard: 4111-1111-1111-1111`r`nCVV: 123`r`nExpiry: 12/25" }
"*key*" { "FAKE_PRIVATE_KEY_FILE_DO_NOT_USE`r`n-----BEGIN FAKE RSA PRIVATE KEY-----`r`nFAKE_KEY_DATA`r`n-----END FAKE RSA PRIVATE KEY-----" }
"*financial*" { "FAKE_FINANCIAL_DATA_FILE_DO_NOT_USE`r`nAccount: 123456789`r`nBalance: $0.00`r`nTransaction: None" }
default { "FAKE_FILE_DO_NOT_USE - Created: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" }
}
Set-Content -Path $honeypotPath -Value $content -ErrorAction SilentlyContinue
# Mark as honeypot with file attribute
try {
$file = Get-Item $honeypotPath -ErrorAction Stop
$file.Attributes = $file.Attributes -bor [System.IO.FileAttributes]::Hidden
} catch { }
$script:HoneypotFiles += @{
Path = $honeypotPath
Created = Get-Date
LastAccessed = $null
AccessCount = 0
}
Write-Output "STATS:$ModuleName`:Created honeypot: $honeypotPath"
}
}
} catch {
Write-Output "ERROR:$ModuleName`:Failed to initialize honeypots: $_"
}
}
function Invoke-HoneypotMonitoring {
$detections = @()
try {
# Check honeypot file access
foreach ($honeypot in $script:HoneypotFiles) {
$honeypotPath = $honeypot.Path
if (Test-Path $honeypotPath) {
try {
$file = Get-Item $honeypotPath -ErrorAction Stop
$lastAccess = $file.LastAccessTime
# Check if file was accessed recently
if ($honeypot.LastAccessed -and $lastAccess -gt $honeypot.LastAccessed) {
$honeypot.AccessCount++
# Detect unauthorized access
if ($honeypot.AccessCount -gt 0) {
# Find process that accessed the file
$accessingProcesses = @()
try {
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
try {
$procObj = Get-CimInstance Win32_Process -Filter "ProcessId=$($proc.Id)" -ErrorAction SilentlyContinue
if ($procObj.CommandLine -like "*$([System.IO.Path]::GetFileName($honeypotPath))*") {
$accessingProcesses += $proc.ProcessName
}
} catch { }
}
} catch { }
$detections += @{
HoneypotPath = $honeypotPath
LastAccess = $lastAccess
AccessCount = $honeypot.AccessCount
AccessingProcesses = $accessingProcesses -join ','
Type = "Honeypot File Accessed"
Risk = "Critical"
}
# Log honeypot trigger
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2041 `
-Message "HONEYPOT TRIGGERED: $honeypotPath was accessed - Processes: $($accessingProcesses -join ', ')"
}
$honeypot.LastAccessed = $lastAccess
}
} catch {
# File may have been deleted or moved
if (-not $honeypot.LastAccessed -or ((Get-Date) - $honeypot.Created).TotalMinutes -lt 5) {
$detections += @{
HoneypotPath = $honeypotPath
Type = "Honeypot File Deleted/Moved"
Risk = "Critical"
}
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2041 `
-Message "HONEYPOT DELETED: $honeypotPath was deleted or moved"
# Recreate honeypot
Initialize-Honeypots
}
}
} else {
# File doesn't exist - recreate
Initialize-Honeypots
}
}
# Check for processes accessing multiple honeypots (malware scanning)
if ($detections.Count -gt 2) {
$detections += @{
HoneypotCount = $detections.Count
Type = "Multiple Honeypots Accessed (Systematic Scanning)"
Risk = "Critical"
}
}
if ($detections.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\Honeypot_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.HoneypotPath)|$($_.AccessingProcesses)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) honeypot access events"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
Initialize-Honeypots
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$count = Invoke-HoneypotMonitoring
$script:LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- LateralMovementDetection ---
# Lateral Movement Detection Module
# Detects lateral movement techniques (SMB, RDP, WMI, PsExec, etc.)
$ModuleName = "LateralMovementDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
function Invoke-LateralMovementDetection {
$detections = @()
try {
# Check for SMB shares enumeration/access
try {
$smbEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=5145} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(1) -and
$_.Message -match 'SMB|share|\\\\.*\\'
}
if ($smbEvents.Count -gt 10) {
$detections += @{
EventCount = $smbEvents.Count
Type = "Excessive SMB Share Access (Lateral Movement)"
Risk = "Medium"
}
}
} catch { }
# Check for RDP connections
try {
$rdpEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} -ErrorAction SilentlyContinue -MaxEvents 200 |
Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(1) -and
$_.Message -match 'RDP|Terminal Services|logon.*type.*10'
}
if ($rdpEvents.Count -gt 5) {
foreach ($event in $rdpEvents) {
$xml = [xml]$event.ToXml()
$subjectUserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'SubjectUserName'}).'#text'
$targetUserName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TargetUserName'}).'#text'
if ($subjectUserName -ne $targetUserName) {
$detections += @{
EventId = $event.Id
SubjectUser = $subjectUserName
TargetUser = $targetUserName
TimeCreated = $event.TimeCreated
Type = "RDP Lateral Movement"
Risk = "High"
}
}
}
}
} catch { }
# Check for PsExec usage
try {
$processes = Get-CimInstance Win32_Process |
Where-Object {
$_.Name -eq "psexec.exe" -or
$_.CommandLine -like "*psexec*" -or
$_.CommandLine -like "*paexec*"
}
foreach ($proc in $processes) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Type = "PsExec Lateral Movement Tool"
Risk = "High"
}
}
} catch { }
# Check for WMI remote execution
try {
$wmiEvents = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-WMI-Activity/Trace'} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(1) -and
($_.Message -match 'remote|Win32_Process.*Create' -or $_.Message -match '\\\\')
}
if ($wmiEvents.Count -gt 5) {
$detections += @{
EventCount = $wmiEvents.Count
Type = "WMI Remote Execution (Lateral Movement)"
Risk = "High"
}
}
} catch { }
# Check for pass-the-hash tools
try {
$pthTools = @("mimikatz", "psexec", "wmiexec", "pth-winexe", "crackmapexec")
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
foreach ($tool in $pthTools) {
if ($proc.ProcessName -like "*$tool*" -or $proc.Path -like "*$tool*") {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Tool = $tool
Type = "Pass-the-Hash Tool Detected"
Risk = "Critical"
}
}
}
}
} catch { }
# Check for suspicious network connections to internal IPs
try {
$connections = Get-NetTCPConnection -ErrorAction SilentlyContinue |
Where-Object {
$_.State -eq "Established" -and
$_.RemoteAddress -match '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)' -and
$_.RemotePort -in @(445, 3389, 135, 5985, 5986) # SMB, RDP, RPC, WinRM
}
$procGroups = $connections | Group-Object OwningProcess
foreach ($group in $procGroups) {
$uniqueIPs = ($group.Group | Select-Object -Unique RemoteAddress).RemoteAddress.Count
if ($uniqueIPs -gt 5) {
try {
$proc = Get-Process -Id $group.Name -ErrorAction SilentlyContinue
if ($proc) {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
InternalIPCount = $uniqueIPs
ConnectionCount = $group.Count
Type = "Multiple Internal Network Connections (Lateral Movement)"
Risk = "High"
}
}
} catch { }
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2039 `
-Message "LATERAL MOVEMENT: $($detection.Type) - $($detection.ProcessName -or $detection.Tool -or $detection.SubjectUser)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\LateralMovement_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.Tool -or $_.SubjectUser)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) lateral movement indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-LateralMovementDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ProcessCreationDetection ---
# Process Creation Detection Module
# Detects WMI-based process creation blocking and suspicious process creation patterns
$ModuleName = "ProcessCreationDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-ProcessCreationDetection {
$detections = @()
try {
# Check for WMI process creation filters (blockers)
try {
$filters = Get-CimInstance -Namespace root\subscription -ClassName __EventFilter -ErrorAction SilentlyContinue |
Where-Object {
$_.Query -match 'Win32_ProcessStartTrace|__InstanceCreationEvent.*Win32_Process'
}
foreach ($filter in $filters) {
# Check if filter is bound to a consumer that blocks processes
$bindings = Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding -ErrorAction SilentlyContinue |
Where-Object { $_.Filter -like "*$($filter.Name)*" }
if ($bindings) {
foreach ($binding in $bindings) {
$consumer = Get-CimInstance -Namespace root\subscription -ClassName CommandLineEventConsumer -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "*$($binding.Consumer)*" }
if ($consumer) {
# Check if consumer command blocks process creation
if ($consumer.CommandLineTemplate -match 'taskkill|Stop-Process|Remove-Process' -or
$consumer.CommandLineTemplate -match 'block|deny|prevent') {
$detections += @{
FilterName = $filter.Name
ConsumerName = $consumer.Name
CommandLine = $consumer.CommandLineTemplate
Type = "WMI Process Creation Blocker Detected"
Risk = "High"
}
}
}
}
}
}
} catch { }
# Check Event Log for process creation failures
try {
$events = Get-WinEvent -FilterHashtable @{LogName='System'; Id=7034} -ErrorAction SilentlyContinue -MaxEvents 100 |
Where-Object {
$_.Message -match 'process.*failed|service.*failed.*start|start.*failed'
}
$processFailures = $events | Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(1)
}
if ($processFailures.Count -gt 10) {
$detections += @{
EventCount = $processFailures.Count
Type = "Excessive Process Creation Failures"
Risk = "Medium"
}
}
} catch { }
# Check for processes with unusual creation patterns
try {
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ParentProcessId, CreationDate, ExecutablePath
# Check for processes spawned in rapid succession
$recentProcs = $processes | Where-Object {
(Get-Date) - $_.CreationDate -lt [TimeSpan]::FromMinutes(5)
}
# Group by parent process
$parentGroups = $recentProcs | Group-Object ParentProcessId
foreach ($group in $parentGroups) {
if ($group.Count -gt 20) {
try {
$parent = Get-CimInstance Win32_Process -Filter "ProcessId=$($group.Name)" -ErrorAction SilentlyContinue
if ($parent) {
$detections += @{
ParentProcessId = $group.Name
ParentProcessName = $parent.Name
ChildCount = $group.Count
Type = "Rapid Process Creation Spawning"
Risk = "Medium"
}
}
} catch { }
}
}
} catch { }
# Check for processes with unusual parent relationships
try {
$suspiciousParents = @{
"winlogon.exe" = @("cmd.exe", "powershell.exe", "wmic.exe")
"services.exe" = @("cmd.exe", "powershell.exe", "rundll32.exe")
"explorer.exe" = @("notepad.exe", "calc.exe")
}
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ParentProcessId
foreach ($proc in $processes) {
if ($proc.ParentProcessId) {
try {
$parent = Get-CimInstance Win32_Process -Filter "ProcessId=$($proc.ParentProcessId)" -ErrorAction SilentlyContinue
if ($parent) {
foreach ($suspParent in $suspiciousParents.Keys) {
if ($parent.Name -eq $suspParent -and
$proc.Name -in $suspiciousParents[$suspParent]) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
ParentProcessId = $proc.ParentProcessId
ParentProcessName = $parent.Name
Type = "Suspicious Parent-Child Process Relationship"
Risk = "Medium"
}
}
}
}
} catch { }
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2035 `
-Message "PROCESS CREATION: $($detection.Type) - $($detection.ProcessName -or $detection.FilterName -or $detection.ParentProcessName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\ProcessCreation_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.FilterName)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) process creation anomalies"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-ProcessCreationDetection
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- QuarantineManagement ---
# Quarantine Management Module
# Manages file quarantine operations and tracks quarantined files
$ModuleName = "QuarantineManagement"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 300 }
$QuarantinePath = "$env:ProgramData\Antivirus\Quarantine"
function Initialize-Quarantine {
try {
if (-not (Test-Path $QuarantinePath)) {
New-Item -Path $QuarantinePath -ItemType Directory -Force | Out-Null
}
# Create quarantine log
$quarantineLog = Join-Path $QuarantinePath "quarantine_log.txt"
if (-not (Test-Path $quarantineLog)) {
"Timestamp|FilePath|QuarantinePath|Reason|FileHash" | Add-Content -Path $quarantineLog
}
return $true
} catch {
Write-Output "ERROR:$ModuleName`:Failed to initialize quarantine: $_"
return $false
}
}
function Invoke-QuarantineFile {
param(
[string]$FilePath,
[string]$Reason = "Threat Detected",
[string]$Source = "Unknown"
)
try {
if (-not (Test-Path $FilePath)) {
Write-Output "ERROR:$ModuleName`:File not found: $FilePath"
return $false
}
$fileName = Split-Path -Leaf $FilePath
$fileDir = Split-Path -Parent $FilePath
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
$fileExt = [System.IO.Path]::GetExtension($fileName)
# Generate unique quarantine filename with timestamp
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$quarantineFileName = "${fileBaseName}_${timestamp}${fileExt}"
$quarantineFilePath = Join-Path $QuarantinePath $quarantineFileName
# Calculate file hash before moving
$fileHash = $null
try {
$hashObj = Get-FileHash -Path $FilePath -Algorithm SHA256 -ErrorAction SilentlyContinue
$fileHash = $hashObj.Hash
} catch { }
# Move file to quarantine
Move-Item -Path $FilePath -Destination $quarantineFilePath -Force -ErrorAction Stop
# Log quarantine action
$quarantineLog = Join-Path $QuarantinePath "quarantine_log.txt"
$logEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$FilePath|$quarantineFilePath|$Reason|$fileHash"
Add-Content -Path $quarantineLog -Value $logEntry
# Write Event Log
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2034 `
-Message "QUARANTINE: File quarantined: $fileName - Reason: $Reason - Source: $Source"
Write-Output "STATS:$ModuleName`:File quarantined: $fileName"
return $true
} catch {
Write-Output "ERROR:$ModuleName`:Failed to quarantine $FilePath`: $_"
return $false
}
}
function Invoke-QuarantineManagement {
$stats = @{
QuarantinedFiles = 0
QuarantineSize = 0
OldFiles = 0
}
try {
# Initialize quarantine if needed
if (-not (Test-Path $QuarantinePath)) {
Initialize-Quarantine
}
# Check quarantine status
if (Test-Path $QuarantinePath) {
$quarantinedFiles = Get-ChildItem -Path $QuarantinePath -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne "quarantine_log.txt" }
$stats.QuarantinedFiles = $quarantinedFiles.Count
# Calculate total quarantine size
foreach ($file in $quarantinedFiles) {
$stats.QuarantineSize += $file.Length
}
# Check for old quarantined files (older than 90 days)
$cutoffDate = (Get-Date).AddDays(-90)
$oldFiles = $quarantinedFiles | Where-Object { $_.LastWriteTime -lt $cutoffDate }
$stats.OldFiles = $oldFiles.Count
# Optionally remove old files
if ($stats.OldFiles -gt 0 -and $stats.QuarantineSize -gt 1GB) {
foreach ($oldFile in $oldFiles) {
try {
Remove-Item -Path $oldFile.FullName -Force -ErrorAction SilentlyContinue
Write-Output "STATS:$ModuleName`:Removed old quarantined file: $($oldFile.Name)"
} catch { }
}
}
}
# Check quarantine log integrity
$quarantineLog = Join-Path $QuarantinePath "quarantine_log.txt"
if (Test-Path $quarantineLog) {
$logEntries = Get-Content -Path $quarantineLog -ErrorAction SilentlyContinue
if ($logEntries.Count -gt 10000) {
# Archive old log entries
$archivePath = Join-Path $QuarantinePath "quarantine_log_$(Get-Date -Format 'yyyy-MM-dd').txt"
Copy-Item -Path $quarantineLog -Destination $archivePath -ErrorAction SilentlyContinue
"Timestamp|FilePath|QuarantinePath|Reason|FileHash" | Set-Content -Path $quarantineLog
}
}
Write-Output "STATS:$ModuleName`:Quarantined=$($stats.QuarantinedFiles), Size=$([Math]::Round($stats.QuarantineSize/1MB, 2))MB, Old=$($stats.OldFiles)"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $stats
}
function Start-Module {
Initialize-Quarantine
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$stats = Invoke-QuarantineManagement
$LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# Export quarantine function for use by other modules
if ($ModuleConfig) {
$ModuleConfig.QuarantineFunction = ${function:Invoke-QuarantineFile}
}
# --- PrivacyForgeSpoofing ---
# PrivacyForge Spoofing Module
# Identity and Fingerprint Spoofing - Generates fake identity data to prevent browser fingerprinting
#Requires -Version 5.1
$ModuleName = "PrivacyForgeSpoofing"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { (Get-TickInterval $ModuleName) }
# PrivacyForge Configuration
$Script:Config = @{
BaseDir = "$env:ProgramData\Antivirus"
LogDir = "$env:ProgramData\Antivirus\Logs"
RotationIntervalSeconds = 3600 # 1 hour
DataThreshold = 50 # Rotate after this much simulated data collection
EnableClipboardSpoofing = $false # Disabled by default - can overwrite user clipboard
EnableNetworkSpoofing = $true
DebugMode = $false
}
# State variables
$Script:PrivacyForgeIdentity = @{}
$Script:PrivacyForgeDataCollected = 0
$Script:PrivacyForgeLastRotation = $null
# ===================== Logging =====================
# Uses standard Write-Log from imported modules
# ===================== Identity Generation =====================
function Invoke-PrivacyForgeGenerateIdentity {
<#
.SYNOPSIS
Generates a fake identity profile for spoofing purposes
#>
$firstNames = @("John", "Jane", "Michael", "Sarah", "David", "Emily", "James", "Jessica",
"Robert", "Amanda", "William", "Ashley", "Richard", "Melissa", "Joseph", "Nicole",
"Thomas", "Jennifer", "Charles", "Elizabeth", "Christopher", "Samantha", "Daniel", "Rebecca")
$lastNames = @("Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Rodriguez", "Martinez", "Hernandez", "Lopez", "Wilson", "Anderson", "Thomas", "Taylor",
"Moore", "Jackson", "Martin", "Lee", "Thompson", "White", "Harris", "Clark")
$domains = @("gmail.com", "yahoo.com", "outlook.com", "hotmail.com", "protonmail.com", "icloud.com", "aol.com")
$cities = @("New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia",
"San Antonio", "San Diego", "Dallas", "San Jose", "Austin", "Jacksonville",
"Fort Worth", "Columbus", "Charlotte", "Seattle", "Denver", "Boston")
$countries = @("United States", "Canada", "United Kingdom", "Australia", "Germany", "France", "Spain", "Italy", "Netherlands", "Sweden")
$languages = @("en-US", "en-GB", "fr-FR", "es-ES", "de-DE", "it-IT", "pt-BR", "nl-NL", "sv-SE")
$interests = @("tech", "gaming", "news", "sports", "music", "movies", "travel", "food",
"fitness", "books", "photography", "cooking", "art", "science", "finance")
$firstName = Get-Random -InputObject $firstNames
$lastName = Get-Random -InputObject $lastNames
$username = "$firstName$lastName" + (Get-Random -Minimum 100 -Maximum 9999)
$domain = Get-Random -InputObject $domains
$email = "$username@$domain".ToLower()
$userAgents = @(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
$resolutions = @("1920x1080", "2560x1440", "1366x768", "1536x864", "1440x900", "1280x720", "3840x2160")
return @{
"name" = "$firstName $lastName"
"email" = $email
"username" = $username
"location" = Get-Random -InputObject $cities
"country" = Get-Random -InputObject $countries
"user_agent" = Get-Random -InputObject $userAgents
"screen_resolution" = Get-Random -InputObject $resolutions
"interests" = (Get-Random -InputObject $interests -Count 4)
"device_id" = [System.Guid]::NewGuid().ToString()
"mac_address" = "{0:X2}-{1:X2}-{2:X2}-{3:X2}-{4:X2}-{5:X2}" -f `
(Get-Random -Minimum 0 -Maximum 256), (Get-Random -Minimum 0 -Maximum 256), `
(Get-Random -Minimum 0 -Maximum 256), (Get-Random -Minimum 0 -Maximum 256), `
(Get-Random -Minimum 0 -Maximum 256), (Get-Random -Minimum 0 -Maximum 256)
"language" = Get-Random -InputObject $languages
"timezone" = (Get-TimeZone).Id
"timestamp" = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
"canvas_hash" = [System.Guid]::NewGuid().ToString("N").Substring(0, 16)
"webgl_vendor" = Get-Random -InputObject @("Google Inc.", "Intel Inc.", "NVIDIA Corporation", "AMD")
"webgl_renderer" = Get-Random -InputObject @("ANGLE (Intel, Intel(R) UHD Graphics 630)", "ANGLE (NVIDIA, GeForce GTX 1080)", "ANGLE (AMD, Radeon RX 580)")
"platform" = Get-Random -InputObject @("Win32", "Win64", "MacIntel", "Linux x86_64")
"cpu_cores" = Get-Random -InputObject @(4, 6, 8, 12, 16)
"memory_gb" = Get-Random -InputObject @(8, 16, 32, 64)
}
}
function Invoke-PrivacyForgeRotateIdentity {
<#
.SYNOPSIS
Rotates to a new fake identity
#>
$Script:PrivacyForgeIdentity = Invoke-PrivacyForgeGenerateIdentity
$Script:PrivacyForgeDataCollected = 0
$Script:PrivacyForgeLastRotation = Get-Date
Write-Log "INFO" "Identity rotated - Name: $($Script:PrivacyForgeIdentity.name), Username: $($Script:PrivacyForgeIdentity.username)"
# Output identity rotation in standard format
Write-Output "SPOOF:$ModuleName`:Identity rotated - $($Script:PrivacyForgeIdentity.name)"
}
# ===================== Spoofing Functions =====================
function Invoke-PrivacyForgeSpoofSoftwareMetadata {
<#
.SYNOPSIS
Sends spoofed HTTP headers to confuse trackers
#>
if (-not $Script:Config.EnableNetworkSpoofing) { return }
try {
$headers = @{
"User-Agent" = $Script:PrivacyForgeIdentity.user_agent
"Cookie" = "session_id=$(Get-Random -Minimum 1000 -Maximum 9999); fake_id=$([System.Guid]::NewGuid().ToString())"
"X-Device-ID" = $Script:PrivacyForgeIdentity.device_id
"Accept-Language" = $Script:PrivacyForgeIdentity.language
"X-Timezone" = $Script:PrivacyForgeIdentity.timezone
"X-Screen-Resolution" = $Script:PrivacyForgeIdentity.screen_resolution
}
# Send to a test endpoint (non-blocking, fire and forget)
$null = Start-Job -ScriptBlock {
param($headers)
try {
Invoke-WebRequest -Uri "https://httpbin.org/headers" -Headers $headers -TimeoutSec 5 -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null
} catch {}
} -ArgumentList $headers
Write-Log "DEBUG" "Sent spoofed software metadata headers"
} catch {
Write-Log "WARN" "Error spoofing software metadata - $_"
}
}
function Invoke-PrivacyForgeSpoofGameTelemetry {
<#
.SYNOPSIS
Generates fake game telemetry data
#>
try {
$fakeTelemetry = @{
"player_id" = [System.Guid]::NewGuid().ToString()
"hardware_id" = -join ((1..32) | ForEach-Object { '{0:X}' -f (Get-Random -Maximum 16) })
"latency" = Get-Random -Minimum 20 -Maximum 200
"game_version" = "$(Get-Random -Minimum 1 -Maximum 5).$(Get-Random -Minimum 0 -Maximum 9).$(Get-Random -Minimum 0 -Maximum 99)"
"fps" = Get-Random -Minimum 30 -Maximum 144
"session_id" = [System.Guid]::NewGuid().ToString()
"playtime_minutes" = Get-Random -Minimum 10 -Maximum 500
}
Write-Log "DEBUG" "Spoofed game telemetry - Player ID: $($fakeTelemetry.player_id)"
} catch {
Write-Log "WARN" "Error spoofing game telemetry - $_"
}
}
function Invoke-PrivacyForgeSpoofSensors {
<#
.SYNOPSIS
Generates random sensor data to confuse fingerprinting
#>
try {
# Generate sensor data for fingerprinting confusion
# Data is generated but not used - the purpose is to create noise for trackers
[math]::Round((Get-Random -Minimum -1000 -Maximum 1000) / 100.0, 2) | Out-Null # accelerometer
[math]::Round((Get-Random -Minimum -18000 -Maximum 18000) / 100.0, 2) | Out-Null # gyroscope
[math]::Round((Get-Random -Minimum -5000 -Maximum 5000) / 100.0, 2) | Out-Null # magnetometer
Get-Random -Minimum 0 -Maximum 1000 | Out-Null # light sensor
Get-Random -InputObject @(0, 5, 10) | Out-Null # proximity sensor
[math]::Round((Get-Random -Minimum 1500 -Maximum 3500) / 100.0, 1) | Out-Null # ambient temp
[math]::Round((Get-Random -Minimum 2000 -Maximum 4000) / 100.0, 1) | Out-Null # battery temp
Write-Log "DEBUG" "Spoofed sensor data"
} catch {
Write-Log "WARN" "Error spoofing sensors - $_"
}
}
function Invoke-PrivacyForgeSpoofSystemMetrics {
<#
.SYNOPSIS
Generates fake system performance metrics
#>
try {
$fakeMetrics = @{
"cpu_usage" = [math]::Round((Get-Random -Minimum 500 -Maximum 5000) / 100.0, 1)
"memory_usage" = [math]::Round((Get-Random -Minimum 3000 -Maximum 8500) / 100.0, 1)
"battery_level" = Get-Random -Minimum 20 -Maximum 100
"charging" = (Get-Random -InputObject @($true, $false))
"disk_usage" = [math]::Round((Get-Random -Minimum 2000 -Maximum 9000) / 100.0, 1)
"network_latency" = Get-Random -Minimum 5 -Maximum 150
"uptime_hours" = Get-Random -Minimum 1 -Maximum 720
}
Write-Log "DEBUG" "Spoofed system metrics - CPU: $($fakeMetrics.cpu_usage)%, Memory: $($fakeMetrics.memory_usage)%"
} catch {
Write-Log "WARN" "Error spoofing system metrics - $_"
}
}
function Invoke-PrivacyForgeSpoofClipboard {
<#
.SYNOPSIS
Overwrites clipboard with fake data (disabled by default)
#>
if (-not $Script:Config.EnableClipboardSpoofing) { return }
try {
$fakeContent = "PrivacyForge: $(Get-Random -Minimum 100000 -Maximum 999999) - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Set-Clipboard -Value $fakeContent -ErrorAction SilentlyContinue
Write-Log "DEBUG" "Spoofed clipboard content"
} catch {
Write-Log "WARN" "Error spoofing clipboard - $_"
}
}
# ===================== Main Detection Function =====================
function Invoke-PrivacyForgeSpoofing {
<#
.SYNOPSIS
Main spoofing tick function - manages identity rotation and spoofing operations
#>
# Initialize on first run
if (-not $Script:PrivacyForgeLastRotation) {
$Script:PrivacyForgeLastRotation = Get-Date
Invoke-PrivacyForgeRotateIdentity
}
try {
# Check if rotation is needed
$timeSinceRotation = (Get-Date) - $Script:PrivacyForgeLastRotation
$shouldRotate = $false
if ($timeSinceRotation.TotalSeconds -ge $Script:Config.RotationIntervalSeconds) {
$shouldRotate = $true
Write-Log "INFO" "Time-based rotation triggered"
}
if ($Script:PrivacyForgeDataCollected -ge $Script:Config.DataThreshold) {
$shouldRotate = $true
Write-Log "INFO" "Data threshold reached ($Script:PrivacyForgeDataCollected/$($Script:Config.DataThreshold))"
}
if ($shouldRotate -or (-not $Script:PrivacyForgeIdentity.ContainsKey("name"))) {
Invoke-PrivacyForgeRotateIdentity
}
# Simulate data collection increment
$Script:PrivacyForgeDataCollected += Get-Random -Minimum 1 -Maximum 6
# Perform spoofing operations
Invoke-PrivacyForgeSpoofSoftwareMetadata
Invoke-PrivacyForgeSpoofGameTelemetry
Invoke-PrivacyForgeSpoofSensors
Invoke-PrivacyForgeSpoofSystemMetrics
Invoke-PrivacyForgeSpoofClipboard
Write-Log "INFO" "Spoofing active - Data collected: $Script:PrivacyForgeDataCollected/$($Script:Config.DataThreshold)"
# Output spoofing activity in standard format
Write-Output "SPOOF:$ModuleName`:Identity rotation active - Data: $Script:PrivacyForgeDataCollected/$($Script:Config.DataThreshold)"
} catch {
Write-Log "ERROR" "Error in main spoofing loop - $_"
}
}
# ===================== Module Start Function =====================
function Start-Module {
Write-Log "INFO" "========================================"
Write-Log "INFO" "PrivacyForge Spoofing Module Starting"
Write-Log "INFO" "Tick Interval: $TickInterval seconds"
Write-Log "INFO" "Rotation Interval: $($Script:Config.RotationIntervalSeconds)s"
Write-Log "INFO" "Data Threshold: $($Script:Config.DataThreshold)"
Write-Log "INFO" "Clipboard Spoofing: $($Script:Config.EnableClipboardSpoofing)"
Write-Log "INFO" "Network Spoofing: $($Script:Config.EnableNetworkSpoofing)"
Write-Log "INFO" "========================================"
# Initial identity generation
Invoke-PrivacyForgeRotateIdentity
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-PrivacyForgeSpoofing
}
Start-Sleep -Seconds (Get-LoopSleep)
} catch {
Write-Output "ERROR:$ModuleName`':$_"
Start-Sleep -Seconds 10
}
}
}
# Start module if not running with configuration
# --- PasswordManagement ---
# Password Management Module
# Monitors password policies and storage
$ModuleName = "PasswordManagement"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 300 }
function Invoke-PasswordManagement {
$detections = @()
try {
# Check password policy
try {
$passwordPolicy = Get-LocalUser | ForEach-Object {
$_.PasswordNeverExpires
}
$expiredPasswords = $passwordPolicy | Where-Object { $_ -eq $true }
if ($expiredPasswords.Count -gt 0) {
$detections += @{
ExpiredPasswordCount = $expiredPasswords.Count
Type = "Accounts with Non-Expiring Passwords"
Risk = "Medium"
}
}
} catch { }
# Check for weak passwords (indirectly through failed logon attempts)
try {
$securityEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -ErrorAction SilentlyContinue -MaxEvents 100
$failedLogons = $securityEvents | Where-Object {
(Get-Date) - $_.TimeCreated -lt [TimeSpan]::FromHours(24)
}
if ($failedLogons.Count -gt 50) {
$detections += @{
FailedLogonCount = $failedLogons.Count
Type = "Excessive Failed Logon Attempts - Possible Weak Passwords"
Risk = "Medium"
}
}
} catch { }
# Check for password storage in plain text
try {
$searchPaths = @(
"$env:USERPROFILE\Desktop",
"$env:USERPROFILE\Documents",
"$env:USERPROFILE\Downloads",
"$env:TEMP"
)
$passwordFiles = @()
foreach ($path in $searchPaths) {
if (Test-Path $path) {
try {
$files = Get-ChildItem -Path $path -Filter "*.txt","*.log","*.csv" -File -ErrorAction SilentlyContinue |
Select-Object -First 50
foreach ($file in $files) {
try {
$content = Get-Content $file.FullName -Raw -ErrorAction SilentlyContinue
if ($content -match '(?i)(password|pwd|passwd)\s*[:=]\s*\S+') {
$passwordFiles += @{
File = $file.FullName
Type = "Plain Text Password Storage"
Risk = "High"
}
}
} catch { }
}
} catch { }
}
}
$detections += $passwordFiles
} catch { }
# Check for credential dumping tools
try {
$credDumpTools = @("mimikatz", "lsadump", "pwdump", "fgdump", "hashdump")
$processes = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $processes) {
foreach ($tool in $credDumpTools) {
if ($proc.ProcessName -like "*$tool*" -or
$proc.Path -like "*$tool*") {
$detections += @{
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Tool = $tool
Type = "Credential Dumping Tool"
Risk = "Critical"
}
}
}
}
} catch { }
# Check for SAM database access
try {
$securityEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4656,4663} -ErrorAction SilentlyContinue -MaxEvents 500 |
Where-Object { $_.Message -match 'SAM|SECURITY|SYSTEM' -and $_.Message -match 'Read|Write' }
if ($securityEvents.Count -gt 0) {
foreach ($event in $securityEvents) {
$detections += @{
EventId = $event.Id
TimeCreated = $event.TimeCreated
Message = $event.Message
Type = "SAM Database Access Attempt"
Risk = "High"
}
}
}
} catch { }
# Check for LSA secrets access
try {
$lsaEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4656} -ErrorAction SilentlyContinue -MaxEvents 200 |
Where-Object { $_.Message -match 'LSA|Local.*Security.*Authority' }
if ($lsaEvents.Count -gt 5) {
$detections += @{
EventCount = $lsaEvents.Count
Type = "Excessive LSA Access Attempts"
Risk = "High"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2030 `
-Message "PASSWORD MANAGEMENT: $($detection.Type) - $($detection.ProcessName -or $detection.File -or $detection.Tool)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\PasswordManagement_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.File)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) password management issues"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-PasswordManagement
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- IdsDetection ---
# IDS Detection
# Command-line IDS patterns (meterpreter, certutil -urlcache, etc.)
$ModuleName = "IdsDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
$Patterns = @(
@{ Pattern = "meterpreter"; Desc = "Metasploit meterpreter" }
@{ Pattern = "certutil\s+-urlcache"; Desc = "Certutil download" }
@{ Pattern = "bitsadmin\s+/transfer"; Desc = "Bitsadmin download" }
@{ Pattern = "powershell\s+-enc"; Desc = "Base64 encoded PS" }
@{ Pattern = "powershell\s+-w\s+hidden"; Desc = "Hidden window PS" }
@{ Pattern = "invoke-expression"; Desc = "IEX usage" }
@{ Pattern = "iex\s*\("; Desc = "IEX usage" }
@{ Pattern = "downloadstring"; Desc = "DownloadString" }
@{ Pattern = "downloadfile"; Desc = "DownloadFile" }
@{ Pattern = "webclient"; Desc = "WebClient" }
@{ Pattern = "net\.webclient"; Desc = "Net.WebClient" }
@{ Pattern = "bypass.*-executionpolicy"; Desc = "Execution policy bypass" }
@{ Pattern = "wmic\s+process\s+call\s+create"; Desc = "WMI process creation" }
@{ Pattern = "reg\s+add.*HKLM.*Run"; Desc = "Registry Run key" }
@{ Pattern = "schtasks\s+/create"; Desc = "Scheduled task creation" }
@{ Pattern = "netsh\s+firewall"; Desc = "Firewall modification" }
@{ Pattern = "sc\s+create"; Desc = "Service creation" }
@{ Pattern = "rundll32.*\.dll"; Desc = "Rundll32 DLL" }
@{ Pattern = "regsvr32\s+.*/s"; Desc = "Regsvr32 silent" }
@{ Pattern = "mshta\s+http"; Desc = "Mshta remote script" }
)
function Invoke-IdsScan {
$detections = @()
try {
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Select-Object ProcessId, Name, CommandLine
foreach ($proc in $processes) {
$cmd = if ($proc.CommandLine) { $proc.CommandLine } else { "" }
foreach ($p in $Patterns) {
if ($cmd -match $p.Pattern) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
Pattern = $p.Pattern
Description = $p.Desc
CommandLine = $cmd.Substring(0, [Math]::Min(500, $cmd.Length))
}
break
}
}
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2095 -Message "IDS: $($d.Description) - $($d.ProcessName) PID:$($d.ProcessId)" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\ids_detection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object { "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Description)|$($_.ProcessName)|PID:$($_.ProcessId)" | Add-Content -Path $logPath }
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) IDS matches"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-IdsScan
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- CredentialDumpDetection ---
# Credential Dumping Detection Module
# Detects attempts to dump credentials from memory
$ModuleName = "CredentialDumpDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 20 }
$CredentialDumpTools = @(
"mimikatz", "sekurlsa", "lsadump", "wce", "fgdump",
"pwdump", "hashdump", "gsecdump", "cachedump",
"procDump", "dumpert", "nanodump", "mslsa"
)
function Invoke-CredentialDumpScan {
$detections = @()
try {
# Check running processes
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, CommandLine, ExecutablePath
foreach ($proc in $processes) {
$procNameLower = $proc.Name.ToLower()
$cmdLineLower = if ($proc.CommandLine) { $proc.CommandLine.ToLower() } else { "" }
$pathLower = if ($proc.ExecutablePath) { $proc.ExecutablePath.ToLower() } else { "" }
# Check for credential dumping tools
foreach ($tool in $CredentialDumpTools) {
if ($procNameLower -like "*$tool*" -or
$cmdLineLower -like "*$tool*" -or
$pathLower -like "*$tool*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Tool = $tool
Risk = "Critical"
}
break
}
}
# Check for suspicious LSASS access
if ($proc.Name -match "lsass|sam|security") {
try {
$lsassProc = Get-Process -Name "lsass" -ErrorAction SilentlyContinue
if ($lsassProc) {
# Check if process has handle to LSASS
$handles = Get-CimInstance Win32_ProcessHandle -Filter "ProcessId=$($proc.ProcessId)" -ErrorAction SilentlyContinue
if ($handles) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
Type = "LSASS Memory Access"
Risk = "High"
}
}
}
} catch { }
}
}
# Check for credential dumping API calls in Event Log
try {
$securityEvents = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4656,4663} -ErrorAction SilentlyContinue -MaxEvents 500
foreach ($event in $securityEvents) {
if ($event.Message -match 'lsass|sam|security' -and
$event.Message -match 'Read|Write') {
$xml = [xml]$event.ToXml()
$objectName = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'ObjectName'}).'#text'
if ($objectName -match 'SAM|SECURITY|SYSTEM') {
$detections += @{
EventId = $event.Id
Type = "Registry Credential Access"
ObjectName = $objectName
Risk = "High"
}
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2005 `
-Message "CREDENTIAL DUMP DETECTED: $($detection.ProcessName -or $detection.Type) - $($detection.Tool -or $detection.ObjectName)"
}
$logPath = "$env:ProgramData\Antivirus\Logs\CredentialDump_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ProcessName -or $_.Type)|$($_.Tool -or $_.ObjectName)|$($_.Risk)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) credential dump attempts"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-CredentialDumpScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- LOLBinDetection ---
# Living-Off-The-Land Binary Detection
# Detects legitimate tools used maliciously
$ModuleName = "LOLBinDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 30 }
$LOLBinPatterns = @{
"cmd.exe" = @("\/c.*certutil|powershell|bitsadmin|regsvr32")
"powershell.exe" = @("-nop.*-w.*hidden|-EncodedCommand|-ExecutionPolicy.*Bypass")
"wmic.exe" = @("process.*call.*create")
"mshta.exe" = @("http|https|\.hta")
"rundll32.exe" = @("javascript:|\.dll.*\,")
"regsvr32.exe" = @("\/s.*\/i.*http|\.sct")
"certutil.exe" = @("-urlcache|-decode|\.exe")
"bitsadmin.exe" = @("/transfer|/rawreturn")
"schtasks.exe" = @("/create.*/tn.*http|/create.*/tr.*powershell")
"msbuild.exe" = @("\.xml.*http|\.csproj.*http")
"csc.exe" = @("\.cs.*http")
}
$DetectionCount = 0
function Get-RunningProcesses {
try {
return Get-CimInstance Win32_Process | Select-Object ProcessId, Name, CommandLine, CreationDate, ParentProcessId
} catch {
return @()
}
}
function Test-LOLBinCommandLine {
param(
[string]$ProcessName,
[string]$CommandLine
)
if ([string]::IsNullOrEmpty($CommandLine)) { return $null }
$processNameLower = $ProcessName.ToLower()
$cmdLineLower = $CommandLine.ToLower()
if ($LOLBinPatterns.ContainsKey($processNameLower)) {
foreach ($pattern in $LOLBinPatterns[$processNameLower]) {
if ($cmdLineLower -match $pattern) {
return @{
Process = $ProcessName
CommandLine = $CommandLine
Pattern = $pattern
Risk = "High"
}
}
}
}
# Additional heuristics
if ($cmdLineLower -match 'powershell.*-encodedcommand' -and
$cmdLineLower.Length -gt 500) {
return @{
Process = $ProcessName
CommandLine = $CommandLine
Pattern = "Long encoded PowerShell command"
Risk = "High"
}
}
if ($cmdLineLower -match '(http|https)://[^\s]+' -and
$processNameLower -in @('cmd.exe','rundll32.exe','mshta.exe')) {
return @{
Process = $ProcessName
CommandLine = $CommandLine
Pattern = "Network download from LOLBin"
Risk = "High"
}
}
return $null
}
function Invoke-LOLBinScan {
$detections = @()
$processes = Get-RunningProcesses
foreach ($proc in $processes) {
$result = Test-LOLBinCommandLine -ProcessName $proc.Name -CommandLine $proc.CommandLine
if ($result) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Pattern = $result.Pattern
Risk = $result.Risk
ParentProcessId = $proc.ParentProcessId
CreationDate = $proc.CreationDate
}
}
}
if ($detections.Count -gt 0) {
$script:DetectionCount += $detections.Count
foreach ($detection in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2002 `
-Message "LOLBIN DETECTED: $($detection.ProcessName) (PID: $($detection.ProcessId)) - $($detection.Pattern)"
# Log details
$logPath = "$env:ProgramData\Antivirus\Logs\LOLBinDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|PID:$($detection.ProcessId)|$($detection.ProcessName)|$($detection.Pattern)|$($detection.CommandLine)" |
Add-Content -Path $logPath
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) LOLBin instances"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
$count = Invoke-LOLBinScan
$LastTick = $now
Write-Output "STATS:$ModuleName`:Scanned processes, Detections=$count"
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- MemoryAcquisitionDetection ---
# Memory Acquisition Detection
# Detects WinPmem, pmem, FTK Imager, etc.
$ModuleName = "MemoryAcquisitionDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 90 }
$ProcessPatterns = @("winpmem", "pmem", "osxpmem", "aff4imager", "winpmem_mini", "memdump", "rawdump")
$CmdPatterns = @("winpmem", "pmem", "\.\pmem", "/dev/pmem", ".aff4", "-o .raw", "-o .aff4", "image.raw", "memory.raw", "physical memory", "memory acquisition", "physicalmemory")
function Invoke-MemoryAcquisitionScan {
$detections = @()
try {
$processes = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Select-Object ProcessId, Name, CommandLine, ExecutablePath
foreach ($proc in $processes) {
$name = if ($proc.Name) { $proc.Name.ToLower() } else { "" }
$cmd = (if ($proc.CommandLine) { $proc.CommandLine } else { "" }) + " " + (if ($proc.ExecutablePath) { $proc.ExecutablePath } else { "" })
foreach ($pat in $ProcessPatterns) {
if ($name -like "*$pat*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
Pattern = $pat
Type = "Memory Acquisition Tool"
Risk = "Critical"
}
break
}
}
if ($detections[-1].ProcessId -eq $proc.ProcessId) { continue }
foreach ($pat in $CmdPatterns) {
if ($cmd -like "*$pat*") {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
Pattern = "cmd:$pat"
Type = "Memory Acquisition (cmd)"
Risk = "Critical"
}
break
}
}
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2093 -Message "MEMORY ACQUISITION: $($d.Pattern) - $($d.ProcessName) (PID: $($d.ProcessId))" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\memory_acquisition_detections_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object { "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.ProcessName)|PID:$($_.ProcessId)" | Add-Content -Path $logPath }
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) memory acquisition indicators"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
return $detections.Count
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-MemoryAcquisitionScan | Out-Null
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- MobileDeviceMonitoring ---
# Mobile Device Monitoring
# Win32_PnPEntity for portable/USB/MTP devices
$ModuleName = "MobileDeviceMonitoring"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 90 }
$DeviceClasses = @("USB", "Bluetooth", "Image", "WPD", "PortableDevice", "MTP")
$KnownDevices = @{}
function Invoke-MobileDeviceScan {
$newDevices = @()
try {
$devices = Get-CimInstance Win32_PnPEntity -ErrorAction SilentlyContinue | Where-Object {
foreach ($cls in $DeviceClasses) {
if (($_.PNPClass -eq $cls) -or ($_.Service -like "*$cls*")) {
return $true
}
}
return $false
}
foreach ($d in $devices) {
$key = $d.DeviceID
if (-not $script:KnownDevices.ContainsKey($key)) {
$script:KnownDevices[$key] = $true
$newDevices += @{
DeviceID = $d.DeviceID
Name = $d.Name
PNPClass = $d.PNPClass
Status = $d.Status
}
}
}
if ($newDevices.Count -gt 0) {
$logPath = "$env:ProgramData\Antivirus\Logs\mobile_device_$(Get-Date -Format 'yyyy-MM-dd').log"
foreach ($nd in $newDevices) {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|New device|$($nd.Name)|$($nd.PNPClass)|$($nd.DeviceID)" | Add-Content -Path $logPath
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Information -EventId 2094 -Message "MOBILE DEVICE: $($nd.Name) - $($nd.PNPClass)" -ErrorAction SilentlyContinue
}
Write-Output "STATS:$ModuleName`:Logged $($newDevices.Count) new mobile/USB devices"
}
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $script:LastTick).TotalSeconds -ge $script:TickInterval) {
$script:LastTick = $now
Invoke-MobileDeviceScan
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
$script:EmbeddedCVEMitigationPatcher = $true
# --- CVE-MitigationPatcher ---
<#
.SYNOPSIS
Fetches CISA Known Exploited Vulnerabilities (KEV), detects new CVEs, and applies
scriptable mitigations where configured—without waiting for Windows Update.
.DESCRIPTION
- Pulls CISA KEV catalog (JSON).
- Tracks which CVEs were already seen (state file) so only NEW entries trigger actions.
- For each new CVE, if a scriptable mitigation exists in CVE-MitigationActions.json,
runs it (registry, services, firewall, etc.). Otherwise reports with advisory links.
- Default is -WhatIf (no changes). Use -Apply to actually apply mitigations.
- Use -RegisterSchedule to install to C:\ProgramData\CVE-MitigationPatcher and run hourly as SYSTEM (fire-and-forget). Log: same folder, CVE-MitigationPatcher.log.
.NOTES
Requires PowerShell 5.1+. Run as Administrator to apply service/registry mitigations.
Mitigations are workarounds; install Windows Updates when available.
#>
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$InstallDir = "C:\ProgramData\CVE-MitigationPatcher"
$TaskName = "CVE-MitigationPatcher"
$StatePath = if ($StatePath) { $StatePath } else { Join-Path $ScriptDir "CVE-PatcherState.json" }
$ConfigPath = if ($ConfigPath) { $ConfigPath } else { Join-Path $ScriptDir "CVE-MitigationActions.json" }
$LogPath = if ($LogPath) { $LogPath } else { Join-Path $ScriptDir "CVE-MitigationPatcher.log" }
# --------------- Embedded config (used when no external JSON found - single-file distribution) ---------------
$EmbeddedConfigJson = @'
{"templates":{"Set-Registry-DWord":{"type":"registry","path":"{{path}}","name":"{{name}}","value":"{{value}}","valueType":"DWord"},"Delete-RegistryKey":{"type":"deleteRegistryKey","path":"{{path}}"},"Disable-Service":{"type":"service","name":"{{name}}","startupType":"Disabled"},"Block-Firewall-Port":{"type":"firewall","displayName":"{{displayName}}","port":"{{port}}","protocol":"TCP","direction":"Inbound","action":"Block"}},"defaultActionsForVendorProduct":[{"vendor":"Microsoft","productPattern":"Windows","description":"Generic Windows hardening","run":["Disable-SMBv1","Block-MSDTProtocol","Disable-WebClient"]}],"actions":{"CVE-2017-0143":{"description":"SMBv1 RCE","run":["Disable-SMBv1"]},"CVE-2017-0144":{"description":"EternalBlue","run":["Disable-SMBv1"]},"CVE-2017-0145":{"description":"EternalRomance","run":["Disable-SMBv1"]},"CVE-2017-0146":{"description":"SMBv1 RCE","run":["Disable-SMBv1"]},"CVE-2017-0147":{"description":"SMBv1 info disclosure","run":["Disable-SMBv1"]},"CVE-2017-0148":{"description":"SMBv1 server RCE","run":["Disable-SMBv1"]},"CVE-2020-0796":{"description":"SMBGhost","run":["Disable-SMBv3Compression"]},"CVE-2019-0708":{"description":"BlueKeep","run":["Enable-RDPNLA"]},"CVE-2020-1350":{"description":"SigRed","run":["Mitigate-SigRed"]},"CVE-2022-30190":{"description":"Follina","run":["Block-MSDTProtocol"]},"CVE-2022-34713":{"description":"MSDT RCE","run":["Block-MSDTProtocol"]},"CVE-2021-34527":{"description":"PrintNightmare","run":["Disable-PrintSpooler"]},"CVE-2021-1675":{"description":"PrintNightmare LPE","run":["Disable-PrintSpooler"]},"CVE-2024-38063":{"description":"IPv6 RCE","run":["Disable-IPv6"]},"CVE-2022-26923":{"description":"PetitPotam","run":["Disable-WebClient"]},"CVE-2022-30136":{"description":"NFSv4 RCE","run":["Disable-NFS"]},"CVE-2022-26937":{"description":"NFS RCE","run":["Disable-NFS"]}}}
'@
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Level] $Message"
Add-Content -Path $LogPath -Value $line -ErrorAction SilentlyContinue
if ($Level -eq "ERROR") { Write-Error $Message } else { Write-Host $Message }
}
# --------------- Install self to ProgramData (for fire-and-forget) ---------------
function Install-Self {
if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null }
$scriptName = Split-Path -Leaf $MyInvocation.MyCommand.Path
$destScript = Join-Path $InstallDir $scriptName
$srcScript = $MyInvocation.MyCommand.Path
Copy-Item -Path $srcScript -Destination $destScript -Force
$configName = "CVE-MitigationActions.json"
$srcConfig = Join-Path (Split-Path -Parent $srcScript) $configName
if (Test-Path $srcConfig) { Copy-Item -Path $srcConfig -Destination (Join-Path $InstallDir $configName) -Force }
$mitDir = Join-Path (Split-Path -Parent $srcScript) "Mitigations"
if (Test-Path $mitDir) {
$destMit = Join-Path $InstallDir "Mitigations"
if (-not (Test-Path $destMit)) { New-Item -ItemType Directory -Path $destMit -Force | Out-Null }
Copy-Item -Path "$mitDir\*" -Destination $destMit -Recurse -Force -ErrorAction SilentlyContinue
}
return $destScript
}
# --------------- Task scheduling (handled at script end when run directly) ---------------
$KevUrl = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
# --------------- Built-in mitigation actions (scriptable) ---------------
$MitigationActions = @{
"Disable-SMBv1" = {
# SMBv1 - EternalBlue, EternalChampion, etc. (CVE-2017-0143, 2017-0144, 2017-0145, 2017-0146)
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name "SMB1" -Value 0 -Type DWord -Force
$fs = Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -ErrorAction SilentlyContinue
if ($fs -and $fs.State -eq "Enabled") {
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart -ErrorAction SilentlyContinue
}
}
"Disable-SMBv3Compression" = {
# CVE-2020-0796 SMBGhost - disables SMBv3 compression on server
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name "DisableCompression" -Value 1 -Type DWord -Force
}
"Disable-PrintSpooler" = {
# PrintNightmare and similar (CVE-2021-34527, CVE-2021-1675)
Stop-Service -Name Spooler -Force -ErrorAction SilentlyContinue
Set-Service -Name Spooler -StartupType Disabled
}
"Block-MSDTProtocol" = {
# CVE-2022-30190 Follina, CVE-2022-34713 - removes ms-msdt URL protocol handler
$path = "HKCR:\ms-msdt"
if (Test-Path $path) {
Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue
}
}
"Mitigate-SigRed" = {
# CVE-2020-1350 SigRed - DNS server only; restricts TCP response size
$svc = Get-Service -Name DNS -ErrorAction SilentlyContinue
if (-not $svc) { return }
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\DNS\Parameters"
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name "TcpReceivePacketSize" -Value 0xFF00 -Type DWord -Force
Restart-Service -Name DNS -Force -ErrorAction SilentlyContinue
}
"Enable-RDPNLA" = {
# CVE-2019-0708 BlueKeep - requires NLA before RDP session
$path = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
if (Test-Path $path) {
Set-ItemProperty -Path $path -Name "UserAuthentication" -Value 1 -Type DWord -Force
}
}
"Disable-WebClient" = {
# NTLM relay, WebDAV abuse vectors
Stop-Service -Name WebClient -Force -ErrorAction SilentlyContinue
Set-Service -Name WebClient -StartupType Disabled
}
"Disable-IPv6" = {
# CVE-2024-38063 workaround: disable IPv6 via registry
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name "DisabledComponents" -Value 0xFF -Type DWord -Force
}
"Disable-LLMNR" = {
# NTLM relay / LLMNR poisoning: disable multicast name resolution
$path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient"
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name "EnableMulticast" -Value 0 -Type DWord -Force
}
"Disable-NBT-NS" = {
# NTLM relay / NetBIOS poisoning: disable NetBIOS over TCP/IP on all interfaces
$basePath = "HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters\Interfaces"
if (Test-Path $basePath) {
Get-ChildItem -Path $basePath -ErrorAction SilentlyContinue | ForEach-Object {
Set-ItemProperty -Path $_.PSPath -Name "NetbiosOptions" -Value 2 -Type DWord -Force -ErrorAction SilentlyContinue
}
}
}
"Disable-RemoteRegistry" = {
# Reduces lateral movement; attackers often use Remote Registry for recon
Stop-Service -Name RemoteRegistry -Force -ErrorAction SilentlyContinue
Set-Service -Name RemoteRegistry -StartupType Disabled -ErrorAction SilentlyContinue
}
"Disable-NFS" = {
# CVE-2022-30136: Windows NFSv4 RCE - disable NFS services (Server/Client/Redirector)
Get-Service -Name *Nfs* -ErrorAction SilentlyContinue | ForEach-Object {
Stop-Service -Name $_.Name -Force -ErrorAction SilentlyContinue
Set-Service -Name $_.Name -StartupType Disabled -ErrorAction SilentlyContinue
}
}
}
# --------------- Fetch CISA KEV and parse ---------------
function Get-CisaKevCatalog {
Write-Log "Fetching CISA KEV catalog..."
try {
$r = Invoke-RestMethod -Uri $KevUrl -Method Get -UseBasicParsing
return $r
} catch {
Write-Log "Failed to fetch KEV: $_" "ERROR"
throw
}
}
function Get-RelevantCves {
param($Catalog, [string]$Vendor, [string]$Product)
$list = @($Catalog.vulnerabilities)
if ($Vendor) {
$list = $list | Where-Object { $_.vendorProject -eq $Vendor }
}
if ($Product) {
$list = $list | Where-Object { $_.product -like "*$Product*" }
}
return $list
}
# --------------- State: last seen CVE IDs ---------------
function Get-SeenCveIds {
if (-not (Test-Path $StatePath)) { return @{} }
try {
$o = Get-Content $StatePath -Raw | ConvertFrom-Json
$h = @{}
$o.seenCveIds.PSObject.Properties | ForEach-Object { $h[$_.Name] = $true }
return $h
} catch {
return @{}
}
}
function Save-SeenCveIds {
param([string[]]$CveIds)
$seen = Get-SeenCveIds
foreach ($id in $CveIds) { $seen[$id] = $true }
@{ seenCveIds = $seen; lastUpdate = (Get-Date -Format "o") } | ConvertTo-Json -Depth 3 | Set-Content $StatePath -Encoding UTF8
}
# --------------- Load config: CVE -> actions, templates (file or embedded) ---------------
$script:ConfigDir = $ScriptDir # fallback for embedded config / relative script paths
$script:Templates = @{}
function Get-MitigationConfig {
$cfg = $null
if (Test-Path $ConfigPath) {
try {
$cfg = Get-Content $ConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json
$script:ConfigDir = Split-Path -Parent $ConfigPath
} catch {
Write-Log "Error loading config file: $_" "ERROR"
}
}
if (-not $cfg) {
try {
$cfg = $EmbeddedConfigJson | ConvertFrom-Json
Write-Log "Using embedded config (no file at $ConfigPath)"
$script:ConfigDir = $ScriptDir
} catch {
Write-Log "Error loading embedded config: $_" "ERROR"
return @{ actions = @{}; defaultActionsForVendorProduct = @() }
}
}
$out = @{}
if ($cfg.actions) {
$cfg.actions.PSObject.Properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
}
if ($cfg.templates) {
$script:Templates = @{}
$cfg.templates.PSObject.Properties | ForEach-Object {
$script:Templates[$_.Name] = $_.Value
}
}
$defaults = @()
if ($cfg.defaultActionsForVendorProduct) {
$defaults = @($cfg.defaultActionsForVendorProduct)
}
return @{ actions = $out; defaultActionsForVendorProduct = $defaults }
}
# --------------- Expand template with params ({{key}} -> params.key) ---------------
function Expand-Template {
param($Template, [hashtable]$Params)
if (-not $Template) { return $null }
$json = ($Template | ConvertTo-Json -Depth 10 -Compress)
foreach ($k in $Params.Keys) {
$val = [string]$Params[$k]
$val = $val -replace '\\','\\\\' -replace '"','\"'
$json = $json -replace [regex]::Escape("{{$k}}"), $val
}
try { return $json | ConvertFrom-Json } catch { return $null }
}
# --------------- Execute generic action (registry, service, firewall, deleteRegistryKey, script) ---------------
function Invoke-GenericAction {
param($Action, [string]$CveId)
$type = if ($Action.PSObject.Properties['type']) { $Action.type } else { $null }
if (-not $type) {
Write-Log "Generic action missing 'type' (CVE: $CveId)" "ERROR"
return $false
}
$desc = "$type for $CveId"
if ($ReportOnly) { return $false }
if (-not $Apply) {
Write-Log "[WhatIf] Would apply: $desc"
return $true
}
try {
switch ($type) {
"registry" {
$path = $Action.path
$name = $Action.name
$val = if ($null -ne $Action.value) { $Action.value } else { $Action.Value }
$vtype = if ($Action.valueType) { $Action.valueType } else { "DWord" }
if ($vtype -eq "DWord" -or $vtype -eq "QWord") { $val = [int]$val }
if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null }
Set-ItemProperty -Path $path -Name $name -Value $val -Type $vtype -Force
}
"deleteRegistryKey" {
$path = $Action.path
if (Test-Path $path) { Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue }
}
"service" {
$svcName = $Action.name
$startup = if ($Action.startupType) { $Action.startupType } else { "Disabled" }
Stop-Service -Name $svcName -Force -ErrorAction SilentlyContinue
Set-Service -Name $svcName -StartupType $startup
}
"firewall" {
$dn = $Action.displayName
$port = [int]$Action.port
$proto = if ($Action.protocol) { $Action.protocol } else { "TCP" }
$dir = if ($Action.direction) { $Action.direction } else { "Inbound" }
$act = if ($Action.action) { $Action.action } else { "Block" }
$rule = Get-NetFirewallRule -DisplayName $dn -ErrorAction SilentlyContinue
if (-not $rule) {
New-NetFirewallRule -DisplayName $dn -Direction $dir -Protocol $proto -LocalPort $port -Action $act -ErrorAction Stop
}
}
"script" {
$relPath = $Action.path
$fullPath = if ([System.IO.Path]::IsPathRooted($relPath)) { $relPath } else { Join-Path $script:ConfigDir $relPath }
$args = if ($Action.arguments) { @($Action.arguments) } else { @() }
if (-not (Test-Path $fullPath)) { Write-Log "Script not found: $fullPath" "ERROR"; return $false }
& $fullPath @args
}
default { Write-Log "Unknown generic action type: $type" "ERROR"; return $false }
}
Write-Log "Applied mitigation: $desc"
return $true
} catch {
Write-Log "Failed to apply $desc : $_" "ERROR"
return $false
}
}
# --------------- Resolve run entry: string (named action) or object (generic/template/script) ---------------
function Invoke-RunEntry {
param($Entry, [string]$CveId)
if ($Entry -is [string]) {
if (-not $MitigationActions[$Entry]) {
Write-Log "Unknown named action: $Entry (CVE: $CveId)" "ERROR"
return $false
}
if ($ReportOnly) { return $false }
if ($Apply) {
try {
& $MitigationActions[$Entry]
Write-Log "Applied mitigation: $Entry for $CveId"
return $true
} catch {
Write-Log "Failed to apply $Entry for $CveId : $_" "ERROR"
return $false
}
} else {
Write-Log "[WhatIf] Would apply: $Entry for $CveId"
return $true
}
}
if ($Entry.PSObject.Properties['template']) {
$tplName = $Entry.template
$params = @{}
if ($Entry.params) {
$Entry.params.PSObject.Properties | ForEach-Object { $params[$_.Name] = $_.Value }
}
$tpl = $script:Templates[$tplName]
if (-not $tpl) { Write-Log "Template not found: $tplName (CVE: $CveId)" "ERROR"; return $false }
$expanded = Expand-Template -Template $tpl -Params $params
if ($expanded) { return Invoke-GenericAction -Action $expanded -CveId $CveId }
return $false
}
if ($Entry.PSObject.Properties['type']) {
return Invoke-GenericAction -Action $Entry -CveId $CveId
}
Write-Log "Invalid run entry (CVE: $CveId)" "ERROR"
return $false
}
# --------------- Apply one mitigation by name (legacy) ---------------
function Invoke-MitigationAction {
param([string]$ActionName, [string]$CveId)
return Invoke-RunEntry -Entry $ActionName -CveId $CveId
}
# --------------- Main entry (used by scheduler in single-file build and by script when run directly) ---------------
function Invoke-CVEMitigationPatcher {
$script:Apply = $Apply.IsPresent -and -not $ReportOnly.IsPresent
$script:ReportOnly = $ReportOnly.IsPresent
try {
$mode = if ($script:ReportOnly) { "ReportOnly" } elseif ($script:Apply) { "Apply" } else { "WhatIf" }
$filter = if ($FilterVendor) { "Vendor=$FilterVendor" } elseif ($FilterProduct) { "Product=$FilterProduct" } else { "All" }
Write-Log "Run started. Mode=$mode Filter=$filter"
$catalog = Get-CisaKevCatalog
$cves = Get-RelevantCves -Catalog $catalog -Vendor $FilterVendor -Product $FilterProduct
if (-not $FilterVendor -and -not $FilterProduct) {
$cves = @($catalog.vulnerabilities)
}
Write-Log "Relevant CVEs in KEV: $($cves.Count)"
$seen = Get-SeenCveIds
$config = Get-MitigationConfig
$newCves = if ($Reapply.IsPresent) { @($cves) } else { @($cves | Where-Object { -not $seen[$_.cveID] }) }
$newIds = @($newCves | ForEach-Object { $_.cveID })
if ($newIds.Count -eq 0) {
Write-Log "No CVEs to process. (Use -Reapply to process previously seen CVEs with updated config.)"
return
}
if ($Reapply.IsPresent) {
Write-Log "Reapply mode: processing all $($newIds.Count) relevant CVEs (including previously seen)"
} else {
Write-Log "New CVEs (since last run): $($newIds.Count)"
}
foreach ($cve in $newCves) {
$id = $cve.cveID
$vendor = $cve.vendorProject
$product = $cve.product
$name = $cve.vulnerabilityName
$notes = $cve.notes
$link = ($notes -split ' ' | Where-Object { $_ -match '^https?://' } | Select-Object -First 1) -replace '[,;]$',''
Write-Log " $id | $vendor | $product | $name"
Write-Log " Link: $link"
$actionConfig = $config.actions[$id]
$runList = $null
if ($actionConfig -and $actionConfig.run) {
$runList = @($actionConfig.run)
} elseif ($config.defaultActionsForVendorProduct -and $config.defaultActionsForVendorProduct.Count -gt 0) {
$matched = $config.defaultActionsForVendorProduct | Where-Object {
$v = $_.vendor; $p = $_.productPattern
(-not $v -or $vendor -eq $v) -and (-not $p -or $product -like "*$p*")
} | Select-Object -First 1
if ($matched -and $matched.run) {
$runList = @($matched.run)
Write-Log " Using vendor/product defaults for $vendor | $product"
}
}
if ($runList) {
foreach ($entry in $runList) {
$null = Invoke-RunEntry -Entry $entry -CveId $id
}
} else {
Write-Log " No scriptable mitigation in config. Apply workarounds from the link above."
}
}
if (-not $Reapply.IsPresent) { Save-SeenCveIds -CveIds $newIds }
Write-Log "State updated. Done."
} catch {
Write-Log "Script failed: $_" "ERROR"
throw
}
}
# --------------- Script entry (when run as .\CVE-MitigationPatcher.ps1 or via CVE-MitigationPatcherJob.ps1) ---------------
if (-not $script:EmbeddedCVEMitigationPatcher) {
if ($UnregisterSchedule) {
$t = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($t) {
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-Host "Scheduled task '$TaskName' removed. To remove files: delete $InstallDir"
} else {
Write-Host "Scheduled task '$TaskName' not found."
}
exit 0
}
if ($RegisterSchedule) {
$installedScript = Install-Self
$workDir = $InstallDir
$argList = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$installedScript`""
if ($ScheduleApply) { $argList += " -Apply" } else { $argList += " -ReportOnly" }
if ($FilterVendor) { $argList += " -FilterVendor `"$FilterVendor`"" }
if ($FilterProduct) { $argList += " -FilterProduct `"$FilterProduct`"" }
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $argList -WorkingDirectory $workDir
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 1) -RepetitionDuration ([TimeSpan]::MaxValue)
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
$principal = if ($ScheduleApply) {
New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
} else {
New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount
}
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
$params = @{ TaskName = $TaskName; Action = $action; Trigger = $trigger; Settings = $settings; Principal = $principal }
if ($task) { Set-ScheduledTask @params } else { Register-ScheduledTask @params }
Write-Host "Fire-and-forget: task '$TaskName' runs every hour as SYSTEM. Log: $InstallDir\CVE-MitigationPatcher.log"
exit 0
}
try {
Invoke-CVEMitigationPatcher -Apply:$Apply -ReportOnly:$ReportOnly -FilterVendor $FilterVendor -FilterProduct $FilterProduct -Reapply:$Reapply
} catch {
exit 1
}
}
# --- BCDSecurity ---
# BCD Security - Boot Configuration Data protection and integrity checks
# Detects tampered boot config, disabled secure boot, test signing, hypervisor abuse
$ModuleName = "BCDSecurity"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 300 }
function Invoke-BCDSecurity {
$detections = @()
try {
# Export current BCD and check for dangerous flags
$bcdOut = bcdedit /enum 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { return 0 }
# Test signing / no integrity checks (often used by bootkits)
if ($bcdOut -match 'testsigning\s+Yes') {
$detections += @{ Type = "Test signing enabled"; Risk = "High"; Detail = "testsigning Yes" }
}
if ($bcdOut -match 'nointegritychecks\s+Yes') {
$detections += @{ Type = "No integrity checks"; Risk = "Critical"; Detail = "nointegritychecks Yes" }
}
if ($bcdOut -match 'nx\s+OptOut') {
$detections += @{ Type = "DEP disabled (nx OptOut)"; Risk = "High"; Detail = "nx OptOut" }
}
# Boot menu policy legacy can bypass secure boot
if ($bcdOut -match 'bootmenupolicy\s+Legacy') {
$detections += @{ Type = "Legacy boot menu policy"; Risk = "Medium"; Detail = "bootmenupolicy Legacy" }
}
# Hypervisor launch options (could hide rootkits)
if ($bcdOut -match 'hypervisorlaunchtype\s+Off' -and $bcdOut -match 'hypervisorlaunchtype') {
# Only flag if explicitly set Off in a hypervisor-related entry and we expect it on in a secure config
}
if ($bcdOut -match 'hypervisorlaunchtype\s+Auto' -and $bcdOut -match 'debug\s+Yes') {
$detections += @{ Type = "Hypervisor debug enabled"; Risk = "Medium"; Detail = "hypervisor debug Yes" }
}
# Unknown or non-default boot loaders
$loaderPaths = [regex]::Matches($bcdOut, 'path\s+(\S+)') | ForEach-Object { $_.Groups[1].Value.Trim() }
$sysRoot = $env:SystemRoot
foreach ($path in $loaderPaths) {
$p = $path -replace '\\Device\\HarddiskVolume\d+', $env:SystemDrive
if ($p -match '\\Windows\\System32\\winload\.(exe|efi)' -or $p -match '\\EFI\\Microsoft\\Boot\\bootmgfw\.efi') { continue }
if ($p -match '\.(exe|efi)$' -and $p -notlike "*$sysRoot*" -and $p -notlike '*\EFI\*') {
$detections += @{ Type = "Non-default boot loader"; Risk = "High"; Detail = $path }
}
}
# Secure Boot status (via WMI / firmware)
try {
$secureBoot = Get-CimInstance -Namespace root\Microsoft\Windows\SecureBoot -ClassName UEFI_SecureBoot -ErrorAction SilentlyContinue
if ($secureBoot -and $secureBoot.SecureBootEnabled -eq $false) {
$detections += @{ Type = "Secure Boot disabled"; Risk = "High"; Detail = "UEFI SecureBoot disabled" }
}
} catch { }
# BCD store location tampering (default is \boot\bcd)
try {
$bcdPath = Join-Path $env:SystemRoot "boot\bcd"
if (Test-Path $bcdPath) {
$acl = Get-Acl -Path $bcdPath -ErrorAction SilentlyContinue
$everyone = $acl.Access | Where-Object { $_.IdentityReference -match 'Everyone|Users' -and $_.FileSystemRights -match 'Write|Modify' }
if ($everyone) {
$detections += @{ Type = "BCD store writable by broad identity"; Risk = "Medium"; Detail = $bcdPath }
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2091 `
-Message "BCDSecurity: $($d.Type) - $($d.Detail)" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\BCDSecurity_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.Detail)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) BCD issues"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-BCDSecurity | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- CredentialProtection ---
# Credential Protection - LSASS/dumping/credential theft indicators
# Detects LSASS access, credential manager abuse, DPAPI/mimikatz patterns
$ModuleName = "CredentialProtection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 300 }
function Invoke-CredentialProtection {
$detections = @()
try {
# Known credential-dump / mimikatz-like process names and paths
$credThreatNames = @('mimikatz','sekurlsa','procdump','proc_dump','lsass_dump','comsvcs','rundll32')
$procs = Get-Process -ErrorAction SilentlyContinue
foreach ($p in $procs) {
$pn = $p.ProcessName.ToLower()
foreach ($t in $credThreatNames) {
if ($pn -like "*$t*") {
$detections += @{
Type = "Known credential-dump related process"
ProcessId = $p.Id
ProcessName = $p.ProcessName
Path = $p.Path
Risk = "Critical"
}
break
}
}
}
# Command lines that suggest LSASS access or credential export
try {
$cimProcs = Get-CimInstance Win32_Process | Where-Object {
$_.CommandLine -match 'lsass|minidump|comsvcs.*MiniDump|sekurlsa|mimikatz|procdump.*lsass'
}
foreach ($proc in $cimProcs) {
$detections += @{
Type = "Suspicious credential-related command line"
ProcessId = $proc.ProcessId
ProcessName = $proc.Name
CommandLine = $proc.CommandLine
Risk = "Critical"
}
}
} catch { }
# Credential Guard / LSA protection registry
try {
$lsaPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
if (Test-Path $lsaPath) {
$runLsaPpl = Get-ItemProperty -Path $lsaPath -Name "RunLsaPpl" -ErrorAction SilentlyContinue
$lsaCfg = Get-ItemProperty -Path $lsaPath -Name "LsaCfgFlags" -ErrorAction SilentlyContinue
# RunLsaPpl=1 and LsaCfgFlags=1 are desirable
if (-not $runLsaPpl -or $runLsaPpl.RunLsaPpl -ne 1) {
$detections += @{ Type = "LSA protection (RunLsaPpl) not enforced"; Risk = "Medium"; Detail = "RunLsaPpl" }
}
}
} catch { }
# Scheduled tasks or startup that run credential-related tools
try {
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Ready' }
foreach ($t in $tasks) {
$action = $t.Actions | Select-Object -First 1
if ($action.Execute -match 'procdump|mimikatz|pwdump' -or $action.Arguments -match 'lsass|minidump') {
$detections += @{
Type = "Scheduled task with credential-dump tool"
TaskName = $t.TaskName
Execute = $action.Execute
Arguments = $action.Arguments
Risk = "Critical"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
$msg = "CredentialProtection: $($d.Type) - $($d.ProcessName -or $d.TaskName -or $d.Detail)"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2092 -Message $msg -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\CredentialProtection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.TaskName -or $_.Detail)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) credential threats"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-CredentialProtection | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- HidMacroGuard ---
# HID Macro Guard - Detects malicious HID/macro devices and suspicious driver/config
# USB HID keyboards/mice with macro firmware or button-injected payloads
$ModuleName = "HidMacroGuard"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-HidMacroGuard {
$detections = @()
try {
# Enumerate HID devices and drivers
try {
$hidDevices = Get-PnpDevice -Class HIDClass -ErrorAction SilentlyContinue
foreach ($dev in $hidDevices) {
if ($dev.Status -ne 'OK') { continue }
$driver = Get-PnpDeviceProperty -InstanceId $dev.InstanceId -KeyName 'DriverDesc' -ErrorAction SilentlyContinue
$hardwareId = (Get-PnpDeviceProperty -InstanceId $dev.InstanceId -KeyName 'HardwareID' -ErrorAction SilentlyContinue).Data
if (-not $hardwareId) { $hardwareId = @() } else { $hardwareId = @($hardwareId) }
$desc = ($driver.Data -join ' ') -replace '\s+', ' '
# Known macro/Rubber Ducky style hardware IDs or driver names
if ($desc -match 'HID.*Boot|Keyboard|Mouse' -and $desc -match 'composite|multi|macro') {
$detections += @{
Type = "HID composite/macro device"
Description = $desc
InstanceId = $dev.InstanceId
Risk = "Medium"
}
}
foreach ($hid in $hardwareId) {
if ($hid -match 'vid_([0-9a-f]{4})&pid_([0-9a-f]{4})') {
# Optional: check against known bad VID/PID list (example placeholders)
$vid = $Matches[1]; $pid = $Matches[2]
# e.g. Rubber Ducky and similar have been seen with certain VID/PIDs - add blocklist if desired
}
}
}
} catch { }
# Registry: HID-related persistence or filter drivers
$hidRegPaths = @(
"HKLM:\SYSTEM\CurrentControlSet\Services\*\Parameters",
"HKLM:\SYSTEM\CurrentControlSet\Enum\HID"
)
try {
$suspiciousHid = Get-ChildItem -Path "HKLM:\SYSTEM\CurrentControlSet\Services" -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^Hid|kbdclass|mouclass' }
foreach ($svc in $suspiciousHid) {
$imgPath = Get-ItemProperty -Path $svc.PSPath -Name "ImagePath" -ErrorAction SilentlyContinue
if ($imgPath -and $imgPath.ImagePath -notmatch '\\System32\\drivers\\') {
$detections += @{
Type = "Non-default HID/keyboard/mouse driver path"
Service = $svc.PSChildName
ImagePath = $imgPath.ImagePath
Risk = "High"
}
}
}
} catch { }
# Filter drivers (upper/lower filter) that could inject keystrokes
try {
$kbd = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\kbdclass" -ErrorAction SilentlyContinue
$mou = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\mouclass" -ErrorAction SilentlyContinue
foreach ($node in @($kbd, $mou)) {
if (-not $node) { continue }
$upper = $node.UpperFilters -join ','
$lower = $node.LowerFilters -join ','
$all = "$upper $lower"
if ($all -match '[\w\.]+\.(sys|dll)' -and $all -notmatch 'kbdclass|mouclass|i8042prt|kbdhid|mouhid') {
$detections += @{
Type = "Unexpected keyboard/mouse filter driver"
Filters = $all
Risk = "Medium"
}
}
}
} catch { }
# Processes that might be HID/macro injectors (named pipes or raw input)
try {
$procs = Get-CimInstance Win32_Process | Where-Object {
$_.Name -match 'hid|macro|keyboard|inject|ducky'
}
foreach ($p in $procs) {
$path = $p.ExecutablePath
if (-not $path -or -not (Test-Path $path)) { continue }
$sig = Get-AuthenticodeSignature -FilePath $path -ErrorAction SilentlyContinue
if ($sig.Status -ne 'Valid') {
$detections += @{
Type = "Unsigned HID/macro-related process"
ProcessId = $p.ProcessId
ProcessName = $p.Name
Path = $path
Risk = "High"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
$msg = "HidMacroGuard: $($d.Type) - $($d.Description -or $d.Service -or $d.ProcessName -or $d.Filters)"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2093 -Message $msg -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\HidMacroGuard_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.Description -or $_.Service -or $_.ProcessName)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) HID/macro issues"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-HidMacroGuard | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- LocalProxyDetection ---
# Local Proxy Detection - Shadow TLS / local proxy pattern
# Detects processes listening on localhost with outbound 443 (TLS camouflage / proxy)
$ModuleName = "LocalProxyDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-LocalProxyDetection {
$detections = @()
try {
# Established TCP connections: local port and remote port 443
$conns = Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue
$localListen = Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue
# Build set of PIDs listening on 127.0.0.1 or ::1
$listeners = $localListen | Where-Object {
$_.LocalAddress -match '^127\.|^::1$' -and $_.OwningProcess -gt 0
} | Group-Object OwningProcess
foreach ($g in $listeners) {
$pid = $g.Name
$proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
if (-not $proc) { continue }
# Same process has outbound 443?
$out443 = $conns | Where-Object { $_.OwningProcess -eq $pid -and $_.RemotePort -eq 443 }
if ($out443.Count -eq 0) { continue }
# Exclude browser and known good
$exclude = @('svchost','msedge','chrome','firefox','opera','brave','iexplore','ApplicationFrameHost')
if ($proc.ProcessName -in $exclude) { continue }
$detections += @{
Type = "Local proxy pattern (listen local + outbound 443)"
ProcessId = $pid
ProcessName = $proc.ProcessName
Path = $proc.Path
LocalPorts = ($g.Group.LocalPort | Sort-Object -Unique) -join ','
Risk = "High"
}
}
# Proxy environment variables pointing to localhost
$proxyVars = @('HTTP_PROXY','HTTPS_PROXY','ALL_PROXY','http_proxy','https_proxy')
foreach ($v in $proxyVars) {
$val = [Environment]::GetEnvironmentVariable($v, 'Process')
if ([string]::IsNullOrEmpty($val)) { $val = [Environment]::GetEnvironmentVariable($v, 'User') }
if ([string]::IsNullOrEmpty($val)) { $val = [Environment]::GetEnvironmentVariable($v, 'Machine') }
if ($val -and $val -match '127\.0\.0\.1|localhost|::1') {
$detections += @{
Type = "System proxy set to localhost"
Variable = $v
Value = $val
Risk = "Medium"
}
}
}
# WinHTTP proxy registry (often used by malware)
try {
$proxyReg = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyServer" -ErrorAction SilentlyContinue
if ($proxyReg -and $proxyReg.ProxyServer -match '127\.0\.0\.1|localhost') {
$detections += @{
Type = "Machine proxy registry points to localhost"
ProxyServer = $proxyReg.ProxyServer
Risk = "High"
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
$msg = "LocalProxyDetection: $($d.Type) - $($d.ProcessName -or $d.Variable -or 'Registry')"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2094 -Message $msg -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\LocalProxyDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.Variable -or $_.ProxyServer)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) local proxy indicators"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-LocalProxyDetection | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ScriptContentScan ---
# Script Content Scan - Suspicious script/RAT patterns in script files
# Scans temp, AppData, downloads for .ps1/.vbs/.bat/.cmd with IEX, DownloadString, encoded, etc.
$ModuleName = "ScriptContentScan"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 120 }
$SuspiciousPatterns = @(
'IEX\s*\(|Invoke-Expression',
'DownloadString\s*\(|DownloadFile\s*\(',
'\[Net\.WebClient\]|New-Object\s+Net\.WebClient',
'-EncodedCommand\s+[A-Za-z0-9+/=]{50,}',
'FromBase64String|\[Convert\]::FromBase64String',
'Bypass.*ExecutionPolicy|ExecutionPolicy\s+Bypass',
'Hidden|WindowStyle\s+Hidden|-w\s+1',
'WScript\.Shell|Shell\.Application',
'ADODB\.Stream|Scripting\.FileSystemObject',
'eval\s*\(|Execute\s*\(|ExecuteGlobal',
'powershell.*-nop.*-w.*hidden',
'certutil.*-urlcache.*-split',
'bitsadmin.*\/transfer',
'mshta\s+(vbscript|http|https):',
'regsvr32\s+.*\/s\s+.*scrobj\.dll'
)
$ScanPaths = @(
$env:TEMP,
[Environment]::GetFolderPath('LocalApplicationData'),
(Join-Path $env:USERPROFILE "Downloads"),
(Join-Path $env:USERPROFILE "Desktop"),
"C:\Windows\Temp"
)
$MaxFiles = 200
$MaxBytesToRead = 8192
function Test-SuspiciousScriptContent {
param([string]$Content, [string]$Ext)
if ([string]::IsNullOrWhiteSpace($Content)) { return $false }
$contentLower = $Content.ToLower()
$matchCount = 0
foreach ($pat in $SuspiciousPatterns) {
if ($Content -match $pat) { $matchCount++ }
if ($matchCount -ge 2) { return $true }
}
if ($matchCount -ge 1 -and $contentLower -match 'http[s]?://[^\s''"]+') { return $true }
return $false
}
function Invoke-ScriptContentScan {
$detections = @()
try {
$extensions = @('*.ps1','*.psm1','*.vbs','*.vbe','*.js','*.jse','*.wsf','*.bat','*.cmd','*.hta')
$totalScanned = 0
foreach ($root in $ScanPaths) {
if (-not (Test-Path $root)) { continue }
foreach ($ext in $extensions) {
$files = Get-ChildItem -Path $root -Filter $ext -Recurse -File -ErrorAction SilentlyContinue |
Select-Object -First ([Math]::Min(50, $MaxFiles - $totalScanned))
foreach ($f in $files) {
$totalScanned++
if ($totalScanned -gt $MaxFiles) { break }
try {
$raw = [System.IO.File]::ReadAllBytes($f.FullName)
$len = [Math]::Min($raw.Length, $MaxBytesToRead)
$content = [System.Text.Encoding]::GetEncoding(28591).GetString($raw, 0, $len)
if ($raw.Length -gt $len) { $content += "..." }
if (Test-SuspiciousScriptContent -Content $content -Ext $f.Extension) {
$detections += @{
Path = $f.FullName
Length = $f.Length
Extension = $f.Extension
Risk = "High"
}
}
} catch { }
}
if ($totalScanned -ge $MaxFiles) { break }
}
if ($totalScanned -ge $MaxFiles) { break }
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2095 `
-Message "ScriptContentScan: Suspicious script $($d.Path)" -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\ScriptContentScan_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Path)|$($_.Risk)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) suspicious scripts"
} else {
Write-Output "STATS:$ModuleName`:Scanned $totalScanned files OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-ScriptContentScan | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- ScriptHostDetection ---
# Script Host Detection - mshta/wscript/cscript used for RAT/remote execution
# Detects script-host processes with HTTP, encoded, or remote script arguments
$ModuleName = "ScriptHostDetection"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
$ScriptHosts = @('mshta.exe','wscript.exe','cscript.exe','scriptrunner.exe')
$SuspiciousPatterns = @(
'http[s]?://[^\s''"]+',
'vbscript:',
'javascript:',
'-EncodedCommand',
'\.hta\b',
'\.vbs\b.*http|http.*\.vbs',
'\.js\b.*http|http.*\.js',
'Execute.*Request|Response\.Write',
'eval\s*\(|Execute\s*\(',
'GetObject\s*\(.*http',
'XMLHTTP|WinHttp|MSXML2\.ServerXMLHTTP',
'Adodb\.Stream.*TypeBinary|\.Open.*adTypeBinary',
'ExpandEnvironmentStrings.*%temp%|%appdata%',
'powershell.*-enc|powershell.*-encodedcommand'
)
function Test-SuspiciousScriptHostCommandLine {
param([string]$CmdLine)
if ([string]::IsNullOrWhiteSpace($CmdLine)) { return $false }
$count = 0
foreach ($pat in $SuspiciousPatterns) {
if ($CmdLine -match $pat) { $count++ }
if ($count -ge 2) { return $true }
}
if ($CmdLine -match 'http[s]?://' -and $CmdLine.Length -gt 80) { return $true }
return $false
}
function Invoke-ScriptHostDetection {
$detections = @()
try {
$procs = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object { $_.Name -in $ScriptHosts }
foreach ($p in $procs) {
$cmd = $p.CommandLine
if (-not (Test-SuspiciousScriptHostCommandLine -CmdLine $cmd)) { continue }
$path = $p.ExecutablePath
$sig = $null
if ($path -and (Test-Path $path)) {
$sig = Get-AuthenticodeSignature -FilePath $path -ErrorAction SilentlyContinue
}
$detections += @{
ProcessId = $p.ProcessId
ProcessName = $p.Name
CommandLine = $cmd
Path = $path
Signed = ($sig.Status -eq 'Valid')
Risk = if ($sig -and $sig.Status -ne 'Valid') { "Critical" } else { "High" }
}
}
# Scheduled tasks that launch script hosts with URLs or encoded args
try {
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Ready' }
foreach ($t in $tasks) {
$action = $t.Actions | Select-Object -First 1
$exec = $action.Execute
$arg = $action.Arguments
$combined = "$exec $arg"
if ($exec -notmatch 'mshta|wscript|cscript') { continue }
if (Test-SuspiciousScriptHostCommandLine -CmdLine $combined) {
$detections += @{
Type = "Scheduled task script host with suspicious args"
TaskName = $t.TaskName
Execute = $exec
Arguments = $arg
Risk = "High"
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
$short = if ($d.CommandLine) { $d.CommandLine.Substring(0, [Math]::Min(200, $d.CommandLine.Length)) } else { $d.TaskName -or $d.Arguments }
$msg = "ScriptHostDetection: $($d.ProcessName -or 'Task') - $short"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2096 -Message $msg -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\ScriptHostDetection_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.ProcessName -or 'Task')|$($_.Risk)|$($_.CommandLine -or $_.Arguments)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) script host abuses"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-ScriptHostDetection | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- MitreMapping ---
# MITRE ATT&CK Mapping
# Maps detections to MITRE ATT&CK and logs stats
$ModuleName = "MitreMapping"
$TechniqueMap = @{
"HashDetection" = "T1204"
"LOLBin" = "T1218"
"ProcessAnomaly" = "T1055"
"AMSIBypass" = "T1562.006"
"CredentialDump" = "T1003"
"MemoryAcquisition" = "T1119"
"WMIPersistence" = "T1547.003"
"ScheduledTask" = "T1053.005"
"RegistryPersistence" = "T1547.001"
"DLLHijacking" = "T1574.001"
"TokenManipulation" = "T1134"
"ProcessHollowing" = "T1055.012"
"Keylogger" = "T1056.001"
"Ransomware" = "T1486"
"NetworkAnomaly" = "T1041"
"Beacon" = "T1071"
"DNSExfiltration" = "T1048"
"Rootkit" = "T1014"
"Clipboard" = "T1115"
"ShadowCopy" = "T1490"
"USB" = "T1052"
"Webcam" = "T1125"
"AttackTools" = "T1588"
"AdvancedThreat" = "T1204"
"EventLog" = "T1562.006"
"FirewallRule" = "T1562.004"
"Fileless" = "T1059"
"MemoryScanning" = "T1003"
"CodeInjection" = "T1055"
"DataExfiltration" = "T1048"
"FileEntropy" = "T1204"
"Honeypot" = "T1204"
"LateralMovement" = "T1021"
"ProcessCreation" = "T1059"
"YaraDetection" = "T1204"
"IdsDetection" = "T1059"
}
function Map-ToMitre {
param([string]$DetectionSource, [string]$Details = "")
$tech = $TechniqueMap[$DetectionSource]
if ($tech) {
$entry = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
DetectionSource = $DetectionSource
MITRETechnique = $tech
Details = $Details
}
$logPath = "$env:ProgramData\Antivirus\Logs\mitre_mapping_$(Get-Date -Format 'yyyy-MM-dd').log"
"$($entry.Timestamp)|$($entry.DetectionSource)|$($entry.MITRETechnique)|$($entry.Details)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
return $tech
}
return $null
}
function Invoke-MitreMapping {
# Periodic summary: aggregate today's MITRE log and write technique counts + any unmapped sources
try {
$logDir = "$env:ProgramData\Antivirus\Logs"
$today = Get-Date -Format 'yyyy-MM-dd'
$logPath = "$logDir\mitre_mapping_$today.log"
if (-not (Test-Path $logPath)) { return }
$lines = Get-Content -Path $logPath -ErrorAction SilentlyContinue
if (-not $lines -or $lines.Count -eq 0) { return }
$byTechnique = @{}
$bySource = @{}
foreach ($line in $lines) {
$parts = $line -split '\|', 4
if ($parts.Count -ge 3) {
$src = $parts[1].Trim()
$tech = $parts[2].Trim()
if ($tech) {
if (-not $byTechnique[$tech]) { $byTechnique[$tech] = 0 }
$byTechnique[$tech]++
}
if ($src) {
if (-not $bySource[$src]) { $bySource[$src] = 0 }
$bySource[$src]++
}
}
}
$summaryPath = "$logDir\mitre_summary_$today.log"
$summary = @()
$summary += "MITRE ATT&CK summary for $today"
$summary += "Technique counts: " + (($byTechnique.GetEnumerator() | Sort-Object -Property Value -Descending | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ", ")
$summary += "Source counts: " + (($bySource.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 20 | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ", ")
$summary | Set-Content -Path $summaryPath -ErrorAction SilentlyContinue
Write-Output "STATS:$ModuleName`:Summarized $($lines.Count) entries"
} catch {
Write-Output "ERROR:$ModuleName`:$_"
}
}
function Start-Module {
while ($true) {
Start-Sleep -Seconds 60
}
}
$script:EmbeddedRealTimeFileMonitor = $true
# --- RealTimeFileMonitor ---
# Real-Time File Monitor
# FileSystemWatcher for exe/dll/sys/winmd on fixed and removable drives
$ModuleName = "RealTimeFileMonitor"
$Watchers = @()
function Start-RealtimeMonitor {
$extensions = @("*.exe", "*.dll", "*.sys", "*.winmd")
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -match '^[A-Z]:\\$' }
foreach ($drive in $drives) {
$root = $drive.Root
if (-not (Test-Path $root)) { continue }
try {
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $root
$watcher.IncludeSubdirectories = $true
$watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor [System.IO.NotifyFilters]::LastWrite
$action = {
$path = $Event.SourceEventArgs.FullPath
$ext = [System.IO.Path]::GetExtension($path).ToLower()
if ($ext -in @(".exe", ".dll", ".sys", ".winmd")) {
Start-Sleep -Seconds 1
if (Test-Path $path) {
try {
$hash = (Get-FileHash -Path $path -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
$logPath = "$env:ProgramData\Antivirus\Logs\realtime_monitor_$(Get-Date -Format 'yyyy-MM-dd').log"
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|Created/Changed|$path|$hash" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2090 -Message "REAL-TIME: $path" -ErrorAction SilentlyContinue
} catch { }
}
}
}
Register-ObjectEvent $watcher Created -Action $action | Out-Null
Register-ObjectEvent $watcher Changed -Action $action | Out-Null
$watcher.EnableRaisingEvents = $true
$script:Watchers += $watcher
Write-Output "STATS:$ModuleName`:Watching $root"
} catch {
Write-Output "ERROR:$ModuleName`:Failed to watch $root - $_"
}
}
Write-Output "STATS:$ModuleName`:Started with $($script:Watchers.Count) watchers"
}
function Invoke-RealTimeFileMonitor {
if (-not $script:RealTimeFileMonitorStarted) {
Start-RealtimeMonitor
$script:RealTimeFileMonitorStarted = $true
}
}
if (-not $ModuleConfig -and -not $script:EmbeddedRealTimeFileMonitor) {
Start-RealtimeMonitor
while ($true) { Start-Sleep -Seconds 60 }
}
$script:EmbeddedAsrRules = $true
# --- AsrRules ---
# AsrRules.ps1 - GEDR-aligned: apply Windows Defender ASR rules (same GUIDs as GEDR EdrAsrRules)
#Requires -RunAsAdministrator
function Invoke-AsrRules {
$AsrRuleIds = @(
"56a863a9-875e-4185-98a7-b882c64b5ce5" # block_office_child_process
"5beb7efe-fd9a-4556-801d-275e5ffc04cc" # block_script_execution
"e6db77e5-3df2-4cf1-b95a-636979351e5b" # block_executable_email
"d4f940ab-401b-4efc-aadc-ad5f3c50688a" # block_office_macros
"b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4" # block_usb_execution
)
foreach ($id in $AsrRuleIds) {
try {
Add-MpPreference -AttackSurfaceReductionRules_Ids $id -AttackSurfaceReductionRules_Actions Enabled -ErrorAction Stop
Write-Host "Applied ASR rule: $id"
} catch {
Write-Warning "ASR rule $id : $_"
}
}
Write-Host "ASR rules applied."
}
if (-not $script:EmbeddedAsrRules) { Invoke-AsrRules }
$script:EmbeddedGRulesC2Block = $true
# --- GRulesC2Block ---
# GRulesC2Block.ps1 - GEDR-aligned: parse YARA/Snort-style rule files for IPs/domains, create outbound firewall blocks (C2 blocklist)
# Optional: set $env:GRulesRulePaths to semicolon- or comma-separated paths to .yar or .rules files. If not set, no-ops.
function Invoke-GRulesC2Block {
param([string]$RulePaths = $env:GRulesRulePaths)
$ErrorActionPreference = "Stop"
$BatchSize = 100
$DisplayNamePrefix = "Block C2 IPs Batch "
$LogDir = "$env:ProgramData\Antivirus\Logs"
$LogFile = "$LogDir\GRulesC2Block_$(Get-Date -Format 'yyyy-MM-dd').log"
if ([string]::IsNullOrWhiteSpace($RulePaths)) { return }
if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
function Write-GRulesLog {
param([string]$Message)
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$Message"
try { $line | Out-File -FilePath $LogFile -Append -Encoding UTF8 } catch {}
}
$paths = $RulePaths -split '[;,]+' | ForEach-Object { $_.Trim() } | Where-Object { $_.Length -gt 0 }
if ($paths.Count -eq 0) { return }
$allIps = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
$ipRegex = [regex]'\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b'
$domainRegex = [regex]'\b([a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b'
foreach ($path in $paths) {
if (-not (Test-Path $path)) { continue }
try {
$content = Get-Content -Path $path -Raw -ErrorAction Stop
foreach ($m in $ipRegex.Matches($content)) {
if ($m.Success -and $m.Value) { [void]$allIps.Add($m.Value) }
}
foreach ($m in $domainRegex.Matches($content)) {
if (-not $m.Success -or [string]::IsNullOrEmpty($m.Value)) { continue }
$domain = $m.Value
if ($domain.IndexOf('.') -ge 0 -and $domain.Length -ge 4) {
try {
[System.Net.Dns]::GetHostAddresses($domain) | ForEach-Object {
if ($_.IPAddressToString) { [void]$allIps.Add($_.IPAddressToString) }
}
} catch {}
}
}
} catch {
Write-GRulesLog "Parse $path : $_"
}
}
if ($allIps.Count -eq 0) { return }
$out = netsh advfirewall firewall show rule name=all 2>&1 | Out-String
$matches = [regex]::Matches($out, 'Rule Name:\s*(Block C2 IPs Batch \d+)')
$names = @($matches | ForEach-Object { $_.Groups[1].Value.Trim() } | Sort-Object -Unique)
foreach ($name in $names) {
if ($name) { netsh advfirewall firewall delete rule name="$name" 2>&1 | Out-Null }
}
$list = @($allIps)
$batchCount = 0
for ($i = 0; $i -lt $list.Count; $i += $BatchSize) {
$take = [Math]::Min($BatchSize, $list.Count - $i)
$batch = $list[$i..($i + $take - 1)]
$batchCount++
$ruleName = $DisplayNamePrefix + $batchCount
$addrs = $batch -join ","
try {
netsh advfirewall firewall add rule name="$ruleName" dir=out action=block remoteip=$addrs enabled=yes 2>&1 | Out-Null
Write-GRulesLog "Created $ruleName for $($batch.Count) IP(s)"
} catch {
Write-GRulesLog "Failed $ruleName : $_"
}
}
}
if (-not $script:EmbeddedGRulesC2Block) { Invoke-GRulesC2Block }
$script:EmbeddedProcessAuditing = $true
# --- ProcessAuditing ---
# ProcessAuditing.ps1 - GEDR-aligned: enable Process Creation auditing so Security log records 4688 events
#Requires -RunAsAdministrator
function Invoke-ProcessAuditing {
$audit = auditpol /get /subcategory:"Process Creation" /r 2>&1 | Out-String
if ($audit -match 'Process Creation\s+(\w+)\s+(\w+)') {
$s = $Matches[1]; $f = $Matches[2]
if ($s -eq 'Enable' -and $f -eq 'Enable') {
Write-Host "Process Creation auditing is already enabled."
return
}
}
$result = auditpol /set /subcategory:"Process Creation" /success:enable /failure:enable 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host "Process Creation auditing enabled. Security log will record 4688 events."
} else {
Write-Warning "Failed to enable Process Creation auditing: $result"
}
}
if (-not $script:EmbeddedProcessAuditing) { Invoke-ProcessAuditing }
# --- KeyScramblerManagement ---
# Key Scrambler Management - Anti-keylogger: KeyScrambler status + keyboard-hook/heuristic detection
# Ensures keyboard protection is active; detects processes with keylogging indicators
$ModuleName = "KeyScramblerManagement"
$LastTick = Get-Date
$TickInterval = if ($ModuleConfig.TickInterval) { $ModuleConfig.TickInterval } else { 60 }
function Invoke-KeyScramblerManagement {
$detections = @()
try {
# KeyScrambler (commercial) service/process check - if installed, ensure it's running
$ksService = Get-Service -Name "KeyScrambler*" -ErrorAction SilentlyContinue
if ($ksService) {
if ($ksService.Status -ne 'Running') {
$detections += @{
Type = "KeyScrambler service not running"
ServiceName = $ksService.Name
Status = $ksService.Status
Risk = "Medium"
}
}
}
# Processes with keyboard-related module names (potential keyloggers)
$keyboardModulePatterns = @('keyboard','keylog','kbd','keyhook','input','rawinput','lowlevelhook','getasynckeystate','setwindowshookex')
$keyloggerProcessNames = @('keylog','keyspy','keycapture','keystroke','keymon','kl_','logkeys','inputlog','keylogger')
try {
$procs = Get-Process -ErrorAction SilentlyContinue
foreach ($proc in $procs) {
$pn = $proc.ProcessName.ToLower()
$isLegit = $pn -in @('explorer','csrss','ctfmon','searchui','textinputhost','applicationframehost','runtimebroker','searchapp','systemsettings')
if ($isLegit) { continue }
try {
$modules = $proc.Modules | Where-Object {
$mn = $_.ModuleName.ToLower()
($keyboardModulePatterns | Where-Object { $mn -like "*$_*" })
}
} catch { continue }
if (-not $modules) {
foreach ($k in $keyloggerProcessNames) {
if ($pn -like "*$k*") {
$detections += @{
Type = "Process name suggests keylogger"
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Path = $proc.Path
Risk = "High"
}
break
}
}
continue
}
$path = $proc.Path
if ($path -and (Test-Path $path)) {
$sig = Get-AuthenticodeSignature -FilePath $path -ErrorAction SilentlyContinue
if ($sig.Status -ne 'Valid') {
$detections += @{
Type = "Unsigned process with keyboard-related modules"
ProcessId = $proc.Id
ProcessName = $proc.ProcessName
Path = $path
ModuleCount = $modules.Count
Risk = "High"
}
}
}
}
} catch { }
# Global keyboard hooks via Win32 (approximation: processes that might have set hooks)
try {
$cimProcs = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue
foreach ($p in $cimProcs) {
$cmd = $p.CommandLine
if ([string]::IsNullOrEmpty($cmd)) { continue }
if ($cmd -match 'setwindowshookex|getkeyboardstate|getasynckeystate|keybd_event' -and
$p.Name -notmatch 'explorer|ctfmon|search') {
$path = $p.ExecutablePath
if ($path -and (Test-Path $path)) {
$sig = Get-AuthenticodeSignature -FilePath $path -ErrorAction SilentlyContinue
if ($sig.Status -ne 'Valid') {
$detections += @{
Type = "Unsigned process with keyboard API in command/context"
ProcessId = $p.ProcessId
ProcessName = $p.Name
Path = $path
Risk = "Medium"
}
}
}
}
}
} catch { }
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
$msg = "KeyScramblerManagement: $($d.Type) - $($d.ProcessName -or $d.ServiceName)"
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2097 -Message $msg -ErrorAction SilentlyContinue
}
$logPath = "$env:ProgramData\Antivirus\Logs\KeyScramblerManagement_$(Get-Date -Format 'yyyy-MM-dd').log"
$detections | ForEach-Object {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$($_.Type)|$($_.Risk)|$($_.ProcessName -or $_.ServiceName)" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) keylogger/keyscrambler issues"
} else {
Write-Output "STATS:$ModuleName`:OK"
}
return $detections.Count
} catch {
Write-Output "ERROR:$ModuleName`:$_"
return 0
}
}
function Start-Module {
while ($true) {
try {
$now = Get-Date
if (($now - $LastTick).TotalSeconds -ge $TickInterval) {
Invoke-KeyScramblerManagement | Out-Null
$script:LastTick = $now
}
Start-Sleep -Seconds 5
} catch {
Write-Output "ERROR:$ModuleName`:$_"
Start-Sleep -Seconds 10
}
}
}
# --- Scheduler ---
$script:ModuleLastRun = @{}
$schedule = @(
@{ Name = 'Initializer'; Interval = 300; Invoke = 'Invoke-Initialization' }
@{ Name = 'HashDetection'; Interval = 90; Invoke = 'Invoke-HashScanOptimized' }
@{ Name = 'AMSIBypassDetection'; Interval = 90; Invoke = 'Invoke-AMSIBypassScan' }
@{ Name = 'GFocus'; Interval = 2; Invoke = 'Invoke-GFocusTick' }
@{ Name = 'ResponseEngine'; Interval = 180; Invoke = 'Invoke-ResponseEngine' }
@{ Name = 'BeaconDetection'; Interval = 60; Invoke = 'Invoke-BeaconDetection' }
@{ Name = 'NetworkTrafficMonitoring'; Interval = 45; Invoke = 'Invoke-NetworkTrafficMonitoringOptimized' }
@{ Name = 'ProcessAnomalyDetection'; Interval = 90; Invoke = 'Invoke-ProcessAnomalyScanOptimized' }
@{ Name = 'EventLogMonitoring'; Interval = 90; Invoke = 'Invoke-EventLogMonitoringOptimized' }
@{ Name = 'FileEntropyDetection'; Interval = 120; Invoke = 'Invoke-FileEntropyDetectionOptimized' }
@{ Name = 'YaraDetection'; Interval = 120; Invoke = 'Invoke-YaraScan' }
@{ Name = 'WMIPersistenceDetection'; Interval = 60; Invoke = 'Invoke-WMIPersistenceScan' }
@{ Name = 'ScheduledTaskDetection'; Interval = 60; Invoke = 'Invoke-ScheduledTaskScan' }
@{ Name = 'RegistryPersistenceDetection'; Interval = 60; Invoke = 'Invoke-RegistryPersistenceScan' }
@{ Name = 'DLLHijackingDetection'; Interval = 60; Invoke = 'Invoke-DLLHijackingScan' }
@{ Name = 'TokenManipulationDetection'; Interval = 30; Invoke = 'Invoke-TokenManipulationScan' }
@{ Name = 'ProcessHollowingDetection'; Interval = 20; Invoke = 'Invoke-ProcessHollowingScan' }
@{ Name = 'KeyloggerDetection'; Interval = 30; Invoke = 'Invoke-KeyloggerScan' }
@{ Name = 'ReflectiveDLLInjectionDetection'; Interval = 30; Invoke = 'Invoke-ReflectiveDLLInjectionDetection' }
@{ Name = 'RansomwareDetection'; Interval = 15; Invoke = 'Invoke-RansomwareScan' }
@{ Name = 'NetworkAnomalyDetection'; Interval = 30; Invoke = 'Invoke-NetworkAnomalyScan' }
@{ Name = 'DNSExfiltrationDetection'; Interval = 60; Invoke = 'Invoke-DNSExfiltrationDetection' }
@{ Name = 'RootkitDetection'; Interval = 60; Invoke = 'Invoke-RootkitScan' }
@{ Name = 'ClipboardMonitoring'; Interval = 10; Invoke = 'Invoke-ClipboardMonitoring' }
@{ Name = 'COMMonitoring'; Interval = 60; Invoke = 'Invoke-COMMonitoring' }
@{ Name = 'BrowserExtensionMonitoring'; Interval = 60; Invoke = 'Invoke-BrowserExtensionMonitoring' }
@{ Name = 'ShadowCopyMonitoring'; Interval = 30; Invoke = 'Invoke-ShadowCopyMonitoring' }
@{ Name = 'USBMonitoring'; Interval = 30; Invoke = 'Invoke-USBMonitoring' }
@{ Name = 'WebcamGuardian'; Interval = 20; Invoke = 'Invoke-WebcamGuardian' }
@{ Name = 'AttackToolsDetection'; Interval = 60; Invoke = 'Invoke-AttackToolsScan' }
@{ Name = 'AdvancedThreatDetection'; Interval = 60; Invoke = 'Invoke-AdvancedThreatScan' }
@{ Name = 'FirewallRuleMonitoring'; Interval = 60; Invoke = 'Invoke-FirewallRuleMonitoring' }
@{ Name = 'ServiceMonitoring'; Interval = 60; Invoke = 'Invoke-ServiceMonitoring' }
@{ Name = 'FilelessDetection'; Interval = 20; Invoke = 'Invoke-FilelessDetection' }
@{ Name = 'MemoryScanning'; Interval = 90; Invoke = 'Invoke-MemoryScanningOptimized' }
@{ Name = 'NamedPipeMonitoring'; Interval = 60; Invoke = 'Invoke-NamedPipeMonitoring' }
@{ Name = 'CodeInjectionDetection'; Interval = 30; Invoke = 'Invoke-CodeInjectionDetection' }
@{ Name = 'DataExfiltrationDetection'; Interval = 60; Invoke = 'Invoke-DataExfiltrationDetection' }
@{ Name = 'HoneypotMonitoring'; Interval = 300; Invoke = 'Invoke-HoneypotMonitoring' }
@{ Name = 'LateralMovementDetection'; Interval = 30; Invoke = 'Invoke-LateralMovementDetection' }
@{ Name = 'ProcessCreationDetection'; Interval = 60; Invoke = 'Invoke-ProcessCreationDetection' }
@{ Name = 'QuarantineManagement'; Interval = 300; Invoke = 'Invoke-QuarantineManagement' }
@{ Name = 'PrivacyForgeSpoofing'; Interval = 300; Invoke = 'Invoke-PrivacyForgeSpoofing' }
@{ Name = 'PasswordManagement'; Interval = 300; Invoke = 'Invoke-PasswordManagement' }
@{ Name = 'IdsDetection'; Interval = 60; Invoke = 'Invoke-IdsScan' }
@{ Name = 'CredentialDumpDetection'; Interval = 20; Invoke = 'Invoke-CredentialDumpScan' }
@{ Name = 'LOLBinDetection'; Interval = 30; Invoke = 'Invoke-LOLBinScan' }
@{ Name = 'MemoryAcquisitionDetection'; Interval = 90; Invoke = 'Invoke-MemoryAcquisitionScan' }
@{ Name = 'MobileDeviceMonitoring'; Interval = 90; Invoke = 'Invoke-MobileDeviceScan' }
@{ Name = 'CVE-MitigationPatcher'; Interval = 3600; Invoke = 'Invoke-CVEMitigationPatcher' }
@{ Name = 'BCDSecurity'; Interval = 300; Invoke = 'Invoke-BCDSecurity' }
@{ Name = 'CredentialProtection'; Interval = 300; Invoke = 'Invoke-CredentialProtection' }
@{ Name = 'HidMacroGuard'; Interval = 60; Invoke = 'Invoke-HidMacroGuard' }
@{ Name = 'LocalProxyDetection'; Interval = 60; Invoke = 'Invoke-LocalProxyDetection' }
@{ Name = 'ScriptContentScan'; Interval = 120; Invoke = 'Invoke-ScriptContentScan' }
@{ Name = 'ScriptHostDetection'; Interval = 60; Invoke = 'Invoke-ScriptHostDetection' }
@{ Name = 'MitreMapping'; Interval = 300; Invoke = 'Invoke-MitreMapping' }
@{ Name = 'RealTimeFileMonitor'; Interval = 60; Invoke = 'Invoke-RealTimeFileMonitor' }
@{ Name = 'AsrRules'; Interval = 86400; Invoke = 'Invoke-AsrRules' }
@{ Name = 'GRulesC2Block'; Interval = 3600; Invoke = 'Invoke-GRulesC2Block' }
@{ Name = 'ProcessAuditing'; Interval = 86400; Invoke = 'Invoke-ProcessAuditing' }
@{ Name = 'KeyScramblerManagement'; Interval = 60; Invoke = 'Invoke-KeyScramblerManagement' }
)
if ($RemoveRules) {
if (Get-Command Remove-BlockedRules -ErrorAction SilentlyContinue) { Remove-BlockedRules }; exit 0
}
Invoke-Initialization | Out-Null
$loopSleep = 5
while ($true) {
$now = Get-Date
foreach ($m in $schedule) {
if (-not $script:ModuleLastRun.ContainsKey($m.Name)) { $script:ModuleLastRun[$m.Name] = [datetime]::MinValue }
if (($now - $script:ModuleLastRun[$m.Name]).TotalSeconds -ge $m.Interval) {
$script:ModuleName = $m.Name
try { & $m.Invoke | Out-Null } catch { Write-Output "ERROR:$($m.Name):$_" }
$script:ModuleLastRun[$m.Name] = $now
}
}
Start-Sleep -Seconds $loopSleep
}
Editor is loading...
Leave a Comment