EpochZero Learn
EpochZero LearnMulti-Domain Tech Learning Hub
All videos
Ep. 4.1reverse-engineering

The Explainer: Dissecting Process Hollowing

8 May 20261 views

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.

What hollowing achieves

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.

The seven steps

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.

Step 1 — Suspended creation

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.

Step 2 — Locating the PEB and ImageBase

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.

Step 3 — NtUnmapViewOfSection

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.

Step 4 — Re-allocate at the same base

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.

Step 5 — Write 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.

Step 6 — Update the thread context

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

Step 7 — Resume

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.

Detection indicators

Even though hollowing is sophisticated, it leaves traces:

  • Image-on-disk vs image-in-memory mismatch. The single strongest indicator. The bytes of the running process do not match the bytes of the file on disk. Tools like pe-sieve and hollow_hunter check this directly.
  • CREATE_SUSPENDED creation. Procmon shows the suspended-state creation. Legitimate uses of CREATE_SUSPENDED are rare outside of debuggers and a handful of Microsoft tools.
  • NtUnmapViewOfSection on the image base. This API call against the calling process's image base is exceedingly rare in benign code. EDRs flag it.
  • WriteProcessMemory on a child process. Particularly when followed by ResumeThread. Some EDRs raise alerts on the combination.
  • Cross-process thread context modification. 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.

What you should be comfortable with after this lesson

  • Naming all seven steps in order and the API for each
  • Explaining why CREATE_SUSPENDED is essential
  • Stating which detection indicator each step produces
  • Recognising hollowing in a live Procmon log within seconds
Section 03

References

Section 04

Exercises

EX.01hard

Walk through the API sequence

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.

EX.02medium

Compare in-memory to on-disk

Use pe-sieve against a hollowed process. Examine the dumped images alongside the on-disk svchost.exe. Show that the in-memory image differs.

EX.03medium

Spot the suspicious creation

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.