Created
August 17, 2018 17:41
-
-
Save davehull/a8f8adaf188cd6922cd663791066e16b to your computer and use it in GitHub Desktop.
Netstat like data with hashes from PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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