
Многопоточный сканер обнаружения Modbus/TCP, написанный на C с использованием libmodbus.
Многопоточный сканер на C для обнаружения сервисов Modbus/TCP с использованием libmodbus.
Сканер не полагается только на открытый TCP-порт 502. Вместо этого он устанавливает соединение Modbus/TCP и отправляет запрос Modbus на прикладном уровне. Действительный ответ Modbus используется в качестве основного индикатора наличия сервиса Modbus.
Modbus — это промышленный протокол связи, обычно используемый ПЛК, RTU, HMI, SCADA-системами, датчиками, счётчиками и другими промышленными устройствами.
Modbus/TCP передаёт прикладной протокол Modbus поверх TCP.
Стандартный порт Modbus/TCP:
TCP/502
Типичный поток связи выглядит так:
Сканер
|
| TCP-соединение → 502
|
| Запрос Modbus/TCP
v
Устройство Modbus
|
| Ответ Modbus/TCP
v
Сканер
В отличие от протоколов, которые предоставляют баннер сразу после подключения, Modbus/TCP обычно требует, чтобы клиент отправил действительный запрос Modbus, прежде чем устройство сформирует ответ на прикладном уровне.
Сканер выполняет обнаружение в два этапа.
Сканер пытается установить TCP-соединение с:
<цель>:502
Если соединение не может быть установлено, цель считается не отвечающей на Modbus/TCP.
Однако открытый порт TCP/502 сам по себе не считается достаточным доказательством наличия Modbus.
После подключения сканер отправляет запрос Modbus с использованием libmodbus.
Основной зонд:
modbus_read_input_registers(ctx, 0, 1, ®);
Это формирует код функции Modbus:
0x04 - Чтение входных регистров
Запрос просит цель вернуть один входной регистр, начиная с адреса 0.
Если цель возвращает действительный ответ Modbus, сканер считает сервис обнаруженным.
Пакет Modbus/TCP состоит из:
+----------------------+----------------------+
| Заголовок MBAP | PDU |
+----------------------+----------------------+
Заголовок MBAP:
+------------------+
| Идентификатор транзакции | 2 байта
| Идентификатор протокола | 2 байта
| Длина | 2 байта
| Идентификатор устройства | 1 байт
+------------------+
PDU:
+------------------+
| Код функции | 1 байт
| Данные | N байт
+------------------+
Заголовок MBAP специфичен для Modbus/TCP.
Первый зонд сканера использует код функции 0x04.
Пример запроса:
00 01 00 00 00 06 01 04 00 00 00 01
Разбор:
00 01 Идентификатор транзакции
00 00 Идентификатор протокола
00 06 Длина
01 Идентификатор устройства
04 Код функции
00 00 Начальный адрес
00 01 Количество
00 01
Идентифицирует транзакцию.
Значение может варьироваться, поскольку идентификатор транзакции обычно управляется клиентской библиотекой Modbus.
00 00
Значение 0 идентифицирует Modbus.
00 06
Указывает количество байтов, следующих за полем длины.
01
Идентифицирует целевое устройство Modbus.
04
Код функции 0x04 означает:
Чтение входных регистров
00 00
Сканер начинает с адреса регистра 0.
00 01
Сканер запрашивает один регистр.
Успешный ответ на запрос содержит код функции 0x04 и данные запрошенного регистра.
Пример ответа:
00 01 00 00 00 05 01 04 02 00 00
Разбор:
00 01 Идентификатор транзакции
00 00 Идентификатор протокола
00 05 Длина
01 Идентификатор устройства
04 Код функции
02 Количество байтов
00 00 Значение регистра
Важная часть для обнаружения — то, что цель успешно обрабатывает запрос Modbus и возвращает действительный ответ на прикладном уровне Modbus.
Фактическое значение регистра зависит от устройства.
Простая проверка:
TCP/502 = ОТКРЫТ
не обязательно доказывает, что сервис является Modbus.
Номера портов — это условности. Другое приложение может прослушивать TCP/502, а устройство Modbus также может вести себя по-разному в зависимости от его конфигурации.
Поэтому сканер использует:
TCP-соединение
+
Ответ протокола Modbus
=
Обнаружение Modbus
Это делает обнаружение на прикладном уровне более значимым, чем простая проверка порта.
Некоторые устройства могут не отвечать на первоначальный запрос 0x04 из-за своей конфигурации регистров или поддерживаемых кодов функций.
Поэтому сканер пытается выполнить второй запрос, если первый не удался:
modbus_read_bits(ctx, 0, 1, bits);
Это использует код функции:
0x01 - Чтение катушек
Пример запроса:
00 02 00 00 00 06 01 01 00 00 00 01
Разбор:
00 02 Идентификатор транзакции
00 00 Идентификатор протокола
00 06 Длина
01 Идентификатор устройства
01 Код функции
00 00 Начальный адрес
00 01 Количество
Сканер считает цель обнаруженной, когда любая из операций Modbus получает успешный ответ.
IP-адрес цели
|
v
TCP-соединение
порт 502
|
+------+------+
| |
Не удалось Подключено
| |
v v
Игнорировать Функция 0x04
|
+------+------+
| |
Действителен Не удалось
| |
v v
MODBUS НАЙДЕН Функция 0x01
|
+------+------+
| |
Действителен Не удалось
| |
v v
MODBUS НАЙДЕН Нет обнаружения
Сканер использует libmodbus для формирования и разбора пакетов Modbus/TCP, а не для ручного конструирования кадров протокола.
Основная операция:
modbus_read_input_registers(ctx, 0, 1, ®);
Если это не удаётся:
modbus_read_bits(ctx, 0, 1, bits);
Затем соединение закрывается, а контекст libmodbus освобождается.
Это сохраняет обработку протокола внутри библиотеки Modbus, пока сканер обрабатывает:
Сканер использует короткие тайм-ауты соединения и ответа:
#define TIMEOUT_SEC 2
Это предотвращает блокировку рабочего потока на чрезмерное время одним недоступным или не отвечающим хостом.
Промышленные сети могут содержать устройства с относительно медленными ответами, поэтому значения тайм-аутов могут потребовать корректировки в зависимости от среды.
Цели распределяются между несколькими рабочими потоками.
Например:
Поток 1 → цели 1–64
Поток 2 → цели 65–128
Поток 3 → цели 129–192
Поток 4 → цели 193–254
Каждый рабочий поток независимо пытается выполнить обнаружение Modbus/TCP.
Это позволяет тестировать несколько хостов одновременно, а не ожидать каждую цель последовательно.
Положительный результат означает, что цель успешно ответила на запрос Modbus, понятный сканеру.
Это не обязательно идентифицирует:
Это отдельные задачи по снятию отпечатков или оценке.
Сканер в первую очередь является инструментом обнаружения сервисов Modbus/TCP.
Метод обнаружения намеренно консервативен.
Устройство может поддерживать Modbus, но не быть обнаруженным, если:
Поэтому:
Нет ответа ≠ Определённо не Modbus
Это означает, что сканер не смог получить успешный ответ, используя предпринятые зонды.
Установите необходимые зависимости и скомпилируйте с помощью:
make
Или напрямую:
gcc -Wall -Wextra -O2 -o modbus modbus.c -lmodbus -lpthread
Запуск:
./modbus -i 192.168.1.0 -s /24 -t 10 -o results.txt
Пример:
[+] Modbus обнаружен: 192.168.1.20:502
[+] Modbus обнаружен: 192.168.1.42:502
[+] Просканировано: 254 | Найдено: 2 Modbus
[+] Результаты сохранены в: results.txt
Сканер обнаруживает сервисы Modbus/TCP, выполняя фактическое взаимодействие с протоколом, а не полагаясь исключительно на обнаружение TCP-порта.
Процесс обнаружения:
Подключиться к TCP/502
↓
Отправить код функции Modbus 0x04
↓
Получен действительный ответ Modbus?
↓
ДА → Modbus обнаружен
|
НЕТ
↓
Отправить код функции Modbus 0x01
↓
Получен действительный ответ Modbus?
↓
ДА → Modbus обнаружен
|
НЕТ
↓
Нет обнаружения
Ключевой принцип прост:
Обнаруживайте протокол, а не только порт.