Gorstaks Antivirus
unknown
powershell
3 months ago
460 kB
6
No Index
# Antivirus.ps1
# Author: Gorstak | Usage: .\Antivirus.ps1 | .\Antivirus.ps1 -RemoveRules | .\Antivirus.ps1 -RegisterSchedule | .\Antivirus.ps1 -UnregisterSchedule
#Requires -RunAsAdministrator
param(
[Parameter(Mandatory=$false)][switch]$RemoveRules = $false,
[Parameter(Mandatory=$false)][string]$Module = $null,
[Parameter(Mandatory=$false)][switch]$RegisterSchedule = $false,
[Parameter(Mandatory=$false)][switch]$UnregisterSchedule = $false
)
$script:ScriptRoot = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
$script:ScriptPath = if ($PSCommandPath) { $PSCommandPath } else { Join-Path $script:ScriptRoot "Antivirus.ps1" }
# --- 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
"GSecurityLite" = 60
"NeuroBehaviorMonitor" = 15
"PasswordRotator" = 86400
}
$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
}
# Standard detection log format for Response Engine: timestamp|Type|Risk|Details (PID:..., ProcessName, etc.)
function Write-DetectionLog {
param(
[string]$LogName,
[string]$DetectionType,
[string]$Risk,
[hashtable]$Details = @{}
)
$logPath = "$env:ProgramData\Antivirus\Logs\${LogName}_$(Get-Date -Format 'yyyy-MM-dd').log"
$parts = @()
if ($Details.ProcessId) { $parts += "PID:$($Details.ProcessId)" }
if ($Details.ProcessName) { $parts += $Details.ProcessName }
if ($Details.FilePath) { $parts += $Details.FilePath }
if ($Details.RemoteAddress) { $parts += $Details.RemoteAddress }
if ($Details.CommandLine) { $parts += ($Details.CommandLine -replace '\|',' ') }
foreach ($k in @('Detail','Tool','Type','Path','Value')) {
if ($Details[$k]) { $parts += $Details[$k] }
}
$detailsStr = ($parts -join '|').Trim('|')
if (-not $detailsStr -and $Details.Count -gt 0) { $detailsStr = ($Details.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '|' }
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')|$DetectionType|$Risk|$detailsStr"
$line | Add-Content -Path $logPath -ErrorAction SilentlyContinue
}
# Kill current (compromised) EDR process and start a clean new one
function Restart-EDRScript {
$scriptPath = if ($script:ScriptPath -and (Test-Path $script:ScriptPath)) { $script:ScriptPath } else { $PSCommandPath }
if (-not $scriptPath -or -not (Test-Path $scriptPath)) {
Write-Output "ERROR:RestartEDR`:Could not resolve script path; cannot restart."
return
}
try {
if ([System.Diagnostics.EventLog]::SourceExists("AntivirusEDR")) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Warning -EventId 2099 `
-Message "EDR restarting: own process (PID $PID) was flagged (injection/hollowing). Starting clean process."
}
Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$scriptPath`"" -WorkingDirectory (Split-Path $scriptPath) -WindowStyle Hidden
} catch {
Write-Output "ERROR:RestartEDR`:$_"
return
}
exit 0
}
# --- 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"
}
ResponseActions = @{
Critical = @("Quarantine", "KillProcess", "BlockNetwork", "Log")
High = @("Quarantine", "Log", "Alert")
Medium = @("Log", "Alert")
Low = @("Log")
}
}
$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) {
# If target is our process, restart clean instead of just skipping
if ($Detection.ProcessId -eq $PID) {
$results += "Restarting EDR (own process flagged)"
Restart-EDRScript
}
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 {
# Load response action overrides from config (single-file: config in ProgramData)
$configFile = "$env:ProgramData\Antivirus\Data\config.json"
if (Test-Path $configFile) {
try {
$cfg = Get-Content $configFile -Raw | ConvertFrom-Json
if ($cfg.ResponseActions) {
$ra = $cfg.ResponseActions
$script:ResponseActions = @{
"Critical" = @(if ($ra.Critical) { $ra.Critical } else { "Quarantine","KillProcess","BlockNetwork","Log" })
"High" = @(if ($ra.High) { $ra.High } else { "Quarantine","Log","Alert" })
"Medium" = @(if ($ra.Medium) { $ra.Medium } else { "Log","Alert" })
"Low" = @(if ($ra.Low) { $ra.Low } else { "Log" })
}
}
} catch { }
}
# 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 = @{ "gsecuritylite" = $true }
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) {
$selfDetect = @($detections | Where-Object { $_.ProcessId -eq $PID })
$others = @($detections | Where-Object { $_.ProcessId -ne $PID })
if ($selfDetect.Count -gt 0) {
Write-Output "DETECTION:$ModuleName`:Own process (PID $PID) flagged - restarting EDR"
Restart-EDRScript
}
foreach ($detection in $others) {
Write-EventLog -LogName Application -Source "AntivirusEDR" -EntryType Error -EventId 2011 `
-Message "PROCESS HOLLOWING: $($detection.ProcessName) (PID: $($detection.ProcessId)) - $($detection.Type)"
}
foreach ($d in $others) {
Write-DetectionLog -LogName "ProcessHollowing" -DetectionType $d.Type -Risk $d.Risk -Details @{
ProcessId = $d.ProcessId; ProcessName = $d.ProcessName
}
}
if ($others.Count -gt 0) {
Write-Output "DETECTION:$ModuleName`:Found $($others.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
}
# --- PasswordRotator ---
# One-shot install (idempotent): deploys scheduled tasks to randomize user password every 10 min after logon, blank at logoff.
$PasswordRotatorTargetDir = 'C:\ProgramData\PasswordRotator'
$PasswordRotatorOnLogonTaskName = 'PasswordRotator-OnLogon'
$PasswordRotatorWorkerScript = @'
param([string]$Mode, [string]$Username)
$ErrorActionPreference = 'Stop'
$TargetDir = if ($PSScriptRoot) { $PSScriptRoot } else { 'C:\ProgramData\PasswordRotator' }
$UserFile = Join-Path $TargetDir 'currentuser.txt'
function Get-LoggedInUser {
$cs = Get-WmiObject -Class Win32_ComputerSystem -ErrorAction SilentlyContinue
$user = $cs.UserName
if (-not $user) { return $null }
if ($user -match '\\') { return $user.Split('\')[-1] }
return $user
}
function Set-UserPassword {
param([string]$U, [string]$P)
if ([string]::IsNullOrWhiteSpace($U)) { return }
try {
Set-LocalUser -Name $U -Password (ConvertTo-SecureString -String $P -AsPlainText -Force) -ErrorAction Stop
} catch {
try {
[ADSI]$adsi = "WinNT://$env:COMPUTERNAME/$U,user"
$adsi.SetPassword($P)
} catch {
"$(Get-Date -Format o) Set-UserPassword: $_" | Out-File (Join-Path $TargetDir 'log.txt') -Append
}
}
}
function Set-UserPasswordBlank {
param([string]$N)
if ([string]::IsNullOrWhiteSpace($N)) { return }
try {
[ADSI]$adsi = "WinNT://$env:COMPUTERNAME/$N,user"
$adsi.SetPassword('')
} catch {
try { & net user $N '' } catch { "$(Get-Date -Format o) Blank: $_" | Out-File (Join-Path $TargetDir 'log.txt') -Append }
}
}
function New-RandomPwd {
$c = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%'
-join ((1..24) | ForEach-Object { $c[(Get-Random -Maximum $c.Length)] })
}
function Remove-TasksForUser { param([string]$U)
$s = $U -replace '[^a-zA-Z0-9]', '_'
@("PasswordRotator-10Min-$s", "PasswordRotator-OnLogoff-$s") | ForEach-Object { Unregister-ScheduledTask -TaskName $_ -Confirm:$false -ErrorAction SilentlyContinue }
}
switch ($Mode) {
'Logon' {
$u = Get-LoggedInUser
if (-not $u) { exit 0 }
if (-not (Test-Path $TargetDir)) { New-Item -Path $TargetDir -ItemType Directory -Force | Out-Null }
$u | Set-Content -Path $UserFile -Force
Remove-TasksForUser -U $u
$safe = $u -replace '[^a-zA-Z0-9]', '_'
$worker = Join-Path $TargetDir 'Worker.ps1'
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$trigger10 = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(10) -RepetitionInterval (New-TimeSpan -Minutes 10) -RepetitionDuration (New-TimeSpan -Days 3650)
$action10 = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$worker`" -Mode Rotate"
Register-ScheduledTask -TaskName "PasswordRotator-10Min-$safe" -Action $action10 -Trigger $trigger10 -Principal $principal -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable) -Force | Out-Null
$triggerOff = New-ScheduledTaskTrigger -AtLogOff -User $u
$actionOff = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$worker`" -Mode Logoff -Username $u"
Register-ScheduledTask -TaskName "PasswordRotator-OnLogoff-$safe" -Action $actionOff -Trigger $triggerOff -Principal $principal -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable) -Force | Out-Null
Start-Sleep -Seconds 60
Set-UserPassword -U $u -P (New-RandomPwd)
}
'Rotate' {
if (-not (Test-Path $UserFile)) { exit 0 }
$u = (Get-Content -Path $UserFile -Raw).Trim()
if ($u) { Set-UserPassword -U $u -P (New-RandomPwd) }
}
'Logoff' {
if ($Username) {
Set-UserPasswordBlank -N $Username
$s = $Username -replace '[^a-zA-Z0-9]', '_'
Unregister-ScheduledTask -TaskName "PasswordRotator-10Min-$s" -Confirm:$false -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName "PasswordRotator-OnLogoff-$s" -Confirm:$false -ErrorAction SilentlyContinue
}
}
}
'@
function Invoke-PasswordRotatorInstall {
try {
if (Get-ScheduledTask -TaskName $PasswordRotatorOnLogonTaskName -ErrorAction SilentlyContinue) { return }
if (-not (Test-Path $PasswordRotatorTargetDir)) { New-Item -Path $PasswordRotatorTargetDir -ItemType Directory -Force | Out-Null }
$PasswordRotatorWorkerScript | Set-Content -Path (Join-Path $PasswordRotatorTargetDir 'Worker.ps1') -Encoding UTF8 -Force
$workerPath = Join-Path $PasswordRotatorTargetDir 'Worker.ps1'
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$trigger = New-ScheduledTaskTrigger -AtLogOn -User 'Everyone'
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$workerPath`" -Mode Logon"
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName $PasswordRotatorOnLogonTaskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
try {
$currentUser = (Get-WmiObject -Class Win32_ComputerSystem).UserName
if ($currentUser -match '\\') { $currentUser = $currentUser.Split('\')[-1] }
if ($currentUser) {
[ADSI]$adsi = "WinNT://$env:COMPUTERNAME/$currentUser,user"
$adsi.SetPassword('')
}
} catch { }
} catch {
Write-Output "ERROR:PasswordRotator`:$_"
}
}
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
}
}
}
# --- GSecurityLite (merged) ---
# GSecurityLite.ps1 logic - process/network/stealth cleanup, runs as a scheduled job
$script:GSecurityLiteSafeProcesses = @(
"Idle", "System", "System Idle Process", "Registry", "MemCompression", "Secure System", "smss", "csrss", "wininit", "services", "winlogon", "lsass", "dwm", "sihost", "fontdrvhost", "explorer",
"360chrome", "360se", "arc", "asb", "avastbrowser", "avgbrowser", "basilisk", "blisk", "brave", "bravesoftware", "browser", "browsec", "browsex", "ccleanerbrowser", "chrome", "citrio", "coc_coc_browser", "coowon", "comet", "coccoc", "dcbrowser", "dragon", "edge", "edgedev", "edgebeta", "edgecanary", "elements", "epic", "falkon", "floorp", "ghostery", "iridium", "k-meleon", "kinza", "librewolf", "maxthon", "msedge", "naver", "orbitum", "opera", "operagx", "operaneon", "palemoon", "pulse", "qqbrowser", "seamonkey", "sidekick", "sleipnir", "slimjet", "surf", "tor", "ucbrowser", "vivaldi", "waterfox", "whale", "yandex",
"brave-browser", "chromium", "iridium-browser", "ladybird", "mullvadbrowser", "thorium", "zen", "zen-browser", "avant", "cyberfox", "dooble", "icecat", "myPal", "netscape", "netSurf", "otter-browser", "qutebrowser",
"Steam", "steamwebhelper", "gameoverlayui", "EpicGamesLauncher", "EpicWebHelper", "Battle.net", "Agent", "BNetHelper", "GOGGalaxy", "GalaxyClient", "XboxPcApp", "XboxApp", "GamingServices", "gamingservicesui", "GameBar", "GameBarPresenceWriter", "EADesktop", "EABackgroundService", "itch",
"Discord", "DiscordPTB", "DiscordCanary", "NVIDIA Share", "nvcontainer", "NvFBCPlugin", "RadeonSoftware", "amddvr", "Overwolf", "owclient",
"EasyAntiCheat", "EasyAntiCheat_EOS", "BattlEye", "BEService", "BEServices", "vgc", "vgtray", "Faceit", "faceitclient",
"RzSDKService", "Razer Central", "LightingService", "iCUE", "ArmouryCrate", "ASUS ROG",
"cs2", "FortniteClient-Win64-Shipping", "VALORANT-Win64-Shipping", "LeagueClient", "GenshinImpact", "YuanShen", "eldenring", "Cyberpunk2077", "Starfield", "BaldursGate3", "gsecuritylite", "Antivirus"
) | Sort-Object -Unique
function Invoke-GSecurityLiteKillProcessAndParent {
param([int]$ProcessId)
try {
$proc = Get-CimInstance Win32_Process -Filter "ProcessId=$ProcessId" -ErrorAction SilentlyContinue
if ($proc) {
$procName = $proc.Name -replace '\.exe$',''
if ($script:GSecurityLiteSafeProcesses -contains $procName) { return }
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
Write-Log -Level "INFO" -Message "GSecurityLite killed process PID $ProcessId ($($proc.Name))" -Category "GSecurityLite"
if ($proc.ParentProcessId) {
$parentProc = Get-Process -Id $proc.ParentProcessId -ErrorAction SilentlyContinue
if ($parentProc) {
if ($parentProc.ProcessName -eq "explorer") {
Stop-Process -Id $parentProc.Id -Force -ErrorAction SilentlyContinue
Start-Process "explorer.exe"
Write-Log -Level "INFO" -Message "GSecurityLite restarted Explorer after killing parent of suspicious process." -Category "GSecurityLite"
} else {
Stop-Process -Id $parentProc.Id -Force -ErrorAction SilentlyContinue
Write-Log -Level "INFO" -Message "GSecurityLite killed parent process: $($parentProc.ProcessName) (PID $($parentProc.Id))" -Category "GSecurityLite"
}
}
}
}
} catch {}
}
function Invoke-GSecurityLiteProcessKiller {
$badNames = @("mimikatz", "procdump", "mimilib", "pypykatz")
foreach ($name in $badNames) {
Get-Process -Name "*$name*" -ErrorAction SilentlyContinue | ForEach-Object {
Write-Log -Level "WARNING" -Message "GSecurityLite found malicious process: $($_.ProcessName) (PID $($_.Id))" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $_.Id
}
}
}
function Invoke-GSecurityLiteConvertCIDRToSubnetMask { param([int]$CIDR); $c = $CIDR; $bin = ("1" * $c + "0" * (32 - $c)).ToCharArray(); $s = (($bin -join '').Insert(8,".").Insert(17,".").Insert(26,".") -replace '(.{8})','$1.'); $bytes = $s.Trim('.').Split('.') | ForEach-Object { [Convert]::ToByte($_, 2) }; [System.Net.IPAddress]::new($bytes) }
function Invoke-GSecurityLiteTestIPInRange {
param([string]$IP, [string]$CIDR)
try {
$network = $CIDR -split '/'; $networkAddress = [System.Net.IPAddress]::Parse($network[0]); $prefixLen = [int]$network[1]
$ipAddress = [System.Net.IPAddress]::Parse($IP)
$ipBytes = $ipAddress.GetAddressBytes(); $networkBytes = $networkAddress.GetAddressBytes()
$fullBytes = [Math]::Floor($prefixLen / 8); $remainingBits = $prefixLen % 8
for ($i = 0; $i -lt $fullBytes; $i++) { if ($ipBytes[$i] -ne $networkBytes[$i]) { return $false } }
if ($remainingBits -gt 0) { $mask = [byte](0xFF -shl (8 - $remainingBits)); if (($ipBytes[$fullBytes] -band $mask) -ne ($networkBytes[$fullBytes] -band $mask)) { return $false } }
return $true
} catch { return $false }
}
function Invoke-GSecurityLiteTestIPv6InRange {
param([string]$IP, [string]$CIDR)
try {
$ipAddress = [System.Net.IPAddress]::Parse($IP); $network = $CIDR -split '/'; $networkAddress = [System.Net.IPAddress]::Parse($network[0]); $prefixLength = [int]$network[1]
$ipBytes = $ipAddress.GetAddressBytes(); $networkBytes = $networkAddress.GetAddressBytes()
$fullBytes = [Math]::Floor($prefixLength / 8); $remainingBits = $prefixLength % 8
for ($i = 0; $i -lt $fullBytes; $i++) { if ($ipBytes[$i] -ne $networkBytes[$i]) { return $false } }
if ($remainingBits -gt 0) { $mask = [byte](0xFF -shl (8 - $remainingBits)); if (($ipBytes[$fullBytes] -band $mask) -ne ($networkBytes[$fullBytes] -band $mask)) { return $false } }
return $true
} catch { return $false }
}
function Invoke-GSecurityLiteKillConnections {
$SuspiciousCIDRs = @("208.95.0.0/16", "208.97.0.0/16", "65.9.0.0/16", "194.36.0.0/16", "52.109.0.0/16", "2.16.0.0/16", "2.18.0.0/16", "20.82.0.0/16", "20.190.0.0/16", "135.236.0.0/16", "23.32.0.0/16", "2.22.89.0/24", "23.35.0.0/16", "40.69.0.0/16", "51.124.0.0/16")
$SuspiciousIPv6CIDRs = @("2001:4860::/32", "2606:4700::/32")
try {
Get-NetTCPConnection -ErrorAction SilentlyContinue | Where-Object {
$c = $_; $s = $false
if ($c.RemoteAddress -match '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') { foreach ($cidr in $SuspiciousCIDRs) { if (Invoke-GSecurityLiteTestIPInRange -IP $c.RemoteAddress -CIDR $cidr) { $s = $true; break } } }
elseif ($c.RemoteAddress -match ':') { foreach ($cidr in $SuspiciousIPv6CIDRs) { if (Invoke-GSecurityLiteTestIPv6InRange -IP $c.RemoteAddress -CIDR $cidr) { $s = $true; break } } }
$s
} | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
if ($proc -and ($script:GSecurityLiteSafeProcesses -notcontains $proc.ProcessName)) {
$remoteIP = $_.RemoteAddress
New-NetFirewallRule -DisplayName "BlockRootkit-$remoteIP" -Direction Outbound -RemoteAddress $remoteIP -Action Block -ErrorAction SilentlyContinue
Write-Log -Level "WARNING" -Message "GSecurityLite blocked connection to $remoteIP for $($proc.ProcessName)" -Category "GSecurityLite"
}
}
} catch {}
}
function Invoke-GSecurityLiteKeyloggers {
try {
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'hook|log|key' } | ForEach-Object {
$process = Get-Process -Id $_.ProcessId -ErrorAction SilentlyContinue
if ($process -and ($script:GSecurityLiteSafeProcesses -notcontains $process.ProcessName)) {
Write-Log -Level "WARNING" -Message "GSecurityLite keylogger-like process: $($process.ProcessName) (PID $($process.Id))" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $process.Id
}
}
} catch {}
}
function Invoke-GSecurityLiteOverlays {
Get-Process -ErrorAction SilentlyContinue | Where-Object {
($script:GSecurityLiteSafeProcesses -notcontains $_.ProcessName) -and ($_.MainWindowHandle -ne 0) -and ([string]::IsNullOrEmpty($_.MainWindowTitle))
} | ForEach-Object {
Write-Log -Level "WARNING" -Message "GSecurityLite suspicious overlay: $($_.ProcessName) (PID $($_.Id))" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $_.Id
}
}
function Invoke-GSecurityLiteStealthKiller {
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | ForEach-Object {
$exePath = $_.ExecutablePath
if ($exePath -and (Test-Path $exePath)) {
$proc = Get-Process -Id $_.ProcessId -ErrorAction SilentlyContinue
if ($proc -and ($script:GSecurityLiteSafeProcesses -notcontains $proc.ProcessName)) {
$isHidden = (Get-Item $exePath -ErrorAction SilentlyContinue).Attributes -match "Hidden"
if ($isHidden) {
Write-Log -Level "WARNING" -Message "GSecurityLite killing hidden-attribute process: $exePath (PID $($_.ProcessId))" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $_.ProcessId
}
}
}
}
try {
$visible = (tasklist /fo csv 2>$null | ConvertFrom-Csv | Select-Object -ExpandProperty "PID") -as [int[]]
$all = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ProcessId)
$hidden = Compare-Object -ReferenceObject $visible -DifferenceObject $all | Where-Object { $_.SideIndicator -eq "=>" }
foreach ($item in $hidden) {
$hiddenPid = $item.InputObject
$proc = Get-Process -Id $hiddenPid -ErrorAction SilentlyContinue
if ($proc -and ($script:GSecurityLiteSafeProcesses -notcontains $proc.ProcessName)) {
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $hiddenPid
Write-Log -Level "WARNING" -Message "GSecurityLite killed stealth (tasklist-hidden) process: $($proc.ProcessName) (PID $hiddenPid)" -Category "GSecurityLite"
}
}
} catch {}
}
function Invoke-GSecurityLiteXSS {
try {
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue | ForEach-Object {
$remoteIP = $_.RemoteAddress
try {
$hostEntry = [System.Net.Dns]::GetHostEntry($remoteIP)
if ($hostEntry.HostName -match "xss") {
Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | Disable-NetAdapter -Confirm:$false -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Get-NetAdapter | Where-Object { $_.Status -eq "Disabled" } | Enable-NetAdapter -Confirm:$false -ErrorAction SilentlyContinue
New-NetFirewallRule -DisplayName "BlockXSS-$remoteIP" -Direction Outbound -RemoteAddress $remoteIP -Action Block -ErrorAction SilentlyContinue
Write-Log -Level "WARNING" -Message "GSecurityLite XSS host blocked: $($hostEntry.HostName) $remoteIP" -Category "GSecurityLite"
}
} catch {}
}
} catch {}
}
function Invoke-GSecurityLiteHiddenProcesses {
Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -and (Test-Path $_.Path) } | ForEach-Object {
if ($script:GSecurityLiteSafeProcesses -notcontains $_.ProcessName) {
$isHidden = (Get-Item $_.Path -ErrorAction SilentlyContinue).Attributes -match "Hidden"
if ($isHidden) { Invoke-GSecurityLiteKillProcessAndParent -ProcessId $_.Id }
}
}
}
function Invoke-GSecurityLiteRootkits {
$Procs = @{}
Get-NetTCPConnection -ErrorAction SilentlyContinue | Where-Object {
$_.LocalAddress -like '127.*' -or $_.LocalAddress -like '192.168.*' -or $_.LocalAddress -like '172.*' -or $_.LocalAddress -like '10.*'
} | ForEach-Object { $Procs[$_.OwningProcess] = $true }
foreach ($ProcessID in $Procs.Keys) {
$Proc = Get-Process -Id $ProcessID -ErrorAction SilentlyContinue
if ($Proc -and ($script:GSecurityLiteSafeProcesses -notcontains $Proc.ProcessName)) {
Write-Log -Level "WARNING" -Message "GSecurityLite killing suspicious local connection process: $($Proc.ProcessName) (PID $ProcessID)" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $ProcessID
}
}
}
$script:GSecurityLiteBaseCLSIDPaths = @("HKLM:\SOFTWARE\WOW6432Node\Classes\CLSID", "HKCR:\WOW6432Node\CLSID", "HKCU:\Software\Classes\CLSID", "HKCU:\Software\Wow6432Node\Classes\CLSID")
function Invoke-GSecurityLiteInProcControls {
foreach ($root in $script:GSecurityLiteBaseCLSIDPaths) {
if (-not (Test-Path $root)) { continue }
$clsidKeys = Get-ChildItem -Path $root -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}$' }
foreach ($clsid in $clsidKeys) {
foreach ($sub in @("InProcServer32", "InprocHandler32")) {
$target = Join-Path $clsid.PSPath $sub
if (-not (Test-Path $target)) { continue }
$defaultValue = (Get-ItemProperty -Path $target -Name "(default)" -ErrorAction SilentlyContinue)."(default)"
if ($defaultValue -and (Test-Path $defaultValue -PathType Leaf)) {
Write-Log -Level "WARNING" -Message "GSecurityLite suspicious InProc: $target -> $defaultValue" -Category "GSecurityLite"
try {
Remove-Item -Path $target -Force -Recurse -ErrorAction SilentlyContinue
if (Test-Path $defaultValue) { Remove-Item -Path $defaultValue -Force -ErrorAction SilentlyContinue }
} catch {}
return
}
}
}
}
}
function Invoke-GSecurityLiteTempUnsigned {
Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -like "*\Temp\*" -or $_.Path -like "*\TEMP\*" } | ForEach-Object {
if ($script:GSecurityLiteSafeProcesses -notcontains $_.ProcessName) {
if ((Get-AuthenticodeSignature $_.Path -ErrorAction SilentlyContinue).Status -ne 'Valid') {
Write-Log -Level "WARNING" -Message "GSecurityLite killing unsigned process in Temp: $($_.ProcessName) at $($_.Path)" -Category "GSecurityLite"
Invoke-GSecurityLiteKillProcessAndParent -ProcessId $_.Id
}
}
}
}
function Invoke-GSecurityLite {
Invoke-GSecurityLiteProcessKiller
Invoke-GSecurityLiteKillConnections
Invoke-GSecurityLiteKeyloggers
Invoke-GSecurityLiteOverlays
Invoke-GSecurityLiteStealthKiller
Invoke-GSecurityLiteXSS
Invoke-GSecurityLiteHiddenProcesses
Invoke-GSecurityLiteRootkits
Invoke-GSecurityLiteInProcControls
Invoke-GSecurityLiteTempUnsigned
Write-Output "STATS:GSecurityLite`:Cycle complete"
}
# --- NeuroBehaviorMonitor (Cognitive/Stimulus Manipulation Behavioral Sensor) ---
# Converted from standalone NBM - focus stealing and luminance flash detection
if (-not ([System.Management.Automation.PSTypeName]'NBMWin32').Type) {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class NBMWin32 {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
public static extern int BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest,
IntPtr hdcSource, int xSrc, int ySrc, int rop);
public const int SRCCOPY = 0x00CC0020;
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
"@
}
$script:NBM = @{
LastWindow = 0
FocusHistory = @{}
LastBrightness = $null
FlashScore = 0
}
function Get-NBMForegroundProcess {
$hWnd = [NBMWin32]::GetForegroundWindow()
if ($hWnd -eq 0) { return $null }
$pid = 0
[NBMWin32]::GetWindowThreadProcessId($hWnd, [ref]$pid) | Out-Null
try {
Get-Process -Id $pid -ErrorAction Stop
} catch { return $null }
}
function Measure-NBMScreenBrightness {
$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$bmp = New-Object System.Drawing.Bitmap 64,64
$g = [System.Drawing.Graphics]::FromImage($bmp)
$g.CopyFromScreen(0,0,0,0,$bmp.Size)
$sum = 0
for ($x=0;$x -lt 64;$x+=4){
for ($y=0;$y -lt 64;$y+=4){
$c = $bmp.GetPixel($x,$y)
$sum += ($c.R + $c.G + $c.B)
}
}
$g.Dispose()
$bmp.Dispose()
return $sum
}
function Test-NBMLuminanceFlash {
$current = Measure-NBMScreenBrightness
if ($script:NBM.LastBrightness -ne $null) {
$delta = [math]::Abs($current - $script:NBM.LastBrightness)
if ($delta -gt 40000) {
$script:NBM.FlashScore++
}
else {
$script:NBM.FlashScore = [math]::Max(0, $script:NBM.FlashScore - 1)
}
if ($script:NBM.FlashScore -ge 6) {
$script:NBM.LastBrightness = $current
return $true
}
}
$script:NBM.LastBrightness = $current
return $false
}
function Test-NBMFocusSteal($proc) {
if (-not $proc) { return $false }
if (-not $script:NBM.FocusHistory.ContainsKey($proc.Id)) {
$script:NBM.FocusHistory[$proc.Id] = @{
Count=0; FirstSeen=(Get-Date)
}
}
$entry = $script:NBM.FocusHistory[$proc.Id]
$entry.Count++
$elapsed = ((Get-Date) - $entry.FirstSeen).TotalSeconds
if ($elapsed -lt 10 -and $entry.Count -gt 8) {
return $true
}
return $false
}
function Invoke-NeuroBehaviorMonitor {
try {
$proc = Get-NBMForegroundProcess
if (-not $proc) { return }
$logPath = "$env:ProgramData\Antivirus\Logs\NeuroBehaviorMonitor_$(Get-Date -Format 'yyyy-MM-dd').log"
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
if (Test-NBMFocusSteal $proc) {
$details = "FocusAbuse|High|PID:$($proc.Id) Process:$($proc.ProcessName) Path:$($proc.Path)"
"$timestamp|$details" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
Write-Output "DETECTION:NeuroBehaviorMonitor`:Focus abuse - $($proc.ProcessName) (PID: $($proc.Id))"
if (Get-Command Invoke-ThreatResponse -ErrorAction SilentlyContinue) {
Invoke-ThreatResponse -ProcessId $proc.Id -ProcessName $proc.ProcessName -ExecutablePath $proc.Path -ThreatLevel "High"
}
}
if (Test-NBMLuminanceFlash) {
$details = "FlashStimulus|High|PID:$($proc.Id) Process:$($proc.ProcessName) Path:$($proc.Path)"
"$timestamp|$details" | Add-Content -Path $logPath -ErrorAction SilentlyContinue
Write-Output "DETECTION:NeuroBehaviorMonitor`:Flash stimulus - $($proc.ProcessName) (PID: $($proc.Id))"
if (Get-Command Invoke-ThreatResponse -ErrorAction SilentlyContinue) {
Invoke-ThreatResponse -ProcessId $proc.Id -ProcessName $proc.ProcessName -ExecutablePath $proc.Path -ThreatLevel "High"
}
}
}
catch {
Write-Output "ERROR:NeuroBehaviorMonitor`:$_"
}
}
# --- StartupPersistenceDetection ---
# Scans Startup folders and Run/RunOnce keys for suspicious persistence
$ModuleName = "StartupPersistenceDetection"
$LastTick = Get-Date
$TickInterval = 120
function Invoke-StartupPersistenceScan {
$detections = @()
try {
$startupPaths = @(
[Environment]::GetFolderPath('Startup'),
[Environment]::GetFolderPath('CommonStartup'),
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup",
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp"
)
foreach ($dir in $startupPaths) {
if (-not (Test-Path $dir)) { continue }
Get-ChildItem -Path $dir -File -ErrorAction SilentlyContinue | ForEach-Object {
$ext = [System.IO.Path]::GetExtension($_.Name).ToLower()
if ($ext -in @('.vbs','.vbe','.js','.jse','.wsf','.ps1','.bat','.cmd','.scr')) {
$detections += @{ Type = "Startup script"; Risk = "Medium"; ProcessId = $null; ProcessName = $null; Detail = $_.FullName; Path = $_.FullName }
}
if ($_.FullName -like "*temp*" -or $_.FullName -like "*tmp*" -or $_.FullName -like "*\AppData\Local\Temp\*") {
$detections += @{ Type = "Startup from temp path"; Risk = "High"; ProcessId = $null; ProcessName = $null; Detail = $_.FullName; Path = $_.FullName }
}
}
}
$runKeys = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
)
foreach ($key in $runKeys) {
if (-not (Test-Path $key)) { continue }
Get-ItemProperty -Path $key -ErrorAction SilentlyContinue | ForEach-Object {
$_.PSObject.Properties | Where-Object { $_.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider') } | ForEach-Object {
$val = $_.Value
if ($val -match 'powershell|cmd\.exe|wscript|cscript|mshta|\.vbs|\.ps1') {
$detections += @{ Type = "Run key script/script host"; Risk = "Medium"; Detail = $val }
}
if ($val -like "*temp*" -or $val -like "*tmp*") {
$detections += @{ Type = "Run key from temp"; Risk = "High"; Detail = $val }
}
}
}
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-DetectionLog -LogName "StartupPersistence" -DetectionType $d.Type -Risk $d.Risk -Details @{ Detail = $d.Detail; Path = $d.Path }
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) startup persistence items"
}
} catch { Write-Output "ERROR:$ModuleName`:$_" }
return $detections.Count
}
# --- SuspiciousParentChildDetection ---
# Detects LOL-style process chains: Office -> cmd/powershell, svchost -> unusual child
$ModuleName = "SuspiciousParentChildDetection"
$LastTick = Get-Date
$TickInterval = 45
$SuspiciousParentChild = @{
'winword.exe' = @('cmd.exe','powershell.exe','wscript.exe','cscript.exe','mshta.exe')
'excel.exe' = @('cmd.exe','powershell.exe','wscript.exe','cscript.exe','mshta.exe')
'powerpnt.exe'= @('cmd.exe','powershell.exe','wscript.exe','cscript.exe','mshta.exe')
'outlook.exe' = @('cmd.exe','powershell.exe','wscript.exe','cscript.exe')
'acrord32.exe'= @('cmd.exe','powershell.exe')
'msedge.exe' = @('cmd.exe','powershell.exe')
}
function Invoke-SuspiciousParentChildScan {
$detections = @()
try {
$procs = Get-CimInstance Win32_Process | Select-Object ProcessId, Name, ParentProcessId, CommandLine
foreach ($proc in $procs) {
$childName = $proc.Name
if (-not $proc.ParentProcessId) { continue }
$parent = $procs | Where-Object { $_.ProcessId -eq $proc.ParentProcessId } | Select-Object -First 1
if (-not $parent) { continue }
$parentName = $parent.Name
$suspiciousChildren = $SuspiciousParentChild[$parentName]
if ($suspiciousChildren -and ($childName -in $suspiciousChildren)) {
$detections += @{
ProcessId = $proc.ProcessId
ProcessName = $childName
Type = "Suspicious parent-child: $parentName -> $childName"
Risk = "High"
Detail = $proc.CommandLine
}
}
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-DetectionLog -LogName "SuspiciousParentChild" -DetectionType $d.Type -Risk $d.Risk -Details @{
ProcessId = $d.ProcessId; ProcessName = $d.ProcessName; Detail = $d.Detail
}
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) suspicious parent-child chains"
}
} catch { Write-Output "ERROR:$ModuleName`:$_" }
return $detections.Count
}
# --- ScriptBlockLoggingCheck ---
# Audits that PowerShell script block logging / transcription are enabled (hardening)
$ModuleName = "ScriptBlockLoggingCheck"
$LastTick = Get-Date
$TickInterval = 86400
function Invoke-ScriptBlockLoggingCheck {
$detections = @()
try {
$base = "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
$enable = $null
if (Test-Path $base) {
$enable = Get-ItemProperty -Path $base -Name "EnableScriptBlockLogging" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty EnableScriptBlockLogging
}
if ($enable -ne 1) {
$detections += @{ Type = "Script block logging disabled"; Risk = "Medium"; Detail = "EnableScriptBlockLogging not 1" }
}
$transcript = "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription"
if (Test-Path $transcript) {
$enableTranscript = Get-ItemProperty -Path $transcript -Name "EnableTranscripting" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty EnableTranscripting
if ($enableTranscript -ne 1) {
$detections += @{ Type = "PowerShell transcription disabled"; Risk = "Low"; Detail = "EnableTranscripting not 1" }
}
} else {
$detections += @{ Type = "PowerShell transcription not configured"; Risk = "Low"; Detail = "Transcription key missing" }
}
if ($detections.Count -gt 0) {
foreach ($d in $detections) {
Write-DetectionLog -LogName "ScriptBlockLogging" -DetectionType $d.Type -Risk $d.Risk -Details @{ Detail = $d.Detail }
}
Write-Output "DETECTION:$ModuleName`:Found $($detections.Count) logging/transcription issues"
}
} catch { Write-Output "ERROR:$ModuleName`:$_" }
return $detections.Count
}
# --- Scheduler ---
$script:ModuleLastRun = @{}
$script:AntivirusTaskName = "GShieldAntivirusEDR"
function Update-AgentStatus {
param([array]$ScheduleList, [hashtable]$LastRun)
$statusFile = "$env:ProgramData\Antivirus\Data\agent_status.json"
$cutoff = (Get-Date).AddMinutes(-10)
$active = @($ScheduleList | Where-Object { $LastRun[$_.Name] -and $LastRun[$_.Name] -ge $cutoff } | ForEach-Object { $_.Name })
if ($active.Count -eq 0) { $active = @($ScheduleList | ForEach-Object { $_.Name }) }
$status = @{
LastCheck = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
ActiveAgents = $active
SystemHealth = "Healthy"
TotalDetections = 0
TotalResponses = 0
}
if (Test-Path $statusFile) {
try {
$existing = Get-Content $statusFile -Raw | ConvertFrom-Json
if ($existing.PSObject.Properties['TotalDetections']) { $status.TotalDetections = $existing.TotalDetections }
if ($existing.PSObject.Properties['TotalResponses']) { $status.TotalResponses = $existing.TotalResponses }
} catch { }
}
$status | ConvertTo-Json -Depth 3 | Set-Content -Path $statusFile -ErrorAction SilentlyContinue
}
$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-RansomwareDetection' }
@{ 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 = 'PasswordRotator'; Interval = 86400; Invoke = 'Invoke-PasswordRotatorInstall' }
@{ 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' }
@{ Name = 'GSecurityLite'; Interval = 60; Invoke = 'Invoke-GSecurityLite' }
@{ Name = 'NeuroBehaviorMonitor'; Interval = 15; Invoke = 'Invoke-NeuroBehaviorMonitor' }
@{ Name = 'StartupPersistenceDetection'; Interval = 120; Invoke = 'Invoke-StartupPersistenceScan' }
@{ Name = 'SuspiciousParentChildDetection'; Interval = 45; Invoke = 'Invoke-SuspiciousParentChildScan' }
@{ Name = 'ScriptBlockLoggingCheck'; Interval = 86400; Invoke = 'Invoke-ScriptBlockLoggingCheck' }
)
if ($UnregisterSchedule) {
$t = Get-ScheduledTask -TaskName $script:AntivirusTaskName -ErrorAction SilentlyContinue
if ($t) {
Unregister-ScheduledTask -TaskName $script:AntivirusTaskName -Confirm:$false
Write-Host "Scheduled task '$($script:AntivirusTaskName)' removed. EDR will no longer start at boot."
} else {
Write-Host "Scheduled task '$($script:AntivirusTaskName)' not found."
}
exit 0
}
if ($RegisterSchedule) {
$scriptPath = if ($PSCommandPath) { $PSCommandPath } else { $MyInvocation.MyCommand.Path }
if (-not $scriptPath -or -not (Test-Path $scriptPath)) {
Write-Host "Could not resolve script path. Run from the folder containing this script."
exit 1
}
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$scriptPath`"" -WorkingDirectory (Split-Path $scriptPath)
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
$task = Get-ScheduledTask -TaskName $script:AntivirusTaskName -ErrorAction SilentlyContinue
if ($task) {
Set-ScheduledTask -TaskName $script:AntivirusTaskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
Write-Host "Updated scheduled task '$($script:AntivirusTaskName)'. EDR will run at system startup."
} else {
Register-ScheduledTask -TaskName $script:AntivirusTaskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
Write-Host "Registered scheduled task '$($script:AntivirusTaskName)'. EDR will run at system startup. Remove with: .\Antivirus.ps1 -UnregisterSchedule"
}
exit 0
}
if ($RemoveRules) {
if (Get-Command Remove-BlockedRules -ErrorAction SilentlyContinue) { Remove-BlockedRules }; exit 0
}
Invoke-Initialization | Out-Null
$loopSleep = 5
$lastStatusUpdate = [datetime]::MinValue
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
}
}
if (($now - $lastStatusUpdate).TotalSeconds -ge 60) {
Update-AgentStatus -ScheduleList $schedule -LastRun $script:ModuleLastRun
$lastStatusUpdate = $now
}
Start-Sleep -Seconds $loopSleep
}
Editor is loading...
Leave a Comment