السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُهُ
اللَّهُمَّ صَلِّ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ، كَمَا صَلَّيْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ، وَبَارِكْ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ، كَمَا بَارَكْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ،
[+] بما إن دي أول مقالة أكتبها ع البلوج الجديدة بتاعتي ، واللـ فكرت أعملها لـ مشاركة شروحات بالعربي عن الـ Malware Development ، فـ حبيت أبدأ بحاجه خفيفة (أو يمكن مش خفيفة أوي) عن أساسيات الـ Windows Internals، واللـ هتفيدك كدا كدا قدام وهتعرف ليه.
[+] هنتكلم النهارده فـ :
– هنا هنشرح البنية الأساسية للويندوز ، إزاي بيتقسم وإيه الأجزاء اللـ لازم تكون عارفها.
– هنغوص شوية فـ إزاي النظام بيتعامل مع الذاكرة وإزاي ده ممكن يبقى نقطة ضعف ممكن الـ malware يستغلها.
[+] أول حاجة محتاجين نفهمها عن نظام الويندوز هي طريقة عمله من الداخل ، وازاي بيقدر يشغل البرامج ويدير العمليات. ببساطة ، الويندوز بيشتغل بنمطين مختلفين:
وده النمط اللـ البرامج العادية بتشتغل فيه ، زي Notepad.exe ، Chrome.exe، وأي برنامج بيستخدمه اليوزر .. البرامج هنا معزولة عن النظام ومتقدرش تتعامل مباشرة مع الهاردوير أو الميموري بتاعة النظام الأساسي.
وده المستوى اللـ بيشتغل فيه نظام التشغيل نفسه ، زي الـ Windows Kernel ، والـ Drivers ، والحاجات اللـ بتتعامل مع الهاردوير مباشرة.
[+] وعشان نفهم الموضوع بكل سلاسة ، تعالي نبسط الصورة أكتر من كده ونخليها بالشكل:
[+] لما أي برنامج يحب ينفذ عملية معينة ، زي إنه يفتح ملف أو يتصل بالإنترنت ، مش بيقدر يعمل ده لوحده .. فيه كذا مرحلة بيمر بيها عشان يوصل لـ Kernel Mode ، وهي:
أي برنامج بتفتحه (زي Notepad.exe) بيشتغل فـ الـ User Mode ، ومش بيقدر يتعامل مع الهاردوير ع طول .. عشان كده ، لما البرنامج يحتاج يعمل عملية معينة (زي فتح ملف) ، بيطلب المساعدة من مكتبات الويندوز.
فيه ملفات مهمة جدًا فـ الويندوز ، زي:
ولسه غيرهم كتير .. المكتبات دي بتساعد البرامج على تنفيذ العمليات بسهولة من غير ما يتعاملوا مع
الكيرنل
بشكل مباشر. وبتكون موجودة فـ المسار:
C:\Windows\System32
لما أي برنامج يستدعي Windows API ، زي CreateFileW من Kernel32.dll ، بيتم تمرير الطلب لـ Ntdll.dll.
الملف ده هو اللـ بيتعامل مع الـ Native APIs الخاصة بالويندوز ، وبيجهز الطلب بحيث يتم إرساله للـ Kernel Mode باستخدام حاجة اسمها System Call (Syscall).
عشان أي عملية تتنفذ فـ الـ Kernel ، لازم يتم استدعاء Syscall ، وده عبارة عن أمر بيخلي النظام يبدّل من User Mode لـ Kernel Mode .. المعالج بينفذ ده باستخدام sysenter (32bit) أو syscall (64bit).
[+] هقولك مثال إزاي Notepad.exe بيعمل Create لملف جديد؟
تعالى نمشي خطوة بخطوة ونشوف اللـ بيحصل لما الـ Notepad.exe يحب ينشئ ملف جديد:
أول حاجة ، البرنامج بيستدعي الـ Windows API اللـ مسؤولة عن إنشاء ملف ، وهي CreateFileW من
Kernel32.dll.
الـ Kernel32.dll بيمرر الطلب لـ Ntdll.dll ، اللـ بدوره بيترجمه لـ
Native API اسمها NtCreateFile.
دلوقتي عشان العملية تتم فعلًا ، لازم ننقل التنفيذ لـ Kernel Mode .. هنا بيتم تنفيذ Syscall ،
واللـ بيحوّل التحكم للـ Kernel.
بمجرد ما التنفيذ يوصل لـ Kernel Mode ، الـ Windows Kernel بيستدعي الـ File System Driver اللـ مسؤول عن إدارة الملفات ، واللـ بدوره بيتعامل مع الهارد ديسك وبينشئ الملف الجديد فـ المكان المطلوب.
وبكدا يكون بقي عندك فكرة عن الـ Windows Architecture
[+] بعد مـ فهمنا ازاي الويندوز بيشتغل من حيث الـ Windows Architecture ، تعالوا نتكلم عن جزء مهم جدًا وهو إدارة الذاكرة (Memory Management).
[+] فـ الأول كدا ، لو كنت فاكر إن البرامج بتتعامل مع الرامات (Physical Memory) بشكل مباشر، فـ حابب أقولك إن ده مش اللـ بيحصل .. اللـ بيحصل إن البرامج بتتعامل مع حاجة اسمها Virtual Memory ، وهي اللـ بتقوم بإدارة وتنظيم الوصول للميموري الفعلية.
[+] طب ليه الأبلكيشنز مش بتتعامل مع الميموري ع طول؟
كان زمان ، لما البرامج كانت بتشتغل مباشرة ع الميموري الفعلية ، كان فـ مشاكل كتير ، منها:
لو عندك برنامج بيستخدم الميموري كلها ، يبقى أي برنامج تاني مش هيلاقي مكان يشتغل فيه.
لو أكتر من برنامج محتاج نفس البيانات ، كان فـ شوية تعقيد للوصول ليها.
أي برنامج كان يقدر يعدّل على بيانات أي برنامج تاني ، وده طبعا غلط.
عشان كدا ، تم تطوير فكرة الـ Virtual Memory ، واللـ بقت زي طبقة إدارية بتخلي كل برنامج يفتكر إنه عنده ميموري خاصة بيه ، فـ حين إن النظام هو اللـ بينظم العملية دي ويمنع أي تداخل غير مرغوب فيه.
[+] طب إزاي الويندوز بيقسم الميموري؟
عشان الويندوز يقدر يدير الذاكرة بكفاءة ، بيقسمها إلى وحدات اسمها Pages (صفحات) ، وحجم كل صفحة في العادة بيكون 4KB.
[+] كل صفحة (Page) ممكن يكون ليها 3 حالات رئيسية:
الصفحة دي فاضية ومش محجوزة لأي عملية.
الصفحة دي بتكون محجوزة ، بس لسه متستخدمتش ، وده معناه إن النظام ممكن يخصصها لعملية معينة بعدين.
ودي فعلًا بتكون مخصصة ومستخدمة من قبل أحد البرامج.
[+] كمان كل Page فـ الميموري بيكون ليها صلاحيات معينة بتحدد إيه اللـ ممكن يحصل عليها:
→ معناها إن المنطقة دي مفيش أي صلاحيات عليها ، لا قراءة ، ولا كتابة ، ولا تنفيذ.
→ المنطقة دي قابلة للقراءة بس ، لكن لا كتابة ولا تنفيذ.
→ هنا الصفحة واخدة كل الصلاحيات ، تقدر تقرأ ، وتكتب ، وتشغل كود برمجي فيها.
[+] طيب إيه اللـ بيحصل لو حاولت تعدي حدودك وتتجاوز الصلاحيات؟
فـ نظام الويندوز ، فيه ميكانيزم أمان اسمها DEP (Data Execution Prevention) ، واللـ بتشتغل ع منع تشغيل أي كود فـ ذاكرة مش مصرح بيها للتنفيذ.
بمعنى إن لو عندك منطقة فـ الميموري واخدة صلاحية READONLY ، وجيت تحاول تشغل كود منها ، الـ DEP هيتدخل ويقولك: "بس يبابا ، روح إلعب بعيد .. مش هسمحلك!" ، وده مهم جدًا عشان يمنع الـ Buffer Overflow Exploits.
فيه كمان تقنية أمان تانية اسمها ASLR (Address Space Layout Randomization) ، واللـ فكرتها ببساطة إنها بتخلي أماكن تخزين البيانات فـ الذاكرة عشوائية كل مرة البرنامج يشتغل فيها .. وده بيمنع الهاكرز من معرفة أماكن البيانات أو الأكواد فـ الميموري ، وده بيصعّب عليهم تنفيذ أي Memory Exploits ، زي هجمات Return-Oriented Programming (ROP).
[+] تعالي أقولك مثال عملي: إزاي النظام بيدير الميموري بين برنامجين؟
خلينا نفترض إنك فاتح Firefox.exe و Onenote.exe فـ نفس الوقت .. كل واحد منهم بيكون ليه Virtual Memory Space خاصة بيه ، وبالتالي مفيش برنامج يقدر يدخل ع بيانات البرنامج التاني مباشرة.
السيناريو الطبيعي كالأتي:
[+] طب إيه اللـ ممكن يحصل لو حصل تجاوز؟
لو برنامج حاول يدخل ع ذاكرة برنامج تاني ، النظام هيمنعه ويرجع Access Violation Error .. ولو برنامج حاول ينفذ كود فـ منطقة مش واخدة صلاحية التنفيذ ، الـ DEP هيتدخل ويمنعه .. ولو هكر جامد جمودة حاول يحدد أماكن البيانات فـ الميموري عشان يستغلها ، الـ ASLR هيخليه تايه ومش عارف يوصل لحاجة.
[+] Memory Allocation in Windows – إدارة الذاكرة عمليًا
بعد ما فهمنا إزاي الويندوز بيتعامل مع الذاكرة وإزاي بيقسمها ، تعالوا بقى ندخل فـ الجزء العملي ونجرب نحجز أماكن فـ الميموري باستخدام كود C.
[ملحوظه مهمه: محتاج يكون عندك خلفية عن لغة برمجة الـ C أو الـ (++C) ، وتقدر تشوف كورس م. أسامة الزيرو ع اليوتيوب من [هنا] ، أو أي كورس تاني عموما .. المهم يكون عندك الأساسيات.]
فيه ٣ طرق أساسية لحجز مكان فـ الميموري:
1. ()Malloc – الحجز باستخدام C Runtime Library
أول طريقة معانا هي ()malloc , واللـ بتستخدم لحجز مساحة معينة فـ الميموري، وبتُرجع Pointer للمنطقة دي.
الكود:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
int main() {
PVOID pAddress = malloc(200);
CHAR* cString = "I'm Still N00P in Malware Development";
memcpy(pAddress, cString, strlen(cString));
printf("Address: 0x%p\n", pAddress);
getchar();
return 0;
}
شرح الكود سطر بسطر:
دلوقتي هنبدأ نـ رن الكود ونشوف ايه اللـ هيحصل:
Debug > Windows > Memory > Memory 1.
2. ()HeapAlloc – الحجز باستخدام Windows API
دي بتتعامل مع Windows Heap Manager ، واللـ هو طريقة الويندوز فـ تنظيم الميموري بدل مـ يعتمد على ()malloc.
الشكل العام:
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
الكود: (نفس الكود اللـ فات بس هتعدل سطر واحد بتاع الحالة الجديدة ، واسم المتغير)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
int main() {
LPVOID pHeapAddress = HeapAlloc(GetProcessHeap(), 0, 200);
CHAR* cString = "I'm Still N00P in Malware Development";
memcpy(pHeapAddress, cString, strlen(cString));
printf("Address: 0x%p\n", pHeapAddress);
getchar();
return 0;
}
ودا أكيد مش محتاج شرح .. إنت كدا فاهم كويس.
طب اي الفرق بين HeapAlloc و malloc؟
3. ()LocalAlloc – الحجز باستخدام Windows API (قديم شوية)
دي بتُستخدم لحجز ميموري ، بس أغلب المطورين بيفضلوا ()HeapAlloc بدلها لأنها بتدي تحكم أكتر.
الشكل العام:
HLOCAL LocalAlloc(UINT uFlags, SIZE_T uBytes);الكود: (زي اللـ فاتوا ، هتعدل سطر واحد بتاع الحالة الجديدة ، واسم المتغير)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
int main() {
HLOCAL pLocalAddress = LocalAlloc(LPTR, 200);
CHAR* cString = "I'm Still N00P in Malware Development";
memcpy(pLocalAddress, cString, strlen(cString));
printf("Address: 0x%p\n", pLocalAddress);
getchar();
return 0;
}
طب إيه الفرق بين التلات طرق؟
| العيوب | المميزات | تابعة لـ | الطريقة |
|---|---|---|---|
| أقل تحكم فـ الميموري | سهلة وبسيطة ، ومناسبة للتطبيقات العامة | C Runtime | ()malloc |
| لازم تعمل ()HeapFree يدويا | بتستخدم الـ Process Heap وأكتر تحكما | Windows API | ()HeapAlloc |
| قديمة ونادرا ما تُستخدم حاليا | كانوا بيستخدموها قبل الـ ()HeapAlloc | Windows API (قديم) | ()LocalAlloc |
إدارة الذاكرة وتفادي الـ Memory Leaks
لما تحجز ميموري ، لازم تفضيها لما تخلص ، وإلا هتقع فـ مشكلة Memory Leak ، واللـ معناها إن فيه أماكن فـ الميموري فضيت بس لسه محجوزة ومحدش بيستخدمها ، وده ممكن يخلي الرام تتملي بعد وقت.
طب إزاي تفضي الميموري بطريقة صحيحة؟
| فانكشن التفريغ | طريقة الحجز |
|---|---|
| ()Free | ()malloc |
| ()HeapFree | ()HeapAlloc |
| ()LocalFree | ()LocalAlloc |
وبكدا يكون دا الكود النهائي فـ حالة الـ ()malloc:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
int main() {
PVOID pAddress = malloc(200);
CHAR* cString = "I'm Still N00P in Malware Development";
memcpy(pAddress, cString, strlen(cString));
printf("Address: 0x%p\n", pAddress);
free(pAddress);
getchar();
return 0;
}
وفـ حالة الـ ()HeapAlloc:
HeapFree(GetProcessHeap(), 0, pHeapAddress);
وفـ حالة الـ ()LocalAlloc:
LocalFree(pLocalAddress);
ودا مثال لتفريغ منطقة كانت محجوزة ..
وبكدا . أتمني تكون استفدت حاجه .. ونلتقي فـ مقالة قادمة .. ولو عندك أي تعليق ، استفسار ، أي حاجه .. متترددش وكلمني.
خدلك لفة فـ المصادر والمراجع يا عزيزي:
لو حابب تـ dive deep فـ ممكن تشوف الكتب دي: