Disclaimer: The implementation of these APIs is likely to change between versions of Windows. I will be referencing 32-bit Windows XP SP3 in my answer. Your results may vary.
How thread creation works
There are three structures that must be initialized before calling
INITIAL_TEB: Contains pointers to the stack region
CONTEXT: Contains the register state
OBJECT_ATTRIBUTES: Contains security attributes for the thread
In my implementation, there are dedicated functions that handle each of these tasks:
BaseInitializeContext function is the one you're interested in, however, since the new thread will begin at
BaseInitializeContext instead puts the thread's start address (i.e. the entry point of the new process) in
CONTEXT.Eip is set to the address of
kernel32 is mapped at the same address in every process, we know this will also be the address of
BaseProcessStartThunk in the other process)
So when we call
NtCreateThread, we start a new thread in the other process at
eax equal to the entry point.
BaseProcessStartThunk saves the start address from register
eax. It sets the start address internally by calling
NtSetInformationThread with a
ntddk.h). It then calls the start address. Finally, when the thread returns, it calls
How the executable image is mapped into the new process
If you want to know the process was created in the first place, we have to go back a few steps.
First, a handle to the new process executable is opened via
The file handle is used to create a section object via
A call to
InformationClass set to
SectionImageInformation is made. This parses the the section object and fills out a
SECTION_IMAGE_INFORMATION structure, which most notably includes fields the
EntryPoint field. This is how the entry point of the new process is determined.
NtCreateProcessEx is called, given the section handle from
NtCreateSection as a parameter. This is what actually creates the new process and maps the executable image into the new process' address space, among many other things.
NtCreateProcessEx also provides the process handle that we pass to
NtCreateThread to create the new thread.