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

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Technorati Tags: , , , , , , , ,

Tags: , , , , , , , ,

3 Responses to “Analyze-Packet Reloaded”

  1. Jason Stangroome Says:

    How does Analyze-Packet cope with IPv6?

  2. Jeffery Hicks Says:

    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.

  3. Robbie Foust Says:

    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. :-)


Entries (RSS) and Comments (RSS).