In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

By die laaste PHDays 9 het ons 'n kompetisie gehou om 'n gaspompaanleg te hack - kompetisie Industriële Ninja. Daar was drie staanplekke op die terrein met verskillende sekuriteitsparameters (Geen Sekuriteit, Lae Sekuriteit, Hoë Sekuriteit), wat dieselfde industriële proses naboots: lug onder druk is in 'n ballon gepomp (en dan vrygelaat).

Ten spyte van die verskillende veiligheidsparameters was die hardeware samestelling van die staanders dieselfde: Siemens Simatic PLC S7-300-reeks; nooddeflasieknoppie en drukmeettoestel (gekoppel aan PLC digitale insette (DI)); kleppe werk vir opblaas en deflasie van lug (gekoppel aan die digitale uitsette van die PLC (DO)) - sien die figuur hieronder.

In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

Die PLC het, afhangende van die druklesings en in ooreenstemming met sy program, 'n besluit geneem om die bal af te blaas of op te blaas (die ooreenstemmende kleppe oopgemaak en toegemaak). Alle staanders het egter 'n handbeheermodus gehad, wat dit moontlik gemaak het om die toestande van die kleppe sonder enige beperkings te beheer.

Die staanplekke het verskil in die kompleksiteit van die aktivering van hierdie modus: by die onbeskermde staanplek was dit die maklikste om dit te doen, en by die Hoë Sekuriteit-staanplek was dit dienooreenkomstig moeiliker.

Vyf van die ses probleme is in twee dae opgelos; Die eerste plek deelnemer het 233 punte verdien (hy het 'n week spandeer om vir die kompetisie voor te berei). Drie wenners: Ek plaas - a1exdandy, II - Rubikoid, III - Ze.

Tydens PHDays kon nie een van die deelnemers egter al drie staanplekke oorkom nie, daarom het ons besluit om 'n aanlyn kompetisie te maak en die moeilikste taak vroeg in Junie gepubliseer. Deelnemers moes die taak binne 'n maand voltooi, die vlag vind en die oplossing in detail en op 'n interessante manier beskryf.

Onder die snit publiseer ons 'n ontleding van die beste oplossing vir die taak van diegene wat oor die maand gestuur is, dit is gevind deur Alexey Kovrizhnykh (a1exdandy) van die Digital Security-maatskappy, wat die XNUMXste plek in die kompetisie tydens PHDays behaal het. Hieronder bied ons sy teks met ons kommentaar aan.

Aanvanklike analise

Dus, die taak bevat 'n argief met die volgende lêers:

  • block_upload_traffic.pcapng
  • DB100.bin
  • wenke.txt

Die hints.txt-lêer bevat die nodige inligting en wenke om die taak op te los. Hier is die inhoud daarvan:

  1. Petrovich het gister vir my gesê dat jy blokke van PlcSim in Stap7 kan laai.
  2. Die Siemens Simatic S7-300-reeks PLC is by die staanplek gebruik.
  3. PlcSim is 'n PLC-emulator waarmee u programme vir Siemens S7 PLC's kan laat loop en ontfout.

Dit lyk of die DB100.bin-lêer die DB100 PLC-datablok bevat: 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 0103a0102 ..w............. 0: 02 00000030 0501 0202 1602 0501 0206 ................ 0100: 0104 0102 00000040 7502 0401 0206 0100a0105 0102 u............... 0: 02 0501 00000050 0202 1602 0501 0206 0100............0106. 0102: 3402 4 00000060 0401 0206 0100 0107 0102 ......... & ..... 2602: 0501C0202 00000070 4 02 0501 0206 0100 0108 L ......... 0102. .. 3302 : 0401 3 00000080 0206 0100a0109 0102 0 02 ................ 0501: 0202 1602 00000090 0501a 0206 0100 010 0102 .......... 3702a0401: 0206 7b 000000 0 0100 010 0102 2202 ......".....F... 0501b0202: 4602 0501 000000c 0 0206 0100 ... 010 ... .. 0102c3302: 0401d 0206 0100a3 000000 0 010 0102 0 ................ 02d0501: 0202 1602e 0501 0206d000000 0 0100 010 0102 6 02 ...... .... 0401e0206: 0100 010 000000 0 0102 1102 0501 0202 ........#...... 2302f0501: 0206 0100 000000 0 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 0102c2602 3 00000120 0501 ....L......

Soos die naam aandui, bevat die block_upload_traffic.pcapng-lêer 'n storting van blokoplaaiverkeer na die PLC.

Dit is opmerklik dat hierdie verkeerstorting by die kompetisieterrein tydens die konferensie 'n bietjie moeiliker was om te verkry. Om dit te doen, was dit nodig om die skrif van die projeklêer vir TeslaSCADA2 te verstaan. Daaruit was dit moontlik om te verstaan ​​waar die stortplek wat met RC4 geïnkripteer is, geleë is en watter sleutel gebruik moes word om dit te dekripteer. Stortings van datablokke op die terrein kon verkry word deur die S7-protokolkliënt te gebruik. Hiervoor het ek die demo-kliënt van die Snap7-pakket gebruik.

Onttrek seinverwerkingsblokke van 'n verkeershoop

As u na die inhoud van die stortingsterrein kyk, kan u verstaan ​​dat dit seinverwerkingsblokke OB1, FC1, FC2 en FC3 bevat:

In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

Hierdie blokke moet verwyder word. Dit kan gedoen word, byvoorbeeld, met die volgende skrif, nadat die verkeer voorheen van die pcapng-formaat na pcap omgeskakel is:

#!/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 ('x1ex00', 'x1ex01'):  # upload
            buf += s7_data[4:]
        elif s7_param == 'x1f':
            with open(next(names), 'wb') as f:
                f.write(buf)
            buf = ''

Nadat u die resulterende blokke ondersoek het, sal u sien dat hulle altyd met grepe 70 70 (pp) begin. Nou moet jy leer hoe om hulle te ontleed. Die opdragwenk dui daarop dat jy PlcSim hiervoor moet gebruik.

Kry mens-leesbare instruksies uit blokke

Kom ons probeer eers om S7-PlcSim te programmeer deur verskeie blokke met herhalende instruksies (= Q 0.0) daarin te laai met behulp van Simatic Manager-sagteware, en die PLC wat in die emulator verkry is, te stoor in die example.plc-lêer. Deur na die inhoud van die lêer te kyk, kan jy maklik die begin van die afgelaaide blokke bepaal deur die handtekening 70 70, wat ons vroeër ontdek het. Voor die blokke word die blokgrootte blykbaar geskryf as 'n 4-grepe klein-endian waarde.

In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

Nadat ons inligting oor die struktuur van plc-lêers ontvang het, het die volgende aksieplan vir die lees van PLC S7-programme verskyn:

  1. Met behulp van Simatic Manager skep ons 'n blokstruktuur in S7-PlcSim soortgelyk aan die een wat ons van die stortingsterrein ontvang het. Die blokgroottes moet ooreenstem (dit word bereik deur die blokke met die vereiste aantal instruksies te vul) en hul identifiseerders (OB1, FC1, FC2, FC3).
  2. Stoor die PLC in 'n lêer.
  3. Ons vervang die inhoud van die blokke in die resulterende lêer met die blokke van die verkeershoop. Die begin van die blokke word deur die handtekening bepaal.
  4. Ons laai die resulterende lêer in S7-PlcSim en kyk na die inhoud van die blokke in Simatic Manager.

Blokke kan byvoorbeeld vervang word met die volgende kode:

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)

Alexey het 'n miskien moeiliker, maar steeds korrekte pad geneem. Ons het aanvaar dat deelnemers die NetToPlcSim-program sou gebruik sodat PlcSim oor die netwerk kon kommunikeer, blokke via Snap7 na PlcSim kon oplaai, en dan hierdie blokke as 'n projek vanaf PlcSim kon aflaai deur die ontwikkelingsomgewing te gebruik.

Deur die resulterende lêer in S7-PlcSim oop te maak, kan jy die oorgeskrewe blokke lees met behulp van die Simatic Manager. Die belangrikste toestelbeheerfunksies word in blok FC1 aangeteken. Van besondere belang is die #TEMP0-veranderlike, wat, wanneer dit aangeskakel word, blykbaar die PLC-beheer op handmatige modus stel gebaseer op die M2.2- en M2.3-bisgeheuewaardes. Die #TEMP0-waarde word deur funksie FC3 gestel.

In die voetspore van Industrial Ninja: hoe 'n PLC by Positive Hack Days 9 gekap is

Om die probleem op te los, moet jy die FC3-funksie ontleed en verstaan ​​wat gedoen moet word sodat dit 'n logiese een gee.

Die PLC seinverwerkingsblokke by die Lae Sekuriteit-stalletjie by die kompetisieterrein is op 'n soortgelyke manier gerangskik, maar om die waarde van die #TEMP0-veranderlike te stel, was dit genoeg om die lyn my ninja-pad in die DB1-blok te skryf. Om die waarde in 'n blok na te gaan was eenvoudig en het nie diepgaande kennis van die blokprogrammeertaal vereis nie. Dit is duidelik dat dit op die hoë sekuriteitsvlak baie moeiliker sal wees om handbeheer te bewerkstellig en dit is nodig om die ingewikkeldhede van die STL-taal te verstaan ​​(een van die maniere om die S7 PLC te programmeer).

Omgekeerde blok FC3

Inhoud van die FC3-blok in STL-voorstelling:

      L     B#16#0
      T     #TEMP13
      T     #TEMP15
      L     P#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
      L     W#16#0
      <>I   
      JC    M00d
      L     P#DBX 0.0
      LAR1  
M00d: L     B [AR1,P#0.0]
      T     #TEMP5
      L     W#16#1
      ==I   
      JC    M007
      L     #TEMP5
      L     W#16#2
      ==I   
      JC    M008
      L     #TEMP5
      L     W#16#3
      ==I   
      JC    M00f
      L     #TEMP5
      L     W#16#4
      ==I   
      JC    M00e
      L     #TEMP5
      L     W#16#5
      ==I   
      JC    M011
      L     #TEMP5
      L     W#16#6
      ==I   
      JC    M012
      JU    M010
M007: +AR1  P#1.0
      L     P#DBX 0.0
      LAR2  
      L     B [AR1,P#0.0]
      L     C#8
      *I    
      +AR2  
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      JL    M003
      JU    M001
      JU    M002
      JU    M004
M003: JU    M005
M001: OPN   DB   101
      L     B [AR2,P#0.0]
      T     #TEMP0
      JU    M006
M002: OPN   DB   101
      L     B [AR2,P#0.0]
      T     #TEMP1
      JU    M006
M004: OPN   DB   101
      L     B [AR2,P#0.0]
      T     #TEMP2
      JU    M006
M00f: +AR1  P#1.0
      L     B [AR1,P#0.0]
      L     C#8
      *I    
      T     #TEMP11
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP7
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9
      TAR1  #TEMP4
      OPN   DB   101
      L     P#DBX 0.0
      LAR1  
      L     #TEMP11
      +AR1  
      LAR2  #TEMP9
      L     B [AR2,P#0.0]
      T     B [AR1,P#0.0]
      L     #TEMP4
      LAR1  
      JU    M006
M008: +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP3
      +AR1  P#1.0
      L     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10
      TAR1  #TEMP4
      LAR1  #TEMP9
      LAR2  #TEMP10
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      AW    
      INVI  
      T     #TEMP12
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      OW    
      L     #TEMP12
      AW    
      T     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10
      TAR1  #TEMP4
      LAR1  #TEMP9
      LAR2  #TEMP10
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      -I    
      T     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10
      TAR1  #TEMP4
      LAR1  #TEMP9
      LAR2  #TEMP10
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      ==I   
      JCN   M013
      JU    M014
M013: L     P#DBX 0.0
      LAR1  
      T     #TEMP4
      L     B#16#0
      T     #TEMP6
      JU    M006
M014: L     #TEMP4
      LAR1  
      L     #TEMP13
      L     L#1
      +I    
      T     #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: L     P#DBX 0.0
      LAR1  
      L     0
      T     #TEMP6
      TAR1  #TEMP4
M005: TAR1  #TEMP4
      CLR   
      =     #TEMP16
      L     #TEMP13
      L     L#20
      ==I   
      S     #TEMP16
      L     #TEMP15
      ==I   
      A     #TEMP16
      JC    M017
      L     #TEMP13
      L     L#20
      <I    
      S     #TEMP16
      L     #TEMP15
      ==I   
      A     #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

Die kode is redelik lank en mag dalk ingewikkeld lyk vir iemand wat nie met STL vertroud is nie. Daar is geen sin om elke instruksie binne die raamwerk van hierdie artikel te ontleed nie; gedetailleerde instruksies en vermoëns van die STL-taal kan gevind word in die ooreenstemmende handleiding: Verklaringslys (STL) vir S7-300- en S7-400-programmering. Hier sal ek dieselfde kode aanbied na verwerking - hernoem die byskrifte en veranderlikes en voeg kommentaar by wat die operasie-algoritme en sommige STL-taalkonstrukte beskryf. Laat ek dadelik daarop let dat die betrokke blok 'n virtuele masjien bevat wat 'n greepkode uitvoer wat in die DB100-blok geleë is, waarvan die inhoud ons ken. Virtuele masjien-instruksies bestaan ​​uit 1 greep bedryfskode en grepe argumente, een greep vir elke argument. Alle oorweegde instruksies het twee argumente; Ek het hul waardes in die opmerkings as X en Y aangewys.

Kode na verwerking]

# Инициализация различных переменных
      L     B#16#0
      T     #CHECK_N        # Счетчик успешно пройденных проверок
      T     #COUNTER_N      # Счетчик общего количества проверок
      L     P#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
      L     W#16#0
      <>I   
      JC    M00d
      L     P#DBX 0.0
      LAR1  

# Конструкция switch - case для обработки различных опкодов
M00d: L     B [AR1,P#0.0]
      T     #OPCODE
      L     W#16#1
      ==I   
      JC    OPCODE_1
      L     #OPCODE
      L     W#16#2
      ==I   
      JC    OPCODE_2
      L     #OPCODE
      L     W#16#3
      ==I   
      JC    OPCODE_3
      L     #OPCODE
      L     W#16#4
      ==I   
      JC    OPCODE_4
      L     #OPCODE
      L     W#16#5
      ==I   
      JC    OPCODE_5
      L     #OPCODE
      L     W#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
      L     P#DBX 0.0
      LAR2  
      L     B [AR1,P#0.0]   # Загрузка аргумента X (индекс в DB101)
      L     C#8
      *I    
      +AR2  
      +AR1  P#1.0
      L     B [AR1,P#0.0]   # Загрузка аргумента Y (индекс регистра)
      JL    M003            # Аналог switch - case на основе значения Y
      JU    M001            # для выбора необходимого регистра для записи.
      JU    M002            # Подобные конструкции используются и в других
      JU    M004            # операциях ниже для аналогичных целей
M003: JU    LOOPEND
M001: OPN   DB   101
      L     B [AR2,P#0.0]
      T     #REG0           # Запись значения DB101[X] в REG[0]
      JU    PRE_LOOPEND
M002: OPN   DB   101
      L     B [AR2,P#0.0]
      T     #REG1           # Запись значения DB101[X] в REG[1]
      JU    PRE_LOOPEND
M004: OPN   DB   101
      L     B [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
      L     B [AR1,P#0.0]
      T     #TEMP3
      +AR1  P#1.0
      L     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7          # первый аргумент - X
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9          # REG[X]
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10         # REG[Y]
      TAR1  #POINTER
      LAR1  #TEMP9          # REG[X]
      LAR2  #TEMP10         # REG[Y]
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      AW    
      INVI  
      T     #TEMP12         # ~(REG[Y] & REG[X])
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      OW    
      L     #TEMP12
      AW                    # (~(REG[Y] & REG[X])) & (REG[Y] | REG[X]) - аналог проверки на равенство
      T     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9          # REG[X]
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10         # REG[Y]
      TAR1  #POINTER
      LAR1  #TEMP9
      LAR2  #TEMP10
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      -I                    # ACCU1 = ACCU2 - ACCU1, REG[X] - REG[Y]
      T     B [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
      L     B [AR1,P#0.0]
      T     #TEMP7          #  REG[X]     
      L     P#M 100.0
      LAR2  
      L     #TEMP7
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP9          #  REG[X]  
      +AR1  P#1.0
      L     B [AR1,P#0.0]
      T     #TEMP8
      L     P#M 100.0
      LAR2  
      L     #TEMP8
      L     C#8
      *I    
      +AR2  
      TAR2  #TEMP10         # REG[Y]
      TAR1  #POINTER
      LAR1  #TEMP9          # REG[Y]
      LAR2  #TEMP10         # REG[X]
      L     B [AR1,P#0.0]
      L     B [AR2,P#0.0]
      ==I   
      JCN   M013
      JU    M014
M013: L     P#DBX 0.0
      LAR1  
      T     #POINTER
      L     B#16#0
      T     #TEMP6
      JU    PRE_LOOPEND
M014: L     #POINTER
      LAR1  
# Инкремент значения #CHECK_N
      L     #CHECK_N
      L     L#1
      +I    
      T     #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: L     P#DBX 0.0
      LAR1  
      L     0
      T     #TEMP6
      TAR1  #POINTER

LOOPEND: TAR1  #POINTER
      CLR   
      =     #TEMP16
      L     #CHECK_N
      L     L#20
      ==I   
      S     #TEMP16
      L     #COUNTER_N
      ==I   
      A     #TEMP16
# Все проверки пройдены, если #CHECK_N == #COUNTER_N == 20
      JC    GOOD
      L     #CHECK_N
      L     L#20
      <I    
      S     #TEMP16
      L     #COUNTER_N
      ==I   
      A     #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

Nadat ons 'n idee gekry het van die virtuele masjien-instruksies, laat ons 'n klein disassembler skryf om die greepkode in die DB100-blok te ontleed:

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

As gevolg hiervan kry ons die volgende virtuele masjienkode:

Virtuele masjien kode

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)

Soos u kan sien, kontroleer hierdie program eenvoudig elke karakter van DB101 vir gelykheid tot 'n sekere waarde. Die finale reël vir die slaag van alle tjeks is: n0w u 4r3 7h3 m4573r. As hierdie lyn in blok DB101 geplaas word, word handmatige PLC-beheer geaktiveer en dit sal moontlik wees om die ballon te ontplof of te laat leegloop.


Dis al! Alexey het 'n hoë vlak van kennis gedemonstreer wat 'n industriële ninja waardig is :) Ons het onvergeetlike pryse aan die wenner gestuur. Baie dankie aan alle deelnemers!

Bron: will.com

Voeg 'n opmerking