Wireshark Plugin für S7-Protokoll

Der s7comm Dissector ist jetzt im offiziellen Wireshark Code-Repository. Ich denke mal dass er bei einem der nächsten Releases dabei sein wird.

An der grundsätzlichen Darstellung habe ich nur Kleinigkeiten geändert (es hat sich niemand beschwert). Ich musste aber noch einige Dinge ändern damit er durch den Code-Review Prozess kommt.
So sind jetzt für alle Felder die aufgeschlüsselt werden Filtermöglichkeiten vorhanden. Wenn man also nach einem bestimmten Feldwert suchen will, klickt man mit der rechten Maustaste auf das Feld und wählt "Apply as Filter".

Das s7comm-plus Plugin (für 1200/1500) ist noch nicht soweit dass man es einreichen könnte.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe mir eben mal angesehen wie ein HMI Daten aus einer 1200er über s7comm-plus ausliest.
Im Antworttelegramm gibt es ein Byte für die diversen in der SPS vorhandenen Datentypen.
Alles mehr oder weniger normal, die Daten werden in der Network Byte Order übertragen.

Bis auf Datentyp DInt, also ein vorzeichenbehafteter 32 Bit Wert. Das scheint so exotisch zu sein dass Siemens sich da was ganz besonderes hat einfallen lassen. Ich schreibe mal ein paar Werte und die Bytes dazu:
Code:
  -1 = 7f 00 00 00
  -2 = 7e 00 00 00
  -3 = 7d 00 00 00
  -4 = 7c 00 00 00
  +1 = 01 00 00 00
  +2 = 02 00 00 00
 +15 = 0f 00 00 00
 +16 = 10 00 00 00
 +32 = 20 00 00 00
 +48 = 30 00 00 00
 +63 = 3f 00 00 00
 +64 = 80 40 00 00

+126 = 80 7e 00 00
+127 = 80 7f 00 00
+128 = 81 00 00 00
+129 = 81 01 00 00

+254 = 81 7e 00 00
+255 = 81 7f 00 00
+256 = 82 00 00 00

-127 = ff 01 00 00
-128 = ff 00 00 00
-129 = fe 7f 00 00

123456789 = ba ef 9a 15

Hat da jemand eine Erklärung wie sich so ein Format nennt, und welchen Grund es hat nur bei diesem Datentyp eine Ausnahme vom Übertragungsformat von allen anderen Daten zu machen?
 
d.h. 8 Bit => überlaufbyte
7 Bit => Negativbit

das 7te bit ist nur im ersten Byte das negativ Bit! ansonsten kann jedes byte 2^7 darstellen, wenn der Wert größer wird, wird das bit bei 2^8 gesetzt und dieses byte steht nun für die größeren werte...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
[COLOR=blue]private[/COLOR] [COLOR=blue]byte[/COLOR][] getBytes([COLOR=blue]int[/COLOR] value)
{
    [COLOR=blue]var[/COLOR] retVal = [COLOR=blue]new[/COLOR] [COLOR=#2b91af]List[/COLOR]<[COLOR=blue]byte[/COLOR]>();
 
    [COLOR=blue]var[/COLOR] wr = value;
    [COLOR=blue]if[/COLOR] (value < 0)
        wr *= -1;
 
    [COLOR=blue]var[/COLOR] anzBytes = 1;
    
    [COLOR=blue]var[/COLOR] anzBits = ([COLOR=blue]int[/COLOR])[COLOR=#2b91af]Math[/COLOR].Ceiling([COLOR=#2b91af]Math[/COLOR].Log(wr) / [COLOR=#2b91af]Math[/COLOR].Log(2));
    [COLOR=blue]if[/COLOR] (anzBits > 6)
    {
        anzBytes++;
        anzBits -= 6;
    }
    [COLOR=blue]while[/COLOR] (anzBits > 7)
    {
        anzBytes++;
        anzBits -= 7;
    }
    
 
    [COLOR=blue]for[/COLOR] ([COLOR=blue]int[/COLOR] i = 1; i <= anzBytes; i++)
    {
        [COLOR=blue]byte[/COLOR] wert = 0;
 
        [COLOR=blue]var[/COLOR] fakt = (anzBytes - i)*7;
        [COLOR=blue]var[/COLOR] divisor = ([COLOR=blue]int[/COLOR]) [COLOR=#2b91af]Math[/COLOR].Pow(2, fakt);
        wert = ([COLOR=blue]byte[/COLOR]) (wr/divisor);
        wr = wr%divisor;
 
        [COLOR=blue]if[/COLOR] (value < 0)
        {
            [COLOR=blue]if[/COLOR] (i == anzBytes || wr == 0)
                wert -= 1;
 
            wert = ([COLOR=blue]byte[/COLOR]) ~(wert);
            [COLOR=blue]if[/COLOR] (i == anzBytes)
                wert &= 0x7F;
 
            [COLOR=blue]if[/COLOR] (i == 1)
                wert |= 0x40;
        }
 
        [COLOR=blue]if[/COLOR] (i != anzBytes)
            wert |= 0x80;
 
 
        retVal.Add(wert);
    }
 
    [COLOR=blue]return[/COLOR] retVal.ToArray();
}


dieser Code funktioniert für deine Werte...
 
Zuletzt bearbeitet:
Das ist doch total kaputt! Große Werte benötigen damit nämlich ein Byte mehr als normal. Dieses gepackte Format wird so wie es aussieht nur bei Ganzzahlen ab 4 Byte verwendet. Gleitkommazahlen werden nicht gepackt.
Ob sich der ganze Aufwand mit der Sonderbehandlung gelohnt hat um ein paar Bytes einzusparen, was in Summe im einstelligen Prozentbereich der Gesamtdatenmenge betragen dürfte? Ich weiß ja nicht...

In Rückwärtssrichtung sieht meine Wandlung jetzt so aus:
Code:
guint32 get_packed_int32(tvbuff_t *tvb, guint8 *number_of_bytes, gint32 *value, guint32 offset)
{
    int n;
    gint32 val = 0;
    guint8 b;

    for (n = 1; n <= 4+1; n++) {        /* große Werte benötigen 5 Bytes */
        b = tvb_get_guint8(tvb, offset);
        offset += 1;

        if ((n == 1) && (b & 0x40)) {   /* Vorzeichen prüfen */
            b &= ~0x40;
            val = 0xffffffff;
            val <<= 6;
        } else {
            val <<= 7;
        }
        if (b & 0x80) {                 /* es folgt noch ein Byte */
            b &= ~0x80;
            val |= b;
        } else {                        /* alle Bytes gelesen */
            val |= b;
            break;
        }
    }
    *number_of_bytes = n;
    *value = val;
    return offset;
}

Konstanten im Programmcode der 1200er Steuerung werden auch in einem ganz speziellen Format gepackt, aber dort nochmal komplett anders.
Bei Siemens scheint der Speicher wirklich teuer zu sein, wenn man an jeder Ecke solche Extrawürste braten musste.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Also was ich so gesehen habe, hat sich Siemens beim TIA-Portal wirklich viel bei Microsoft abgeguckt. Sei es hier, oder beim Bytecode für die 1200er.

Aber die Sache hier mit dem varuint Typ war wirklich ein Schlüssel. Es lüftet sich der Schleier, und man findet altbekannte Werte wie die CRC aus dem anderen Adressierungsmodus den die V11 verwendet hat wieder.
 
Also mein Algorythmus zum Codieren ist ja großer Müll... Aber hab im Moment keinen besseren Parat...
Wie ist es damit?
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx

Mono macht das so:
https://github.com/mono/mono/blob/master/mcs/class/corlib/System.IO/BinaryWriter.cs
Code:
[TABLE="class: highlight tab-size-8 js-file-line-container"]
[TR]
[TD="class: blob-code js-file-line"]protected void Write7BitEncodedInt(int value) {
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]			do {
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				int high = (value >> 7) & 0x01ffffff;[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				byte b = (byte)(value & 0x7f);[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"] 
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				if (high != 0) {
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]					b = (byte)(b | 0x80);[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				}[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"] 
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				Write(b);[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]				value = high;[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]			} while(value != 0);
[/TD]
       [/TR]
       [TR]
                  [TD="class: blob-code js-file-line"]		}
[/TD]
[/TR]
[/TABLE]
 
Zuletzt bearbeitet:
Das Siemens Format ist aber etwas anders.
Bei den .Net Funktionen werden negative Zahlen zwar komprimiert aber immer noch als Zweierkomplement abgelegt.
-127 = 0x 81 ff ff ff 0f
+127 = 0x 7f

Da ist das Siemens Format bei negativen Zahlen platzsparender.
 
Hab mal beide Funktionen in eine Klasse gebaut... wie gesagt, meine gehört noch verbessert, aber funzt im Moment...

Code:
[FONT=Consolas][COLOR=#0433ff]using[/COLOR] System;[/FONT]
[FONT=Consolas][COLOR=#0433ff]using[/COLOR] System.Collections.Generic;[/FONT]
[FONT=Consolas][COLOR=#0433ff]using[/COLOR] System.Linq;[/FONT]
[FONT=Consolas][COLOR=#0433ff]using[/COLOR] System.Text;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas][COLOR=#0433ff]namespace[/COLOR] DotNetSiemensPLCToolBoxLibrary.PLCs.S7_1xyy[/FONT]
[FONT=Consolas]{[/FONT]
[COLOR=#33A2BD][FONT=Consolas][COLOR=#000000]    [/COLOR][COLOR=#0433ff]public[/COLOR][COLOR=#000000] [/COLOR][COLOR=#0433ff]static[/COLOR][COLOR=#000000] [/COLOR][COLOR=#0433ff]class[/COLOR][COLOR=#000000] [/COLOR]VariableLengthQuantityHelper[/FONT][/COLOR]
[FONT=Consolas]    {[/FONT]
[FONT=Consolas]        [COLOR=#0433ff]public[/COLOR] [COLOR=#0433ff]static[/COLOR] [COLOR=#0433ff]int[/COLOR] DecodeInt([COLOR=#0433ff]byte[/COLOR][] bytes, [COLOR=#0433ff]int[/COLOR] offset)[/FONT]
[FONT=Consolas]        {[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]int[/COLOR] n;[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]uint[/COLOR] val = 0;[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]byte[/COLOR] b;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]for[/COLOR] (n = 1; n <= 4 + 1; n++)[/FONT]
[COLOR=#008F00][FONT=Consolas][COLOR=#000000]            {        [/COLOR]/* große Werte benötigen 5 Bytes */[/FONT][/COLOR]
[FONT=Consolas]                b = bytes[offset];[/FONT]
[FONT=Consolas]                offset += 1;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]if[/COLOR] ((n == 1) && ((b & 0x40) > 0))[/FONT]
[COLOR=#008F00][FONT=Consolas][COLOR=#000000]                {   [/COLOR]/* Vorzeichen prüfen */[/FONT][/COLOR]
[FONT=Consolas]                    b &= [COLOR=#0433ff]unchecked[/COLOR](([COLOR=#0433ff]byte[/COLOR])~0x40);[/FONT]
[FONT=Consolas]                    val = 0xffffffff;[/FONT]
[FONT=Consolas]                    val <<= 6;[/FONT]
[FONT=Consolas]                }[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]else[/COLOR][/FONT]
[FONT=Consolas]                {[/FONT]
[FONT=Consolas]                    val <<= 7;[/FONT]
[FONT=Consolas]                }[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]if[/COLOR] ((b & 0x80) > 0)[/FONT]
[FONT=Consolas]                {                 [COLOR=#008f00]/* es folgt noch ein Byte */[/COLOR][/FONT]
[FONT=Consolas]                    b &= [COLOR=#0433ff]unchecked[/COLOR](([COLOR=#0433ff]byte[/COLOR])~0x80);[/FONT]
[FONT=Consolas]                    val |= b;[/FONT]
[FONT=Consolas]                }[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]else[/COLOR][/FONT]
[FONT=Consolas]                {                        [COLOR=#008f00]/* alle Bytes gelesen */[/COLOR][/FONT]
[FONT=Consolas]                    val |= b;[/FONT]
[FONT=Consolas]                    [COLOR=#0433ff]break[/COLOR];[/FONT]
[FONT=Consolas]                }[/FONT]
[FONT=Consolas]            }[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]return[/COLOR] ([COLOR=#0433ff]int[/COLOR])val;[/FONT]
[FONT=Consolas]        }[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]        [COLOR=#0433ff]public[/COLOR] [COLOR=#0433ff]static[/COLOR] [COLOR=#0433ff]byte[/COLOR][] EncodeInt([COLOR=#0433ff]int[/COLOR] value)[/FONT]
[FONT=Consolas]        {[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]var[/COLOR] retVal = [COLOR=#0433ff]new[/COLOR] [COLOR=#33a2bd]List[/COLOR]<[COLOR=#0433ff]byte[/COLOR]>();[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]var[/COLOR] wr = value;[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]if[/COLOR] (value < 0)[/FONT]
[FONT=Consolas]                wr *= -1;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]var[/COLOR] anzBytes = 1;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]var[/COLOR] anzBits = ([COLOR=#0433ff]int[/COLOR])[COLOR=#33a2bd]Math[/COLOR].Ceiling([COLOR=#33a2bd]Math[/COLOR].Log(wr) / [COLOR=#33a2bd]Math[/COLOR].Log(2));[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]if[/COLOR] (anzBits >= 6)[/FONT]
[FONT=Consolas]            {[/FONT]
[FONT=Consolas]                anzBytes++;[/FONT]
[FONT=Consolas]                anzBits -= 6;[/FONT]
[FONT=Consolas]            }[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]while[/COLOR] (anzBits > 7)[/FONT]
[FONT=Consolas]            {[/FONT]
[FONT=Consolas]                anzBytes++;[/FONT]
[FONT=Consolas]                anzBits -= 7;[/FONT]
[FONT=Consolas]            }[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]for[/COLOR] ([COLOR=#0433ff]int[/COLOR] i = 1; i <= anzBytes; i++)[/FONT]
[FONT=Consolas]            {[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]byte[/COLOR] wert = 0;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]var[/COLOR] fakt = (anzBytes - i) * 7;[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]var[/COLOR] divisor = ([COLOR=#0433ff]int[/COLOR])[COLOR=#33a2bd]Math[/COLOR].Pow(2, fakt);[/FONT]
[FONT=Consolas]                wert = ([COLOR=#0433ff]byte[/COLOR])(wr / divisor);[/FONT]
[FONT=Consolas]                wr = wr % divisor;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]if[/COLOR] (value < 0)[/FONT]
[FONT=Consolas]                {[/FONT]
[FONT=Consolas]                    [COLOR=#0433ff]if[/COLOR] (i == anzBytes || wr == 0)[/FONT]
[FONT=Consolas]                        wert -= 1;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                    wert = ([COLOR=#0433ff]byte[/COLOR])~(wert);[/FONT]
[FONT=Consolas]                    [COLOR=#0433ff]if[/COLOR] (i == anzBytes)[/FONT]
[FONT=Consolas]                        wert &= 0x7F;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                    [COLOR=#0433ff]if[/COLOR] (i == 1)[/FONT]
[FONT=Consolas]                        wert |= 0x40;[/FONT]
[FONT=Consolas]                }[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                [COLOR=#0433ff]if[/COLOR] (i != anzBytes)[/FONT]
[FONT=Consolas]                    wert |= 0x80;[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]                retVal.Add(wert);[/FONT]
[FONT=Consolas]            }[/FONT]
[FONT=Consolas]
[/FONT]
[FONT=Consolas]            [COLOR=#0433ff]return[/COLOR] retVal.ToArray();[/FONT]
[FONT=Consolas]        }[/FONT]
[FONT=Consolas]    }[/FONT]
[FONT=Consolas]}[/FONT]
 
Ich denke im TIA Projekt sind Bausteine Daten mit ZLib komprimiert gespeichert... Bin mir da aber nicht sicher, könnte aber ja auch sein das auf die PLC etwas komprimiert übertragen wird...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe nur Zugriff auf eine S7-1200, aber bei dem Aufbau der Kommunikation eines Panels zur SPS bin ich ein ganzes Stück weiter. Zumindest beim Aufbau der Variablenanfragen und der Werte in den Antworttelegrammen.

Um einen Kommunikationstreiber zu schreiben der symbolisch auf eine 1200er zugreifen kann reicht es aber noch nicht ganz aus, wobei auf jeden Fall die LIDs aus dem TIA-Projekt ausgelesen werden müssen. Die CRC kann man sich aus dem Symbol ja selber berechnen.
Vielleicht hat ja jemand Lust am rätseln wie der Aufbau der fehlenden Stücke wohl sein könnte. Vor allem beim Antworttelegramm zum ersten Verbindungsaufbau gibt es mittendrin eine seltsame Struktur, bei der ich nicht weiterkomme. Ich denke mal man wird irgendetwas von diesen Werten benötigen um die Session-Id für den weiteren Datenverkehr zu bestimmen.

Das unpraktische an den varuint-Typen ist, dass man im Telegramm nicht sehen kann ob ein Feld ein Typ mit fester Größe oder eben der varuint ist, außer wenn man Werte größer 2^7 für das Feld provizieren kann.

Ich hänge mal den aktuellen Stand der dll für 32 Bit, und zwei Logfiles an, bei denen ein Panel mit einer 1200er kommuniziert.
 

Anhänge

  • s7comm-plus-demo-und-samples.zip
    14,9 KB · Aufrufe: 18
Ja, das ist ein komplett anderes Protokoll als bei der 300/400 mit der 0x32 als Protokoll-Id, welche jetzt 0x72 lautet.
Innendrin steckt zumindest für die 1200 aber immer noch der Zugriffsmechanismus über die Symbol-Prüfsumme und den LID-Nummern, nur die LID-Flags sind weggefallen. Seit V12 verwendet die HMI-Simulation nur noch diese Telegramme. Ich weiß nicht ob die vorige Variante von neuen CPUs überhaupt noch unterstützt wird.

Von der 1500 habe ich selber nur ein paar Logfiles gesehen. Die verwendet was ich da gesehen habe vom Prinzip her den gleichen Telegramm-Aufbau wie die 1200 (also die 0x72er Telegramme), aber innendrin sieht es etwas anders aus.
 
Zurück
Oben