Analyze-Packet Reloaded

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