Local Thread Hijacking Inject DLL into Main Thread

This article logs the interesting things happened when I inject a DLL into the main thread of Notepad.exe.

The main thread is the thread which handles all code execution, so what happens if I inject a dll into the main thread?

In order to hijack the thread I have to first suspend it. Nothing went wrong, I can suspend the thread, and I can update the Context to point Rip to my payload.

But the payload is not executed, and Notepad.exe is dead when I tried to input anything into it.

If I tried to input something to freeze Notepad.exe, then close the app, the process will be terminated.

But, if I don't input anything, close Notepad.exe right after I hijack the main thread, Notepad.exe process maintains running in the background.

Looking at Threads, I got a kernel32.dll!LoadLibraryW thread in Suspended state.

file

I guess it's because the main thread is hijacked, all the code just cannot be processed, that means the call to LoadLibrary too.

So, what I did is to manually resume this kernel32.dll!LoadLibraryW thread (I tried to resume the notepad.exe one, nothing happened).

Code below:

#include <Windows.h>
#include <stdio.h>
#include <Tlhelp32.h>

BOOL GetRemoteThreadHandle(OUT DWORD* dwThreadId, OUT HANDLE* hThread)
{
    DWORD dwProcessId = 25260;
    HANDLE hSnapShot = NULL;

    THREADENTRY32 stThEntry = {
        .dwSize = sizeof(THREADENTRY32)
    };

    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);

    if (INVALID_HANDLE_VALUE == hSnapShot)
        goto _EndOfFunction;

    if (!Thread32First(hSnapShot, &stThEntry))
        goto _EndOfFunction;

    do
    {
        if (stThEntry.th32OwnerProcessID == dwProcessId && stThEntry.th32ThreadID == 34672)
        {
            *dwThreadId = stThEntry.th32ThreadID;
            *hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, stThEntry.th32ThreadID);

            if (NULL == *hThread)
                return FALSE;

            break;
        }
    } while (Thread32Next(hSnapShot, &stThEntry));

_EndOfFunction:
    if (NULL != hSnapShot)
        CloseHandle(hSnapShot);
    if (NULL == *dwThreadId || NULL == *hThread)
        return FALSE;

    return TRUE;
}

int main()
{
    DWORD dwThreadId = NULL;
    HANDLE hThread = NULL;

    if (!GetRemoteThreadHandle(&dwThreadId, &hThread))
    {
        printf("[-]Failed to get thread handle...\n");
        return -1;
    }

    ResumeThread(hThread);

    printf("[*]Done...\n");

    return 0;
}

And after executing it, calculator pops up.

file

Notice that, the state of the thread turned to WrUserRequest, meaning that it resumed.

Beased on the behavior:

  1. kernel32.dll!LoadLibraryW is created as a worker thread, but why in suspended state?
  2. main thread is dead, but what kept the process running in the background even after kernel32.dll!LoadLibraryW is resumed and finished executing?

file


Edit:
May have a clue here.

Inspecting the thread Stack here, the next job to be executed is ntdll.dll!NtSuspendThread.

file

Since the main thread is suspended, the code stops executing.

After resuming the main thread, now the thread Stack is going to execute ZwWaitForSingleObject, meaning that my payload is executed.

file

Still a question, which one is the main thread in the Threads tab?