W1nD0w5 1N73rN4L5

W1nD0w5 1N73rN4L5

Tijdens het uitvoeren van privilege escalation op een Windows-machine liep ik tegen een grens aan: mijn gebrek aan inzicht in hoe Windows intern van binnen werkt. Processen, services, gebruikersrechten en kernel-interacties — het voelde alsof ik op knoppen drukte zonder echt te begrijpen wat erachter gebeurde.

In deze blog begin ik met het verkennen van Windows Internals: niet als theoretische bijzaak, maar als noodzakelijke basis om scherper, gerichter en dieper te kunnen escaleren in toekomstige labs. In deze blog vind je mijn samenvatting van de Windows Internals module op THM.

Processes

Wat is een proces?

Een proces is wat er gebeurt als je een programma opent op je computer. Bijvoorbeeld: als je Word of Spotify start, dan begint er een proces.

Een proces zorgt ervoor dat het programma kan draaien. Het krijgt daarvoor een stukje geheugen, code om uit te voeren, en toegang tot bestanden of instellingen die nodig zijn.

Elke proces heeft:

  • Een naam (zoals conhost.exe)
  • Een uniek nummer (PID) om het te herkennen
  • Een gebruiker die het gestart heeft
  • Een status (zoals “actief” of “gepauzeerd”)

Wat is een thread?

Een thread is als een klein werkmannetje binnen het proces dat de taken uitvoert.

Sommige processen hebben maar één thread, andere hebben er meerdere die tegelijk dingen doen.


Voorbeelden van processen in Windows:

  • MsMpEng = Windows Defender
  • wininit = regelt je toetsenbord en muis
  • lsass = slaat je wachtwoorden veilig op

Waarom zijn processen belangrijk voor hackers?

Hackers proberen soms processen te gebruiken of verstoppen, zodat hun kwaadaardige software niet wordt opgemerkt. Ze gebruiken technieken zoals:

  • Process injection = kwaadaardige code in een normaal proces stoppen
  • Process hollowing = een proces openen, de inhoud vervangen
  • Masquerading = zich voordoen als een bekend proces

Hoe kun je processen bekijken?

Met Taakbeheer op Windows kun je processen bekijken:

  • Je ziet de naam van het proces
  • Wie het heeft gestart
  • Wat het aan het doen is
  • En hoeveel geheugen of CPU het gebruikt

Er zijn ook andere tools die meer laten zien, zoals:

  • Process Hacker
  • Process Explorer
  • Procmon

Opdrachten processes:

  • What is the process ID of "notepad.exe"?
    • 5984
  • What is the parent process ID of the previous process?
    • 3412. Als we in procmon switchen naar tree overzicht dan zien we dat de parent ID van Explorer.EXE is.
  • What is the integrity level of the process?
    • High

Threads

Wat is een thread?

Een thread is een klein onderdeel van een proces dat het echte werk uitvoert. Het is als een werkkracht die de instructies van het programma stap voor stap uitvoert. De computer kiest wanneer welke thread aan de beurt is, afhankelijk van dingen zoals de snelheid van de processor, hoeveel geheugen er is, en hoe belangrijk de taak is.

Een makkelijke manier om het uit te leggen:

Een thread regelt de uitvoering van een proces.

Omdat threads verantwoordelijk zijn voor het uitvoeren van de code, worden ze vaak misbruikt door hackers. Ze gebruiken threads om hun eigen code te draaien of als onderdeel van grotere aanvallen.


Wat deelt een thread met zijn proces?

Een thread deelt veel dingen met het proces waar het bij hoort:

  • De code van het programma
  • Geheugen
  • Variabelen

Maar een thread heeft ook een paar eigen onderdelen:

Onderdeel Wat doet het?
Stack Bevat alle gegevens die alleen bij die thread horen (zoals fouten of functies).
Thread Local Storage Zorgt ervoor dat elke thread z'n eigen gegevens kan opslaan.
Stack Argument Een uniek kenmerk dat elke thread meekrijgt.
Context Structure Slaat de huidige status van de processor op voor die thread.

Opdrachten threads:

  • What is the thread ID of the first thread created by notepad.exe?
    • 5908
Voeg bij Process Monitor Filter de entry ‘Operation’ toe met als Value Thread Create.
  • What is the stack argument of the previous thread?
    • 6584

Vervolgens zien we bij Event Properties een Thread staan.

Virtual Memory

Virtueel geheugen is een belangrijk onderdeel van hoe Windows van binnen werkt. Het zorgt ervoor dat programma’s met geheugen kunnen werken alsof het echt fysiek geheugen is, terwijl dat niet altijd zo is. Hierdoor botsen programma’s niet met elkaar als ze allebei geheugen gebruiken.


Hoe werkt dat?

  • Elk programma (proces) krijgt zijn eigen virtuele geheugenruimte.
  • Een geheugenbeheerder vertaalt de virtuele adressen naar de echte (fysieke) adressen in het RAM-geheugen.
  • Omdat programma’s niet direct schrijven naar het echte geheugen, is de kans kleiner dat ze fouten maken of schade veroorzaken.

Wat als er niet genoeg RAM is?

Als een programma meer geheugen nodig heeft dan er beschikbaar is in het RAM:

  • De geheugenbeheerder verplaatst (of “paged”) delen van het geheugen naar de harde schijf.
  • Zo lijkt het alsof er meer geheugen is dan er echt is.
  • De geheugenbeheerder gebruikt ook pagina's of overdrachten om het geheugen te beheren. Soms gebruikt een applicatie meer virtueel geheugen dan er fysiek geheugen beschikbaar is. Om dat probleem op te lossen, verplaatst de geheugenbeheerder delen van het virtuele geheugen naar de harde schijf. Dit heet pagineren of swappen.

32-bit systemen:

  • Het maximale virtuele geheugen is 4 GB.
  • Die 4 GB wordt verdeeld in twee delen:
    • Onderste helft (0x00000000 - 0x7FFFFFFF) → voor programma’s.
    • Bovenste helft (0x80000000 - 0xFFFFFFFF) → voor Windows zelf.
  • Beheerders kunnen deze indeling aanpassen via instellingen zoals increaseUserVA of via AWE (Address Windowing Extensions) als een programma meer geheugen nodig heeft.

64-bit systemen:

  • Hier is het maximale virtuele geheugen 256 terabyte (TB).
  • De verdeling van het geheugen is vergelijkbaar met 32-bit, maar dan veel groter.
  • Meestal zijn er geen speciale instellingen meer nodig dankzij deze ruime capaciteit.

Oefenvragen:

  • What is the total theoretical maximum virtual address space of a 32-bit x86 system?
    • 4 GB
  • What default setting flag can be used to reallocate user process address space?
    • increaseuserva
  • What is the base address of "notepad.exe"?
    • 0x7ff652ec0000
We voegen als entry ‘Operation is Load Image’ toe.
We vinden de Address in de event properties

Dynamic Link Libraries

Wat is een DLL?

Microsoft beschrijft een DLL als:

“Een bibliotheek die code en gegevens bevat die door meerdere programma’s tegelijk kunnen worden gebruikt.”

Met andere woorden: een DLL is een soort hulpprogramma-bestand dat meerdere programma’s kunnen gebruiken. In plaats van dat elk programma dezelfde code kopieert, delen ze één DLL. Dit bespaart geheugen en opslagruimte.


Waarom gebruikt Windows DLL’s?

Volgens Microsoft helpt het gebruik van DLL’s om:

  • Code beter te organiseren (modulair maken)
  • Code herbruikbaar te maken
  • Minder geheugen en schijfruimte te gebruiken

Hierdoor starten programma’s sneller, werken ze efficiënter en nemen ze minder ruimte in.


Gevaar: DLL’s kunnen ook worden misbruikt

Omdat programma’s afhankelijk zijn van DLL’s, kunnen aanvallers zich richten op de DLL’s in plaats van het programma zelf. Voorbeelden van zulke aanvallen zijn:

  • DLL Hijacking: Het programma laadt een verkeerde (kwaadaardige) DLL.
  • DLL Side-Loading: Een nep-DLL wordt naast een echt programma geplaatst.
  • DLL Injection: Kwaadaardige code wordt via een DLL in een ander proces geïnjecteerd.

Hoe ziet een DLL eruit in code?

Een DLL is eigenlijk een normaal programma, maar met een paar aanpassingen. Hieronder een voorbeeld in C++ van een eenvoudige DLL die een pop-up toont met "Hello World":

void HelloWorld()
{
    MessageBox(NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

De bijbehorende header file geeft aan welke functies toegankelijk zijn voor andere programma’s:

extern __declspec(dllexport) void HelloWorld();


Hoe gebruik je een DLL in een programma?

Er zijn twee manieren om een DLL te gebruiken:


1. Load-time linking (bij het opstarten)

De DLL wordt meteen geladen als het programma start.

Je hebt een .h-bestand en een .lib-bestand nodig.

De functies in de DLL worden rechtstreeks aangeroepen:

HelloWorld();  // Wordt meteen uitgevoerd


2. Run-time linking (tijdens het draaien)

De DLL wordt pas geladen terwijl het programma al draait.

Je gebruikt dan functies zoals LoadLibrary() en GetProcAddress() om de DLL en de functie op te halen:

hinstDLL = LoadLibrary("sampleDLL.dll");
HelloWorld = (DLLPROC)GetProcAddress(hinstDLL, "HelloWorld");
(HelloWorld)();


Waarom gebruiken hackers vooral run-time linking?

Deze methode is flexibeler en minder opvallend.

Kwaadaardige software kan DLL’s dynamisch laden zonder vaste bestanden of headers.

Het maakt het ook makkelijker om data tussen geheugenlocaties te verplaatsen of functionaliteit heimelijk uit te voeren.


Oefenvragen:

Oefenvragen:

  • What is the base address of “ntdll.dll” loaded from “notepad.exe”
    • 0x7ffd0be20000
  • What is the size of “ntdl.dll” loaded from “notepad.exe”
    • 0x1ec000
  • How many DLLs were loaded by “notepad.exe”?
    • 51

Portable Executable Format

Wat is een PE-bestand?

Programma’s in Windows (zoals .exe-bestanden) gebruiken een speciale structuur die PE (Portable Executable) wordt genoemd. Deze structuur bepaalt hoe het bestand is opgebouwd, welke gegevens erin zitten, en hoe Windows het moet uitvoeren.

De PE-structuur wordt ook gebruikt in combinatie met COFF-bestanden (Common Object File Format).

Je kunt PE-gegevens bekijken in een zogenaamde hex dump (een weergave van de inhoud van een bestand in hexadecimale code).


Hoe is een PE-bestand opgebouwd?

Een PE-bestand bestaat uit verschillende onderdelen (headers en secties). Hieronder vind je de belangrijkste onderdelen, simpel uitgelegd:


1. DOS Header

  • Dit is het eerste deel van het bestand.
  • Het bevat het kenmerk "MZ" wat betekent dat het een .exe-bestand is.
  • Oudere systemen zoals DOS konden deze bestanden niet draaien.
  • Daarom zit er een DOS-stub in, die een foutmelding toont:"This program cannot be run in DOS mode."

2. PE Header

  • Dit deel begint met het kenmerk "PE".
  • Het geeft aan dat dit een Windows-programma is.
  • Het bevat informatie zoals:
    • Het soort bestand
    • Het aantal secties
    • Wanneer het bestand is gemaakt
    • Hoe groot het is
    • Wat voor systeem het nodig heeft om te draaien

Dit deel is vaak niet leesbaar voor mensen, maar erg belangrijk voor Windows.


3. Image Optional Header

  • De naam zegt “optioneel”, maar dit deel is wel degelijk belangrijk.
  • Hierin staan:
    • Adressen
    • Grootte van het geheugen dat nodig is
    • Informatie over waar de uitvoerbare code begint
    • Tabellen die verwijzen naar extra onderdelen

4. Section Table

  • Hier worden de verschillende delen van het programma gedefinieerd.
  • Denk aan de code zelf, variabelen, afbeeldingen, DLL’s, enzovoort.

Hieronder zie je een paar veelvoorkomende secties:

Sectie Doel
.text Bevat de uitvoerbare code van het programma
.data Bevat gegevens zoals tekst of variabelen
.rdata / .idata Bevat informatie over DLL’s en Windows-functies (imports)
.reloc Bevat info over het verplaatsen van het programma in geheugen
.rsrc Bevat hulpbronnen zoals afbeeldingen of iconen
.debug Bevat informatie voor foutopsporing (debugging)

Waarom is dit belangrijk?

Door te begrijpen hoe een PE-bestand in elkaar zit, kun je:

  • Leren hoe Windows programma’s start en beheert
  • Begrijpen hoe programma’s communiceren met DLL’s en het geheugen
  • Beter analyseren of manipuleren van programma’s (bijvoorbeeld bij reverse engineering of malware-analyse)

Oefenvragen:

  • What PE component prints the message "This program cannot be run in DOS mode"?
    • DOS Stub
  • Open "notepad.exe" in Detect It Easy and answer the questions below.
  • What is the entry point reported by DiE?
    • 000000014001acd0
  • What is the value of “NumberOfSections?
    • 0006
  • Wat is the virtual address of “.data”?
    • 00024000
  • What string is located at the offset “0001f99c”?
    • Microsoft.Notepad
Gevonden onder ‘strings’

Interacting with Windows Internals

De Windows API is een verzameling van functies die je kunt gebruiken om dingen te doen binnen Windows, zoals programma’s starten, geheugen gebruiken of bestanden openen. De meest gebruikte versie is de Win32 API, en minder vaak de Win64 API.


Hoe werkt dit op technisch niveau?

Veel onderdelen van Windows werken dicht bij het geheugen en de hardware van je computer.

Daarom is er een kern (kernel) die alles beheert:

  • Het houdt controle over programma’s en processen
  • Het zorgt dat software en hardware goed samenwerken

Je kunt niet zomaar van een gewoon programma direct naar de kernel of hardware schrijven. Daarvoor zijn speciale manieren nodig — dit heet werken met modes.


Wat zijn user mode en kernel mode?

Je processor heeft twee standen:

User Mode (normaal programma) Kernel Mode (systeemniveau)
Geen directe toegang tot hardware Directe toegang tot hardware en geheugen
Alleen toegang tot eigen geheugen Toegang tot alle geheugen op de computer
Veiliger, maar beperkt Machtiger, maar gevaarlijk als fout gemaakt wordt

Programma’s draaien standaard in user mode, en schakelen alleen over naar kernel mode als ze via een API-call of system call iets diepers moeten doen.

Bijvoorbeeld: een programma dat een bestand wil openen, roept een functie aan uit de Windows API → die schakelt over naar kernel mode om de echte actie uit te voeren.


Programmeertalen en de Win32 API

Sommige programmeertalen, zoals C#, gaan eerst via hun eigen "taalomgeving" (zoals de CLR) voordat ze via de API praten met Windows. Hierdoor ziet het proces er soms iets ingewikkelder uit.


Voorbeeld: Messagebox injecteren in een ander proces

We gaan laten zien hoe je via geheugeninjectie een messagebox kunt starten in een ander proces — als proof-of-concept.

Stappen:

  1. Geheugen reserveren in het doelproces
  2. De code (payload) schrijven in dat geheugen
  3. De code uitvoeren via een nieuwe thread

Stap 1: Proces openen

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, <proces-ID>);

→ Hiermee open je het doelproces en krijg je toegang tot het geheugen ervan.


Stap 2: Geheugen reserveren

remoteBuffer = VirtualAllocEx(hProcess, NULL, sizeof payload, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);

→ Hiermee maak je ruimte vrij in het geheugen van het doelproces, zodat je daar iets in kunt schrijven én uitvoeren.


Stap 3: Payload schrijven

WriteProcessMemory(hProcess, remoteBuffer, payload, sizeof payload, NULL);

→ Hiermee schrijf je jouw code (payload) in het eerder gereserveerde geheugen.


Stap 4: Code uitvoeren in doelproces

CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);

→ Hiermee start je een nieuwe thread in het doelproces, die jouw code (bijvoorbeeld een messagebox) uitvoert.


Waarom is dit belangrijk?

Deze techniek is de basis van veel legitieme én kwaadaardige toepassingen, zoals:

  • Debuggers
  • Penetration testing tools
  • Malware

Het begrijpen van API-calls en geheugeninteractie is een essentieel onderdeel van Windows internals en ethical hacking.


Oefenvragen:

  • Enter the flag obtained from the executable below: