Sign in to track your watch progress.
Process hollowing in detail. The seven API calls, what the target process looks like at each step, and the indicators that betray hollowing during analysis.
A process listed in Task Manager as C:\Windows\System32\svchost.exe, signed by Microsoft, with the expected command line — but whose in-memory code is malicious. Hollowing replaces the body of a legitimate process while keeping the wrapper intact. The OS sees a normal process. Antivirus tools that whitelist by image path or signing certificate are bypassed. Behavioural monitors that check process names against expected baselines are bypassed.
This is why hollowing has been a favourite technique of organised crimeware (Dridex, Trickbot, Emotet) and APT operations (Lazarus, FIN7) for over a decade.
1. CreateProcess → spawn target in CREATE_SUSPENDED state
2. ZwQueryProcessInformation → locate the target's PEB, find ImageBaseAddress
3. ReadProcessMemory → read the original image base
4. NtUnmapViewOfSection → unmap the original executable from the target
5. VirtualAllocEx → allocate fresh memory at the same base address
6. WriteProcessMemory → write the malicious payload into the new region
7. SetThreadContext → point the suspended thread at the payload's entry point
ResumeThread → release the thread; the payload runs
Each step is necessary. Skipping any breaks the technique.
CreateProcessA(
"C:\\Windows\\System32\\svchost.exe", // legitimate target
cmdline, NULL, NULL, FALSE,
CREATE_SUSPENDED, // critical
NULL, NULL, &si, &pi
);
The CREATE_SUSPENDED flag means the process is loaded into memory but its primary thread does not start running. The image is in place, the PEB is set up, the entry point is queued — but no instruction has executed yet. This is the window in which the swap happens.
The Process Environment Block (PEB) sits at a known offset in every process. Inside the PEB, ImageBaseAddress points to where the executable image was loaded. The malware needs this address to know where to unmap and where to write the replacement.
ZwQueryProcessInformation (or NtQueryProcessInformation) with class ProcessBasicInformation returns a structure containing the PEB pointer.
NtUnmapViewOfSection(targetHandle, imageBaseAddress);
This removes the legitimate image from the target's address space. After this call, the address range that held svchost.exe is unmapped — accessing it would fault. The wrapper still holds the process metadata (name, PID, command line) but the body is gone.
VirtualAllocEx(targetHandle, imageBaseAddress, sizeOfImage,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
The same base address is requested deliberately. Many payloads contain absolute addresses (function pointers, jump tables) that assume their original base. Re-allocating at the original base avoids the need to perform PE base relocations on the payload.
WriteProcessMemory(targetHandle, imageBaseAddress, payloadBytes, sizeOfImage, NULL);
Section by section, the malicious image is written into the target's address space. Headers, code, data, imports — the full PE.
The suspended thread is currently set to begin at svchost.exe's original entry point. The payload's entry point is at a different offset. We retrieve the thread's CONTEXT structure, update the EAX (32-bit) or RCX (64-bit) register that holds the entry point address, and write it back:
GetThreadContext(targetThreadHandle, &ctx);
ctx.Eax = payloadEntryPoint; // or ctx.Rcx for 64-bit
SetThreadContext(targetThreadHandle, &ctx);
ResumeThread(targetThreadHandle);
The thread runs. Its first instruction is the payload's entry point. The legitimate svchost.exe code never executes. From now until the process exits, every instruction the OS schedules in this process is malicious code wearing a legitimate uniform.
Even though hollowing is sophisticated, it leaves traces:
pe-sieve and hollow_hunter check this directly.CREATE_SUSPENDED are rare outside of debuggers and a handful of Microsoft tools.ResumeThread. Some EDRs raise alerts on the combination.SetThreadContext on a thread in another process, especially modifying the entry point register, is highly suspicious.A modern EDR generally raises an alert on the API call combination even before the payload runs. This is one reason advanced malware now uses variants — Process Doppelgänging uses TxF transactions, Process Reimaging uses the section cache, Process Ghosting uses delete-pending files. Each variant changes which APIs are involved while preserving the result.
CREATE_SUSPENDED is essentialSection 03
Scans running processes for replaced or hooked code. The reference tool for hollowing detection.
toolDetailed defender's view of hollowing detection in production environments.
blogSub-technique reference with real-world threat group examples.
mitreSection 04
In a debugger, run any hollowing sample. Set breakpoints on each of the seven APIs in order. At each breakpoint, document the values being passed. By the end, you should have a complete trace of one hollowing event.
Use pe-sieve against a hollowed process. Examine the dumped images alongside the on-disk svchost.exe. Show that the in-memory image differs.
Filter Procmon for 'Process Create' events. Find a CREATE_SUSPENDED creation that is not made by Visual Studio, a debugger, or a known build tool. Treat that as a hollowing candidate.