Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

Son PHDays 9'da bir gaz pompalama tesisini hacklemek için bir yarışma düzenledik - yarışma Endüstriyel Ninja. Sahada, aynı endüstriyel süreci taklit eden farklı güvenlik parametrelerine (Güvenlik Yok, Düşük Güvenlik, Yüksek Güvenlik) sahip üç stant vardı: basınç altındaki hava bir balona pompalandı (ve ardından serbest bırakıldı).

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.

Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

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:

  1. Petrovich dün bana PlcSim'den Step7'ye blok yükleyebileceğinizi söyledi.
  2. Standda Siemens Simatic S7-300 serisi PLC kullanıldı.
  3. 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:

Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

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.

Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

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ı:

  1. 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.
  2. PLC'yi bir dosyaya kaydedin.
  3. 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.
  4. 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.

Endüstriyel Ninja'nın izinde: Positive Hack Days 9'da bir PLC nasıl saldırıya uğradı

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: S7-300 ve S7-400 Programlama için Bildirim Listesi (STL). Burada aynı kodu işledikten sonra sunacağım; etiketleri ve değişkenleri yeniden adlandıracağım ve işlem algoritmasını ve bazı STL dil yapılarını açıklayan yorumları ekleyeceğim. Söz konusu bloğun, içeriğini bildiğimiz DB100 bloğunda yer alan bazı bayt kodlarını çalıştıran bir sanal makine içerdiğini hemen belirteyim. Sanal makine talimatları, her argüman için bir bayt olmak üzere 1 baytlık işletim kodu ve baytlık argümanlardan oluşur. Dikkate alınan tüm talimatların iki argümanı vardır; yorumlarda değerlerini X ve Y olarak belirledim.

İş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

Yorum ekle