Detection/Find-SuspiciousProcesses.ps1

function Find-SuspiciousProcesses {
    <#
    .SYNOPSIS
        Detects suspicious running processes on the system
    .DESCRIPTION
        Identifies potentially malicious processes based on various indicators including
        unsigned binaries, unusual locations, suspicious names, and abnormal behavior
    .PARAMETER CheckSignatures
        Verify digital signatures of process executables
    .PARAMETER CheckNetwork
        Include network connection analysis
    .EXAMPLE
        Find-SuspiciousProcesses
        Find-SuspiciousProcesses -CheckSignatures -CheckNetwork
    .OUTPUTS
        Array of suspicious process objects
    #>

    
    [CmdletBinding()]
    param(
        [Parameter()]
        [switch]$CheckSignatures,
        
        [Parameter()]
        [switch]$CheckNetwork
    )
    
    begin {
        Write-Host "Scanning for suspicious processes..." -ForegroundColor Cyan
        
        $suspiciousProcesses = @()
        
        # Suspicious process names and patterns
        $suspiciousNames = @(
            'mimikatz', 'procdump', 'pwdump', 'htran', 'psexec',
            'netcat', 'ncat', 'nc64', 'nmap', 'tcpdump',
            'wireshark', 'ettercap', 'cain', 'john', 'hashcat',
            'meterpreter', 'cobalt', 'beacon', 'empire', 'covenant'
        )
        
        # Suspicious paths
        $suspiciousPaths = @(
            '\\Temp\\',
            '\\AppData\\Local\\Temp\\',
            '\\Users\\Public\\',
            '\\ProgramData\\.*\\.\\',
            '\\Windows\\Tasks\\'
        )
        
        # Legitimate system processes that should only run from System32
        $systemProcesses = @(
            'svchost', 'lsass', 'services', 'csrss', 'winlogon',
            'smss', 'wininit', 'taskhost', 'dwm'
        )
    }
    
    process {
        try {
            # Get all running processes with detailed info
            # -IncludeUserName requires elevation; fall back to a plain listing so a
            # non-elevated run still scans processes instead of silently returning nothing
            $processes = @(Get-Process -IncludeUserName -ErrorAction SilentlyContinue)
            $includeUserNames = $true
            if ($processes.Count -eq 0) {
                $processes = @(Get-Process -ErrorAction SilentlyContinue)
                $includeUserNames = $false
            }
            
            foreach ($proc in $processes) {
                if ($null -eq $proc) { continue }
                $suspicionScore = 0
                $suspicionReasons = @()
                
                try {
                    $processPath = $proc.Path
                    $processName = $proc.ProcessName.ToLower()
                    
                    # Skip if no path (system processes)
                    if (-not $processPath) {
                        continue
                    }

                    $cimProcess = Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" -ErrorAction SilentlyContinue

                    # Check 1: Suspicious process names
                    foreach ($suspName in $suspiciousNames) {
                        if ($processName -match $suspName) {
                            $suspicionScore += 50
                            $suspicionReasons += "Matches known suspicious tool name: $suspName"
                            break
                        }
                    }
                    
                    # Check 2: System process in wrong location
                    if ($systemProcesses -contains $processName) {
                        if ($processPath -notmatch '\\Windows\\System32\\') {
                            $suspicionScore += 80
                            $suspicionReasons += "System process running from non-standard location"
                        }
                    }
                    
                    # Check 3: Running from suspicious paths
                    foreach ($suspPath in $suspiciousPaths) {
                        if ($processPath -match $suspPath) {
                            $suspicionScore += 40
                            $suspicionReasons += "Running from suspicious location: $suspPath"
                            break
                        }
                    }
                    
                    # Check 4: No file description
                    try {
                        $fileInfo = Get-Item -Path $processPath -ErrorAction Stop
                        if (-not $fileInfo.VersionInfo.FileDescription) {
                            $suspicionScore += 20
                            $suspicionReasons += "No file description"
                        }
                        
                        # Check 5: Very recent file creation (last 24 hours)
                        if ($fileInfo.CreationTime -gt (Get-Date).AddDays(-1)) {
                            $suspicionScore += 30
                            $suspicionReasons += "Executable created within last 24 hours"
                        }
                    }
                    catch {
                        $suspicionScore += 30
                        $suspicionReasons += "Cannot access executable file"
                    }
                    
                    # Check 6: Unusual parent-child relationship
                    try {
                        $parentProcess = Get-Process -Id $cimProcess.ParentProcessId -ErrorAction Stop
                        
                        # PowerShell/cmd spawned by Office apps
                        if ($parentProcess.ProcessName -match '(WINWORD|EXCEL|POWERPNT|OUTLOOK)' -and 
                            $processName -match '(powershell|cmd|wscript|cscript|mshta)') {
                            $suspicionScore += 60
                            $suspicionReasons += "Scripting engine spawned by Office application"
                        }
                        
                        # Browser spawning suspicious processes
                        if ($parentProcess.ProcessName -match '(chrome|firefox|iexplore|msedge)' -and
                            $processName -match '(powershell|cmd|wscript|cscript)') {
                            $suspicionScore += 50
                            $suspicionReasons += "Scripting engine spawned by browser"
                        }
                    }
                    catch {
                        # Parent process no longer exists or inaccessible
                    }
                    
                    # Check 7: High CPU or memory usage for unknown process
                    if ($proc.CPU -gt 50 -and $processPath -notmatch '(Microsoft|Windows|Program Files)') {
                        $suspicionScore += 20
                        $suspicionReasons += "High CPU usage for non-standard process"
                    }
                    
                    # Check 8: Multiple instances of same process with different paths
                    $sameNameProcs = $processes | Where-Object { 
                        $_.ProcessName -eq $proc.ProcessName -and $_.Path -ne $proc.Path 
                    }
                    if ($sameNameProcs) {
                        $suspicionScore += 30
                        $suspicionReasons += "Multiple instances with different paths"
                    }
                    
                    # Check 9: Digital signature verification (if requested)
                    if ($CheckSignatures) {
                        try {
                            $signature = Get-AuthenticodeSignature -FilePath $processPath -ErrorAction Stop
                            if ($signature.Status -ne 'Valid') {
                                $suspicionScore += 40
                                $suspicionReasons += "Invalid or missing digital signature: $($signature.Status)"
                            }
                        }
                        catch {
                            $suspicionScore += 30
                            $suspicionReasons += "Could not verify signature"
                        }
                    }
                    
                    # Check 10: Network connections (if requested)
                    if ($CheckNetwork) {
                        try {
                            $connections = Get-NetTCPConnection -OwningProcess $proc.Id -ErrorAction SilentlyContinue |
                                Where-Object { $_.State -eq 'Established' }
                            
                            if ($connections) {
                                # Check for connections to uncommon ports
                                $uncommonPorts = $connections | Where-Object { 
                                    $_.RemotePort -notin @(80, 443, 53, 22, 21, 25, 110, 143, 993, 995, 3389)
                                }
                                
                                if ($uncommonPorts) {
                                    $suspicionScore += 25
                                    $suspicionReasons += "Network connections to uncommon ports: $(($uncommonPorts.RemotePort -join ', '))"
                                }
                            }
                        }
                        catch {
                            # Could not get network connections
                        }
                    }
                    
                    # If suspicion score is high enough, add to results
                    if ($suspicionScore -ge 40) {
                        # StartTime/CPU getters throw for protected processes; capture them
                        # safely so an inaccessible property never drops a flagged process
                        $procStartTime = $null
                        try { $procStartTime = $proc.StartTime } catch { $procStartTime = $null }
                        $procCpu = $null
                        try { $procCpu = $proc.CPU } catch { $procCpu = $null }

                        $suspiciousProcesses += [PSCustomObject]@{
                            ProcessName = $proc.ProcessName
                            ProcessId = $proc.Id
                            Path = $processPath
                            CommandLine = if ($cimProcess) { $cimProcess.CommandLine } else { 'N/A' }
                            UserName = if ($includeUserNames) { $proc.UserName } else { 'N/A' }
                            StartTime = $procStartTime
                            CPU = if ($null -ne $procCpu) { [math]::Round($procCpu, 2) } else { $null }
                            WorkingSet = [math]::Round($proc.WorkingSet64 / 1MB, 2)
                            SuspicionScore = $suspicionScore
                            Reasons = ($suspicionReasons -join '; ')
                            ParentProcess = try { (Get-Process -Id $cimProcess.ParentProcessId -ErrorAction Stop).ProcessName } catch { 'N/A' }
                        }
                    }
                }
                catch {
                    $failedProcName = if ($null -ne $proc -and $proc.PSObject.Properties['ProcessName']) { $proc.ProcessName } else { 'unknown' }
                    Write-Verbose "Error processing process ${failedProcName}: $_"
                }
            }
            
        }
        catch {
            Write-Error "Error during process scan: $_"
            throw
        }
    }
    
    end {
        Write-Host "`nFound $($suspiciousProcesses.Count) suspicious processes" -ForegroundColor $(if ($suspiciousProcesses.Count -gt 0) { 'Red' } else { 'Green' })
        
        return $suspiciousProcesses | Sort-Object -Property SuspicionScore -Descending
    }
}