سلسلة من البرامج المتزايدة التعقيد التي توضح ربط الوظائف على نظام التشغيل Windows 64 بت.
لقد كتبت هذه البرامج بينما كنت أعلم نفسي كيفية عمل وظيفة الربط. قد تكون مفيدة للأشخاص الآخرين الذين يحاولون فعل الشيء نفسه (أو في المستقبل بعد أن أنسى كيف يعمل كل هذا مرة أخرى). من المفترض أن يتم النظر إليها بالترتيب. في المرة الأولى التي يتم فيها استخدام دالة في برنامج، سيتم تضمينها في ملف .cpp لهذا البرنامج المحدد، مع أن يكون اسمها مسبوقًا بشرطة سفلية. أي أمثلة لاحقة تستخدم نفس الوظيفة ستستخدم نسخة تلك الوظيفة المضمنة في Hooking_common.h، لتقليل تكرار التعليمات البرمجية والحفاظ على أمثلة البرامج اللاحقة صغيرة بما يكفي لتكون قابلة للقراءة بسهولة.
لقد قمت ببعض العمل لتنظيفها، لكن الأمثلة اللاحقة لا تزال فوضوية بعض الشيء. من المهم ملاحظة أن النتيجة النهائية في هذا الريبو ليست كافية لكتابة مكتبة ربط كاملة الميزات، ولكنها كافية للبدء في هذا المسار.
في وقت التشغيل، قد تعتمد بعض المشاريع على مشاريع أخرى قيد الإنشاء (يتطلب حقن ملف dll إنشاء ملف dll)، أو تشغيله في نفس الوقت (يتطلب ربط البرنامج المستهدف أن يكون قيد التشغيل بالفعل).
تم إنشاء جميع الأمثلة باستخدام Visual Studio 2019 (v142) مع Windows SDK 10.0.17763.0. لا أعتقد أن هناك أي شيء يعتمد على إصدار VS أو SDK، لكنني أدرجه هنا في حالة حدوث ذلك. من المؤكد تقريبًا أن هناك بعض الأشياء الخاصة بـ MSVC.
أخيرًا، المثال الأخير للترامبولين يقوم بتثبيت خطاف في mspaint. أفترض أنه في مرحلة ما في المستقبل، سيؤدي تحديث mspaint إلى تعطل هذا المثال. في وقت كتابة هذا التقرير، كان الإصدار الحالي من mspaint هو 1909 (OS Build 18363.1016).
تنقسم الأمثلة إلى فئتين: تلك التي تستخدم الترامبولين، وتلك التي لا تستخدمها. توجد الأمثلة غير الترامبولين فقط لتوضيح إعادة توجيه تدفق البرنامج من وظيفة إلى أخرى في مواقف مختلفة. يعد بناء الترامبولين أمرًا معقدًا، وعندما كنت أحاول معرفة كيفية عمل وظيفة التثبيت، كان من المفيد للغاية أن أبدأ ببناء أمثلة غير الترامبولين أولاً. بالإضافة إلى ذلك، هناك 4 "برامج مستهدفة" يتم استخدامها بواسطة الأمثلة التي تريد توضيح كيفية تثبيت الخطافات في عمليات مختلفة (قيد التشغيل بالفعل).
معظم هذه الأمثلة تتسرب من الذاكرة المتعلقة بالخطافات. لا أهتم حقًا، لأن هذه الأمثلة هي فقط لتوضيح مفهوم الربط، ولأن هذه التخصيصات "المتسربة" يجب أن تكون موجودة حتى إنهاء البرنامج على أي حال.
في حين أنه لا يبدو أن هناك الكثير من المصطلحات القياسية لتقنيات ربط الوظائف، فإن التعليمات البرمجية (والملفات التمهيدية) في هذا المستودع تستخدم المصطلحات التالية:
نظرًا لأن هذه الأمثلة لا تنشئ ترامبولين عند تثبيت خطافاتها، فأنا أعتقد أن هذه الوظائف توضح التثبيت "المدمر"، حيث أن الوظيفة الأصلية غير قابلة للاستخدام تمامًا بعد ربطها.
مثال صغير على الكتابة فوق بايتات البداية لوظيفة ما باستخدام تعليمات القفز التي تعيد توجيه تدفق البرنامج إلى وظيفة مختلفة داخل نفس البرنامج. نظرًا لعدم إنشاء ترامبولين، فإن هذه العملية مدمرة، ولم تعد الوظيفة الأصلية قابلة للاستدعاء. هذا هو المثال الوحيد 32 بت في المستودع.
نسخة 64 بت من المثال السابق. في تطبيقات 64 بت، يمكن وضع الوظائف بعيدًا بما يكفي في الذاكرة بحيث لا يمكن الوصول إليها عبر تعليمات الانتقال النسبي 32 بت. نظرًا لعدم وجود تعليمات انتقال نسبي 64 بت، يقوم هذا البرنامج أولاً بإنشاء وظيفة "ترحيل"، والتي تحتوي على بايتات لتعليمات jmp مطلقة يمكن أن تصل إلى أي مكان في الذاكرة (وتنتقل إلى وظيفة الحمولة النافعة). تنتقل القفزة 32 بت التي تم تثبيتها في الوظيفة المستهدفة إلى وظيفة الترحيل هذه، بدلاً من الانتقال مباشرة إلى الحمولة.
يوفر مثالاً لاستخدام التقنيات من المشروع السابق لربط وظيفة عضو، بدلاً من وظيفة مجانية.
يختلف هذا البرنامج قليلاً عن الأمثلة السابقة، فهو يوضح كيفية تثبيت خطاف في وظيفة عضو افتراضي عن طريق الحصول على عنوان تلك الوظيفة من خلال جدول vtable الخاص بالكائن. لا توجد أمثلة أخرى تتناول الوظائف الافتراضية، لكنني أعتقد أنه من المثير للاهتمام أن ندرجها هنا.
أبسط مثال على تثبيت الخطاف في عملية جارية أخرى. يستخدم هذا المثال مكتبة DbgHelp لتحديد موقع وظيفة في العمليات المستهدفة (A - الهدف مع وظيفة مجانية) حسب اسم السلسلة. وهذا ممكن فقط لأن البرنامج المستهدف تم إنشاؤه مع تمكين رموز التصحيح. على الرغم من بساطة هذا المثال، إلا أنه أطول قليلاً من البرامج السابقة نظرًا للعدد الكبير من الوظائف الجديدة التي يقدمها (لتحديد موقع عملية عن بعد ومعالجتها).
يوضح هذا المثال كيفية ربط دالة قامت عملية أخرى باستيرادها من ملف dll. هناك بعض الفروق الدقيقة حول كيفية الحصول على عنوان دالة dll في عملية عن بعد نظرًا لكيفية عمل ASLR، وهو ما هو موضح هنا. بخلاف ذلك، هذا المثال مطابق تقريبًا للمثال السابق.
يوضح هذا المثال كيفية تثبيت خطاف في وظيفة لم يتم استيرادها بواسطة ملف dll، وهي غير موجودة في جدول الرموز (من المحتمل أن العملية البعيدة لا تحتوي على رموز تصحيح). هذا يعني أنه لا توجد طريقة (سهلة) للعثور على الوظيفة المستهدفة حسب اسم السلسلة. بدلاً من ذلك، يفترض هذا المثال أنك استخدمت أداة فك التجميع مثل x64dbg للحصول على العنوان الظاهري النسبي (RVA) للوظيفة التي تريد ربطها. يستخدم هذا البرنامج RVA لتثبيت الخطاف.
على غرار ما ورد أعلاه، باستثناء أن هذا المثال يستخدم حقن dll لتثبيت وظيفة الحمولة بدلاً من كتابة بايتات التعليمات البرمجية الأولية للجهاز. يعد التعامل مع هذا أسهل كثيرًا، حيث يمكن كتابة حمولاتك بلغة C++ مرة أخرى. الحمولة لهذا المثال موجودة في المشروع 08B-DLL-Payload.
تقوم الأمثلة التالية بتثبيت الترامبولين عند التثبيت، مما يعني أن البرنامج لا يزال بإمكانه تنفيذ المنطق في الوظيفة المستهدفة بعد تثبيت الخطاف. نظرًا لأن تثبيت الخطاف يحل محل أول 5 بايتات على الأقل في الوظيفة المستهدفة، فسيتم نقل التعليمات الموجودة في هذه البايتات الخمسة إلى وظيفة الترامبولين. وبالتالي، فإن استدعاء وظيفة الترامبولين ينفذ بشكل فعال المنطق الأصلي للوظيفة المستهدفة.
ما يعادل تركيب الترامبولين للمثال رقم 2. هذا المثال غريب بعض الشيء لأنني أردت أن أوضح كيفية إنشاء الترامبولين دون الحاجة إلى استخدام محرك التفكيك. في هذه الحالة، تم إنشاء الوظيفة المستهدفة بحيث تحتوي على تعليمات معروفة بـ 5 بايت في البداية، لذلك يمكننا فقط نسخ البايتات الخمس الأولى من تلك الوظيفة إلى وظيفة الترامبولين. وهذا يعني أن إنشاء الترامبولين أمر سهل حقًا، لأننا نعلم حجمه الدقيق وأنه لا يستخدم أي عنوان نسبي يحتاج إلى إصلاح. إذا كنت تكتب ترامبولين لحالة استخدام محددة حقًا، فمن المحتمل أن تفلت من إجراء تغيير في هذا الأمر.
يُظهر هذا المثال سيناريو مشابهًا للسيناريو السابق، باستثناء أنني أستخدم هذه المرة أداة فك التجميع (كابستون) للحصول على البايتات التي نحتاجها لسرقة الوظيفة المستهدفة. يسمح هذا باستخدام رمز الربط في أي وظيفة، وليس فقط تلك التي نعرف أنها ستكون حالات سهلة. في الواقع هناك الكثير مما يحدث في هذا المثال، لأنه ينتقل من الخطاف المستهدف (مثل الخطاف السابق) إلى بناء وظيفة ربط عامة. يتعين على الترامبولين تحويل المكالمات/القفزات النسبية إلى تعليمات تستخدم العناوين المطلقة، مما يزيد الأمور تعقيدًا. هذا ليس مثالًا مصقولًا بنسبة 100% للربط العام أيضًا، وسيفشل مع تعليمات الحلقة، وإذا حاولت ربط الوظائف بأقل من 5 بايت من التعليمات.
بشكل أساسي نفس ما ورد أعلاه، باستثناء أن هذا المثال يتضمن تعليمات برمجية لإيقاف جميع سلاسل العمليات قيد التنفيذ مؤقتًا أثناء تثبيت الخطاف. لا يُضمن أن يكون هذا آمنًا في جميع الحالات، ولكنه بالتأكيد أكثر أمانًا من عدم القيام بأي شيء.
يتوسع هذا في كود الربط/الترامبولين المستخدم في المثالين السابقين لدعم إعادة توجيه وظائف متعددة إلى نفس الحمولة، وللسماح لوظائف الحمولة باستدعاء وظائف أخرى بها خطافات مثبتة فيها.
هذا هو أول مثال على الترامبولين الذي يقوم بتثبيت خطاف في عملية مختلفة (في هذه الحالة، التطبيق المستهدف B - Target with Free Functions From DLL). كل منطق الربط موجود في حمولة dll 13B - حمولة Trampoline Imported Func DLL. ليس هناك الكثير من الجديد هنا، هذا المثال يجمع فقط عناصر ربط الترامبولين التي تم تنفيذها بالفعل مع التقنيات الموضحة مسبقًا لربط وظيفة مستوردة من ملف dll.
جوهرة التاج في الريبو. يقوم هذا المثال بإدخال حمولة dll (14B - Trampoline Hook MSPaint Payload) في نسخة قيد التشغيل من mspaint (يجب عليك تشغيل mspaint بنفسك قبل تشغيل هذا). يؤدي الخطاف المثبت إلى رسم الفرش باللون الأحمر، بغض النظر عن اللون الذي حددته بالفعل في MSPaint. بصراحة، لا يوجد شيء هنا لم يتم عرضه في المثال السابق، ومن الرائع رؤية هذا يعمل على برنامج غير مفتعل.
تطبيق هدف بسيط يستدعي وظيفة مجانية في حلقة. تم تجميعها مع تضمين معلومات التصحيح.
التطبيق المستهدف الذي يستدعي وظيفة مجانية تم استيرادها من ملف dll (B2 - GetNum-DLL) في حلقة.
التطبيق المستهدف الذي يستدعي وظيفة عضو غير افتراضية في حلقة.
التطبيق المستهدف الذي يستدعي وظيفة عضو ظاهري في حلقة.