はじめに
前回作成したネットワークスキャナーに、ポートスキャン機能を付けました。同一ネットワーク上の全デバイスのMACアドレスを収集し、各デバイスのベンダー(製造元)情報を自動で取得できるだけでなく、ポートスキャン機能も備えているため、デバイスのセキュリティ状態も確認できます。
このスクリプトで出来ること
- ローカルネットワーク内の全IPアドレスに対してPingを実行
- 応答があったデバイスのMACアドレスを取得
- IEEE OUIデータベースを使用して、MACアドレスからベンダー情報を特定
- オプションで各デバイスに対してポートスキャンを実行し、オープンポートを検出(新)
- 検出したデバイス情報とポート情報をCSVファイルに出力(新)
- 結果をコンソール上にも分かりやすく表示
スクリプトのコード
# 同一ネットワークに接続している端末のMACアドレスを収集し、ベンダー情報を取得して
# CSVファイルに保存するPowerShellスクリプト (ポートスキャン機能追加版)
param (
[string]$OutputPath = "$PSScriptRoot\network_devices.csv",
[string]$OuiDatabasePath = "$PSScriptRoot\oui_database.csv",
[string]$NetworkPrefix = "192.168.1.",
[int]$StartIP = 1,
[int]$EndIP = 254,
[switch]$EnablePortScan = $false,
[int[]]$PortsToScan = @(21, 22, 23, 25, 53, 80, 443, 445, 3389, 8080)
)
# OUIデータベースの初期化と作成
function Initialize-OUIDatabase {
param ([string]$DatabasePath)
Write-Host "OUIデータベースをダウンロードしています..." -ForegroundColor Cyan
# IEEE OUIデータベースをダウンロード
$ouiUrl = "http://standards-oui.ieee.org/oui/oui.txt"
$tempFile = "$env:TEMP\oui_temp.txt"
try {
Invoke-WebRequest -Uri $ouiUrl -OutFile $tempFile
Write-Host "ダウンロード完了" -ForegroundColor Green
}
catch {
Write-Host "ダウンロード失敗: $($_.Exception.Message)" -ForegroundColor Red
return $null
}
# OUIデータを処理してCSVに変換
$ouiData = @()
Write-Host "OUIデータベースを処理しています..." -ForegroundColor Cyan
Get-Content -Path $tempFile | ForEach-Object {
if ($_ -match "([0-9A-F]{6})\s+\(hex\)\s+(.+)") {
$ouiData += [PSCustomObject]@{
OUI = $matches[1]
Vendor = $matches[2].Trim()
}
}
}
# CSVとして保存
$ouiData | Export-Csv -Path $DatabasePath -NoTypeInformation
Remove-Item -Path $tempFile -ErrorAction SilentlyContinue
Write-Host "OUIデータベースを作成しました: $DatabasePath" -ForegroundColor Green
return $DatabasePath
}
# MACアドレスからベンダー情報を取得する関数
function Get-MACVendor {
param (
[string]$MACAddress,
[string]$DatabasePath
)
$oui = ($MACAddress -replace '[-:]', '').Substring(0, 6).ToUpper()
$vendor = Import-Csv -Path $DatabasePath |
Where-Object { $_.OUI -eq $oui } |
Select-Object -ExpandProperty Vendor -First 1
if ($vendor) {
return $vendor
} else {
return "不明"
}
}
# 特定のIPアドレスの指定ポートをスキャンする関数
function Test-Port {
param(
[string]$IPAddress,
[int]$Port,
[int]$Timeout = 100
)
$tcpClient = New-Object System.Net.Sockets.TcpClient
try {
$connectionResult = $tcpClient.BeginConnect($IPAddress, $Port, $null, $null)
$waitResult = $connectionResult.AsyncWaitHandle.WaitOne($Timeout, $false)
if ($waitResult) {
try {
$tcpClient.EndConnect($connectionResult)
return $true
} catch {
return $false
}
}
} catch {
return $false
} finally {
$tcpClient.Close()
}
return $false
}
# IPアドレスに対してポートスキャンを実行し、オープンポートのリストを返す関数
function Get-OpenPorts {
param(
[string]$IPAddress,
[int[]]$Ports
)
$openPorts = @()
$portServices = @{
20 = "FTP-Data"
21 = "FTP"
22 = "SSH"
23 = "Telnet"
25 = "SMTP"
53 = "DNS"
80 = "HTTP"
110 = "POP3"
123 = "NTP"
143 = "IMAP"
443 = "HTTPS"
445 = "SMB"
993 = "IMAPS"
995 = "POP3S"
3306 = "MySQL"
3389 = "RDP"
5900 = "VNC"
8080 = "HTTP-Proxy"
8443 = "HTTPS-Alt"
}
Write-Host " ポートスキャン中..." -NoNewline
$progressCount = 0
foreach ($port in $Ports) {
$progressCount++
Write-Progress -Activity "ポートスキャン" -Status "ポート: $port" -PercentComplete (($progressCount / $Ports.Count) * 100) -Id 2 -ParentId 1
if (Test-Port -IPAddress $IPAddress -Port $port) {
$serviceName = if ($portServices.ContainsKey($port)) { $portServices[$port] } else { "不明" }
$openPorts += [PSCustomObject]@{
Port = $port
Service = $serviceName
}
}
}
Write-Progress -Activity "ポートスキャン" -Completed -Id 2
Write-Host " 完了" -ForegroundColor Green
return $openPorts
}
# メイン処理開始
Write-Host "ネットワークデバイス検出ツール(ポートスキャン機能付き)" -ForegroundColor Cyan
Write-Host "--------------------------------------------" -ForegroundColor Cyan
if ($EnablePortScan) {
Write-Host "ポートスキャンが有効: $($PortsToScan -join ', ')" -ForegroundColor Yellow
Write-Host "注意: ポートスキャンは時間がかかる場合があります" -ForegroundColor Yellow
} else {
Write-Host "ポートスキャンは無効です (有効にするには -EnablePortScan を使用)" -ForegroundColor Gray
}
# OUIデータベースの確認
if (-not (Test-Path -Path $OuiDatabasePath)) {
$OuiDatabasePath = Initialize-OUIDatabase -DatabasePath $OuiDatabasePath
if ($null -eq $OuiDatabasePath) {
Write-Host "OUIデータベースの作成に失敗しました。処理を終了します。" -ForegroundColor Red
exit
}
}
# ARPテーブルをクリア
Write-Host "ARPテーブルをクリアしています..." -ForegroundColor Cyan
arp -d
Write-Host "スキャンを開始: ${NetworkPrefix}${StartIP} から ${NetworkPrefix}${EndIP}" -ForegroundColor Cyan
$activeHosts = @()
# IPスキャン実行
for ($i = $StartIP; $i -le $EndIP; $i++) {
$ip = "${NetworkPrefix}${i}"
$progress = [math]::Round((($i - $StartIP) / ($EndIP - $StartIP + 1)) * 100)
Write-Progress -Activity "ネットワークスキャン" -Status "IPアドレス: $ip" -PercentComplete $progress -Id 1
# Ping送信
if (Test-Connection -ComputerName $ip -Count 1 -Quiet -ErrorAction SilentlyContinue) {
Write-Host "${ip} は応答中..." -NoNewline
# MACアドレス取得
$arpResult = arp -a $ip | Select-String '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
if ($arpResult) {
$arpResult -match '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | Out-Null
$macAddress = $matches[0] -replace ':', '-'
# ベンダー取得
$vendor = Get-MACVendor -MACAddress $macAddress -DatabasePath $OuiDatabasePath
# ホスト名取得
try {
$hostname = [System.Net.Dns]::GetHostEntry($ip).HostName
} catch {
$hostname = "不明"
}
# ポートスキャン(有効な場合)
$openPorts = @()
$portsInfo = ""
if ($EnablePortScan) {
$openPorts = Get-OpenPorts -IPAddress $ip -Ports $PortsToScan
if ($openPorts.Count -gt 0) {
$portsInfo = ($openPorts | ForEach-Object { "$($_.Port)($($_.Service))" }) -join ", "
Write-Host " オープンポート: $portsInfo" -ForegroundColor Yellow
} else {
Write-Host " オープンポートなし" -ForegroundColor Gray
}
} else {
Write-Host " 完了" -ForegroundColor Green
}
Write-Host " MAC: $macAddress ベンダー: $vendor" -ForegroundColor Gray
# 結果追加
$activeHosts += [PSCustomObject]@{
IPAddress = $ip
MACAddress = $macAddress
Vendor = $vendor
HostName = $hostname
DetectedTime = Get-Date -Format "yyyy/MM/dd HH:mm"
OpenPorts = if ($EnablePortScan) { $portsInfo } else { "スキャンなし" }
}
} else {
Write-Host " MAC取得失敗" -ForegroundColor Red
}
}
}
Write-Progress -Activity "ネットワークスキャン" -Completed -Id 1
# 結果表示
if ($activeHosts.Count -gt 0) {
Write-Host "`n検出結果: $($activeHosts.Count)台" -ForegroundColor Cyan
if ($EnablePortScan) {
# ポートスキャン有効の場合、ポート情報を含めて表示
$activeHosts | Format-Table -Property IPAddress, MACAddress, Vendor, HostName, OpenPorts -AutoSize
} else {
# ポートスキャン無効の場合、通常表示
$activeHosts | Format-Table -Property IPAddress, MACAddress, Vendor, HostName -AutoSize
}
# CSV出力
$activeHosts | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "CSVファイル出力完了: $OutputPath" -ForegroundColor Green
} else {
Write-Host "デバイスが検出されませんでした" -ForegroundColor Yellow
}
スクリプトの解説
主要機能
-
IEEE OUIデータベースの自動ダウンロードと処理
スクリプト初回実行時に、IEEEのウェブサイトからOUI(Organizationally Unique Identifier)データベースをダウンロードし、検索しやすい形式に変換します。 -
ネットワークスキャン
指定したIPアドレス範囲に対してPingを送信し、応答のあったホストを検出します。 -
MACアドレス収集
ARPテーブルから各デバイスのMACアドレスを取得します。 -
ベンダー情報の照会
MACアドレスの先頭6桁(OUI部分)を使って、デバイスの製造元を特定します。 -
ポートスキャン(オプション)
検出したデバイスに対して、指定したポート(デフォルトでは一般的な21, 22, 23, 25, 53, 80, 443, 445, 3389, 8080)が開いているかを確認します。 -
結果のエクスポート
収集した情報を見やすい表形式で表示し、CSVファイルとしても保存します。
パラメータ設定
スクリプトは以下のパラメータをカスタマイズできます:
- OutputPath: 結果を出力するCSVファイルのパス
- OuiDatabasePath: OUIデータベースを保存するパス
- NetworkPrefix: スキャンするネットワークのプレフィックス(例:192.168.1.)
- StartIP: スキャンを開始するIPアドレスの最後の数字
- EndIP: スキャンを終了するIPアドレスの最後の数字
- EnablePortScan: ポートスキャンを有効にするスイッチパラメータ
- PortsToScan: スキャンするポート番号の配列
使用方法
基本的な実行方法
- スクリプトをダウンロードして任意の場所に保存します(例:network_scanner.ps1)
- PowerShellを管理者権限で起動
- スクリプトのあるディレクトリに移動
- 以下のコマンドを実行
.\network_scanner.ps1
ポートスキャンを有効して実行
.\network_scanner.ps1 -EnablePortScan
カスタムポートを指定してポートスキャンを実行
.\network_scanner.ps1 -EnablePortScan -PortsToScan @(80, 443, 8080, 3306)
特定のIPアドレス範囲をスキャンする場合
.\network_scanner.ps1 -NetworkPrefix "10.0.0." -StartIP 1 -EndIP 100 -EnablePortScan
結果の出力先を変更
.\network_scanner.ps1 -OutputPath "C:\Reports\network_scan_result.csv" -EnablePortScan
実行結果
スクリプトを実行すると、以下のような結果が表示されます。
- OUIデータベースのダウンロードと処理状況
- ポートスキャンが有効かどうかと、スキャン対象のポート一覧
- スキャンの進捗状況(パーセンテージ表示)
- 検出されたデバイスごとのIP、MAC、ベンダー、ホスト名
- ポートスキャンが有効な場合は、各デバイスのオープンポートと関連サービス
- 最後に、検出されたデバイスの総数と結果のサマリー
また、指定したパスにCSVファイルが生成され、以下の情報が含まれます。
- IPアドレス
- MACアドレス
- ベンダー名
- ホスト名(取得できた場合)
- 検出日時
- オープンポートとサービス(ポートスキャンが有効な場合)
<実行結果のサンプル>
技術的なポイント
OUIデータベースの自動取得
このスクリプトでは、IEEEのウェブサイトから最新のOUIデータベースを自動的に取得し、ローカルにCSVとして保存します。
# IEEE OUIデータベースをダウンロード
$ouiUrl = "http://standards-oui.ieee.org/oui/oui.txt"
$tempFile = "$env:TEMP\oui_temp.txt"
try {
Invoke-WebRequest -Uri $ouiUrl -OutFile $tempFile
Write-Host "ダウンロード完了" -ForegroundColor Green
}
catch {
Write-Host "ダウンロード失敗: $($_.Exception.Message)" -ForegroundColor Red
return $null
}
TCPClientによるポートスキャン
ポートスキャンは、.NET FrameworkのTcpClientクラスを使用して実装されています。
function Test-Port {
param(
[string]$IPAddress,
[int]$Port,
[int]$Timeout = 100
)
$tcpClient = New-Object System.Net.Sockets.TcpClient
try {
$connectionResult = $tcpClient.BeginConnect($IPAddress, $Port, $null, $null)
$waitResult = $connectionResult.AsyncWaitHandle.WaitOne($Timeout, $false)
if ($waitResult) {
try {
$tcpClient.EndConnect($connectionResult)
return $true
} catch {
return $false
}
}
} catch {
return $false
} finally {
$tcpClient.Close()
}
return $false
}
arp コマンドの活用
MACアドレスを正確に取得するために、最初にARPテーブルをクリアしてから、Pingを実行後にARPテーブルから情報を取得します。
# ARPテーブルをクリア
arp -d
# Pingで応答を確認後
$arpResult = arp -a $ip | Select-String '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
進捗表示の実装
スクリプトは、ユーザーへのフィードバックとして、IPスキャンとポートスキャンの両方で進捗状況を表示します。
Write-Progress -Activity "ネットワークスキャン" -Status "IPアドレス: $ip" -PercentComplete $progress -Id 1
Write-Progress -Activity "ポートスキャン" -Status "ポート: $port" -PercentComplete (($progressCount / $Ports.Count) * 100) -Id 2 -ParentId 1
最後に
ポートスキャン機能を追加しました。
小規模ネットワークの管理やセキュリティチェックに使用してもらえたら幸いです。
今後は、オープンポートで動作しているサービスのバージョン情報を取得する機能、GUIインターフェイスの追加、既知のデバイスリストとの照合、などの機能を追加していきます。
以上です。
コメント