
Multithreaded Modbus/TCP-Erkennungsscanner, geschrieben in C mit libmodbus.
Ein multithreaded C-Scanner zur Erkennung von Modbus/TCP-Diensten mit libmodbus.
Der Scanner verlässt sich nicht nur darauf, dass der TCP-Port 502 geöffnet ist. Stattdessen stellt er eine Modbus/TCP-Verbindung her und sendet eine Modbus-Anfrage auf Anwendungsebene. Eine gültige Modbus-Antwort wird als primärer Indikator dafür verwendet, dass ein Modbus-Dienst vorhanden ist.
Modbus ist ein industrielles Kommunikationsprotokoll, das häufig von SPSen, RTUs, HMIs, SCADA-Systemen, Sensoren, Zählern und anderen industriellen Geräten verwendet wird.
Modbus/TCP transportiert das Modbus-Anwendungsprotokoll über TCP.
Der Standard-Modbus/TCP-Port ist:
TCP/502
Ein typischer Kommunikationsablauf ist:
Scanner
|
| TCP-Verbindung → 502
|
| Modbus/TCP-Anfrage
v
Modbus-Gerät
|
| Modbus/TCP-Antwort
v
Scanner
Im Gegensatz zu Protokollen, die unmittelbar nach dem Verbindungsaufbau ein Banner bereitstellen, erfordert Modbus/TCP in der Regel, dass der Client eine gültige Modbus-Anfrage sendet, bevor das Gerät eine Antwort auf Anwendungsebene erzeugt.
Der Scanner führt die Erkennung in zwei Phasen durch.
Der Scanner versucht, eine TCP-Verbindung herzustellen zu:
<Ziel>:502
Wenn die Verbindung nicht hergestellt werden kann, wird das Ziel als nicht auf Modbus/TCP reagierend behandelt.
Ein offener TCP/502-Port allein ist jedoch kein ausreichender Nachweis für Modbus.
Nach dem Verbindungsaufbau sendet der Scanner eine Modbus-Anfrage mit libmodbus.
Die primäre Sonde ist:
modbus_read_input_registers(ctx, 0, 1, ®);
Dies erzeugt einen Modbus-Funktionscode:
0x04 - Read Input Registers
Die Anfrage fordert vom Ziel ein Eingangsregister ab der Adresse 0 an.
Wenn das Ziel eine gültige Modbus-Antwort zurückgibt, betrachtet der Scanner den Dienst als erkannt.
Ein Modbus/TCP-Paket besteht aus:
+----------------------+----------------------+
| MBAP-Header | PDU |
+----------------------+----------------------+
MBAP-Header:
+------------------+
| Transaktions-ID | 2 Bytes
| Protokoll-ID | 2 Bytes
| Länge | 2 Bytes
| Einheiten-ID | 1 Byte
+------------------+
PDU:
+------------------+
| Funktionscode | 1 Byte
| Daten | N Bytes
+------------------+
Der MBAP-Header ist spezifisch für Modbus/TCP.
Die erste Sonde des Scanners verwendet den Funktionscode 0x04.
Eine repräsentative Anfrage ist:
00 01 00 00 00 06 01 04 00 00 00 01
Aufgeschlüsselt:
00 01 Transaktionskennung
00 00 Protokollkennung
00 06 Länge
01 Einheitenkennung
04 Funktionscode
00 00 Startadresse
00 01 Anzahl
00 01
Identifiziert die Transaktion.
Der Wert kann variieren, da die Transaktionskennung normalerweise von der Modbus-Client-Bibliothek verwaltet wird.
00 00
Ein Wert von 0 identifiziert Modbus.
00 06
Gibt die Anzahl der Bytes an, die auf das Längenfeld folgen.
01
Identifiziert die Ziel-Modbus-Einheit.
04
Funktionscode 0x04 bedeutet:
Read Input Registers
00 00
Der Scanner beginnt bei Registeradresse 0.
00 01
Der Scanner fordert ein Register an.
Eine erfolgreiche Antwort auf die Anfrage enthält den Funktionscode 0x04 und die angeforderten Registerdaten.
Eine repräsentative Antwort könnte wie folgt aussehen:
00 01 00 00 00 05 01 04 02 00 00
Aufgeschlüsselt:
00 01 Transaktionskennung
00 00 Protokollkennung
00 05 Länge
01 Einheitenkennung
04 Funktionscode
02 Byteanzahl
00 00 Registerwert
Der wichtige Teil für die Erkennung ist, dass das Ziel die Modbus-Anfrage erfolgreich verarbeitet und eine gültige Modbus-Antwort auf Anwendungsebene zurückgibt.
Der tatsächliche Registerwert ist geräteabhängig.
Allein die Prüfung:
TCP/502 = OFFEN
beweist nicht unbedingt, dass der Dienst Modbus ist.
Portnummern sind Konventionen. Eine andere Anwendung kann auf TCP/502 lauschen, und ein Modbus-Gerät kann sich je nach Konfiguration ebenfalls unterschiedlich verhalten.
Der Scanner verwendet daher:
TCP-Konnektivität
+
Modbus-Protokollantwort
=
Modbus-Erkennung
Dies macht die Erkennung auf Anwendungsebene aussagekräftiger als einen einfachen Portscan.
Einige Geräte reagieren möglicherweise nicht auf die anfängliche 0x04-Anfrage, weil ihre Registerkonfiguration oder unterstützten Funktionscodes dies nicht zulassen.
Der Scanner versucht daher eine zweite Anfrage, wenn die erste fehlschlägt:
modbus_read_bits(ctx, 0, 1, bits);
Dies verwendet den Funktionscode:
0x01 - Read Coils
Eine repräsentative Anfrage ist:
00 02 00 00 00 06 01 01 00 00 00 01
Aufschlüsselung:
00 02 Transaktionskennung
00 00 Protokollkennung
00 06 Länge
01 Einheitenkennung
01 Funktionscode
00 00 Startadresse
00 01 Anzahl
Der Scanner betrachtet das Ziel als erkannt, wenn eine der beiden Modbus-Operationen eine erfolgreiche Antwort erhält.
Ziel-IP
|
v
TCP-Verbindung
Port 502
|
+------+------+
| |
Fehlgeschlagen Verbunden
| |
v v
Ignorieren Funktion 0x04
|
+------+------+
| |
Gültig Fehlgeschlagen
| |
v v
MODBUS GEFUNDEN Funktion 0x01
|
+------+------+
| |
Gültig Fehlgeschlagen
| |
v v
MODBUS GEFUNDEN Keine Erkennung
Der Scanner verwendet libmodbus, um Modbus/TCP-Pakete zu erstellen und zu analysieren, anstatt Protokollrahmen manuell zu konstruieren.
Die primäre Operation ist:
modbus_read_input_registers(ctx, 0, 1, ®);
Wenn dies fehlschlägt:
modbus_read_bits(ctx, 0, 1, bits);
Die Verbindung wird dann geschlossen und der libmodbus-Kontext wird freigegeben.
Dies hält die Protokollverarbeitung innerhalb der Modbus-Bibliothek, während der Scanner Folgendes übernimmt:
Der Scanner verwendet kurze Verbindungs- und Antwort-Timeouts:
#define TIMEOUT_SEC 2
Dies verhindert, dass ein einzelner nicht erreichbarer oder nicht reagierender Host einen Worker übermäßig lange blockiert.
Industrielle Netzwerke können Geräte mit relativ langsamen Antwortzeiten enthalten, daher müssen die Timeout-Werte je nach Umgebung möglicherweise angepasst werden.
Ziele werden auf mehrere Worker-Threads aufgeteilt.
Zum Beispiel:
Thread 1 → Ziele 1–64
Thread 2 → Ziele 65–128
Thread 3 → Ziele 129–192
Thread 4 → Ziele 193–254
Jeder Worker versucht unabhängig die Modbus/TCP-Erkennung.
Dies ermöglicht das gleichzeitige Testen mehrerer Hosts, anstatt auf jedes Ziel sequenziell zu warten.
Ein positives Ergebnis bedeutet, dass das Ziel erfolgreich auf eine Modbus-Anfrage geantwortet hat, die der Scanner versteht.
Es identifiziert nicht unbedingt:
Dies sind separate Fingerprinting- oder Bewertungsaufgaben.
Der Scanner ist in erster Linie ein Modbus/TCP-Diensterkennungswerkzeug.
Die Erkennungsmethode ist bewusst konservativ.
Ein Gerät kann Modbus-fähig sein, aber die Erkennung schlägt fehl, wenn:
Daher:
Keine Antwort ≠ Definitiv kein Modbus
Es bedeutet, dass der Scanner mit den von ihm versuchten Sonden keine erfolgreiche Antwort erhalten konnte.
Installieren Sie die erforderlichen Abhängigkeiten und kompilieren Sie mit:
make
Oder direkt:
gcc -Wall -Wextra -O2 -o modbus modbus.c -lmodbus -lpthread
Ausführen:
./modbus -i 192.168.1.0 -s /24 -t 10 -o results.txt
Beispiel:
[+] Modbus erkannt: 192.168.1.20:502
[+] Modbus erkannt: 192.168.1.42:502
[+] Gescannt: 254 | Gefunden: 2 Modbus
[+] Ergebnisse gespeichert in: results.txt
Der Scanner erkennt Modbus/TCP-Dienste durch tatsächliche Protokollinteraktion, anstatt sich ausschließlich auf die TCP-Porterkennung zu verlassen.
Der Erkennungsprozess ist:
Verbinden mit TCP/502
↓
Modbus-Funktionscode 0x04 senden
↓
Gültige Modbus-Antwort empfangen?
↓
JA → Modbus erkannt
|
NEIN
↓
Modbus-Funktionscode 0x01 senden
↓
Gültige Modbus-Antwort empfangen?
↓
JA → Modbus erkannt
|
NEIN
↓
Keine Erkennung
Das Kernprinzip ist einfach:
Erkenne das Protokoll, nicht nur den Port.