Detector PowerShell de segurança em conexões TCP/UDP.

Funcionalidades Principais
 
Uma Arquitetura Inteligente e Modular
 
Links
 

O tópico a seguir tem conteúdo extenso e avançado!
O tópico a seguir tem conteúdo extenso e avançado sobre todos os arquivos do projeto e pode ter alterações com o tempo, recomendado visualizar o código pelo Repositório no Github!
Explicando o Código
 
start.ps1
[CmdletBinding()]
param()

try {
	$scriptDir = $PSScriptRoot
	if (-not $scriptDir -or [string]::IsNullOrWhiteSpace($scriptDir)) {
		$def = $MyInvocation.MyCommand.Definition
		if ([string]::IsNullOrWhiteSpace($def)) {
			# Fallback to current location
			$scriptDir = (Get-Location).ProviderPath
		}
		else {
			$scriptDir = Split-Path -Path $def -Parent
		}
	}
	$target = Join-Path -Path $scriptDir -ChildPath 'connector-detector.ps1'
	if (-not (Test-Path -Path $target -PathType Leaf)) {
		Write-Error "Could not find script: $target. Ensure 'connector-detector.ps1' is in the same folder as this script."
		exit 2
	}
	$oldEAP = $ErrorActionPreference
	$ErrorActionPreference = 'Stop'
	try {
		Write-Verbose "Invoking: $target with args: $($args -join ' ')"
		& $target @args
		$succeeded = $true
	}
	catch {
		Write-Error "Execution of connector-detector.ps1 failed: $_"
		$succeeded = $false
	}
	finally {
		$ErrorActionPreference = $oldEAP
	}
	if ($succeeded) { exit 0 } else { exit 1 }
}
catch {
	Write-Error "Launcher failure: $_"
	exit 1
}

connector-language.ps1
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
if ([string]::IsNullOrEmpty($scriptDir)) { $scriptDir = $PSScriptRoot }
if ([string]::IsNullOrEmpty($scriptDir)) { $scriptDir = (Get-Location).Path }

$Global:__LanguageCode = 'en'
$Global:__LanguageData = @{}
$Global:__LanguageBase = @{} # baseline (en) keys/values for validation/fallback

function Get-LocaleDirectory {
    $basePath = Join-Path -Path $scriptDir -ChildPath 'locales'
    return $basePath
}

function Initialize-LanguageBase {
    if ($Global:__LanguageBase.Count -gt 0) { return }
    $basePath = Get-LocaleDirectory
    $enPath = Join-Path $basePath 'en.json'
    if (Test-Path $enPath) {
        try {
            $json = Get-Content -Raw -Path $enPath | ConvertFrom-Json -ErrorAction Stop
            $tmp = @{}
            foreach ($p in $json.PSObject.Properties) { $tmp[$p.Name] = $p.Value }
            $Global:__LanguageBase = $tmp
        } catch {
            # If en.json cannot be read, keep empty baseline; validation will be skipped
            Write-Verbose "Failed to load baseline 'en.json': $_"
            $Global:__LanguageBase = @{}
        }
    }
}

function Test-LanguageData {
    param(
        [Parameter(Mandatory=$true)][hashtable]$Data
    )
    Initialize-LanguageBase
    $missing = @()
    $empty = @()
    if ($Global:__LanguageBase.Count -eq 0) {
        # No baseline available; consider valid
        return [PSCustomObject]@{ IsValid = $true; MissingKeys = @(); EmptyKeys = @() }
    }

    foreach ($k in $Global:__LanguageBase.Keys) {
        if (-not $Data.ContainsKey($k)) { $missing += $k; continue }
        $v = $Data[$k]
        if (-not ($v -is [string]) -or [string]::IsNullOrWhiteSpace([string]$v)) { $empty += $k }
    }
    $isValid = ($missing.Count -eq 0 -and $empty.Count -eq 0)
    return [PSCustomObject]@{
        IsValid = $isValid
        MissingKeys = $missing
        EmptyKeys = $empty
    }
}

function Get-LanguageDisplayName {
    param(
        [Parameter(Mandatory=$true)][string]$Code,
        [hashtable]$RawData
    )
    # Try to read name metadata if present
    if ($RawData) {
        if ($RawData.ContainsKey('LanguageName') -and -not [string]::IsNullOrWhiteSpace($RawData['LanguageName'])) {
            return $RawData['LanguageName']
        }
        if ($RawData.ContainsKey('_meta.name') -and -not [string]::IsNullOrWhiteSpace($RawData['_meta.name'])) {
            return $RawData['_meta.name']
        }
    }
    $map = @{
        'en'   = 'English'
        'es'   = 'Español'
        'pt'   = 'Português'
        'pt-BR'= 'Português (Brasil)'
    }
    if ($map.ContainsKey($Code)) { return $map[$Code] }
    return $Code
}

function Get-AvailableLanguages {
    param(
        [switch]$ValidOnly
    )
    Initialize-LanguageBase
    $dir = Get-LocaleDirectory
    if (-not (Test-Path $dir)) { return @() }
    $items = Get-ChildItem -Path $dir -Filter '*.json' -File -ErrorAction SilentlyContinue
    $list = @()
    foreach ($it in $items) {
        $code = [System.IO.Path]::GetFileNameWithoutExtension($it.FullName)
        try {
            $rawJson = Get-Content -Raw -Path $it.FullName | ConvertFrom-Json -ErrorAction Stop
            $data = @{}
            foreach ($p in $rawJson.PSObject.Properties) { $data[$p.Name] = $p.Value }
            $test = Test-LanguageData -Data $data
            if ($ValidOnly -and -not $test.IsValid) { 
                Write-Warning "Skipping locale '$code' due to validation errors. Missing: $($test.MissingKeys -join ', ') Empty: $($test.EmptyKeys -join ', ')"
                continue 
            }
            $name = Get-LanguageDisplayName -Code $code -RawData $data
            $list += [PSCustomObject]@{
                Code = $code
                Name = $name
                FilePath = $it.FullName
                IsValid = $test.IsValid
                MissingKeys = $test.MissingKeys
                EmptyKeys = $test.EmptyKeys
            }
        } catch {
            Write-Warning "Failed to read locale file '$($it.Name)': $_"
        }
    }
    # Sort by Name, then Code for stable ordering
    return ($list | Sort-Object Name, Code)
}

function Set-Language {
    param(
        [Parameter(Mandatory=$true)][string]$lang
    )

    $global:__LanguageCode = $lang
    $basePath = Get-LocaleDirectory
    $filePath = Join-Path $basePath ($lang + '.json')

    if (-not (Test-Path $filePath)) {
        # try language without region (e.g. 'pt-BR' -> 'pt')
        if ($lang -match '^([a-z]{2})-') { $short = $Matches[1]; $f2 = Join-Path $basePath ($short + '.json') ; if (Test-Path $f2) { $filePath = $f2 } }
    }

    if (Test-Path $filePath) {
        try {
            Initialize-LanguageBase
            $json = Get-Content -Raw -Path $filePath | ConvertFrom-Json -ErrorAction Stop
            $data = @{}
            foreach ($p in $json.PSObject.Properties) { $data[$p.Name] = $p.Value }

            # validate and merge with baseline
            $test = Test-LanguageData -Data $data
            if (-not $test.IsValid) {
                Write-Warning "Locale '$lang' has issues. Missing: $($test.MissingKeys -join ', ') Empty: $($test.EmptyKeys -join ', '). Falling back to English for those keys."
            }
            $merged = @{}
            if ($Global:__LanguageBase.Count -gt 0) {
                foreach ($k in $Global:__LanguageBase.Keys) {
                    $val = $null
                    if ($data.ContainsKey($k) -and -not [string]::IsNullOrWhiteSpace([string]$data[$k])) { $val = [string]$data[$k] }
                    else { $val = [string]$Global:__LanguageBase[$k] }
                    $merged[$k] = $val
                }
                # also include any extra keys present only in the translation
                foreach ($k in $data.Keys) { if (-not $merged.ContainsKey($k)) { $merged[$k] = $data[$k] } }
                $global:__LanguageData = $merged
            } else {
                # No baseline; use as-is
                $global:__LanguageData = $data
            }
            return $true
        } catch {
            Write-Verbose "Failed to load language file '$filePath': $_"
        }
    }

    # fallback to en built-in minimal set if file missing
    if ($lang -ne 'en') { Set-Language -lang 'en' } else { $global:__LanguageData = @{} }
    return $false
}

function Get-Text {
    param(
        [Parameter(Mandatory=$true)][string]$key,
        [Parameter(Mandatory=$false)][object]$args
    )

    $val = $null
    if ($global:__LanguageData.ContainsKey($key)) { $val = $global:__LanguageData[$key] }
    else { $val = $null }

    if (-not $val) {
        # fallback messages for a small set of keys
        switch ($key) {
            'SelectLanguagePrompt' { $val = "Select language: 1) English  2) Portugues (Brasil)"; break }
            default { $val = $key }
        }
    }

    if ($args) {
        try {
            if ($args -is [hashtable]) {
                foreach ($k in $args.Keys) { $val = $val -replace "\{${k}\}", [string]$args[$k] }
            } elseif ($args -is [array]) {
                $val = [string]::Format($val, $args)
            } else {
                $val = [string]::Format($val, $args)
            }
        } catch {
            # ignore formatting errors and return raw value
        }
    }
    return $val
}

# Show a dynamic language selection menu based on locales/*.json
function Select-LanguageInteractive {
    param(
        [switch]$ValidOnly
    )
    $langs = Get-AvailableLanguages -ValidOnly:$ValidOnly
    if (-not $langs -or $langs.Count -eq 0) {
        Write-Host "No locale files found. Using English."
        Set-Language -lang 'en' | Out-Null
        return 'en'
    }

    Write-Host "Select language (by number):"
    for ($i = 0; $i -lt $langs.Count; $i++) {
        $mark = if ($langs[$i].IsValid) { '' } else { ' (incomplete)' }
        Write-Host ("{0}) {1}{2} [{3}]" -f ($i+1), $langs[$i].Name, $mark, $langs[$i].Code)
    }
    $selection = Read-Host "Enter your choice (1..$($langs.Count))"
    $idx = 0
    if ([int]::TryParse($selection, [ref]$idx)) {
        if ($idx -ge 1 -and $idx -le $langs.Count) {
            $chosen = $langs[$idx-1].Code
            Set-Language -lang $chosen | Out-Null
            return $chosen
        }
    }
    Write-Host "Invalid selection. Defaulting to English."
    Set-Language -lang 'en' | Out-Null
    return 'en'
}

conector-detector.ps1
# Connector Detector PowerShell Script
# This script detects and lists all IP and PORT connectors on a Windows system.
# It can detect both TCP and UDP connectors coming from outside of the system and inside the system.
# It provides details about each connector including its state and associated process.
# It also includes error handling to manage potential issues during execution and logging.
# Includes comments for clarity.
# System Requirements: Windows OS with PowerShell installed.
# Basic functionality: Lists all active TCP and UDP connections with their states and associated processes using netstat and lets the user select a specific connection for detailed information or search for specific program using name or ip/port.
# Extra: when detects an strange and suspected connection. send an toast alert (IF ENABLED) to the user and if clicked lets user to block/unblock the connection.

# Ensure script runs with administrative privileges
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Warning "You do not have Administrator rights to run this script! re-running this script as an Administrator!"
    Start-Process powershell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
    exit
}

# Ensure the BurntToast module is installed for toast notifications
if (-not (Get-Module -ListAvailable -Name BurntToast)) {
    Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
    Install-Module -Name BurntToast -Force
}

# Function to get all active connections
function Get-ActiveConnections {
    try {
        # Parse netstat output into structured objects so searches/filtering work correctly
        $raw = netstat -a -n -o 2>$null | Select-String "TCP|UDP"

        # initialize an array to hold results
        $results = @()
        foreach ($entry in $raw) {
            # split the line into parts
            $line = $entry.ToString().Trim()
            $parts = $line -split "\s+"
            # check if we have enough parts
            if ($parts.Length -ge 3) {
                # extract relevant fields
                $protocol = $parts[0]
                $localAddress = $parts[1]
                $foreignAddress = $parts[2]

                if ($protocol -eq 'TCP') {
                    # TCP protocol has a state
                    $state = if ($parts.Length -ge 4) { $parts[3] } else { 'N/A' }
                    $piid = if ($parts.Length -ge 5) { $parts[4] } else { $null }
                } else {
                    # UDP protocol does not have a state
                    $state = 'N/A'
                    $piid = if ($parts.Length -ge 4) { $parts[3] } else { $null }
                }

                # get process name from PID
                $processName = 'Unknown'
                if ($piid -and ($piid -as [int])) {
                    try {
                        $p = Get-Process -Id $piid -ErrorAction Stop
                        $processName = $p.ProcessName
                    } catch {
                        # leave as Unknown
                    }
                }

                # create a custom object for each connection
                $obj = [PSCustomObject]@{
                    Protocol       = $protocol
                    LocalAddress   = $localAddress
                    ForeignAddress = $foreignAddress
                    State          = $state
                    PID            = $piid
                    ProcessName    = $processName
                }
                # add the object to results
                $results += $obj
            }
        }

        # return the results
        return $results
    } catch {
        # error handling
        Write-Error "Error retrieving active connections: $_"
    }
}

# function to block a specific connection (placeholder, actual implementation may vary)
function Block-Connection {
    # parameters
    param (
        [string]$ip,
        [int]$port
    )
    try {
        # Placeholder for blocking logic, e.g., using Windows Firewall commands
        Write-Output "Blocking connection to $ip on port $port"
        New-NetFirewallRule -DisplayName "Block $ip $port" -Direction Outbound -RemoteAddress $ip -RemotePort $port -Protocol TCP -Action Block
    } catch {
        # error handling
        Write-Error "Error blocking connection: $_"
    }
}

# function to unblock a specific connection (placeholder, actual implementation may vary)
function Unblock-Connection {
    # parameters
    param (
        [string]$ip,
        [int]$port
    )
    try {
        # Placeholder for unblocking logic, e.g., using Windows Firewall commands
        Write-Output "Unblocking connection to $ip on port $port"
        Remove-NetFirewallRule -DisplayName "Block $ip $port"
    } catch {
        # error handling
        Write-Error "Error unblocking connection: $_"
    }
}

$ListMaliciousIPs = @("123.456.789.0", "987.654.321.0")
$ListunusualPorts = @(6666, 12345, 54321)

# Add User Defined Lists for Malicious IPs and Unusual Ports
function Add-UserDefinedLists {
    param (
        [string[]]$maliciousIPs,
        [int[]]$unusualPorts
    )
    $global:ListMaliciousIPs += $maliciousIPs
    $global:ListunusualPorts += $unusualPorts
}

# Remove User Defined Lists for Malicious IPs and Unusual Ports
function Remove-UserDefinedLists {
    param (
        [string[]]$maliciousIPs,
        [int[]]$unusualPorts
    )
    $global:ListMaliciousIPs = $global:ListMaliciousIPs | Where-Object { $maliciousIPs -notcontains $_ }
    $global:ListunusualPorts = $global:ListunusualPorts | Where-Object { $unusualPorts -notcontains $_ }
}

# Function to check for suspicious connections
function Check-SuspiciousConnections {
    try {
        $connections = Get-ActiveConnections
        $suspiciousConnections = @()
        # Define criteria for suspicious connections
        # Example: connections to known malicious IPs or unusual ports
        $maliciousIPs = $ListMaliciousIPs
        $unusualPorts = $ListunusualPorts

        foreach ($conn in $connections) {
            $foreignIP = $null
            $foreignPort = $null
            $portStr = ''

            if ($conn.ForeignAddress) {
                $foreignAddress = $conn.ForeignAddress.ToString()

                # Use a regex to capture a trailing :port (handles IPv4, IPv6 with brackets and wildcards like '*')
                $m = [regex]::Match($foreignAddress, '(:\d+)$')
                if ($m.Success) {
                    $portStr = $m.Groups[1].Value.TrimStart(':')
                    $parsedPort = 0
                    if ([int]::TryParse($portStr, [ref]$parsedPort)) {
                        $foreignPort = $parsedPort
                    }
                }

                # Extract IP part by removing the trailing :port if present
                if ($portStr -ne '') {
                    $foreignIP = $foreignAddress.Substring(0, $foreignAddress.Length - ($portStr.Length + 1))
                } else {
                    $foreignIP = $foreignAddress
                }

                # Normalize IPv6 bracket notation like [fe80::1]
                if ($foreignIP -match '^\[(.*)\]$') {
                    $foreignIP = $Matches[1]
                }
            }

            if (($foreignIP -and ($maliciousIPs -contains $foreignIP)) -or ($foreignPort -and ($unusualPorts -contains $foreignPort))) {
                $suspiciousConnections += $conn
            }
        }
        return $suspiciousConnections
    } catch {
        Write-Error "Error checking for suspicious connections: $_"
    }
}

$enableToastNotifications = $false

# enable/disable toast notifications (placeholder, actual implementation may vary)
function Enable-ToastNotifications {
    param (
        [bool]$enable
    )
    try {
        if ($enable) {
            Write-Output "Toast notifications enabled."
            $enableToastNotifications = $true
        } else {
            Write-Output "Toast notifications disabled."
            $enableToastNotifications = $false
        }
    } catch {
        Write-Error "Error setting toast notification preference: $_"
    }
}

# Toast notification function (placeholder, actual implementation may vary)
function Show-ToastNotification {
    param (
        [string]$title,
        [string]$message
    )
    try {
        if (-not $enableToastNotifications) {
            return
        }
        # Placeholder for toast notification logic using BurntToast module
        New-BurntToastNotification -Text $title, $message
    } catch {
        Write-Error "Error showing toast notification: $_"
    }
}

## Background monitor control functions
## Uses Start-ThreadJob with an inline ScriptBlock so the job does not depend on parent runspace functions.
function Start-Monitoring {
    param(
        [int]$IntervalSeconds = 30,
        [string]$JobName = 'ConnectorDetectorMonitor'
    )

    # Check for existing job
    $existing = Get-Job -Name $JobName -ErrorAction SilentlyContinue
    if ($existing -and ($existing.State -eq 'Running' -or $existing.State -eq 'NotStarted')) {
        Write-Output "Monitor already running as job '$JobName' (State: $($existing.State))."
        return
    }

    # Prefer Start-ThreadJob when available (more efficient). Otherwise fall back to Start-Job.
    if (Get-Command -Name Start-ThreadJob -ErrorAction SilentlyContinue) {
        # Capture lists and notification preference at job start using $using:
        $job = Start-ThreadJob -Name $JobName -ScriptBlock {
            $malicious = $using:ListMaliciousIPs
            $unusualPorts = $using:ListunusualPorts
            $toastEnabled = $using:enableToastNotifications

            while ($true) {
                Start-Sleep -Seconds $using:IntervalSeconds

                try {
                    $raw = netstat -a -n -o 2>$null | Select-String "TCP|UDP"
                } catch {
                    continue
                }

                $suspicious = @()
                foreach ($entry in $raw) {
                    $line = $entry.ToString().Trim()
                    $parts = $line -split "\s+"
                    if ($parts.Length -lt 3) { continue }
                    $protocol = $parts[0]
                    $local = $parts[1]
                    $foreign = $parts[2]

                    # Extract port
                    $m = [regex]::Match($foreign, '(:\d+)$')
                    $port = $null
                    if ($m.Success) {
                        $portStr = $m.Groups[1].Value.TrimStart(':')
                        $parsed = 0
                        if ([int]::TryParse($portStr, [ref]$parsed)) { $port = $parsed }
                    }

                    # Extract IP
                    if ($m.Success) {
                        $ip = $foreign.Substring(0, $foreign.Length - ($m.Groups[1].Value.Length))
                    } else {
                        $ip = $foreign
                    }
                    if ($ip -match '^\[(.*)\]$') { $ip = $Matches[1] }

                    $isMalicious = ($ip -and ($malicious -contains $ip))
                    $isUnusualPort = ($port -and ($unusualPorts -contains $port))

                    if ($isMalicious -or $isUnusualPort) {
                        $procName = 'Unknown'
                        # try to get PID if present
                        $procId = $null
                        if ($protocol -eq 'TCP') {
                            if ($parts.Length -ge 5) { $procId = $parts[4] }
                        } else {
                            if ($parts.Length -ge 4) { $procId = $parts[3] }
                        }
                        if ($procId -and ($procId -as [int])) {
                            try { $p = Get-Process -Id $procId -ErrorAction Stop; $procName = $p.ProcessName } catch {}
                        }

                        $suspicious += [PSCustomObject]@{
                            Protocol = $protocol
                            LocalAddress = $local
                            ForeignAddress = $foreign
                            Port = $port
                            IP = $ip
                            PID = $procId
                            ProcessName = $procName
                        }
                    }
                }

                if ($suspicious.Count -gt 0) {
                    foreach ($conn in $suspicious) {
                        $title = "Suspicious Connection Detected"
                        $message = "Protocol: $($conn.Protocol)`nLocal: $($conn.LocalAddress)`nForeign: $($conn.ForeignAddress)`nProcess: $($conn.ProcessName)"
                        if ($toastEnabled -and (Get-Command -Name New-BurntToastNotification -ErrorAction SilentlyContinue)) {
                            try { New-BurntToastNotification -Text $title, $message } catch {}
                        } else {
                            # write to job output (can be inspected with Receive-Job)
                            Write-Output $title
                            Write-Output $message
                        }
                    }
                }
            }
        } -ArgumentList $IntervalSeconds
    } else {
        # Fallback to Start-Job (works on systems without ThreadJob). Pass required data via ArgumentList.
        $job = Start-Job -Name $JobName -ScriptBlock {
            param($malicious, $unusualPorts, $toastEnabled, $interval)
            while ($true) {
                Start-Sleep -Seconds $interval
                try {
                    $raw = netstat -a -n -o 2>$null | Select-String "TCP|UDP"
                } catch {
                    continue
                }

                $suspicious = @()
                foreach ($entry in $raw) {
                    $line = $entry.ToString().Trim()
                    $parts = $line -split "\s+"
                    if ($parts.Length -lt 3) { continue }
                    $protocol = $parts[0]
                    $local = $parts[1]
                    $foreign = $parts[2]

                    $m = [regex]::Match($foreign, '(:\d+)$')
                    $port = $null
                    if ($m.Success) {
                        $portStr = $m.Groups[1].Value.TrimStart(':')
                        $parsed = 0
                        if ([int]::TryParse($portStr, [ref]$parsed)) { $port = $parsed }
                    }

                    if ($m.Success) {
                        $ip = $foreign.Substring(0, $foreign.Length - ($m.Groups[1].Value.Length))
                    } else {
                        $ip = $foreign
                    }
                    if ($ip -match '^\[(.*)\]$') { $ip = $Matches[1] }

                    $isMalicious = ($ip -and ($malicious -contains $ip))
                    $isUnusualPort = ($port -and ($unusualPorts -contains $port))

                    if ($isMalicious -or $isUnusualPort) {
                        $procName = 'Unknown'
                        $procId = $null
                        if ($protocol -eq 'TCP') {
                            if ($parts.Length -ge 5) { $procId = $parts[4] }
                        } else {
                            if ($parts.Length -ge 4) { $procId = $parts[3] }
                        }
                        if ($procId -and ($procId -as [int])) {
                            try { $p = Get-Process -Id $procId -ErrorAction Stop; $procName = $p.ProcessName } catch {}
                        }

                        $suspicious += [PSCustomObject]@{
                            Protocol = $protocol
                            LocalAddress = $local
                            ForeignAddress = $foreign
                            Port = $port
                            IP = $ip
                            PID = $procId
                            ProcessName = $procName
                        }
                    }
                }

                if ($suspicious.Count -gt 0) {
                    foreach ($conn in $suspicious) {
                        $title = "Suspicious Connection Detected"
                        $message = "Protocol: $($conn.Protocol)`nLocal: $($conn.LocalAddress)`nForeign: $($conn.ForeignAddress)`nProcess: $($conn.ProcessName)"
                        if ($toastEnabled -and (Get-Command -Name New-BurntToastNotification -ErrorAction SilentlyContinue)) {
                            try { New-BurntToastNotification -Text $title, $message } catch {}
                        } else {
                            Write-Output $title
                            Write-Output $message
                        }
                    }
                }
            }
        } -ArgumentList ($ListMaliciousIPs, $ListunusualPorts, $enableToastNotifications, $IntervalSeconds)
    }

    if ($job) { Write-Output "Started monitor job '$($job.Name)' (Id: $($job.Id))." }
}

function Stop-Monitoring {
    param([string]$JobName = 'ConnectorDetectorMonitor')
    $job = Get-Job -Name $JobName -ErrorAction SilentlyContinue
    if ($job) {
        Stop-Job -Job $job -ErrorAction SilentlyContinue
        Remove-Job -Job $job -ErrorAction SilentlyContinue
        Write-Output "Stopped and removed monitor job '$JobName'."
    } else {
        Write-Output "No monitor job named '$JobName' was found."
    }
}

function Get-MonitorStatus {
    param([string]$JobName = 'ConnectorDetectorMonitor')
    $job = Get-Job -Name $JobName -ErrorAction SilentlyContinue
    if ($job) { return $job.State } else { return 'NotFound' }
}

function Monitor-SuspiciousConnections { Start-Monitoring }

# Main script execution (Let user choose an action)
# 0 - Exit
# 1 - List all active connections
# 2 - Get details of a specific connection
# 2.1 - Search for a specific program by name
# 2.2 - Search for a specific IP/Port
# 3 - Block/Unblock a specific connection
# 3.1 - Block a specific connection
# 3.2 - Unblock a specific connection
# 4 - Check for suspicious connections and alert user
# 4.1 - Activate toast notification for suspicious connections
# 4.2 - Let user block/unblock from notification
# 4.3 - Add User Defined Lists for Malicious IPs and Unusual Ports
# 4.4 - Remove User Defined Lists for Malicious IPs and Unusual Ports

. "$PSScriptRoot\connector-language.ps1"
# Dynamic language discovery and selection (lists locales/*.json). Only valid locales are shown.
Select-LanguageInteractive -ValidOnly | Out-Null

$exitScript = $false
while (-not $exitScript) {
    Write-Output (Get-Text -key 'lineBreak')
    Write-Output (Get-Text -key 'MenuLine')
    Write-Output (Get-Text -key 'TitleHeader')
    Write-Output (Get-Text -key 'MenuLine')
    
    $connections = Get-ActiveConnections
    if ($connections) {

        # check for suspicious connections (on-demand)
        $suspiciousConnections = Check-SuspiciousConnections

    # show options to the user
    Write-Output (Get-Text -key 'SelectOptionPrompt')
    Write-Output (Get-Text -key 'Option1')
    Write-Output (Get-Text -key 'Option2')
    Write-Output (Get-Text -key 'Option3')
    Write-Output (Get-Text -key 'Option4')
    Write-Output (Get-Text -key 'Option0')
    Write-Output (Get-Text -key 'lineBreak')

    $userChoice = Read-Host (Get-Text -key 'EnterChoice')
        switch ($userChoice) {
            "1" {
                # List all active connections as a table
                if ($connections.Count -gt 0) {
                    # Display connections sorted by Protocol and LocalAddress
                    $connections | Sort-Object Protocol,LocalAddress | Format-Table -AutoSize
                } else {
                    # No connections found
                    Write-Output (Get-Text -key 'NoActiveConnections')
                }
            }
            "2" {
                
                # show options 2.1 and 2.2 as 1 and 2 sub-options
                Write-Output (Get-Text -key 'lineBreak')
                Write-Output (Get-Text -key 'SubOptionPrompt')
                Write-Output (Get-Text -key 'Sub2Option1')
                Write-Output (Get-Text -key 'Sub2Option2')

                $subChoice = Read-Host (Get-Text -key 'EnterSubChoice12')
                # Handle sub-options
                switch ($subChoice) {
                    "1" {
                        # Search for a specific program by name
                        $programName = Read-Host (Get-Text -key 'EnterProgramName')
                        if ([string]::IsNullOrWhiteSpace($programName)) {
                            # Empty input handling
                            Write-Output (Get-Text -key 'ProvideNonEmptyProgramName')
                        } else {
                            $matchingConnections = $connections | Where-Object { $_.ProcessName -match $programName }
                            if ($matchingConnections.Count -gt 0) {
                                # Matches found
                                Write-Output "Connections for program '$programName':"
                                $matchingConnections | Format-Table -AutoSize
                            } else {
                                # No matches found
                                Write-Output "No connections found for program '$programName'."
                            }
                        }
                    }
                    "2" {
                        # Search for a specific IP/Port
                        $ipOrPort = Read-Host (Get-Text -key 'EnterIPOrPort')
                        if ([string]::IsNullOrWhiteSpace($ipOrPort)) {
                            Write-Output (Get-Text -key 'ProvideNonEmptyIPPort')
                        } else {
                            $matchingConnections = $connections | Where-Object {
                                # Check if LocalAddress or ForeignAddress contains the IP/Port or if PID matches
                                ($_.LocalAddress -match $ipOrPort) -or ($_.ForeignAddress -match $ipOrPort) -or ($_.PID -eq $ipOrPort)
                            }
                            if ($matchingConnections.Count -gt 0) {
                                # Matches found
                                Write-Output "Connections for IP/Port '$ipOrPort':"
                                $matchingConnections | Format-Table -AutoSize
                            } else {
                                # No matches found
                                Write-Output "No connections found for IP/Port '$ipOrPort'."
                            }
                        }
                    }
                    default {
                        # Invalid sub-choice
                        Write-Output (Get-Text -key 'InvalidChoice')
                    }
                }
            }
            "3" {
                # show options 3.1 and 3.2 as 1 and 2 sub-options
                Write-Output (Get-Text -key 'lineBreak')
                Write-Output (Get-Text -key 'SubOptionPrompt')
                Write-Output (Get-Text -key 'Sub3Option1')
                Write-Output (Get-Text -key 'Sub3Option2')

                $subChoice = Read-Host (Get-Text -key 'EnterSubChoice12')
                # Handle sub-options
                switch ($subChoice) {
                    "1" {
                        # Block a specific connection
                        $ipToBlock = Read-Host (Get-Text -key 'EnterIPToBlock')
                        $portToBlock = Read-Host (Get-Text -key 'EnterPortToBlock')
                        if ([int]::TryParse($portToBlock, [ref]$null)) {
                            Block-Connection -ip $ipToBlock -port [int]$portToBlock
                        } else {
                            Write-Output (Get-Text -key 'InvalidPortNumber')
                        }
                    }
                    "2" {
                        # Unblock a specific connection
                        $ipToUnblock = Read-Host (Get-Text -key 'EnterIPToUnblock')
                        $portToUnblock = Read-Host (Get-Text -key 'EnterPortToUnblock')
                        if ([int]::TryParse($portToUnblock, [ref]$null)) {
                            Unblock-Connection -ip $ipToUnblock -port [int]$portToUnblock
                        } else {
                            Write-Output (Get-Text -key 'InvalidPortNumber')
                        }
                    }
                    default {
                        # Invalid sub-choice
                        Write-Output (Get-Text -key 'InvalidChoice')
                    }
                }
            }
            "4" {
                Write-Output (Get-Text -key 'lineBreak')
                Write-Output (Get-Text -key 'Sub4Option1')
                Write-Output (Get-Text -key 'Sub4Option2')
                Write-Output (Get-Text -key 'Sub4Option3')
                Write-Output (Get-Text -key 'Sub4Option4')
                Write-Output (Get-Text -key 'Sub4Option5')
                Write-Output (Get-Text -key 'Sub4Option6')
                Write-Output (Get-Text -key 'Sub4Option7')

                $suspiciousChoice = Read-Host (Get-Text -key 'EnterChoice1to7')
                switch ($suspiciousChoice) {
                    "1" {
                        # Enable/Disable toast notifications
                        $toastChoice = Read-Host (Get-Text -key 'EnterEnableDisableToast')
                        if ($toastChoice -eq "enable") {
                            Enable-ToastNotifications -enable $true
                        } elseif ($toastChoice -eq "disable") {
                            Enable-ToastNotifications -enable $false
                        } else {
                            Write-Output (Get-Text -key 'InvalidEnableDisableChoice')
                        }
                    }
                    "2" {
                        # View suspicious connections
                        if ($suspiciousConnections.Count -gt 0) {
                            Write-Output (Get-Text -key 'SuspiciousConnectionsDetected')
                            $suspiciousConnections | Format-Table -AutoSize
                        } else {
                            Write-Output (Get-Text -key 'NoSuspiciousConnections')
                        }
                    }
                    "3" {
                        # Add User Defined Lists for Malicious IPs and Unusual Ports
                        $newMaliciousIPs = Read-Host (Get-Text -key 'EnterMaliciousIPsToAdd')
                        $newUnusualPorts = Read-Host (Get-Text -key 'EnterUnusualPortsToAdd')

                        $maliciousIPArray = $newMaliciousIPs -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
                        $unusualPortArray = $newUnusualPorts -split "," | ForEach-Object { 
                            $portTrimmed = $_.Trim()
                            if ([int]::TryParse($portTrimmed, [ref]$null)) {
                                [int]$portTrimmed
                            }
                        } | Where-Object { $_ -ne $null }

                        Add-UserDefinedLists -maliciousIPs $maliciousIPArray -unusualPorts $unusualPortArray

                        Write-Output (Get-Text -key 'UserDefinedListsUpdated')
                    }
                    "4" {
                        # Remove User Defined Lists for Malicious IPs and Unusual Ports
                        $removeMaliciousIPs = Read-Host (Get-Text -key 'EnterMaliciousIPsToRemove')
                        $removeUnusualPorts = Read-Host (Get-Text -key 'EnterUnusualPortsToRemove')

                        $maliciousIPArray = $removeMaliciousIPs -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
                        $unusualPortArray = $removeUnusualPorts -split "," | ForEach-Object { 
                            $portTrimmed = $_.Trim()
                            if ([int]::TryParse($portTrimmed, [ref]$null)) {
                                [int]$portTrimmed
                            }
                        } | Where-Object { $_ -ne $null }

                        Remove-UserDefinedLists -maliciousIPs $maliciousIPArray -unusualPorts $unusualPortArray

                        Write-Output (Get-Text -key 'UserDefinedListsUpdated')
                    }
                    "5" {
                        # Start background monitor
                        $intervalInput = Read-Host (Get-Text -key 'EnterMonitorInterval')
                        # default
                        $intervalNum = 30
                        if (-not [int]::TryParse($intervalInput, [ref]$intervalNum)) { $intervalNum = 30 }
                        Start-Monitoring -IntervalSeconds $intervalNum
                    }
                    "6" {
                        # Stop background monitor
                        Stop-Monitoring
                    }
                    "7" {
                        # Monitor status
                        $status = Get-MonitorStatus
                        Write-Output (Get-Text -key 'MonitorStatus' -args @($status))
                    }
                    default {
                        Write-Output (Get-Text -key 'InvalidChoice')
                    }
                }

            }
            "0" {
                # Exit the script
                Write-Output "Exiting the script. Stopping monitor if running..."
                Stop-Monitoring
                $exitScript = $true
            }
            default {
                # Invalid choice
                Write-Output (Get-Text -key 'InvalidChoice')
            }
        }
    } else {
        # No active connections found
        Write-Output (Get-Text -key 'NoActiveConnections')
    }
}
Conclusão
 
Imagem em destaque gerada pela IA generativa Gemini (Gemini.google.com)
Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet, Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet.

About The Author

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *