في أعقاب النينجا الصناعية: كيف تم اختراق PLC في أيام الاختراق الإيجابية 9



في PHDays 9 ، عقدنا مسابقة لاختراق محطة ضخ الغاز - مسابقة النينجا الصناعية . كان هناك ثلاثة منصات في الموقع مع معايير أمان مختلفة (لا أمان ، أمان منخفض ، أمان عالي) تحاكي نفس العملية الصناعية: تم ضخ الهواء في البالون (ثم نزوله) تحت الضغط.

على الرغم من معايير السلامة المختلفة ، كانت أجهزة المدرجات هي نفسها: Siemens Simatic S7-300 PLC؛ زر التفجير في حالات الطوارئ وجهاز قياس الضغط (متصل بالمدخلات الرقمية PLC (DI)) ؛ صمامات الضخ والنزيف (متصلة بالمخرجات الرقمية لـ PLC (DO)) - انظر الشكل أدناه.



PLC ، اعتمادًا على قراءات الضغط ووفقًا لبرنامجها ، اتخذت قرارًا بشأن نفخ أو نفخ الكرة (فتح وإغلاق الصمامات المقابلة). ومع ذلك ، تم توفير وضع التحكم اليدوي في جميع المدرجات ، مما مكن من التحكم في حالات الصمام دون أي قيود.

تميزت المدرجات بتعقيد تمكين هذا الوضع: على الحامل غير المحمي ، كان من الأسهل القيام به ، ولكن في الحامل العالي الأمان ، كان الأمر أكثر صعوبة.

خلال يومين ، تم حل خمس من المشكلات الست ؛ حصل الفائز بالمركز الأول على 233 نقطة (أمضى أسبوعًا في التحضير للمسابقة). ثلاثة فائزين: أضع - a1exdandy ، II - Rubikoid ، III - Ze.

ومع ذلك ، خلال PHDays ، لم يتمكن أي من المشاركين من التغلب على المواقف الثلاثة ، لذلك قررنا إجراء مسابقة عبر الإنترنت ونشرنا أصعب مهمة في أوائل يونيو. كان على المشاركين إتمام المهمة في غضون شهر ، والعثور على العلم ، ووصف الحل بالتفصيل وبشكل مثير للاهتمام.

تحت الخفض ، ننشر تحليلًا لأفضل حل للمهمة المرسلة على مدار الشهر ، تم العثور عليه بواسطة Alexey Kovrizhnykh (a1exdandy) من شركة Digital Security ، التي احتلت المركز الأول في المسابقة خلال PHDays. أدناه نحن نقدم نصها مع تعليقاتنا.

التحليل الأولي


لذلك ، في المهمة كان هناك أرشيف مع الملفات:

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

يحتوي ملف hints.txt على المعلومات والنصائح الضرورية لحل المهمة. وهنا محتوياته:

  1. أخبرني بتروفيتش أمس أنه من PlcSim يمكنك تنزيل القطع في Step7.
  2. في المنصة ، تم استخدام PLCs من سلسلة Siemens Simatic S7-300.
  3. 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: 0102 7702 0401 0206 0100 0103 0102 0a02 ..w ............. 00000030: 0501 0202 1602 0501 0206 0100 0104 0102 ................ 00000040: 7502 0401 0206 0100 0105 0102 0a02 0501 u ............... 00000050: 0202 1602 0501 0206 0100 0106 0102 3402 .............. 4.  00000060: 0401 0206 0100 0107 0102 2602 0501 0202 .......... & ..... 00000070: 4c02 0501 0206 0100 0108 0102 3302 0401 L ........... 3. .. 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 ...... m. ........ 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 .... 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 ('\x1e\x00', '\x1e\x01'): # 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 ، الذي اكتشفناه سابقًا. قبل الكتل ، على ما يبدو ، يتم كتابة حجم الكتلة في شكل قيمة endian صغيرة 4 بايت.



بعد تلقينا معلومات حول بنية ملفات 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) 

لقد مر أليكسي بمزيد من التعقيد ، لكنه لا يزال في الطريق الصحيح. افترضنا أن المشاركين سوف يستخدمون برنامج NetToPlcSim حتى يتمكن PlcSim من التواصل عبر الشبكة ، وتحميل الكتل في PlcSim عبر Snap7 ، ثم تنزيل هذه الكتل كمشروع من PlcSim باستخدام بيئة التطوير.

عن طريق فتح الملف الناتج في S7-PlcSim ، يمكنك قراءة الكتل المكتوبة باستخدام Simatic Manager. يتم تسجيل وظائف إدارة الجهاز الرئيسية في الكتلة FC1. يجذب المتغير # TEMP0 اهتمامًا خاصًا ، عند تشغيله ، يبدو أن عنصر التحكم PLC في وضع التشغيل اليدوي استنادًا إلى قيم ذاكرة البت M2.2 و M2.3. يتم تعيين # TEMP0 بواسطة FC3.



لحل المشكلة ، من الضروري تحليل وظيفة FC3 وفهم ما يجب القيام به حتى تقوم بإرجاع وحدة منطقية.

تم ترتيب كتل معالجة الإشارات PLC في حامل الأمان المنخفض في موقع المسابقة بنفس الطريقة ، ولكن لتعيين قيمة المتغير # TEMP0 ، كان يكفي كتابة خط طريقي النينجا إلى كتلة DB1. تم التحقق من القيمة في الكتلة بشكل واضح ولا يتطلب معرفة متعمقة بلغة برمجة الكتلة. من الواضح ، على مستوى الأمان العالي ، سيكون من الصعب للغاية تحقيق التحكم اليدوي وتحتاج إلى فهم تعقيدات لغة STL (إحدى طرق برمجة S7 PLC).

عكس كتلة FC3


محتويات كتلة FC3 في تمثيل STL:
  LB#16#0 T #TEMP13 T #TEMP15 LP#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 LW#16#0 <>I JC M00d LP#DBX 0.0 LAR1 M00d: LB [AR1,P#0.0] T #TEMP5 LW#16#1 ==I JC M007 L #TEMP5 LW#16#2 ==I JC M008 L #TEMP5 LW#16#3 ==I JC M00f L #TEMP5 LW#16#4 ==I JC M00e L #TEMP5 LW#16#5 ==I JC M011 L #TEMP5 LW#16#6 ==I JC M012 JU M010 M007: +AR1 P#1.0 LP#DBX 0.0 LAR2 LB [AR1,P#0.0] LC#8 *I +AR2 +AR1 P#1.0 LB [AR1,P#0.0] JL M003 JU M001 JU M002 JU M004 M003: JU M005 M001: OPN DB 101 LB [AR2,P#0.0] T #TEMP0 JU M006 M002: OPN DB 101 LB [AR2,P#0.0] T #TEMP1 JU M006 M004: OPN DB 101 LB [AR2,P#0.0] T #TEMP2 JU M006 M00f: +AR1 P#1.0 LB [AR1,P#0.0] LC#8 *IT #TEMP11 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 TAR1 #TEMP4 OPN DB 101 LP#DBX 0.0 LAR1 L #TEMP11 +AR1 LAR2 #TEMP9 LB [AR2,P#0.0] TB [AR1,P#0.0] L #TEMP4 LAR1 JU M006 M008: +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP3 +AR1 P#1.0 LB [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 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] AW INVI T #TEMP12 LB [AR1,P#0.0] LB [AR2,P#0.0] OW L #TEMP12 AW TB [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 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] -ITB [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 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 TAR1 #TEMP4 LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] ==I JCN M013 JU M014 M013: LP#DBX 0.0 LAR1 T #TEMP4 LB#16#0 T #TEMP6 JU M006 M014: L #TEMP4 LAR1 L #TEMP13 LL#1 +IT #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: LP#DBX 0.0 LAR1 L 0 T #TEMP6 TAR1 #TEMP4 M005: TAR1 #TEMP4 CLR = #TEMP16 L #TEMP13 LL#20 ==IS #TEMP16 L #TEMP15 ==IA #TEMP16 JC M017 L #TEMP13 LL#20 <IS #TEMP16 L #TEMP15 ==IA #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) ل S7-300 و S7-400 البرمجة . هنا سأقدم الرمز نفسه بعد المعالجة - إعادة تسمية الملصقات والمتغيرات وإضافة تعليقات تصف خوارزمية العمل وبعض التركيبات للغة STL. ألاحظ على الفور أنه في الكتلة قيد النظر ، يتم تنفيذ جهاز افتراضي ينفذ بعض الكود الثنائي الموجود في كتلة DB100 ، التي نعرف محتوياتها. إرشادات الجهاز الظاهري هي 1 بايت من رمز التشغيل وبايتات الوسائط ، بايت واحد لكل وسيطة. كل التعليمات التي تمت مراجعتها لها حجة ، قمت بتحديد قيمها في التعليقات كـ X و Y.

بعد معالجة الرمز
]
 #    LB#16#0 T #CHECK_N #     T #COUNTER_N #     LP#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 LW#16#0 <>I JC M00d LP#DBX 0.0 LAR1 #  switch - case     M00d: LB [AR1,P#0.0] T #OPCODE LW#16#1 ==I JC OPCODE_1 L #OPCODE LW#16#2 ==I JC OPCODE_2 L #OPCODE LW#16#3 ==I JC OPCODE_3 L #OPCODE LW#16#4 ==I JC OPCODE_4 L #OPCODE LW#16#5 ==I JC OPCODE_5 L #OPCODE LW#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 LP#DBX 0.0 LAR2 LB [AR1,P#0.0] #   X (  DB101) LC#8 *I +AR2 +AR1 P#1.0 LB [AR1,P#0.0] #   Y ( ) JL M003 #  switch - case    Y JU M001 #      . JU M002 #       JU M004 #      M003: JU LOOPEND M001: OPN DB 101 LB [AR2,P#0.0] T #REG0 #   DB101[X]  REG[0] JU PRE_LOOPEND M002: OPN DB 101 LB [AR2,P#0.0] T #REG1 #   DB101[X]  REG[1] JU PRE_LOOPEND M004: OPN DB 101 LB [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 LB [AR1,P#0.0] T #TEMP3 +AR1 P#1.0 LB [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 LB [AR1,P#0.0] T #TEMP7 #   - X LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 # REG[X] LAR2 #TEMP10 # REG[Y] LB [AR1,P#0.0] LB [AR2,P#0.0] AW INVI T #TEMP12 # ~(REG[Y] & REG[X]) LB [AR1,P#0.0] LB [AR2,P#0.0] OW L #TEMP12 AW # (~(REG[Y] & REG[X])) & (REG[Y] | REG[X]) -     TB [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 LB [AR1,P#0.0] T #TEMP7 LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 LAR2 #TEMP10 LB [AR1,P#0.0] LB [AR2,P#0.0] -I # ACCU1 = ACCU2 - ACCU1, REG[X] - REG[Y] TB [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 LB [AR1,P#0.0] T #TEMP7 # REG[X] LP#M 100.0 LAR2 L #TEMP7 LC#8 *I +AR2 TAR2 #TEMP9 # REG[X] +AR1 P#1.0 LB [AR1,P#0.0] T #TEMP8 LP#M 100.0 LAR2 L #TEMP8 LC#8 *I +AR2 TAR2 #TEMP10 # REG[Y] TAR1 #POINTER LAR1 #TEMP9 # REG[Y] LAR2 #TEMP10 # REG[X] LB [AR1,P#0.0] LB [AR2,P#0.0] ==I JCN M013 JU M014 M013: LP#DBX 0.0 LAR1 T #POINTER LB#16#0 T #TEMP6 JU PRE_LOOPEND M014: L #POINTER LAR1 #   #CHECK_N L #CHECK_N LL#1 +IT #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: LP#DBX 0.0 LAR1 L 0 T #TEMP6 TAR1 #POINTER LOOPEND: TAR1 #POINTER CLR = #TEMP16 L #CHECK_N LL#20 ==IS #TEMP16 L #COUNTER_N ==IA #TEMP16 #   ,  #CHECK_N == #COUNTER_N == 20 JC GOOD L #CHECK_N LL#20 <IS #TEMP16 L #COUNTER_N ==IA #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 وسيكون من الممكن تفجير البالون أو تفريغه.

هذا كل شئ! أظهر أليكسي مستوى عالًا من المعرفة يستحق النينجا الصناعي :) لقد أرسلنا جوائز لا تنسى للفائز. شكرا جزيلا لجميع المشاركين!

Source: https://habr.com/ru/post/ar460615/


All Articles