“The primary mechanism for managing running code is the thread, not the process”.

So what is a process?
- virtual address space (that is, the value used for CR3)
- access token, including SID
- handle table for win32 kernel objects
- working set (physical memory “owned” by the process)
Rather than creating new processes, it’s possible to create new threads and assign them to an existing process. Rarely would a whole new process need to be created. When a context switch to a new thread occurs, the old thread state is saved.
Each thread has its own kernel stack, so the thread state is pushed onto the top of the thread kernel stack. If the new thread belongs to a different process, the new page directory address for the new process is loaded into CR3.
The page directory address can be found in the KPROCESS structure for the process. Once the new thread kernel stack is found, the new thread context is popped from the top of the new thread kernel stack, and the new thread begins execution.
If a rootkit modifies the page tables of a process, the modifications will be applied to all threads in that process, because the threads all share the same CR3 value
MZ-Start is the address of the module as it is currently loaded into memory. Preferred ImageBase is parsed straight from the PE Header and is the location that it prefers to be loaded into. If this memory address is already taken, it will relocate.
When an .exe is executed, the windows loader create a process for it and give it it’s own virtual memory space. The loader loads the executable into memory and then any .dlls that are called by the process. The PE header for the .dll defines a ImageBase address. The windows loader will try to load the .dll into the virtual memory space of the process that requires it. If that space is already occupied, it will be loaded into a different location.
https://windows-internals.com/thread-and-process-state-change/