見出し画像

【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向けに書いていたスクリプトが動作しない”という懸念があります。
バージョンに依存しない記載にしたり、使用環境を固定したりと、工夫した運用が必要そうです。

参考


この記事が気に入ったらサポートをしてみませんか?