0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

windowsのバッチで、遠隔のOS種別を推測するバッチ(勉強用)を作ってみました。

Last updated at Posted at 2023-04-03

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
)
0
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?