The hitchhiker’s guide to Windows APIs for Process Injection. — Part 2

Raafat Abualazm
6 min readOct 4, 2023

--

Now let’s do process injection in the most stupid way, shall we?

Getting the code to inject

Getting shellcode that can be injected and executed is an easy task nowadays using tools like MSFVENOM. Although it is not the most OPSEC-friendly tool to be used, any EDR worth its salt — or not actually — will ring the bells upon seeing the output but is a good starting point.

MSFVENOM command to return a reverse meterpreter connection.

We need to keep note of the payload size, that’s why I have highlighted it. In our case the shellcode takes up 510 bytes.

Creating the process injection program

I am using Visual Studio Community 2022, but you are free to use any IDE/Text Editor with any C/C++ Compiler of your liking.

First create a rather basic Console App project in Visual Studio and import windows.h header file which includes references to other Windows header files and Windows-specific type definitions. In addition, we will need to put the shellcode from MSFVENOM in the main function.

A moment of silence for the victim

As a victim process, I have chosen Notepad, because I couldn’t think of any cheesier choice.

Having opened Notepad, we need to know its PID. This piece of information can be easily known using Task Manager in the “Details” tab.

Task Manager showing PID of Notepad

Excuse moi Windows, I need a Handle for Le Notepad

We will use OpenProcess from Kernel32.dll to get a Handle for Notepad using its PID. Not only that, but we also ask for an ALL_ACCESS permission over the process which Windows happily allow because both processes are spawned using my user with the same integrity level.

Opening Notepad with PID of 9452

OpenProcess per the Microsoft Documentation takes three arguments the desired Access, whether the returned handle be inherited by child process and the PID of the process to which we want a handle.

OpenProcess in Win32 Documentation

Desired Access constants are also mentioned in the Win32 Documentation here.

Let’s see an excerpt from the documentation.

Some of Process protection constants

In our case we want PROCESS_ALL_ACCESS for PID 9452 and we don’t the returned handle be used elsewhere so we pass false to bInheritHandle.

Give me some space!

Now that we have a handle to our victim, we need to allocate a memory space for use that fits our shellcode. For this endeavour we use VirtualAllocEx which returns a pointer to the allocated memory region in the other process.

VirtualAllocEx in Win32 Documentation

As seen above, we need to pass it the handle to the process, the address from which the region shall start but we can also pass 0 or NULL to let the system pick a start address for us. Furthermore, we need to pass the size of the region to be allocated, which is 510 in our case.

Next comes flAllocationType which specifies, rather unsurprisingly, the allocation type. These are allways MEM_RESERVE to reserve a region in the virtual address space (not neccessarily with Physical Memory or Page on disk backing) and MEM_COMMIT to commit changes and allocate actual physical memory range in memory or a Page on the disk. More information can be found on Win32 Documentation for VirtualAllocEx.

Allocation types.

Last but not least, we have flProtect which specifies the region’s protection. What this means is whether region should be Readable, Writable, Executable or any combination of the three for our purposes. Details can be found here.

Some of Page protection constants

Here is the final call in our code.

Creating a memory region in Notepad to house our shellcode

Look Mom, I can write

Having reserved a memory region for our shellcode, it stands to reason that we write our shellcode to it. Windows happily gives WriteProcessMemory for this exact type of action.

WriteProcessMemory in Win32 Documentation

Rather intuitively it takes a Handle to the process in whose memory we want to write in hProcess, a pointer to start of the memory region in lpBaseAddress, a pointer to the buffer whose contents (the shellcode y’all) we want to copy to that memory region in lpBuffer and its size in bytes in nSize. Lastly, we can get the actual number of bytes written if for any reason it is different from the size of the buffer, we can pass to NULL if we don’t want to bother with this.

Writing shellcode

It is SHOWTIME!

Now that the shellcode is in place, we need to create a thread that executes the code in the memory region we have written.

CreateRemoteThread is a function exposed by the Windsows OS to create a thread in another process given its handle and the address containing the code to be executed. Get more information.

CreateRemoteThread in Win32 Documentation

This function takes the Handle to the process in hProcess, a pointer the Security Attributes structure (which contains the SACL, DACL and Owner and Group for various Windows objects) but we can pass NULL to get the default Security Descriptor which is good enough for our purposes.

Next, we can set size of the stack or pass NULL in dwStackSize to get the default stack size. Most importantly we pass the address (pointer) of the function (code) to be executed but note that we want to type cast the pointer to LPTHREAD_START_ROUTINE. If we have arguments to pass to the function to be run, we pass a pointer to them in lpParameter.

dwCreationFlags specifies the state of the created thread, to be run immediately or suspended and whether the stack size is committed upon creation or just reservation. We pass NULL (0) to run the thread immediately. We can pass a pointer to a Double-Word (32-bit integer) to get the Thread ID (Like PID but for threads)

Creation flags

Let’s take a look at the final code for creating a thread.

Creating a remote thread to run the shellcode in notepad

If we set up a listener on our Kali box and run the program we get a call back.

connection received.

--

--