1

Тема: CMD/BAT: Асинхронный ICMP-сканер подсети

Довольно часто возникает необходимость выяснить "демографию" того или иного участка корпоративной сети - какие ip заняты, какие хосты живы, какие у хостов имена и т.п. Зачастую эти участки являются обособленными подсетями, не имеющими непосредственного контакта ни с интернетом, ни с собственно сетью предприятия (например, биржевые терминалы трейдеров в банке). А если еще ИБ запрещает посторонний софт...
В таких случаях, когда под рукой только консоль cmd и собственные ручки, на помощь приходит простенький цикл, который, к слову, здесь уже не раз обсуждался:

 for /l %i in (1,1,254) do @ping -a -n 1 192.168.100.%i

Однако, есть у этого решения досадные минусы: долго работает и выводит в не слишком удобном для просмотра и анализа виде.

Как бы сделать батник многопоточным, да еще чтоб данные сверстались в нужном формате?

Легко! Пинги запускаем асинхронно с помощью команды start. Результат каждого пинга перенаправляем в файл с именем пингуемого ip-адреса. Дожидаемся завершения всех пингов, считываем результирующие файлы, выкидываем из них всё лишнее и выводим:

@echo off
:: Запуск:
:: scan.cmd <subnet>
::
:: Параметры:
:: <subnet> - три первых октета ip-адреса (идентификатор подсети)
::

if "%1"=="" goto usage

:: Задаем сигнальные строки (отличаются в зависимости от локали)
echo | find "is" >nul || goto set_ru
:set_en
set STR1="Reply ["
set STR2=": bytes"
set TKNS=2,3
goto set_end
:set_ru
set STR1="Ответ ["
set STR2=": число"
set TKNS=4,5
:set_end

:: Создаем временную директорию
pushd %APPDATA%
md %1
cd %1

:: Полный пинг подсети (асинхронно)
echo * Залп из пингов. Не пугайтесь, в считанные секунды всё утихнет...
for /l %%i in (1,1,254) do start /b cmd /c ping -a -n 1 %1.%%i ^| findstr %STR1% > %1.%%i

:: Ожидание завершения всех пингов
echo * Ожидание завершения всех пингов (20 сек)...
ping -n 20 127.0.0.1 > nul

echo * Маленькое причесывание результата...
:: Зачистка безответных пингов
for %%i in (*) do find %STR2% <%%i >nul || del %%i

:: Причесывание оставшихся живых
for /f "tokens=%TKNS% delims=[] " %%i in ('for %%h in ^(*^) do @type %%h ^|find "["') do ren %%j "%%j %%i"
echo * Готово!
echo.
:: Вывод результата
::dir /b /o:d
::dir /b /o:n
:: Штатной числовой сортировки нет, поэтому мутим ея своими силами
for /l %%i in (1,1,254) do if exist %1.%%i (dir /b %1.%%i) else if exist "%1.%%i *" (dir /b "%1.%%i *")

:: Убираем за собой
echo Y | del * >nul
cd ..
rd %1
popd
exit /b

:usage
echo Использование: %~nx0 ^<маска^>
echo где ^<маска^> ~= N.N.N ^(e.g. 192.168.99^)
echo.
echo Описание: Сканирует подсеть класса C на предмет живых хостов.
exit /b

2

Re: CMD/BAT: Асинхронный ICMP-сканер подсети

Вышеприведенный скрипт - довольно топорный. В нем сканируется только подсеть C-класса и время работы составляет свыше 20 сек., т.к. время ожидания завершения пингов подобрано опытным путем и хардкодно установлено в 20 сек.

Между тем, сканировать подсеть класса C нужно не всегда - иногда бывает побольше, а иногда и значительно меньше.

Таким образом, нужно добавить в скрипт еще один параметр - маску подсети. А также научиться определять время завершения всех порожденных пингов.

Для решения второго вопроса будем в цикле пытаться переименовать файлы сами в себя до тех пор, пока не удастся сделать это для каждого из них.
А для работы с масками (в CIDR-формате) - задействуем богатый механизм двоичной арифметики в команде set /A. Ну и добавим немного плюшек, типа контроля введенных значений и подсказки с ip и маской данного хоста:

::
:: Simple network scanner
:: 2011 (c) vors
::
@echo off
setlocal ENABLEDELAYEDEXPANSION
:: Refine input to comply with the pattern of ip/cidr-mask
if "%1"=="" goto usage
echo %1 | findstr "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/[0-9]*" >nul || goto usage

set input=%1
set ip=%input:~0,-3%
set cidr=%input:*/=%
if %cidr% gtr 30 set cidr=30
if %cidr% lss 16 echo Scanning such much half of teh Intarnet no possible^^! You crazy. & exit /b
::echo %ip%

:: Parse given ip-address to extract network and host parts
for /F "tokens=1,2,3* delims=." %%i in ('echo %ip%') do (
    set /A host="%%k << 8 | %%l"
    set /A host4="host & 0xFF"
    set /A host3="host >> 8"
    set netw=%%i.%%j
)
::echo network = %netw%
::echo %host% = %host3%.%host4%

:: Determine range to scan
set /A range="(1 << 32-cidr)-2"
::echo range= %range%
set /A base="((0xffff << 32-cidr) & host)+1"
set /A base4="base & 0xFF"
set /A base3="base >> 8"
::echo %base% = %base3%.%base4%
set /A ceil="base+range-1"
set /A ceil4="ceil & 0xFF"
set /A ceil3="ceil >> 8"
::echo %ceil% = %ceil3%.%ceil4%
echo * Range: %netw%.%base3%.%base4% - %netw%.%ceil3%.%ceil4% ^(%range% hosts^)
echo.
:: Задаем сигнальные строки (отличаются в зависимости от локали)
echo | find "is" >nul || goto set_ru
:set_en
set STR1="Reply ["
set STR2=": bytes"
set TKNS=2,3
set STR3=Scattering pings. Don't be scared, they all will stop in a moment...
set STR4=Waiting for pings to stop (approx. 10-20 sec)...
set STR5=A bit of make-up...
set STR6=All done^^!
goto set_end
:set_ru
set STR1="Ответ ["
set STR2=": число"
set TKNS=4,5
set STR3=Залп из пингов. Не пугайтесь, в считанные секунды всё утихнет...
set STR4=Ожидание завершения всех пингов (~10-20 сек)...
set STR5=Маленькое причесывание результата...
set STR6=Готово^^!
:set_end

:: Создаем временную директорию
pushd %TEMP%
if exist %netw% rd /S /Q %netw%
md %netw%
cd %netw%

set IMAX=128
:: Полный пинг подсети (асинхронно)
echo * %time% %STR3%
for /L %%i in (%base%,1,%ceil%) do (
    set /A i0="%%i - IMAX"
    if !i0! geq %base% call :wait !i0!
    set /A i4="%%i & 0xFF"
    set /A i3="%%i >> 8"
    start /b cmd /c ping -a -n 1 %netw%.!i3!.!i4! ^| findstr %STR1% > %netw%.!i3!.!i4!
)

:: Ожидание завершения всех пингов
echo * %time% %STR4%
:loop
ping -n 2 127.0.0.1 >nul
if not exist %netw%.%ceil3%.%ceil4% goto loop
for %%i in (*) do ren %%i %%i 2>nul || goto loop

:: Зачистка безответных пингов
echo * %time% %STR5%
for %%i in (*) do find %STR2% <%%i >nul || del %%i

:: Причесывание оставшихся живых
for /f "tokens=%TKNS% delims=[] " %%i in ('for %%h in ^(*^) do @type %%h ^|find "["') do ren %%j "%%j %%i"
echo * %time% %STR6%
echo.
:: Вывод результата
:: dir /b
:: Штатной числовой сортировки нет, поэтому мутим ея своими силами
for /L %%i in (%base3%,1,%ceil3%) do for /L %%j in (%base4%,1,%ceil4%) do if exist %netw%.%%i.%%j (dir /b %netw%.%%i.%%j) else if exist "%netw%.%%i.%%j *" (dir /b "%netw%.%%i.%%j *")

:: Убираем за собой
cd ..
rd /S /Q %netw%
popd
endlocal
exit /b

:: Functions & procedures
:usage
echo Usage: %~nx0 ^<ip-address^>/^<cidr-mask^>
echo.
echo Scans a network (calculated from given ip and mask) with pings to
echo determine which hosts are really alive hence which IPs are vacant.
echo.
call :myip
echo Example: %~nx0 %ip%/%cidr%
endlocal
exit /b

:myip
for /F "tokens=1* delims=:" %%i in ('ipconfig^|find "IP"^|find ":"') do set ip=%%j
for /F "tokens=1* delims=:" %%i in ('ipconfig^|find "255"') do set mask=%%j
for /F "tokens=1,2,3* delims=." %%i in ("%mask%") do set /A hexmask="%%i<<24|%%j<<16|%%k<<8|%%l"
for /L %%i in (0,1,31) do if !hexmask!==0 (set cidr=%%i & goto done) else set /A hexmask="hexmask<<1"
:done
exit /b

:wait
set /A p4="%1 & 0xFF"
set /A p3="%1 >> 8"
:wait_loop
ren %netw%.%p3%.%p4% %netw%.%p3%.%p4% 2>nul || goto wait_loop
goto :EOF

При сканировании подсети большого размера (с маской меньше /24) даже на мощных машинах могли возникать проблемы с ресурсами, поэтому был введен параметр IMAX - максимальное число одновременно запущенных пингов.

Есть косячок с подсказкой на машинах с включенным IPv6 - может подхватиться именно IPv6-адрес.

3

Re: CMD/BAT: Асинхронный ICMP-сканер подсети

ping -4

Я конечно далек от мысли... (с)

4

Re: CMD/BAT: Асинхронный ICMP-сканер подсети

Спасибо, smaharbA. Но я не об этом.

Собственно, вопроса никакого не было. Я просто поделился. И предупредил о возможном эффекте.

Речь была о подсказке по использованию (usage). Там для примера подставляется локальный ip/маска, который вытягивается из вывода ipconfig.
Проблемы никакой нет - можно просто еще один фильтр find /V наложить:

 for /F "tokens=1* delims=:" %%i in ('ipconfig^|find "IP"^|find /V "IPv6"^|find ":"') do set ip=%%j

5

Re: CMD/BAT: Асинхронный ICMP-сканер подсети

netsh inter ip show address
netsh inter ipv6 show address

Я конечно далек от мысли... (с)

6

Re: CMD/BAT: Асинхронный ICMP-сканер подсети

vors, посмотрите на такой: VBScript: асинхронная обработка множественных запросов WMI вариант.