Private/Tcp445.ps1
|
#Requires -Version 5.1 function Initialize-SMBeatTcp445Native { if ('SMBeat.Native.Tcp445' -as [type]) { return } $code = @' using System; using System.Collections.Generic; using System.Net; using System.Runtime.InteropServices; namespace SMBeat.Native { public sealed class Tcp445Conn { public int Af; public string LocalIp; public int LocalPort; public string RemoteIp; public int RemotePort; public uint State; public uint LocalAddrV4; public uint RemoteAddrV4; public byte[] LocalAddrV6; public byte[] RemoteAddrV6; public uint LocalScopeId; public uint RemoteScopeId; public ulong BytesOut; public ulong BytesIn; public bool ReadOk; } public static class Tcp445 { private const int AF_INET = 2; private const int AF_INET6 = 23; private const int TCP_TABLE_OWNER_PID_CONNECTIONS = 4; private const uint ERROR_INSUFFICIENT_BUFFER = 122; private const int TcpConnectionEstatsData = 1; private const int MIB_TCP_STATE_ESTAB = 5; private const int SMB_PORT = 445; [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedTcpTable( IntPtr pTcpTable, ref int pdwSize, bool bOrder, int ulAf, int TableClass, int Reserved); [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint SetPerTcpConnectionEStats( IntPtr Row, int EstatsType, byte[] Rw, uint RwVersion, uint RwSize, uint Offset); [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetPerTcpConnectionEStats( IntPtr Row, int EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, IntPtr Ros, uint RosVersion, uint RosSize, byte[] Rod, uint RodVersion, uint RodSize); [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint SetPerTcp6ConnectionEStats( IntPtr Row, int EstatsType, byte[] Rw, uint RwVersion, uint RwSize, uint Offset); [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetPerTcp6ConnectionEStats( IntPtr Row, int EstatsType, IntPtr Rw, uint RwVersion, uint RwSize, IntPtr Ros, uint RosVersion, uint RosSize, byte[] Rod, uint RodVersion, uint RodSize); private static int HostPort(uint netPort) { int p = (int)(netPort & 0xFFFF); return ((p >> 8) & 0xFF) | ((p & 0xFF) << 8); } private static bool IsLoopbackV4(uint addr) { return (addr & 0xFF) == 127; } private static bool IsLoopbackV6(byte[] addr) { if (addr == null || addr.Length < 16) { return false; } for (int i = 0; i < 15; i++) { if (addr[i] != 0) { return false; } } return addr[15] == 1; } public static Tcp445Conn[] ListEstablished() { List<Tcp445Conn> list = new List<Tcp445Conn>(); AddV4(list); AddV6(list); return list.ToArray(); } public static bool Enable(Tcp445Conn conn) { if (conn == null) { return false; } byte[] rw = new byte[] { 1 }; IntPtr row = AllocRow(conn); try { uint rc; if (conn.Af == AF_INET6) { rc = SetPerTcp6ConnectionEStats(row, TcpConnectionEstatsData, rw, 0, 1, 0); } else { rc = SetPerTcpConnectionEStats(row, TcpConnectionEstatsData, rw, 0, 1, 0); } return rc == 0; } finally { Marshal.FreeHGlobal(row); } } public static bool TryRead(Tcp445Conn conn) { if (conn == null) { return false; } byte[] rod = new byte[96]; IntPtr row = AllocRow(conn); try { uint rc; if (conn.Af == AF_INET6) { rc = GetPerTcp6ConnectionEStats(row, TcpConnectionEstatsData, IntPtr.Zero, 0, 0, IntPtr.Zero, 0, 0, rod, 0, 96); } else { rc = GetPerTcpConnectionEStats(row, TcpConnectionEstatsData, IntPtr.Zero, 0, 0, IntPtr.Zero, 0, 0, rod, 0, 96); } if (rc != 0) { conn.ReadOk = false; return false; } conn.BytesOut = BitConverter.ToUInt64(rod, 0); conn.BytesIn = BitConverter.ToUInt64(rod, 8); conn.ReadOk = true; return true; } finally { Marshal.FreeHGlobal(row); } } private static void AddV4(List<Tcp445Conn> list) { int size = 0; uint rc = GetExtendedTcpTable(IntPtr.Zero, ref size, false, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0); if (rc != ERROR_INSUFFICIENT_BUFFER || size <= 0) { return; } IntPtr buf = Marshal.AllocHGlobal(size); try { rc = GetExtendedTcpTable(buf, ref size, false, AF_INET, TCP_TABLE_OWNER_PID_CONNECTIONS, 0); if (rc != 0) { return; } int count = Marshal.ReadInt32(buf); int offset = 4; for (int i = 0; i < count; i++) { uint state = (uint)Marshal.ReadInt32(buf, offset); uint localAddr = (uint)Marshal.ReadInt32(buf, offset + 4); uint localPort = (uint)Marshal.ReadInt32(buf, offset + 8); uint remoteAddr = (uint)Marshal.ReadInt32(buf, offset + 12); uint remotePort = (uint)Marshal.ReadInt32(buf, offset + 16); offset += 24; if ((int)state != MIB_TCP_STATE_ESTAB) { continue; } if (HostPort(localPort) != SMB_PORT) { continue; } if (IsLoopbackV4(localAddr) || IsLoopbackV4(remoteAddr)) { continue; } Tcp445Conn c = new Tcp445Conn(); c.Af = AF_INET; c.State = state; c.LocalAddrV4 = localAddr; c.RemoteAddrV4 = remoteAddr; c.LocalPort = HostPort(localPort); c.RemotePort = HostPort(remotePort); c.LocalIp = new IPAddress((long)localAddr).ToString(); c.RemoteIp = new IPAddress((long)remoteAddr).ToString(); list.Add(c); } } finally { Marshal.FreeHGlobal(buf); } } private static void AddV6(List<Tcp445Conn> list) { int size = 0; uint rc = GetExtendedTcpTable(IntPtr.Zero, ref size, false, AF_INET6, TCP_TABLE_OWNER_PID_CONNECTIONS, 0); if (rc != ERROR_INSUFFICIENT_BUFFER || size <= 0) { return; } IntPtr buf = Marshal.AllocHGlobal(size); try { rc = GetExtendedTcpTable(buf, ref size, false, AF_INET6, TCP_TABLE_OWNER_PID_CONNECTIONS, 0); if (rc != 0) { return; } int count = Marshal.ReadInt32(buf); int offset = 4; for (int i = 0; i < count; i++) { byte[] localAddr = new byte[16]; Marshal.Copy(IntPtr.Add(buf, offset), localAddr, 0, 16); uint localScope = (uint)Marshal.ReadInt32(buf, offset + 16); uint localPort = (uint)Marshal.ReadInt32(buf, offset + 20); byte[] remoteAddr = new byte[16]; Marshal.Copy(IntPtr.Add(buf, offset + 24), remoteAddr, 0, 16); uint remoteScope = (uint)Marshal.ReadInt32(buf, offset + 40); uint remotePort = (uint)Marshal.ReadInt32(buf, offset + 44); uint state = (uint)Marshal.ReadInt32(buf, offset + 48); offset += 56; if ((int)state != MIB_TCP_STATE_ESTAB) { continue; } if (HostPort(localPort) != SMB_PORT) { continue; } if (IsLoopbackV6(localAddr) || IsLoopbackV6(remoteAddr)) { continue; } Tcp445Conn c = new Tcp445Conn(); c.Af = AF_INET6; c.State = state; c.LocalAddrV6 = localAddr; c.RemoteAddrV6 = remoteAddr; c.LocalScopeId = localScope; c.RemoteScopeId = remoteScope; c.LocalPort = HostPort(localPort); c.RemotePort = HostPort(remotePort); c.LocalIp = new IPAddress(localAddr).ToString(); c.RemoteIp = new IPAddress(remoteAddr).ToString(); list.Add(c); } } finally { Marshal.FreeHGlobal(buf); } } private static IntPtr AllocRow(Tcp445Conn conn) { if (conn.Af == AF_INET6) { IntPtr p = Marshal.AllocHGlobal(52); Marshal.WriteInt32(p, 0, (int)conn.State); if (conn.LocalAddrV6 != null) { Marshal.Copy(conn.LocalAddrV6, 0, IntPtr.Add(p, 4), 16); } Marshal.WriteInt32(p, 20, (int)conn.LocalScopeId); int lp = ((conn.LocalPort & 0xFF) << 8) | ((conn.LocalPort >> 8) & 0xFF); Marshal.WriteInt32(p, 24, lp); if (conn.RemoteAddrV6 != null) { Marshal.Copy(conn.RemoteAddrV6, 0, IntPtr.Add(p, 28), 16); } Marshal.WriteInt32(p, 44, (int)conn.RemoteScopeId); int rp = ((conn.RemotePort & 0xFF) << 8) | ((conn.RemotePort >> 8) & 0xFF); Marshal.WriteInt32(p, 48, rp); return p; } IntPtr v4 = Marshal.AllocHGlobal(20); Marshal.WriteInt32(v4, 0, (int)conn.State); Marshal.WriteInt32(v4, 4, (int)conn.LocalAddrV4); int lp4 = ((conn.LocalPort & 0xFF) << 8) | ((conn.LocalPort >> 8) & 0xFF); Marshal.WriteInt32(v4, 8, lp4); Marshal.WriteInt32(v4, 12, (int)conn.RemoteAddrV4); int rp4 = ((conn.RemotePort & 0xFF) << 8) | ((conn.RemotePort >> 8) & 0xFF); Marshal.WriteInt32(v4, 16, rp4); return v4; } } } '@ Add-Type -TypeDefinition $code -Language CSharp -ErrorAction Stop } function Get-SMBeatTcp445ConnectionKey { param( [Parameter(Mandatory = $true)] $Conn ) '{0}|{1}|{2}|{3}|{4}' -f $Conn.Af, $Conn.LocalIp, $Conn.LocalPort, $Conn.RemoteIp, $Conn.RemotePort } function Get-SMBeatTcp445LiveConnections { $result = New-Object System.Collections.Generic.List[object] try { Initialize-SMBeatTcp445Native } catch { return @() } $rows = @([SMBeat.Native.Tcp445]::ListEstablished()) foreach ($row in $rows) { [void][SMBeat.Native.Tcp445]::Enable($row) $ok = [SMBeat.Native.Tcp445]::TryRead($row) if (-not $ok) { continue } $result.Add([PSCustomObject]@{ Key = (Get-SMBeatTcp445ConnectionKey -Conn $row) RemoteIp = [string]$row.RemoteIp BytesOut = [uint64]$row.BytesOut BytesIn = [uint64]$row.BytesIn }) | Out-Null } return $result.ToArray() } function New-SMBeatTcp445Tracker { @{} } function Update-SMBeatTcp445Tracker { param( [Parameter(Mandatory = $true)] [hashtable]$Tracker, [AllowEmptyCollection()] [object[]]$Live ) foreach ($key in @($Tracker.Keys)) { $Tracker[$key].Seen = $false } if ($null -eq $Live) { $Live = @() } foreach ($row in $Live) { $key = [string]$row.Key $out = [uint64]$row.BytesOut $inb = [uint64]$row.BytesIn if (-not $Tracker.ContainsKey($key)) { $Tracker[$key] = @{ RemoteIp = [string]$row.RemoteIp LastOut = $out LastIn = $inb PendingOut = [int64]0 PendingIn = [int64]0 Seen = $true } continue } $e = $Tracker[$key] $e.PendingOut += (Get-SMBeatByteDelta -Current $out -Previous ([uint64]$e.LastOut)) $e.PendingIn += (Get-SMBeatByteDelta -Current $inb -Previous ([uint64]$e.LastIn)) $e.LastOut = $out $e.LastIn = $inb $e.Seen = $true $e.RemoteIp = [string]$row.RemoteIp } } function Get-SMBeatTcp445PendingDeltas { param( [Parameter(Mandatory = $true)] [hashtable]$Tracker, [switch]$Reset ) $byClient = @{} foreach ($key in @($Tracker.Keys)) { $e = $Tracker[$key] $ip = [string]$e.RemoteIp if ([string]::IsNullOrWhiteSpace($ip)) { continue } if (-not $byClient.ContainsKey($ip)) { $byClient[$ip] = @{ Sent = [int64]0 Received = [int64]0 } } $byClient[$ip].Sent += [int64]$e.PendingOut $byClient[$ip].Received += [int64]$e.PendingIn if ($Reset) { $e.PendingOut = [int64]0 $e.PendingIn = [int64]0 } } $list = New-Object System.Collections.Generic.List[object] foreach ($ip in $byClient.Keys) { $list.Add([PSCustomObject]@{ RemoteIp = $ip Sent = [int64]$byClient[$ip].Sent Received = [int64]$byClient[$ip].Received }) | Out-Null } return $list.ToArray() } function Remove-SMBeatTcp445Stale { param( [Parameter(Mandatory = $true)] [hashtable]$Tracker ) foreach ($key in @($Tracker.Keys)) { if (-not [bool]$Tracker[$key].Seen) { $Tracker.Remove($key) } } } function ConvertTo-SMBeatTcp445FlushRecords { param( [AllowEmptyCollection()] [object[]]$Deltas, [Parameter(Mandatory = $true)] [string]$Server, [Parameter(Mandatory = $true)] [datetime]$Utc, [Parameter(Mandatory = $true)] [int]$IntervalSec ) $records = New-Object System.Collections.Generic.List[object] $sumSent = [int64]0 $sumRecv = [int64]0 if ($null -eq $Deltas) { $Deltas = @() } foreach ($row in $Deltas) { $sumSent += [int64]$row.Sent $sumRecv += [int64]$row.Received $snap = New-SMBeatCounterSnapshot -Kind 'tcp445' -Name ([string]$row.RemoteIp) ` -ReadRaw 0 -WriteRaw 0 -SentRaw 0 -ReceivedRaw 0 -Client ([string]$row.RemoteIp) $records.Add((ConvertTo-SMBeatSampleRecord -Snapshot $snap -Server $Server -Utc $Utc -IntervalSec $IntervalSec ` -ReadDelta 0 -WriteDelta 0 -SentDelta ([int64]$row.Sent) -ReceivedDelta ([int64]$row.Received))) | Out-Null } $serverSnap = New-SMBeatCounterSnapshot -Kind 'tcp445' -Name '_listen445' ` -ReadRaw 0 -WriteRaw 0 -SentRaw 0 -ReceivedRaw 0 $records.Insert(0, (ConvertTo-SMBeatSampleRecord -Snapshot $serverSnap -Server $Server -Utc $Utc -IntervalSec $IntervalSec ` -ReadDelta 0 -WriteDelta 0 -SentDelta $sumSent -ReceivedDelta $sumRecv)) return $records.ToArray() } function Update-SMBeatEtwShareHook { # TODO B: resident ETW Microsoft-Windows-SMBServer for bytes per share. } |