IP Scanner en Powershell

Per raons de feina tenia qeu fer molt sovint pings a un rang d’ips de la xarxa, així que vaig pensar que millor fer un petit script en Powershell i Windows Forms per poder fer quelcom.

Ja sé que existeixen diferentes eines que ens ajuden amb això (Angrey Ip Scanner per exemple), però mai s’ha de perdre l’oportunitat de poder picar codi i fer-se un mateix les propies eines.

L’aplicació funciona al voltant d’una simple linia de codi que des de la consola de Powershell ja podem fer l’escaneig d’IPs

1..254 | ForEach-Object {Get-WmiObject Win32_PingStatus -Filter "Address='192.168.0.$_' and Timeout=200 and ResolveAddressNames='true' and StatusCode=0" | select ProtocolAddress*}

Aquesta linia de codi la podem trobar a https://daniel.streefkerkonline.com/2015/04/28/simple-ip-range-scan-using-powershell/

Però la gràcia és la de passar això a una interfícia mes visual, així que ens posem a la feina i amb https://poshgui.com/ (que ja n’he parlat altres vegades https://www.kamencesc.com/formularos-amb-powershell/), aquest cop afegirem un “objecte” al formulari qeu no hem fet servir, el DataGrid.

Aquest no deix ade ser una malla de cel·les, a forma de full d’excel, i que podem seleccionar el número de columenes qeu volem i llavors anar afegir valors en forma de linia per valor. Us deixo la part en que configurem el DataGrid, i en el qeu afegim les 3 columnes.

$DataGridView1                   = New-Object system.Windows.Forms.DataGridView
$DataGridView1.width             = 375
$DataGridView1.height            = 487
$DataGridView1.ColumnCount = 3
$DataGridView1.ColumnHeadersVisible = $true
$DataGridView1.Columns[0].Name = "IP"
$DataGridView1.Columns[1].Name = "Nom"
$DataGridView1.Columns[2].Name = "Status"
$DataGridView1.Anchor            = 'top,right,bottom,left'
$DataGridView1.location          = New-Object System.Drawing.Point(10,51)

Hem d’observar els valors de .ColumnCount, ColumnHeaderVisible (que mostrara la llegenda de cada columa) i els respectius noms.

Una cos que no he comentat en altres entrades és la propietat .Anchor, aquesta permetrà que al canviar la mida del formulari els objectes actuin al respecte, en aquest cas anclem les 4 posicions i al canviar la mida del formulari el nostre DataGrid també canvia la mida respecta la seva posició en el formulari, utilitzat correctament permet que tot continuï al seu lloc al canviar la mida.

Una altre cosa interessant que he afegit, és que a la segona caixa de text, ón posem el rang final de IPs, si apretem ‘Enter’, ja executa l’escanèig.

$TextBox_To.Add_KeyDown( {
  if ($_.KeyCode -eq "Enter") 
    {
    $Button1.PerformClick()
    }
  }
)

TantsSenzill com al configurar l’event de KeyDown (important, no KeyPress!) si el .KeyCode pertant a ‘Enter’ aquest executa el click al botó principal de l’aplicació.

En acabat una ullada al bucle principal:

Function btnVinga() {
    $DataGridView1.Rows.Clear() # Borrem DataGrid

    $ProgressBar1.Value=0 # Barra de progres a 0
    
    $subXarxa = $TextBox_IP.Text | % { ([ipaddress]$_).GetAddressBytes()[0..2] -join '.' } # Seleccionem la subxarxa

    # Passem a variables les IPs d'inici i de fi i en calculem el total a recorrer per calcular el %
    $ipFrom = [int]$TextBox_From.Text
    $ipTo = [int]$TextBox_To.Text
    $total = $ipTo - $ipFrom

    # iniciem el bucle, no fem servir un foreach per poder gestionar la barra de progrés
    for ($i=$ipFrom; $i -le $ipTo; $i++) {
        $ProgressBar1.value = ( ($i-$ipFrom) / $total ) * 100 # Barra de progrés %

        # es guarda el resultat del ping
        $resultat = Get-WmiObject Win32_PingStatus -Filter "Address='$subXarxa.$i' and Timeout=200 and ResolveAddressNames='true'"

        # afageix al DataGrid la informació que volem,
        # Address (es podria canviar per IPV4Address) ens diu l'adreça de destí
        # ProtocolAddressResolved retorna el nom de la máquina si escau
        # StatusCode retorna el codi del ping, alguns exemple:
        #
        #      0: Success
        #  11002: Destination Net Unreachable
        #  11003: Destination Host Unreachable
        #  11004: Destination Protocol Unreachable
        #  11005: Destination Port Unreachable
        #  11011: Time Out

        $DataGridView1.Rows.Add($resultat.IPV4Address,$resultat.ProtocolAddressResolved,$resultat.StatusCode) # Afageix al DataGrid
    }
}

Tal i com us he presentat al inici, la base de l’script és la mateixa, la crida a Get-WmiObject Win32_PingStatus, però en lloc d’aprofitarl la funció ‘foreach’, fem un ‘for’ de tota la vida agafant com a inici i fi els valors de IPs que ha posat l’usuari (i que per defecte apareixen 1 i 254, el rang complet).

Perquè he fet el ‘for’ en contra del ‘foreach’? amb el ‘for’ tinc un control de cada cop que es resolt un ping, per tant podem incrementar la barra de progrés, aquesta la posem a 0 abans del bucle i l’incrementem abans de la crida de Win32_PingStatus, ja que sino genera com un petit retard en aquesta.

No hi ha molt mes a a dir, aquest cop us deixo el codi complet i el fitxer per descarregar.

<# This form was created using POSHGUI.com  a free online gui designer for PowerShell
.NAME
    IP Scanner

Code by Kamencesc // DigichafarFan // Bonus
#&gt;

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

$Form                            = New-Object system.Windows.Forms.Form
$Form.ClientSize                 = '400,597'
$Form.text                       = "IP Scanner"
$Form.TopMost                    = $false

$TextBox_IP                      = New-Object system.Windows.Forms.TextBox
$TextBox_IP.multiline            = $false
$TextBox_IP.text                 = "000.000.000.000"
$TextBox_IP.width                = 109
$TextBox_IP.height               = 20
$TextBox_IP.location             = New-Object System.Drawing.Point(89,20)
$TextBox_IP.Font                 = 'Microsoft Sans Serif,10'

$TextBox_From                        = New-Object system.Windows.Forms.TextBox
$TextBox_From.multiline              = $false
$TextBox_From.width                  = 40
$TextBox_From.height                 = 20
$TextBox_From.location               = New-Object System.Drawing.Point(259,20)
$TextBox_From.Font                   = 'Microsoft Sans Serif,10'
$TextBox_From.Text                   = "1"

$Labler1                         = New-Object system.Windows.Forms.Label
$Labler1.text                    = "IP subxarxa"
$Labler1.AutoSize                = $true
$Labler1.width                   = 25
$Labler1.height                  = 10
$Labler1.location                = New-Object System.Drawing.Point(10,20)
$Labler1.Font                    = 'Microsoft Sans Serif,10'

$Label1                          = New-Object system.Windows.Forms.Label
$Label1.text                     = "Des de"
$Label1.AutoSize                 = $true
$Label1.width                    = 25
$Label1.height                   = 10
$Label1.location                 = New-Object System.Drawing.Point(209,20)
$Label1.Font                     = 'Microsoft Sans Serif,10'

$Label2                          = New-Object system.Windows.Forms.Label
$Label2.text                     = "fins a"
$Label2.AutoSize                 = $true
$Label2.width                    = 25
$Label2.height                   = 10
$Label2.location                 = New-Object System.Drawing.Point(305,19)
$Label2.Font                     = 'Microsoft Sans Serif,10'

$TextBox_To                        = New-Object system.Windows.Forms.TextBox
$TextBox_To.multiline              = $false
$TextBox_To.width                  = 40
$TextBox_To.height                 = 20
$TextBox_To.location               = New-Object System.Drawing.Point(348,20)
$TextBox_To.Font                   = 'Microsoft Sans Serif,10'
$TextBox_To.Text                   = "254"

$ProgressBar1                    = New-Object system.Windows.Forms.ProgressBar
$ProgressBar1.width              = 294
$ProgressBar1.height             = 20
$ProgressBar1.Anchor             = 'right,bottom,left'
$ProgressBar1.location           = New-Object System.Drawing.Point(9,561)

$Button1                         = New-Object system.Windows.Forms.Button
$Button1.text                    = "Vinga"
$Button1.width                   = 60
$Button1.height                  = 30
$Button1.Anchor                  = 'right,bottom'
$Button1.location                = New-Object System.Drawing.Point(326,555)
$Button1.Font                    = 'Microsoft Sans Serif,10'

$DataGridView1                   = New-Object system.Windows.Forms.DataGridView
$DataGridView1.width             = 375
$DataGridView1.height            = 487
$DataGridView1.ColumnCount = 3
$DataGridView1.ColumnHeadersVisible = $true
$DataGridView1.Columns[0].Name = "IP"
$DataGridView1.Columns[1].Name = "Nom"
$DataGridView1.Columns[2].Name = "Status"
$DataGridView1.Anchor            = 'top,right,bottom,left'
$DataGridView1.location          = New-Object System.Drawing.Point(10,51)

$Form.controls.AddRange(@($TextBox_IP,$TextBox_From,$Labler1,$Label1,$Label2,$TextBox_To,$ProgressBar1,$Button1,$DataGridView1))

# Events

$Button1.Add_Click({ btnVinga })
$TextBox_To.Add_KeyDown( {
  if ($_.KeyCode -eq "Enter") 
    {
    $Button1.PerformClick()
    }
  }
)

# Agafea la IP per la nostre lan
$ethName = "Ethernet" # "Ethernet" sol ser el nom de l'adptador de xarxa principal de Windows

$NetInfo = Get-NetAdapter -name $ethName # agafa la informació del dispositiu $ethNAme
$IpInfo = $Netinfo | Get-NetIPAddress | Where AddressFamily -like "IPv4" # agafa la IPv4 del dispositiu $ethName

$TextBox_IP.Text = $IpInfo.IPAddress # Desa el valor a la caixa de text que toca

Function btnVinga() {
    $DataGridView1.Rows.Clear() # Borrem DataGrid

    $ProgressBar1.Value=0 # Barra de progres a 0
    
    $subXarxa = $TextBox_IP.Text | % { ([ipaddress]$_).GetAddressBytes()[0..2] -join '.' } # Seleccionem la subxarxa

    # Passem a variables les IPs d'inici i de fi i en calculem el total a recorrer per calcular el %
    $ipFrom = [int]$TextBox_From.Text
    $ipTo = [int]$TextBox_To.Text
    $total = $ipTo - $ipFrom

    # iniciem el bucle, no fem servir un foreach per poder gestionar la barra de progrés
    for ($i=$ipFrom; $i -le $ipTo; $i++) {
        $ProgressBar1.value = ( ($i-$ipFrom) / $total ) * 100 # Barra de progrés %

        # es guarda el resultat del ping
        $resultat = Get-WmiObject Win32_PingStatus -Filter "Address='$subXarxa.$i' and Timeout=200 and ResolveAddressNames='true'"

        # afageix al DataGrid la informació que volem,
        # Address (es podria canviar per IPV4Address) ens diu l'adreça de destí
        # ProtocolAddressResolved retorna el nom de la máquina si escau
        # StatusCode retorna el codi del ping, alguns exemple:
        #
        #      0: Success
        #  11002: Destination Net Unreachable
        #  11003: Destination Host Unreachable
        #  11004: Destination Protocol Unreachable
        #  11005: Destination Port Unreachable
        #  11011: Time Out

        $DataGridView1.Rows.Add($resultat.IPV4Address,$resultat.ProtocolAddressResolved,$resultat.StatusCode) # Afageix al DataGrid
    }
}

# Mostra formulari
[void]$Form.ShowDialog()

Descàrga: IP Scanner, Powershell Script, Windows Forms (25 downloads)

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *

Aquest lloc utilitza Akismet per reduir els comentaris brossa. Apreneu com es processen les dades dels comentaris.