Oxirgi PhDays 9da biz gaz nasoslari zavodini buzish bo'yicha tanlov o'tkazdik - musobaqa . 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.

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:
- Petrovich kecha menga bloklarni PlcSim-dan Step7-ga yuklashingiz mumkinligini aytdi.
- Stendda Siemens Simatic S7-300 seriyali PLC ishlatildi.
- 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:

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.

PLC fayllarining tuzilishi haqida ma'lumot olganimizdan so'ng, PLC S7 dasturlarini o'qish uchun quyidagi harakatlar rejasi paydo bo'ldi:
- 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).
- PLC ni faylga saqlang.
- Olingan fayldagi bloklarning mazmunini trafik axlatxonasidagi bloklar bilan almashtiramiz. Bloklarning boshlanishi imzo bilan belgilanadi.
- 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.

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_VALKod 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: . 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_VALVirtual 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))
breakNatijada 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
