
Ein Werkzeug zur Manipulation von Schneider Electric PLC-Archivdateien
Dieses Werkzeug kann SPS-"Archiv"-Dateien extrahieren und wieder zusammenbauen, die von mehreren Schneider Electric SPS (unter anderem M580, M340 und Quantum) verwendet werden. Die Archivdateien mit der Erweiterung .sta sind eigentlich ZIP-Archive, die einige Dateien enthalten. Innerhalb des Archivs ist die wirklich wichtige Datei Station.apx. Der Inhalt dieser Datei wird bei einem (vollständigen) Programm-Download/-Upload zur/von der SPS übertragen. Natürlich enthält sie den eigentlichen ausführbaren Code des SPS-Programms. Sie enthält aber auch alle notwendigen Informationen, um das SPS-Programm in Control Expert Classic (oder Unity Pro) zu bearbeiten. Das .apx-Dateiformat ist proprietär und es sind nur wenige Informationen darüber verfügbar. Einige Informationen sind von Liras en la red und Team82 verfügbar, aber diese Arbeit geht tiefer als das, was sie veröffentlicht haben. Mit diesem Werkzeug können die verschiedenen "Abschnitte" aus der Station.apx extrahiert (und bei Bedarf dekomprimiert) werden, um sie zu untersuchen. Das Werkzeug kann die Station.apx auch aus zuvor extrahierten – und möglicherweise modifizierten – Abschnittsdaten und Metadaten wieder zusammensetzen. Und diese zusammengesetzte Datei öffnet sich in Control Expert Classic / Unity Pro fehlerfrei, sofern die vorgenommenen Änderungen die "Abschnittslogik" nicht verletzen.
Es gab nie einen großen Plan hinter diesem Projekt. Ich verwende ab und zu Schneider Electric SPS und war daran interessiert, wie sie auf einer grundlegenderen Ebene funktionieren (oder nicht). Es stellte sich heraus, dass diese Untersuchung komplex genug war, um mich zu unterhalten (vermutlich wie das Lösen von Kreuzworträtseln für normale Menschen), und am Ende hatte ich dieses Werkzeug entwickelt.
Das Werkzeug gibt sein Bestes, um die Dateien, mit denen es arbeitet, zu extrahieren und wieder zusammenzusetzen. Der Benutzer sollte sich jedoch bewusst sein, dass das Werkzeug entwickelt wurde mit:
Die Verwendung neu zusammengesetzter Dateien auf einer echten SPS kann zu Problemen führen, insbesondere wenn Sie die Abschnitte oder Metadaten geändert haben. Es ist sogar möglich, dass die SPS dadurch unbrauchbar wird. Laden Sie daher modifizierte Archivdateien auf eigenes Risiko auf die SPS.
Das Werkzeug wird über die Befehlszeile mit dem Python-Interpreter verwendet:
python apxutil.py -h
usage: apxutil.py [-h] [-f filaname-apx] [-F filaname-sta] [-e dir] [-E dir] [-a manifestpath] [-A manifestpath] [-d]
[-x] [-B] [-r]
Tool to manipulate Schneider Electric .sta and .apx files
options:
-h, --help show this help message and exit
-f, --apxfile filaname-apx
.apx file to read or write
-F, --stafile filaname-sta
.sta file to read or write
-e, --extract-apx dir
extract contents of the Station.apx file
-E, --extract-sta dir
extract contents of the .sta file
-a, --assemble-apx manifestpath
create Station.apx file based on apx_manifest.ini
-A, --assemble-sta manifestpath
create a .sta file from files in sta_manifest.ini
-d, --decompress decompress Station.apx sections that are compressed
-x, --hexdump print hexdump of Station.apx with header information
-B, --include-apd include Station.apd when creating .sta archive
-r, --restart-offsets
restart offsets at 0 for each section in hexdump print (useful for diffing)
Die Hilfe sollte weitgehend selbsterklärend sein. Im Allgemeinen arbeiten Kurznotationen mit Großbuchstaben an .sta-Dateien und Kleinbuchstaben an .apx-Dateien. Einige Anwendungsbeispiele sind unten angegeben.
Um den Inhalt einer .sta-Datei in das Verzeichnis "extracted" zu extrahieren, würden Sie verwenden:
python apxutil.py -F archive.sta -E extracted
Um den Inhalt von Station.apx in das Verzeichnis "contents" zu extrahieren, würden Sie verwenden:
python apxutil.py -f extracted/BinAppli/Station.apx -e contents -d
Die Option "-d" bedeutet, dass komprimierte Abschnitte dekomprimiert werden, kann aber weggelassen werden, wenn (alle) Rohdaten der Abschnitte gewünscht werden.
Um Station.apx wieder zusammenzusetzen, würden Sie verwenden:
python apxutil.py -f extracted/BinAppli/Station.apx -a contents
Dadurch wird die Station.apx basierend auf der apx_manifest.ini im Verzeichnis contents zusammengesetzt, wobei Abschnittsdaten komprimiert werden, falls sie zuvor dekomprimiert wurden. Abschnittsgrößen und CRC werden neu berechnet und nicht aus der apx_manifest.ini gelesen.
Um eine .sta-Datei wieder zusammenzusetzen, würden Sie verwenden:
python apxutil.py -F modified-archive.sta -A extracted
Dadurch wird standardmäßig die Datei Station.apd weggelassen (die hauptsächlich eine Integritätsprüfung für den Station.apx-Dateikopf zu sein scheint). Wenn Sie diese einschließen möchten, fügen Sie die Option "-B" hinzu. Dies kann jedoch dazu führen, dass Control Expert Classic die Projektarchivdatei nicht öffnen kann.
Um den Inhalt der Station.apx-Datei direkt "anzuzeigen", kann der folgende Befehl verwendet werden:
python apxutil.py -F modified-archive.sta -x
Dies gibt einen "kanonischen Hexdump" der Station.apx-Datei aus, d.h. Sie sehen die Offsets, Hex-Daten und ASCII-Daten, 16 Bytes auf einmal, einschließlich der Metadaten. Die Option "-d" kann verwendet werden, um komprimierte Abschnitte zu dekomprimieren (aber Vorsicht: die Offsets ergeben in diesem Fall wenig Sinn). Die Option "-r" kann verwendet werden, um den Offset für jeden Abschnitt bei 0 neu zu starten. Dies ist nützlich, wenn Sie Änderungen vorgenommen haben und diese ohne Offsets "diffen" möchten.
Wenn Sie das APX-Dateiformat verstehen möchten (auf dem Niveau von mir und diesem Werkzeug), ist der beste Weg wirklich, sich den Quellcode von apxutil.py anzusehen. Der Code sollte auch mit wenig Programmiererfahrung nicht zu schwer zu lesen sein. Er könnte jedoch echte Programmierer übel machen. Auf jeden Fall wird hier ein Überblick auf hoher Ebene über das Dateiformat gegeben. Die APX-Datei beginnt mit einem 32-Byte-Dateikopf. Der Rest der Datei ist in verschiedene Abschnitte unterteilt, jeder mit einer ähnlichen Struktur. Jeder Abschnitt beginnt mit einem Abschnittskopf, dessen Länge vom Abschnittstyp abhängt (definiert am Anfang des Kopfes). Auf den Abschnittskopf folgt ein RTE (Kopf). Nach dem RTE kommen die Abschnittsdaten (falls vorhanden). Und nach den Abschnittsdaten beginnt der nächste Abschnitt (Kopf). Es scheint, dass der Abschnittskopf eher mit dem Station.apx-Format zusammenhängt und der RTE eher mit der Ausführungsumgebung (RunTime Environment oder RealTime Environment?), aber die Unterscheidung ist nicht eindeutig.
Der APX-Dateikopf ist 32 Bytes lang und beginnt mit dem ASCII-Text "APX", gefolgt von einigen Metadaten. Vielleicht am wichtigsten: Er definiert den Typ und die Größe des verwendeten RTE. Typ 0x02 habe ich gesehen. Es ist wahrscheinlich, dass Typ 0x01 für 16-Bit-Adressraum und Typ 0x02 für 32-Bit-Adressraum steht, aber dies ist keineswegs sicher. Es gibt einige Unbekannte, die wahrscheinlich eine Bedeutung haben. Ein paar 32-Bit-Werte und ein 16-Bit-Wert sind im Abschnitt "SD" 0x0001 "wiederholt", aber ihre Bedeutung ist unbekannt. Die letzten 11 Bytes scheinen immer Nullen zu sein. Ein Beispiel unten (im kanonischen Hexdump-Format mit Metadaten):
00000000 41 50 58 00 00 01 02 01 10 01 10 60 f6 c3 30 00 |APX........`..0.| ApxFileHeader(magic=0x00585041, version_maybe=0x0100, rte_type=0x02, header_count_maybe=0x01, sdsection_total_size=0x0110, rte_size=0x10, sdsection_39_4=0x30c3f660, sdsection_35_4=0x0b926500, sesection_8_2=0x0106, zero_pad_21_11=0000000000000000000000)
00000010 65 92 0b 06 01 00 00 00 00 00 00 00 00 00 00 00 |e...............|
Der APX-Abschnittskopf beginnt mit einem 16-Bit-Wert, der seinen Typ definiert. Die Typen 0x0001 - 0x0004 scheinen gültig zu sein, aber ich habe nur die Typen 0x0000, 0x0001 und 0x0002 gesehen. Die Länge des Abschnittskopfes hängt vom Typ ab. Für 0x0000 beträgt die Länge 8 Bytes, für 0x0001 - 0x0002 beträgt sie 16 Bytes und für die Typen 0x0003 - 0x0004 sollte sie 24 Bytes betragen. Die Abschnittsnummer wird durch einen 16-Bit-Wert an Byte-Offset 4 angegeben. Für die Abschnittstypen 0x0000 und 0x0001 ist dies alles, was ich weiß, und sie haben keine Abschnittsdaten. Für den Abschnittstyp 0x0002 gibt der 32-Bit-Wert an Byte-Offset 10 die Größe der Abschnittsdaten an, wie sie in der Station.apx-Datei vorhanden sind.
Der RTE-Kopf, der auf den Abschnittskopf folgt, ist 16 Bytes lang (zumindest für Typ 0x02). Die Bedeutung des ersten 32-Bit-Werts ist unbekannt (aber hier als "block_count" bezeichnet). Der 32-Bit-Wert, der bei Offset 4 beginnt, ist die Größe der Abschnittsdaten (aus dem Abschnittskopf) minus 1, falls der Abschnitt Daten hat. Wenn der Abschnitt keine Daten hat, ist die Bedeutung unbekannt (und Abschnitt 0x0011 bricht diese Regel). Der 32-Bit-Wert, der bei Byte-Offset 8 beginnt, sind Attribute/Flags, deren Bedeutung größtenteils unbekannt ist. Einige Gedanken sind am Ende von apxutil.py aufgeführt. Der 16-Bit-Wert, der bei Byte-Offset 12 beginnt, ist die CRC16/Kermit-Prüfsumme der Abschnittsdaten (und wiederum bricht Abschnitt 0x0011 diese Regel). Die oberen 3 Bits von Byte 14 definieren den Speicherbereich und die unteren 5 Bits das Speicherfolio. Das letzte Byte 15 ist wahrscheinlich ungenutzt.
Die Abschnittsdaten sind im Allgemeinen "Rohdaten", die ausführbaren Code, Projektinformationen, komprimierte Daten usw. enthalten können. Es scheint, dass wenn Bit 13 in den RTE-Attributen gesetzt ist, die Abschnittsdaten komprimiert sind. Die gesehenen Komprimierungstypen sind zip/"pk", zlib mit nicht standardmäßigem Header und "raw deflate".
Während alle Abschnitte ihre eigene Bedeutung haben, scheinen die Abschnitte 0x0001 "RT" und 0x0011 "SD" grundlegender zu sein als die anderen. Sie scheinen mit den RTE und dem tatsächlichen Speicherlayout der SPS-Ausführungsumgebung zusammenzuhängen. Eine teilweise Dekodierung dieser Abschnitte ist gegen Ende von apxutil.py enthalten, wird aber derzeit nicht vom Werkzeug selbst verwendet.
Es gibt viele "niedrig hängende Früchte", falls jemand weiterforschen möchte. Einige offene Fragen: