ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

ในงาน PHDays 9 ที่ผ่านมา เราได้จัดการแข่งขันเจาะระบบปั๊มแก๊ส-แข่งขัน นินจาอุตสาหกรรม- ไซต์งานมีอัฒจันทร์สามแห่งที่มีพารามิเตอร์ความปลอดภัยที่แตกต่างกัน (ไม่มีการรักษาความปลอดภัย ความปลอดภัยต่ำ ความปลอดภัยสูง) จำลองกระบวนการทางอุตสาหกรรมแบบเดียวกัน นั่นคือ อากาศภายใต้ความกดดันถูกสูบเข้าไปในบอลลูน (แล้วปล่อย)

แม้จะมีพารามิเตอร์ความปลอดภัยที่แตกต่างกัน แต่องค์ประกอบฮาร์ดแวร์ของขาตั้งก็เหมือนกัน: Siemens Simatic PLC S7-300 series; ปุ่มภาวะเงินฝืดฉุกเฉินและอุปกรณ์วัดความดัน (เชื่อมต่อกับอินพุตดิจิตอล PLC (DI)) วาล์วที่ทำงานสำหรับการพองตัวและภาวะเงินฝืดของอากาศ (เชื่อมต่อกับเอาต์พุตดิจิทัลของ PLC (DO)) - ดูรูปด้านล่าง

ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

PLC ตัดสินใจยุบหรือขยายลูกบอล (เปิดและปิดวาล์วที่เกี่ยวข้อง) ขึ้นอยู่กับการอ่านค่าความดันและตามโปรแกรม อย่างไรก็ตาม ขาตั้งทั้งหมดมีโหมดการควบคุมแบบแมนนวล ซึ่งทำให้สามารถควบคุมสถานะของวาล์วได้โดยไม่มีข้อจำกัดใดๆ

ขาตั้งมีความแตกต่างกันในด้านความซับซ้อนในการเปิดใช้งานโหมดนี้: สำหรับขาตั้งที่ไม่มีการป้องกัน วิธีที่ง่ายที่สุดในการทำเช่นนี้ และสำหรับขาตั้งที่มีความปลอดภัยสูงนั้นยากกว่าตามลำดับ

ปัญหาห้าในหกข้อได้รับการแก้ไขภายในสองวัน ผู้เข้าร่วมอันดับที่หนึ่งได้รับ 233 คะแนน (เขาใช้เวลาหนึ่งสัปดาห์ในการเตรียมตัวสำหรับการแข่งขัน) ผู้ชนะสามคน: ฉันวาง - a1exdandy, II - Rubikoid, III - Ze

อย่างไรก็ตาม ในช่วง PHDays ไม่มีผู้เข้าร่วมคนใดสามารถเอาชนะทั้งสามอัฒจันทร์ได้ ดังนั้นเราจึงตัดสินใจจัดการแข่งขันออนไลน์และเผยแพร่งานที่ยากที่สุดในช่วงต้นเดือนมิถุนายน ผู้เข้าร่วมต้องทำงานให้เสร็จสิ้นภายในหนึ่งเดือน ค้นหาธง และอธิบายวิธีแก้ปัญหาอย่างละเอียดและด้วยวิธีที่น่าสนใจ

ด้านล่างนี้เราได้เผยแพร่การวิเคราะห์วิธีแก้ปัญหาที่ดีที่สุดสำหรับงานจากที่ส่งไปในช่วงเดือนนั้น ซึ่งถูกค้นพบโดย Alexey Kovrizhnykh (a1exdandy) จากบริษัท Digital Security ซึ่งได้อันดับที่ XNUMX ในการแข่งขันในช่วง PHDays ด้านล่างนี้เรานำเสนอข้อความพร้อมความคิดเห็นของเรา

การวิเคราะห์เบื้องต้น

ดังนั้นงานจึงมีไฟล์เก็บถาวรพร้อมไฟล์ต่อไปนี้:

  • block_upload_traffic.pcapng
  • DB100.bin
  • คำแนะนำ.txt

ไฟล์ tips.txt มีข้อมูลที่จำเป็นและคำแนะนำในการแก้ปัญหา นี่คือเนื้อหา:

  1. Petrovich บอกฉันเมื่อวานนี้ว่าคุณสามารถโหลดบล็อกจาก PlcSim ลงในขั้นตอนที่ 7 ได้
  2. มีการใช้ PLC ของ Siemens Simatic S7-300 series ที่ขาตั้ง
  3. PlcSim เป็นโปรแกรมจำลอง PLC ที่ช่วยให้คุณสามารถเรียกใช้และแก้ไขข้อบกพร่องของโปรแกรมสำหรับ PLC ของ Siemens S7 ได้

ดูเหมือนว่าไฟล์ DB100.bin จะมีบล็อกข้อมูล DB100 PLC: 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 02a00000030 ..w............. 0501: 0202 1602 0501 0206 0100 0104 0102 00000040 ................ 7502: 0401 0206 0100 0105 0102 0 02a0501 00000050 คุณ............. 0202: 1602 0501 0206 0100 0106 0102 3402 4............00000060. 0401: 0206 0100 0107 0102 2602 0501 0202 00000070 ......... & ..... 4: 02C0501 0206 0100 0108 0102 3302 0401 3 L ......... 00000080 : 0206 0100 0109 0102 0a02 0501 0202 1602 ................ 00000090: 0501 0206 0100 010a 0102 3702 0401 0206 .........7. 000000a0: 0100 010b 0102 2202 0501 0202 4602 0501 ........."F... 000000b0: 0206 0100 010c 0102 3302 0401 0206 0100 ........3. .... .. 000000c0: 010d 0102 0a02 0501 0202 1602 0501 0206 ................ 000000d0: 0100 010e 0102 6d02 0401 0206 0100 010f ......ม. .... 000000e0: 0102 1102 0501 0202 2302 0501 0206 0100 ........#...... 000000f0: 0110 0102 3502 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 4c02 0501 0206 0100 ....ล......

ตามชื่อที่แนะนำ ไฟล์ block_upload_traffic.pcapng มีดัมพ์ของการรับส่งข้อมูลการอัปโหลดแบบบล็อกไปยัง PLC

เป็นที่น่าสังเกตว่าปริมาณการเข้าชมที่ไซต์การแข่งขันระหว่างการประชุมนั้นยากกว่าเล็กน้อย ในการดำเนินการนี้ จำเป็นต้องเข้าใจสคริปต์จากไฟล์โปรเจ็กต์สำหรับ TeslaSCADA2 จากนั้นจึงเป็นไปได้ที่จะเข้าใจว่าดัมพ์ที่เข้ารหัสโดยใช้ RC4 อยู่ที่ใด และคีย์ใดที่จำเป็นต้องใช้ในการถอดรหัส สามารถรับการดัมพ์ของบล็อกข้อมูลบนไซต์ได้โดยใช้ไคลเอ็นต์โปรโตคอล S7 สำหรับสิ่งนี้ ฉันใช้ไคลเอนต์สาธิตจากแพ็คเกจ Snap7

แยกบล็อกการประมวลผลสัญญาณออกจากทราฟฟิกดัมพ์

เมื่อดูเนื้อหาของดัมพ์ คุณจะเข้าใจได้ว่าดัมพ์มีบล็อกการประมวลผลสัญญาณ OB1, FC1, FC2 และ FC3:

ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

บล็อกเหล่านี้จะต้องถูกลบออก ซึ่งสามารถทำได้โดยใช้สคริปต์ต่อไปนี้ โดยก่อนหน้านี้ได้แปลงการรับส่งข้อมูลจากรูปแบบ 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) ลงไปโดยใช้ซอฟต์แวร์ Simatic Manager และบันทึก PLC ที่ได้รับในโปรแกรมจำลองลงในไฟล์ example.plc เมื่อดูเนื้อหาของไฟล์ คุณสามารถกำหนดจุดเริ่มต้นของบล็อกที่ดาวน์โหลดได้อย่างง่ายดายด้วยลายเซ็น 70 70 ซึ่งเราค้นพบก่อนหน้านี้ เห็นได้ชัดว่าก่อนบล็อก ขนาดบล็อกจะถูกเขียนเป็นค่า little-endian ขนาด 4 ไบต์

ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

หลังจากที่เราได้รับข้อมูลเกี่ยวกับโครงสร้างของไฟล์ plc แผนปฏิบัติการต่อไปนี้จะปรากฏขึ้นสำหรับการอ่านโปรแกรม PLC S7:

  1. การใช้ Simatic Manager เราสร้างโครงสร้างบล็อกใน S7-PlcSim คล้ายกับที่เราได้รับจากดัมพ์ ขนาดบล็อกจะต้องตรงกัน (ทำได้โดยการกรอกบล็อกตามจำนวนคำสั่งที่ต้องการ) และตัวระบุ (OB1, FC1, FC2, FC3)
  2. บันทึก PLC ลงในไฟล์
  3. เราแทนที่เนื้อหาของบล็อกในไฟล์ผลลัพธ์ด้วยบล็อกจากทราฟฟิกดัมพ์ จุดเริ่มต้นของบล็อกถูกกำหนดโดยลายเซ็น
  4. เราโหลดไฟล์ผลลัพธ์ลงใน S7-PlcSim และดูเนื้อหาของบล็อกใน Simatic Manager

คุณสามารถแทนที่บล็อกได้ เช่น ด้วยโค้ดต่อไปนี้:

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 ใช้เส้นทางที่ยากกว่า แต่ก็ยังถูกต้อง เราสันนิษฐานว่าผู้เข้าร่วมจะใช้โปรแกรม NetToPlcSim เพื่อให้ PlcSim สามารถสื่อสารผ่านเครือข่าย อัปโหลดบล็อกไปยัง PlcSim ผ่าน Snap7 จากนั้นดาวน์โหลดบล็อกเหล่านี้เป็นโปรเจ็กต์จาก PlcSim โดยใช้สภาพแวดล้อมการพัฒนา

เมื่อเปิดไฟล์ผลลัพธ์ใน S7-PlcSim คุณสามารถอ่านบล็อกที่ถูกเขียนทับได้โดยใช้ Simatic Manager ฟังก์ชั่นการควบคุมอุปกรณ์หลักจะถูกบันทึกไว้ในบล็อก FC1 สิ่งที่น่าสังเกตเป็นพิเศษคือตัวแปร #TEMP0 ซึ่งเมื่อเปิดใช้งานดูเหมือนว่าจะตั้งค่าการควบคุม PLC ให้เป็นโหมดแมนนวลตามค่าหน่วยความจำบิต M2.2 และ M2.3 ค่า #TEMP0 ถูกกำหนดโดยฟังก์ชัน FC3

ตามรอยของ Industrial Ninja: PLC ถูกแฮ็กอย่างไรที่ Positive Hack Days 9

ในการแก้ปัญหา คุณต้องวิเคราะห์ฟังก์ชัน FC3 และทำความเข้าใจว่าต้องทำอะไรจึงจะส่งกลับค่าตรรกะ

บล็อกการประมวลผลสัญญาณ PLC ที่จุดรักษาความปลอดภัยต่ำที่สถานที่แข่งขันได้รับการจัดเรียงในลักษณะเดียวกัน แต่เพื่อตั้งค่าของตัวแปร #TEMP0 ก็เพียงพอแล้วที่จะเขียนเส้นในแบบนินจาของฉันลงในบล็อก DB1 การตรวจสอบค่าในบล็อกนั้นง่ายต่อการเข้าใจ และไม่จำเป็นต้องมีความรู้เชิงลึกเกี่ยวกับภาษาการเขียนโปรแกรมของบล็อก แน่นอนว่าในระดับความปลอดภัยสูง การบรรลุการควบคุมด้วยตนเองจะยากกว่ามากและจำเป็นต้องเข้าใจความซับซ้อนของภาษา STL (วิธีหนึ่งในการเขียนโปรแกรม S7 PLC)

ย้อนกลับบล็อก FC3

เนื้อหาของบล็อก FC3 ในการเป็นตัวแทน STL:

      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) สำหรับการเขียนโปรแกรม S7-300 และ S7-400- ที่นี่ฉันจะนำเสนอโค้ดเดียวกันหลังการประมวลผล - เปลี่ยนชื่อป้ายกำกับและตัวแปรและเพิ่มความคิดเห็นที่อธิบายอัลกอริทึมการดำเนินการและโครงสร้างภาษา STL บางส่วน ฉันขอทราบทันทีว่าบล็อกที่เป็นปัญหานั้นมีเครื่องเสมือนที่รันโค้ดไบต์บางส่วนที่อยู่ในบล็อก DB100 ซึ่งเป็นเนื้อหาที่เราทราบ คำสั่งเครื่องเสมือนประกอบด้วยโค้ดปฏิบัติการ 1 ไบต์และอาร์กิวเมนต์จำนวน XNUMX ไบต์สำหรับแต่ละอาร์กิวเมนต์ คำแนะนำที่พิจารณาทั้งหมดมีสองข้อโต้แย้ง ฉันกำหนดค่าไว้ในความคิดเห็นเป็น 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 แบบแมนนวลจะถูกเปิดใช้งาน และจะสามารถระเบิดหรือยุบบอลลูนได้


นั่นคือทั้งหมด! Alexey แสดงให้เห็นถึงความรู้ระดับสูงที่คู่ควรกับนินจาอุตสาหกรรม :) เราส่งรางวัลที่น่าจดจำให้กับผู้ชนะ ขอบคุณมากสำหรับผู้เข้าร่วมทุกคน!

ที่มา: will.com

ซื้อโฮสติ้งที่เชื่อถือได้สำหรับไซต์ที่มีการป้องกัน DDoS เซิร์ฟเวอร์ VPS VDS 🔥 ซื้อบริการเว็บโฮสติ้งที่เชื่อถือได้ พร้อมระบบป้องกัน DDoS และเซิร์ฟเวอร์ VPS/VDS | ProHoster