Valminud PHDays 9-l viisime läbi gaasi pumpamise tehase häkkimise võistluse — konkurss . Kohapeal oli kolm stendi erinevate turvaparameetritega (No Security, Low Security, High Security), mis emuleerisid sama tööstusprotsessi: õhupall täideti (ja seejärel tühjendati) rõhu all olevast õhust.
Hoolimata erinevatest turvaparameetritest oli stendi riistvara sama: Siemens Simatic S7-300 PLC; hädaolukorra õhu väljalaskepõrkepäeval küllastuse nupp ja rõhu mõõteseade (ühendatud PLC digitaalsete sisenditega (DI)); ventiilid, mis töötavad õhu pumpamise ja vabastamisega (ühendatud PLC digitaalsete väljunditega (DO)) — vt allolevat joonist.

PLC tegi oma programmi alusel ja rõhuandurite näitude põhjal otsuse õhupalli täitmise või tühjendamise kohta (ava ja sulge vastavad ventiilid). Kuid kõigil stendidel oli mõeldud ka manuaalne juhtimise režiim, mis võimaldas ventiilide olekute juhtimist ilma igasuguste piiranguteta.
Stendid erinesid selle režiimi aktiveerimise keerukuse poolest: kaitsmata stendil oli selle teostamine kõige lihtsam, samas kui High Security stendil oli see vastavalt keerulisem.
Kaks päevaga lahendati viis kuuest ülesandest; esikoha saavutanud osaleja teenis 233 punkti (ta kulutas konkursiks ettevalmistamiseks nädala). Kolme parima hulka kuulusid: I koht — a1exdandy, II — Rubikoid, III — Ze.
Kuid PHDaysi ajal ei suutnud keegi osalejatest ületada kolme stendi, seetõttu otsustasime korraldada veebikonkursi ja juuni alguses avaldasime kõige keerulisema ülesande. Osalejad pidid kuu jooksul ülesande täitma, leidma lipu ning huvitavalt ja detailselt oma lahenduse kirjeldama.
Allpool avaldame parima lahenduse analüüsi kuu jooksul saadetud ülesannetest, mille leidis Aleksei Kovrizhnykh (a1exdandy) ettevõttest Digital Security, kes saavutas PHDaysi konkursil I koha. Allpool toome tema teksti koos meie kommentaaridega.
Algne analüüs
Nii, ülesandes oli arhiiv failidega:
- block_upload_traffic.pcapng
- DB100.bin
- hints.txt
Fail hints.txt sisaldab vajalikku teavet ja vihjeid ülesande lahendamiseks. Siin on selle sisu:
- Petrovich rääkis mulle eile, et PlcSimiga saab Step7-sse plokke laadida.
- Stendil kasutati Siemens Simatic S7-300 seeria PLC-d.
- PlcSim on PLC emulaator, mis võimaldab käitada ja siluda Siemens S7 PLC-de programme.
Fail DB100.bin sisaldab ilmselt PLK andmeploki DB100: 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.......
Tuginedes nimele, fail block_upload_traffic.pcapng sisaldab PLK blokkide üleslaadimise liikluse dumpi.
Oluline on märkida, et selle konkursi liiklustamp saamine konverentsi ajal oli veidi keeruline. Selleks tuli aru saada TeslaSCADA2 projekti faili skriptist. Sealt sai teada, kus asub RC4 abil krüpteeritud tamp ja millist võtit tuleb selle dekrüpteerimiseks kasutada. Andmeplokkide tampide saamiseks platsil oli võimalik kasutada S7 protokolli klienti. Mina kasutasin selleks Snap7 paketi demo klienti.
Signaalitöötluse plokkide eraldamine liiklustampist
Tampide sisu üle vaadates saab aru, et need edastavad signaalitöötluse plokke OB1, FC1, FC2 ja FC3:

Need plokid tuleb eraldada. Seda saab teha näiteks järgmise skripti abil, muutes liiklust esmalt pcapng vormingust pcap-ks:
#!/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 = ''Uurides saadud plokke, võib märgata, et need algavad alati baitidega 70 70 (pp). Nüüd tuleb õppida, kuidas neid analüüsida. Ülesande vihje viitab sellele, et selleks tuleb kasutada PlcSim'i.
Arusaadavate käskude saamine plokkidest
Alustuseks proovime S7-PlcSim'i programmeerida, laadides sisse mõned plokid korduvate käskudega (= Q 0.0) Simatic Manageri tarkvara abil ning salvestame emulaatoris PLC saadud faili nimega example.plc. Faili sisu vaadates on lihtne kindlaks teha laaditud plokkide algus, mida näete eelnevalt tuvastatud allkirja 70 70 põhjal. Plokkide ees on ilmselt kirjas ploki suurus 4-baidise little-endian väärtusena.

Pärast seda, kui oleme saanud ülevaate plc-failide struktuurist, tekkis järgmine plaan PLC S7 programmide lugemiseks:
- Simatic Manageri abil loome S7-PlcSim'is plokkide struktuuri, mis vastab sellele, mille saime dump'ist. Plokkide suurused peavad olema samad (seda saavutatakse plokkide täitmisega vajaliku hulga käskudega) ning nende identifikaatorid (OB1, FC1, FC2, FC3).
- Salvestame PLC faili.
- Asendame saadud faili plokkide sisu koodi dump'ist saadud plokkidega. Plokkide algust määrame allkirja põhjal.
- Saadud faili laadime uuesti S7-PlcSim'i ja vaatame plokkide sisu Simatic Manageris.
Plokkide asendamist saab näiteks teha järgmise koodiga:
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)Aleksei läks võib-olla keerulisemat, kuid siiski õiget teed. Eeldasime, et osalejad kasutavad programmid NetToPlcSim, et saaks suhelda PlcSimiga üle võrgu, laadivad plokid PlcSim-i läbi Snap7 ja seejärel laadivad need plokid välja projektina PlcSim-st arenduskeskkonna abil.
Avades saadud faili S7-PlcSim-is, saab Simatic Manageriga lugeda üle kirjutatud plokke. Seadmete juhtimise põhirombid on talletatud plokis FC1. Erilist tähelepanu väärib muutuja #TEMP0, mille aktiveerimisel, nagu näib, kantakse PLC käitamine käsitsi režiimi mälubitide M2.2 ja M2.3 väärtuste põhjal. Muutuja #TEMP0 väärtus määratakse funktsiooni FC3 abil.

Küsimuse lahendamiseks on vajalik analüüsida funktsiooni FC3 ja mõista, mida tuleb teha, et see tagastaks loogilise 1.
PLC signaalitöötlusplokid madala turvalisuse seistes olid korraldatud sarnasel viisil, kuid muutuja #TEMP0 väärtuse seadmiseks piisas DB1 plokis rea my ninja way kirjutamisest. Bloki väärtuse kontrollimine oli selgelt korraldatud ja ei nõudnud sügavaid teadmisi plokikeelest. On selge, et kõrge turvalisuse tasemel on käsitsi juhtimise saavutamine oluliselt keerulisem ja tuleb süveneda STL-keele (üks PLC S7 programmeerimise meetodeid) nüanssidesse.
FC3 ploki tagurpidi
FC3 ploki sisu STL esitusel:
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_VALKood on üsna mahukas ja inimesele, kes ei tunne STL-i, võib see tunduda keeruline. Iga juhendi arutamine käesoleva artikli raames pole mõistlik; STL-i keele juhendite ja võimalustega saab tutvuda vastavas manus: . Siin toon välja sama koodi pärast töötlemist — siltide ja muutujate ümbernimetamist ning kommentaaride lisamist, mis kirjeldavad algoritmi toimimist ja teatud STL-i keele konstruktsioone. Tõstan kohe esile, et käsitletavas plokis on rakendatud virtuaalne masin, mis täidab teatud bait-koodi, mis paikneb plokis DB100 ja mille sisu on meile teada. Virtuaalse masina käsud koosnevad 1 bait operatsioonikoodist ja argumentide baitidest, üks bait iga argumendi kohta. Kõik käsitletud käsud omavad kahte argumenti, mille väärtused olen kommentaarides tähistanud kui X ja Y.
Kood pärast töötlemist]
# Инициализация различных переменных
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_VALSaades ülevaate virtuaalse masina käskudest, kirjutame väikese disassembleri bait-koodi analüüsimiseks plokis 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))
breakTulemusena saame järgmise virtuaalse masina koodi:
Virtuaalse masina kood
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)Nagu näha, kontrollib see programm iga DB101 sümbolit selle tietiiväärtusesse võrdlemiseks. Kõikide kontrollide läbimiseks vajalik lõppstring on: n0w u 4r3 7h3 m4573r. Kui see string sisestada DB101 plokki, aktiveeritakse PLC käsijuhtimine ja saab õhupalli plahvatada või tühjendada.
Ja kõik! Aleksei näitas kõrgetasemelist teadlikkust, väärilist tööstuslikku nindža 🙂 Võitjale saatsime mälestuskingitused. Suur tänu kõigile osalejatele!
Allikas: habr.com
