So I was working on the next few entries on this blog, and a question occurred to me: are the base addresses of NTDLL and Kernel32 fixed? The reason I ask is that Inside Windows NT says that the first thing to be put in the the virtual address space of a new process (during process creation) is the executable, followed by NTDLL. Inside Windows NT doesn't mention Kernel32, but I was assuming that it got loaded sometime later.
So, I pull up my loyal test program ThingyTron and set it to load at 0x7C900000 and 0x7C800000 (in two seperate trials; the former is NTDLL's address on my system, the latter is Kernel32's). The results were rather humorous (and unexpected). In both cases, Windows killed the process halfway through startup, either giving no error message or something about "This program will not run correctly".
Even though I didn't really expect Windows to just kill the process if the executable tried to claim NTDLL's or Kernel32's space, it does make sense. Module (executable and DLL) loading is done in two parts: mapping the file into the address space, which can only be done from kernel mode, and preparing the module for execution (fixing up addresses, linking to DLLs, etc.), which is done in user mode. Both the executable and NTDLL, while they are mapped into the address space fairly early, do not get prepared for execution until much later (in fact they don't get prepared for execution until just before the program's main function gets called. LdrInitializeThunk, the user-mode function which performs the preparation of the modules in the new process is stored in NTDLL. So... is that a paradox? Not quite. The solution is that NTDLL requires no preparation to be able to run. Logicially, that requires that it have no addresses that need fixing up (among other requirements), meaning that it MUST be loaded at its ideal location.
Now we know.