Anrufen Fernwartung WhatsApp
Modbus IoT Reverse-Engineering Automatisierungstechnik Kärnten

Wie Sie Modbus-Register ohne Herstellerdokumentation reverse-engineeren

Thomas Kohlweiss 8 Min. Lesezeit

Keine Registerkarte vom Hersteller? So entlocken Sie Modbus-Steuerungen ihre Register per Wireshark & Python – Know-how aus Kärnten. Jetzt anfragen!

Wie Sie Modbus-Register ohne Herstellerdokumentation reverse-engineeren

Die meisten Heizungs-, Gebäude- und Industriesteuerungen sprechen Modbus/TCP – simpel, lizenzfrei und seit über 30 Jahren stabil. Das Problem: Hersteller liefern selten eine Registerkarte mit, sondern nur eine grafische Visualisierung, hinter der niemand weiß, welches Register welchen Wert trägt. Genau das braucht man aber, um eine Anlage in Home Assistant, Grafana oder ein eigenes Monitoring einzubinden.

Was Sie in diesem Artikel erfahren:

  • Wie sich Video und Netzwerkmitschnitt ohne gemeinsame Uhr trotzdem synchronisieren lassen
  • Wie man Modbus/TCP aus einem Wireshark-Mitschnitt dekodiert, auch ohne tshark
  • Warum zwei unabhängige Beweise pro Register nötig sind, bevor eine Zuordnung als sicher gilt
  • Welche Fallstricke bei Nullwerten, doppelten Registern und gepackten Feldern lauern

Das Grundproblem: zwei Datenquellen ohne gemeinsame Uhr

Bei einer Blackbox-Steuerung ohne Herstellerdokumentation gibt es zwei unabhängige Datenquellen: ein Video der Bedienoberfläche und einen Wireshark-Mitschnitt (tcp.port==502) der Modbus/TCP-Kommunikation im Hintergrund. Beide laufen parallel, aber mit unterschiedlichem Startzeitpunkt und ohne gemeinsame Zeitbasis.

Bei einer Registerkarte mit tausenden Adressen und ähnlich aussehenden Werten – viele Temperaturen liegen zwischen 0 und 100 – lässt sich die Zuordnung nicht raten. In der Beratungspraxis bei Kärntner Betrieben mit älteren WAGO- oder Heizungssteuerungen ist das die Regel, nicht die Ausnahme: Die Anlage funktioniert seit Jahren zuverlässig, aber niemand weiß mehr, welches Register die Rücklauftemperatur trägt. Es braucht einen belastbaren Beweis statt einer Vermutung.

Ein konkretes Beispiel aus der Praxis

Am Beispiel einer KWB-Heizungssteuerung mit Windows-Visualisierung habe ich in einer einzigen rund sechsminütigen Aufnahme 17 Register mit Dezimalstellen-Genauigkeit verifiziert – ganz ohne Herstellerdokumentation, nur mit Wireshark, einer Bildschirmaufnahme und Python. Die Methode dahinter lässt sich auf jede Modbus-Anlage übertragen, egal ob Heizung, Produktionsmaschine oder Gebäudetechnik.


Schritt 1: Eine gemeinsame Zeitbasis schaffen

Der naheliegende, aber unzuverlässige Ansatz ist, die Wireshark-„No.”-Spalte im Video abzulesen und mit der Paketnummer im PCAP abzugleichen. Das funktioniert nicht mehr, sobald ein Anzeigefilter aktiv ist oder das Video komprimiert ist – und ist mühsam, weil für jeden Wechsel eine fünfstellige Paketnummer per Auge gelesen werden müsste.

Robuster ist ein Klartext-Zeitstempel, den man nur einmal statt pro Ereignis abgleicht. Der Dateiname der Bildschirmaufnahme trägt den Aufnahmestart (ref_2026-07-14_16-03-55.mkv → 16:03:55), Wireshark schreibt beim Speichern automatisch den Capture-Start in den Dateinamen, und der PCAP selbst liefert zusätzlich einen Unix-Epoch-Zeitstempel im ersten Paket:

from scapy.all import rdpcap
import datetime

pkts = rdpcap("ref_ws.pcapng")
t0 = float(pkts[0].time)
print(datetime.datetime.utcfromtimestamp(t0) + datetime.timedelta(hours=2))  # CEST
# → 2026-07-14 16:04:21.695220

In diesem Fall ergab sich eine Differenz von 26,7 Sekunden, sodass für den gesamten Mitschnitt gilt: pcap_relative_time ≈ video_time − 26.7. Diese Konstante habe ich zusätzlich an sechs Stellen gegengeprüft, an denen die Steuerung selbst eine Uhrzeit einblendet – die Abweichung lag überall unter einer Sekunde. Das reicht, weil Modbus-Polling-Zyklen ohnehin nur alle 2–3 Sekunden laufen: Man muss nicht auf die Millisekunde genau sein, sondern nur genau genug, um den richtigen Polling-Zyklus zu treffen.

Sobald der Offset steht, muss man Wireshark im Video gar nicht mehr lesen. Jeder Zeitpunkt, an dem die Bedienoberfläche einen Wert zeigt, lässt sich direkt in einen PCAP-Zeitstempel übersetzen – und von dort aus per Python die passenden Pakete holen. Für Betriebe, die eine WAGO-Steuerung oder ähnliche Anlagen betreiben, lohnt sich vorab ein Blick auf den grundlegenden Unterschied zwischen Modbus RTU und Modbus TCP, bevor man in die Rohdaten einsteigt.

Wenn Sie wissen möchten, wie eine solche Analyse für Ihre eigene Anlage funktioniert, helfen wir weiter: IoT-Beratung für Unternehmen in Kärnten


Schritt 2 und 3: Modbus/TCP dekodieren und Query mit Response verknüpfen

Ohne tshark verfügbar, aber mit scapy, das jedes TCP-Payload roh herausgibt, lässt sich Modbus/TCP auch von Hand parsen. Vor der eigentlichen Modbus-PDU liegt ein einfacher 7-Byte-Header (MBAP), spezifiziert in der offiziellen Modbus-Application-Protocol-Spezifikation:

FeldBytesInhalt
Transaction ID2vom Master frei vergeben, in der Response gespiegelt
Protocol ID2immer 0x0000
Length2Restlänge ab Unit ID
Unit ID1Slave-Adresse
Function Code1z. B. 0x03 = Read Holding Registers
from scapy.all import rdpcap, TCP, Raw
import struct

pkts = rdpcap("ref_ws.pcapng")
records = []
for p in pkts:
    if not (p.haslayer(TCP) and p.haslayer(Raw)):
        continue
    tcp = p[TCP]
    if tcp.sport != 502 and tcp.dport != 502:
        continue
    data = bytes(p[Raw].load)
    if len(data) < 8:
        continue
    trans_id, proto_id, length, unit_id, func = struct.unpack(">HHHBB", data[:8])
    if proto_id != 0:
        continue
    direction = "Q" if tcp.dport == 502 else "R"
    records.append((float(p.time), trans_id, unit_id, func, direction, data[8:]))

In diesem Fall ergab das 2831 Modbus-Frames, davon 1326× Function 3 (Read Holding Registers). Eine einzelne Response ist dabei wertlos ohne die passende Query – die Startadresse steht nur in der Query, die Werte nur in der Response. Beide tragen dieselbe Transaction ID, also matcht man per Dictionary und dekodiert anschließend die Registerwerte:

pending = {}
results = []
for rel_t, trans_id, unit_id, func, direction, payload in sorted(records):
    key = (trans_id, unit_id, func)
    if direction == "Q":
        pending[key] = (rel_t, payload)
    elif key in pending and func == 3:
        qt, qpayload = pending.pop(key)
        start_addr, qty = struct.unpack(">HH", qpayload[:4])
        bytecount = payload[0]
        values = struct.unpack(f">{bytecount // 2}H", payload[1:1 + bytecount])
        results.append({"t": rel_t, "start": start_addr, "values": values})

Damit existiert eine Liste im Format „Zum Zeitpunkt X wurden Register Y bis Z mit diesen Werten gelesen” – die Rohdatenbasis für alles Weitere. Für Automatisierungstechniker in Kärnten, die zuvor noch nie mit scapy gearbeitet haben: Die Bibliothek lässt sich einfach per pip install scapy --break-system-packages nachinstallieren, ein Ersatz für tshark ist damit meist nicht nötig.


Schritt 4: Zwei unabhängige Beweise statt einer Vermutung

Mit hunderten Register-Blöcken und Dutzenden Feldern in der Bedienoberfläche reicht „ungefähr zur gleichen Zeit gelesen” allein nicht – zu viele Kandidaten kommen infrage. Es braucht zwei sich gegenseitig verstärkende Indizien.

Value Matching nutzt aus, dass Dezimalstellen Gold wert sind: Zeigt die Oberfläche „31,4 %” Sauerstoff, ist ein Register mit exakt 314 bei unauffälligen Nachbarregistern praktisch kein Zufallstreffer mehr. Ganzzahlige Werte wie „0 %” sind dagegen schlechte Beweise, weil zu viele Register zufällig 0 enthalten.

Change Detection nutzt aus, dass nicht jedes Register ständig gepollt wird. Ein fester Satz „Basis”-Blöcke läuft durchgehend, zusätzliche Blöcke werden nur abgefragt, solange eine bestimmte Seite geöffnet ist:

from collections import Counter
blocks = Counter((r["start"], len(r["values"])) for r in results)
for (start, qty), count in sorted(blocks.items()):
    print(f"start={start:5d} qty={qty:4d}  count={count:3d}")

Ein Block mit nur wenigen Registern, der genau neunmal auftaucht – exakt in dem Zeitfenster, in dem im Video eine bestimmte Seite offen war – ist die Signatur eines seiten-spezifischen Requests. Damit lässt sich die Seitenzugehörigkeit eines Registers oft schon vor jedem Wertabgleich eingrenzen, rein über das Timing. Wer eine WAGO-Steuerung nachrüsten statt neu anschaffen möchte, findet dazu ergänzend den Artikel WAGO PFC200 nachrüsten: Bestandsanlagen vernetzen und visualisieren.


Praxisbeispiel Pufferspeicher und typische Fallstricke

So sieht der komplette Weg von der Bedienoberfläche bis zum Register aus. Bei Sekunde 235,2 im Video zeigte die Seite „Puffer” vier Werte: Temperatur 1 Ist/Soll (70 °C / 20 °C), Temperatur 2 Ist/Soll (58 °C / 20 °C), Maximum (60 °C) und Minimum (45 °C). Umgerechnet in PCAP-Zeit (235,2 − 26,7 = 208,5 s) lieferte der nächstgelegene Read vier aufeinanderfolgende Register mit den Rohwerten 695, 200, 580, 200 – durch zehn geteilt exakt deckungsgleich mit der Anzeige.

Ein separater, nur gelegentlich gepollter Block lieferte zeitgleich 600, 450, 1 – passend zu Maximum, Minimum und einem Programm-Enum. Zwei unabhängige Register-Bereiche, zwei unabhängige Beweise, ein konsistentes Bild. Erst an diesem Punkt gilt eine Zuordnung als bestätigt statt als Kandidat.

In der Praxis lauern dabei einige Fallstricke:

  • Nullwerte sind mehrdeutig. Steht ein Wert während der gesamten Aufnahme auf 0, gibt es dutzende Register mit demselben Inhalt – Value Matching versagt hier. Abhilfe: den Zustand aktiv ändern und per Change Detection das Register suchen, das sich zeitgleich mit der Anzeige bewegt.
  • Gespiegelte Register. Manche physikalischen Messwerte liegen doppelt vor, etwa weil eine Übersichtsseite einen eigenen Adressbereich für ihr Dashboard nutzt. Ohne zweite unabhängige Quelle lässt sich nicht sicher sagen, welches „das” Register ist – beide dokumentieren, nicht raten.
  • Kollidierende Zufallstreffer. Runde Zahlen wie 130 °C treten oft mehrfach identisch auf, weil mehrere physikalische Größen zufällig denselben Stand haben. Hier hilft nur eine zweite Aufnahme mit geänderten Werten.
  • Bit-gepackte Werte. Datumsfelder und Zeitprogramm-Schaltzeiten liegen selten als einfache Dezimalzahl vor (z. B. High-Byte = Monat, Low-Byte = Tag). Für eine sichere Dekodierung braucht es mehrere unterschiedliche Beobachtungswerte zum Vergleich.
  • Verzögertes Polling. Ein Registerblock, der zum Zielzeitpunkt gerade nicht gepollt wurde, liefert unter Umständen einen mehrere Sekunden alten Wert. Bei sich schnell ändernden Größen im Zweifel den zeitlich nächstgelegenen Read explizit gegen den Zeit-Diff prüfen, nicht blind den ersten Treffer nehmen.

Für den kompletten Durchlauf reichte im Übrigen ein schlanker Werkzeugkasten: ffmpeg zur automatischen Szenenwechsel-Erkennung im Video, scapy zum Parsen des PCAP, sowie reines Python (struct, collections.Counter) für Pairing und Wertsuche. Kein Spezialwerkzeug, keine Herstellerdokumentation – nur eine gemeinsame Zeitbasis, dezimalstellen-genaues Value Matching und eine Häufigkeitsanalyse der Poll-Zyklen.


Fazit: Zwei unabhängige Beweise statt geratener Adressen

Reverse Engineering einer Blackbox-Steuerung ist im Kern ein Abgleichproblem zwischen zwei Beobachtungsströmen ohne gemeinsame Uhr. Der Trick liegt selten im Protokoll selbst – Modbus ist technisch simpel –, sondern darin, für jede Registerzuordnung zwei voneinander unabhängige Beweise zu sammeln: einen aus dem Wert, einen aus dem Zeitverhalten. Wo beide übereinstimmen, ist die Zuordnung belastbar; wo nur einer greift oder beide mehrdeutig sind, ist ein ehrliches „braucht eine zweite Aufnahme” die bessere Antwort als eine geratene Adresse in einer Automatisierung, die monatelang falsch loggt.

Für Kärntner Betriebe mit Bestandsanlagen ohne Registerkarte spart diese Methode oft Wochen im Vergleich zum Warten auf eine Herstellerauskunft. Jetzt Kontakt aufnehmen – wir prüfen, ob sich Ihre Steuerung ohne Herstellerdokumentation erschließen lässt, und zeigen den nächsten Schritt.

Häufige Fragen

Ist das Reverse-Engineering von Modbus-Registern erlaubt?

Solange nur die eigene Anlage oder eine Anlage mit ausdrücklicher Zustimmung des Betreibers untersucht wird und keine Software des Herstellers verändert oder umgangen wird, ist das Mitlesen des eigenen Netzwerkverkehrs unproblematisch. Basic4web führt solche Analysen ausschließlich an Anlagen durch, für die eine klare Berechtigung vorliegt – etwa im Rahmen eines Beratungsauftrags in Kärnten.

Welche Werkzeuge braucht man für Modbus-Reverse-Engineering?

Im Kern reichen Wireshark für den Netzwerkmitschnitt, eine Bildschirmaufnahme der Bedienoberfläche und Python mit der Bibliothek scapy zur Auswertung. Spezialwerkzeuge oder Herstellerzugänge sind nicht nötig – die Methode funktioniert allein mit frei verfügbarer Software.

Wie lange dauert die Erstellung einer Registerkarte für eine Heizungssteuerung?

Für eine einzelne Aufnahmesession von wenigen Minuten, in der alle relevanten Seiten der Bedienoberfläche einmal durchgeklickt werden, lassen sich meist 15 bis 25 Register zuverlässig verifizieren. Mehrdeutige Werte wie Nullstände oder Datumsfelder brauchen oft eine zweite, gezielte Aufnahme.

Übernimmt Basic4web die Registeranalyse auch für Anlagen in Kärnten?

Ja, Basic4web unterstützt Kärntner Betriebe dabei, undokumentierte Modbus-Steuerungen für Home Assistant, Grafana oder eigene Monitoring-Lösungen zugänglich zu machen. Nehmen Sie Kontakt auf, wenn Ihre Anlage keine brauchbare Registerkarte mitbringt.

Thomas Kohlweiss

Dipl.-Ing. Ing. · Basic4web Kärnten

IT-Experte für KMU – Internet, IT-Sicherheit, KI & Smart Home. 20+ Jahre Erfahrung.

Kostenlose Erstberatung

Fragen zu Wie Sie Modbus-Register ohne Herstellerdokumentation reverse-engineeren?

Persönliche Beratung für Ihr Unternehmen in Kärnten – unverbindlich und kostenlos.

  • Antwort innerhalb 24 Stunden
  • Kein Callcenter – direkter Ansprechpartner
  • Erstberatung kostenlos & unverbindlich

Hilfe & Support

Nicht gefunden, wonach Sie suchen?

Falls die Angebote auf dieser Seite Ihre Frage nicht beantwortet haben, kontaktieren Sie uns direkt – wir helfen persönlich weiter.

Direkt erreichbar

Rückruf-Service

Wir rufen Sie zurück – teilen Sie uns Ihre Telefonnummer und die gewünschte Anrufzeit mit.

Rückruf anfordern

Erreichbar

Mo – Do:  08:00 – 18:00

Fr:         08:00 – 15:00