Level 0 · Chapter 1

Executable loading & runtime

The bridge between 'a process exists' and 'a Windows program is actually running': images, imports, and runtime metadata.

System architecture described a process as a container the Executive creates — an address space, a security token, a handle table. But an empty address space doesn't run anything. This level starts at the point where that container gets filled in: with a mapped executable image, its dependencies, and the runtime bookkeeping Windows needs to keep track of all of it.

This chapter is the overview; the next three go one layer deeper into the pieces that make it work: the PE format the image itself is written in, the DLL loader that finds and maps dependencies, and WOW64, the compatibility layer that lets 32-bit code run alongside a 64-bit kernel.

Windows doesn't run source, and it doesn't run raw bytes either

A .exe file isn't a flat blob of machine code that gets copied into memory and jumped to. It's a structured image — headers, sections, tables — that tells Windows exactly how to turn a file on disk into a running address space. The loader's job is to read that structure and act on it:

  1. Parse the image. Read the PE headers to find out what sections exist (code, data, resources), where they should live in memory, and what permissions each one needs (executable code is mapped read+execute, not read+write, precisely so a bug can't casually turn into arbitrary code execution).
  2. Map it into virtual memory. The Memory Manager reserves address space for each section, at the image's preferred base address if that address is free — or somewhere else if it isn't, which is where relocations come in.
  3. Resolve imports. The image lists the DLLs and functions it depends on (kernel32.dll!CreateFileW, for instance). The loader has to find each dependency, load it if it isn't already loaded, and patch the caller's import table with the real function addresses.
  4. Apply relocations if needed. If a DLL couldn't load at its preferred address (because something else got there first, or because of ASLR — Address Space Layout Randomization — deliberately picking a random one), every absolute address baked into its code has to be patched to match wherever it actually landed.
  5. Run initialization code. Each DLL gets one chance to run its own setup code (DllMain) before the program's own entry point runs.
  6. Build runtime metadata. Structures like the PEB (Process Environment Block) and TEB (Thread Environment Block) get populated so that both the running program and external tools (debuggers, Process Explorer, EDR) can see what's loaded and how the process was started.

None of this is exotic — it happens for every process, every time, including the one running your web browser right now. It's just invisible, because it all finishes before your code's main() runs.

Why this matters beyond "programs start"

A huge amount of practical Windows work sits directly on top of loader behavior:

  • Security: DLL search order and hijacking (see the DLL loader chapter) is one of the most common persistence and privilege-escalation techniques, precisely because it abuses step 3 above.
  • Debugging: symbols, module lists, and "why did this crash before main even ran" questions are all loader questions.
  • Compatibility: WOW64 exists specifically to keep step 1–6 working for 32-bit binaries on a 64-bit kernel, without the kernel itself having to understand two instruction sets.
  • Performance: the "why is my app slow to start" question is very often "how many DLLs, in what search order, from what storage, did the loader have to resolve before your code ran."

A process is not "an EXE in memory"

The common intuition — that a process is just your program's code sitting in RAM — undersells what's actually there by the time your first line executes: your image, every dependency it pulled in (often dozens of DLLs, most of them from the OS itself), one or more heaps, a stack per thread, and the PEB/TEB metadata tracking all of it. Tools like Process Explorer's "DLL view" or dumpbin /imports are really just windows into this loader-built state.

Where this connects

  • The image the loader maps is placed inside the address space Memory management is responsible for — the loader decides what goes where; the Memory Manager decides how virtual memory actually backs it.
  • Before any of this runs, the I/O system had to open the file from the filesystem.
  • The very first user-mode instruction after all this setup usually calls straight into Ntdll, the thinnest layer between a running program and the kernel.