Skip to content

Instantly share code, notes, and snippets.

@davehull
Created August 17, 2018 17:41
Show Gist options
  • Save davehull/a8f8adaf188cd6922cd663791066e16b to your computer and use it in GitHub Desktop.
Save davehull/a8f8adaf188cd6922cd663791066e16b to your computer and use it in GitHub Desktop.
Netstat like data with hashes from PowerShell
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,Position=0)]
[String]$TargetHostname,
[Parameter(Mandatory=$False,Position=1)]
[String]$HashAlgorithm
)
## We will handle errors via Try/Catch
$ErrorActionPreference = 'Stop'
<###
Based on work from Lee Christensen (@tifkin_)
License: BSD 3-Clause
##>
#region import psreflect
# Requires PSReflect.ps1 and PowerForensics in the same directory
$PSReflect = '.\PSReflect.ps1'
Try
{
# We'll need this for hashing locked files
Import-Module ('.\powerforensics')
}
catch
{
Write-Out 'PowerForensics module not found.'
exit
}
if ( -Not (Test-Path $PSReflect) )
{
Write-Out 'Error: PSReflect module not found.'
exit
}
Try
{
# PSReflect is required for the Win APIs we need
Get-Content $PSReflect | Out-String | Invoke-Expression
} catch
{
Write-Out 'Failed to import PSReflect module.'
exit
}
#endregion import psreflect
#region Windows API Definitions
$Mod = New-InMemoryModule -ModuleName NetworkConnection
#endregion Windows API Definitions
#region Enums
$TCP_TABLE_CLASS = psenum $Mod TCP_TABLE_CLASS UInt16 @{
TCP_TABLE_BASIC_LISTENER = 0
TCP_TABLE_BASIC_CONNECTIONS = 1
TCP_TABLE_BASIC_ALL = 2
TCP_TABLE_OWNER_PID_LISTENER = 3
TCP_TABLE_OWNER_PID_CONNECTIONS = 4
TCP_TABLE_OWNER_PID_ALL = 5
TCP_TABLE_OWNER_MODULE_LISTENER = 6
TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7
TCP_TABLE_OWNER_MODULE_ALL = 8
}
$TCP_STATE = psenum $Mod TCP_STATE UInt16 @{
CLOSED = 1
LISTENING = 2
SYN_SENT = 3
SYN_RECEIVED = 4
ESTABLISHED = 5
FIN_WAIT1 = 6
FIN_WAIT2 = 7
CLOSE_WAIT = 8
CLOSING = 9
LAST_ACK = 10
TIME_WAIT = 11
DELETE_TCB = 12
}
$UDP_TABLE_CLASS = psenum $Mod UDP_TABLE_CLASS UInt16 @{
UDP_TABLE_BASIC = 0
UDP_TABLE_OWNER_PID = 1
UDP_TABLE_OWNER_MODULE = 2
}
$TAG_INFO_LEVEL = psenum $Mod TAG_INFO_LEVEL UInt16 @{
eTagInfoLevelNameFromTag = 1
eTagInfoLevelNamesReferencingModule = 2
eTagInfoLevelNameTagMapping = 3
eTagInfoLevelMax = 4
}
#endregion Enums
#region Structs
$MIB_UDPROW_OWNER_MODULE = struct $Mod MIB_UDPROW_OWNER_MODULE @{
LocalAddr = field 0 UInt32 0
LocalPort = field 1 UInt32 4
OwningPid = field 2 UInt32 8
CreateTimestamp = field 3 UInt64 16
SpecificPortBind = field 4 UInt32 24 # Union
Flags = field 5 UInt32 24
OwningModuleInfo = field 6 UInt64[] -MarshalAs @('ByValArray', 16) 32
} -ExplicitLayout
$MIB_UDP6ROW_OWNER_MODULE = struct $Mod MIB_UDP6ROW_OWNER_MODULE @{
LocalAddr = field 0 Byte[] -MarshalAs @('ByValArray', 16) 0
LocalScopeId = field 1 UInt32 16
LocalPort = field 2 UInt32 20
OwningPid = field 3 UInt32 24
CreateTimestamp = field 4 UInt64 32
SpecificPortBind = field 5 UInt32 40 # Union
Flags = field 6 UInt32 40
OwningModuleInfo = field 7 UInt64[] -MarshalAs @('ByValArray', 16) 48
} -ExplicitLayout
$MIB_UDPTABLE_OWNER_MODULE = struct $Mod MIB_UDPTABLE_OWNER_MODULE @{
NumEntries = field 0 UInt32
Table = field 1 $MIB_UDPROW_OWNER_MODULE
}
$MIB_UDP6TABLE_OWNER_MODULE = struct $Mod MIB_UDP6TABLE_OWNER_MODULE @{
NumEntries = field 0 UInt32
Table = field 1 $MIB_UDPROW_OWNER_MODULE
}
$MIB_TCPROW_OWNER_MODULE = struct $Mod MIB_TCPROW_OWNER_MODULE @{
State = field 0 $TCP_STATE
LocalAddr = field 1 UInt32
LocalPort = field 2 UInt32
RemoteAddr = field 3 UInt32
RemotePort = field 4 UInt32
OwningPid = field 5 UInt32
CreateTimestamp = field 6 UInt64
OwningModuleInfo = field 7 UInt64[] -MarshalAs @('ByValArray', 16)
}
$MIB_TCP6ROW_OWNER_MODULE = struct $Mod MIB_TCP6ROW_OWNER_MODULE @{
LocalAddr = field 0 Byte[] -MarshalAs @('ByValArray', 16)
LocalScopeId = field 1 UInt32
LocalPort = field 2 UInt32
RemoteAddr = field 3 Byte[] -MarshalAs @('ByValArray', 16)
RemoteScopeId = field 4 UInt32
RemotePort = field 5 UInt32
State = field 6 $TCP_STATE
OwningPid = field 7 UInt32
CreateTimestamp = field 8 UInt64
OwningModuleInfo = field 9 UInt64[] -MarshalAs @('ByValArray', 16)
}
$MIB_TCPTABLE_OWNER_MODULE = struct $Mod MIB_TCPTABLE_OWNER_MODULE @{
NumEntries = field 0 UInt32
Table = field 1 $MIB_TCPROW_OWNER_MODULE
}
$MIB_TCP6TABLE_OWNER_MODULE = struct $Mod MIB_TCP6TABLE_OWNER_MODULE @{
NumEntries = field 0 UInt32
Table = field 1 $MIB_TCP6ROW_OWNER_MODULE
}
#endregion Structs
#region FunctionDefinitions
$FunctionDefinitions = @(
(func iphlpapi GetExtendedTcpTable ([UInt32]) @([IntPtr], [Int32].MakeByRefType(), [Bool], [Int32], [Int32], [Int32]))
(func iphlpapi GetExtendedUdpTable ([UInt32]) @([IntPtr], [Int32].MakeByRefType(), [Bool], [Int32], [Int32], [Int32]))
)
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'NetworkConnection'
$IPHelperAPI = $Types['iphlpapi']
#endregion FunctionDefinitions
#region Helper Functions
function Get-FileHash
{
Param(
[ValidateSet('MD5','SHA1','SHA256')]
[String]
$Algorithm,
[String]
$Path
)
try
{
## Using PF here for locked file coverage
$record = Get-ForensicFileRecord -Path $Path
$Stream = $record.GetStream("")
} catch
{
throw ('Failed to open {0} for hashing.' -f $Path)
}
$StrBldr = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($Algorithm).ComputeHash($Stream) | Foreach-Object {
[Void]$StrBldr.Append($_.ToString("x2"))
}
$Stream.Close()
$StrBldr.ToString()
}
function Get-NetConnections
{
Param(
[ValidateSet('MD5','SHA1','SHA256')]
[string]
$HashAlgorithm,
[ValidateSet('TCP4','UDP4','TCP6','UDP6')]
[string]
$ProtocolVersion
)
$TableBufferSize = 0
if ($ProtocolVersion -eq 'TCP4' -or $ProtocolVersion -eq 'UDP4') {
# 2 is the magic number for IPv4
$AF_INET = 2
}
else
{
# 23 is the magic number for IPv6
$AF_INET = 23
}
if ($ProtocolVersion -eq 'TCP4' -or $ProtocolVersion -eq 'TCP6')
{
$null = $IPHelperAPI::GetExtendedTcpTable([IntPtr]::Zero, [ref]$TableBufferSize, $true, $AF_INET, $TCP_TABLE_CLASS::TCP_TABLE_OWNER_MODULE_ALL, 0)
}
else
{
$null = $IPHelperAPI::GetExtendedUdpTable([IntPtr]::Zero, [ref]$TableBufferSize, $true, $AF_INET, $UDP_TABLE_CLASS::UDP_TABLE_OWNER_MODULE, 0)
}
$TableBuffer = [Runtime.InteropServices.Marshal]::AllocHGlobal($TableBufferSize)
try
{
if ($ProtocolVersion -eq 'TCP4' -or $ProtocolVersion -eq 'TCP6')
{
$Ret = $IPHelperAPI::GetExtendedTcpTable($TableBuffer, [ref] $TableBufferSize, $true, $AF_INET, $TCP_TABLE_CLASS::TCP_TABLE_OWNER_MODULE_ALL, 0);
}
else
{
$Ret = $IPHelperAPI::GetExtendedUdpTable($TableBuffer, [ref] $TableBufferSize, $true, $AF_INET, $UDP_TABLE_CLASS::UDP_TABLE_OWNER_MODULE, 0);
}
if ($Ret -ne 0)
{
Write-Error ('Failed to get connection information. Return code: {0}' -f $Ret)
return
}
if ($ProtocolVersion -eq 'TCP4') {
$OwnerModuleTable = $TableBuffer -as $MIB_TCPTABLE_OWNER_MODULE
$RowPtr = [IntPtr]($TableBuffer.ToInt64() + [Runtime.InteropServices.Marshal]::OffsetOf($MIB_TCPTABLE_OWNER_MODULE, "Table").ToInt64())
}
elseif ($ProtocolVersion -eq 'TCP6')
{
$OwnerModuleTable = $TableBuffer -as $MIB_TCP6TABLE_OWNER_MODULE
$RowPtr = [IntPtr]($TableBuffer.ToInt64() + [Runtime.InteropServices.Marshal]::OffsetOf($MIB_TCPTABLE_OWNER_MODULE, "Table").ToInt64())
}
elseif ($ProtocolVersion -eq 'UDP4')
{
$OwnerModuleTable = $TableBuffer -as $MIB_UDPTABLE_OWNER_MODULE
$RowPtr = [IntPtr]($TableBuffer.ToInt64() + [Runtime.InteropServices.Marshal]::OffsetOf($MIB_UDPTABLE_OWNER_MODULE, "Table").ToInt64())
}
else
{
$OwnerModuleTable = $TableBuffer -as $MIB_UDP6TABLE_OWNER_MODULE
$RowPtr = [IntPtr]($TableBuffer.ToInt64() + [Runtime.InteropServices.Marshal]::OffsetOf($MIB_UDPTABLE_OWNER_MODULE, "Table").ToInt64())
}
Get-NetProperties -OwnerModuleTable $OwnerModuleTable -RowPtr $RowPtr -Protocol $ProtocolVersion -HashAlgorithm $HashAlgorithm
}
catch
{
Write-Error $_
}
finally
{
[Runtime.InteropServices.Marshal]::FreeHGlobal($TableBuffer)
}
}
function Get-NetProperties {
Param
(
$OwnerModuleTable,
$RowPtr,
$Protocol,
$HashAlgorithm
)
if ($Protocol -eq 'TCP4')
{
$MIB = $MIB_TCPROW_OWNER_MODULE
}
elseif ($Protocol -eq 'TCP6')
{
$MIB = $MIB_TCP6ROW_OWNER_MODULE
}
elseif ($Protocol -eq 'UDP4')
{
$MIB = $MIB_UDPROW_OWNER_MODULE
}
else
{
$MIB = $MIB_UDP6ROW_OWNER_MODULE
}
$NetProto = $Protocol.substring(0,$Protocol.Length-1)
for($i=0; $i -lt $OwnerModuleTable.NumEntries; $i++)
{
# Get the properties we want
$NetRow = $RowPtr -as $MIB
$LocalAddr = [System.Net.IPAddress]$NetRow.LocalAddr
$PortBytes = [System.BitConverter]::GetBytes($NetRow.LocalPort)
$LocalPort = $PortBytes[0]*256 + $PortBytes[1]
if ($Protocol -match 'TCP') {
$RemoteAddr = [System.Net.IPAddress]$NetRow.RemoteAddr
$PortBytes = [System.BitConverter]::GetBytes($NetRow.RemotePort)
$RemotePort = $PortBytes[0]*256 + $PortBytes[1]
$State = $NetRow.State.ToString()
}
else
{
$RemoteAddr = '*'
$RemotePort = '*'
$State = 'Stateless'
}
Try
{
if ($NetRow.OwningPid -ne 4 -and $NetRow.OwningPid -ne 0)
{
Get-WmiObject win32_process -Filter ProcessId=$($NetRow.OwningPid) | % { $ProcessPath = $_.Path; $ParentPID = $_.ParentProcessId}
}
elseif ($NetRow.OwningPid -eq 4)
{
$ProcessPath = 'System'
$ParentPid = 'N/A'
}
elseif ($NetRow.OwningPid -eq 0)
{
$ProcessPath = 'Idle'
$ParentPid = 'N/A'
}
} Catch {
if ([string]::IsNullOrEmpty($ProcessPath))
{
$ProcessPath = 'Process path not given'
}
if ([string]::IsNullOrEmpty($ParentPid))
{
$ParentPid = 'Parent process not given'
}
}
$Hash = $HashHashTable.$ProcessPath
if ([string]::IsNullOrEmpty($Hash))
{
if ($ProcessPath -match 'System|Idle|Process path not given')
{
$Hash = 'N/A'
$HashHashTable[$ProcessPath] = $Hash
}
else
{
$Hash = Get-FileHash -Path $ProcessPath -Algorithm $HashAlgorithm
try
{
$HashHashTable[$Path] = $Hash
}
catch {
# Ignore duplicates∫
}
}
}
$Output = @{
LocalAddress = [string]$LocalAddr
LocalPort = $LocalPort
RemoteAddress = [string]$RemoteAddr
RemotePort = $RemotePort
Hash = $Hash
ProcessName = $ProcessPath
ProcessID = $NetRow.OwningPid
ParentPID = $ParentPid
Protocol = $NetProto
State = $State
}
New-Object PSObject -Property $Output
# Move to the next row in the TCP table
$RowPtr = [IntPtr]($RowPtr.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf($NetRow))
$ProcessPath = $null
}
}
#endregion Helper Functions
## Add a global hash table for tracking paths and hash pairs
$HashHashTable = @{}
if ($env:COMPUTERNAME -eq $TargetHostname)
{
$SelectProperties = 'LocalAddress','LocalPort','RemoteAddress','RemotePort','Hash','ProcessName','ProcessId','ParentPID','Protocol','State'
Get-NetConnections -ProtocolVersion TCP4 -HashAlgorithm $HashAlgorithm | Select-Object -Property $SelectProperties | ConvertTo-Csv -NoTypeInformation -Delimiter "`t" | % { $_ -replace '"','' }
Get-NetConnections -ProtocolVersion TCP6 -HashAlgorithm $HashAlgorithm | Select-Object -Property $SelectProperties | ConvertTo-Csv -NoTypeInformation -Delimiter "`t" | Select-Object -Skip 1 | % { $_ -replace '"','' }
Get-NetConnections -ProtocolVersion UDP4 -HashAlgorithm $HashAlgorithm | Select-Object -Property $SelectProperties | ConvertTo-Csv -NoTypeInformation -Delimiter "`t" | Select-Object -Skip 1 | % { $_ -replace '"','' }
Get-NetConnections -ProtocolVersion UDP6 -HashAlgorithm $HashAlgorithm | Select-Object -Property $SelectProperties | ConvertTo-Csv -NoTypeInformation -Delimiter "`t" | Select-Object -Skip 1 | % { $_ -replace '"','' }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment