Gorstaks Antivirus

 avatar
unknown
powershell
2 months ago
75 kB
17
No Index
<#
.SYNOPSIS
    EDR / Antivirus — single PowerShell script; auto-installs and persists. Use -Uninstall to remove.
.DESCRIPTION
    Runs detection jobs in background. No tray or dashboard. Install and persistence are automatic.
.PARAMETER Uninstall
    Remove persistence and delete install folder, then exit.
.NOTES
    Author: Gorstak
#>

param([switch]$Uninstall)

#Requires -Version 5.1

# --- Config (matches C# EdrConfig) ---
$script:EDRName       = 'MalwareDetector'
$script:InstallPath   = 'C:\ProgramData\AntivirusProtection'
$script:LogPath       = 'C:\ProgramData\AntivirusProtection\Logs'
$script:QuarantinePath= 'C:\ProgramData\AntivirusProtection\Quarantine'
$script:ReportsPath   = 'C:\ProgramData\AntivirusProtection\Reports'
$script:DataPath      = 'C:\ProgramData\AntivirusProtection\Data'
$script:YaraSubFolder = 'Yara'
$script:YaraExeName   = 'yara.exe'
$script:YaraRulesFileName = 'rules.yar'
$script:AutoKillThreats   = $true
$script:AutoQuarantine   = $true
$script:CirclHashLookupUrl = 'https://hashlookup.circl.lu/lookup/sha256'
$script:CymruApiUrl      = 'https://api.malwarehash.cymru.com/v1/hash'
$script:MalwareBazaarApiUrl = 'https://mb-api.abuse.ch/api/v1/'

# --- State ---
# PowerShell 5.1 has no ?? operator; use this instead
function script:NullCoalesce { param($Value, $Default = ''); if ($null -eq $Value) { $Default } else { $Value } }
$script:ThreatCount   = 0
$script:FilesQuarantined = 0
$script:ProcessesTerminated = 0

# --- Logging ---
$script:EdrLogLock = [object]::new()
function Write-EdrLog {
    param([string]$Source, [string]$Message, [string]$Level = 'INFO', [string]$LogFile)
    $line = "[{0:yyyy-MM-dd HH:mm:ss}] [{1}] [{2}] {3}" -f (Get-Date), $Level, $Source, $Message
    [System.Threading.Monitor]::Enter($script:EdrLogLock)
    try {
        if (-not (Test-Path -LiteralPath $script:LogPath)) { New-Item -ItemType Directory -Path $script:LogPath -Force | Out-Null }
        $fileName = if ($LogFile) { $LogFile } else { "edr_$(Get-Date -Format 'yyyy-MM-dd').log" }
        $path = Join-Path $script:LogPath $fileName
        Add-Content -LiteralPath $path -Value $line
    } finally { [System.Threading.Monitor]::Exit($script:EdrLogLock) }
}

# --- Install / Persistence (script-based) ---
$script:PersistenceValueName = 'AntivirusProtection'
$script:RunKeyPath = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'

function Get-CurrentScriptPath {
    if ($PSCommandPath) { return $PSCommandPath }
    return $MyInvocation.MyCommand.Path
}

function Get-InstalledScriptPath {
    return Join-Path $script:InstallPath 'Antivirus.ps1'
}

function Test-EdrInstalled {
    return Test-Path -LiteralPath (Get-InstalledScriptPath)
}

function Install-Edr {
    $src = Get-CurrentScriptPath
    if (-not $src -or -not (Test-Path -LiteralPath $src)) { return 'Current script not found.' }
    try {
        if (-not (Test-Path -LiteralPath $script:InstallPath)) { New-Item -ItemType Directory -Path $script:InstallPath -Force | Out-Null }
        foreach ($sub in @('Logs','Quarantine','Reports','Data',$script:YaraSubFolder)) {
            $d = Join-Path $script:InstallPath $sub
            if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null }
        }
        $dest = Get-InstalledScriptPath
        Copy-Item -LiteralPath $src -Destination $dest -Force
        $baseDir = [System.IO.Path]::GetDirectoryName($src)
        $yaraSrc = Join-Path $baseDir $script:YaraExeName
        if (Test-Path -LiteralPath $yaraSrc) {
            $yaraDest = Join-Path (Join-Path $script:InstallPath $script:YaraSubFolder) $script:YaraExeName
            $yaraDir = [System.IO.Path]::GetDirectoryName($yaraDest)
            if (-not (Test-Path -LiteralPath $yaraDir)) { New-Item -ItemType Directory -Path $yaraDir -Force | Out-Null }
            Copy-Item -LiteralPath $yaraSrc -Destination $yaraDest -Force
        }
        $rulesSrc = Join-Path $baseDir $script:YaraRulesFileName
        if (Test-Path -LiteralPath $rulesSrc) {
            $rulesDest = Join-Path $script:InstallPath $script:YaraRulesFileName
            Copy-Item -LiteralPath $rulesSrc -Destination $rulesDest -Force
        }
        Write-EdrLog -Source 'EdrInstall' -Message "Installed to $($script:InstallPath)" -Level INFO
        return $null
    } catch {
        Write-EdrLog -Source 'EdrInstall' -Message "Install failed: $($_.Exception.Message)" -Level ERROR
        return "Install failed: $($_.Exception.Message)"
    }
}

function Test-EdrPersisted {
    try {
        $v = Get-ItemProperty -Path $script:RunKeyPath -Name $script:PersistenceValueName -ErrorAction SilentlyContinue
        return $null -ne $v -and $null -ne $v.($script:PersistenceValueName)
    } catch { return $false }
}

function Enable-EdrPersistence {
    if (-not (Test-EdrInstalled)) {
        $err = Install-Edr
        if ($err) { return $err }
    }
    $ps1Path = Get-InstalledScriptPath
    $arg = "-WindowStyle Hidden -ExecutionPolicy Bypass -NoProfile -File `"$ps1Path`""
    try {
        Set-ItemProperty -Path $script:RunKeyPath -Name $script:PersistenceValueName -Value "powershell.exe $arg" -Type String
        Write-EdrLog -Source 'EdrInstall' -Message 'Persistence enabled (HKCU Run).' -Level INFO
        return $null
    } catch {
        Write-EdrLog -Source 'EdrInstall' -Message "Enable persistence failed: $($_.Exception.Message)" -Level ERROR
        return "Persistence enable failed: $($_.Exception.Message)"
    }
}

function Disable-EdrPersistence {
    try {
        Remove-ItemProperty -Path $script:RunKeyPath -Name $script:PersistenceValueName -ErrorAction SilentlyContinue
        Write-EdrLog -Source 'EdrInstall' -Message 'Persistence disabled (HKCU Run removed).' -Level INFO
        return $null
    } catch {
        Write-EdrLog -Source 'EdrInstall' -Message "Disable persistence failed: $($_.Exception.Message)" -Level ERROR
        return "Persistence disable failed: $($_.Exception.Message)"
    }
}

function Uninstall-Edr {
    Disable-EdrPersistence | Out-Null
    try {
        if (Test-Path -LiteralPath $script:InstallPath) {
            Remove-Item -LiteralPath $script:InstallPath -Recurse -Force
        }
        return $null
    } catch {
        try { Write-EdrLog -Source 'EdrInstall' -Message "Uninstall delete failed: $($_.Exception.Message)" -Level ERROR } catch { }
        return "Uninstall failed (folder in use?): $($_.Exception.Message)"
    }
}

# --- Helpers (PowerShell equivalents of EdrFile, EdrWhitelist, EdrQuarantine, EdrProcess, CleanGuard, GlobalRules, etc.) ---
$script:SuspiciousExtensions = @('.exe', '.dll', '.scr', '.vbs', '.ps1', '.bat', '.cmd')
$script:WhitelistNames = @('explorer.exe', 'explorer', 'Antivirus.exe', 'Antivirus.ps1', 'dllhost.exe', 'conhost.exe', 'sihost.exe', 'fontdrvhost.exe', 'SearchHost.exe', 'RuntimeBroker.exe', 'StartMenuExperienceHost.exe', 'SystemSettings.exe', 'ApplicationFrameHost.exe', 'Taskmgr.exe', 'msiexec.exe', 'TrustedInstaller.exe')

function Get-SuspiciousScanPaths {
    $localApp = [Environment]::GetFolderPath('LocalApplicationData')
    $appData = [Environment]::GetFolderPath('ApplicationData')
    $userProfile = [Environment]::GetEnvironmentVariable('USERPROFILE', 'User')
    $win = [Environment]::GetFolderPath('Windows')
    @(
        [System.IO.Path]::GetTempPath(),
        $appData,
        (Join-Path $localApp 'Temp'),
        (Join-Path $win 'Temp'),
        (Join-Path $userProfile 'Downloads')
    ) | Where-Object { $_ }
}

function Get-SuspiciousFiles {
    $list = [System.Collections.Generic.List[string]]::new()
    foreach ($basePath in Get-SuspiciousScanPaths) {
        if (-not $basePath -or -not (Test-Path -LiteralPath $basePath)) { continue }
        try {
            foreach ($ext in $script:SuspiciousExtensions) {
                Get-ChildItem -LiteralPath $basePath -Filter "*$ext" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $list.Add($_.FullName) }
            }
        } catch { }
    }
    return $list
}

function Get-FileSha256 {
    param([string]$Path)
    try {
        $fs = [System.IO.File]::OpenRead($Path)
        try {
            $sha = [System.Security.Cryptography.SHA256]::Create()
            $bytes = $sha.ComputeHash($fs)
            return [BitConverter]::ToString($bytes).Replace('-', '').ToLowerInvariant()
        } finally { $fs.Close() }
    } catch { return $null }
}

function Measure-FileEntropy {
    param([string]$Path)
    try {
        $fs = [System.IO.File]::OpenRead($Path)
        $toRead = [Math]::Min(4097, $fs.Length)
        $bytes = New-Object byte[] $toRead
        $r = $fs.Read($bytes, 0, $toRead)
        $fs.Close()
        if ($r -le 0) { return 0 }
        if ($r -lt $bytes.Length) { $bytes = $bytes[0..($r - 1)] }
        $freq = @{}
        foreach ($b in $bytes) { $freq[$b] = ($freq[$b] + 1) }
        $n = $bytes.Length
        $ent = 0.0
        foreach ($c in $freq.Values) { $p = $c / $n; $ent -= $p * [Math]::Log($p, 2) }
        return $ent
    } catch { return 0 }
}

function Get-FileLength { param([string]$Path) try { return (Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue).Length } catch { return 0 } }

function Test-SuspiciousDllPath {
    param([string]$Path)
    if (-not $Path) { return $false }
    $p = $Path.ToUpperInvariant()
    if ($p -match '\\TEMP\\' -or $p -match '\\TEMP"') { return $true }
    if ($p -match '\\APPDATA\\' -or $p -match '\\LOCALAPPDATA\\' -or $p -match '\\DOWNLOAD' -or $p -match '\\DESKTOP') { return $true }
    return $false
}

function Test-WhitelistedPath {
    param([string]$Path)
    if (-not $Path) { return $true }
    $name = [System.IO.Path]::GetFileName($Path)
    if (-not $name) { return $false }
    foreach ($n in $script:WhitelistNames) { if ($name -eq $n) { return $true } }
    return $false
}

function Move-ToQuarantine {
    param([string]$FilePath, [string]$Reason)
    if (-not $FilePath -or -not (Test-Path -LiteralPath $FilePath)) { return $false }
    try {
        if (-not (Test-Path -LiteralPath $script:QuarantinePath)) { New-Item -ItemType Directory -Path $script:QuarantinePath -Force | Out-Null }
        $fileName = [System.IO.Path]::GetFileName($FilePath)
        $dest = Join-Path $script:QuarantinePath ("{0}_{1}" -f [DateTime]::UtcNow.Ticks, $fileName)
        Move-Item -LiteralPath $FilePath -Destination $dest -Force
        $script:FilesQuarantined++
        Write-EdrLog -Source 'EdrQuarantine' -Message "Quarantined: $FilePath (Reason: $Reason)" -Level THREAT
        return $true
    } catch {
        Write-EdrLog -Source 'EdrQuarantine' -Message "Quarantine failed for $FilePath : $($_.Exception.Message)" -Level ERROR
        return $false
    }
}

function Get-ProcessesWmi {
    $list = [System.Collections.Generic.List[PSCustomObject]]::new()
    try {
        $searcher = New-Object System.Management.ManagementObjectSearcher 'SELECT ProcessId,Name,CommandLine,ExecutablePath,ParentProcessId FROM Win32_Process'
        foreach ($o in $searcher.Get()) {
            $list.Add([PSCustomObject]@{
                ProcessId = [int]$o['ProcessId']
                Name = [string]$o['Name']
                CommandLine = [string]$o['CommandLine']
                ExecutablePath = [string]$o['ExecutablePath']
                ParentProcessId = [int]$o['ParentProcessId']
            })
        }
    } catch { Write-EdrLog -Source 'EdrProcess' -Message "GetProcesses error: $($_.Exception.Message)" -Level ERROR }
    return $list
}

function Get-ParentProcess { param($Child, $All) if (-not $Child -or -not $All) { return $null }; foreach ($p in $All) { if ($p.ProcessId -eq $Child.ParentProcessId) { return $p } }; return $null }

function Get-ProcessExecutablePathWmi { param([int]$ProcessId) try { $s = New-Object System.Management.ManagementObjectSearcher ("SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = {0}" -f $ProcessId); foreach ($o in $s.Get()) { $p = [string]$o['ExecutablePath']; if ($p) { return $p } } } catch { }; return $null }

function Get-ProcessOwnerWmi { param([int]$ProcessId); try { $q = "SELECT * FROM Win32_Process WHERE ProcessId = $ProcessId"; $s = New-Object System.Management.ManagementObjectSearcher $q; foreach ($o in $s.Get()) { $argList = @('', ''); $o.InvokeMethod('GetOwner', $argList) | Out-Null; return ($argList[1], $argList[0]) } } catch { }; return ($null, $null) }

function Stop-ThreatProcess {
    param([int]$ProcessId, [string]$ProcessName)
    if ($ProcessId -eq $pid) { return }
    try {
        $proc = Get-Process -Id $ProcessId -ErrorAction Stop
        $proc.Kill()
        $script:ProcessesTerminated++
        Write-EdrLog -Source 'EdrProcess' -Message "Terminated threat process: $ProcessName (PID: $ProcessId)" -Level ACTION
    } catch { Write-EdrLog -Source 'EdrProcess' -Message "Failed to terminate $ProcessName : $($_.Exception.Message)" -Level ERROR }
}

$script:CleanGuardCirclTrustThreshold = 50
$script:CleanGuardSelfHash = $null
function Get-CleanGuardSelfHash {
    if ($null -ne $script:CleanGuardSelfHash) { return $script:CleanGuardSelfHash }
    try {
        $selfPath = (Get-Process -Id $pid).Path
        if ($selfPath -and (Test-Path -LiteralPath $selfPath)) { $script:CleanGuardSelfHash = Get-FileSha256 -Path $selfPath }
    } catch { }
    return $script:CleanGuardSelfHash
}

function Test-CleanGuardMalicious {
    param([string]$Path, [string]$Sha256)
    if (-not $Path -or -not (Test-Path -LiteralPath $Path)) { return $false }
    if (Test-WhitelistedPath -Path $Path) { return $false }
    $hash = if ($Sha256) { $Sha256 } else { Get-FileSha256 -Path $Path }
    if (-not $hash) { return $false }
    $sh = Get-CleanGuardSelfHash
    if ($sh -and $hash -eq $sh) { return $false }
    try {
        $url = $script:CirclHashLookupUrl.TrimEnd('/') + '/' + $hash
        $json = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 8 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Content
        if ($json -match 'hashlookup:trust["\s]*:\s*(\d+)') { $trust = [int]$Matches[1]; if ($trust -ge $script:CleanGuardCirclTrustThreshold) { return $false } }
    } catch { }
    try {
        $body = '{"query":"get_info","hash":"' + $hash + '"}'
        $r = Invoke-WebRequest -Uri $script:MalwareBazaarApiUrl -Method POST -Body $body -ContentType 'application/json' -UseBasicParsing -TimeoutSec 12 -ErrorAction SilentlyContinue
        if ($r.Content -match 'hash_found') { return $true }
    } catch { }
    try {
        $url = $script:CymruApiUrl.TrimEnd('/') + '/' + $hash
        $body = (Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 8 -ErrorAction SilentlyContinue).Content
        if ($body -match '"malware":\s*true') { return $true }
    } catch { }
    return $false
}

function Move-QuarantineIfAllowed {
    param([string]$Path, [string]$Reason)
    if (-not $script:AutoQuarantine) { return $false }
    if (-not (Test-CleanGuardMalicious -Path $Path)) { return $false }
    return (Move-ToQuarantine -FilePath $Path -Reason $Reason)
}

function Stop-ProcessIfAllowed {
    param([int]$ProcessId, [string]$ProcessName, [string]$ExePath, [switch]$Fileless)
    if (-not $script:AutoKillThreats) { return }
    $path = if ($ExePath) { $ExePath } else { Get-ProcessExecutablePathWmi -ProcessId $ProcessId }
    if (-not $path -or -not (Test-Path -LiteralPath $path)) { return }
    if (Test-WhitelistedPath -Path $path) { return }
    # Fileless: no file to hash (payload in memory/command line); kill on detection without API check
    if ($Fileless) { Stop-ThreatProcess -ProcessId $ProcessId -ProcessName $ProcessName; return }
    $hash = Get-FileSha256 -Path $path
    if (-not $hash) { return }
    $sh = Get-CleanGuardSelfHash
    if ($sh -and $hash -eq $sh) { return }
    if (-not (Test-CleanGuardMalicious -Path $path -Sha256 $hash)) { return }
    Stop-ThreatProcess -ProcessId $ProcessId -ProcessName $ProcessName
}

function Get-RunEntriesRegistry {
    $list = [System.Collections.Generic.List[PSCustomObject]]::new()
    try {
        foreach ($hive in @('HKLM', 'HKCU')) {
            foreach ($sub in @('Run', 'RunOnce')) {
                $keyPath = "${hive}:\SOFTWARE\Microsoft\Windows\CurrentVersion\$sub"
                if (-not (Test-Path -LiteralPath $keyPath)) { continue }
                Get-ItemProperty -Path $keyPath -ErrorAction SilentlyContinue | ForEach-Object {
                    $_.PSObject.Properties | Where-Object { $_.Name -notmatch '^(PSPath|PSParentPath|PSChildName|PSDrive|PSProvider)' } | ForEach-Object {
                        $list.Add([PSCustomObject]@{ KeyName = "$hive\...\$sub"; ValueName = $_.Name; Value = [string]$_.Value })
                    }
                }
            }
        }
    } catch { Write-EdrLog -Source 'EdrRegistry' -Message "GetRunEntries error: $($_.Exception.Message)" -Level ERROR }
    return $list
}

function Test-AmsiDisabledRegistry {
    try {
        $k = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\AMSI' -ErrorAction SilentlyContinue
        if (-not $k) { return $false }
        $v = $k.DisableAMSI
        if ($null -eq $v) { return $false }
        if ($v -is [int]) { return $v -ne 0 }
        return ([string]$v) -eq '1'
    } catch { return $false }
}

function Get-WmiEventFilters {
    $list = [System.Collections.Generic.List[PSCustomObject]]::new()
    try {
        $s = New-Object System.Management.ManagementObjectSearcher 'root\subscription', 'SELECT * FROM __EventFilter'
        foreach ($o in $s.Get()) { $list.Add([PSCustomObject]@{ Name = [string]$o['Name']; Query = [string]$o['Query'] }) }
    } catch { Write-EdrLog -Source 'EdrWmi' -Message "GetEventFilters error: $($_.Exception.Message)" -Level ERROR }
    return $list
}

function Get-WmiCommandLineConsumers {
    $list = [System.Collections.Generic.List[PSCustomObject]]::new()
    try {
        $s = New-Object System.Management.ManagementObjectSearcher 'root\subscription', 'SELECT * FROM CommandLineEventConsumer'
        foreach ($o in $s.Get()) { $list.Add([PSCustomObject]@{ Name = [string]$o['Name']; CommandLineTemplate = [string]$o['CommandLineTemplate'] }) }
    } catch { Write-EdrLog -Source 'EdrWmi' -Message "GetCommandLineConsumers error: $($_.Exception.Message)" -Level ERROR }
    return $list
}

function Get-ScheduledTasksList {
    $list = [System.Collections.Generic.List[PSCustomObject]]::new()
    try {
        $out = & schtasks /query /fo LIST /v 2>$null
        $cur = [PSCustomObject]@{ TaskName = ''; State = ''; RunAsUser = ''; Execute = '' }
        foreach ($line in $out) {
            if ($line -match '^TaskName:\s*(.+)') { $cur.TaskName = $Matches[1].Trim() }
            elseif ($line -match '^Status:\s*(.+)') { $cur.State = $Matches[1].Trim() }
            elseif ($line -match '^Run As User:\s*(.+)') { $cur.RunAsUser = $Matches[1].Trim() }
            elseif ($line -match '^Task To Run:\s*(.+)') { $cur.Execute = $Matches[1].Trim(); $list.Add($cur); $cur = [PSCustomObject]@{ TaskName = ''; State = ''; RunAsUser = ''; Execute = '' } }
        }
    } catch { Write-EdrLog -Source 'EdrScheduledTask' -Message "QueryTasks error: $($_.Exception.Message)" -Level ERROR }
    return $list
}

function Test-ScheduledTaskSuspicious { param($t); if (-not $t.Execute) { return $false }; $exe = $t.Execute.ToLower(); if ($exe -notmatch 'powershell|cmd|wscript|cscript|mshta') { return $false }; $user = (NullCoalesce $t.RunAsUser).ToLower(); if ($user -match 'system|administrator') { return $false }; if ((NullCoalesce $t.TaskName) -match '^AntivirusAutoRestart_|^AntivirusProtection$') { return $false }; return $true }

function Get-BrowserRoots {
    $local = [Environment]::GetFolderPath('LocalApplicationData')
    $app = [Environment]::GetFolderPath('ApplicationData')
    if (-not $local) { $local = [Environment]::GetEnvironmentVariable('LOCALAPPDATA', 'User') }
    if (-not $app) { $app = [Environment]::GetEnvironmentVariable('APPDATA', 'User') }
    $entries = @(
        (Join-Path $local 'Google\Chrome\User Data'), (Join-Path $local 'Microsoft\Edge\User Data'), (Join-Path $local 'BraveSoftware\Brave-Browser\User Data'),
        (Join-Path $local 'Opera Software\Opera Stable'), (Join-Path $local 'Opera Software\Opera GX Stable'), (Join-Path $local 'Vivaldi\User Data'),
        (Join-Path $local 'Yandex\YandexBrowser\User Data'), (Join-Path $app 'Mozilla\Firefox\Profiles'), (Join-Path $local 'Mozilla\Firefox\Profiles'),
        (Join-Path $local 'Chromium\User Data')
    )
    $entries | Where-Object { $_ -and (Test-Path -LiteralPath $_) }
}

function Get-ElfDllsInBrowsers {
    $out = [System.Collections.Generic.List[string]]::new()
    foreach ($root in Get-BrowserRoots) {
        try { Get-ChildItem -LiteralPath $root -Filter '*_elf.dll' -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $out.Add($_.FullName) } } catch { }
    }
    return $out
}

# --- Job functions (PowerShell implementations; no C#) ---
function Invoke-JobHashDetection {
    $files = Get-SuspiciousFiles
    foreach ($path in $files) {
        try {
            if (Test-WhitelistedPath -Path $path) { continue }
            if (Move-QuarantineIfAllowed -Path $path -Reason 'HashDetection') {
                $hash = Get-FileSha256 -Path $path
                Write-EdrLog -Source 'HashDetection' -Message ("THREAT (CleanGuard): {0} | Hash: {1}" -f $path, $hash) -Level THREAT -LogFile 'hash_detections.log'
                $script:ThreatCount++
            } else {
                $ent = Measure-FileEntropy -Path $path
                $len = Get-FileLength -Path $path
                if ($ent -gt 7.5 -and $len -gt 0 -and $len -lt 1MB) { Write-EdrLog -Source 'HashDetection' -Message ("High entropy: {0} | Entropy: {1:N2}" -f $path, $ent) -Level WARNING -LogFile 'hash_detections.log' }
            }
        } catch { Write-EdrLog -Source 'HashDetection' -Message "Error scanning $path : $($_.Exception.Message)" -Level ERROR -LogFile 'hash_detections.log' }
    }
}

function Invoke-JobLOLBinDetection {
    $self = $pid
    $procs = Get-ProcessesWmi
    $lolBins = @(
        @{ Name = 'certutil'; Patterns = @('-decode', '-urlcache', '-verifyctl', '-encode'); Severity = 'HIGH'; Description = 'Certutil abuse' },
        @{ Name = 'bitsadmin'; Patterns = @('transfer', 'addfile', '/download'); Severity = 'HIGH'; Description = 'BITS abuse' },
        @{ Name = 'mshta'; Patterns = @('http://', 'https://', 'javascript:', 'vbscript:'); Severity = 'CRITICAL'; Description = 'MSHTA remote code execution' },
        @{ Name = 'regsvr32'; Patterns = @('scrobj.dll', '/s', '/u', 'http://', 'https://'); Severity = 'HIGH'; Description = 'Regsvr32 squiblydoo' },
        @{ Name = 'rundll32'; Patterns = @('javascript:', 'http://', 'https://', 'shell32.dll,Control_RunDLL'); Severity = 'MEDIUM'; Description = 'Rundll32 proxy execution' },
        @{ Name = 'wmic'; Patterns = @('process call create', '/node:', 'format:"http', 'xsl:http'); Severity = 'HIGH'; Description = 'WMIC remote/XSL abuse' },
        @{ Name = 'powershell'; Patterns = @('-enc ', '-encodedcommand', 'downloadstring', 'iex ', 'invoke-expression', '-nop', '-w hidden', 'bypass'); Severity = 'HIGH'; Description = 'PowerShell obfuscation' },
        @{ Name = 'sc'; Patterns = @('create', 'config', 'binpath='); Severity = 'MEDIUM'; Description = 'Service manipulation' },
        @{ Name = 'msiexec'; Patterns = @('/quiet', '/q', 'http://', 'https://'); Severity = 'MEDIUM'; Description = 'Silent MSI from remote' }
    )
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $cmd = (NullCoalesce $p.CommandLine)
        if (-not $cmd) { continue }
        $pname = ((NullCoalesce $p.Name)).Replace('.exe', '').Replace('.EXE', '')
        foreach ($lb in $lolBins) {
            if ($pname -notmatch $lb.Name) { continue }
            $matched = @(); foreach ($pat in $lb.Patterns) { if ($cmd -match [regex]::Escape($pat)) { $matched += $pat } }
            if ($matched.Count -eq 0) { continue }
            Write-EdrLog -Source 'LOLBinDetection' -Message ("LOLBin [{0}] Process: {1} (PID: {2}) | {3} | Patterns: {4} | {5}" -f $lb.Severity, $p.Name, $p.ProcessId, $lb.Description, ($matched -join ', '), $cmd) -Level THREAT -LogFile 'behavior_detections.log'
            $script:ThreatCount++
            if ($lb.Severity -in @('HIGH', 'CRITICAL')) { Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath }
            break
        }
    }
}

function Invoke-JobProcessAnomalyDetection {
    $self = $pid
    $procs = Get-ProcessesWmi
    foreach ($proc in $procs) {
        if ($proc.ProcessId -eq $self) { continue }
        $score = 0; $anomalies = @()
        $parent = Get-ParentProcess -Child $proc -All $procs
        if ($parent) {
            if (((NullCoalesce $parent.Name)) -match 'winword|excel|powerpnt|outlook' -and ((NullCoalesce $proc.Name)) -match 'powershell|cmd|wscript|cscript') { $score += 5; $anomalies += 'OfficeSpawnScript' }
            if (((NullCoalesce $parent.Name)) -eq 'explorer.exe' -and ((NullCoalesce $proc.CommandLine)) -match '-w hidden|-windowstyle hidden|-nop|-enc') { $score += 4; $anomalies += 'ExplorerHiddenScript' }
            if (((NullCoalesce $parent.Name)) -eq 'svchost.exe') { $n = ((NullCoalesce $proc.Name)).ToLower(); if ($n -notmatch 'dllhost|conhost|rundll32') { $score += 3; $anomalies += 'SvchostUnexpectedChild' } }
        }
        $path = (NullCoalesce $proc.ExecutablePath)
        if ($path -and $path -match 'Users\\.*\\AppData|Users\\.*\\Downloads|Users\\.*\\Desktop' -and ((NullCoalesce $proc.Name)) -match '\.exe$') { $score += 2; $anomalies += 'UserDirExecution' }
        foreach ($sys in @('svchost.exe', 'lsass.exe', 'csrss.exe', 'smss.exe')) {
            if (((NullCoalesce $proc.Name)) -eq $sys -and $path -notmatch '\\Windows\\System32') { $score += 6; $anomalies += 'SystemBinaryWrongLocation'; break }
        }
        $cmd = (NullCoalesce $proc.CommandLine)
        if ($cmd -match '-enc |-encodedcommand |FromBase64String') { $score += 3; $anomalies += 'Base64Encoding' }
        if ($cmd -match '-exec bypass|-executionpolicy bypass|-ep bypass') { $score += 2; $anomalies += 'ExecutionPolicyBypass' }
        if ($cmd -match 'DownloadString|DownloadFile|WebClient|Invoke-WebRequest|wget |curl ') { $score += 3; $anomalies += 'DownloadCradle' }
        if ($score -ge 6) {
            Write-EdrLog -Source 'ProcessAnomalyDetection' -Message ("CRITICAL process anomaly - {0} (PID: {1}) | Parent: {2} | Score: {3} | {4} | {5} | {6}" -f $proc.Name, $proc.ProcessId, ((NullCoalesce $parent.Name)), $score, ($anomalies -join ', '), $path, $cmd) -Level THREAT -LogFile 'behavior_detections.log'
            $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $proc.ProcessId -ProcessName $proc.Name -ExePath $proc.ExecutablePath
        } elseif ($score -ge 3) { Write-EdrLog -Source 'ProcessAnomalyDetection' -Message ("Process anomaly - {0} (PID: {1}) | Score: {2} | {3}" -f $proc.Name, $proc.ProcessId, $score, ($anomalies -join ', ')) -Level WARNING -LogFile 'behavior_detections.log' }
    }
}

function Invoke-JobAMSIBypassDetection {
    $bypassPatterns = @('AmsiUtils', 'AmsiScanBuffer', 'amsiInitFailed', 'Bypass', 'amsi.dll', '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')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $name = ((NullCoalesce $p.Name)).ToLower()
        if ($name -notmatch 'powershell|pwsh|wscript|cscript') { continue }
        $cmd = (NullCoalesce $p.CommandLine); if (-not $cmd) { continue }
        foreach ($pat in $bypassPatterns) { if ($cmd -match $pat) { Write-EdrLog -Source 'AMSIBypassDetection' -Message ("AMSI BYPASS: {0} (PID: {1}) - {2}" -f $p.Name, $p.ProcessId, $pat) -Level THREAT -LogFile 'amsi_bypass_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath -Fileless; break } }
        if ($cmd -match '-enc' -and $cmd -match '-encodedcommand' -and $cmd.Length -gt 500) { Write-EdrLog -Source 'AMSIBypassDetection' -Message ("AMSI BYPASS (obfuscated): {0} (PID: {1}) long encoded command" -f $p.Name, $p.ProcessId) -Level THREAT -LogFile 'amsi_bypass_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath -Fileless }
    }
    if (Test-AmsiDisabledRegistry) { Write-EdrLog -Source 'AMSIBypassDetection' -Message 'AMSI BYPASS: Registry tampering HKLM\SOFTWARE\Microsoft\AMSI DisableAMSI' -Level THREAT -LogFile 'amsi_bypass_detections.log'; $script:ThreatCount++ }
}

function Invoke-JobCredentialDumpDetection {
    $self = $pid; $procs = Get-ProcessesWmi
    $credTools = @('mimikatz', 'sekurlsa', 'pwdump', 'gsecdump', 'wce', 'procdump', 'dumpert', 'nanodump', 'lsassy', 'lsadump', 'cachedump')
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $cmd = ((NullCoalesce $p.CommandLine)).ToLower(); $pname = ((NullCoalesce $p.Name)).ToLower()
        if ($cmd -match 'lsass') { Write-EdrLog -Source 'CredentialDumpDetection' -Message ("LSASS access - {0} (PID: {1}) | {2}" -f $p.Name, $p.ProcessId, $p.CommandLine) -Level THREAT -LogFile 'credential_dumping_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; continue }
        if ($cmd -match 'reg' -and ($cmd -match 'save' -or $cmd -match 'export') -and ($cmd -match 'sam' -or $cmd -match 'security' -or $cmd -match 'system')) { Write-EdrLog -Source 'CredentialDumpDetection' -Message ("Registry credential hive access - {0} (PID: {1}) | {2}" -f $p.Name, $p.ProcessId, $p.CommandLine) -Level THREAT -LogFile 'credential_dumping_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; continue }
        if ($cmd -match 'minidump|createdump|\.dmp') { Write-EdrLog -Source 'CredentialDumpDetection' -Message ("Memory dump creation - {0} (PID: {1}) | {2}" -f $p.Name, $p.ProcessId, $p.CommandLine) -Level WARNING -LogFile 'credential_dumping_detections.log'; continue }
        foreach ($tool in $credTools) { if ($pname -match $tool -or $cmd -match $tool) { Write-EdrLog -Source 'CredentialDumpDetection' -Message ("Credential dumping tool - {0} | {1} (PID: {2}) | {3}" -f $tool, $p.Name, $p.ProcessId, $p.CommandLine) -Level THREAT -LogFile 'credential_dumping_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobCredentialProtection {
    try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -Name 'RunAsPPL' -Value 1 -Type DWord -ErrorAction SilentlyContinue
        Write-EdrLog -Source 'CredentialProtection' -Message 'Enabled LSASS as Protected Process Light. Reboot required.' -Level INFO -LogFile 'credential_protection.log'
    } catch { Write-EdrLog -Source 'CredentialProtection' -Message "RunAsPPL: $($_.Exception.Message)" -Level ERROR -LogFile 'credential_protection.log' }
    try { & schtasks /delete /tn "GenerateRandomPassword" /f 2>$null } catch { }
    $scriptPath = Join-Path [Environment]::GetFolderPath('CommonApplicationData') 'PasswordTasks.ps1'
    if (Test-Path -LiteralPath $scriptPath) { Remove-Item -LiteralPath $scriptPath -Force -ErrorAction SilentlyContinue }
    $cmdkeyPath = Join-Path $env:SystemRoot 'System32\cmdkey.exe'
    if (Test-Path -LiteralPath $cmdkeyPath) { try { $o = & $cmdkeyPath /list 2>$null; if ($o) { Write-EdrLog -Source 'CredentialProtection' -Message 'Cleared cached credentials.' -Level INFO -LogFile 'credential_protection.log' } } catch { Write-EdrLog -Source 'CredentialProtection' -Message "cmdkey: $($_.Exception.Message)" -Level ERROR -LogFile 'credential_protection.log' } }
    try { Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name 'CachedLogonsCount' -Value '0' -Type String -ErrorAction SilentlyContinue; Write-EdrLog -Source 'CredentialProtection' -Message 'Disabled credential caching.' -Level INFO -LogFile 'credential_protection.log' } catch { Write-EdrLog -Source 'CredentialProtection' -Message "CachedLogonsCount: $($_.Exception.Message)" -Level ERROR -LogFile 'credential_protection.log' }
    try { & auditpol /set /subcategory:"Credential Validation" /success:enable /failure:enable 2>$null; Write-EdrLog -Source 'CredentialProtection' -Message 'Enabled auditing for credential validation.' -Level INFO -LogFile 'credential_protection.log' } catch { Write-EdrLog -Source 'CredentialProtection' -Message "auditpol: $($_.Exception.Message)" -Level ERROR -LogFile 'credential_protection.log' }
}

function Invoke-JobWMIPersistenceDetection {
    foreach ($f in Get-WmiEventFilters) { Write-EdrLog -Source 'WMIPersistenceDetection' -Message ("WMI Event filter: {0} | Query: {1}" -f $f.Name, $f.Query) -Level INFO -LogFile 'wmi_persistence.log' }
    foreach ($c in Get-WmiCommandLineConsumers) { Write-EdrLog -Source 'WMIPersistenceDetection' -Message ("WMI Command consumer: {0} | Command: {1}" -f $c.Name, $c.CommandLineTemplate) -Level INFO -LogFile 'wmi_persistence.log' }
}

function Invoke-JobScheduledTaskDetection {
    foreach ($t in Get-ScheduledTasksList) {
        if (-not (Test-ScheduledTaskSuspicious $t)) { continue }
        Write-EdrLog -Source 'ScheduledTaskDetection' -Message ("SUSPICIOUS ScheduledTask: {0} | Action: {1} | User: {2}" -f $t.TaskName, $t.Execute, $t.RunAsUser) -Level THREAT -LogFile 'scheduled_task_detections.log'
        $script:ThreatCount++
    }
}

function Invoke-JobRegistryPersistenceDetection {
    $suspicious = @('powershell.*-enc', 'cmd.*/c.*powershell', 'https?://', '\.vbs|\.js|\.bat|\.cmd', 'wscript|cscript|mshta', 'rundll32.*\.dll', 'regsvr32.*\.dll')
    foreach ($e in Get-RunEntriesRegistry) {
        $v = (NullCoalesce $e.Value)
        if (-not $v) { continue }
        foreach ($r in $suspicious) { if ($v -match $r) { Write-EdrLog -Source 'RegistryPersistenceDetection' -Message ("REGISTRY PERSISTENCE: {0} | {1} | {2}" -f $e.KeyName, $e.ValueName, $v) -Level THREAT -LogFile 'registry_persistence_detections.log'; $script:ThreatCount++; break } }
    }
}

function Invoke-JobDLLHijackingDetection {
    $self = $pid
    Get-Process -ErrorAction SilentlyContinue | ForEach-Object {
        if ($_.Id -eq $self) { return }
        try {
            foreach ($m in $_.Modules) {
                $path = $m.FileName
                if (-not $path -or $path -notmatch '\.dll') { continue }
                if (-not (Test-SuspiciousDllPath -Path $path)) { continue }
                Write-EdrLog -Source 'DLLHijackingDetection' -Message ("DLL HIJACKING: Suspicious DLL | {0} (PID: {1}) | {2}" -f $_.ProcessName, $_.Id, $path) -Level THREAT -LogFile 'dll_hijacking_detections.log'
                $script:ThreatCount++
                Stop-ProcessIfAllowed -ProcessId $_.Id -ProcessName $_.ProcessName -ExePath (Get-ProcessExecutablePathWmi -ProcessId $_.Id)
                break
            }
        } catch { } finally { $_.Dispose() }
    }
}

function Invoke-JobTokenManipulationDetection {
    $self = $pid; $win = [Environment]::GetFolderPath('Windows')
    Get-Process -ErrorAction SilentlyContinue | ForEach-Object {
        if ($_.Id -eq $self) { return }
        try {
            $path = $_.Path
            if (-not $path) { return }
            $domain, $user = Get-ProcessOwnerWmi -ProcessId $_.Id
            if (-not $domain -or $domain -notmatch 'NT AUTHORITY') { return }
            if ($path -like "$win*") { return }
            Write-EdrLog -Source 'TokenManipulationDetection' -Message ("SUSPICIOUS: Non-system binary as SYSTEM | {0} | {1}" -f $_.ProcessName, $path) -Level THREAT -LogFile 'token_manipulation.log'
            $script:ThreatCount++
            Stop-ProcessIfAllowed -ProcessId $_.Id -ProcessName $_.ProcessName -ExePath $path
        } catch { } finally { $_.Dispose() }
    }
}

function Invoke-JobProcessHollowingDetection {
    $suspParents = @('explorer.exe', 'winlogon.exe', 'services.exe')
    $suspChildren = @('notepad.exe', 'calc.exe', 'cmd.exe', 'powershell.exe', 'wmic.exe', 'rundll32.exe')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $path = (NullCoalesce $p.ExecutablePath); if (-not $path) { continue }
        $parent = Get-ParentProcess -Child $p -All $procs
        if ($parent -and $parent.Name -and ($suspParents -contains $parent.Name) -and $p.Name -and ($suspChildren -contains $p.Name)) {
            Write-EdrLog -Source 'ProcessHollowingDetection' -Message ("PROCESS HOLLOWING: Suspicious parent-child | {0} (PID: {1}) | Parent: {2}" -f $p.Name, $p.ProcessId, $parent.Name) -Level THREAT -LogFile 'process_hollowing_detections.log'
            $script:ThreatCount++
            Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath
        }
    }
}

function Invoke-JobKeyloggerDetection {
    $patterns = @('keylogger', 'keylog', 'keystroke', 'keyboard.*hook', 'GetAsyncKeyState', 'SetWindowsHookEx', 'WH_KEYBOARD')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $cmd = ((NullCoalesce $p.CommandLine)) + ' ' + ((NullCoalesce $p.Name))
        foreach ($pat in $patterns) { if ($cmd -match $pat) { Write-EdrLog -Source 'KeyloggerDetection' -Message ("KEYLOGGER: {0} (PID: {1}) | {2}" -f $p.Name, $p.ProcessId, $pat) -Level THREAT -LogFile 'keylogger_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobRansomwareDetection {
    $patterns = @('vssadmin delete shadows', 'vssadmin.exe delete', 'wbadmin delete catalog', 'bcdedit', 'shadow copy', 'shadowcopy', 'cryptolocker', 'wannacry', '.encrypted', '.locked', '.crypto')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $cmd = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($pat in $patterns) { if ($cmd -match $pat) { Write-EdrLog -Source 'RansomwareDetection' -Message ("RANSOMWARE: {0} (PID: {1}) | {2}" -f $p.Name, $p.ProcessId, $pat) -Level THREAT -LogFile 'ransomware_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobNetworkAnomalyDetection {
    try {
        $suspPorts = @(4444, 5555, 6666, 8080, 31337, 12345)
        $conns = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpConnections()
        foreach ($c in $conns) {
            if ($c.State -ne [System.Net.NetworkInformation.TcpState]::Established) { continue }
            $remotePort = $c.RemoteEndPoint.Port
            if ($suspPorts -contains $remotePort) { Write-EdrLog -Source 'NetworkAnomalyDetection' -Message ("Suspicious remote port {0} | Local: {1} -> {2}" -f $remotePort, $c.LocalEndPoint, $c.RemoteEndPoint) -Level WARNING -LogFile 'network_anomaly.log'; $script:ThreatCount++ }
        }
    } catch { Write-EdrLog -Source 'NetworkAnomalyDetection' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'network_anomaly.log' }
}

function Invoke-JobNetworkTrafficMonitoring {
    try {
        $props = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
        $listeners = $props.GetActiveTcpListeners().Count
        $conns = $props.GetActiveTcpConnections().Count
        Write-EdrLog -Source 'NetworkTrafficMonitoring' -Message "Listeners: $listeners | TCP connections: $conns" -Level INFO -LogFile 'network_traffic.log'
    } catch { Write-EdrLog -Source 'NetworkTrafficMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'network_traffic.log' }
}

function Invoke-JobRootkitDetection {
    try {
        $s = New-Object System.Management.ManagementObjectSearcher "SELECT Name,PathName,State FROM Win32_Service WHERE State = 'Running'"
        foreach ($o in $s.Get()) {
            $path = [string]$o['PathName']
            if (-not $path) { continue }
            if ($path -match 'system32' -or $path -match 'windows') { continue }
            Write-EdrLog -Source 'RootkitDetection' -Message ("Non-standard running service: {0} | {1}" -f $o['Name'], $path) -Level WARNING -LogFile 'rootkit_detections.log'
        }
    } catch { Write-EdrLog -Source 'RootkitDetection' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'rootkit_detections.log' }
}

function Invoke-JobCOMMonitoring {
    try {
        $s = New-Object System.Management.ManagementObjectSearcher 'SELECT Caption,Status FROM Win32_COMApplication'
        $n = 0; foreach ($o in $s.Get()) { $n++ }
        Write-EdrLog -Source 'COMMonitoring' -Message "COM applications enumerated: $n" -Level INFO -LogFile 'com_monitoring.log'
    } catch { Write-EdrLog -Source 'COMMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'com_monitoring.log' }
}

function Invoke-JobShadowCopyMonitoring {
    try {
        $o = & vssadmin list shadows 2>&1 | Out-String
        if ($o -match 'No items found') { Write-EdrLog -Source 'ShadowCopyMonitoring' -Message 'No shadow copies found (potential deletion attempt)' -Level WARNING -LogFile 'shadow_copy.log' }
        else { Write-EdrLog -Source 'ShadowCopyMonitoring' -Message 'Shadow copy check completed' -Level INFO -LogFile 'shadow_copy.log' }
    } catch { Write-EdrLog -Source 'ShadowCopyMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'shadow_copy.log' }
}

function Invoke-JobUSBMonitoring {
    try {
        $s = New-Object System.Management.ManagementObjectSearcher "SELECT Caption,DeviceID FROM Win32_DiskDrive WHERE InterfaceType = 'USB'"
        foreach ($o in $s.Get()) { Write-EdrLog -Source 'USBMonitoring' -Message ("USB drive: {0} | {1}" -f $o['Caption'], $o['DeviceID']) -Level INFO -LogFile 'usb_monitoring.log' }
    } catch { Write-EdrLog -Source 'USBMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'usb_monitoring.log' }
}

function Invoke-JobMobileDeviceMonitoring {
    try {
        $s = New-Object System.Management.ManagementObjectSearcher "SELECT Caption,DeviceID FROM Win32_PnPEntity WHERE Caption LIKE '%portable%' OR Caption LIKE '%USB%' OR Caption LIKE '%MTP%'"
        $n = 0; foreach ($o in $s.Get()) { $n++ }
        if ($n -gt 0) { Write-EdrLog -Source 'MobileDeviceMonitoring' -Message "Portable/MTP devices: $n" -Level INFO -LogFile 'mobile_device_monitoring.log' }
    } catch { Write-EdrLog -Source 'MobileDeviceMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'mobile_device_monitoring.log' }
}

function Invoke-JobAttackToolsDetection {
    $tools = @('mimikatz', 'pwdump', 'procdump', 'wce', 'gsecdump', 'cain', 'john', 'hashcat', 'hydra', 'medusa', 'nmap', 'metasploit', 'armitage')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $n = ((NullCoalesce $p.Name)).ToLower(); $c = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($t in $tools) { if ($n -match $t -or $c -match $t) { Write-EdrLog -Source 'AttackToolsDetection' -Message ("Attack tool: {0} | {1} (PID: {2})" -f $t, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'attack_tools_detection.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobAdvancedThreatDetection {
    $paths = @('C:\Windows\Temp', 'C:\Windows\System32\Tasks')
    $ext = @('.exe', '.dll', '.ps1', '.vbs')
    foreach ($basePath in $paths) {
        if (-not (Test-Path -LiteralPath $basePath)) { continue }
        try {
            foreach ($e in $ext) {
                Get-ChildItem -LiteralPath $basePath -Filter "*$e" -File -ErrorAction SilentlyContinue | ForEach-Object {
                    $ent = Measure-FileEntropy -Path $_.FullName
                    if ($ent -le 7.5) { return }
                    if (Move-QuarantineIfAllowed -Path $_.FullName -Reason 'AdvancedThreat') {
                        Write-EdrLog -Source 'AdvancedThreatDetection' -Message ("High-entropy file (CleanGuard): {0} | Entropy: {1:N2}" -f $_.FullName, $ent) -Level THREAT -LogFile 'advanced_threat_detection.log'
                        $script:ThreatCount++
                    }
                }
            }
        } catch { Write-EdrLog -Source 'AdvancedThreatDetection' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'advanced_threat_detection.log' }
    }
}

function Invoke-JobEventLogMonitoring {
    try {
        $log = New-Object System.Diagnostics.EventLog 'Application', '.'
        $n = $log.Entries.Count
        if ($n -gt 0) { $e = $log.Entries[$n - 1]; Write-EdrLog -Source 'EventLogMonitoring' -Message ("Last Application event: {0} | {1} | {2}" -f $e.TimeGenerated, $e.Source, $e.InstanceId) -Level INFO -LogFile 'eventlog_monitoring.log' }
    } catch { Write-EdrLog -Source 'EventLogMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'eventlog_monitoring.log' }
}

function Invoke-JobFirewallRuleMonitoring {
    try {
        $o = & netsh advfirewall firewall show rule name=all 2>&1 | Out-String
        $n = 0; foreach ($line in ($o -split "`n")) { if ($line.TrimStart() -match '^Rule Name:') { $n++ } }
        Write-EdrLog -Source 'FirewallRuleMonitoring' -Message "Firewall rules enumerated: $n" -Level INFO -LogFile 'firewall_monitoring.log'
    } catch { Write-EdrLog -Source 'FirewallRuleMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'firewall_monitoring.log' }
}

function Invoke-JobServiceMonitoring {
    try {
        $s = New-Object System.Management.ManagementObjectSearcher "SELECT Name,State,PathName FROM Win32_Service WHERE State = 'Running'"
        $n = 0; foreach ($o in $s.Get()) { $n++ }
        Write-EdrLog -Source 'ServiceMonitoring' -Message "Running services: $n" -Level INFO -LogFile 'service_monitoring.log'
    } catch { Write-EdrLog -Source 'ServiceMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'service_monitoring.log' }
}

function Invoke-JobFilelessDetection {
    $indicators = @('-enc ', '-encodedcommand', 'iex(', 'invoke-expression', 'frombase64string', 'scriptblock', 'reflection.assembly')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $n = ((NullCoalesce $p.Name)).ToLower(); if ($n -notmatch 'powershell|pwsh|wscript|cscript') { continue }
        $c = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($pat in $indicators) { if ($c -match $pat) { Write-EdrLog -Source 'FilelessDetection' -Message ("Fileless indicator: {0} | {1} (PID: {2})" -f $pat, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'fileless_detection.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath -Fileless; break } }
    }
}

function Invoke-JobMemoryScanning {
    try {
        $total = 0
        Get-Process -ErrorAction SilentlyContinue | ForEach-Object { try { $total += $_.WorkingSet64 } catch { } }
        Write-EdrLog -Source 'MemoryScanning' -Message ("Total working set (all processes): {0} MB" -f ([Math]::Round($total / 1MB))) -Level INFO -LogFile 'memory_scanning.log'
    } catch { Write-EdrLog -Source 'MemoryScanning' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'memory_scanning.log' }
}

function Invoke-JobNamedPipeMonitoring {
    $suspiciousPatterns = @('paexec', 'psexec', 'cobalt', 'mimikatz', 'remcom', 'evil', 'backdoor', 'shell', 'meterpreter', 'beacon', 'c2', 'lateral', 'dcom', 'wmi.*pipe', 'winlogon', 'lsass.*pipe', 'dcsync')
    try {
        $pipeNames = [System.IO.Directory]::GetFiles('\\.\pipe\')
        $total = $pipeNames.Count
        $suspicious = @()
        foreach ($full in $pipeNames) {
            $name = [System.IO.Path]::GetFileName($full)
            if (-not $name) { continue }
            $lower = $name.ToLower()
            foreach ($pat in $suspiciousPatterns) {
                if ($lower -match $pat) { $suspicious += $name; break }
            }
        }
        if ($suspicious.Count -gt 0) {
            foreach ($s in $suspicious) {
                Write-EdrLog -Source 'NamedPipeMonitoring' -Message "SUSPICIOUS named pipe: $s" -Level THREAT -LogFile 'named_pipe.log'
                $script:ThreatCount++
            }
            Write-EdrLog -Source 'NamedPipeMonitoring' -Message "Pipes: $total total | Suspicious: $($suspicious.Count) | $($suspicious -join ', ')" -Level WARNING -LogFile 'named_pipe.log'
        } else { Write-EdrLog -Source 'NamedPipeMonitoring' -Message "Pipes: $total total | No suspicious names" -Level INFO -LogFile 'named_pipe.log' }
    } catch { Write-EdrLog -Source 'NamedPipeMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'named_pipe.log' }
}

function Invoke-JobDNSExfiltrationDetection {
    try {
        $stats = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetUdpIPv4Statistics()
        Write-EdrLog -Source 'DNSExfiltrationDetection' -Message ("UDP stats (DNS proxy): Received {0} Sent {1}" -f $stats.DatagramsReceived, $stats.DatagramsSent) -Level INFO -LogFile 'dns_exfiltration.log'
    } catch { Write-EdrLog -Source 'DNSExfiltrationDetection' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'dns_exfiltration.log' }
}

function Invoke-JobBeaconDetection {
    try {
        $beaconPorts = @(4444, 5555, 6666, 8080, 443, 80)
        $conns = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpConnections()
        foreach ($c in $conns) {
            if ($c.State -ne [System.Net.NetworkInformation.TcpState]::Established) { continue }
            $remotePort = $c.RemoteEndPoint.Port
            if ($beaconPorts -contains $remotePort) { Write-EdrLog -Source 'BeaconDetection' -Message ("Potential beacon port {0} | {1}" -f $remotePort, $c.RemoteEndPoint) -Level WARNING -LogFile 'beacon_detections.log'; $script:ThreatCount++ }
        }
    } catch { Write-EdrLog -Source 'BeaconDetection' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'beacon_detections.log' }
}

function Invoke-JobCodeInjectionDetection {
    $patterns = @('VirtualAllocEx', 'WriteProcessMemory', 'CreateRemoteThread', 'NtCreateThreadEx', 'RtlCreateUserThread', 'QueueUserAPC')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $c = (NullCoalesce $p.CommandLine)
        foreach ($pat in $patterns) { if ($c -match $pat) { Write-EdrLog -Source 'CodeInjectionDetection' -Message ("CODE INJECTION: {0} | {1} (PID: {2})" -f $pat, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'code_injection_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobDataExfiltrationDetection {
    $patterns = @('upload', 'exfil', 'pastebin', 'transfer.*http', 'webclient.*upload', 'ftp.*put', 'scp ', 'curl.*-T')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $c = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($pat in $patterns) { if ($c -match $pat) { Write-EdrLog -Source 'DataExfiltrationDetection' -Message ("Data exfiltration: {0} | {1} (PID: {2})" -f $pat, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'data_exfiltration_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobFileEntropyDetection {
    foreach ($basePath in Get-SuspiciousScanPaths) {
        if (-not (Test-Path -LiteralPath $basePath)) { continue }
        try {
            Get-ChildItem -LiteralPath $basePath -Filter '*.exe' -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
                $e = Measure-FileEntropy -Path $_.FullName
                $len = Get-FileLength -Path $_.FullName
                if ($e -gt 7.5 -and $len -lt (5 * 1024 * 1024)) { Write-EdrLog -Source 'FileEntropyDetection' -Message ("High entropy: {0} | {1:N2}" -f $_.FullName, $e) -Level WARNING -LogFile 'file_entropy_detections.log' }
            }
        } catch { }
    }
}

function Invoke-JobHoneypotMonitoring {
    $path = Join-Path $script:InstallPath 'Data\honeypot'
    try {
        if (-not (Test-Path -LiteralPath $path)) { return }
        $n = (Get-ChildItem -LiteralPath $path -File -ErrorAction SilentlyContinue).Count
        if ($n -gt 0) { Write-EdrLog -Source 'HoneypotMonitoring' -Message "Honeypot files: $n" -Level INFO -LogFile 'honeypot.log' }
    } catch { Write-EdrLog -Source 'HoneypotMonitoring' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'honeypot.log' }
}

function Invoke-JobLateralMovementDetection {
    $patterns = @('psexec', 'wmic /node:', 'winrs ', 'sc \\\\', 'schtasks /s ', 'at \\\\', 'copy \\\\', 'net use')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $c = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($pat in $patterns) { if ($c -match $pat) { Write-EdrLog -Source 'LateralMovementDetection' -Message ("Lateral movement: {0} | {1} (PID: {2})" -f $pat, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'lateral_movement.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobProcessCreationDetection {
    $procs = Get-ProcessesWmi
    Write-EdrLog -Source 'ProcessCreationDetection' -Message ("Process count: {0}" -f $procs.Count) -Level INFO -LogFile 'process_creation_detections.log'
}

function Invoke-JobQuarantineManagement {
    try {
        if (-not (Test-Path -LiteralPath $script:QuarantinePath)) { return }
        $n = (Get-ChildItem -LiteralPath $script:QuarantinePath -File -ErrorAction SilentlyContinue).Count
        Write-EdrLog -Source 'QuarantineManagement' -Message "Quarantine count: $n" -Level INFO -LogFile 'quarantine_management.log'
    } catch { Write-EdrLog -Source 'QuarantineManagement' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'quarantine_management.log' }
}

function Invoke-JobReflectiveDLLInjectionDetection {
    $patterns = @('ReflectiveLoader', 'LoadLibraryR', 'LdrLoadDll', 'NtMapViewOfSection', 'VirtualAllocEx', 'WriteProcessMemory')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $c = (NullCoalesce $p.CommandLine)
        foreach ($pat in $patterns) { if ($c -match $pat) { Write-EdrLog -Source 'ReflectiveDLLInjectionDetection' -Message ("Reflective DLL: {0} | {1} (PID: {2})" -f $pat, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'reflective_dll_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobSimpleAntivirus {
    foreach ($path in Get-ElfDllsInBrowsers) {
        try {
            if (-not (Test-Path -LiteralPath $path)) { continue }
            Remove-Item -LiteralPath $path -Force -ErrorAction Stop
            Write-EdrLog -Source 'SimpleAntivirus' -Message "Removed ELF from browser: $path" -Level ACTION -LogFile 'simple_antivirus.log'
            $script:ThreatCount++
        } catch {
            if (Move-QuarantineIfAllowed -Path $path -Reason 'ELF') { Write-EdrLog -Source 'SimpleAntivirus' -Message "ELF remove failed, quarantined: $path | $($_.Exception.Message)" -Level WARNING -LogFile 'simple_antivirus.log' }
        }
    }
    $ext = @('.dll', '.winmd'); $maxUnsigned = 50; $unsignedCount = 0
    foreach ($basePath in Get-SuspiciousScanPaths) {
        if (-not (Test-Path -LiteralPath $basePath) -or $unsignedCount -ge $maxUnsigned) { break }
        try {
            foreach ($e in $ext) {
                Get-ChildItem -LiteralPath $basePath -Filter "*$e" -File -ErrorAction SilentlyContinue | ForEach-Object {
                    if ($unsignedCount -ge $maxUnsigned) { return }
                    if (-not (Test-Path -LiteralPath $_.FullName)) { return }
                    if (Test-WhitelistedPath -Path $_.FullName) { return }
                    if (Move-QuarantineIfAllowed -Path $_.FullName -Reason 'SimpleAntivirus') { Write-EdrLog -Source 'SimpleAntivirus' -Message "Malicious (API): $($_.FullName)" -Level THREAT -LogFile 'simple_antivirus.log'; $script:ThreatCount++; $unsignedCount++ }
                }
            }
        } catch { }
    }
}

function Invoke-JobResponseEngine {
    $ext = @('.exe', '.dll', '.sys', '.winmd'); $maxFiles = 100; $handled = 0
    foreach ($basePath in Get-SuspiciousScanPaths) {
        if ($handled -ge $maxFiles) { break }
        if (-not (Test-Path -LiteralPath $basePath)) { continue }
        try {
            foreach ($e in $ext) {
                Get-ChildItem -LiteralPath $basePath -Filter "*$e" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
                    if ($handled -ge $maxFiles) { return }
                    if (-not (Test-Path -LiteralPath $_.FullName)) { return }
                    if (Test-WhitelistedPath -Path $_.FullName) { return }
                    if (Move-QuarantineIfAllowed -Path $_.FullName -Reason 'ResponseEngine') { Write-EdrLog -Source 'ResponseEngine' -Message "CleanGuard Malicious -> quarantined $($_.FullName)" -Level THREAT -LogFile 'response_engine.log'; $script:ThreatCount++; $handled++ }
                }
            }
        } catch { }
    }
    Write-EdrLog -Source 'ResponseEngine' -Message ("Tick | Threats: {0} | Terminated: {1} | Quarantined: {2} | Handled: {3}" -f $script:ThreatCount, $script:ProcessesTerminated, $script:FilesQuarantined, $handled) -Level INFO -LogFile 'response_engine.log'
}

function Invoke-JobPrivacyForgeSpoofing {
    $systemNames = @('svchost.exe', 'lsass.exe', 'csrss.exe', 'smss.exe', 'winlogon.exe', 'services.exe', 'lsm.exe')
    $self = $pid
    try {
        $procs = Get-ProcessesWmi
        foreach ($p in $procs) {
            if ($p.ProcessId -eq $self) { continue }
            $name = (NullCoalesce $p.Name)
            $path = (NullCoalesce $p.ExecutablePath)
            $isSystemNamed = $false
            foreach ($sys in $systemNames) { if ($name -eq $sys) { $isSystemNamed = $true; break } }
            if (-not $isSystemNamed) { continue }
            if (-not $path) { Write-EdrLog -Source 'PrivacyForgeSpoofing' -Message "System-named process with no path: $name (PID: $($p.ProcessId))" -Level WARNING -LogFile 'privacy_forge.log'; continue }
            $pathLower = $path.ToLower()
            $fromSystem32 = $pathLower -match '\\windows\\system32\\'
            $fromSysWOW64 = $pathLower -match '\\windows\\syswow64\\'
            if (-not $fromSystem32 -and -not $fromSysWOW64) {
                Write-EdrLog -Source 'PrivacyForgeSpoofing' -Message "SPOOFING: System binary not in System32/SysWOW64 | $name (PID: $($p.ProcessId)) | Path: $path" -Level THREAT -LogFile 'privacy_forge.log'
                $script:ThreatCount++
            }
            $parent = Get-ParentProcess -Child $p -All $procs
            if ($parent) {
                $parentPath = (NullCoalesce $parent.ExecutablePath)
                $parentName = (NullCoalesce $parent.Name)
                if ($parentPath -and $parentName -match 'explorer|svchost') {
                    $parentLower = $parentPath.ToLower()
                    if ($parentLower -notmatch '\\windows\\' -and $parentLower -notmatch '\\program files') {
                        Write-EdrLog -Source 'PrivacyForgeSpoofing' -Message "SPOOFING: Suspicious parent path for $parentName | Child: $name (PID: $($p.ProcessId)) | Parent path: $parentPath" -Level THREAT -LogFile 'privacy_forge.log'
                        $script:ThreatCount++
                    }
                }
            }
        }
        Write-EdrLog -Source 'PrivacyForgeSpoofing' -Message "Spoofing check completed (system-named processes and parent paths)" -Level INFO -LogFile 'privacy_forge.log'
    } catch { Write-EdrLog -Source 'PrivacyForgeSpoofing' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'privacy_forge.log' }
}

function Invoke-JobGFocus {
    try {
        $conns = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().GetActiveTcpConnections()
        $est = ($conns | Where-Object { $_.State -eq [System.Net.NetworkInformation.TcpState]::Established }).Count
        Write-EdrLog -Source 'GFocus' -Message "Established TCP connections: $est" -Level INFO -LogFile 'gfocus.log'
    } catch { Write-EdrLog -Source 'GFocus' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'gfocus.log' }
}

function Invoke-JobMitreMapping {
    $sourceToMitre = @{
        'HashDetection' = @('T1204.002')
        'LOLBinDetection' = @('T1218')
        'ProcessAnomalyDetection' = @('T1059.001', 'T1047')
        'AMSIBypassDetection' = @('T1562.001')
        'CredentialDumpDetection' = @('T1003.001', 'T1003.002')
        'RansomwareDetection' = @('T1486')
        'RegistryPersistenceDetection' = @('T1547.001')
        'ScheduledTaskDetection' = @('T1053.005')
        'DLLHijackingDetection' = @('T1574.001')
        'KeyloggerDetection' = @('T1056.001')
        'NamedPipeMonitoring' = @('T1570')
        'PrivacyForgeSpoofing' = @('T1036.005')
        'IdsDetection' = @('T1059.001', 'T1047')
        'WMIPersistenceDetection' = @('T1047')
        'TokenManipulationDetection' = @('T1134')
        'ProcessHollowingDetection' = @('T1055.012')
        'FilelessDetection' = @('T1059.001')
        'LateralMovementDetection' = @('T1021.002')
    }
    $techniquesSeen = @{}
    try {
        if (Test-Path -LiteralPath $script:LogPath) {
            Get-ChildItem -LiteralPath $script:LogPath -Filter '*detections*.log' -ErrorAction SilentlyContinue | ForEach-Object {
                Get-Content -LiteralPath $_.FullName -Tail 100 -ErrorAction SilentlyContinue | ForEach-Object {
                    if ($_ -match '\[THREAT\]\s*\[([^\]]+)\]') {
                        $src = $Matches[1]
                        if ($sourceToMitre.ContainsKey($src)) {
                            foreach ($t in $sourceToMitre[$src]) { $techniquesSeen[$t] = $true }
                        }
                    }
                }
            }
        }
        $techList = @($techniquesSeen.Keys) | Sort-Object
        $msg = "ThreatCount: $($script:ThreatCount)"
        if ($techList.Count -gt 0) { $msg += " | Techniques: $($techList -join ', ')" }
        Write-EdrLog -Source 'MitreMapping' -Message $msg -Level INFO -LogFile 'mitre_detections.log'
    } catch { Write-EdrLog -Source 'MitreMapping' -Message "Error: $($_.Exception.Message)" -Level ERROR -LogFile 'mitre_detections.log' }
}

function Invoke-JobIdsDetection {
    $sigs = @('meterpreter', 'reverse_shell', 'bind_shell', 'exploit/', 'payload', 'cmd.exe /c', 'powershell -enc', 'rundll32 javascript:', 'mshta http', 'certutil -urlcache', 'bitsadmin /transfer', 'regsvr32 /s /n /u', 'Invoke-Mimikatz', 'sekurlsa::', ' Invoke-WebRequest ', 'Net.WebClient')
    $self = $pid; $procs = Get-ProcessesWmi
    foreach ($p in $procs) {
        if ($p.ProcessId -eq $self) { continue }
        $cmd = ((NullCoalesce $p.CommandLine)).ToLower()
        foreach ($sig in $sigs) { if ($cmd -match $sig) { Write-EdrLog -Source 'IdsDetection' -Message ("IDS signature: {0} | {1} (PID: {2})" -f $sig, $p.Name, $p.ProcessId) -Level THREAT -LogFile 'ids_detections.log'; $script:ThreatCount++; Stop-ProcessIfAllowed -ProcessId $p.ProcessId -ProcessName $p.Name -ExePath $p.ExecutablePath; break } }
    }
}

function Invoke-JobYaraDetection {
    $baseDir = [System.IO.Path]::GetDirectoryName((Get-CurrentScriptPath))
    $yaraExe = $null
    if (Test-Path -LiteralPath (Join-Path $baseDir $script:YaraExeName)) { $yaraExe = Join-Path $baseDir $script:YaraExeName }
    if (-not $yaraExe -and (Test-Path -LiteralPath (Join-Path (Join-Path $script:InstallPath $script:YaraSubFolder) $script:YaraExeName))) { $yaraExe = Join-Path (Join-Path $script:InstallPath $script:YaraSubFolder) $script:YaraExeName }
    $rulesPath = $null
    if (Test-Path -LiteralPath (Join-Path $baseDir $script:YaraRulesFileName)) { $rulesPath = Join-Path $baseDir $script:YaraRulesFileName }
    if (-not $rulesPath -and (Test-Path -LiteralPath (Join-Path $script:DataPath $script:YaraRulesFileName))) { $rulesPath = Join-Path $script:DataPath $script:YaraRulesFileName }
    if (-not $yaraExe -or -not $rulesPath) { Write-EdrLog -Source 'YaraDetection' -Message "YARA skipped: yara.exe or rules.yar not found." -Level INFO -LogFile 'yara_detections.log'; return }
    $files = @()
    foreach ($path in (Get-SuspiciousFiles)) {
        if ($files.Count -ge 200) { break }
        if (Test-WhitelistedPath -Path $path) { continue }
        try { if ((Test-Path -LiteralPath $path) -and (Get-Item -LiteralPath $path).Length -gt 0 -and (Get-Item -LiteralPath $path).Length -lt 50MB) { $files += $path } } catch { }
    }
    if ($files.Count -eq 0) { Write-EdrLog -Source 'YaraDetection' -Message 'YARA scan: no files in scope.' -Level INFO -LogFile 'yara_detections.log'; return }
    $batchSize = 40; $matches = 0; $yaraDir = [System.IO.Path]::GetDirectoryName($yaraExe)
    $stdoutPath = Join-Path $script:LogPath 'yara_stdout.txt'; $stderrPath = Join-Path $script:LogPath 'yara_stderr.txt'
    for ($i = 0; $i -lt $files.Count; $i += $batchSize) {
        $batch = $files[$i..([Math]::Min($i + $batchSize - 1, $files.Count - 1))]
        $yaraArgs = @("`"$rulesPath`"") + ($batch | ForEach-Object { "`"$_`"" })
        try {
            $proc = Start-Process -FilePath $yaraExe -ArgumentList $yaraArgs -WorkingDirectory $yaraDir -NoNewWindow -PassThru -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
            $proc.WaitForExit(60000)
            $stdout = Get-Content -LiteralPath $stdoutPath -ErrorAction SilentlyContinue
            foreach ($line in $stdout) {
                $trimmed = $line.Trim()
                if (-not $trimmed) { continue }
                if ($trimmed -match '^(\S+)\s+(.+)$') {
                    $ruleName = $Matches[1]; $filePath = $Matches[2].Trim()
                    Write-EdrLog -Source 'YaraDetection' -Message ("YARA match: rule=`"$ruleName`" file=`"$filePath`"") -Level THREAT -LogFile 'yara_detections.log'
                    $matches++
                    if (Move-QuarantineIfAllowed -Path $filePath -Reason "YARA:$ruleName") { $script:ThreatCount++ }
                }
            }
        } catch { Write-EdrLog -Source 'YaraDetection' -Message "YARA run error: $($_.Exception.Message)" -Level ERROR -LogFile 'yara_detections.log' }
    }
    Write-EdrLog -Source 'YaraDetection' -Message ("YARA scan completed. Files in scope: $($files.Count), matches: $matches.") -Level $(if ($matches -gt 0) { 'THREAT' } else { 'INFO' }) -LogFile 'yara_detections.log'
}

# --- Job runner: each job is { Name, IntervalSeconds }; timer invokes Invoke-Job<Name> ---
$script:Jobs = @(
    @{ Name = 'HashDetection';               IntervalSeconds = 15 }
    @{ Name = 'LOLBinDetection';              IntervalSeconds = 60 }
    @{ Name = 'ProcessAnomalyDetection';      IntervalSeconds = 30 }
    @{ Name = 'AMSIBypassDetection';          IntervalSeconds = 60 }
    @{ Name = 'CredentialDumpDetection';      IntervalSeconds = 45 }
    @{ Name = 'CredentialProtection';          IntervalSeconds = 300 }
    @{ Name = 'WMIPersistenceDetection';      IntervalSeconds = 60 }
    @{ Name = 'ScheduledTaskDetection';        IntervalSeconds = 120 }
    @{ Name = 'RegistryPersistenceDetection'; IntervalSeconds = 60 }
    @{ Name = 'DLLHijackingDetection';        IntervalSeconds = 90 }
    @{ Name = 'TokenManipulationDetection';   IntervalSeconds = 45 }
    @{ Name = 'ProcessHollowingDetection';     IntervalSeconds = 30 }
    @{ Name = 'KeyloggerDetection';           IntervalSeconds = 60 }
    @{ Name = 'RansomwareDetection';          IntervalSeconds = 30 }
    @{ Name = 'NetworkAnomalyDetection';      IntervalSeconds = 60 }
    @{ Name = 'NetworkTrafficMonitoring';     IntervalSeconds = 45 }
    @{ Name = 'RootkitDetection';             IntervalSeconds = 120 }
    @{ Name = 'COMMonitoring';                IntervalSeconds = 60 }
    @{ Name = 'ShadowCopyMonitoring';         IntervalSeconds = 90 }
    @{ Name = 'USBMonitoring';                 IntervalSeconds = 30 }
    @{ Name = 'MobileDeviceMonitoring';       IntervalSeconds = 120 }
    @{ Name = 'AttackToolsDetection';         IntervalSeconds = 60 }
    @{ Name = 'AdvancedThreatDetection';      IntervalSeconds = 45 }
    @{ Name = 'EventLogMonitoring';           IntervalSeconds = 60 }
    @{ Name = 'FirewallRuleMonitoring';       IntervalSeconds = 90 }
    @{ Name = 'ServiceMonitoring';            IntervalSeconds = 60 }
    @{ Name = 'FilelessDetection';            IntervalSeconds = 60 }
    @{ Name = 'MemoryScanning';               IntervalSeconds = 90 }
    @{ Name = 'NamedPipeMonitoring';          IntervalSeconds = 45 }
    @{ Name = 'DNSExfiltrationDetection';     IntervalSeconds = 60 }
    @{ Name = 'BeaconDetection';               IntervalSeconds = 45 }
    @{ Name = 'CodeInjectionDetection';       IntervalSeconds = 30 }
    @{ Name = 'DataExfiltrationDetection';    IntervalSeconds = 60 }
    @{ Name = 'FileEntropyDetection';         IntervalSeconds = 60 }
    @{ Name = 'HoneypotMonitoring';           IntervalSeconds = 60 }
    @{ Name = 'LateralMovementDetection';     IntervalSeconds = 60 }
    @{ Name = 'ProcessCreationDetection';     IntervalSeconds = 30 }
    @{ Name = 'QuarantineManagement';         IntervalSeconds = 120 }
    @{ Name = 'ReflectiveDLLInjectionDetection'; IntervalSeconds = 45 }
    @{ Name = 'SimpleAntivirus';               IntervalSeconds = 60 }
    @{ Name = 'ResponseEngine';                IntervalSeconds = 30 }
    @{ Name = 'PrivacyForgeSpoofing';         IntervalSeconds = 90 }
    @{ Name = 'GFocus';                        IntervalSeconds = 60 }
    @{ Name = 'MitreMapping';                  IntervalSeconds = 120 }
    @{ Name = 'IdsDetection';                  IntervalSeconds = 45 }
    @{ Name = 'YaraDetection';                 IntervalSeconds = 60 }
)

$script:JobTimers = @{}
$script:JobRunnerStopped = $false

function Start-JobRunner {
    $script:JobRunnerStopped = $false
    foreach ($j in $script:Jobs) {
        $intervalMs = [Math]::Max(1, $j.IntervalSeconds) * 1000
        $name = $j.Name
        $timer = New-Object System.Timers.Timer $intervalMs
        $timer.AutoReset = $true
        Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
            if ($script:JobRunnerStopped) { return }
            $jobName = $Event.MessageData
            try {
                $fn = Get-Command -Name "Invoke-Job$jobName" -ErrorAction SilentlyContinue
                if ($fn) { & $fn.Name } else { Write-EdrLog -Source $jobName -Message "$jobName tick" -Level INFO -LogFile "${jobName}.log" }
            } catch {
                Write-EdrLog -Source $jobName -Message "Error: $($_.Exception.Message)" -Level ERROR
            }
        } -MessageData $name | Out-Null
        $timer.Start()
        $script:JobTimers[$name] = $timer
    }
    Write-EdrLog -Source 'JobRunner' -Message "Started $($script:Jobs.Count) jobs."
}

function Stop-JobRunner {
    $script:JobRunnerStopped = $true
    foreach ($t in $script:JobTimers.Values) {
        if ($t) { try { $t.Stop(); $t.Dispose() } catch { } }
    }
    $script:JobTimers.Clear()
}

# --- Main (no tray/dashboard; auto-install and persist unless -Uninstall) ---
if ($Uninstall) {
    $err = Uninstall-Edr
    if ($err) { Write-Warning $err; exit 1 }
    Write-Host 'Uninstalled: persistence removed and install folder deleted.'
    exit 0
}

# Auto-install and persist when running normally
if (-not (Test-EdrInstalled)) {
    $err = Install-Edr
    if ($err) { Write-Warning "Install failed: $err"; exit 1 }
    Write-EdrLog -Source 'Main' -Message 'Auto-installed to ' + $script:InstallPath -Level INFO
}
if (-not (Test-EdrPersisted)) {
    $err = Enable-EdrPersistence
    if ($err) { Write-Warning "Persistence failed: $err" }
    else { Write-EdrLog -Source 'Main' -Message 'Auto-persistence enabled (HKCU Run).' -Level INFO }
}

Start-JobRunner
Write-EdrLog -Source 'Main' -Message 'Antivirus started (no UI).' -Level INFO

# Keep process alive (job timers run in background)
try {
    while ($true) { Start-Sleep -Seconds 3600 }
} finally {
    Stop-JobRunner
}

Editor is loading...
Leave a Comment