【PowerShell】Test-Connectionによるpingでアドレススキャン
はじめに
2週間前に書いた記事で、「インフラエンジニアならPowerShellやJScriptが便利では」と考え、JScriptでコードの書き方の記事を書いてきました。が、PowerShellも知ってみたく触ってみました。
今回は、指定されたセグメント内のアドレスに総当たりでpingを実行するツールを作ってみました。
コード
doPing.bat
@echo off
powershell.exe -NoProfile -ExecutionPolicy RemoteSigned .\ping.ps1
pause
PowerShellを実行する際は、ポリシーを一時的に変更する必要があるため、
>-ExecutionPolicy RemoteSigned
で変更を実施しています。
ping.ps1
# PowerShellのバージョンを表示
Set-StrictMode -Version 3.0
Write-Host "PowerShell version"
Write-Host $PSVersionTable.PSVersion
Write-Host ""
# 設定項目
$segment = "192.168.11.0/24" # ひとまずは /24 固定とする
$count = 2
# 結果の行を出力する
function printResult($str_1, $str_2){
# 左から16文字を取得
$str_1 = $str_1 + " " # 削る前に右に空白を足す
$str_1 = $str_1.Substring(0, 16)
# 右から8文字を取得
$str_2 = " " + $str_2 # 削る前に左に空白を足す
$str_2 = $str_2.Substring($str_2.Length - 8 , 8)
Write-Host($str_1 + $str_2)
}
# アドレス一覧の配列を出力する
function printAddresses($arr){
Write-Host("address")
Write-Host("-------")
for($i = 0; $i -lt $arr.Count; $i++){
Write-Host($arr[$i])
}
Write-Host("")
}
# アドレス一覧を作成する
function setAddressesFromSegment($seg){
$arr1 = $seg.split("/")
$arr2 = $arr1[0].split(".")
$oct1 = $arr2[0]
$oct2 = $arr2[1]
$oct3 = $arr2[2]
$oct4 = $arr2[3]
$pf = $arr1[1]
#Write-Host($seg)
#Write-Host($oct1 +"."+ $oct2 +"."+ $oct3 +"."+ $oct4 +"/"+ $pf)
$addr = @()
for($i = 1; $i -lt 254; $i++){
$addr += $oct1 +"."+ $oct2 +"."+ $oct3 +"."+ $i
}
#Write-Host($addr.Count)
return $addr
}
# 対象アドレスと、結果を格納する変数
$addresses = @(setAddressesFromSegment $segment) # 返り値を配列として受け取る(要素1でも配列にする)
$results = @{}
# countの回数分pingをする
for($i = 0; $i -lt $count; $i++){
for($j = 0; $j -lt $addresses.Count; $j++){
# Keyが無ければ初期化 (0にする)、1以上ならスキップする
if($results.ContainsKey($addresses[$j]) -eq $false){
$results[$addresses[$j]] = 0
}elseif( $results[$addresses[$j]] -eq 1 ){
continue
}
# Ping実行
Write-Host ("ping to " + $addresses[$j] + " (" + ($i+1) +"/"+ $count + ")" )
$res = Test-Connection $addresses[$j] -Count 1 -AsJob
sleep -m 150 # 150msまつ
# 結果を更新
# pingが150ms以内に返っていればCompleted
# pingが150ms以内に返らなければRunning
# arp解決のwaitを考慮して、countは2以上が良い
if($res.State -eq "Completed") {
$results[$addresses[$j]] += 1
}
}
}
# 結果をを表示
$output = @();
printResult "target" "success"
printResult "------" "-------"
$results.GetEnumerator() | sort Name | ForEach-Object{
if($_.value -ne 0){
printResult $_.Name $_.Value
}
}
コードの詳細は割愛しますが、
・PowerShellのバージョンによってpingするコマンドや動作が異なる
※今回のコードはWindows PowerShell 5.1 で書いています
※参考の「PowerShellで死活監視」のサイトに詳細有り
・Ping成功の判定は、ping実行後 150ms待ってもping実行中かpingが完了しているかを見るようにしています。1回pingしただけではarp解決によりpingがかける必要があるので、2回pingを打つなどチューニングが必要
・254個のアドレスにpingを1回ずつ打つと40秒近くかかるため、2回目のpingは1回目に疎通不可だったもののみにする
といった点に注意や工夫が必要でした。
(並列処理はもっとやり方がありそうですが力不足…)
動作結果
target success
------ -------
192.168.11.1 1
192.168.11.100 1
192.168.11.101 1
192.168.11.12 1
192.168.11.13 1
192.168.11.200 1
192.168.11.32 1
上記7つのアドレスからpingが返ってきたことが分かります。
おわりに
PowerShellは機能が多いので慣れれば便利そうです。
いずれはポートスキャンやtraceroute等も実装してみます。
一方で、バージョンによりコマンドの機能が異なる点が難点で、”その日使える端末が古くPowerShellのバージョンが5.1だから7.2向けに書いていたスクリプトが動作しない”という懸念があります。
バージョンに依存しない記載にしたり、使用環境を固定したりと、工夫した運用が必要そうです。
参考
この記事が気に入ったらサポートをしてみませんか?