Son PHDays 9'da bir gaz pompalama tesisini hacklemek için bir yarışma düzenledik - yarışma
Farklı güvenlik parametrelerine rağmen standların donanım yapısı aynıydı: Siemens Simatic PLC S7-300 serisi; acil durum indirme butonu ve basınç ölçüm cihazı (PLC dijital girişlerine (DI) bağlı); havanın şişirilmesi ve söndürülmesi için çalışan valfler (PLC'nin (DO) dijital çıkışlarına bağlıdır) - aşağıdaki şekle bakın.
PLC, basınç okumalarına bağlı olarak ve programına uygun olarak topun havasını indirme veya şişirme kararı aldı (ilgili vanaları açıp kapattı). Ancak tüm standlarda, vanaların durumlarının herhangi bir kısıtlama olmaksızın kontrol edilmesini mümkün kılan manuel kontrol modu vardı.
Standlar, bu modu etkinleştirmenin karmaşıklığı açısından farklılık gösteriyordu: korumasız standta bunu yapmak en kolayıydı ve Yüksek Güvenlikli standta buna bağlı olarak daha zordu.
Altı problemden beşi iki günde çözüldü; Birinci olan katılımcı 233 puan kazandı (yarışmaya hazırlanmak için bir hafta harcadı). Üç kazanan: Ben - a1exdandy, II - Rubikoid, III - Ze.
Ancak PhDays sırasında hiçbir katılımcı üç aşamanın da üstesinden gelemedi, bu yüzden çevrimiçi bir yarışma yapmaya karar verdik ve en zor görevi Haziran başında yayınladık. Katılımcıların görevi bir ay içinde tamamlamaları, bayrağı bulmaları ve çözümü detaylı ve ilgi çekici bir şekilde anlatmaları gerekiyordu.
Kesimin altında, ay boyunca gönderilenlerden göreve en iyi çözümün bir analizini yayınlıyoruz; bu, PHDays sırasında yarışmada 1. olan Dijital Güvenlik şirketinden Alexey Kovrizhnykh (aXNUMXexdandy) tarafından bulundu. Aşağıda metnini yorumlarımızla birlikte sunuyoruz.
İlk analiz
Yani görev aşağıdaki dosyaları içeren bir arşiv içeriyordu:
- Block_upload_traffic.pcapng
- DB100.bin
- ipuçları.txt
Hints.txt dosyası, görevi çözmek için gerekli bilgileri ve ipuçlarını içerir. İşte içeriği:
- Petrovich dün bana PlcSim'den Step7'ye blok yükleyebileceğinizi söyledi.
- Standda Siemens Simatic S7-300 serisi PLC kullanıldı.
- PlcSim, Siemens S7 PLC'ler için programları çalıştırmanıza ve hata ayıklamanıza olanak tanıyan bir PLC emülatörüdür.
DB100.bin dosyası, DB100 PLC veri bloğunu içeriyor gibi görünüyor: 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 00000030a0501 ..w............. 0202: 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 L.......0206. .. 0100 0109 : 0102 0 02 0501 0202a1602 00000090 0501 0206 ................ 0100: 010 0102 3702 0401a 0206 7 000000 0 .........0100. .... 010a0102: 2202 0501b 0202 4602 0501 000000 0 0206 ......"..F... 0100b010: 0102 3302 0401c 0206 0100 3 000000 0 ........010. .... .. 0102c0: 02d 0501 0202a1602 0501 0206 000000 0 0100 ................ 010d0102: 6 02e 0401 0206d0100 010 000000 0 0102f ......m. .... .... 1102e0501: 0202 2302 0501 0206 0100 000000 0 0110 ........#...... 0102f3502: 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. ..... .....&. 4: 02 0501 0206c0100 XNUMX XNUMX XNUMX ....L......
Adından da anlaşılacağı gibi, Block_upload_traffic.pcapng dosyası, PLC'ye blok yükleme trafiğinin bir dökümünü içerir.
Konferans sırasında yarışma sahasındaki bu trafik yükünün elde edilmesinin biraz daha zor olduğunu belirtmekte fayda var. Bunun için TeslaSCADA2 proje dosyasından scripti anlamak gerekiyordu. Buradan RC4 kullanılarak şifrelenen dökümün nerede bulunduğunu ve şifresini çözmek için hangi anahtarın kullanılması gerektiğini anlamak mümkündü. Sahadaki veri bloklarının dökümleri S7 protokol istemcisi kullanılarak elde edilebilir. Bunun için Snap7 paketindeki demo istemcisini kullandım.
Trafik dökümünden sinyal işleme bloklarını çıkarma
Dökümün içeriğine baktığınızda OB1, FC1, FC2 ve FC3 sinyal işleme bloklarını içerdiğini anlayabilirsiniz:
Bu blokajların kaldırılması gerekiyor. Bu, örneğin trafiği daha önce pcapng formatından pcap formatına dönüştürmüş olan aşağıdaki komut dosyasıyla yapılabilir:
#!/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 = ''
Ortaya çıkan blokları inceledikten sonra bunların her zaman 70 70 (pp) bayt ile başladığını fark edeceksiniz. Şimdi bunları nasıl analiz edeceğinizi öğrenmeniz gerekiyor. Atama ipucu bunun için PlcSim kullanmanız gerektiğini önerir.
Bloklardan insan tarafından okunabilen talimatların alınması
Öncelikle Simatic Manager yazılımını kullanarak tekrar eden komutlara (= Q 7) sahip birkaç blok yükleyerek ve emülatörde elde edilen PLC'yi example.plc dosyasına kaydederek S0.0-PlcSim'i programlamayı deneyelim. Dosyanın içeriğine bakarak daha önce keşfettiğimiz 70 70 imzasıyla indirilen blokların başlangıcını kolaylıkla belirleyebilirsiniz. Görünüşe göre bloklardan önce blok boyutu 4 baytlık küçük endian değeri olarak yazılıyor.
Plc dosyalarının yapısı hakkında bilgi aldıktan sonra PLC S7 programlarını okumak için aşağıdaki eylem planı ortaya çıktı:
- Simatic Manager'ı kullanarak S7-PlcSim'de dump'tan aldığımız blok yapısına benzer bir blok yapısı oluşturuyoruz. Blok boyutları (bu, blokların gerekli sayıda talimatla doldurulmasıyla elde edilir) ve tanımlayıcılarının (OB1, FC1, FC2, FC3) eşleşmesi gerekir.
- PLC'yi bir dosyaya kaydedin.
- Ortaya çıkan dosyadaki blokların içeriğini trafik dökümünden gelen bloklarla değiştiriyoruz. Blokların başlangıcı imza ile belirlenir.
- Ortaya çıkan dosyayı S7-PlcSim'e yüklüyoruz ve Simatic Manager'da blokların içeriklerine bakıyoruz.
Bloklar örneğin aşağıdaki kodla değiştirilebilir:
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 belki daha zor ama yine de doğru bir yol izledi. PlcSim'in ağ üzerinden iletişim kurabilmesi için katılımcıların NetToPlcSim programını kullanacaklarını, blokları Snap7 aracılığıyla PlcSim'e yükleyeceklerini ve daha sonra bu blokları geliştirme ortamını kullanarak PlcSim'den proje olarak indireceklerini varsaydık.
Ortaya çıkan dosyayı S7-PlcSim'de açarak üzerine yazılan blokları Simatic Manager'ı kullanarak okuyabilirsiniz. Ana cihaz kontrol fonksiyonları FC1 bloğuna kaydedilir. Açıldığında M0 ve M2.2 bit hafıza değerlerine dayalı olarak PLC kontrolünü manuel moda ayarlamak için görünen #TEMP2.3 değişkeni özellikle dikkat çekicidir. #TEMP0 değeri FC3 fonksiyonu tarafından ayarlanır.
Sorunu çözmek için FC3 işlevini analiz etmeniz ve mantıksal bir işlev döndürmesi için ne yapılması gerektiğini anlamanız gerekir.
Yarışma sahasındaki Low Security standındaki PLC sinyal işleme blokları da benzer şekilde düzenlenmişti ancak #TEMP0 değişkeninin değerini ayarlamak için DB1 bloğuna my ninja way satırını yazmanız yeterliydi. Bir bloktaki değerin kontrol edilmesi basitti ve blok programlama dili hakkında derin bilgi gerektirmiyordu. Açıktır ki, Yüksek Güvenlik seviyesinde manuel kontrole ulaşmak çok daha zor olacaktır ve STL dilinin (S7 PLC'yi programlamanın yollarından biri) inceliklerini anlamak gerekir.
Ters blok FC3
STL gösteriminde FC3 bloğunun içeriği:
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
Kod oldukça uzundur ve STL'ye aşina olmayan biri için karmaşık görünebilir. Bu makale çerçevesinde her talimatı analiz etmenin bir anlamı yok; STL dilinin ayrıntılı talimatları ve yetenekleri ilgili kılavuzda bulunabilir:
İşlemden sonra kod]
# Инициализация различных переменных
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
Sanal makine talimatları hakkında fikir sahibi olduktan sonra, DB100 bloğundaki bayt kodunu ayrıştırmak için küçük bir disassembler yazalım:
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
Sonuç olarak aşağıdaki sanal makine kodunu alıyoruz:
Sanal makine kodu
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)
Gördüğünüz gibi bu program DB101'deki her karakterin belirli bir değere eşit olup olmadığını kontrol eder. Tüm kontrolleri geçmek için son satır şudur: n0w u 4r3 7h3 m4573r. Bu hattın DB101 bloğuna yerleştirilmesi durumunda manuel PLC kontrolü devreye girecek ve balonun patlatılması veya söndürülmesi mümkün olacaktır.
Bu kadar! Alexey, endüstriyel bir ninjaya yakışan yüksek düzeyde bilgi sergiledi :) Kazanana unutulmaz ödüller gönderdik. Tüm katılımcılara çok teşekkürler!
Kaynak: habr.com