It's actually quite easy. Similar technique has been described in a paper I read like 3 years ago.
Windows allow you to call the CreateProcess function with CREATE_SUSPENDED flag, that tells the API to keep the process suspended until the ResumeThread function is called.
This gives us time to grab the suspended thread's context using GetThreadContext function, then the EBX register will hold a pointer to the PBE(Process Enviroment Block) structure, which we need to determine the base address.
From the layout of the PBE structure we can see that the ImageBaseAddress is stored at the 8th byte, therefore [EBX+8] will give us actual base address of the process being suspended.
Now we need the in-memory EXE and do appropiate alignment if the alignment of memory and in-memory EXE differs.
If the base address of suspended process and in-memory exe matches, plus if the imageSize of the in-memory exe is lesser or equal to the suspended process' we can simply use WriteProcessMemory to write in-memory exe into the memory space of the suspended process.
But if the aforementioned conditions weren't met, we need a little more magic.
First, we need to unmap the original image using ZwUnmapViewOfSection, and then allocate enough memory using VirtualAllocEx within the memory space of the suspended process. Now we need to write the in-memory exe into the memory space of the suspended process using the WriteProcessMemory function.
Next, patch the BaseAddress of the in-memory exe into the PEB->ImageBaseAddress of the suspended process.
EAX register of the thread context holds EntryPoint address, which we need to rewrite with the EntryPoint address of the in-memory exe. Now we need to save the altered thread context using the SetThreadContext function.
Voila! We're ready to call the ResumeThread function on the suspended process to execute it!
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…