شعار زيفيرنت

كيفية استخدام الهندسة العكسية الديناميكية للأجهزة المدمجة | TechTarget

التاريخ:

ترافق انتشار إنترنت الأشياء مع انتشار نقاط الضعف الأمنية. إذا تُرك المهاجمون الخبثاء دون رادع ، فيمكنهم استخدام نقاط الضعف هذه للتسلل إلى أنظمة المنظمات.

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

لجعل فرق الأمان على دراية باختبار قلم الجهاز المضمن ، كتب جان جورج فالي ، نائب الرئيس الأول في Kroll ، وهي شركة استشارية للمخاطر السيبرانية والخدمات المالية ، الاختراق العملي للأجهزة: تعلم تقنيات الهجوم والدفاع للأنظمة المضمنة في إنترنت الأشياء والأجهزة الأخرى.

في المقتطف التالي من الفصل 10 ، يوضح Valle بالتفصيل كيف يمكن لمختبري القلم استخدام الهندسة العكسية الديناميكية لمعرفة كيف تتصرف التعليمات البرمجية أثناء التنفيذ على الأجهزة المضمنة. يقدم Valle مثالاً على الهندسة العكسية الديناميكية ليُظهر لمختبري القلم التحديات التي قد تنشأ أثناء مراقبة سلوك الكود.

ملاحظة المحرر: المقتطف التالي مأخوذ من إصدار الوصول المبكر من عملية Pentesting للأجهزة ، الإصدار الثاني وقابلة للتغيير.

استخدام الهندسة العكسية الديناميكية - مثال

لقد أعددت متغيرًا من المثال السابق سيشكل لنا بعض التحديات. سأوضح لك كيفية التغلب على هذه التحديات بشكل ثابت وديناميكي حتى تتمكن من مقارنة مقدار الجهد المطلوب في كلتا الحالتين.

القاعدة العامة عند مقارنة المناهج الديناميكية والثابتة هي أن 99٪ من الوقت ، الأساليب الديناميكية أسهل فقط ويجب إعطاؤها الأولوية إذا كان ذلك ممكنًا (لا تنس أنك قد لا تتمكن من الوصول إلى JTAG / SWD أو غير ذلك على بروتوكولات التصحيح على الرقاقة).

في هذا القسم ، سوف نتعلم أيضًا كيفية كسر المكان الذي نريده ، وفحص الذاكرة باستخدام GDB ، وكل هذه الأشياء الجيدة!

يوجد البرنامج الهدف هنا في المجلد الذي نسخته ، في مجلد ch12.

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

أول تفتيش غيدرة

للوهلة الأولى ، تبدو الوظيفة الرئيسية مشابهة جدًا للوظيفة الرئيسية في الفصل السابق. يمكننا العثور على مرجع الوظيفة الرئيسية من خلال البحث في سلسلة كلمة المرور تمامًا كما في الفصل السابق والبحث في تحليل هيكلها.

سأدعك تعمل على المهارات التي اكتسبتها في الفصل السابق للعثور على الوظائف المختلفة. في هذا الملف القابل للتنفيذ ، ستجد ما يلي مرة أخرى:

  • A كبيرة احيانا صحيح) حلقة تعمل بمثابة حلقة الحدث الرئيسية وتومض مؤشر LED للمخطط أثناء العمل على إدخال كلمة مرور
  • وظيفة لتهيئة الساعة
  • وظيفة لتهيئة GPIOs
  • وظيفة لتهيئة UART
  • يتم حساب القيمة التي تعتمد على المعرف الفريد للشريحة مرة أخرى بنفس الطريقة تقريبًا (احسب هذه القيمة لشريحتك ولاحظ هذه القيمة أسفل)
  • وظيفة تتحقق من صحة كلمة المرور (قبل ملف if يؤدي ذلك إلى تشغيل طباعة ملفات فزت or لا)
  • تقوم الدالة بفك تشفير السلسلة الفائزة إذا قامت دالة التحقق من الصحة بإرجاع (uint16_t) 0 .

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

الآن ، دعنا ننتقل إلى طرق متعددة لتجاوز التحقق من صحة كلمة المرور من خلال التفاعل الديناميكي مع النظام. سننتقل من الأكثر تعقيدًا إلى الأبسط من أجل الحفاظ على تركيزك واكتساب المعرفة الفنية (إذا كنت مثلي ، إذا كانت هناك طريقة سهلة لتجاوز شيء ما ، فلماذا تذهب إلى الطريق الصعب؟).

عكس كلمة المرور المتوقعة

أول شيء سنفعله هو محاولة معرفة كيفية التحقق من صحة كلمة المرور لفهم كيفية إنشاء كلمة مرور تجتاز الاختبارات.

دعنا نلقي نظرة على كود C المكافئ لوظيفة التحقق التي يتم إخراجها بواسطة Ghidra:

Screenshot of Ghidra output of decompiled code
الشكل 12.2 - وظيفة التحقق من الصحة decompiled في الواقع لا تفعل ما تعتقده!

هام ... هذا لا يفعل شيئًا بشكل مباشر مع المعلمات. هذا هو نسخ محتوى 0x47 (71) مجموعة ثابتة طويلة من البايت لذاكرة الوصول العشوائي (وليس ذلك) ثم تسميها كدالة.

هذا غريب.

أو هو؟

هذه تقنية شائعة جدًا لتمويه الشفرة (بالطبع ، نسخة بسيطة جدًا منها). إذا لم يكن هناك إصدار واضح من كود التشغيل في ملف .bin (وبالتالي ليس في وميض MCU) ، فإن أداة الهندسة العكسية مثل Ghidra لا يمكنها اكتشاف أنها رمز! هنا ، لدينا طريقتان محتملتان:

  • إما أن نستخرج محتوى المخزن المؤقت يدويًا من ملف .bin ، ونفك تشفيره (هنا ، لا يكون التشفير مجرد بايت بالبايت ، فهو تافه عن قصد) ، ويتم إلغاء تجميعه بواسطة Ghidra.
  • أو ، نظرًا لأن لدينا وصول JTAG إلى الشريحة ، يمكننا فقط وضع نقطة توقف على العنوان الصحيح في الذاكرة والسماح لـ MCU بالقيام بالعمل الشاق من أجلنا.

سأترك لك الحل الأول لتنفيذه كتدريب. يجب أن يستغرق الأمر أكثر أو أقل من 10 أسطر من كود Python أو C لمثل هذه المهمة البسيطة! تريد أن تكون هاكر؟ الإختراق بعيدا!

أنا؟ أنا رجل كسول. إذا كان بإمكان الكمبيوتر العمل من أجلي ، حسنًا ... فليكن! سأذهب للحل الثاني.

أولاً ، دعنا نطلق جلسة شاشة في محطة طرفية حتى نتمكن من إدخال كلمات المرور ونرى كيف تتفاعل:

شاشة / ديف / ttyUSB0 115200

دعنا نشغل OpenOCD و GDB في محطة ثانية ، كما فعلنا في بداية الفصل ، ودعنا ننتقل:

openocd -f ./ftdi2232h.cfg.tcl -f ./clone_CSK.cfg & gdb-multiarch -x ./gdbinit
# openocd إطلاق
[...]
توقف الهدف بسبب طلب التصحيح ، الوضع الحالي: الموضوع xPSR: 0x01000000 الكمبيوتر: 0x080013b8 msp: 0x20005000
[...]

و… و اللعنة! لا يعيدني التحكم! لا مشكلة إذا حدث ذلك لك - قليلاً CTRL + C سيمنحك التحكم مرة أخرى على الفور:

^C
تلقى البرنامج إشارة SIGINT ، مقاطعة.
0x080003aa في ؟؟ ()
(جدب)

لدينا بعد CTRL + C (^ ج) ، جدب يخبرنا أن الإعدام توقف عند العنوان 0x080003aa في وظيفة غير معروفة (؟؟).

اعتمادًا على حالتك المحددة ، قد تنفصل عن عنوان آخر.

لا داعي للذعر - ارتدي قبعة التفكير الخاصة بك وخذ المنشفة معك (دائمًا).

هذه ليست مشكلة. من المحتمل أنك ستكسر بالقرب من هذا العنوان نظرًا لأنه في حلقة الانتظار التي تومض مؤشر LED ، في انتظار تلقي كلمة مرور على الواجهة التسلسلية.

أول الأشياء أولاً ، دعنا نلقي نظرة على سجلاتنا:

(gdb) ir
ص0x0
r1 0x8001a1d 134224413
r2 0x5b8d7f 5999999
r3 0x335d7 210391
r4 0x20004f88 536891272
r5 0x8001a74 134224500
ص6x0
r7 0x20004f88 536891272
ص8x0
ص9x0
ص10x0
ص11x0
آر 12 0xf 15
sp 0x20004f88 0x20004f88
لير 0x80003bf 134218687
الكمبيوتر 0x80003aa 0x80003aa
xPSR 0x81000000 -2130706432
msp 0x20004f88 0x20004f88
[...]

نحن نرى أن جهاز الكمبيوتر هو بالفعل حيث من المفترض أن يكون ، كل شيء يبدو على ما يرام ورائع. لذا ، دعنا الآن نحاول إدخال كلمة مرور.

و ... لا شيء يعمل على نافذة الواجهة التسلسلية! Thinking hat on… GDB هو في الواقع يمنع تنفيذ الكود؛ لن تتفاعل الواجهة التسلسلية مع مدخلاتك. هذا امر طبيعي.

لذا ، دعنا نسمح لها بالاستمرار (استمر or c في ال جدب نافذة) ومعرفة ما إذا كان المسلسل يعمل الآن. نعم إنها كذلك. دعنا نكسرها مرة أخرى ونضع نقطة توقف على عنوان وظيفة التحقق من صحة كلمة المرور ، أليس كذلك؟

في Ghidra ، يمكننا أن نرى أن عنوان التعليمات الأولى للوظيفة هو 0x080002b0:

Screenshot of finding a function address in Ghidra
الشكل 12.3 - البحث عن عنوان دالة في Ghidra

دعونا نضع نقطة توقف هناك ، دعنا جدب استأنف التنفيذ ، وأدخل كلمة مرور وهمية:

(جي دي بي) ب * 0x080002b0
#1
نقطة التوقف 1 عند 0x80002b0
#2
(gdb) ج
#3
استمرار.
ملاحظة: استخدام نقاط توقف الأجهزة تلقائيًا لعناوين القراءة فقط.
#4
[إدخال "aaa" في وحدة التحكم التسلسلية وإدخال]
نقطة التوقف 1 ، 0x080002b0 في ؟؟ ()
#5
(جدب)

دعونا نحلل ما يلي:

  • ب * 0x080002b0 يسأل جدب لوضع نقطة توقف على التعليمات المخزنة في العنوان 0x080002b0. تحقق من المؤشرات الخاصة بك.
  • جدب يقول لي ، حسنًا ، لقد وضعت نقطة توقف هناك.
  • أكمل الإعدام من فضلك يا عزيزتي جدب وتقول إنها سعيدة بفعل ذلك.
  • لكنه يخطر لي أنه لا يمكنه الكتابة في العنوان 0x080002b0 (إنه في صورة وميض ولا يمكن كتابته على هذا النحو تمامًا ؛ يجب إلغاء قفله وكتابته قطعة تلو الأخرى). من أجل تجنب القيام بالكثير ذهابًا وإيابًا ، تأتي رقائق ARM مع بعض أنظمة تصحيح الأخطاء الداخلية التي تسمح لها بالانهيار عندما يصل الكمبيوتر إلى عناوين محددة لا يمكن الكتابة إليها بسهولة).
  • بام! تم الوصول إلى نقطة التوقف! تم إيقاف التنفيذ بعد أن أدخل كلمة مرور وهمية.

حسنًا ، الآن ماذا يمكننا أن نفعل بهذا؟

أول الأشياء أولاً ، إذا كنت تتذكر رمز وظيفة التحقق من الصحة ، فسيتم تمرير وسيطاتها مباشرةً إلى الشفرة التي تم فك ترميزها. دعنا نلقي نظرة على ما يمكن أن تكون عليه (تذكر اصطلاح استدعاء الدوال: الوسائط موجودة في r0-3):

(gdb) p / x $ r0
2 دولار = 0x20000028
(gdb) p / x $ r1
3 دولار = 0x2169

الحجة الأولى هي شيء ما في ذاكرة الوصول العشوائي ، والثانية هي نوع من القيمة. (هذه هي قيمة UUID المحولة لشريحتك ، والتي سجلتها ، أليس كذلك؟)

الآن ، ما هو المخزن في هذا العنوان الأول؟ دعنا نفحصها:

(gdb) x / x 0x20000028
0x20000028: 0x00616161
(gdb) x / s 0x20000028
0x20000028: "aaa"

آه! آه! آه! (انظر ماذا فعلت هناك؟) هذه هي كلمة المرور الخاصة بنا. يرجى ملاحظة استخدام معدِّل التنسيق للأمر x.

لذلك ، هذا متوقع.

الآن دعونا ننظر في الشفرة التي تم فك شفرتها.

يخبرنا Ghidra أن التعليمات التي تتبع حلقات فك التشفير موجودة في 0x080002f0. دعنا نكسر هناك:

(جي دي بي) ب * 0x080002f0
نقطة الإيقاف 2 عند 0x80002f0
(gdb) ج
استمرار.
نقطة التوقف 2 ، 0x080002f0 في ؟؟ ()
(gdb) ج
(gdb) x / 4i دولار للكمبيوتر
=> 0x80002f0: movs r0 ، # 0
   0x80002f2: بليكس r3
   0x80002f4: mov r3 ، r0
   0x80002f6: mov r0 ، r3

إذن ، عنوان الشفرة التي تم فك تشفيرها في r3. رأينا كان العازلة 0x47 (71) طويلة. نحن في وضع الإبهام (لذا حجم التعليمات 2). يجب أن يكون هذا 47/2: حوالي 35 تعليمات. الجزء الأخير من العنوان هو للوضع ؛ يمكننا التخلص من ذلك:

(gdb) x / 35i ($ r3 & (~ 1))
   0x20000128: دفع {r4، r5، r6، r7، lr}
   0x2000012a: eors r4 ، r4
   0x2000012c: eors r3 ، r3
   0x2000012e: eors r5، r5
   0x20000130: ldrb r5، [r1، r4]
   0x20000132: موف r8 ، r5
   0x20000134: موف r6 ، r8
   0x20000136: lsrs r6، r6، # 4
   0x20000138: lsls r5، r5، # 4
   0x2000013a: أورس r5 ، r6
   0x2000013c: movs r6، # 255 ؛ 0xff
   0x2000013e: ands r5، r6
   0x20000140: movs r6 ، # 15
   0x20000142: موف r8 ، r4
   0x20000144: موف r7 ، r8
   0x20000146: ands r7، r6
   0x20000148: إضافة r6 ، جهاز كمبيوتر ، # 16 ؛ (adr r6، 0x2000015c) # 1
   0x2000014a: ldrb r6، [r6، r7]
   0x2000014c: eors r5 ، r6
   0x2000014e: يضيف r0 و r0 و r5
   0x20000150: يضيف r4 ، # 1
   0x20000152: ldrb r5، [r1، r4]
   0x20000154: cmp r5 ، r3
   0x20000156: بغت.ن 0x20000132
   0x20000158: eors r0 ، r2
   0x2000015a: pop {r4، r5، r6، r7، pc}
   0x2000015c: str r5، [r4، # 36] ؛ 0x24
   0x2000015e: ldrb r4، [r6، # 5]
   0x20000160: ldr r7، [r6، # 32]
   0x20000162: الغواصات r2 ، # 55 ؛ 0x37
   0x20000164: ldr r4، [r2، r5]
   0x20000166: ldr r5، [r1، # 100] ؛ 0x64
   0x20000168: أضف r3 ، r12
   0x2000016a: يضيف r4 ، # 68 ؛ 0x44
   0x2000016c: vqadd.u8 q0 ، q8 ،

وهذا أشبه ذلك! نرى مقدمة دالة عادية (حفظ السجلات داخل الوظيفة في المكدس) ، وبعض المعالجة ، وعودة دالة. لكن GDB يحذرنا من معلمات التعليمات غير القانونية (0x2000016c).

عند النظر إلى القائمة ، نرى أن GDB تشير إلى استخدام جزء من البيانات النسبية لجهاز الكمبيوتر:

رقم 1: علّق: adr r6، 0x2000015c)

غالبًا ما يستخدم هذا لتخزين البيانات في برنامج التجميع. ADR هي تعليمات زائفة تخبر المجمّع ، يرجى إضافة الإزاحة إلى ملصق (موضع مسمى) في الكود.

لنلقِ نظرة على ما يتم تخزينه هناك:

(gdb) x / 4wx 0x2000015c
0x2000015c: 0x79746265 0x3a376a37 0x6e4d5954 0x34444463
(gdb) x / s 0x2000015c
0x2000015c: "ebty7j7:TYMncDD4"

هذا بالفعل عبارة عن سلسلة يتم استخدامها في العملية بطريقة ما.

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

(gdb) عرض / x $ r0
1: / x $ r0 = 0x20000028
(gdb) عرض / x $ r1
2: / x $ r1 = 0x20000028
(gdb) عرض / x $ r2
3: / x $ r2 = 0x2169
(gdb) عرض / x $ r3
4: / x $ r3 = 0x20000129
(gdb) عرض / x $ r4
5: / x $ r4 = 0x20004f88
(gdb) عرض / x $ r5
6: / x $ r5 = 0x8001a74
(gdb) عرض / x $ r6
7: / x $ r6 = 0x0
(gdb) عرض / x $ r7
8: / x $ r7 = 0x20004f70
(gdb) عرض / x $ r8
9: / x $ r8 = 0x2
(gdb) ديسب / i $ الكمبيوتر
10: x / i $ جهاز كمبيوتر
=> 0x80002f0: movs r0 ، # 0
=> 0x80002f2: blx r3

الآن نحن جاهزون للاستخدام ستيبى (تعليمات الخطوة) لمعرفة ما يجري:

0x2000012b: eors r4 ، r4
0x2000012d: eors r3 ، r3
0x2000012f: eors r5 ، r5

هذه الأصفار r4, r3و r5 (س ^ س = 0):

0x20000130: ldrb r5، [r1، r4]
0x20000132: موف r8 ، r5
0x20000134: موف r6 ، r8

يؤدي هذا إلى تحميل الحرف الأول من سلسلة كلمة المرور بتنسيق r5 (r1 هو العنوان و r4 صفرية عند هذه النقطة) ونسخها إلى r8 و r6:

0x20000136: lsrs r6، r6، # 4
0x20000138: lsls r5، r5، # 4
0x2000013a: أورس r5 ، r6
0x2000013c: movs r6، # 255 ؛ 0xff
0x2000013e: ands r5، r6

هذه التحولات r6 4 بتات إلى اليمين ، r5 4 بتات إلى اليسار ، وتضع قيمة ORed فيها r4. ثم يخفي نتيجة ORed مع 0xff، تبادل 4 بت أقل و 4 بتات أعلى من حرف كلمة المرور وتنظيف البتات الزائدة!

0x20000140: movs r6 ، # 15
0x20000142: موف r8 ، r4
0x20000144: موف r7 ، r8
0x20000146: ands r7، r6

هذا يتحرك 15 بوصة r6، نسخ r4 in r8 و r7، والأقنعة r7 مع 15. لكن لماذا؟ عند هذه النقطة، r4 هو 0! يمكن استخدام هذا لاحقًا - لأننا رأينا ذلك r4 تم استخدامه كإزاحة عند تحميل حرف كلمة المرور ، r4 ربما يكون عداد! إذا كان هذا هو الحال ، فيمكن استخدام هذا الإخفاء كنوع من modulo ... (من الشائع جدًا استخدام إخفاء modulo بقوة اثنين -1):

0x20000148: إضافة r6 ، جهاز كمبيوتر ، # 16 ؛ (adr r6، 0x2000015c)
0x2000014a: ldrb r6، [r6، r7]

يؤدي هذا إلى تحميل الحرف الأول من السلسلة المخفية فيه r6 ويستخدم r7 وتعويض! r4 هو بالتأكيد عداد هنا و r7 نسخة معدلة منه. هذه طريقة برمجة نموذجية جدًا للتعامل مع هذا:

0x2000014c: eors r5 ، r6
0x2000014e: يضيف r0 و r0 و r5
0x20000150: يضيف r4 ، # 1

هذا هو XORing قيمة حرف كلمة المرور التي تم تبديلها مع الرتب الحالية للسلسلة الغريبة ، مضيفًا هذا إلى r0 وزيادة r4 عداد:

0x20000152: ldrb r5، [r1، r4]
0x20000154: cmp r5 ، r3
0x20000156: بغت.ن 0x20000132

يؤدي هذا إلى تحميل حرف كلمة مرور جديد بالتعويض الجديد r5. r3 هو 0 لذلك يتحقق cmp r5- r3 و انتظر … bgt.n؟ ما هذا؟ هل تتذكر ماذا تفعل عندما تكون لديك شكوك؟ اذهب لقراءة الوثائق هنا: https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/condition-codes-1-condition-flags-and-codes.

لذلك ، يقفز إذا r5 > r3. و r3 is 0، لذا؟ هذا اختبار لـ 0 سلسلة منتهية!

هذه هي حلقة منطق التحقق الرئيسية!

بمجرد الانتهاء من ذلك ، يقوم بما يلي:

0x20000158: eors r0 ، r2
0x2000015a: pop {r4، r5، r6، r7، pc}

إنه XORs هذا المجموع مع UUID اعتمادًا على القيمة التي يحسبها ، ويستعيد قيم سجل المتصل ، ويعيد هذه القيمة. يتحقق رمز C بعد ذلك مما إذا كانت هذه القيمة خالية لعرض السلسلة الفائزة بالفعل. نحتاج بعد ذلك فقط إلى ترتيبها بحيث يكون مجموعنا مساويًا لقيمة UUID التابعة حتى يصبح XOR فارغًا!

لدينا المنطق كله!

بقعة_صورة

أحدث المعلومات الاستخباراتية

بقعة_صورة