آخری PHDays 9 میں ہم نے گیس پمپنگ پلانٹ کو ہیک کرنے کے لیے ایک مقابلہ منعقد کیا۔ . سائٹ پر تین اسٹینڈز تھے جن میں مختلف سیکیورٹی پیرامیٹرز تھے (کوئی سیکیورٹی نہیں، کم سیکیورٹی، ہائی سیکیورٹی)، اسی صنعتی عمل کی تقلید کرتے ہوئے: دباؤ کے تحت ہوا کو غبارے میں ڈالا گیا (اور پھر چھوڑ دیا گیا)۔
مختلف حفاظتی پیرامیٹرز کے باوجود، اسٹینڈز کی ہارڈ ویئر کی ترکیب ایک جیسی تھی: سیمنز سیمیٹک PLC S7-300 سیریز؛ ایمرجنسی ڈیفلیشن بٹن اور پریشر ماپنے والا آلہ (PLC ڈیجیٹل ان پٹ (DI) سے منسلک)؛ مہنگائی اور ہوا کی تنزلی کے لیے کام کرنے والے والوز (PLC (DO) کے ڈیجیٹل آؤٹ پٹس سے منسلک) - نیچے دی گئی تصویر دیکھیں۔

PLC نے، پریشر ریڈنگ پر منحصر ہے اور اپنے پروگرام کے مطابق، گیند کو ڈیفلیٹ کرنے یا فلانے کا فیصلہ کیا (متعلقہ والوز کو کھولا اور بند کیا)۔ تاہم، تمام اسٹینڈز میں دستی کنٹرول موڈ تھا، جس نے بغیر کسی پابندی کے والوز کی حالتوں کو کنٹرول کرنا ممکن بنایا۔
اس موڈ کو فعال کرنے کی پیچیدگی میں اسٹینڈز مختلف تھے: غیر محفوظ اسٹینڈ پر یہ کرنا سب سے آسان تھا، اور ہائی سیکیورٹی اسٹینڈ پر یہ اسی مناسبت سے زیادہ مشکل تھا۔
چھ میں سے پانچ مسائل دو دن میں حل ہو گئے۔ پہلی پوزیشن حاصل کرنے والے نے 233 پوائنٹس حاصل کیے (اس نے مقابلے کی تیاری میں ایک ہفتہ گزارا)۔ تین فاتح: میں جگہ دیتا ہوں - a1exdandy، II - Rubikoid، III - Ze۔
تاہم، PHDays کے دوران، شرکاء میں سے کوئی بھی تینوں اسٹینڈز پر قابو نہیں پا سکا، اس لیے ہم نے ایک آن لائن مقابلہ کرنے کا فیصلہ کیا اور جون کے اوائل میں سب سے مشکل کام شائع کیا۔ شرکاء کو ایک ماہ کے اندر ٹاسک مکمل کرنا تھا، جھنڈا ڈھونڈنا تھا، اور تفصیل سے اور دلچسپ انداز میں حل بیان کرنا تھا۔
کٹ کے نیچے ہم مہینے بھر بھیجے گئے کام کے بہترین حل کا تجزیہ شائع کرتے ہیں، یہ ڈیجیٹل سیکیورٹی کمپنی کے Alexey Kovrizhnykh (a1exdandy) نے پایا، جس نے PHDays کے دوران مقابلے میں پہلی پوزیشن حاصل کی۔ ذیل میں ہم اس کا متن اپنے تبصروں کے ساتھ پیش کرتے ہیں۔
ابتدائی تجزیہ
لہذا، کام میں مندرجہ ذیل فائلوں کے ساتھ ایک محفوظ شدہ دستاویزات شامل ہیں:
- block_upload_traffic.pcapng
- DB100.bin
- hints.txt
hints.txt فائل میں کام کو حل کرنے کے لیے ضروری معلومات اور اشارے شامل ہیں۔ اس کے مندرجات یہ ہیں:
- پیٹرووچ نے کل مجھے بتایا کہ آپ PlcSim سے Step7 میں بلاکس لوڈ کر سکتے ہیں۔
- سیمنز سیمیٹک S7-300 سیریز PLC اسٹینڈ پر استعمال کی گئی تھی۔
- PlcSim ایک PLC ایمولیٹر ہے جو آپ کو Siemens S7 PLCs کے لیے پروگرام چلانے اور ڈیبگ کرنے کی اجازت دیتا ہے۔
ایسا لگتا ہے کہ DB100.bin فائل DB100 PLC ڈیٹا بلاک پر مشتمل ہے: 00000000: 0100 0102 6e02 0401 0206 0100 0101 0102 ....n........ 00000010 1002 ..... ......... 0501: 0202 2002 0501 0206 0100 0102 00000020 0102a7702 ..w............. ................0401:0206 0100 0103 0102 0 02 00000030a0501 0202 u............... 1602: 0501 0206 0100 0104 0102 00000040 7502............ 0401۔ 0206: 0100 0105 0102 0 02 0501 00000050 0202 ......... & ..... 1602: 0501C0206 0100 0106 0102 3402 4 00000060 0401 L ......... 0206. .. 0100 : 0107 0102 2602 0501 0202a00000070 4 02 0501 ................ 0206: 0100 0108 0102 3302a 0401 3 00000080 0206 .......... 0100a0109: 0102 0b 02 0501 0202 1602 00000090 0501 ......".....F... 0206b0100: 010 0102 3702c 0401 0206 7 .... … .... 000000e0: 0100 010 0102 2202 0501 0202 4602 0501 ........#...... 000000f0: 0206 0100 010 0102 3302 0401..... ... .....&. 0206: 0100 3 000000c0 010 0102 0 ....L......
جیسا کہ نام سے پتہ چلتا ہے، block_upload_traffic.pcapng فائل PLC پر بلاک اپ لوڈ ٹریفک کا ایک ڈمپ پر مشتمل ہے۔
یہ بات قابل غور ہے کہ کانفرنس کے دوران مقابلے کی جگہ پر اس ٹریفک ڈمپ کو حاصل کرنا کچھ زیادہ مشکل تھا۔ ایسا کرنے کے لیے، TeslaSCADA2 کے لیے پروجیکٹ فائل سے اسکرپٹ کو سمجھنا ضروری تھا۔ اس سے یہ سمجھنا ممکن تھا کہ RC4 کا استعمال کرتے ہوئے خفیہ کردہ ڈمپ کہاں واقع تھا اور اسے ڈکرپٹ کرنے کے لیے کونسی کلید استعمال کرنے کی ضرورت ہے۔ S7 پروٹوکول کلائنٹ کا استعمال کرتے ہوئے سائٹ پر ڈیٹا بلاکس کے ڈمپ حاصل کیے جا سکتے ہیں۔ اس کے لیے میں نے Snap7 پیکیج سے ڈیمو کلائنٹ استعمال کیا۔
ٹریفک ڈمپ سے سگنل پروسیسنگ بلاکس نکالنا
ڈمپ کے مواد کو دیکھ کر، آپ سمجھ سکتے ہیں کہ اس میں سگنل پروسیسنگ بلاکس OB1، FC1، FC2 اور FC3 شامل ہیں:

ان بلاکس کو ہٹانا ضروری ہے۔ یہ کیا جا سکتا ہے، مثال کے طور پر، درج ذیل اسکرپٹ کے ساتھ، پہلے ٹریفک کو pcapng فارمیٹ سے pcap میں تبدیل کر دیا گیا تھا۔
#!/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 = ''نتیجے میں آنے والے بلاکس کا جائزہ لینے کے بعد، آپ دیکھیں گے کہ وہ ہمیشہ بائٹس 70 70 (pp) سے شروع ہوتے ہیں۔ اب آپ کو ان کا تجزیہ کرنے کا طریقہ سیکھنے کی ضرورت ہے۔ اسائنمنٹ کا اشارہ بتاتا ہے کہ آپ کو اس کے لیے PlcSim استعمال کرنے کی ضرورت ہے۔
بلاکس سے انسانی پڑھنے کے قابل ہدایات حاصل کرنا
سب سے پہلے، آئیے S7-PlcSim پروگرام کرنے کی کوشش کریں جس میں کئی بلاکس کو دہرانے والی ہدایات (= Q 0.0) کے ساتھ اس میں سمیٹک مینیجر سافٹ ویئر کا استعمال کرتے ہوئے لوڈ کریں، اور ایمولیٹر میں حاصل کردہ PLC کو example.plc فائل میں محفوظ کریں۔ فائل کے مواد کو دیکھ کر، آپ آسانی سے دستخط 70 70 کے ذریعہ ڈاؤن لوڈ کردہ بلاکس کی شروعات کا تعین کرسکتے ہیں، جو ہم نے پہلے دریافت کیا تھا۔ بلاکس سے پہلے، بظاہر، بلاک کا سائز 4 بائٹ لٹل اینڈین ویلیو کے طور پر لکھا جاتا ہے۔

plc فائلوں کی ساخت کے بارے میں معلومات حاصل کرنے کے بعد، PLC S7 پروگراموں کو پڑھنے کے لیے درج ذیل ایکشن پلان سامنے آیا:
- سمیٹک مینیجر کا استعمال کرتے ہوئے، ہم S7-PlcSim میں ایک بلاک ڈھانچہ بناتے ہیں جیسا کہ ہمیں ڈمپ سے موصول ہوا ہے۔ بلاک کے سائز کا مماثل ہونا چاہیے (یہ بلاکس کو مطلوبہ تعداد میں ہدایات کے ساتھ بھر کر حاصل کیا جاتا ہے) اور ان کے شناخت کنندگان (OB1, FC1, FC2, FC3)۔
- PLC کو فائل میں محفوظ کریں۔
- ہم نتیجے میں آنے والی فائل میں بلاکس کے مواد کو ٹریفک ڈمپ کے بلاکس سے بدل دیتے ہیں۔ بلاکس کا آغاز دستخط سے طے ہوتا ہے۔
- ہم نتیجے میں فائل کو S7-PlcSim میں لوڈ کرتے ہیں اور سمیٹک مینیجر میں بلاکس کے مواد کو دیکھتے ہیں۔
بلاکس کو تبدیل کیا جا سکتا ہے، مثال کے طور پر، درج ذیل کوڈ کے ساتھ:
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)الیکسی نے شاید زیادہ مشکل لیکن پھر بھی درست راستہ اختیار کیا۔ ہم نے فرض کیا کہ شرکاء NetToPlcSim پروگرام کا استعمال کریں گے تاکہ PlcSim نیٹ ورک پر بات چیت کر سکے، Snap7 کے ذریعے PlcSim پر بلاکس اپ لوڈ کر سکے، اور پھر ترقیاتی ماحول کا استعمال کرتے ہوئے PlcSim سے پروجیکٹ کے طور پر ان بلاکس کو ڈاؤن لوڈ کر سکے۔
نتیجے میں آنے والی فائل کو S7-PlcSim میں کھول کر، آپ سمیٹک مینیجر کا استعمال کرکے اوور رائٹ شدہ بلاکس کو پڑھ سکتے ہیں۔ اہم ڈیوائس کنٹرول کے افعال بلاک FC1 میں ریکارڈ کیے گئے ہیں۔ خاص طور پر نوٹ کریں #TEMP0 متغیر ہے، جو آن ہونے پر PLC کنٹرول کو M2.2 اور M2.3 بٹ میموری ویلیوز کی بنیاد پر مینوئل موڈ پر سیٹ کرتا دکھائی دیتا ہے۔ #TEMP0 ویلیو فنکشن FC3 کے ذریعے سیٹ کی گئی ہے۔

مسئلہ کو حل کرنے کے لیے، آپ کو FC3 فنکشن کا تجزیہ کرنے کی ضرورت ہے اور یہ سمجھنے کی ضرورت ہے کہ کیا کرنے کی ضرورت ہے تاکہ یہ ایک منطقی واپس آجائے۔
مقابلے کی جگہ پر لو سیکیورٹی اسٹینڈ پر PLC سگنل پروسیسنگ بلاکس کو اسی طرح ترتیب دیا گیا تھا، لیکن #TEMP0 متغیر کی قدر کو سیٹ کرنے کے لیے، DB1 بلاک میں میری ننجا کے راستے کو لکھنا کافی تھا۔ کسی بلاک میں قدر کی جانچ کرنا سیدھا سیدھا تھا اور اس کے لیے بلاک پروگرامنگ زبان کے گہرے علم کی ضرورت نہیں تھی۔ ظاہر ہے، ہائی سیکیورٹی کی سطح پر، دستی کنٹرول حاصل کرنا بہت زیادہ مشکل ہوگا اور STL زبان کی پیچیدگیوں کو سمجھنا ضروری ہے (S7 PLC کو پروگرام کرنے کا ایک طریقہ)۔
ریورس بلاک ایف سی 3
STL نمائندگی میں FC3 بلاک کے مشمولات:
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کوڈ کافی لمبا ہے اور STL سے ناواقف کسی کے لیے پیچیدہ معلوم ہو سکتا ہے۔ اس مضمون کے فریم ورک کے اندر ہر ہدایت کا تجزیہ کرنے کا کوئی فائدہ نہیں؛ STL زبان کی تفصیلی ہدایات اور صلاحیتیں متعلقہ کتابچے میں دیکھی جا سکتی ہیں: . یہاں میں پروسیسنگ کے بعد وہی کوڈ پیش کروں گا - لیبلز اور متغیرات کا نام تبدیل کرنا اور آپریشن الگورتھم اور کچھ STL زبان کی تعمیرات کو بیان کرنے والے تبصرے شامل کرنا۔ مجھے فوری طور پر نوٹ کرنے دیجئے کہ زیر بحث بلاک میں ایک ورچوئل مشین ہے جو DB100 بلاک میں موجود کچھ بائیک کوڈ پر عمل کرتی ہے، جس کے مواد کو ہم جانتے ہیں۔ ورچوئل مشین ہدایات میں 1 بائٹ آپریٹنگ کوڈ اور آرگیومینٹس کے بائٹس، ہر دلیل کے لیے ایک بائٹ پر مشتمل ہوتا ہے۔ تمام سمجھی جانے والی ہدایات کے دو دلائل ہیں؛ میں نے تبصروں میں ان کی اقدار کو X اور Y کے طور پر نامزد کیا ہے۔
پروسیسنگ کے بعد کوڈ]
# Инициализация различных переменных
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ورچوئل مشین کی ہدایات کا اندازہ ہونے کے بعد، آئیے DB100 بلاک میں بائیک کوڈ کو پارس کرنے کے لیے ایک چھوٹا سا جدا کرنے والا لکھتے ہیں:
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نتیجے کے طور پر، ہمیں درج ذیل ورچوئل مشین کوڈ ملتا ہے:
ورچوئل مشین کوڈ
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)جیسا کہ آپ دیکھ سکتے ہیں، یہ پروگرام صرف DB101 سے ہر ایک کردار کو ایک خاص قدر کی برابری کے لیے چیک کرتا ہے۔ تمام چیک پاس کرنے کی آخری لائن ہے: n0w u 4r3 7h3 m4573r۔ اگر یہ لائن بلاک DB101 میں رکھی جاتی ہے، تو دستی PLC کنٹرول چالو ہو جاتا ہے اور غبارے کو پھٹنا یا پھٹنا ممکن ہو گا۔
بس! الیکسی نے ایک صنعتی ننجا کے لائق علم کی اعلیٰ سطح کا مظاہرہ کیا :) ہم نے فاتح کو یادگار انعامات بھیجے۔ تمام شرکاء کا بہت شکریہ!
ماخذ: www.habr.com
