I create a process using CreateProcess()
with the CREATE_SUSPENDED
and then go ahead to create a little patch of code inside the remote process to load a DLL and call a function (exported by that DLL), using VirtualAllocEx()
(with ..., MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE
), WriteProcessMemory()
, then call FlushInstructionCache()
on that patch of memory with the code.
After that I call CreateRemoteThread()
to invoke that code, creating me a hRemoteThread
. I have verified that the remote code works as intended. Note: this code simply returns, it does not call any APIs other than LoadLibrary()
and GetProcAddress()
, followed by calling the exported stub function that currently simply returns a value that will then get passed on as the exit status of the thread.
Now comes the peculiar observation: remember that the PROCESS_INFORMATION::hThread
is still suspended. When I simply ignore hRemoteThread
's exit code and also don't wait for it to exit, all goes "fine". The routine that calls CreateRemoteThread()
returns and PROCESS_INFORMATION::hThread
gets resumed and the (remote) program actually gets to run.
However, if I call WaitForSingleObject(hRemoteThread, INFINITE)
or do the following (which has the same effect):
DWORD exitCode = STILL_ACTIVE;
while(STILL_ACTIVE == exitCode)
{
Sleep(500);
if(!GetExitCodeThread(hRemoteThread, &exitCode))
break;
}
followed by CloseHandle()
this leads to hRemoteThread
finishing before PROCESS_INFORMATION::hThread
gets resumed and the process simply "disappears". It is enough to allow hRemoteThread
to finish somehow without PROCESS_INFORMATION::hThread
to cause the process to die.
This looks suspiciously like a race condition, since under certain circumstances hRemoteThread
may still be faster and the process would likely still "disappear", even if I leave the code as is.
Does that imply that the first thread that gets to run within a process becomes automatically the primary thread and that there are special rules for that primary thread?
I was always under the impression that a process finishes when its last thread dies, not when a particular thread dies.
Also note: there is no call to ExitProcess()
involved here in any way, because hRemoteThread
simply returns and PROCESS_INFORMATION::hThread
is still suspended when I wait for hRemoteThread
to return.
This happens on Windows XP SP3, 32bit.
Edit: I have just tried Sysinternals Process Monitor to see what's happening and I could verify my observations from before. The injected code does not crash or anything, instead I get to see that if I don't wait for the thread it doesn't exit before I close the program where the code got injected. I'm thinking whether the call to CloseHandle(hRemoteThread)
should be postponed or something ...
Edit+1: it's not CloseHandle()
. If I leave that out just for a test, the behavior doesn't change when waiting for the thread to finish.