# My Recent Powershell Modules

By
,
Powershell
,
Modules
Published 2022-07-16

TShark Module
https://www.powershellgallery.com/packages/TShark/1.0.2

# Windows Administrators 🐭

So I have found over the decades of working in IT that not many system administrators know how to code. Yeah they know how to google and copy and paste code, and run the script they found. But when it comes to modifying that code, it doesn't seem possible to that person, as they don't have a scooby-doo about what the code is doing, or how to read it. Most places I have worked only a very few people can actually code to solve a given problem. Due to the stress of certain jobs, you might find that the one person who can help is a bit like

An angry sysadmin coder
An angry sysadmin coder

# I needed a point and click 🐁

Yep I hate to admit it but this year I was trying to solve a complicated issue that had existed before I even started the job. On the plus side I knew it wasn't my fault, or I couldn't take the blame. However neither of those plus signs was going to solve the problem. Although I had know about the program for sometime, I hadn't had a real use-case for using WireShark. Before I knew it I had used WireShark to analyze a number of bizarre issues I had been gifted with to solve. Again this got me thinking why isn't there a Powershell equivalent of this program? I did a bit of training on the program through various sources and it was mentioned there is the command-line version TShark.exe which could also be used to take network captures.

# TShark for Powershell 🦈

TShark lets you hunt down network issues just like a real shark
TShark lets you hunt down network issues just like a real shark

First of all just incase you didn't know, but WireShark has a command line tool that allows you to capture a network trace file. Although this may not be as graphical as the GUI version, it does give you all the same functionality, and allows you to do cool things like starting a network capture, that will output in 50MB files and make 10 of these files then keep cycling through over-writing the oldest file, so you could keep a network trace running for a long time, without worrying about filling up the disk, or having a gigantic trace file at the end of it. This is just one of the many things you can do with TShark. One of the things I do not like about using certain command line tools, is the parameters they use sometimes make little to no sense, or they might just be a bit too much to remember. By re-building this command line tool TShark into a Powershell module I would be able to decide on the parameter names, and which parameters to include. The only downside to deciding to rebuild an application into a module using crescendo, is it will not include the program in the module. However a few lines of Powershell code that you can install WireShark which includes TShark:-

Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install git
scoop bucket add extras
scoop install wireshark

I have included the code above in the module as an additonal function, to allow you to automatically install the required software to use the module. This module is a function module, please look through the code to see what it does, as I did spend a good amount of time on the help for this module so that you could fully understand how to get a network trace easily from Powershell to then either diagnose in the shell or to open the captured trace file in WireShark.

# Module created by Microsoft.PowerShell.Crescendo
class PowerShellCustomFunctionAttribute : System.Attribute { 
    [bool]$RequiresElevation
    [string]$Source
    PowerShellCustomFunctionAttribute() { $this.RequiresElevation = $false; $this.Source = "Microsoft.PowerShell.Crescendo" }
    PowerShellCustomFunctionAttribute([bool]$rElevation) {
        $this.RequiresElevation = $rElevation
        $this.Source = "Microsoft.PowerShell.Crescendo"
    }
}

function Start-TShark
{
[PowerShellCustomFunctionAttribute(RequiresElevation=$False)]
[CmdletBinding()]

param(
[Parameter()]
[String]$SetInterface,
[Parameter()]
[String]$SetCaptureFilter,
[Parameter()]
[Switch]$SetDataLinkType,
[Parameter()]
[Switch]$GetInterfaces,
[Parameter()]
[Switch]$GetDataLinkTypes,
[Parameter()]
[String]$Path,
[Parameter()]
[ValidateSet("pcap","pcapng","5views","btsnoop","commview-ncf","commview-ncfx","dct2000","erf","eyesdn","k12text","lanalyzer","logcat","logcat-brief","logcat-long","logcat-process","logcat-tag","logcat-thread","logcat-threadtime","logcat-time","modpcap","netmon1","netmon2","nettl","ngsniffer","ngwsniffer_1_1","ngwsniffer_2_0","nokiapcap","nsecpcap","nstrace10","nstrace20","nstrace30","nstrace35","observer","rf5","rh6_1pcap","snoop","suse6_3pcap","visual")]
[String]$SetFileType,
[Parameter()]
[String]$LogPath,
[Parameter()]
[ValidateScript(
    {if ($_ -match "filesize:[0-9]+$")
     {
       $true  
     }
     else
     {
         throw 'use filesize: followed by number which represents KiloBytes Example:- filesize:1024 this will output 1mb capture files.'
     }
  })]
  [ArgumentCompleter(
    {
      param($cmd, $param, $wordToComplete)
      # This is the duplicated part of the code in the [ValidateScipt] attribute.
      [array] $validValues = [regex]"filesize:[0-9]+$"
      $validValues -like "$wordToComplete*"
    }
  )]
[String]$SetFileCaptureSizeKB,
[Parameter()]
[ValidateScript(
    {if ($_ -match "duration:[0-9]+$")
     {
       $true  
     }
     else
     {
         throw 'use duration: followed by number which represents seconds in time Example:- duration:60 this will output a capture files 60 seconds apart.'
     }
  })]
  [ArgumentCompleter(
    {
      param($cmd, $param, $wordToComplete)
      # This is the duplicated part of the code in the [ValidateScipt] attribute.
      [array] $validValues = [regex]"duration:[0-9]+$"
      $validValues -like "$wordToComplete*"
    }
  )]
[String]$SetFileCaptureDuration,
[Parameter()]
[ValidateScript(
    {if ($_ -match "files:[0-9]+$")
     {
       $true  
     }
     else
     {
         throw 'use files: followed by number which represents the number of 5 in total to capture Example:- files:10 this will output 10 capture files and then replace.'
     }
  })]
  [ArgumentCompleter(
    {
      param($cmd, $param, $wordToComplete)
      # This is the duplicated part of the code in the [ValidateScipt] attribute.
      [array] $validValues = [regex]"files:[0-9]+$"
      $validValues -like "$wordToComplete*"
    }
  )]
[String]$SetNumberCaptureFiles,
[Parameter()]
[Switch]$GetVersion,
[Parameter()]
[String]$OpenFile,
[Parameter()]
[ValidateSet("expert","afp,srt","ancp,tree","ansi_a,bsmap","ansi_a,dtap","ansi_map","asap,stat","bacapp_instanceid,tree","bacapp_ip,tree","bacapp_objectid,tree","bacapp_service,tree","calcappprotocol,stat","camel,counter","camel,srt","collectd,tree","componentstatusprotocol,stat","conv,bluetooth","conv,dccp","conv,eth","conv,fc","conv,fddi","conv,ip","conv,ipv6","conv,ipx","conv,jxta","conv,mptcp","conv,ncp","conv,rsvp","conv,sctp","conv,sll","conv,tcp","conv,tr","conv,udp","conv,usb","conv,wlan","conv,wpan","conv,zbee_nwk","credentials","dcerpc,srt","dests,tree","dhcp,stat","diameter,avp","diameter,srt","dns,tree","endpoints,bluetooth","endpoints,dccp","endpoints,eth","endpoints,fc","endpoints,fddi","endpoints,ip","endpoints,ipv6","endpoints,ipx","endpoints,jxta","endpoints,mptcp","endpoints,ncp","endpoints,rsvp","endpoints,sctp","endpoints,sll","endpoints,tcp","endpoints,tr","endpoints,udp","endpoints,usb","endpoints,wlan","endpoints,wpan","endpoints,zbee_nwk","enrp,stat","expert","f1ap,tree","f5_tmm_dist,tree","f5_virt_dist,tree","fc,srt","flow,any","flow,icmp","flow,icmpv6","flow,lbm_uim","flow,tcp","follow,dccp","follow,http","follow,http2","follow,quic","follow,sip","follow,tcp","follow,tls","follow,udp","fractalgeneratorprotocol,stat","gsm_a","gsm_a,bssmap","gsm_a,dtap_cc","gsm_a,dtap_gmm","gsm_a,dtap_mm","gsm_a,dtap_rr","gsm_a,dtap_sacch","gsm_a,dtap_sm","gsm_a,dtap_sms","gsm_a,dtap_ss","gsm_a,dtap_tp","gsm_map,operation","gtp,srt","h225,counter","h225_ras,rtd","hart_ip,tree","hosts","hpfeeds,tree","http,stat","http,tree","http2,tree","http_req,tree","http_seq,tree","http_srv,tree","icmp,srt","icmpv6,srt","io,phs","io,stat","ip_hosts,tree","ip_srcdst,tree","ipv6_dests,tree","ipv6_hosts,tree","ipv6_ptype,tree","ipv6_srcdst,tree","isup_msg,tree","lbmr_queue_ads_queue,tree","lbmr_queue_ads_source,tree","lbmr_queue_queries_queue,tree","lbmr_queue_queries_receiver,tree","lbmr_topic_ads_source,tree","lbmr_topic_ads_topic,tree","lbmr_topic_ads_transport,tree","lbmr_topic_queries_pattern,tree","lbmr_topic_queries_pattern_receiver,tree","lbmr_topic_queries_receiver,tree","lbmr_topic_queries_topic,tree","ldap,srt","mac-lte,stat","megaco,rtd","mgcp,rtd","mtp3,msus","ncp,srt","ngap,tree","npm,stat","osmux,tree","pingpongprotocol,stat","plen,tree","proto,colinfo","ptype,tree","radius,rtd","rlc-lte,stat","rpc,programs","rpc,srt","rtp,streams","rtsp,stat","rtsp,tree","sametime,tree","scsi,srt","sctp,stat","sip,stat","smb,sids","smb,srt","smb2,srt","smpp_commands,tree","snmp,srt","ssprotocol,stat","sv","ucp_messages,tree","wsp,stat")]
[String]$GetStatistics
    )

BEGIN {
    $__PARAMETERMAP = @{
         SetInterface = @{
               OriginalName = '-i'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetCaptureFilter = @{
               OriginalName = '-f'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetDataLinkType = @{
               OriginalName = '-y'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'Switch'
               ApplyToExecutable = $False
               NoGap = $False
               }
         GetInterfaces = @{
               OriginalName = '-D'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'Switch'
               ApplyToExecutable = $False
               NoGap = $False
               }
         GetDataLinkTypes = @{
               OriginalName = '-L'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'Switch'
               ApplyToExecutable = $False
               NoGap = $False
               }
         Path = @{
               OriginalName = '-w'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetFileType = @{
               OriginalName = '-F'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         LogPath = @{
               OriginalName = '--log-file'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetFileCaptureSizeKB = @{
               OriginalName = '-b'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetFileCaptureDuration = @{
               OriginalName = '-b'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         SetNumberCaptureFiles = @{
               OriginalName = '-b'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
         GetVersion = @{
               OriginalName = '-v'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'Switch'
               ApplyToExecutable = $False
               NoGap = $False
               }
        OpenFile = @{
               OriginalName = '-r'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
        GetStatistics = @{
               OriginalName = '-z'
               OriginalPosition = '0'
               Position = '2147483647'
               ParameterType = 'String'
               ApplyToExecutable = $False
               NoGap = $False
               }
    }

    $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } }
}

PROCESS {
    $__boundParameters = $PSBoundParameters
    $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name
    $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_})
    $__commandArgs = @()
    $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)})
    if ($__boundParameters["Debug"]){wait-debugger}
    foreach ($paramName in $__boundParameters.Keys|
            Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}|
            Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) {
        $value = $__boundParameters[$paramName]
        $param = $__PARAMETERMAP[$paramName]
        if ($param) {
            if ($value -is [switch]) {
                 if ($value.IsPresent) {
                     if ($param.OriginalName) { $__commandArgs += $param.OriginalName }
                 }
                 elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue }
            }
            elseif ( $param.NoGap ) {
                $pFmt = "{0}{1}"
                if($value -match "\s") { $pFmt = "{0}""{1}""" }
                $__commandArgs += $pFmt -f $param.OriginalName, $value
            }
            else {
                if($param.OriginalName) { $__commandArgs += $param.OriginalName }
                $__commandArgs += $value | Foreach-Object {$_}
            }
        }
    }
    $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null}
    if ($__boundParameters["Debug"]){wait-debugger}
    if ( $__boundParameters["Verbose"]) {
         Write-Verbose -Verbose -Message tshark.exe
         $__commandArgs | Write-Verbose -Verbose
    }
    $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName]
    if (! $__handlerInfo ) {
        $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present
    }
    $__handler = $__handlerInfo.Handler
    if ( $PSCmdlet.ShouldProcess("tshark.exe $__commandArgs")) {
    # check for the application and throw if it cannot be found
        if ( -not (Get-Command -ErrorAction Ignore "tshark.exe")) {
          throw "Cannot find executable 'tshark.exe'"
        }
        if ( $__handlerInfo.StreamOutput ) {
            & "tshark.exe" $__commandArgs | & $__handler
        }
        else {
            $result = & "tshark.exe" $__commandArgs
            & $__handler $result
        }
    }
  } # end PROCESS

<#
.SYNOPSIS
This is allowing you to run TShark.exe in powershell using powershell
.DESCRIPTION 
Inspired by Adam Driscoll and the Sysinternals module I wanted to do something similar 
so bringing tshark to powershell. Although this module does not at the moment include
every single parameter tshark does, I still think it was worth bringing this to powershell
to act as another tool for the sysadmins tool-kit you have. I like hanging out in my shell
all day so this now allows me to easily use tshark from Powershell

.PARAMETER SetInterface
This is the same as the -i parameter in tshark but with a more helpful paramter name I think. So this
will set the name of the network interface to use for live packet capture.  Please note to get a list
of YOUR valid network interfaces use the -GetInterface paramter first.

.PARAMETER SetCaptureFilter
Set the capture filter expression using wireshark syntax, to only filter on dns just simply type dns after this
parameter name. Please do not use this if you are un-sure of the syntax, you can always filter the capture file in the
wireshark GUI after the capture is complete.

.PARAMETER SetDataLinkType
Set the data link type to use while capturing packets. The values reported by -GetDataLinkTypes are the values that can be used
as the value for this parameter

.PARAMETER GetInterfaces
Print a list of the interfaces on which TShark can capture, and exit. For each network interface, a number and an interface name,
possibly followed by a text description of the interface, is printed. The interface name or the number can be supplied to the -SetInterface option to specify an interface on which to capture.

.PARAMETER GetDataLinkTypes
List the data link types supported by the interface and exit. The reported link types can be used with the -SetDataLinkTypes
parameter

.PARAMETER Path
Type the full file output path you want the file to go to including the file name and file extension. For example:-
-Path C:\Users\MyAccount\MyCapture.pcapng

.PARAMETER SetFileType
This allows you to see all the possible file extension output types. So you can select a different output to the default
pcapng format should you so wish to. Please remember to also specify this in the -Path value the parameter file-type
extension you selected  

.PARAMETER LogPath
This will allow you to specify a .txt or .log file to output messages to that appear on the screen during the capture

.PARAMETER SetFileCaptureSizeKB
This is using the capture ring buffer option from tshark -b and using this to set the file size for the capture you wish to
perform. After using this parameter name press TAB to auto-complete the parameter validation pattern

.PARAMETER SetFileCaptureDuration
This is using the capture ring buffer option from tshark -b this will set the amount of time in seconds that the capture is taken for to a file before going to the next file.
After using this parameter name press TAB to auto-complete the parameter validation pattern

.PARAMETER SetNumberCaptureFiles
This is using the capture ring buffer option from tshark -b allowing you to set the number of rotating capture files you wish to
capture. You would use this parameter in with either the -SetFileCaptureSizeKB or -SetFileCaptureDuration to allow you to
fix the amount of files to store in the output directory. This will allow you to keep a trace running for days without flooding 
the hard drive full of data. After using this parameter name press TAB to auto-complete the parameter validation pattern

.PARAMETER GetVersion
This will display the current version of tshark you are using in the background to perform the task at hand.

.PARAMETER OpenFile
Allows you to display a capture file within the Powershell window you have open, should you wish to do some quick analysis
on the capture

.PARAMETER GetStatistics
This allows you to choose from a large choice of statistics to display after it has opened the capture file. This is very
useful to gain certain insights or to just get the expert statistics

.EXAMPLE
   Start-TShark -Path C:\Zelda.pcapng -SetFileCaptureSizeKB filesize:10240 -SetNumberCaptureFiles files:10
This will output a rotating set of 10 files which will be 10MB in size. Pressing CTRL+C to exit the capture after the
desired issue has been captured

.EXAMPLE
  Start-TShark -SetInterface "Wi-Fi" -Path C:\DnsOutput.pcapng -LogPath C:\DnsOutput.txt
This will being a capture on the Wi-Fi network interface, outputting to a .pcapng file and also record the output
from messages on the Powershell screen to the log file specified in the -LogPath this file capture will continue
to capture information until CTRL+C is pressed to stop the capture

.EXAMPLE
 Start-TShark -OpenFile C:\SlowNetwork.pcapng -GetStatistics expert
 Will open the capture file specified in the -OpenFile parameter, this will also then provide you with the expert analysis
 information by specifying that value from the pre-defined list of choices on the -GetStatistics parameter

.FUNCTIONALITY
To use when you have a weird network issue you have been told to fix, allowing you to collect packet information to analyze
in Wireshark 
#>
}

Function Install-TShark {
if (Test-Path "$env:USERPROFILE\scoop")
{
    Write-Host -ForegroundColor Yellow "Awesome it looks like you have scoop installed, checking to see if TShark present."
    if (Test-Path "$env:USERPROFILE\scoop\shims\tshark.exe")
    {
    Write-Host -ForegroundColor Yellow "It appears you have TShark installed you are all good to use Start-TShark now"
    }
    else {
    scoop install wireshark
    }
}
else
{
Write-Host -ForegroundColor Yellow "I am now installing scoop for you and TShark, then you can use Start-TShark once complete."
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install git
scoop bucket add extras
scoop install wireshark
Write-Host -ForegroundColor Yellow "All done have a great day."
}

}

# Examples 👆

If you look at the bottom of the above code I did include a help file for this module, and a couple of examples of how to use this program. Once again please remember this module does not install the software automatically, although it does include a function to do this automatically for you after you have downloaded the module.

# I hope this helps you 🌱

So if you find yourself with a pickle of a problem to solve and know it could be network related, then give this tool a chance to find that issue for you. I really hope you enjoyed reading the about the first recent module I released and how this could potentially help you solve a complex problem 😁