ping応答を利用して、遠隔のサーバのOSを調べてみました。
ネットで調べると、pingのTTLで相手のOSを推測できることが分かりました。
でも、それを覚えるのはしんどいので、バッチで表示するようにしていました。
初めての投稿なので、とりあえず、ほぼプレーンテキストで投稿します。
もし間違っていたら指摘ください。
バッチのコードは以下の通りです。
100%オリジナルなので、どこで利用いただいても問題ないです。
一応自分では、ping2.batという名前で保存して使ってます。
ping2 サーバ名またはipアドレス
で、すぐに結果が表示されます。
勉強を兼ねて作っているので、コメントだらけです。
注意いただきたいのは、-listという一括調査機能を使うと、「バッチ名.csv」というファイルを上書きすることがある点です。
「上書きしていいですか?」はうっとうしいので、あえて出してません。
ping2.bat
@echo off
setlocal enabledelayedexpansion
chcp 932 >nul
set err_code=0
rem *Encode with shift-JIS(Japanese)
rem このファイルを拡張子.txtで入手した場合は、拡張子を.batに変更してから利用ください。
rem shift-JISで保存しないと文字化けして正常に動作しません。
rem 勉強用に、コメントを多用しています。remで始まる分はコメントなので、削除しても構いません。
rem RFCによりHOST名の制約(記号は-のみ利用可)があるので、大丈夫と思いますが、"(",")","&",""",",",スペース等がホスト名に使われると、誤動作します。
rem バグを含めてコンピュータ名に使われるかもしれませんが、対応するつもりはありません。
rem 第1パラメータは、必須で、IPアドレス または、ホスト名、または-listでホスト名をファイル指定です。
rem /iは大文字小文字を区別しないという意味です。
if "%1"=="" ( goto :usage )
if /i "%1"=="/h" ( goto :usage )
if /i "%1"=="/help" ( goto :usage )
if /i "%1"=="-list" ( goto :list_scan )
rem 一つのホストを指定して実行するパターン
call :scan_host %1
set err_code=0
goto :end
:list_scan
rem 複数のホストを指定して実行のパターン
set list_file=%~n0.list
if not exist %list_file% (
echo 「%list_file%」ファイルがカレントディレクトリに見つかりません。
echo -list指定の場合は、調べたいホスト名を「%list_file%」に1行、1ホストで列挙してください。
set err_code=1
goto :end
)
rem 出力結果ファイルが既にあればタイトルで初期化します。
rem %~n0.csvは常時上書きになるので、ご注意ください。
echo 指定ターゲット,IP-addr,OS,TTL,応答時間>%~n0.csv
rem 指定ホストを1台ずつ調べて、結果出力します。
rem %0.listに1行も無い場合は、メッセージが不自然ですが、複雑になるだけなので、あえてコーディングしません。
rem for文中で、変化する変数を扱う場合は、setlocal enabledelayedexpansionを宣言しておいて、該当変数を%でなく、!で囲みます。
rem %で囲むと、for文に入った時点の値に固定されるので、for文中で値を変更しても参照値は変化しません。
rem また、ping応答の形式が変わった場合は、全ターゲットを調べてもしょうがないので中断します。
for /f "delims=" %%a in (%list_file%) do (
call :scan_host %%a
if "!ERRORLEVEL!"=="2" (
set err_code=2
goto :end
)
echo !target!,!ip_addr!,!target_machine!,!TTL!,!response_time!>>%~n0.csv
)
echo.
echo 「%list_file%」で指定された上記の結果をファイル「%~n0.csv」に出力しました。
set err_code=0
goto :end
rem ===============Sub Routines=================
:scan_host
rem 【このサブルーチンの仕様概要】
rem ホスト名または、IPアドレスから、PINGコマンドを利用し、OSを判定・表示し、判定結果をERRORLEVELとして返します。
rem また、pingの結果から、一部の情報をグローバル変数としてセットします。
rem 【入出力変数】
rem 入力(パラメータ):ホスト名または、IPアドレス。
rem 出力(ERRORLEVEL):0=判定OK、1=ping応答なし、または期限切れ(この場合、スキップ)、2=ping応答フォーマット変更(この場合、中止)
rem その他出力:グローバル変数として以下の変数を出力:
rem %ip_addr%=PINGコマンド内のIPアドレス、%target_machine%=OS種別、%TTL%=TTL、%response_time%=response time
rem 【OS判断のしくみの概要解説】
rem pingコマンドの応答にあるTTL(Time To Live)でOSを判定・表示します。
rem TTLでOSを判断する方法は、ネットで「TTL OS」で調べると見つかります。
rem なお、デフォルトのTTLは公式に規程されているわけでなく、例外的なものもあるので、100%正確ではありません。
rem 詳細は以下のページに良くまとまっていますが、例外的なものは一般的でないOSが多いので、例外にはあまり当たらないかと。
rem https://subinsb.com/default-device-ttl-values/
rem input
set target=%1
rem output
set ip_addr=-
set target_machine=OS不明
set TTL=-
set response_time=-
rem for文で、usebackq指定すると、ファイルでなく、バッククオート(`)の中をコマンドとみなして、コマンド出力結果を処理できます。
rem これを使うと、一時ファイルに出力せずに、コマンド出力結果をハンドリングできます。
rem ちなみに、バッククオート(`)は、半角で、シフト押しながら@で入力できます。普段使わないなー。
rem 現状バージョンのPINGでは、スペース区切りで、1番目にIPアドレス、6番目に=応答時間、7番目にTTL=XXXが入っています。
rem 調査の時間を短縮したいので、pingは1回だけ、タイムアウトは、デフォルトの1/5である 800msとしています。
FOR /F "usebackq tokens=1,5,6,7" %%a in (`ping -n 1 -w 800 %target% ^| findstr TTL=`) DO (
set ip_addr=%%a
set kigengire=%%b
set response_time=%%c
set TTL_phrase=%%d
)
rem pingに応答がないと、上記で代入されない。
if "%ip_addr%"=="-" (
echo (※ %target% は、PINGの応答がないので、OSは不明です。)
exit /b 1
)
rem TTLが期限切れになることもあるらしい。
if "%kigengire:~1,4%"=="期限切れ" (
echo (※ %target% は、TTLが期限切れになりました。特殊な環境と思われます。)
exit /b 1
)
rem コマンド形式が変わっていなければ、%TTL_phrase%はTTL=で始まるはずです。
if not "%TTL_phrase:~0,4%"=="TTL=" (
echo PING応答のフォーマット(TTL=の場所など)が変わった可能性があります。
echo プログラムを修正ください。
exit /b 2
)
rem %V:~m% m文字目から、最後まで
rem response_timeは、0文字目が=なので、それを取るために、1文字目から最後まで。
set response_time=%response_time:~1%
rem 「TTL=」を取って数字だけにするので、4文字目から最後まで。
set TTL=%TTL_phrase:~4%
rem TTLの値でOSを推測します。
rem UNIX系>128>=Windows系>64>=Linux系(Macを含む)>32>=Windows95/98 となっています。
set target_machine=UNIX OS / Network機器
if %TTL% leq 128 ( set target_machine=Windows OS)
if %TTL% leq 64 ( set target_machine=Linux OS / Mac OS)
if %TTL% leq 32 ( set target_machine=Windows 95/98かもしれない!)
rem 結果を表示します。(ターゲットをipアドレスで指定した場合、ダブって表示すると見にくいので、片方を省略します。)
if "%ip_addr%"=="%target%" ( set disp_name=[%ip_addr%]
) else ( set disp_name=%target% [%ip_addr%])
echo ■ %disp_name% は、「%target_machine%」です。 -------------- TTLは %TTL% ,応答時間は %response_time% でした。
rem 正常終了
exit /b 0
rem =====================================================================
rem PingコマンドのTTLを利用して、OSを推定するお遊びプログラムです。
rem 実際には、ほぼ100%推定できますので、「あれ?このIPなんだ?」という時に、OSだけですが分かります。
rem Ver1.0 Created by Ken.Matsubara 2023/3/7
rem Ver1.1 Updated by Ken.Matsubara 2023/3/8 一括調査機能追加(-listパラメータ)
rem Ver1.2 Updated by Ken.Matsubara 2023/3/16 プログラムの簡素化と表示見直ししました。
rem Ver1.3 Updated by Ken.Matsubara 2023/3/16 コメントを見直ししました。
:usage
rem 一般的にプログラムの使い方は:usageというラベルでまとめることが多いです。
echo Pingコマンドの応答を(TTL)利用して、ターゲットのOSを調べます。
echo.
echo 使用法: %~n0 [ターゲット(IPアドレス または、ホスト名)、または -list]
echo.
echo オプション:
echo ターゲットを-listとした場合は、カレントディレクトリの「%~n0.list」にIPアドレスまたは、ホスト名を「1行に1つ」書いてください。
echo -listとした場合は、画面表示だけでなく、カレントディレクトリの「%~n0.csv」ファイルに「, 区切り」で結果を出力します。
echo ※「%~n0.csv」ファイルは、確認せずに常に上書きします。
echo.
echo なお、Pingコマンドの応答のTTL(Time To Live)を代表的なOSの値として判断しています。
echo ほぼ、間違いはありませんが、100%%正確ではありません。
echo.
echo 調査の時間を短縮したいので、pingは1回だけ、タイムアウトは、デフォルトの1/5である 800msとしています。
rem 使い方表示の際に、自分のPCのIPアドレスを表示するようにした。
set key_word=IPv6
set key_word=IPv4
rem ipconfigコマンドから情報を取得する。
set count=0
for /f "usebackq delims=" %%a in (`ipconfig^|findstr /i "%key_word%"`) do (
set contents=%%a
set "contents=!contents:*: =!"
set /a count+=1
set contents[!count!]=!contents!
)
rem 情報を表示する。
rem echo.
rem echo なお、このPCの情報は、以下の通りです。
rem echo ドメイン名は、%USERDOMAIN%
rem echo ログインユーザ名は、%USERNAME%
rem echo コンピュータ名は、%COMPUTERNAME%
rem for /l %%i in (1,1,%count%) do ( echo IPアドレスは、%key_word%[%%i]=!contents[%%i]!)
rem echo.
rem 注意)LFの下にある改行を削除するとうまく動かなくなるよ!
set LF=^
set ip_msg=
for /l %%i in (1,1,%count%) do (set ip_msg=!ip_msg!IPアドレスは、%key_word%[%%i]=!contents[%%i]!!LF!)
msg %username% なお、このPCの情報は、以下の通りです。^
-------------------------------------^
ドメイン名は、%USERDOMAIN%^
ログインユーザ名は、%USERNAME%^
コンピュータ名は、%COMPUTERNAME%^
%ip_msg%
rem この辺りまで、無駄に見える改行を変更しないでね!
:end
if "%SESSIONNAME%"=="" pause
endlocal
exit /b %err_code%
上記のポイントを解説します。
1.コマンドの結果を変数に入れる。
for文のusebackqというバッククォート指定を使います。
``で囲んだコマンドを、for文のインプットにしています。
コマンドの結果の1つ目、5つ目、6つ目、7つ目にそれぞれ、IPアドレス、期限切れ、応答時間、TTLが返ってくるので、それを取り出しています。
FOR /F "usebackq tokens=1,5,6,7" %%a in (`ping -n 1 -w 800 %target% ^| findstr TTL=`) DO (
set ip_addr=%%a
set kigengire=%%b
set response_time=%%c
set TTL_phrase=%%d
)