Windows Internals

HomeLab

السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُهُ

اللَّهُمَّ صَلِّ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ، كَمَا صَلَّيْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ، وَبَارِكْ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ، كَمَا بَارَكْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ، إِنَّكَ حَمِيدٌ مَجِيدٌ،

Introduction

[+] بما إن دي أول مقالة أكتبها ع البلوج الجديدة بتاعتي ، واللـ فكرت أعملها لـ مشاركة شروحات بالعربي عن الـ Malware Development ، فـ حبيت أبدأ بحاجه خفيفة (أو يمكن مش خفيفة أوي) عن أساسيات الـ Windows Internals، واللـ هتفيدك كدا كدا قدام وهتعرف ليه.

[+] هنتكلم النهارده فـ :

  1. Windows Architecture
  2. – هنا هنشرح البنية الأساسية للويندوز ، إزاي بيتقسم وإيه الأجزاء اللـ لازم تكون عارفها.

  3. Memory Management
  4. – هنغوص شوية فـ إزاي النظام بيتعامل مع الذاكرة وإزاي ده ممكن يبقى نقطة ضعف ممكن الـ malware يستغلها.

1. Windows Architecture

WinArch

[+] أول حاجة محتاجين نفهمها عن نظام الويندوز هي طريقة عمله من الداخل ، وازاي بيقدر يشغل البرامج ويدير العمليات. ببساطة ، الويندوز بيشتغل بنمطين مختلفين:

  • User Mode (Ring 3) :
  • وده النمط اللـ البرامج العادية بتشتغل فيه ، زي Notepad.exe ، Chrome.exe، وأي برنامج بيستخدمه اليوزر .. البرامج هنا معزولة عن النظام ومتقدرش تتعامل مباشرة مع الهاردوير أو الميموري بتاعة النظام الأساسي.

  • Kernel Mode (Ring 0) :
  • وده المستوى اللـ بيشتغل فيه نظام التشغيل نفسه ، زي الـ Windows Kernel ، والـ Drivers ، والحاجات اللـ بتتعامل مع الهاردوير مباشرة.

WinArch1

[+] وعشان نفهم الموضوع بكل سلاسة ، تعالي نبسط الصورة أكتر من كده ونخليها بالشكل:

WinArch2

[+] لما أي برنامج يحب ينفذ عملية معينة ، زي إنه يفتح ملف أو يتصل بالإنترنت ، مش بيقدر يعمل ده لوحده .. فيه كذا مرحلة بيمر بيها عشان يوصل لـ Kernel Mode ، وهي:

  1. User Processes – البرامج اللـ بيشغلها اليوزر
  2. أي برنامج بتفتحه (زي Notepad.exe) بيشتغل فـ الـ User Mode ، ومش بيقدر يتعامل مع الهاردوير ع طول .. عشان كده ، لما البرنامج يحتاج يعمل عملية معينة (زي فتح ملف) ، بيطلب المساعدة من مكتبات الويندوز.

  3. Subsystem DLLs – مكتبات نظام الويندوز
  4. فيه ملفات مهمة جدًا فـ الويندوز ، زي:

    • Kernel32.dll: وده فيه مجموعة ضخمة من الـ Windows APIs اللـ البرامج بتستخدمها.
    • User32.dll : مسؤول عن التعامل مع الـ GUI ، زي النوافذ والأزرار والـ Input من الكيبورد والماوس.

    ولسه غيرهم كتير .. المكتبات دي بتساعد البرامج على تنفيذ العمليات بسهولة من غير ما يتعاملوا مع الكيرنل بشكل مباشر. وبتكون موجودة فـ المسار: C:\Windows\System32

  5. NTDLL.dll – الوسيط بين الـ User Mode والـ Kernel Mode
  6. لما أي برنامج يستدعي Windows API ، زي CreateFileW من Kernel32.dll ، بيتم تمرير الطلب لـ Ntdll.dll.

    الملف ده هو اللـ بيتعامل مع الـ Native APIs الخاصة بالويندوز ، وبيجهز الطلب بحيث يتم إرساله للـ Kernel Mode باستخدام حاجة اسمها System Call (Syscall).

  7. System Call – الانتقال من User Mode إلى Kernel Mode
  8. عشان أي عملية تتنفذ فـ الـ Kernel ، لازم يتم استدعاء Syscall ، وده عبارة عن أمر بيخلي النظام يبدّل من User Mode لـ Kernel Mode .. المعالج بينفذ ده باستخدام sysenter (32bit) أو syscall (64bit).

[+] هقولك مثال إزاي Notepad.exe بيعمل Create لملف جديد؟

تعالى نمشي خطوة بخطوة ونشوف اللـ بيحصل لما الـ Notepad.exe يحب ينشئ ملف جديد:

  1. الـ Notepad.exe بيطلب العملية
  2. أول حاجة ، البرنامج بيستدعي الـ Windows API اللـ مسؤولة عن إنشاء ملف ، وهي CreateFileW من Kernel32.dll.

  3. الطلب بيعدي على الـ Ntdll.dll
  4. الـ Kernel32.dll بيمرر الطلب لـ Ntdll.dll ، اللـ بدوره بيترجمه لـ Native API اسمها NtCreateFile.

  5. تنفيذ الـ Syscall للانتقال لـ Kernel Mode
  6. دلوقتي عشان العملية تتم فعلًا ، لازم ننقل التنفيذ لـ Kernel Mode .. هنا بيتم تنفيذ Syscall ، واللـ بيحوّل التحكم للـ Kernel.

  7. الـ Kernel بينفذ العملية وبيعمل Create للملف
  8. بمجرد ما التنفيذ يوصل لـ Kernel Mode ، الـ Windows Kernel بيستدعي الـ File System Driver اللـ مسؤول عن إدارة الملفات ، واللـ بدوره بيتعامل مع الهارد ديسك وبينشئ الملف الجديد فـ المكان المطلوب.

وبكدا يكون بقي عندك فكرة عن الـ Windows Architecture

2. Memory Management

MemoryManagement

[+] بعد مـ فهمنا ازاي الويندوز بيشتغل من حيث الـ Windows Architecture ، تعالوا نتكلم عن جزء مهم جدًا وهو إدارة الذاكرة (Memory Management).

[+] فـ الأول كدا ، لو كنت فاكر إن البرامج بتتعامل مع الرامات (Physical Memory) بشكل مباشر، فـ حابب أقولك إن ده مش اللـ بيحصل .. اللـ بيحصل إن البرامج بتتعامل مع حاجة اسمها Virtual Memory ، وهي اللـ بتقوم بإدارة وتنظيم الوصول للميموري الفعلية.

HomeLab

[+] طب ليه الأبلكيشنز مش بتتعامل مع الميموري ع طول؟

كان زمان ، لما البرامج كانت بتشتغل مباشرة ع الميموري الفعلية ، كان فـ مشاكل كتير ، منها:

  1. مشكلة تعدد البرامج:
  2. لو عندك برنامج بيستخدم الميموري كلها ، يبقى أي برنامج تاني مش هيلاقي مكان يشتغل فيه.

  3. مشكلة الـ Memory Sharing:
  4. لو أكتر من برنامج محتاج نفس البيانات ، كان فـ شوية تعقيد للوصول ليها.

  5. مشكلة الأمان:
  6. أي برنامج كان يقدر يعدّل على بيانات أي برنامج تاني ، وده طبعا غلط.

عشان كدا ، تم تطوير فكرة الـ Virtual Memory ، واللـ بقت زي طبقة إدارية بتخلي كل برنامج يفتكر إنه عنده ميموري خاصة بيه ، فـ حين إن النظام هو اللـ بينظم العملية دي ويمنع أي تداخل غير مرغوب فيه.

[+] طب إزاي الويندوز بيقسم الميموري؟

عشان الويندوز يقدر يدير الذاكرة بكفاءة ، بيقسمها إلى وحدات اسمها Pages (صفحات) ، وحجم كل صفحة في العادة بيكون 4KB.

[+] كل صفحة (Page) ممكن يكون ليها 3 حالات رئيسية:

  1. Free:
  2. الصفحة دي فاضية ومش محجوزة لأي عملية.

  3. Reserved:
  4. الصفحة دي بتكون محجوزة ، بس لسه متستخدمتش ، وده معناه إن النظام ممكن يخصصها لعملية معينة بعدين.

  5. Committed:
  6. ودي فعلًا بتكون مخصصة ومستخدمة من قبل أحد البرامج.

[+] كمان كل Page فـ الميموري بيكون ليها صلاحيات معينة بتحدد إيه اللـ ممكن يحصل عليها:

  • PAGE_NOACCESS:
  • → معناها إن المنطقة دي مفيش أي صلاحيات عليها ، لا قراءة ، ولا كتابة ، ولا تنفيذ.

  • PAGE_READONLY:
  • → المنطقة دي قابلة للقراءة بس ، لكن لا كتابة ولا تنفيذ.

  • PAGE_EXECUTE_READWRITE:
  • → هنا الصفحة واخدة كل الصلاحيات ، تقدر تقرأ ، وتكتب ، وتشغل كود برمجي فيها.

[+] طيب إيه اللـ بيحصل لو حاولت تعدي حدودك وتتجاوز الصلاحيات؟

  1. DEP – حماية من تنفيذ الأكواد غير المسموح بيها
  2. فـ نظام الويندوز ، فيه ميكانيزم أمان اسمها DEP (Data Execution Prevention) ، واللـ بتشتغل ع منع تشغيل أي كود فـ ذاكرة مش مصرح بيها للتنفيذ.

    بمعنى إن لو عندك منطقة فـ الميموري واخدة صلاحية READONLY ، وجيت تحاول تشغل كود منها ، الـ DEP هيتدخل ويقولك: "بس يبابا ، روح إلعب بعيد .. مش هسمحلك!" ، وده مهم جدًا عشان يمنع الـ Buffer Overflow Exploits.

  3. ASLR – حماية من التلاعب بعناوين الذاكرة
  4. فيه كمان تقنية أمان تانية اسمها ASLR (Address Space Layout Randomization) ، واللـ فكرتها ببساطة إنها بتخلي أماكن تخزين البيانات فـ الذاكرة عشوائية كل مرة البرنامج يشتغل فيها .. وده بيمنع الهاكرز من معرفة أماكن البيانات أو الأكواد فـ الميموري ، وده بيصعّب عليهم تنفيذ أي Memory Exploits ، زي هجمات Return-Oriented Programming (ROP).

[+] تعالي أقولك مثال عملي: إزاي النظام بيدير الميموري بين برنامجين؟

خلينا نفترض إنك فاتح Firefox.exe و Onenote.exe فـ نفس الوقت .. كل واحد منهم بيكون ليه Virtual Memory Space خاصة بيه ، وبالتالي مفيش برنامج يقدر يدخل ع بيانات البرنامج التاني مباشرة.

السيناريو الطبيعي كالأتي:

  1. لما تفتح Firefox.exe ، النظام بيخصص ليه مجموعة من الـ Pages فـ الميموري.
  2. نفس الكلام بيحصل مع Onenote.exe ، وليه مساحة منفصلة خاصة بيه.
  3. لو الـ Firefox محتاج يشارك بيانات مع Onenote ، لازم يتم ده من خلال آلية اسمها Inter-Process Communication (IPC) ، مش بشكل مباشر فـ الميموري.
HomeLab

[+] طب إيه اللـ ممكن يحصل لو حصل تجاوز؟

لو برنامج حاول يدخل ع ذاكرة برنامج تاني ، النظام هيمنعه ويرجع Access Violation Error .. ولو برنامج حاول ينفذ كود فـ منطقة مش واخدة صلاحية التنفيذ ، الـ DEP هيتدخل ويمنعه .. ولو هكر جامد جمودة حاول يحدد أماكن البيانات فـ الميموري عشان يستغلها ، الـ ASLR هيخليه تايه ومش عارف يوصل لحاجة.

[+] Memory Allocation in Windows – إدارة الذاكرة عمليًا

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

[ملحوظه مهمه: محتاج يكون عندك خلفية عن لغة برمجة الـ C أو الـ (++C) ، وتقدر تشوف كورس م. أسامة الزيرو ع اليوتيوب من [هنا] ، أو أي كورس تاني عموما .. المهم يكون عندك الأساسيات.]

فيه ٣ طرق أساسية لحجز مكان فـ الميموري:

  1. ()Malloc – تابعة للـ C Runtime Library
  2. ()HeapAlloc – تابعة لـ Windows API
  3. ()LocalAlloc – تابعة لـ Windows API برضه

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;
}

شرح الكود سطر بسطر:

  • Stdio.h → استدعاء مكتبة Standard I/O لاستخدام دالة ()printf.
  • stdlib.h → مكتبة فيها ()malloc
  • string.h → مكتبة التعامل مع الـ strings
  • Windows.h → استدعاء مكتبة Windows API.
  • بدأت أعمل variable من نوع PVOID وسميته pAddress ، ودا اللـ هيكون فيه المنطقة اللـ حجزتها فـ الميموري
  • malloc(200) → بتحجز 200 بايت فـ الميموري وبتُرجع Pointer لأول مكان محجوز.
  • بعدها عملت variable تاني من نوع *CHAR وكتبت فيه string.
  • memcpy(pAddress, cString, strlen(cString)) → بتنقل الـ String للمنطقة المحجوزة ، ودي بتاخد 3 arguments وهما ، عنوان المنطقة اللـ حجزت فيها ، الـ string ، وحجم الـ string اللـ حابب أنقله.
  • printf("Address: 0x%p\n", pAddress) → بتطبع العنوان اللـ تم حجزه.
  • ()getchar → عشان أخلي البرنامج ميقفلش.

دلوقتي هنبدأ نـ رن الكود ونشوف ايه اللـ هيحصل:

  1. من الـ Visual Studio 2022 هنـ رن الـ Local Windows Debugger
  2. code1
  3. هيظهر العنوان اللـ اتحجز فيه المنطقة بتاعتنا ، واللـ موجود فيه الـ string حاليا ، هناخده كوبي.
  4. code2
  5. هنروح ع الـ Debug > Windows > Memory > Memory 1.
  6. code3
  7. هنوقف العملية لما نضغط ع علامة الـ Break All
  8. هنضيف العنوان اللـ ظهرلنا واللـ خدناه كوبي ونبحث عنه
  9. هنلاقي المكان اللـ حجزناه ونقلنا فيه الـ string بتاعنا بنجاح.
  10. code4

2. ()HeapAlloc – الحجز باستخدام Windows API

دي بتتعامل مع Windows Heap Manager ، واللـ هو طريقة الويندوز فـ تنظيم الميموري بدل مـ يعتمد على ()malloc.

الشكل العام:

LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
  • hHeap → الـ Handle للـ Heap ، وده بنجيبه باستخدام ()GetProcessHeap.
  • dwFlags → ممكن يكون 0 أو HEAP_ZERO_MEMORY لو عاوز تصفر الميموري قبل الاستخدام.
  • 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؟

  • ()HeapAlloc بتشتغل على Process Heap ، وده بيدي تحكم أكتر فـ الميموري.
  • ()Malloc بتستخدم C Runtime Library وبتتعامل مع الميموري بطريقة عامة.

3. ()LocalAlloc – الحجز باستخدام Windows API (قديم شوية)

دي بتُستخدم لحجز ميموري ، بس أغلب المطورين بيفضلوا ()HeapAlloc بدلها لأنها بتدي تحكم أكتر.

الشكل العام:

HLOCAL LocalAlloc(UINT uFlags, SIZE_T uBytes);
  • uFlags → الفلاج اللـ بيحدد طريقة الحجز (مثلاً LPTR عشان يصفر الميموري).
  • 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);

ودا مثال لتفريغ منطقة كانت محجوزة ..

code5

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

References

خدلك لفة فـ المصادر والمراجع يا عزيزي:

لو حابب تـ dive deep فـ ممكن تشوف الكتب دي: