مقدمة في الاستغلال والعكس باستخدام أدوات IDA FREE والأدوات المجانية الأخرى. الفصل 2

في الجزء الأول ، قمنا بتثبيت العديد من الأدوات التي ستكون مفيدة لنا لأخذ هذه الدورة. ميزتها هي أنها كلها مجانية. لن نستخدم أي أداة مدفوعة ، ومن تلك التي لها نسخة مدفوعة ، مثل IDA أو PYCHARM ، سنستخدم الإصدار المجاني أو COMMUNITY.

دعونا نلقي نظرة على بعض المفاهيم قبل أن نبدأ في التمارين.

ما هي الحقيبة؟

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

كما أقول دائمًا ، يمكن للمبرمج أن يرتكب أخطاء ، ويمكن أن تتسبب هذه الأخطاء في تعطل البرنامج أو أخطاء. حتى الآن ، لم أقل أي شيء جديد.

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

ما هي القابلية للتأثر؟

القابلية للتأثر هي نوع معين من الأخطاء في برنامج يسمح باستخدامه لانتهاك أمان نظام الكمبيوتر.

وبالتالي ، تسمح لك الثغرات الأمنية بتنفيذ إجراءات لم يكن البرنامج مخصصًا لها ، وإساءة استخدامها.

بعبارة أخرى ، الضعف هو نوع معين من الأخطاء ، مجموعة فرعية بينهما.



بالطبع ، هناك العديد من أنواع الثغرات الأمنية. سنركز على دراسة واستغلال الثغرات الأمنية في WINDOWS.

ما هو استغلال؟


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

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

النوع الأول من الثغرات التي سندرسها سيكون تجاوزات العازلة. سنبدأ بأبسط الأمثلة ، ثم سنزيد التعقيد تدريجيًا.

في البداية ، لن يتم تنشيط ميزات أمان النظام ، ولكننا سنقوم بتنشيطها تدريجيًا لمعرفة كيف يمكننا التعامل معها وفي المواقف.

ما هو BUFFER؟


BUFFER هي مساحة ذاكرة بحجم معين محفوظة لتخزين البيانات وإدارتها.

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

ما هو الملء المفرط؟


يحدث BUFFER OVERFLOW عندما يتجاوز برنامج الكمبيوتر مقدار الذاكرة المحجوزة له عن طريق كتابة البيانات إلى كتلة ذاكرة متجاورة.

https://www.welivesecurity.com/la-es/tag/buffer-overflow-la-es

في الحقيقة ، يحدث تجاوز سعة المخزن المؤقت في تطبيق عندما لا يكون لديه عمليات التحقق الأمنية اللازمة في رمز البرنامج الخاص به ، مثل قياس كمية البيانات التي سيتم نسخها إلى المخزن المؤقت والتي لا تتجاوز حجم المخزن المؤقت.

أكثر أنواع تدفقات المخزن المؤقت شيوعًا هي تجاوزات المخزن المؤقت للمكدس وتدفقات المخزن المؤقت للكومة.

هنا نرى تعريف تجاوز سعة المخزن المؤقت ، وفي مثالنا السابق ، إذا حاولت صب 40 لترًا في خزان سعة 20 لترًا ، فسيتم تجاوزه ، كما نفهمه. هذا هو تجاوز سعة يتسبب في تجاوز سعة المخزن المؤقت ، أي تجاوز سعة خزاني عند تجاوز سعته القصوى.

الآن شرح الفرق بين المكدس وكومة.

ما هو المكدس؟


يتم استخدام STACK لتخزين متغيرات الوظيفة المحلية التي تكون مطلوبة فقط طالما تم تنفيذ الوظيفة. في معظم لغات البرمجة ، من المهم أن نعرف في وقت الترجمة حجم المتغير إذا أردنا إبقائه على المكدس.

ما هو LOT؟


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

يتم استخدام الكومة أيضًا للكائنات التي تختلف في الحجم لأننا لا نعرف في وقت الترجمة كم من الوقت سيتم استخدامها.

لقد كنت أعمل في شركتنا منذ أكثر من 13 عامًا كمؤلف لمآثر ، وأول شيء فعلناه مع جميع الأشخاص الذين تم توظيفهم حتى عندما انضممت كان محاولة الكشف عن مكدسات وأكوام GERARDO RICHART الشهيرة. وهو أحد مؤسسي CORE SECURITY وخبير تحليل الاستغلال.

سنبدأ ببطء مع أبسط مكدسات. بالطبع ، كما قلت ، يتم تجميعها في الوقت الحالي بأقل قدر من الحماية وهي 32 بت من أجل تسهيل التشغيل.

دعنا نلقي نظرة على شفرة المصدر لمهمة STACK1.

https://drive.google.com/open؟id=16btJAetpa1V5yHDZE2bnnFWTQNpsUR4H

نرى مجلدًا به تمارين ، وفي الداخل يوجد كود المصدر STACK1 المسمى STACK1_VS_2017.CPP.

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

#include <stdlib.h>
#include  <stdio.h> 
#include "Windows.h"


int main(int argc, char **argv) 
{


	MessageBoxA((HWND)-0, (LPCSTR) "Imprimir You win..\n", (LPCSTR)"Vamosss", (UINT)0);

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}


سنحاول فهم هذا الرمز ومعرفة أين قد يحدث تجاوز سعة المخزن المؤقت ، وما إذا كان سيتم تجاوز سعة المخزن المؤقت على المكدس أو على كومة الذاكرة المؤقتة.

تمت إضافة استدعاء دالة MessageBoxA إلى كود مصدر STACK1 لتظهر لنا رسالة صغيرة تطالبنا بحلها. هذه مجرد إضافة لا تؤثر على أي شيء. هذه مكالمة قياسية لوظيفة WINDOWS المحددة ، والتي لن نقوم بتحليلها هنا.

من يحتاج إلى معلومات حول هذه الوظيفة ، يمكنك الحصول عليها هنا.

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

لذلك تبقى لنا شفرة المصدر هذه التي أنشأها GERARDO RICHART.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

نرى باللون الأحمر الجزء الأول من البرنامج ، حيث المساحة المحجوزة للمتغيرات المحلية. في هذه الحالة ، هناك متغيرين محليين ، COOKIE و BUF.

يمكنك مشاهدة أنواع البيانات في الجدول . توجد أيضًا أنواع أخرى من المتغيرات.

سيتم تجميع الكود في 32 بت.

نرى أن متغير COOKIE سيكون من نوع INT ، لذا سيتم حجز 4 بايت من الذاكرة لهذا المتغير.



في حالة متغير BUF ، نرى أنه صفيف أو سلسلة من الأحرف (حجم الحرف = 1 بايت).



أولئك. سيكون عبارة عن مصفوفة من 80 حرفًا ، أي سيكون طوله 80 × 1 = 80 بايت.

أي شخص لا يعرف ما يمكن للصفيف أن يقرأ عنه هنا:

https://www.programiz.com/c-programming/c-arrays

وبالتالي ، يمكن للصفيف تخزين العديد من القيم من نفس نوع البيانات. عليك فقط أن تخبره عن نوع البيانات التي ستكون وكم سيكون هناك.



في المثال الأول ، هذه مجموعة من الأعداد الصحيحة ، أي ستكون 100 بايت ، وبما أن كل عدد صحيح يأخذ 4 بايت ، سيكون طول المصفوفة 100 × 4 = 400 بايت.

في المثال الثاني ، يأخذ FLOAT 4 بايت ، لذلك سيكون صفيفًا من 5 FLOAT ، لذا سيكون طوله 5 × 4 = 20 بايت.

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

العودة إلى تمريننا:

char buf[80];

هذه مجموعة من الأحرف 80 × 1 = 80 بايت ، أي يبدو جرة لدينا 20 لتر. إذا حاولنا تخزين أكثر من 80 بايت ، فسيفيض البنك.

الآن دعونا نرى أين يتم استخدام المخزن المؤقت BUF.



نرى أن المخزن المؤقت يستخدم في مكانين مميزين بأسهم حمراء.

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

"buf: %08x cookie: %08x\n"

لكن الدالة PRINTF لا تطبع السلسلة بعلامات اقتباس فحسب ، بل تطبع السلسلة أيضًا بالتنسيق المحدد. تخبرنا النسب المئوية بالداخل أنه سيتم إنشاء خط إخراج. نرى أن السلسلة هي أول حجة للدالة. قد يكون تنسيق الإخراج والوسيطات الأخرى عدة (سيكون هناك وسيطة لكل وسيطة٪ في التنسيق). في حالتنا ، هناك اثنان منهم.



في هذه الحالة ، لدينا تنسيقان X٪ ، لذلك إذا أشرت إلى جدول تنسيق PRINTF:



نرى أن الوظيفة ستأخذ هذه الأعداد الصحيحة (INT) وإدراجها في سطر الإخراج مع قاعدة نظام الأرقام 16 ، أي بتنسيق سداسي عشري. يشير 08 إلى أنه إذا كان الرقم يحتوي على أقل من 8 أرقام ، فستقوم الوظيفة بملئه بمسافات.

سيكون ناتج "buf:٪ 31x" ، و buf على هذا النحو

buf:             19FED4 

نرى أنه في هذا المثال مليء بمسافات قبل الرقم. هناك العديد من المعدلات لإظهار الإخراج.

جميع الحالات المحتملة مدرجة هنا:

http://www.cplusplus.com/reference/cstdio/printf/

حالتنا هي:





نرى أن النتيجة ليست مقطوعة ، فهي مليئة بمسافات فقط إذا كان طول الوسيطة لإدراجها أقل من القيمة قبل X.

لذلك ، نعلم أن الدالة تطبع رقمين سداسيًا عشريًا ، والتي يتم الحصول عليها من وسيطتين.

printf("buf: %08x cookie: %08x\n", &buf, &cookie);

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

يشير الرمز & إلى هذا. تقوم بإرجاع عنوان أو موقع الجرة ، وليس محتوياتها أو قيمتها.

تعريف AMPERSAND


يتم استخدام AMPERSAND للإشارة إلى عنوان الذاكرة للمتغير الذي سيتم تخزين البيانات فيه.

لذلك ، إذا قمت بتشغيل الملف القابل للتنفيذ في وحدة التحكم ، فسوف أرى ، على سبيل المثال ، أنه عند تنفيذ وظيفة PRINTF ، ستتم طباعته:



قد يتغير العنوانان على جهاز الكمبيوتر الخاص بك ، ولكن نظرًا لأن العنوان السفلي لكلا العنوانين يتطابق مع عنوان BUF ، فإننا نرى أنهما موجودان بهذه الطريقة:



عنوان BUF أقل من عنوان COOKIE ، لذا سيزيد.

وماذا تخبرنا هذه العناوين المتغيرة؟ (في حالتي ، & BUF = 0x19FED4 و & COOKIE = 0x19FF24)



كلاهما بتنسيق سداسي عشري. هل تذكر أن هذا هو تنسيق٪ X؟ لذلك أضع 0x أمامنا لتمييز الأرقام العشرية التي سنمثلها بدون أي إضافات.

إذا قمت بإجراء عملية طرح في وحدة التحكم PYTHON أو في PYCHARM:



نحصل على نتيجة 80 بايت ، حيث يفترض أن متغير COOKIE يبدأ بالضبط حيث ينتهي المخزن المؤقت BUF ، لذا يعطينا الفرق حجم المخزن المؤقت.

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

والحقيقة هي أننا نعرف بالفعل شيئًا عن الكود وحجم المتغيرات وموقعها نظرًا لوجود وظيفة PRINTF.

الآن دعونا نلقي نظرة على مكان آخر حيث يتم استخدام المخزن المؤقت BUF ، لأن البرنامج الآن يطبع عنوانه فقط ، ولكنه لا يستخدمه لتخزين البيانات فيه.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

هنا ، على الخط الأحمر ، GET هي وظيفة لإدخال البيانات من لوحة المفاتيح. سيتم إدخال البيانات حتى أضغط على Enter.

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

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



والحقيقة هي أنه تحت BUF هو متغير COOKIE ، لذلك سيتم تجاوز الفائض ويملأه بقيمة يمكنك التحكم فيها.

على سبيل المثال ، إذا كتب شخص يطبع 80 * A و 4 * B ، فإن 80 * A سيملأ BUF ، و 4 * B سيملأ ملف COOKIE ، وكما نعلم ، عندما يطبع شخص ما حرفًا في وحدة التحكم ، فستظل القيمة منخفضة. ASCII.



نظرًا لأنه سيتم ملء ملف تعريف الارتباط بأربعة أحرف B ، والتي تعادل قيمة 0x42 ، يمكننا أن نضمن أن تكون قيمة ملف تعريف الارتباط هي 0x42424242 ، أي على جهاز الكمبيوتر الخاص بي ، سيكون عنوان ملف تعريف الارتباط 0x19FF24 هو 0x42424242 كمحتوى.

0x19FF24 => 42424242



والحقيقة أننا رأينا بالفعل كيفية تجاوز قيمة COOKIE والتحكم فيها.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

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

في هذه الحالة ، لا يمكنك أبدًا كتابة "فزت" ، ولن يسمح لك ذلك إلا بالزيادة.

AAAAAAAA

بمعنى آخر ، بدلاً من المرور ، على سبيل المثال ، 80 * A و 4 * B ، لطباعة "فزت" ، يجب عليك تمرير 80 * A ثم الحروف DCBA ، حيث سيؤدي ذلك إلى تخزين القيم في ملف تعريف الارتباط ASCII.

44434241

https://www.arumeinformatica.es/blog/los-formatos-big-endian-y-little-endian/

التنسيق الذي يتم تخزين البيانات به هو LITTLE ENDIAN. وبعبارة أخرى ، يتم تخزين البيانات في الذاكرة بترتيب عكسي ، ببساطة.



وإذا تم حفظ التسلسل 0x41424344 ، سيقوم النظام بحفظه في الذاكرة على النحو التالي:

44 43 42 41

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

يمكننا تشغيل الملف التنفيذي في وحدة التحكم.



وسيومض المؤشر ، حيث تطلب مني وظيفة GET إدخال الإدخال. اكتب 80 حرفًا بعناية ثم DCBA.

في وحدة التحكم PYTHON أو PYCHARM ، يمكنني طباعة السطر ، ونسخه بدون علامات اقتباس ولصقه في وحدة التحكم حتى لا أطبعه بشكل مجنون ، ثم اضغط على مفتاح ENTER لإدخاله.





نرى أننا حصلنا على "تكسب".

يمكننا أن نرى هذا في المصحح. لهذا سوف نستخدم X64DBG.



اخترت الإصدار 32 بت.





إذا توقفنا عند مكتبة NTDLL.DLL ، نضغط على RUN مرة أخرى باستخدام F9.

نرى أن المصحح يتوقف عند أول تعليمات لوحدة STACK1 ، والتي تسمى ENTRY POINT أو التعليمات الأولى التي تنفذها الوحدة.



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



اخترنا البحث فقط في المنطقة الحالية. نحن نعلم أن الخطوط ستكون في نفس القسم.



هنا نرى أن هناك خطوط برامج وأخرى أضافها المترجم. نضغط مرتين على أحد خطوطنا.



يمكنك الآن مشاهدة المزيد. نرى استدعاء للدالة MessageBoxA و PRINTF و GETS ومقارنة مع القيمة 0x41424344.

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





نرى أن هذه ليست مثالية ، لكنها أفضل مما كانت عليه.

سأضع BP في بداية الوظيفة واضغط F9 حتى يتوقف المصحح.



بالنسبة لأولئك الذين لا يعرفون ما هي حجة الدالة.



في حالتنا ، الوظيفة الرئيسية لها الحجج ، لكنها لا تستخدم داخل الدالة.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

نرى هنا أن هناك وسيطتان ، ويتم تمريرهما عبر المكدس عندما يتم ترجمة الملف القابل للتنفيذ إلى 32 بت.

قبل استدعاء الوظيفة مباشرة ، سيتم تخزين الوسائط على المكدس.

عندما نتوقف عند بداية الوظيفة ، ستكون القيمة الأولى على المكدس هي RETURN ADDRESS ، أي حيث ستعود الوظيفة بعد اكتمال الوظيفة ، وستكون أسفل هذه القيمة وسيطات هذه الدالة.

إذا قمت بالنقر بزر الماوس الأيمن فوق RETURN ADDRESS وحدد FWLOW DWORD IN DISASSEMBLER ، فسأرى أين يجب أن يعود المصحح بعد اكتمال الوظيفة.



سيعود هنا. هذا يعني أنه تم استدعاء الوظيفة الرئيسية من مكالمة أعلى. يمكنني وضع BP هنا ، وإعادة تشغيل التمرين والتأكد من ذلك.



سأضع BP قبل ذلك بقليل وأعيد تشغيل البرنامج.



سيتوقف المصحح هنا.



سيتم حفظ الوسيطات في الوظيفة الرئيسية باستخدام تعليمات PUSH هذه.

يوجد أدناه رابط لأولئك الذين يريدون معرفة المزيد عن الحجج الوظيفية:

https://publications.gbdirect.co.uk/c_book/chapter10/arguments_to_main.html

هذا ليس صعبًا جدًا. الوسيطة الأولى لـ ARGC هي INT ، والتي تشير إلى عدد معلمات وحدة التحكم المستخدمة في تنفيذ البرنامج ، بما في ذلك المسار إلى الملف التنفيذي ، و ARGV عبارة عن صفيف من المؤشرات إلى السلاسل.

نرى أنه إذا قمت بتغيير سطر الأوامر ، فسأمر بالمزيد من الحجج وإعادة التحميل.





هنا يتوقف المصحح عندما يكون على وشك حفظ الوسيطات باستخدام تعليمات PUSH. الوسيطة الأولى التي يتم تخزينها هي الأبعد ، والأخيرة التي تقوم بحفظها ستكون الوسيطة الأولى للدالة.

أتتبع وكل تعليمة PUSH تحفظ الحجج.



هنا أستطيع أن أرى الحجج الدالة. أعلاه هو الحجة الأولى ل ARGC. يكون الرقم 3 ، حيث إنه يمثل عدد الوسائط التي تم تمريرها إلى وحدة التحكم.



فيما يلي 3 حجج.

الآن نضغط على F7 للقيام بالخطوة INTO وإدخال الوظيفة.

هنا نرى أنه عند دخول CALL ، يقوم المصحح بحفظ عنوان العودة إلى المكدس.



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



الحجة الثانية ، كما رأينا ، هي مجموعة من المؤشرات. هنا نرى في الذاكرة أن هناك ثلاث مؤشرات لثلاثة أسطر يتم تمريرها كوسيطة.



توقفنا هنا في بداية الوظيفة ، وأقل قليلاً لدينا عناوين وعودة.

نوضح أن الملف تم تجميعه في 32 بت ، لأنه في الإصدار 64 بت ، يتم تمرير الوسيطات بطريقة مختلفة. سنراها لاحقا.

ثم تبدأ الوظيفة في التنفيذ. أول شيء هو ما يسمى PROLOGUE ، الذي يخزن قيمة EBP للوظيفة التي تسمى وظيفتنا.



سيؤدي ذلك إلى إبقاء قيمة EBP أعلى عنوان الإرجاع مباشرةً.



إذا اتبعت التعليمات باستخدام F7.

أرى أن قيمة EBP GUARDADO على المكدس أعلى عنوان المرسل.



التعليمات التالية في PROLOGUE:

MOV EBP، ESP

يقوم بتعيين قيمة EBP للوظيفة الحالية التي تم حفظها وكانت الوظيفة الرئيسية التي تستدعي وظيفتنا (في هذه الحالة ، وظيفتي الرئيسية هي الوظيفة المستندة إلى EBP ، وفي حالات أخرى قد تكون مختلفة ، ونحن نراهم لاحقًا) من

خلال وضع قيمة ESP الحالية في EBP ، نضمن إنشاء إطار لوظيفتنا الحالية.



الآن ، نظرًا لأن هذه الوظيفة تستند إلى EBP أو EBP BASED ، فسيتم تخزين قيمة EBP داخل الوظيفة وسيتم قبولها كمرجع ، وسيتغير ESP.



تؤخذ قيمة EBP هذه كأساس.

في الوظائف القائمة على EBP ، يمكن تسمية المتغيرات والحجج من خلال بعدها عن هذا العنوان ، والتي سيتم تخزينها في قيمة EBP حتى خاتمة.

يمكننا أن نرى العديد من المتغيرات في القائمة التي يشار إليها باسم EBP-4 أو EBP-54 ، تشير إلى قيمة EBP التي تقبلها حاليًا.

يمكننا القول أنه بمجرد أن يأخذ EBP قيمته بعد PROLOGUE ، سيبدو وكأنه استنزاف ، لذا فإن الحجج ستذهب دائمًا في هذا الاتجاه ، لذلك يشير EBP + XXX إلى الحجج (EBP المخزن و RETURN ADDRESS أقل أيضًا ، ولكن لن يكون لديهم روابط في الكود) ، في حين أن المتغيرات ، كما سنرى ، ستكون فوق هذا العنوان ، لذا يشير الرابط إلى EBP-XXX إلى بعض المتغيرات المحلية.

وبالتالي ، في الدالات المستندة إلى

EBP : EBP + XXXX = الوسائط التي تم تمريرها إلى الدالة
EBP - XXXX = متغيرات الوظيفة المحلية

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

00401043 | 83EC 54 | SUB ESP، 54

نرى أن ESP يقع فوق EBP ، الذي سيظل الرابط ، وأن 0x54 المحول إلى عشري هو 84 ، وهو مجموع أطوال BUF و COOKIE. تذكر أنهم كانوا 80 و 4 على التوالي.







عند التنفيذ ، يتم إنشاء مسافة لمتغيرات BUF و COOKIE بحجم 84 بايت. يمكنك النقر فوق العمود الأول في الاتجاه الأفقي وإلقاء نظرة على قيمة EBP والعثور على هذه القيمة على المكدس. من الواضح أنه الآن سيكون أقل.



انقر مرتين هنا.



وبالتالي ، سيكون لدينا قيم بخصوص EBP أيضًا على المكدس.

على سبيل المثال ، يتطابق EBP-4 مع القائمة ، ويتم عرض -4 في العمود الأول من المكدس ، وفي الشرح ، يتم عرضه أيضًا على أنه EBP-4.



إذا قمت بتتبع ذلك ، فإننا نرى أنه من المكان الذي تم فيه تحديد موقع ESP لحجز المتغيرات ، فإنه سينتقل دائمًا لأعلى ، لأنه يجب أن يأخذ في الاعتبار المساحة المخصصة للمتغيرات. عند تنفيذ 4 PUSHs لـ MessageBoxA ، يضع المصحح المتغيرات فوق المساحة المحجوزة ويزيد ESP.



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



عند إدخال وظيفة MessageBoxA ، يتم تخزين عنوان الإرجاع لهذه الوظيفة على المكدس.



إليك عنوان المرسل لـ MessageBoxA. عندما أصل إلى RET لهذه الوظيفة من خلال تتبع F8 ، وأقوم بتنفيذ MessageBoxA.



نرى أن المصحح سيعود أسفل المكالمة MessageBoxA.



وقد تم بالفعل استخدام قيم PUSH التي قمت بتمريرها للدالة MessageBoxA و RETURN ADDRESS لهذه الوظيفة ، كما أن ESP مرة أخرى أعلى المنطقة المحجوزة تمامًا ، كما كان قبل استدعاء أي دالة. سيحدث نفس الشيء عند استدعاء دالة PRINTF.

بعد اجتياز وظيفة PRINTF ، ستتم طباعة عناوين BUF و COOKIE.



سيكون عنوان BUF على جهازي 0x19FED4 ، وسيكون عنوان COOKIE 0x19FF24.

هنا ، يقرأ البرنامج عنوان BUF لتمريره إلى دالة GETS وملء BUF. يمكننا التحقق مما إذا كان العنوان يطابق ما تعرضه وحدة التحكم 0x19FED4.





نرى هنا أنه EBP-54. إذا قمت بالنقر نقرًا مزدوجًا فوق المكدس حيث تظهر -54 ، فسيظهر أن العنوان هو BUF = 0x19FED4 على جهازي.



الآن ، عندما أقوم بحفظ البيانات المدخلة على هذا العنوان ، يمكنني وضعها في تفريغ لمعرفة كيفية تخزين البايتات هناك.





ها هم. علاوة على ذلك ، لا يتم عرض أي شيء أدناه ، نظرًا لعدم وجود بيانات.

عندما أتصل بوظيفة GETS باستخدام F8 ، سأحتاج إلى الانتقال إلى وحدة التحكم ، واكتب واضغط ENTER لتعبئة المخزن المؤقت BUF وإعادة كتابة COOKIE.



نرى أن متغير COOKIE كان على 19FF24 على جهازي.

هنا ، يقارن البرنامج ملف تعريف الارتباط مع 0x41424344.



نرى أن EBP-4 يقول أنه ملف تعريف ارتباط ، بالإضافة إلى العنوان ، إذا قمنا بتعيين HORIZON على قيمة EBP ، كما كان من قبل.



انقر مرتين هنا.

نرى أن EBP-4 عبارة عن ملف تعريف ارتباط ، حيث أن المتغير عند مستوى المكدس -4 ، مما يؤدي إلى عدم وجود الأفق.



نرى أن البرنامج لن يقفز وسيظهر لك فوزك!





لذلك نحقق الهدف الذي تفوز به يدويًا. يقول ،

لقد قمنا بتحليل STACK1 ديناميكيًا باستخدام X64DBG ، وهو مصحح أخطاء ولا يسمح لنا بتحليل البرنامج دون بدء تشغيله. للقيام بذلك ، يجب علينا استخدام أدوات أخرى ، مثل IDA PRO أو GHIDRA أو RADARE.

يمكنني عمل نموذج نصي لتشغيل التمرين من PYTHON.

import sys
from subprocess import Popen, PIPE

payload = b"A" * 80 + b"\x44\x43\x42\x41"

p1 = Popen(r"C:\Users\ricardo\Desktop\abos y stack nuevos\STACK1_VS_2017.exe", stdin=PIPE)
print ("PID: %s" % hex(p1.pid))
print ("Enter para continuar")
p1.communicate(payload)
p1.wait()
input()

في حالة PYTHON 3 ، يجب أن أضع الأقواس في دالة PRINT وأن أكون حذراً عند إضافة الأسطر التي يجب أن تكون بايت (ضع b أمام الأسطر في PYTHON 2).

أتحقق من صحة المسار وعندما أقوم بتشغيل الملف.



حسن. لدينا بالفعل نموذج برنامج نصي لـ PYTHON 3 لتشغيل STACK1. في الجزء التالي ، سنستمر في التحليل الثابت في المؤسسة الدولية للتنمية ، ورادار والغيدرة.


يرجى ملاحظة أنه بالإضافة إلى مهمة STACK1 ، هناك أيضًا إصدارات 2 و 3 و 4. يمكنك محاولة حلها. إنها بسيطة جدًا ومماثلة لـ STACK1 ، لذلك لا تشعر بالملل.

في الجزء التالي سنرى IDA FREE و RADARE و GHIDRA.

نراكم في الجزء التالي 3.

ريكاردو نارفاها
25/10/2019

PS # 1
يمكن تنزيل ملف PDF جميل على صفحتي الرئيسية - yasha.su

PS # 2
قريبًا سأكتب استمرارًا للمقالة https://habr.com/en/post/464117/ حول كيفية جمع المساعدة للأب كريس كاسبيرسكي وماذا عن هذا حدث.

لا تمل.

All Articles