Reprogrammed

Let's Make Malware - Remote Process Injection

Preamble

Last week, we covered Local Process Injection. This allowed us to run shellcode in a local process using the Windows API. So, this time, we’re going to cover Remote Process Injection. With it, we’ll be able to force other processes to run our shellcode.

This time, we’ll use five Windows API calls.

OpenProcess

First, we’ll use OpenProcess to gain a handle to the process we choose. A handle can be thought of as a key to a house or toolbox. Without the key, we can’t access what’s inside. Any time we want to perform some sort of operation on a process or file, we need to first get a handle to it.

OpenProcess is straightforward.

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

The first parameter is simply the permissions we’re asking for. Windows (thankfully) will not allow us free reign to do anything we want on arbitrary processes, so we must choose carefully. In this case, we’ll ask for the following permissions:

PROCESS_VM_WRITE to write to the virtual memory of the process.

PROCESS_VM_OPERATION is also for writing to the virtual memory of the process. This one is also needed when we want to do things like change memory protections (RWX).

Lastly, PROCESS_CREATE_THREAD to, as the name suggests, create threads in the process.

Alternatively, we can use PROCESS_ALL_ACCESS instead of these three values.

The second parameter is a true/false value (Boolean) for inheritance. That is, allowing child processes to inherit this handle. We do not require this, so we set it to FALSE.

Finally, we set the process ID (PID) of the process we want a handle to. To keep things simple, we will open notepad.exe and use its PID, which we can get from Task Manager in the “details” tab.

VirtualAllocEx

Now that we have a handle, we will use VirtualAllocEx for creating our memory buffer. It is very similar to VirtualAlloc, except this time it will allocate memory inside of a remote process. It looks like this:

LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

The parameters are the same as VirtualAlloc, except for the first parameter, which is the handle returned from OpenProcess.

The second parameter, as defined by MSDN, is “The starting address of the region to allocate”. We can set this to NULL to let the system determine where to allocate the region.

The third parameter, simply put, is the size of the buffer in bytes. The next parameter is the type of memory allocation. There are a few different ones we could choose from, but for this example we want the MEM_COMMIT (0x000010000) type.

Finally, the last parameter, flProtect, is the memory protection. Again, there are a few different ones we could choose from, but we want PAGE_EXECUTE_READWRITE.

WriteProcessMemory

We’ll use WriteProcessMemory for moving our shellcode into the buffer. It looks like this:

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

The first parameter, again, is the handle returned by OpenProcess.

The second parameter is the location (starting memory address) we want to copy to. In other words, this is our destination. Our buffer.

The third parameter is the location (starting memory address) of the thing we’re copying. Simply put, this is our source. Our payload.

For the fourth parameter, it needs to know how much to copy into our buffer. Of course, we want to copy all our shellcode or else it won’t work.

Finally, we can have WriteProcessMemory write how many bytes were copied and save that to a variable. This is not necessary, so we set it to NULL.

CreateRemoteThread

We will use CreateRemoteThread for executing the shellcode inside the buffer. It is very similar to CreateThread and it looks like this:

HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

The first parameter needs the handle we obtained earlier from OpenProcess. This should be familiar by now.

The second parameter isn’t necessary for our purposes. We set it to NULL.

The third parameter also is necessary for our purposes. We set it to zero.

The fourth parameter is the starting address of what we want to execute. This is our buffer variable.

We are not passing any variables to our thread, so we can set the fifth parameter to zero.

The sixth parameter is for any flags we want to set. We could use this to create the thread in a suspended state, however we will use 0 here. The 0 flag just runs the thread immediately after its created.

Finally, the sixth parameter is not necessary. We will set it to NULL.

CloseHandle

Our last API call is CloseHandle. You don’t technically need to use this one, but it is best practice to close handles you are no longer using. It’s kind of like returning a key you were lent. Its only parameter should be self-explanatory. It needs (once again) the handle returned by OpenProcess.

Here’s how that looks in C++ altogether.

#include “windows.h”
int main() {
  //msfvenom Calc shellcode
  unsigned char payload[] = { dont trust random shellcode blobs you find on the Internet  };
  HANDLE hProc = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, FALSE, 7492);
  LPVOID buffer = VirtualAllocEx(hProc, 0, sizeof payload, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  WriteProcessMemory(hProc, buffer, payload, sizeof payload, NULL);
  HANDLE tHandle = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE) buffer, 0, 0, NULL);
  CloseHandle(hProc);
  return 0;
} 

After compiling and running, we should see calc.exe pop up. Success! One thing of note is that you must be careful about which processes you inject into. You may end up crashing them.

Resources

MSDN: VirtualAllocEx

MSDN: WriteProcessMemory

MSDN: CreateRemoteThread

MSDN: CloseHandle

MSDN: Data Types

WikiBooks: Data Types