Skip to content

Instantly share code, notes, and snippets.

@fenneh
Created October 27, 2016 10:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fenneh/e569714df68ec3c96d39a32a484310c1 to your computer and use it in GitHub Desktop.
Save fenneh/e569714df68ec3c96d39a32a484310c1 to your computer and use it in GitHub Desktop.
Log-EphemeralStats.ps1
<#
.SYNOPSIS
Runs in an infinite loop getting the TCP ephemeral port and listening port statistics for each local IP address and outputs the data to a text file log.
.DESCRIPTION
Runs in an infinite loop getting the TCP ephemeral port and listening port statistics for each local IP address and outputs the data to a text file log. The script writes the ephemeral port stats every 60 seconds by default. To get data from remote computers, this script requires PsExec.exe (SysInternals) to be in the same directory as this script. WARNING: Credentials passed into PSExec are sent over the network in clear text! Prevent this by logging in interactively with a domain account that has administrator rights on the target computers and not specifying credentials to this script. PsExec is a Sysinternals tool owned by Microsoft Corporation. PsExec can be downloaded for free at http://live.sysinternals.com/psexec.exe.
.Parameter CollectionInterval
This must be an integer in seconds. This is how often you want the script to update the ephemeral port stats and write to the console and to the log. If omitted, 60 seconds is used.
.Parameter OutputFilePath
This must be a file path to write to. This will append to an existing text file. If omitted, .\EphemeralPortStats.log is used.
.EXAMPLE
.\Log-EphemeralStats.ps1
This will get TCP ephemeral port and listening port statistics for each local IP address of this computer and outputs the data to a the console and log every 60 seconds by default.
.EXAMPLE
.\Log-EphemeralStats.ps1 -CollectionInterval 10
This will get TCP ephemeral port and listening port statistics for each local IP address of this computer and outputs the data to a the console and log (.\EphemeralPortStats.log is the default) every 10 seconds.
.EXAMPLE
.\Log-EphemeralStats.ps1 -CollectionInterval 10 -OutputFilePath '.\output.log'
This will get TCP ephemeral port and listening port statistics for each local IP address of this computer and outputs the data to a the console and log (in this case .\output.log) every 10 seconds.
.Notes
Name: Log-EphemeralStats.ps1
Author: Clint Huffman (clinth@microsoft.com)
LastEdit: December 3rd, 2011
Version: 1.0
Keywords: PowerShell, TCP, ephemeral, ports, listening
.Link
Download PsExec (Sysinternals owned by Microsoft corporation) http://live.sysinternals.com/psexec.exe
#>
param([string]$Computers="$env:computername",[string]$User='',[string]$Password='',[int]$CollectionInterval=5,[string]$OutputFilePath='.\EphemeralPortStats.log')
#// Argument processing
$global:Computers = $Computers
$global:User = $User
$global:Password = $Password
$global:CollectionInterval = $CollectionInterval
$global:OutputFilePath = $OutputFilePath
#// Remove all of the jobs that might be running previously to this session.
If (@(Get-Job).Count -gt 0)
{
Remove-Job -Name * -Force
}
#// Stop any network traces already running
netsh trace stop
#// Start the network trace
netsh trace start capture=yes tracefile=$env:WINDIR\Tools\NetTrace_DO_NOT_COPY_YET.etl maxsize=1024 filemode=circular overwrite=yes persistent=yes
Function ProcessArguments
{
#// Processes the arguments passed into the script such as getting the appropriate credentials if specified.
$global:aComputers = $global:Computers.Split(';')
If ($global:aComputers -isnot [System.String[]])
{
$global:aComputers = @($global:Computers)
}
#// If credentials are passed into this script, then make them secure.
If ($global:User -ne '')
{
If ($global:Password -ne '')
{
$global:Password = ConvertTo-SecureString -AsPlainText -Force -String $global:Password
$global:oCredential = New-Object System.Management.Automation.PsCredential($global:User,$global:Password)
}
Else
{
$global:oCredential = Get-Credential -Credential $global:User
}
}
}
Function Invoke-PsExec
{
#// Executes PsExec to get data from remote computers.
param([string]$Computer,[string]$Command)
If ($global:User -ne '')
{
If ($global:Password -eq '')
{
$global:Password = $global:oCredential.GetNetworkCredential().Password
}
$sPsCmd = '.\psexec \\' + "$Computer" + ' /AcceptEula -u ' + "$global:User" + ' -p "' + "$global:Password" + '" -s ' + $Command + ' 2> $null'
Write-Warning 'Credentials sent in clear text to the remote computer using PsExec! Prevent this by not providing credentials to this script and logging in with a domain account with admin privileges to the remote computer or by using network encryption such as IPSec.'
}
Else
{
$sPsCmd = ".\psexec \\$Computer /AcceptEula $Command"
}
Invoke-Expression -Command $sPsCmd
}
Function Get-TcpDynamicPortRange
{
param($Computer="$env:computername")
If ($Computer -eq $env:COMPUTERNAME)
{
$bIsLocal = $true
}
Else
{
$bIsLocal = $false
}
If ($bIsLocal -eq $true)
{
$oOutput = Invoke-Expression -Command 'netsh int ipv4 show dynamicportrange tcp'
}
Else
{
#// Use PsExec
$sCmd = 'netsh int ipv4 show dynamicportrange tcp'
$oOutput = Invoke-PsExec -Computer $Computer -Command $sCmd -User $global:User -Password $global:Password
}
$oDynamicPortRange = New-Object pscustomobject
Add-Member -InputObject $oDynamicPortRange -MemberType NoteProperty -Name StartPort -Value 0
Add-Member -InputObject $oDynamicPortRange -MemberType NoteProperty -Name EndPort -Value 0
Add-Member -InputObject $oDynamicPortRange -MemberType NoteProperty -Name NumberOfPorts -Value 0
ForEach ($sLine in $oOutput)
{
If ($($sLine.IndexOf('Start Port')) -ge 0)
{
$aLine = $sLine.Split(':')
[System.Int32] $oDynamicPortRange.StartPort = $aLine[1]
}
If ($($sLine.IndexOf('Number of Ports')) -ge 0)
{
$aLine = $sLine.Split(':')
[System.Int32] $oDynamicPortRange.NumberOfPorts = $aLine[1]
}
}
$oDynamicPortRange.EndPort = ($($oDynamicPortRange.StartPort) + $($oDynamicPortRange.NumberOfPorts)) - 1
$oDynamicPortRange
}
Function Get-ActiveTcpConnections
{
param($Computer="$env:computername")
If ($Computer -eq $env:COMPUTERNAME)
{
$bIsLocal = $true
}
Else
{
$bIsLocal = $false
}
If ($bIsLocal -eq $true)
{
$oOutput = Invoke-Expression -Command 'netstat -ano -p tcp'
}
Else
{
#// Use PsExec
$sCmd = 'netstat -ano -p tcp'
$oOutput = Invoke-PsExec -Computer $Computer -Command $sCmd -User $global:User -Password $global:Password
}
If ($oOutput -ne $null)
{
$u = $oOutput.GetUpperBound(0)
$oOutput = $oOutput[4..$u]
$aActiveConnections = @()
ForEach ($sLine in $oOutput)
{
$iPropertyIndex = 0
$aLine = $sLine.Split(' ')
$oActiveConnection = New-Object System.Object
For ($c = 0; $c -lt $aLine.Count;$c++)
{
If ($aLine[$c] -ne '')
{
switch ($iPropertyIndex)
{
0 {Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name Proto -Value $($aLine[$c])}
1 {
$aIpPort = $aLine[$c].Split(':')
Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name LocalAddress -Value $($aIpPort[0])
Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name LocalPort -Value $([System.Int32] $aIpPort[1])
}
2 {
$aIpPort = $aLine[$c].Split(':')
Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name ForeignAddress -Value $($aIpPort[0])
Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name ForeignPort -Value $([System.Int32] $aIpPort[1])
}
3 {Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name State -Value $($aLine[$c])}
4 {Add-Member -InputObject $oActiveConnection -MemberType NoteProperty -Name PID -Value $([System.Int32] $aLine[$c])}
}
$iPropertyIndex++
}
}
$aActiveConnections += $oActiveConnection
}
$aActiveConnections
}
}
$global:htDynamicPortRange = @{}
Function Get-EphemeralPortStats
{
param($ArrayOfComputerNames)
$aLocalAddressStats = @()
ForEach ($Computer in $ArrayOfComputerNames)
{
If ($($global:htDynamicPortRange.ContainsKey($Computer)) -eq $false)
{
$oDynamicPortRange = Get-TcpDynamicPortRange -Computer $Computer
[System.Int32] $iDynamicStartPort = $oDynamicPortRange.StartPort
[System.Int32] $iDynamicEndPort = $oDynamicPortRange.EndPort
[System.Int32] $iDynamicNumberOfPorts = $oDynamicPortRange.NumberOfPorts
[Void] $global:htDynamicPortRange.Add($Computer,$oDynamicPortRange)
}
Else
{
$oDynamicPortRange = $global:htDynamicPortRange[$Computer]
[System.Int32] $iDynamicStartPort = $oDynamicPortRange.StartPort
[System.Int32] $iDynamicEndPort = $oDynamicPortRange.EndPort
[System.Int32] $iDynamicNumberOfPorts = $oDynamicPortRange.NumberOfPorts
}
$oActiveConnections = Get-ActiveTcpConnections -Computer $Computer | Sort-Object LocalPort -Descending
$aUniqueLocalAddresses = $oActiveConnections | Sort-Object -Property LocalAddress | Select LocalAddress | Get-Unique -AsString
$aDynamicPortRangeConnections = $oActiveConnections | Where-Object -FilterScript {($_.LocalPort -ge $iDynamicStartPort) -and ($_.LocalPort -le $iDynamicEndPort)}
ForEach ($oUniqueLocalAddress in $aUniqueLocalAddresses)
{
If ($($oUniqueLocalAddress.LocalAddress) -ne '0.0.0.0')
{
#// Ephemeral ports of each LocalAddress
[string] $sUniqueLocalAddress = $oUniqueLocalAddress.LocalAddress
$aIpEphemeralPortConnections = @($aDynamicPortRangeConnections | Where-Object -FilterScript {($_.LocalAddress -eq $sUniqueLocalAddress)} | Select LocalPort, PID | Sort-Object | Get-Unique -AsString)
If ($aIpEphemeralPortConnections -ne $null)
{
$oStats = New-Object System.Object
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Computer' -Value $Computer
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'DateTime' -Value $(Get-Date)
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'LocalAddress' -Value $sUniqueLocalAddress
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'InUse' -Value $([System.Int32] $aIpEphemeralPortConnections.Count)
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Max' -Value $([System.Int32] $oDynamicPortRange.NumberOfPorts)
$iPercentage = ($([System.Int32] $aIpEphemeralPortConnections.Count) / $([System.Int32] $oDynamicPortRange.NumberOfPorts)) * 100
$iPercentage = [Math]::Round($iPercentage,1)
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Percent' -Value $iPercentage
}
Else
{
$oStats = New-Object System.Object
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Computer' -Value $Computer
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'DateTime' -Value $(Get-Date)
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'LocalAddress' -Value $sUniqueLocalAddress
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'InUse' -Value 0
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Max' -Value $([System.Int32] $oDynamicPortRange.NumberOfPorts)
$iPercentage = 0
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Percent' -Value $iPercentage
}
#// Listening ports of each LocalAddress
$aIpListeningPorts = $oActiveConnections | Where-Object -FilterScript {($_.State -eq 'LISTENING') -and (($_.LocalAddress -eq $sUniqueLocalAddress) -or ($_.LocalAddress -eq '0.0.0.0'))} | Select LocalPort | Sort-Object LocalPort | Get-Unique -AsString
If ($aIpListeningPorts -ne $null)
{
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Listening' -Value $([System.Int32] $aIpListeningPorts.Count)
}
Else
{
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'Listening' -Value 0
}
#// Number of PIDs
$aIpPids = $oActiveConnections | Where-Object -FilterScript {($_.LocalAddress -eq $sUniqueLocalAddress) -or ($_.LocalAddress -eq '0.0.0.0')} | Select PID | Sort-Object PID
$aUniquePids = $aIpPids | Get-Unique -AsString
If ($aUniquePids -ne $null)
{
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'NumOfPids' -Value $([System.Int32] $aUniquePids.Count)
}
Else
{
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'NumOfPids' -Value 0
}
$aPidStats = @()
ForEach ($iPid in $aUniquePids)
{
$iPidCount = @($aIpPids | Where-Object -FilterScript {$_.PID -eq $iPid.PID}).Count
$oPidStats = New-Object System.Object
Add-Member -InputObject $oPidStats -MemberType NoteProperty -Name 'PID' -Value $iPid.PID
Add-Member -InputObject $oPidStats -MemberType NoteProperty -Name 'Count' -Value $iPidCount
$aPidStats += $oPidStats
}
Add-Member -InputObject $oStats -MemberType NoteProperty -Name 'PidStats' -Value $aPidStats
$aLocalAddressStats += $oStats
}
}
}
$aLocalAddressStats
}
ProcessArguments
$bNeverTrue = $false
do
{
$oPortStats = Get-EphemeralPortStats -ArrayOfComputerNames $global:aComputers
$oPortStats | Select Computer, DateTime, LocalAddress, InUse, Max, Percent, Listening | Format-Table -AutoSize
$oPortStats | Select Computer, DateTime, LocalAddress, InUse, Max, Percent, Listening | Format-Table -AutoSize >> $OutputFilePath
$iCount = @($oPortStats | Where-Object {$_.Percent -ge 10}).Count
If ($iCount -gt 0)
{
Tasklist /svc >> $OutputFilePath
ForEach ($oItem in $oPortStats)
{
$oItem.LocalAddress >> $OutputFilePath
$oItem.PidStats | Sort-Object Count -Descending | ft -AutoSize >> $OutputFilePath
ForEach ($oPid in $oItem.PidStats)
{
If ($oPid.Count -ge 1000)
{
gwmi -Query "SELECT * FROM Win32_Process WHERE Handle = $($oPid.PID)" | SELECT Name, ProcessId, HandleCount, Path | Format-List >> $OutputFilePath
gwmi -Query "ASSOCIATORS OF {Win32_Process.Handle=$($oPid.PID)} WHERE ResultClass = CIM_DataFile" | SELECT Caption, LastModified, Manufacturer, Version | Format-List >> $OutputFilePath
}
}
}
$sDateTime = (Get-Date).ToString('yyyyMMddHHmmss')
$sNewNetTraceName = "$env:WINDIR\Tools\$($sDateTime)_NetTrace.etl"
netsh trace stop
Rename-Item -Path $env:WINDIR\Tools\NetTrace_DO_NOT_COPY_YET.etl -NewName $sNewNetTraceName
netsh trace start capture=yes tracefile=$env:WINDIR\Tools\NetTrace_DO_NOT_COPY_YET.etl maxsize=1024 filemode=circular overwrite=yes persistent=yes
}
Write-Host "Sleeping for $global:CollectionInterval seconds..." -NoNewline
Start-Sleep -Seconds $global:CollectionInterval
Write-Host 'Done!'
} until ($bNeverTrue)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment