Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

Yn ystod PHDays diwethaf 9 cynhaliwyd cystadleuaeth hacio gwaith pwmpio nwy - cystadleuaeth Ninja diwydiannol. Roedd tri stondin ar y safle gyda pharamedrau diogelwch gwahanol (Dim Diogelwch, Diogelwch Isel, Diogelwch Uchel), yn efelychu'r un broses ddiwydiannol: cafodd aer dan bwysau ei bwmpio i mewn i falŵn (ac yna ei ryddhau).

Er gwaethaf y paramedrau diogelwch gwahanol, roedd cyfansoddiad caledwedd y stondinau yr un fath: cyfres Siemens Simatic PLC S7-300; botwm datchwyddiant brys a dyfais mesur pwysau (yn gysylltiedig â mewnbynnau digidol PLC (DI)); falfiau sy'n gweithredu ar gyfer chwyddiant a datchwyddiant aer (yn gysylltiedig ag allbynnau digidol y PLC (DO)) - gweler y ffigur isod.

Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

Yn dibynnu ar y darlleniadau pwysau ac yn unol â'i raglen, penderfynodd y PLC i ddatchwyddo neu chwyddo'r bêl (agorodd a chaeodd y falfiau cyfatebol). Fodd bynnag, roedd gan bob stondin ddull rheoli â llaw, a oedd yn ei gwneud hi'n bosibl rheoli cyflwr y falfiau heb unrhyw gyfyngiadau.

Roedd y standiau'n amrywio o ran cymhlethdod galluogi'r modd hwn: ar y stand diamddiffyn roedd yn haws gwneud hyn, ac ar y stand Diogelwch Uchel roedd yn anoddach i'r un graddau.

Cafodd pump o'r chwe phroblem eu datrys mewn dau ddiwrnod; Enillodd y cyfranogwr lle cyntaf 233 o bwyntiau (treuliodd wythnos yn paratoi ar gyfer y gystadleuaeth). Tri buddugol : I place — a1exdandy, II — Rubikoid, III — Ze.

Fodd bynnag, yn ystod Dyddiau PHD, nid oedd yr un o'r cyfranogwyr yn gallu goresgyn pob un o'r tri stondin, felly fe wnaethom benderfynu gwneud cystadleuaeth ar-lein a chyhoeddi'r dasg anoddaf yn gynnar ym mis Mehefin. Roedd yn rhaid i gyfranogwyr gwblhau'r dasg o fewn mis, dod o hyd i'r faner, a disgrifio'r ateb yn fanwl ac mewn ffordd ddiddorol.

O dan y toriad rydym yn cyhoeddi dadansoddiad o'r ateb gorau i'r dasg gan y rhai a anfonwyd dros y mis, fe'i canfuwyd gan Alexey Kovrizhnykh (a1exdandy) o'r cwmni Diogelwch Digidol, a gymerodd le XNUMXaf yn y gystadleuaeth yn ystod PHDays. Isod rydym yn cyflwyno ei destun gyda'n sylwadau.

Dadansoddiad cychwynnol

Felly, roedd y dasg yn cynnwys archif gyda'r ffeiliau canlynol:

  • block_upload_traffic.pcapng
  • DB100.bin
  • awgrymiadau.txt

Mae'r ffeil hints.txt yn cynnwys y wybodaeth a'r awgrymiadau angenrheidiol i ddatrys y dasg. Dyma ei gynnwys:

  1. Dywedodd Petrovich wrthyf ddoe y gallwch chi lwytho blociau o PlcSim i Step7.
  2. Defnyddiwyd y gyfres Siemens Simatic S7-300 PLC yn y stondin.
  3. Mae PlcSim yn efelychydd PLC sy'n eich galluogi i redeg a dadfygio rhaglenni ar gyfer Siemens S7 PLCs.

Mae'n ymddangos bod y ffeil DB100.bin yn cynnwys y bloc data DB100 PLC: 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 0 02 00000030 0501a0202 ..w........... 1602: 0501 0206 0100 0104 0102 00000040 7502 ................ 0401: 0206 0100 0105 0102 0 02 0501a00000050 0202 u............... 1602: 0501 0206 0100 0106 0102 3402 4 00000060............0401. 0206: 0100 0107 0102 2602 0501 0202 00000070 4 .........&..... 02: 0501c0206 0100 0108 0102 3302 0401 3 . . : 00000080 0206 0100 0109 0102a0 02 0501 0202 ................ 1602: 00000090 0501 0206 0100a 010 0102 3702 0401 ............ 0206a7: 000000 0b 0100 010 0102 2202 0501 0202 ......".....F... 4602b0501: 000000 0 0206c 0100 010..."... .. 0102c3302: 0401d 0206 0100a3 000000 0 010 0102 0 ................ 02d0501: 0202 1602e 0501 0206d000000 0 0100 .... .f .... 010e0102: 6 02 0401 0206 0100 010 000000 0 ........#...... 0102f1102: 0501 0202 2302 0501 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 0113c0102 2602 3 00000120 ....L......

Fel y mae'r enw'n ei awgrymu, mae'r ffeil block_upload_traffic.pcapng yn cynnwys dymp o draffig uwchlwytho bloc i'r PLC.

Mae'n werth nodi bod y domen draffig hon ar safle'r gystadleuaeth yn ystod y gynhadledd ychydig yn anoddach ei chael. I wneud hyn, roedd angen deall y sgript o'r ffeil prosiect ar gyfer TeslaSCADA2. Oddi yno roedd yn bosibl deall ble roedd y domen wedi'i hamgryptio gan ddefnyddio RC4 wedi'i leoli a pha allwedd oedd angen ei ddefnyddio i'w ddadgryptio. Gellid cael dympiau o flociau data ar y safle gan ddefnyddio cleient protocol S7. Ar gyfer hyn defnyddiais y cleient demo o'r pecyn Snap7.

Echdynnu blociau prosesu signal o dymp traffig

Wrth edrych ar gynnwys y domen, gallwch ddeall ei fod yn cynnwys blociau prosesu signal OB1, FC1, FC2 a FC3:

Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

Rhaid cael gwared ar y blociau hyn. Gellir gwneud hyn, er enghraifft, gyda'r sgript ganlynol, ar ôl trosi'r traffig o'r fformat pcapng i pcap o'r blaen:

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

Ar ôl archwilio'r blociau canlyniadol, byddwch yn sylwi eu bod bob amser yn dechrau gyda beit 70 70 (pp). Nawr mae angen i chi ddysgu sut i'w dadansoddi. Mae awgrym yr aseiniad yn awgrymu bod angen i chi ddefnyddio PlcSim ar gyfer hyn.

Cael cyfarwyddiadau y gall pobl eu darllen o flociau

Yn gyntaf, gadewch i ni geisio rhaglennu S7-PlcSim trwy lwytho sawl bloc gyda chyfarwyddiadau ailadrodd (= Q 0.0) i mewn iddo gan ddefnyddio meddalwedd Simatic Manager, ac arbed y PLC a gafwyd yn yr efelychydd i'r ffeil example.plc. Trwy edrych ar gynnwys y ffeil, gallwch yn hawdd benderfynu ar ddechrau'r blociau wedi'u llwytho i lawr gan y llofnod 70 70, a ddarganfuwyd gennym yn gynharach. Cyn y blociau, mae'n debyg, mae maint y bloc wedi'i ysgrifennu fel gwerth endian bach 4-beit.

Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

Ar ôl i ni dderbyn gwybodaeth am strwythur ffeiliau ccc, ymddangosodd y cynllun gweithredu canlynol ar gyfer darllen rhaglenni PLC S7:

  1. Gan ddefnyddio Simatic Manager, rydym yn creu strwythur bloc yn S7-PlcSim tebyg i'r un a gawsom o'r domen. Mae'n rhaid i'r meintiau blociau gydweddu (cyflawnir hyn trwy lenwi'r blociau â'r nifer gofynnol o gyfarwyddiadau) a'u dynodwyr (OB1, FC1, FC2, FC3).
  2. Arbedwch y PLC i ffeil.
  3. Rydym yn disodli cynnwys y blociau yn y ffeil canlyniadol gyda'r blociau o'r domen traffig. Pennir dechrau'r blociau gan y llofnod.
  4. Rydym yn llwytho'r ffeil canlyniadol i S7-PlcSim ac yn edrych ar gynnwys y blociau yn Simatic Manager.

Gellir disodli blociau, er enghraifft, â'r cod canlynol:

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)

Cymerodd Alexey llwybr anoddach efallai, ond eto'n gywir. Tybiwyd y byddai cyfranogwyr yn defnyddio rhaglen NetToPlcSim fel y gallai PlcSim gyfathrebu dros y rhwydwaith, uwchlwytho blociau i PlcSim trwy Snap7, ac yna lawrlwytho'r blociau hyn fel prosiect o PlcSim gan ddefnyddio'r amgylchedd datblygu.

Trwy agor y ffeil canlyniadol yn S7-PlcSim, gallwch ddarllen y blociau trosysgrifedig gan ddefnyddio'r Rheolwr Simatic. Mae'r prif swyddogaethau rheoli dyfais yn cael eu cofnodi yn bloc FC1. O bwys arbennig yw'r newidyn #TEMP0, sydd, o'i droi ymlaen, yn ymddangos ei fod yn gosod y rheolaeth PLC i fodd â llaw yn seiliedig ar werthoedd cof did M2.2 a M2.3. Mae'r gwerth #TEMP0 yn cael ei osod gan swyddogaeth FC3.

Yn ôl troed Ninja Diwydiannol: sut y cafodd PLC ei hacio yn Positive Hack Days 9

I ddatrys y broblem, mae angen i chi ddadansoddi swyddogaeth FC3 a deall beth sydd angen ei wneud fel ei fod yn dychwelyd un rhesymegol.

Trefnwyd y blociau prosesu signal PLC yn y stondin Diogelwch Isel ar safle'r gystadleuaeth mewn ffordd debyg, ond i osod gwerth y newidyn #TEMP0, roedd yn ddigon i ysgrifennu'r llinell fy ffordd ninja i mewn i'r bloc DB1. Roedd gwirio'r gwerth mewn bloc yn syml ac nid oedd angen gwybodaeth ddofn o'r iaith raglennu bloc. Yn amlwg, ar lefel Diogelwch Uchel, bydd cyflawni rheolaeth â llaw yn llawer anoddach ac mae angen deall cymhlethdodau'r iaith STL (un o'r ffyrdd o raglennu'r S7 PLC).

Bloc gwrthdroi FC3

Cynnwys y bloc FC3 mewn cynrychiolaeth STL:

      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

Mae'r cod yn eithaf hir a gall ymddangos yn gymhleth i rywun sy'n anghyfarwydd â STL. Nid oes unrhyw ddiben dadansoddi pob cyfarwyddyd o fewn fframwaith yr erthygl hon; gellir dod o hyd i gyfarwyddiadau manwl a galluoedd yr iaith STL yn y llawlyfr cyfatebol: Rhestr Datganiad (STL) ar gyfer Rhaglennu S7-300 a S7-400. Yma byddaf yn cyflwyno'r un cod ar ôl prosesu - ailenwi'r labeli a'r newidynnau ac ychwanegu sylwadau yn disgrifio'r algorithm gweithredu a rhai lluniadau iaith STL. Gadewch imi nodi ar unwaith bod y bloc dan sylw yn cynnwys peiriant rhithwir sy'n gweithredu rhywfaint o god beit sydd wedi'i leoli yn y bloc DB100, y gwyddom ei gynnwys. Mae cyfarwyddiadau peiriant rhithwir yn cynnwys 1 beit o god gweithredu a beit o ddadleuon, un beit ar gyfer pob dadl. Mae gan bob cyfarwyddyd ystyriol ddwy ddadl; dynodais eu gwerthoedd yn y sylwadau fel X ac Y.

Cod ar ôl prosesu]

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

Ar ôl cael syniad o'r cyfarwyddiadau peiriant rhithwir, gadewch i ni ysgrifennu dadosodwr bach i ddosrannu'r cod beit yn y bloc DB100:

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

O ganlyniad, rydym yn cael y cod peiriant rhithwir canlynol:

Cod peiriant rhithwir

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)

Fel y gallwch weld, mae'r rhaglen hon yn gwirio pob cymeriad o DB101 am gydraddoldeb i werth penodol. Y llinell olaf ar gyfer pasio pob siec yw: n0w u 4r3 7h3 m4573r. Os gosodir y llinell hon ym mloc DB101, yna gweithredir rheolaeth PLC â llaw a bydd yn bosibl ffrwydro neu ddatchwyddo'r balŵn.


Dyna i gyd! Dangosodd Alexey lefel uchel o wybodaeth sy'n deilwng o ninja diwydiannol :) Anfonwyd gwobrau cofiadwy i'r enillydd. Diolch yn fawr i'r holl gyfranogwyr!

Ffynhonnell: hab.com

Ychwanegu sylw