ندفع المعلمات إلى عمليات غير آمنة في رمز آمن

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

تنويه


قبل البدء في القصة ، أوصي بشدة بقراءة المنشور السابق حول StructLayout ، لأنه الأشياء المتفق عليها لن تتكرر هنا.

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

بعض المعلومات الأولية


قبل أن نبدأ ، دعنا نتذكر كيف يتم تحويل رمز C #.
لنأخذ مثالاً بسيطًا. دعني أذكرك أنه من أجل الاستمتاع بـ StructLayout ، أستخدم الأساليب الافتراضية فقط.

public class Helper { public virtual void Foo(int param) { } } public class Program { public void Main() { Helper helper = new Helper(); var param = 5; helper.Foo(param); } } 

لا يحتوي هذا الرمز على أي شيء معقد ، ولكن التعليمات التي تم إنشاؤها بواسطة JiT تحتوي على عدة نقاط رئيسية. أقترح تحليل جزء صغير فقط من التعليمات البرمجية التي تم إنشاؤها.

  1: mov dword [ebp-0x8], 0x5 2: mov ecx, [ebp-0xc] 3: mov edx, [ebp-0x8] 4: mov eax, [ecx] 5: mov eax, [eax+0x28] 6: call dword [eax+0x10] 

في هذا المثال الصغير ، يمكنك ملاحظة الاتصال السريع - اتفاقية بشأن تمرير المعلمات من خلال السجلات (أول معلمتين من اليسار إلى اليمين في سجلات ecx و edx) ، ويتم تمرير المعلمات المتبقية من اليمين إلى اليسار على المكدس. المعلمة الأولى (الضمنية) هي عنوان مثيل الفئة الذي يتم استدعاء الأسلوب عليه. يتم تمريره كأول معلمة ضمنية لكل طريقة مثيل. المعلمة الثانية هي متغير محلي من النوع int (في حالتنا).

لذا ، في السطر الأول نرى المتغير المحلي 5 ، لا يوجد شيء مثير للاهتمام هنا.
في السطر الثاني ، نقوم بنسخ عنوان مثيل المساعد في سجل ecx. هذا هو عنوان جدول الأسلوب نفسه.
يحتوي السطر الثالث على نسخ المتغير المحلي 5 إلى سجل edx
السطر الرابع ينسخ عنوان جدول الطريقة إلى سجل eax
يحتوي السطر الخامس على إزاحة تسجيل eax من 40 بايت وتحميل قيمة من الذاكرة على عنوان 40 بايت أكبر من عنوان جدول الطريقة: عنوان بداية الطرق في جدول الطريقة. (يحتوي جدول الطريقة على معلومات متنوعة تم تخزينها من قبل. تتضمن هذه المعلومات ، على سبيل المثال ، عنوان جدول أسلوب الفئة الأساسية وعنوان EEClass والأعلام المختلفة ، بما في ذلك علامة جامع البيانات المهملة وما إلى ذلك). وفقًا لذلك ، يتم الآن تخزين عنوان الطريقة الأولى من جدول الطريقة في سجل eax.
في السطر السادس ، يتم استدعاء الطريقة عند الإزاحة 16 من البداية ، أي الخامسة في جدول الطريقة. لماذا طريقتنا الوحيدة هي الخامسة؟ أذكرك أن الكائن يحتوي على 4 طرق افتراضية (ToString و Equals و GetHashCode و Finalise) ، والتي ، وفقًا لذلك ، ستكون في جميع الفئات.

دعنا ننتقل إلى الممارسة


حان الوقت لبدء مظاهرة صغيرة. أقترح هنا مثل هذا الفراغ (يشبه إلى حد كبير الفراغ من المقالة السابقة).

 [StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public Test1 Test1; [FieldOffset(0)] public Test2 Test2; } public class Test1 { public virtual int Useless(int param) { Console.WriteLine(param); return param; } } public class Test2 { public virtual int Useless() { return 888; } } public class Stub { public void Foo(int stub) { } } 

والأشياء التالية من الطريقة الرئيسية:

  class Program { static void Main(string[] args) { Test2 fake = new CustomStructWithLayout { Test2 = new Test2(), Test1 = new Test1() }.Test2; Stub bar = new Stub(); int param = 55555; bar.Foo(param); fake.Useless(); Console.Read(); } } 

كما قد تتخيل ، من تجربة المقالة السابقة ، سيتم استدعاء طريقة غير مفيدة (int j) من نوع Test1.

ولكن ماذا سيتم استنتاجه؟ أعتقد أن القارئ اليقظ قد أجاب بالفعل على هذا السؤال. يتم عرض 55555 على وحدة التحكم.

ولكن دعونا نلقي نظرة على أجزاء الكود الذي تم إنشاؤه.

  mov ecx, [ebp-0x20] mov edx, [ebp-0x10] cmp [ecx], ecx call Stub.Foo(Int32) nop mov ecx, [ebp-0x1c] mov eax, [ecx] mov eax, [eax+0x28] call dword [eax+0x10] 

أعتقد أنك تتعرف على نمط استدعاء الأسلوب الظاهري ، يبدأ بعد L00cc: nop. كما نرى ، في ecx ، من المتوقع كتابة عنوان المثيل الذي يتم استدعاء الطريقة عليه. لكن منذ ذلك الحين إذا قمنا باستدعاء طريقة مثل Test2 ، والتي لا تحتوي على معلمات ، فلن تتم كتابة أي شيء إلى edx. ومع ذلك ، قبل ذلك ، تم استدعاء الطريقة ، التي مرت المعلمة من خلال تسجيل edx ، على التوالي ، وظلت القيمة فيها. ويمكننا ملاحظته في نافذة الإخراج.

هناك فارق بسيط آخر مثير للاهتمام. استخدمت نوعًا محددًا على وجه التحديد. أقترح محاولة استبدال نوع المعلمة لطريقة Foo من نوع Stub بأي نوع مرجع ، على سبيل المثال ، سلسلة. لكن نوع المعلمة للأسلوب عديم الفائدة لم يتغير. أدناه يمكنك رؤية النتيجة على جهازي مع بعض العناصر التوضيحية: WinDBG و Calculator :)


صورة قابلة للنقر

تعرض نافذة الإخراج عنوان نوع المرجع في نظام الأرقام العشري

الملخص


لقد قاموا بتجديد معرفة طرق الاتصال باستخدام اصطلاح fastcall واستخدموا على الفور سجل edx الرائع لتمرير طرق المعلمة 2 في كل مرة. كما أنهم لم يهتموا بكل الأنواع وتذكروا أن كل ما يوجد فقط من وحدات البايت يتلقى عنوان الكائن بسهولة دون استخدام مؤشرات ورمز غير آمن. علاوة على ذلك ، أخطط لاستخدام العنوان المستلم لأغراض غير قابلة للتطبيق!

شكرا لكم على اهتمامكم!

يمكن العثور على كود PS C # هنا

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


All Articles