Im Gefolge von Industrial Ninja: Wie PLC an den Positive Hack Days 9 gehackt wurde



An PHDays 9 veranstalteten wir einen Wettbewerb zum Hacken einer Gaspumpanlage - den Industrial Ninja- Wettbewerb. Auf dem Gelände befanden sich drei Stände mit unterschiedlichen Sicherheitsparametern (Keine Sicherheit, Niedrige Sicherheit, Hohe Sicherheit), die denselben industriellen Prozess nachahmten: Luft wurde unter Druck in den Ballon gepumpt (und dann abgesenkt).

Trotz verschiedener Sicherheitsparameter war die Hardware der Stände dieselbe: Siemens Simatic S7-300 PLC; Notabblasknopf und Druckmessgerät (angeschlossen an digitale SPS-Eingänge (DI)); Ventile zum Pumpen und Entlüften (angeschlossen an die digitalen Ausgänge der SPS (DO)) - siehe Abbildung unten.



Die SPS entschied sich abhängig von den Druckwerten und gemäß ihrem Programm, die Kugel zu blasen oder aufzublasen (öffnete und schloss die entsprechenden Ventile). An allen Ständen war jedoch ein manueller Steuermodus vorgesehen, der es ermöglichte, die Ventilzustände ohne Einschränkungen zu steuern.

Die Stände zeichneten sich durch die Komplexität der Aktivierung dieses Modus aus: Auf einem ungeschützten Stand war dies am einfachsten, auf dem Hochsicherheitsstand war es dementsprechend schwieriger.

In zwei Tagen waren fünf der sechs Probleme gelöst; Der Gewinner des ersten Platzes erhielt 233 Punkte (er verbrachte eine Woche damit, sich auf den Wettbewerb vorzubereiten). Drei Gewinner: Ich platziere - a1exdandy, II - Rubikoid, III - Ze.

Während der PHDays konnte jedoch keiner der Teilnehmer alle drei Stände überwinden. Daher entschieden wir uns für einen Online-Wettbewerb und veröffentlichten Anfang Juni die schwierigste Aufgabe. Die Teilnehmer mussten die Aufgabe in einem Monat erledigen, die Flagge finden, die Lösung detailliert und interessant beschreiben.

Im Rahmen der Kürzung veröffentlichen wir eine Analyse der besten Lösung für den im Laufe des Monats gesendeten Auftrag. Sie wurde von Alexey Kovrizhnykh (a1exdandy) von der Digital Security Company gefunden, die während der PHDays den 1. Platz im Wettbewerb belegte. Nachfolgend geben wir den Text mit unseren Kommentaren an.

Erste Analyse


In der Aufgabe gab es also ein Archiv mit Dateien:

  • block_upload_traffic.pcapng
  • DB100.bin
  • hints.txt

Die Datei hints.txt enthält die erforderlichen Informationen und Tipps zur Lösung der Aufgabe. Hier sind die Inhalte:

  1. Petrovich sagte mir gestern, dass Sie von PlcSim Blöcke in Schritt 7 herunterladen können.
  2. Am Stand wurden SLCs der Siemens Simatic S7-300-Serie eingesetzt.
  3. PlcSim ist ein SPS-Emulator, mit dem Sie Programme für Siemens S7-SPS ausführen und debuggen können.

  Die Datei DB100.bin enthält anscheinend einen DB100-SPS-Datenblock:
 00000000: 0100 0102 6e02 0401 0206 0100 0101 0102 .... n ...........
 00000010: 1002 0501 0202 2002 0501 0206 0100 0102 ...... .........
 00000020: 0102 7702 0401 0206 0100 0103 0102 0a02 ..w .............
 00000030: 0501 0202 1602 0501 0206 0100 0104 0102 ................
 00000040: 7502 0401 0206 0100 0105 0102 0a02 0501 u ...............
 00000050: 0202 1602 0501 0206 0100 0106 0102 3402 .............. 4.
 00000060: 0401 0206 0100 0107 0102 2602 0501 0202 .......... & .....
 00000070: 4c02 0501 0206 0100 0108 0102 3302 0401 L ........... 3 ...
 00000080: 0206 0100 0109 0102 0a02 0501 0202 1602 ................
 00000090: 0501 0206 0100 010a 0102 3702 0401 0206 .......... 7 .....
 000000a0: 0100 010b 0102 2202 0501 0202 4602 0501 ...... "..... F ...
 000000b0: 0206 0100 010c 0102 3302 0401 0206 0100 ........ 3 .......
 000000c0: 010d 0102 0a02 0501 0202 1602 0501 0206 ................
 000000d0: 0100 010e 0102 6d02 0401 0206 0100 010f ...... m .........
 000000e0: 0102 1102 0501 0202 2302 0501 0206 0100 ........ # .......
 000000f0: 0110 0102 3502 0401 0206 0100 0111 0102 .... 5 ...........
 00000100: 1202 0501 0202 2502 0501 0206 0100 0112 ......% .........
 00000110: 0102 3302 0401 0206 0100 0113 0102 2602 ..3 ........... &.
 00000120: 0501 0202 4c02 0501 0206 0100 .... L ....... 

Dem Namen nach zu urteilen, enthält die Datei block_upload_traffic.pcapng einen Speicherauszug des Blockladeverkehrs zur SPS.

Es ist erwähnenswert, dass dieser Verkehrsstau auf dem Wettbewerbsgelände während der Konferenz etwas schwieriger zu bekommen war. Dazu musste das Skript aus der Projektdatei für TeslaSCADA2 verstanden werden. Daraus konnte man erkennen, wo sich der mit RC4 verschlüsselte Dump befindet und welcher Schlüssel zum Entschlüsseln verwendet werden sollte. Dumps von Datenblöcken am Standort können mit dem S7-Protokollclient abgerufen werden. Ich habe dafür den Demo-Client aus dem Snap7-Paket verwendet.

Extrahieren von Signalverarbeitungseinheiten aus einem Verkehrsdump


Wenn Sie sich den Inhalt des Speicherauszugs ansehen, können Sie verstehen, dass die Signalverarbeitungsblöcke OB1, FC1, FC2 und FC3 darin übertragen werden:



Diese Blöcke müssen extrahiert werden. Dies kann beispielsweise mit dem folgenden Skript erfolgen, nachdem der Datenverkehr vom pcapng-Format in pcap konvertiert wurde:

#!/usr/bin/env python2 import struct from scapy.all import * packets = rdpcap('block_upload_traffic.pcap') s7_hdr_struct = '>BBHHHHBB' s7_hdr_sz = struct.calcsize(s7_hdr_struct) tpkt_cotp_sz = 7 names = iter(['OB1.bin', 'FC1.bin', 'FC2.bin', 'FC3.bin']) buf = '' for packet in packets: if packet.getlayer(IP).src == '10.0.102.11': tpkt_cotp_s7 = str(packet.getlayer(TCP).payload) if len(tpkt_cotp_s7) < tpkt_cotp_sz + s7_hdr_sz: continue s7 = tpkt_cotp_s7[tpkt_cotp_sz:] s7_hdr = s7[:s7_hdr_sz] param_sz = struct.unpack(s7_hdr_struct, s7_hdr)[4] s7_param = s7[12:12+param_sz] s7_data = s7[12+param_sz:] if s7_param in ('\x1e\x00', '\x1e\x01'): # upload buf += s7_data[4:] elif s7_param == '\x1f': with open(next(names), 'wb') as f: f.write(buf) buf = '' 

Nachdem Sie die empfangenen Blöcke untersucht haben, können Sie feststellen, dass sie immer mit den Bytes 70 70 (pp) beginnen. Jetzt müssen Sie lernen, wie Sie sie analysieren. Ein Hinweis auf die Aufgabe legt nahe, dass Sie hierfür PlcSim verwenden müssen.

Vom Menschen lesbare Anweisungen aus Blöcken erhalten


Versuchen wir zunächst, S7-PlcSim zu programmieren, indem wir mehrere Blöcke mit sich wiederholenden Anweisungen (= Q 0.0) mit der Simatic Manager-Software laden und das Ergebnis im SPS-Emulator in der Datei example.plc speichern. Durch Betrachten des Inhalts der Datei können Sie den Beginn der geladenen Blöcke leicht anhand der Signatur 70 70 bestimmen, die wir zuvor entdeckt haben. Vor den Blöcken wird die Blockgröße anscheinend in Form eines 4-Byte-Little-Endian-Werts geschrieben.



Nachdem wir Informationen über die Struktur von SPS-Dateien erhalten hatten, wurde der folgende Aktionsplan zum Lesen von SPS-S7-Programmen angezeigt:

  1. Mit dem Simatic Manager erstellen wir in S7-PlcSim eine Blockstruktur ähnlich der, die wir aus dem Dump erhalten haben. Die Blockgrößen müssen mit ihren Bezeichnern (OB1, FC1, FC2, FC3) übereinstimmen (erreicht durch Füllen der Blöcke mit der erforderlichen Anzahl von Anweisungen).
  2. Speichern Sie die SPS in einer Datei.
  3. Wir ersetzen den Inhalt der Blöcke in der empfangenen Datei durch die Blöcke aus dem Verkehrsdump. Der Beginn der Blöcke wird durch die Signatur bestimmt.
  4. Die resultierende Datei wird auf S7-PlcSim hochgeladen und wir sehen uns den Inhalt der Blöcke in Simatic Manager an.

Blöcke können beispielsweise durch folgenden Code ersetzt werden:

 with open('original.plc', 'rb') as f: plc = f.read() blocks = [] for fname in ['OB1.bin', 'FC1.bin', 'FC2.bin', 'FC3.bin']: with open(fname, 'rb') as f: blocks.append(f.read()) i = plc.find(b'pp') for block in blocks: plc = plc[:i] + block + plc[i+len(block):] i = plc.find(b'pp', i + 1) with open('target.plc', 'wb') as f: f.write(plc) 

Aleksey ging einen möglicherweise komplizierteren, aber immer noch richtigen Weg. Wir gingen davon aus, dass die Teilnehmer das NetToPlcSim-Programm verwenden würden, damit PlcSim über das Netzwerk kommunizieren, Blöcke über Snap7 in PlcSim laden und diese Blöcke dann als Projekt von PlcSim unter Verwendung der Entwicklungsumgebung herunterladen könnte.

Durch Öffnen der resultierenden Datei in S7-PlcSim können Sie überschriebene Blöcke mit Simatic Manager lesen. Die wichtigsten Geräteverwaltungsfunktionen sind in Block FC1 aufgezeichnet. Die Variable # TEMP0 zieht besondere Aufmerksamkeit auf sich, wenn sie eingeschaltet ist, scheint es, dass die SPS-Steuerung basierend auf den Werten des Bitspeichers M2.2 und M2.3 in den manuellen Modus geschaltet wird. # TEMP0 wird von FC3 eingestellt.



Um das Problem zu lösen, muss die FC3-Funktion analysiert und verstanden werden, was zu tun ist, damit eine logische Einheit zurückgegeben wird.

Die SPS-Signalverarbeitungsblöcke am Stand für niedrige Sicherheit am Wettbewerbsstandort wurden auf die gleiche Weise angeordnet, aber um den Wert der Variablen # TEMP0 festzulegen, reichte es aus, die Zeile my ninja way in den DB1-Block zu schreiben. Die Überprüfung des Werts im Block war klar angeordnet und erforderte keine gründlichen Kenntnisse der Blockprogrammiersprache. Auf der Hochsicherheitsstufe wird es natürlich viel schwieriger sein, eine manuelle Steuerung zu erreichen, und es ist notwendig, die Feinheiten der STL-Sprache zu verstehen (eine der Möglichkeiten, die S7-SPS zu programmieren).

Reverse Block FC3


Der Inhalt des FC3-Blocks in der STL-Darstellung:
  LB#16#0 T #TEMP13 T #TEMP15 LP#DBX 0.0 T #TEMP4 CLR = #TEMP14 M015: L #TEMP4 LAR1 OPN DB 100 L DBLG TAR1 <=D JC M016 L DW#16#0 T #TEMP0 L #TEMP6 LW#16#0 <>I JC M00d LP#DBX 0.0 LAR1 M00d: LB [AR1,P#0.0] T #TEMP5 LW#16#1 ==I JC M007 L #TEMP5 LW#16#2 ==I JC M008 L #TEMP5 LW#16#3 ==I JC M00f L #TEMP5 LW#16#4 ==I JC M00e L #TEMP5 LW#16#5 ==I JC M011 L #TEMP5 LW#16#6 ==I JC M012 JU M010 M007: +AR1 P#1.0 LP#DBX 0.0 LAR2 LB [AR1,P#0.0] LC#8 *I +AR2 +AR1 P#1.0 LB [AR1,P#0.0] JL M003 JU M001 JU M002 JU M004 M003: JU M005 M001: OPN DB 101 LB [AR2,P#0.0] T #TEMP0 JU M006 M002: OPN DB 101 LB [AR2,P#0.0] T #TEMP1 JU M006 M004: OPN DB 101 LB [AR2,P#0.0] T #TEMP2 JU M006 M00f: +AR1 P#1.0 LB [AR1,P#0.0] LC#8 *IT #TEMP11 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 TAR1 #TEMP4 OPN DB 101 LP#DBX 0.0 LAR1 L #TEMP11 +AR1 LAR2 #TEMP9 LB [AR2,P#0.0] TB [AR1,P#0.0] L #TEMP4 LAR1 JU M006 M008: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP3 +AR1 P#1.0 LB [AR1,P#0.0] JL M009 JU M00b JU M00a JU M00c M009: JU M005 M00b: L #TEMP3 T #TEMP0 JU M006 M00a: L #TEMP3 T #TEMP1 JU M006 M00c: L #TEMP3 T #TEMP2 JU M006 M00e: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] AW INVI T #TEMP12 LB [AR1,P#0.0] LB [AR2,P#0.0] OW L #TEMP12 AW TB [AR1,P#0.0] L DW#16#0 T #TEMP0 L MB 101 T #TEMP1 L MB 102 T #TEMP2 L #TEMP4 LAR1 JU M006 M011: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] -ITB [AR1,P#0.0] L DW#16#0 T #TEMP0 L MB 101 T #TEMP1 L MB 102 T #TEMP2 L #TEMP4 LAR1 JU M006 M012: L #TEMP15 INC 1 T #TEMP15 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] ==I JCN M013 JU M014 M013: LP#DBX 0.0 LAR1 T #TEMP4 LB#16#0 T #TEMP6 JU M006 M014: L #TEMP4 LAR1 L #TEMP13 LL#1 +IT #TEMP13 JU M006 M006: L #TEMP0 T MB 100 L #TEMP1 T MB 101 L #TEMP2 T MB 102 +AR1 P#1.0 L #TEMP6 + 1 T #TEMP6 JU M005 M010: LP#DBX 0.0 LAR1 L 0 T #TEMP6 TAR1 #TEMP4 M005: TAR1 #TEMP4 CLR = #TEMP16 L #TEMP13 LL#20 ==IS #TEMP16 L #TEMP15 ==IA #TEMP16 JC M017 L #TEMP13 LL#20 <IS #TEMP16 L #TEMP15 ==IA #TEMP16 JC M018 JU M019 M017: SET = #TEMP14 JU M016 M018: CLR = #TEMP14 JU M016 M019: CLR O #TEMP14 = #RET_VAL JU M015 M016: CLR O #TEMP14 = #RET_VAL 

Der Code ist ziemlich umfangreich und für eine Person, die mit STL nicht vertraut ist, kann er kompliziert erscheinen. Es ist nicht sinnvoll, jede Anweisung im Rahmen dieses Artikels zu zerlegen. Detaillierte Anweisungen und STL- Sprachfunktionen finden Sie im entsprechenden Handbuch: Anweisungsliste (STL) für die S7-300- und S7-400-Programmierung . Hier werde ich nach der Verarbeitung denselben Code angeben - Bezeichnungen und Variablen umbenennen und Kommentare hinzufügen, die den Arbeitsalgorithmus und einige Konstruktionen der STL-Sprache beschreiben. Ich stelle sofort fest, dass in dem betrachteten Block eine virtuelle Maschine implementiert ist, die einen im DB100-Block befindlichen Bytecode ausführt, dessen Inhalt wir kennen. Anweisungen für virtuelle Maschinen bestehen aus 1 Byte Betriebscode und Bytes mit Argumenten, einem Byte für jedes Argument. Alle überprüften Anweisungen haben zwei Argumente. Ich habe ihre Werte in den Kommentaren als X und Y bezeichnet.

Nachbearbeitungscode
]]
 #    LB#16#0 T #CHECK_N #     T #COUNTER_N #     LP#DBX 0.0 T #POINTER #     CLR = #PRE_RET_VAL #     - LOOP: L #POINTER LAR1 OPN DB 100 L DBLG TAR1 <=D #       JC FINISH L DW#16#0 T #REG0 L #TEMP6 LW#16#0 <>I JC M00d LP#DBX 0.0 LAR1 #  switch - case     M00d: LB [AR1,P#0.0] T #OPCODE LW#16#1 ==I JC OPCODE_1 L #OPCODE LW#16#2 ==I JC OPCODE_2 L #OPCODE LW#16#3 ==I JC OPCODE_3 L #OPCODE LW#16#4 ==I JC OPCODE_4 L #OPCODE LW#16#5 ==I JC OPCODE_5 L #OPCODE LW#16#6 ==I JC OPCODE_6 JU OPCODE_OTHER #   01:    DB101[X]   Y # OP01(X, Y): REG[Y] = DB101[X] OPCODE_1: +AR1 P#1.0 LP#DBX 0.0 LAR2 LB [AR1,P#0.0] #   X (  DB101) LC#8 *I +AR2 +AR1 P#1.0 LB [AR1,P#0.0] #   Y ( ) JL M003 #  switch - case    Y JU M001 #      . JU M002 #       JU M004 #      M003: JU LOOPEND M001: OPN DB 101 LB [AR2,P#0.0] T #REG0 #   DB101[X]  REG[0] JU PRE_LOOPEND M002: OPN DB 101 LB [AR2,P#0.0] T #REG1 #   DB101[X]  REG[1] JU PRE_LOOPEND M004: OPN DB 101 LB [AR2,P#0.0] T #REG2 #   DB101[X]  REG[2] JU PRE_LOOPEND #   02:   X   Y # OP02(X, Y): REG[Y] = X OPCODE_2: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP3 +AR1 P#1.0 LB [AR1,P#0.0] JL M009 JU M00b JU M00a JU M00c M009: JU LOOPEND M00b: L #TEMP3 T #REG0 JU PRE_LOOPEND M00a: L #TEMP3 T #REG1 JU PRE_LOOPEND M00c: L #TEMP3 T #REG2 JU PRE_LOOPEND #  03    ,    ... #   04:   X  Y # OP04(X, Y): REG[0] = 0; REG[X] = (REG[X] == REG[Y]) OPCODE_4: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 #   - X LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 # REG[X] LAR2 #TEMP10 # REG[Y] LB [AR1,P#0.0] LB [AR2,P#0.0] AW INVI T #TEMP12 # ~(REG[Y] & REG[X]) LB [AR1,P#0.0] LB [AR2,P#0.0] OW L #TEMP12 AW # (~(REG[Y] & REG[X])) & (REG[Y] | REG[X]) -     TB [AR1,P#0.0] L DW#16#0 T #REG0 L MB 101 T #REG1 L MB 102 T #REG2 L #POINTER LAR1 JU PRE_LOOPEND #   05:   Y  X # OP05(X, Y): REG[0] = 0; REG[X] = REG[X] - REG[Y] OPCODE_5: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] -I # ACCU1 = ACCU2 - ACCU1, REG[X] - REG[Y] TB [AR1,P#0.0] L DW#16#0 T #REG0 L MB 101 T #REG1 L MB 102 T #REG2 L #POINTER LAR1 JU PRE_LOOPEND #   06:  #CHECK_N    X  Y # OP06(X, Y): #CHECK_N += (1 if REG[X] == REG[Y] else 0) OPCODE_6: L #COUNTER_N INC 1 T #COUNTER_N +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 # REG[X] LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 # REG[Y] LAR2 #TEMP10 # REG[X] LB [AR1,P#0.0] LB [AR2,P#0.0] ==I JCN M013 JU M014 M013: LP#DBX 0.0 LAR1 T #POINTER LB#16#0 T #TEMP6 JU PRE_LOOPEND M014: L #POINTER LAR1 #   #CHECK_N L #CHECK_N LL#1 +IT #CHECK_N JU PRE_LOOPEND PRE_LOOPEND: L #REG0 T MB 100 L #REG1 T MB 101 L #REG2 T MB 102 +AR1 P#1.0 L #TEMP6 + 1 T #TEMP6 JU LOOPEND OPCODE_OTHER: LP#DBX 0.0 LAR1 L 0 T #TEMP6 TAR1 #POINTER LOOPEND: TAR1 #POINTER CLR = #TEMP16 L #CHECK_N LL#20 ==IS #TEMP16 L #COUNTER_N ==IA #TEMP16 #   ,  #CHECK_N == #COUNTER_N == 20 JC GOOD L #CHECK_N LL#20 <IS #TEMP16 L #COUNTER_N ==IA #TEMP16 JC FAIL JU M019 GOOD: SET = #PRE_RET_VAL JU FINISH FAIL: CLR = #PRE_RET_VAL JU FINISH M019: CLR O #PRE_RET_VAL = #RET_VAL JU LOOP FINISH: CLR O #PRE_RET_VAL = #RET_VAL 

Nachdem wir eine Vorstellung von den Anweisungen der virtuellen Maschine haben, schreiben wir einen kleinen Disassembler zum Parsen des Bytecodes im DB100-Block:

 import string alph = string.ascii_letters + string.digits with open('DB100.bin', 'rb') as f: m = f.read() pc = 0 while pc < len(m): op = m[pc] if op == 1: print('R{} = DB101[{}]'.format(m[pc + 2], m[pc + 1])) pc += 3 elif op == 2: c = chr(m[pc + 1]) c = c if c in alph else '?' print('R{} = {:02x} ({})'.format(m[pc + 2], m[pc + 1], c)) pc += 3 elif op == 4: print('R0 = 0; R{} = (R{} == R{})'.format( m[pc + 1], m[pc + 1], m[pc + 2])) pc += 3 elif op == 5: print('R0 = 0; R{} = R{} - R{}'.format( m[pc + 1], m[pc + 1], m[pc + 2])) pc += 3 elif op == 6: print('CHECK (R{} == R{})\n'.format( m[pc + 1], m[pc + 2])) pc += 3 else: print('unk opcode {}'.format(op)) break 

Als Ergebnis erhalten wir den folgenden Code der virtuellen Maschine:

Code der virtuellen Maschine
 R1 = DB101[0] R2 = 6e (n) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[1] R2 = 10 (?) R0 = 0; R1 = R1 - R2 R2 = 20 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[2] R2 = 77 (w) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[3] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[4] R2 = 75 (u) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[5] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[6] R2 = 34 (4) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[7] R2 = 26 (?) R0 = 0; R1 = R1 - R2 R2 = 4c (L) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[8] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[9] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[10] R2 = 37 (7) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[11] R2 = 22 (?) R0 = 0; R1 = R1 - R2 R2 = 46 (F) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[12] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[13] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[14] R2 = 6d (m) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[15] R2 = 11 (?) R0 = 0; R1 = R1 - R2 R2 = 23 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[16] R2 = 35 (5) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[17] R2 = 12 (?) R0 = 0; R1 = R1 - R2 R2 = 25 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[18] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[19] R2 = 26 (?) R0 = 0; R1 = R1 - R2 R2 = 4c (L) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) 

Wie Sie sehen können, überprüft dieses Programm einfach jedes Symbol aus DB101 auf Gleichheit mit einem bestimmten Wert. Die letzte Zeile zum Bestehen aller Prüfungen: n0w u 4r3 7h3 m4573r. Wenn diese Leitung im DB101-Block platziert ist, wird die manuelle Steuerung der SPS aktiviert und es ist möglich, den Ballon zu sprengen oder zu entleeren.

Das ist alles! Alexey hat ein hohes Maß an Wissen bewiesen, das eines industriellen Ninja würdig ist :) Wir haben dem Gewinner unvergessliche Preise geschickt. Vielen Dank an alle Teilnehmer!

Source: https://habr.com/ru/post/de460615/


All Articles