While getting caught up on my podcast back log I was listening to an episode of Mind of Root. In it Steven Murawski mentioned my Get-Packet and Analyze-Packet PowerShell scripts. One of his (legitimate) issues with Analyze-Packet was the way it presented results. The original version simply wrote information to the console. It didn’t use a good object model so that you could format the results the way you wanted. I took this as a challenge to improve the script. Here is a new improved version of Analyze-Packet with some additional improvements from Steven.
1: Param([object]$sniff =$(throw 'USAGE: Analyze-Packet -Sniff (result of Get-Packet script) [-NoResolution]'), [switch]$NoResolution)
2:
3:
4: #Changed by Steven Murawski 08/14/08
5: #Refactored common code from DNS lookup for source and destination IPs
6: function ResolveIPAddress([string]$status, [Object[]]$IPAdresses )
7: {
8: #turn off errors to prevent the script from halting if an IP can't be resolved
9: $errorActionPreference="SilentlyContinue"
10:
11: $hosts = @()
12:
13: for ($i=0;$i -lt $IPAdresses.count;$i++)
14: {
15: Write-Progress -Activity $activity -status $status -currentoperation $IPAdresses[$i].IP -percentcomplete (($i/$IPAdresses.count)*100)
16: Write-Debug $IPAdresses[$i].IP
17:
18: $resolved = [system.Net.dns]::getHostEntry($IPAdresses[$i].IP)
19:
20: if ($resolved.HostName)
21: {
22: $resolvedhost=$resolved.hostname
23: }
24: else
25: {
26: $resolvedhost=$IPAdresses[$i].IP
27: }
28:
29: $obj=New-Object PSObject
30:
31: $obj | Add-Member -MemberType Noteproperty -name "Count" -value $IPAdresses[$i].Count
32: $obj | Add-Member -MemberType Noteproperty -name "IP" -value $IPAdresses[$i].IP
33: $obj | Add-Member -MemberType Noteproperty -name "Host" -value $resolvedhost
34:
35: $hosts+=$obj
36:
37: #reset $resolved
38: Remove-Variable resolved
39:
40:
41: }
42: #turn error pipeline back on. Not really necessary because of scop
43: #but including for consistency
44:
45: $errorActionPreference="Continue"
46: $hosts
47: }
48:
49: #main body of script
50:
51: $activity="Analyzing network trace"
52: Write-Progress -Activity $activity -status "Counting packets" -percentcomplete 1
53: $count = $sniff.count
54:
55: Write-Progress -Activity $activity -status "Calculating elapsed time" -percentcomplete 5
56: $elapsed = $sniff[-1].time -$sniff[0].time
57:
58: #calculate packets per second
59: $pps = $sniff.count/(($sniff[-1].time -$sniff[0].time).totalseconds)
60: $pps="{0:N4}" -f $pps
61:
62: Write-Progress -Activity $activity -status "Calculating protocol distribution" -percentcomplete 20
63: $protocols = $sniff | sort protocol | group protocol | sort count -descending | select Count,@{name="Protocol";Expression={$_.name}}
64:
65: Write-Progress -Activity $activity -status "Calculating source port distribution" -percentcomplete 40
66: $sourceport = $sniff | sort sourceport | group sourceport |sort count -descending | select Count,@{name="Port";Expression={$_.name}}
67:
68: Write-Progress -Activity $activity -status "Calculating destination distribution" -percentcomplete 50
69: $destinationlist=$sniff | sort Destination | select Destination
70:
71: Write-Progress -Activity $activity -status "Calculating destination port distribution" -percentcomplete 60
72: $destinationport = $sniff | sort destport | group destport |sort count -descending | select Count,@{name="IP";Expression={$_.name}}
73:
74: Write-Progress -Activity $activity -status "Building source list" -percentcomplete 30
75: $sourcelist = $sniff | sort source | select Source
76:
77: Write-Progress -Activity $activity -status "Building source IP list" -percentcomplete 35
78: $ips = $sourcelist| group source |sort count -descending | select Count,@{Name="IP";Expression={$_.Name}}
79:
80:
81: $sources = $null
82:
83: if ($NoResolution)
84: {
85: $sources = $ips
86: }
87: else
88: {
89: $sources = ResolveIPAddress 'Resolving Source IP addresses' $ips
90: }
91:
92: Write-Progress -Activity $activity -status "Build destination IP list" -percentcomplete 35
93: $ipd = $destinationlist| group destination |sort count -descending | select Count,@{Name="IP";Expression={$_.Name}}
94:
95: $destinations = $null
96:
97: if ($NoResolution)
98: {
99: $destinations = $ipd
100: }
101: else
102: {
103: $destinations = ResolveIPAddress 'Resolving Destination IP addresses' $ipd
104: }
105:
106: Write-Progress -Activity $activity -status "Presenting data" -Completed $True -percentcomplete 100
107:
108: $protocols = $protocols | Select Count,Protocol,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
109:
110: $destinationport = $destinationport | select Count,Port,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
111:
112: $sourceport = $sourceport | Select Count,Port,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
113:
114: if ($NoResolution)
115: {
116: $destinations = $destinations | Select Count,IP,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
117: $sources = $sources | select Count,IP,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
118: }
119: else
120: {
121: $destinations = $destinations | Select Count,IP,Host,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
122: $sources = $sources | Select Count,IP,Host,@{Name="Percentage";Expression={"{0:P4}" -f ($_.count/$sniff.count)}}
123: }
124:
125: #write results to a custom object
126: $results=New-Object PSobject
127:
128: $results | Add-Member -MemberType Noteproperty -Name "TotalPackets" -Value $sniff.count
129: $results | Add-Member -MemberType Noteproperty -Name "Elapsedtime" -Value $elapsed
130: $results | Add-Member -MemberType Noteproperty -Name "PacketsPerSec" -Value $pps
131: $results | Add-Member -MemberType Noteproperty -Name "Protocols" -value $protocols
132: $results | Add-Member -MemberType Noteproperty -Name "Destinations" -value $destinations
133: $results | Add-Member -MemberType Noteproperty -Name "DestinationPorts" -value $destinationport
134: $results | Add-Member -MemberType Noteproperty -Name "Sources" -value $sources
135: $results | Add-Member -MemberType Noteproperty -Name "SourcePorts" -value $sourceport
136:
137: write $results
The new version includes a switch parameter to disable name resolution. By default the script will attempt to resolve host names from IP addresses which is what I prefer. But there may be times when you don’t want to resolve names which will speed up analysis considerably. In those instances use -NoResolution. Of course, you can modify the script to make this the default and use the switch to enable name resolution.
During testing and development I also found an issue with the way I was getting and stored IP addresses. I was having problems when using a network capture re-hydrated from an XML file. The export file was storing an IP object when all I really wanted was the IP address as a string. I went back and modified Get-Packet and now you can use its results with this version of Analyze-Packet.
The main addition to Analyze-Packet is that it writes a single object to the pipeline. Some of the property values are nested objects. For example, the Protocols property is an embedded object with properties Count,Protocol and Percentage:
Count Protocol Percentage
—– ——– ———-
22353 TCP 98.4670 %
316 UDP 1.3920 %
32 ICMP 0.1410 %
Other PowerShell objects use do this so I figured I would as well. Here’s an example of what this looks like and how you might use it.
PS C:\test> $report=c:\scripts\posh\analyze-packet $sniff
PS C:\test> $report
TotalPackets : 22701
Elapsedtime : 00:23:16.7070473
PacketsPerSec : 16.2532
Protocols : {@{Count=22353; Protocol=TCP; Percentage=98.4670 %}, @{Count=316; Protocol=UDP; Percentage=1.3920 %}
, @{Count=32; Protocol=ICMP; Percentage=0.1410 %}}
Destinations : {@{Count=22556; IP=172.16.10.102; Host=172.16.10.102; Percentage=99.3613 %}, @{Count=78; IP=255.255.
255.255; Host=255.255.255.255; Percentage=0.3436 %}, @{Count=61; IP=172.16.255.255; Host=172.16.255.
255; Percentage=0.2687 %}, @{Count=6; IP=224.0.0.252; Host=224.0.0.252; Percentage=0.0264 %}}
DestinationPorts : {@{Count=2453; Port=; Percentage=10.8057 %}, @{Count=2424; Port=; Percentage=10.6779 %}, @{Count=240
0; Port=; Percentage=10.5722 %}, @{Count=2337; Port=; Percentage=10.2947 %}…}
Sources : {@{Count=2497; IP=208.117.234.45; Host=mia-v148.mia.youtube.com; Percentage=10.9995 %}, @{Count=2453
; IP=74.125.15.20; Host=74.125.15.20; Percentage=10.8057 %}, @{Count=2424; IP=208.117.234.177; Host=
mia-v228.mia.youtube.com; Percentage=10.6779 %}, @{Count=2400; IP=74.125.15.36; Host=74.125.15.36; P
ercentage=10.5722 %}…}
SourcePorts : {@{Count=18032; Port=80; Percentage=79.4326 %}, @{Count=2552; Port=445; Percentage=11.2418 %}, @{Cou
nt=618; Port=3000; Percentage=2.7223 %}, @{Count=308; Port=2492; Percentage=1.3568 %}…}
PS C:\test> $report.destinations
Count IP Host Percentage
—– — —- ———-
22556 172.16.10.102 172.16.10.102 99.3613 %
78 255.255.255.255 255.255.255.255 0.3436 %
61 172.16.255.255 172.16.255.255 0.2687 %
6 224.0.0.252 224.0.0.252 0.0264 %
PS C:\test> $report.sources | select -first 5
Count IP Host Percentage
—– — —- ———-
2497 208.117.234.45 mia-v148.mia.youtube.com 10.9995 %
2453 74.125.15.20 74.125.15.20 10.8057 %
2424 208.117.234.177 mia-v228.mia.youtube.com 10.6779 %
2400 74.125.15.36 74.125.15.36 10.5722 %
2337 64.15.113.36 lax-v253.lax.youtube.com 10.2947 %
PS C:\test> $report.sourceports | select -first 5
Count Port Percentage
—– —- ———-
18032 80 79.4326 %
2552 445 11.2418 %
618 3000 2.7223 %
308 2492 1.3568 %
305 143 1.3436 %
PS C:\test>
While these packet scripts may not find much production use, I hope they provide examples of scripting techniques especially the use of custom objects, custom properties, Write-Progress and functions. If you have additional suggestions, please leave a comment and let me know.
Download a zip file with both updated scripts here
How does Analyze-Packet cope with IPv6?
All Analyze-Packet is doing is parsing a saved network trace from Get-Packet. But I understand your question. I know that get-packet is reading different bytes of the captured packet to identify items like source and destination. I think the IPv6 packet has a different structure but don’t know for a fact. That’s outside my realm these days. If the packet layout is the same it would work, except for name resolution probably. If you have an IPv6 network to test on please let me know the results.
Yes, IPv6 uses a different structure. I’ll have to see if I can figure it out (I’m on a ipv6 network). Everything I did in the original get-packet script was trial & error, and a *lot* of googling, so no promises. 🙂