Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

Op 'e lêste PHDays 9 hawwe wy in kompetysje hâlden om in gaspompynstallaasje te hacken - kompetysje Yndustriële Ninja. D'r wiene trije tribunes op 'e side mei ferskate feiligensparameters (Gjin feiligens, lege feiligens, hege feiligens), dy't itselde yndustriële proses emulearje: lucht ûnder druk waard yn in ballon pompt (en dan frijlitten).

Nettsjinsteande de ferskillende feiligens parameters wie de hardware gearstalling fan de tribunes itselde: Siemens Simatic PLC S7-300 rige; emergency deflation knop en druk mjitapparaat (ferbûn mei PLC digitale yngongen (DI)); kleppen wurkje foar ynflaasje en deflation fan lucht (ferbûn mei de digitale útgongen fan de PLC (DO)) - sjoch de figuer hjirûnder.

Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

De PLC hat, ôfhinklik fan 'e druklêzingen en yn oerienstimming mei har programma, in beslút makke om de bal te leegjen of op te blazen (iepene en slute de oerienkommende kleppen). Lykwols, alle tribunes hiene in hânmjittich kontrôle modus, dat makke it mooglik om te kontrolearjen de steat fan de kleppen sûnder beheinings.

De tribunes ferskille yn 'e kompleksiteit fan it ynskeakeljen fan dizze modus: op 'e ûnbeskerme stand wie it it maklikst om dit te dwaan, en op 'e High Security-stand wie it navenant dreger.

Fiif fan de seis problemen binne yn twa dagen oplost; De dielnimmer op it earste plak fertsjinne 233 punten (hy brocht in wike tariede op de kompetysje). Trije winners: I plak - a1exdandy, II - Rubikoid, III - Ze.

Lykwols, tidens PHDays koe gjinien fan 'e dielnimmers alle trije tribunes oerwinne, dus besleaten wy in online kompetysje te meitsjen en begjin juny de dreechste taak publisearre. Dielnimmers moasten de taak binnen in moanne ôfmeitsje, de flagge fine en de oplossing yn detail en op in nijsgjirrige wize beskriuwe.

Under de besuniging publisearje wy in analyze fan 'e bêste oplossing foar de taak fan dyjingen dy't oer de moanne ferstjoerd binne, it waard fûn troch Alexey Kovrizhnykh (a1exdandy) fan it bedriuw Digital Security, dy't it XNUMXe plak yn 'e konkurrinsje naam tidens PHDays. Hjirûnder presintearje wy syn tekst mei ús opmerkingen.

Inisjele analyze

Dat, de taak befette in argyf mei de folgjende bestannen:

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

It hints.txt-bestân befettet de nedige ynformaasje en hints om de taak op te lossen. Hjir binne de ynhâld:

  1. Petrovich fertelde my juster dat jo blokken fan PlcSim kinne laden yn Step7.
  2. De Siemens Simatic S7-300 rige PLC waard brûkt by de stand.
  3. PlcSim is in PLC-emulator wêrmei jo programma's kinne útfiere en debuggen foar Siemens S7 PLC's.

It DB100.bin-bestân liket it DB100 PLC-gegevensblok te befetsjen: 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 0206a0100 0104 u............ 0102 00000040............7502. 0401: 0206 0100 0105 0102 0 02 0501 00000050 0202 ...............1602 0501 0206 0100 0106 0102 3402 4 00000060 L ......... 0401. .. 0206 : 0100 0107 0102 2602 0501a0202 00000070 4 02 ................ 0501: 0206 0100 0108 0102a 3302 0401 3 00000080 .......... 0206a0100: 0109 0102b 0 02 0501 0202 1602 00000090 ......".....F... 0501b0206: 0100 010 0102c 3702 0401 0206 7 ... … .... 000000e0: 0100 010 0102 2202 0501 0202 4602 0501 ........#...... 000000f0: 0206 0100 010 0102 3302 0401 0206.....0100 3 000000 0..... ..... 010: 0102 0 02 0501 0202 1602 0501 0206 ......%......... 000000: 0 0100 010 0102 6 02 0401 ..... .....&. 0206: 0100 010 000000c0 0102 1102 0501 ....L......

Lykas de namme al fermoeden docht, befettet de block_upload_traffic.pcapng-bestân in dump fan blok-uploadferkear nei de PLC.

It is de muoite wurdich op te merken dat dizze ferkearsdump op 'e konkurrinsjeside tidens de konferinsje wat dreger wie te krijen. Om dit te dwaan, wie it nedich om it skript te begripen fan it projektbestân foar TeslaSCADA2. Dêrút wie it mooglik om te begripen wêr't de dump fersifere mei RC4 siet en hokker kaai moast wurde brûkt om it te ûntsiferjen. Dumps fan gegevensblokken op side koenen wurde krigen mei de S7-protokolclient. Hjirfoar brûkte ik de demo-kliïnt fan it Snap7-pakket.

It útheljen fan sinjaalferwurkingsblokken fan in ferkearsdump

Sjoch nei de ynhâld fan 'e dump, kinne jo begripe dat it sinjaalferwurkingsblokken OB1, FC1, FC2 en FC3 befettet:

Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

Dizze blokken moatte fuortsmiten wurde. Dit kin bygelyks dien wurde mei it folgjende skript, nei't it ferkear earder konvertearre is fan it pcapng-formaat nei pcap:

#!/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 = ''

Nei't jo de resultearjende blokken hawwe ûndersocht, sille jo merke dat se altyd begjinne mei bytes 70 70 (pp). No moatte jo leare hoe't jo se analysearje. De opdracht hint suggerearret dat jo hjirfoar PlcSim moatte brûke.

It krijen fan minsklik lêsbere ynstruksjes út blokken

Litte wy earst besykje S7-PlcSim te programmearjen troch ferskate blokken te laden mei werheljende ynstruksjes (= Q 0.0) yn it mei Simatic Manager-software, en bewarje de PLC dy't yn 'e emulator krigen is yn' e example.plc-bestân. Troch nei de ynhâld fan it bestân te sjen, kinne jo it begjin fan 'e downloade blokken maklik bepale troch de hantekening 70 70, dy't wy earder ûntdutsen. Foardat de blokken, blykber, de blokgrutte wurdt skreaun as in 4-byte little-endian wearde.

Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

Nei't wy ynformaasje krigen hawwe oer de struktuer fan plc-bestannen, ferskynde it folgjende aksjeplan foar it lêzen fan PLC S7-programma's:

  1. Mei help fan Simatic Manager meitsje wy in blokstruktuer yn S7-PlcSim fergelykber mei dejinge dy't wy krigen fan 'e dump. De blokgrutte moat oerienkomme (dit wurdt berikt troch it foljen fan de blokken mei it fereaske oantal ynstruksjes) en har identifiers (OB1, FC1, FC2, FC3).
  2. Bewarje de PLC nei in bestân.
  3. Wy ferfange de ynhâld fan 'e blokken yn' e resultearjende triem mei de blokken fan 'e ferkearsdump. It begjin fan 'e blokken wurdt bepaald troch de hantekening.
  4. Wy lade de resultearjende triem yn S7-PlcSim en besjoch de ynhâld fan 'e blokken yn Simatic Manager.

Blokken kinne wurde ferfongen, bygelyks, mei de folgjende koade:

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 naam in miskien dreger, mar dochs korrekt paad. Wy geane derfan út dat dielnimmers it programma NetToPlcSim soene brûke, sadat PlcSim koe kommunisearje oer it netwurk, blokken uploade nei PlcSim fia Snap7, en dan dizze blokken downloade as in projekt fan PlcSim mei de ûntwikkelingsomjouwing.

Troch it resultearjende bestân yn S7-PlcSim te iepenjen, kinne jo de oerskreaune blokken lêze mei de Simatic Manager. De haadfunksjes foar apparaatkontrôle wurde opnommen yn blok FC1. Fan bysûndere notysje is de fariabele #TEMP0, dy't as ynskeakele liket de PLC-kontrôle yn te stellen yn 'e manuele modus basearre op de M2.2- en M2.3-bit-ûnthâldwearden. De wearde #TEMP0 wurdt ynsteld troch funksje FC3.

Yn 'e fuotstappen fan Industrial Ninja: hoe't in PLC waard hacked op Positive Hack Days 9

Om it probleem op te lossen, moatte jo de FC3-funksje analysearje en begripe wat der dien wurde moat, sadat it in logyske werombringt.

De PLC-sinjaalferwurkingsblokken op 'e Low Security-stand op' e konkurrinsjeside waarden op in fergelykbere manier regele, mar om de wearde fan 'e #TEMP0-fariabele yn te stellen, wie it genôch om de line myn ninja-wei yn it DB1-blok te skriuwen. It kontrolearjen fan de wearde yn in blok wie ienfâldich en frege gjin djippe kennis fan 'e blokprogrammearringstaal. Fansels sil op it nivo fan hege feiligens it realisearjen fan manuele kontrôle folle dreger wêze en it is nedich om de yngewikkeldheden fan 'e STL-taal te begripen (ien fan 'e manieren om de S7 PLC te programmearjen).

Omkearblok FC3

Ynhâld fan it FC3-blok yn STL-fertsjintwurdiging:

      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

De koade is frij lang en kin lykje yngewikkeld foar immen net bekend mei STL. It hat gjin punt om elke ynstruksje te analysearjen yn it ramt fan dit artikel; detaillearre ynstruksjes en mooglikheden fan 'e STL-taal kinne fûn wurde yn' e oerienkommende hantlieding: Statement List (STL) foar S7-300 en S7-400 Programming. Hjir sil ik presintearje deselde koade nei ferwurking - omneame de labels en fariabelen en it tafoegjen fan opmerkingen beskriuwe de operaasje algoritme en guon STL taal konstruksjes. Lit my daliks opmerke dat it blok yn kwestje in firtuele masine befettet dy't wat bytekoade útfiert yn it DB100-blok, wêrfan de ynhâld wy witte. Ynstruksjes foar firtuele masines besteane út 1 byte fan bestjoeringskoade en bytes fan arguminten, ien byte foar elk argumint. Alle beskôge ynstruksjes hawwe twa arguminten; Ik haw har wearden yn 'e opmerkingen oanwiisd as X en Y.

Koade nei ferwurking]

# Инициализация различных переменных
      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

Nei't wy in idee hawwe fan 'e ynstruksjes foar firtuele masines, litte wy in lytse disassembler skriuwe om de bytekoade yn it DB100-blok te parsearjen:

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 gefolch krije wy de folgjende firtuele masinekoade:

Firtuele masine koade

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)

Sa't jo sjen kinne, kontrolearret dit programma gewoan elk karakter fan DB101 foar gelikensens oan in bepaalde wearde. De lêste rigel foar it trochjaan fan alle kontrôles is: n0w u 4r3 7h3 m4573r. As dizze line wurdt pleatst yn blok DB101, dan wurdt hânmjittich PLC kontrôle aktivearre en it sil mooglik te eksplodearjen of deflate de ballon.


Da's alles! Alexey demonstrearre in heech nivo fan kennis weardich fan in yndustriële ninja :) Wy stjoerde memorabele prizen nei de winner. Tige tank oan alle dielnimmers!

Boarne: www.habr.com

Add a comment