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

a Izoh qo'shish