Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

Oxirgi PhDays 9da biz gaz nasoslari zavodini buzish bo'yicha tanlov o'tkazdik - musobaqa Sanoat Ninja. Saytda bir xil sanoat jarayoniga taqlid qiluvchi turli xil xavfsizlik parametrlari (Xavfsizlik yo'q, Kam xavfsizlik, Yuqori xavfsizlik) bo'lgan uchta stend bor edi: bosim ostida havo havo shariga pompalandi (va keyin chiqarildi).

Turli xil xavfsizlik parametrlariga qaramasdan, stendlarning apparat tarkibi bir xil edi: Siemens Simatic PLC S7-300 seriyali; favqulodda deflyatsiya tugmasi va bosim o'lchash moslamasi (PLC raqamli kirishlariga (DI) ulangan); havoni inflyatsiya qilish va tushirish uchun ishlaydigan klapanlar (PLC (DO) ning raqamli chiqishlariga ulangan) - quyidagi rasmga qarang.

Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

PLC, bosim ko'rsatkichlariga qarab va o'z dasturiga muvofiq, to'pni o'chirish yoki puflash to'g'risida qaror qabul qildi (tegishli valflarni ochdi va yopdi). Shu bilan birga, barcha stendlarda qo'lda boshqarish rejimi mavjud edi, bu esa klapanlarning holatini hech qanday cheklovlarsiz nazorat qilish imkonini berdi.

Stendlar ushbu rejimni yoqishning murakkabligi bilan ajralib turardi: himoyalanmagan stendda buni qilish eng oson, Oliy xavfsizlik stendida esa mos ravishda qiyinroq edi.

Oltita muammodan beshtasi ikki kun ichida hal qilindi; Birinchi o‘rin ishtirokchisi 233 ball to‘pladi (u bir hafta davomida musobaqaga tayyorgarlik ko‘rdi). Uchta g'olib: I o'rin - a1exdandy, II - Rubikoid, III - Ze.

Biroq, PHDays davomida ishtirokchilarning hech biri uchala stendni yengib chiqa olmadi, shuning uchun biz onlayn tanlov o'tkazishga qaror qildik va iyun oyi boshida eng qiyin vazifani e'lon qildik. Ishtirokchilar bir oy ichida topshiriqni bajarishlari, bayroqni topishlari va yechimni batafsil va qiziqarli tarzda tasvirlashlari kerak edi.

Kesish ostida biz bir oy davomida yuborilgan topshiriqning eng yaxshi yechimi tahlilini e'lon qilamiz, uni PhDays davomida tanlovda 1-o'rinni egallagan Digital Security kompaniyasidan Aleksey Kovrijnix (aXNUMXexdandy) topdi. Quyida uning matnini sharhlarimiz bilan taqdim etamiz.

Dastlabki tahlil

Shunday qilib, vazifa quyidagi fayllar bilan arxivni o'z ichiga oladi:

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

hints.txt fayli vazifani hal qilish uchun kerakli ma'lumotlarni va maslahatlarni o'z ichiga oladi. Mana uning mazmuni:

  1. Petrovich kecha menga bloklarni PlcSim-dan Step7-ga yuklashingiz mumkinligini aytdi.
  2. Stendda Siemens Simatic S7-300 seriyali PLC ishlatildi.
  3. PlcSim - bu Siemens S7 PLC uchun dasturlarni ishga tushirish va disk raskadrovka qilish imkonini beruvchi PLC emulyatoridir.

DB100.bin faylida DB100 PLC ma'lumotlar bloki bor ko'rinadi: 00000000: 0100 0102 6e02 0401 0206 0100 0101 0102 ....n............ 00000010: 1002 0501 0202 2002 0501. ..... ......... 0206: 0100 0102 00000020 0102 7702 0401 0206 0100a0103 ..w............. 0102: 0 02 00000030 0501 0202 1602 ................ 0501: 0206 0100 0104 0102 00000040 7502 0401a0206 0100 u............... 0105: 0102 0 02 0501 00000050 0202 1602 0501............0206. 0100: 0106 0102 3402 4 00000060 0401 0206 0100 .........&..... 0107: 0102c2602 0501 0202 00000070 4 02 0501 0206........ : 0100 0108 0102 3302 0401a3 00000080 0206 0100 ................ 0109: 0102 0 02 0501a 0202 1602 00000090 0501 .........0206. 0100a010: 0102 3702b 0401 0206 7 000000 0 0100 ......".....F... 010b0102: 2202 0501 0202c 4602 0501 000000 ..........0 .. 0206c0100: 010d 0102 3302a0401 0206 0100 3 000000 0 ................ 010d0102: 0 02e 0501 0202d1602 0501 0206 .... .... 000000e0: 0100 010 0102 6 02 0401 0206 0100 ......#...... 010f000000: 0 0102 1102 0501 0202 2302 ......... ..... 0501: 0206 0100 000000 0 0110 0102 3502 0401 ......%............ 0206: 0100 0111 0102 5 00000100 1202 0501 ...... 0202 . .....&. 2502: 0501 0206 0100c0112 00000110 0102 3302 ....L......

Nomidan ko'rinib turibdiki, block_upload_traffic.pcapng fayli PLC ga blokni yuklash trafigini o'z ichiga oladi.

Ta'kidlash joizki, konferentsiya davomida musobaqalar o'tkaziladigan joydagi ushbu traffikni olish biroz qiyinroq edi. Buning uchun TeslaSCADA2 uchun loyiha faylidan skriptni tushunish kerak edi. Undan RC4 yordamida shifrlangan axlatxona qayerda joylashganligini va uning shifrini ochish uchun qanday kalitdan foydalanish kerakligini tushunish mumkin edi. Saytdagi ma'lumotlar bloklarini S7 protokoli mijozi yordamida olish mumkin edi. Buning uchun men Snap7 paketidagi demo mijozdan foydalandim.

Trafik axlatxonasidan signalni qayta ishlash bloklarini chiqarish

Axlatxonaning tarkibiga qarab, unda OB1, FC1, FC2 va FC3 signalni qayta ishlash bloklari mavjudligini tushunishingiz mumkin:

Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

Ushbu bloklarni olib tashlash kerak. Buni, masalan, trafikni pcapng formatidan pcapga o'zgartirgan holda, masalan, quyidagi skript yordamida amalga oshirish mumkin:

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

Olingan bloklarni o'rganib chiqib, ular har doim 70 70 (pp) baytdan boshlanishini payqadingiz. Endi siz ularni qanday tahlil qilishni o'rganishingiz kerak. Topshiriq maslahati buning uchun PlcSim-dan foydalanishingiz kerakligini ko'rsatadi.

Bloklardan odam o'qiy oladigan ko'rsatmalarni olish

Birinchidan, keling, S7-PlcSim-ni Simatic Manager dasturi yordamida takroriy ko'rsatmalarga ega bir nechta bloklarni (= Q 0.0) yuklash va emulyatorda olingan PLCni example.plc fayliga saqlash orqali dasturlashga harakat qilaylik. Fayl mazmunini ko'rib chiqib, biz avval kashf etgan 70 70 imzosi bilan yuklab olingan bloklarning boshlanishini osongina aniqlashingiz mumkin. Bloklardan oldin, ko'rinishidan, blok o'lchami 4 baytlik kichik endian qiymati sifatida yozilgan.

Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

PLC fayllarining tuzilishi haqida ma'lumot olganimizdan so'ng, PLC S7 dasturlarini o'qish uchun quyidagi harakatlar rejasi paydo bo'ldi:

  1. Simatic Manager-dan foydalanib, biz S7-PlcSim-da axlatdan olinganga o'xshash blok tuzilmasini yaratamiz. Blok o'lchamlari mos kelishi kerak (bu bloklarni kerakli miqdordagi ko'rsatmalar bilan to'ldirish orqali erishiladi) va ularning identifikatorlari (OB1, FC1, FC2, FC3).
  2. PLC ni faylga saqlang.
  3. Olingan fayldagi bloklarning mazmunini trafik axlatxonasidagi bloklar bilan almashtiramiz. Bloklarning boshlanishi imzo bilan belgilanadi.
  4. Olingan faylni S7-PlcSim-ga yuklaymiz va Simatic Manager-da bloklarning mazmunini ko'rib chiqamiz.

Bloklarni, masalan, quyidagi kod bilan almashtirish mumkin:

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)

Aleksey, ehtimol, qiyinroq, ammo baribir to'g'ri yo'lni tanladi. Ishtirokchilar NetToPlcSim dasturidan PlcSim tarmoq orqali muloqot qilishi, bloklarni Snap7 orqali PlcSim-ga yuklashi va keyin PlcSim-dan ishlab chiqish muhiti yordamida ushbu bloklarni loyiha sifatida yuklab olishi uchun foydalanishini taxmin qildik.

Olingan faylni S7-PlcSim-da ochish orqali siz Simatic Manager yordamida qayta yozilgan bloklarni o'qishingiz mumkin. Qurilmani boshqarishning asosiy funktsiyalari FC1 blokida qayd etilgan. #TEMP0 o'zgaruvchisi alohida e'tiborga loyiqdir, u yoqilganda M2.2 va M2.3 bit xotira qiymatlari asosida PLC boshqaruvini qo'lda rejimga o'rnatadi. #TEMP0 qiymati FC3 funktsiyasi tomonidan o'rnatiladi.

Industrial Ninja izidan: Positive Hack Days 9 da PLC qanday buzilganligi

Muammoni hal qilish uchun siz FC3 funktsiyasini tahlil qilishingiz va mantiqiy funktsiyani qaytarishi uchun nima qilish kerakligini tushunishingiz kerak.

Musobaqa maydonchasidagi Low Security stendidagi PLC signalini qayta ishlash bloklari shunga o'xshash tarzda joylashtirilgan, ammo #TEMP0 o'zgaruvchisining qiymatini belgilash uchun DB1 blokiga my ninja yo'lini yozish kifoya edi. Blokdagi qiymatni tekshirish oddiy edi va blok dasturlash tilini chuqur bilishni talab qilmadi. Shubhasiz, Oliy xavfsizlik darajasida qo'lda boshqarishga erishish ancha qiyin bo'ladi va STL tilining nozik tomonlarini tushunish kerak (S7 PLC-ni dasturlash usullaridan biri).

FC3 teskari blok

STL ko'rinishidagi FC3 blokining tarkibi:

      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 juda uzun va STL bilan tanish bo'lmagan odam uchun murakkab ko'rinishi mumkin. Ushbu maqola doirasida har bir ko'rsatmani tahlil qilishning ma'nosi yo'q, STL tilining batafsil ko'rsatmalari va imkoniyatlarini tegishli qo'llanmada topish mumkin: S7-300 va S7-400 dasturlash uchun bayonotlar ro'yxati (STL).. Bu erda men qayta ishlashdan so'ng bir xil kodni taqdim etaman - teglar va o'zgaruvchilar nomini o'zgartirish va operatsiya algoritmi va ba'zi STL tili konstruktsiyalarini tavsiflovchi sharhlar qo'shish. Darhol ta'kidlashim kerakki, ko'rib chiqilayotgan blokda DB100 blokida joylashgan bayt-kodni bajaradigan virtual mashina mavjud, uning mazmuni bizga ma'lum. Virtual mashina ko'rsatmalari 1 bayt operatsion kod va bayt argumentlardan iborat bo'lib, har bir argument uchun bir bayt. Barcha ko'rib chiqilgan ko'rsatmalar ikkita dalilga ega; men izohlarda ularning qiymatlarini X va Y deb belgiladim.

Qayta ishlashdan keyin 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

Virtual mashina ko'rsatmalari haqida tasavvurga ega bo'lgandan so'ng, keling, DB100 blokidagi bayt kodini tahlil qilish uchun kichik disassembler yozamiz:

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

Natijada biz quyidagi virtual mashina kodini olamiz:

Virtual mashina kodi

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)

Ko'rib turganingizdek, ushbu dastur DB101 dan har bir belgining ma'lum bir qiymatga tengligini tekshiradi. Barcha tekshiruvlardan o'tish uchun oxirgi qator: n0w u 4r3 7h3 m4573r. Agar bu chiziq DB101 blokiga joylashtirilsa, qo'lda PLC boshqaruvi faollashtiriladi va balonni portlatish yoki o'chirish mumkin bo'ladi.


Ana xolos! Aleksey sanoat ninjasiga loyiq bo'lgan yuqori darajadagi bilimni namoyish etdi :) Biz g'olibga esdalik sovg'alarini yubordik. Barcha ishtirokchilarga katta rahmat!

Manba: www.habr.com

DDoS himoyasi, VPS VDS serverlari bo'lgan saytlar uchun ishonchli hosting sotib oling 🔥 DDoS himoyasi, VPS VDS serverlari bilan ishonchli veb-sayt xostingini sotib oling | ProHoster