1 | /* $Id: nt_child_inject_standard_handles.c 3584 2023-01-20 15:29:36Z bird $ */
|
---|
2 | /** @file
|
---|
3 | * Injecting standard handles into a child process.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (c) 2004-2018 knut st. osmundsen <[email protected]>
|
---|
8 | *
|
---|
9 | * This file is part of kBuild.
|
---|
10 | *
|
---|
11 | * kBuild is free software; you can redistribute it and/or modify
|
---|
12 | * it under the terms of the GNU General Public License as published by
|
---|
13 | * the Free Software Foundation; either version 3 of the License, or
|
---|
14 | * (at your option) any later version.
|
---|
15 | *
|
---|
16 | * kBuild is distributed in the hope that it will be useful,
|
---|
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
19 | * GNU General Public License for more details.
|
---|
20 | *
|
---|
21 | * You should have received a copy of the GNU General Public License
|
---|
22 | * along with kBuild. If not, see <http://www.gnu.org/licenses/>
|
---|
23 | *
|
---|
24 | */
|
---|
25 |
|
---|
26 |
|
---|
27 | /*********************************************************************************************************************************
|
---|
28 | * Header Files *
|
---|
29 | *********************************************************************************************************************************/
|
---|
30 | #include <Windows.h>
|
---|
31 | #include <Winternl.h>
|
---|
32 | #include <stdio.h>
|
---|
33 | #include <assert.h>
|
---|
34 | #include <k/kDefs.h>
|
---|
35 | #include "nt_child_inject_standard_handles.h"
|
---|
36 |
|
---|
37 | /**
|
---|
38 | * Wrapper around ReadProcessMemory in case WOW64 tricks are needed.
|
---|
39 | *
|
---|
40 | * @returns Success indicator.
|
---|
41 | * @param hProcess The target process.
|
---|
42 | * @param ullSrc The source address (in @a hProcess).
|
---|
43 | * @param pvDst The target address (this process).
|
---|
44 | * @param cbToRead How much to read.
|
---|
45 | * @param pcbRead Where to return how much was actually read.
|
---|
46 | */
|
---|
47 | static BOOL MyReadProcessMemory(HANDLE hProcess, ULONGLONG ullSrc, void *pvDst, SIZE_T cbToRead, SIZE_T *pcbRead)
|
---|
48 | {
|
---|
49 | #if K_ARCH_BITS != 64
|
---|
50 | if (ullSrc + cbToRead - 1 > ~(uintptr_t)0)
|
---|
51 | {
|
---|
52 | typedef NTSTATUS(NTAPI *PFN_NtWow64ReadVirtualMemory64)(HANDLE, ULONGLONG, PVOID, ULONGLONG, PULONGLONG);
|
---|
53 | static PFN_NtWow64ReadVirtualMemory64 volatile s_pfnNtWow64ReadVirtualMemory64= NULL;
|
---|
54 | static BOOL volatile s_fInitialized = FALSE;
|
---|
55 | PFN_NtWow64ReadVirtualMemory64 pfnNtWow64ReadVirtualMemory64 = s_pfnNtWow64ReadVirtualMemory64;
|
---|
56 | if (!pfnNtWow64ReadVirtualMemory64 && !s_fInitialized)
|
---|
57 | {
|
---|
58 | *(FARPROC *)&pfnNtWow64ReadVirtualMemory64 = GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtWow64ReadVirtualMemory64");
|
---|
59 | s_pfnNtWow64ReadVirtualMemory64 = pfnNtWow64ReadVirtualMemory64;
|
---|
60 | }
|
---|
61 | if (pfnNtWow64ReadVirtualMemory64)
|
---|
62 | {
|
---|
63 | struct
|
---|
64 | {
|
---|
65 | ULONGLONG volatile ullBefore;
|
---|
66 | ULONGLONG cbRead64;
|
---|
67 | ULONGLONG volatile ullAfter;
|
---|
68 | } Wtf = { ~(ULONGLONG)0, 0, ~(ULONGLONG)0 };
|
---|
69 | NTSTATUS rcNt = pfnNtWow64ReadVirtualMemory64(hProcess, ullSrc, pvDst, cbToRead, &Wtf.cbRead64);
|
---|
70 | *pcbRead = (SIZE_T)Wtf.cbRead64;
|
---|
71 | SetLastError(rcNt); /* lazy bird */
|
---|
72 | return NT_SUCCESS(rcNt);
|
---|
73 | }
|
---|
74 | }
|
---|
75 | #endif
|
---|
76 | return ReadProcessMemory(hProcess, (void *)(uintptr_t)ullSrc, pvDst, cbToRead, pcbRead);
|
---|
77 | }
|
---|
78 |
|
---|
79 |
|
---|
80 | /**
|
---|
81 | * Wrapper around WriteProcessMemory in case WOW64 tricks are needed.
|
---|
82 | *
|
---|
83 | * @returns Success indicator.
|
---|
84 | * @param hProcess The target process.
|
---|
85 | * @param ullDst The target address (in @a hProcess).
|
---|
86 | * @param pvSrc The source address (this process).
|
---|
87 | * @param cbToWrite How much to write.
|
---|
88 | * @param pcbWritten Where to return how much was actually written.
|
---|
89 | */
|
---|
90 | static BOOL MyWriteProcessMemory(HANDLE hProcess, ULONGLONG ullDst, void const *pvSrc, SIZE_T cbToWrite, SIZE_T *pcbWritten)
|
---|
91 | {
|
---|
92 | #if K_ARCH_BITS != 64
|
---|
93 | if (ullDst + cbToWrite - 1 > ~(uintptr_t)0)
|
---|
94 | {
|
---|
95 | typedef NTSTATUS (NTAPI *PFN_NtWow64WriteVirtualMemory64)(HANDLE, ULONGLONG, VOID const *, ULONGLONG, PULONGLONG);
|
---|
96 | static PFN_NtWow64WriteVirtualMemory64 volatile s_pfnNtWow64WriteVirtualMemory64= NULL;
|
---|
97 | static BOOL volatile s_fInitialized = FALSE;
|
---|
98 | PFN_NtWow64WriteVirtualMemory64 pfnNtWow64WriteVirtualMemory64 = s_pfnNtWow64WriteVirtualMemory64;
|
---|
99 | if (!pfnNtWow64WriteVirtualMemory64 && !s_fInitialized)
|
---|
100 | {
|
---|
101 | *(FARPROC *)&pfnNtWow64WriteVirtualMemory64 = GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtWow64WriteVirtualMemory64");
|
---|
102 | s_pfnNtWow64WriteVirtualMemory64 = pfnNtWow64WriteVirtualMemory64;
|
---|
103 | }
|
---|
104 | if (pfnNtWow64WriteVirtualMemory64)
|
---|
105 | {
|
---|
106 | struct
|
---|
107 | {
|
---|
108 | ULONGLONG volatile ullBefore;
|
---|
109 | ULONGLONG cbWritten64;
|
---|
110 | ULONGLONG volatile ullAfter;
|
---|
111 | } Wtf = { ~(ULONGLONG)0, 0, ~(ULONGLONG)0 };
|
---|
112 | NTSTATUS rcNt = pfnNtWow64WriteVirtualMemory64(hProcess, ullDst, pvSrc, cbToWrite, &Wtf.cbWritten64);
|
---|
113 | *pcbWritten = (SIZE_T)Wtf.cbWritten64;
|
---|
114 | SetLastError(rcNt); /* lazy bird */
|
---|
115 | return NT_SUCCESS(rcNt);
|
---|
116 | }
|
---|
117 | }
|
---|
118 | #endif
|
---|
119 | return WriteProcessMemory(hProcess, (void *)(uintptr_t)ullDst, pvSrc, cbToWrite, pcbWritten);
|
---|
120 | }
|
---|
121 |
|
---|
122 |
|
---|
123 | /**
|
---|
124 | * Injects standard handles into a child process (created suspended).
|
---|
125 | *
|
---|
126 | * @returns 0 on success. On failure a non-zero windows error or NT status,
|
---|
127 | * with details in @a pszErr.
|
---|
128 | * @param hProcess The child process (created suspended).
|
---|
129 | * @param pafReplace Selects which handles to actually replace (TRUE) and
|
---|
130 | * which to leave as-is (FALSE). The first entry is
|
---|
131 | * starndard input, second is standard output, and the
|
---|
132 | * final is standard error.
|
---|
133 | * @param pahHandles The handle in the current process to inject into the
|
---|
134 | * child process. This runs parallel to pafReplace. The
|
---|
135 | * values NULL and INVALID_HANDLE_VALUE will be written
|
---|
136 | * directly to the child without duplication.
|
---|
137 | * @param pszErr Pointer to error message buffer.
|
---|
138 | * @param cbErr Size of error message buffer.
|
---|
139 | */
|
---|
140 | int nt_child_inject_standard_handles(HANDLE hProcess, BOOL pafReplace[3], HANDLE pahHandles[3], char *pszErr, size_t cbErr)
|
---|
141 | {
|
---|
142 | typedef NTSTATUS (NTAPI *PFN_NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
---|
143 | static PFN_NtQueryInformationProcess volatile s_pfnNtQueryInformationProcess = NULL;
|
---|
144 | PFN_NtQueryInformationProcess pfnNtQueryInformationProcess;
|
---|
145 | #if K_ARCH_BITS != 64
|
---|
146 | static PFN_NtQueryInformationProcess volatile s_pfnNtWow64QueryInformationProcess64 = NULL;
|
---|
147 | PFN_NtQueryInformationProcess pfnNtWow64QueryInformationProcess64;
|
---|
148 |
|
---|
149 | static BOOL s_fHostIs64Bit = K_ARCH_BITS == 64;
|
---|
150 | static BOOL volatile s_fCheckedHost = FALSE;
|
---|
151 | #endif
|
---|
152 |
|
---|
153 | static const unsigned s_offProcessParametersInPeb32 = 0x10;
|
---|
154 | static const unsigned s_offProcessParametersInPeb64 = 0x20;
|
---|
155 | static const unsigned s_offStandardInputInProcParams32 = 0x18;
|
---|
156 | static const unsigned s_offStandardInputInProcParams64 = 0x20;
|
---|
157 | static const char * const s_apszNames[3] = { "standard input", "standard output", "standard error" };
|
---|
158 |
|
---|
159 |
|
---|
160 | ULONG cbActual1 = 0;
|
---|
161 | union
|
---|
162 | {
|
---|
163 | PROCESS_BASIC_INFORMATION Natural;
|
---|
164 | struct
|
---|
165 | {
|
---|
166 | NTSTATUS ExitStatus;
|
---|
167 | ULONGLONG PebBaseAddress;
|
---|
168 | ULONGLONG AffinityMask;
|
---|
169 | ULONG BasePriority;
|
---|
170 | ULONGLONG UniqueProcessId;
|
---|
171 | ULONGLONG InheritedFromUniqueProcessId;
|
---|
172 | } Wow64;
|
---|
173 | } BasicInfo = { { 0, 0, } };
|
---|
174 | ULONGLONG ullBasicInfoPeb;
|
---|
175 | NTSTATUS rcNt;
|
---|
176 | ULONGLONG ullPeb32 = 0;
|
---|
177 | ULONGLONG ullPeb64 = 0;
|
---|
178 | ULONGLONG ullProcParams32 = 0;
|
---|
179 | ULONGLONG ullProcParams64 = 0;
|
---|
180 | DWORD au32Handles[3] = { 0, 0, 0 };
|
---|
181 | ULONGLONG au64Handles[3] = { 0, 0, 0 };
|
---|
182 | unsigned iFirstToInject;
|
---|
183 | unsigned cHandlesToInject;
|
---|
184 | unsigned i;
|
---|
185 |
|
---|
186 | /*
|
---|
187 | * Analyze the input to figure out exactly what we need to do.
|
---|
188 | */
|
---|
189 | iFirstToInject = 0;
|
---|
190 | while (iFirstToInject < 3 && !pafReplace[iFirstToInject])
|
---|
191 | iFirstToInject++;
|
---|
192 | if (iFirstToInject >= 3)
|
---|
193 | return 0;
|
---|
194 |
|
---|
195 | cHandlesToInject = 3 - iFirstToInject;
|
---|
196 | while ( cHandlesToInject > 1
|
---|
197 | && !pafReplace[iFirstToInject + cHandlesToInject - 1])
|
---|
198 | cHandlesToInject--;
|
---|
199 |
|
---|
200 | #if K_ARCH_BITS != 64
|
---|
201 | /*
|
---|
202 | * Determine host bit count first time through.
|
---|
203 | */
|
---|
204 | if (!s_fCheckedHost)
|
---|
205 | {
|
---|
206 | BOOL fAmIWow64 = FALSE;
|
---|
207 | if ( IsWow64Process(GetCurrentProcess(), &fAmIWow64)
|
---|
208 | && fAmIWow64)
|
---|
209 | s_fHostIs64Bit = TRUE;
|
---|
210 | else
|
---|
211 | s_fHostIs64Bit = FALSE;
|
---|
212 | s_fCheckedHost = TRUE;
|
---|
213 | }
|
---|
214 | #endif
|
---|
215 |
|
---|
216 | /*
|
---|
217 | * Resolve NT API first time through.
|
---|
218 | */
|
---|
219 | pfnNtQueryInformationProcess = s_pfnNtQueryInformationProcess;
|
---|
220 | #if K_ARCH_BITS != 64
|
---|
221 | pfnNtWow64QueryInformationProcess64 = s_pfnNtWow64QueryInformationProcess64;
|
---|
222 | #endif
|
---|
223 | if (!pfnNtQueryInformationProcess)
|
---|
224 | {
|
---|
225 | HMODULE hmodNtDll = GetModuleHandleA("NTDLL.DLL");
|
---|
226 | #if K_ARCH_BITS != 64
|
---|
227 | *(FARPROC *)&pfnNtWow64QueryInformationProcess64 = GetProcAddress(hmodNtDll, "NtWow64QueryInformationProcess64");
|
---|
228 | s_pfnNtWow64QueryInformationProcess64 = pfnNtWow64QueryInformationProcess64;
|
---|
229 | #endif
|
---|
230 | *(FARPROC *)&pfnNtQueryInformationProcess = GetProcAddress(hmodNtDll, "NtQueryInformationProcess");
|
---|
231 | if (!pfnNtQueryInformationProcess)
|
---|
232 | {
|
---|
233 | _snprintf(pszErr, cbErr, "The NtQueryInformationProcess API was not found in NTDLL");
|
---|
234 | return ERROR_PROC_NOT_FOUND;
|
---|
235 | }
|
---|
236 | s_pfnNtQueryInformationProcess = pfnNtQueryInformationProcess;
|
---|
237 | }
|
---|
238 |
|
---|
239 | /*
|
---|
240 | * Get the PEB address.
|
---|
241 | *
|
---|
242 | * If we're a WOW64 process, we must use NtWow64QueryInformationProcess64
|
---|
243 | * here or the PEB address will be set to zero for 64-bit children.
|
---|
244 | */
|
---|
245 | #if K_ARCH_BITS != 64
|
---|
246 | if (s_fHostIs64Bit && pfnNtWow64QueryInformationProcess64)
|
---|
247 | {
|
---|
248 | rcNt = pfnNtWow64QueryInformationProcess64(hProcess, ProcessBasicInformation, &BasicInfo.Wow64,
|
---|
249 | sizeof(BasicInfo.Wow64), &cbActual1);
|
---|
250 | if (!NT_SUCCESS(rcNt))
|
---|
251 | {
|
---|
252 | _snprintf(pszErr, cbErr, "NtWow64QueryInformationProcess64 failed: %#x", rcNt);
|
---|
253 | return rcNt;
|
---|
254 | }
|
---|
255 | if ((ULONGLONG)BasicInfo.Wow64.PebBaseAddress < 0x1000)
|
---|
256 | {
|
---|
257 | _snprintf(pszErr, cbErr, "NtWow64QueryInformationProcess64 returned bad PebBaseAddress: %#llx",
|
---|
258 | BasicInfo.Wow64.PebBaseAddress);
|
---|
259 | return ERROR_INVALID_ADDRESS;
|
---|
260 | }
|
---|
261 | ullBasicInfoPeb = BasicInfo.Wow64.PebBaseAddress;
|
---|
262 | }
|
---|
263 | else
|
---|
264 | #endif
|
---|
265 | {
|
---|
266 | rcNt = pfnNtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo.Natural,
|
---|
267 | sizeof(BasicInfo.Natural), &cbActual1);
|
---|
268 | if (!NT_SUCCESS(rcNt))
|
---|
269 | {
|
---|
270 | _snprintf(pszErr, cbErr, "NtQueryInformationProcess failed: %#x", rcNt);
|
---|
271 | return rcNt;
|
---|
272 | }
|
---|
273 | if ((uintptr_t)BasicInfo.Natural.PebBaseAddress < 0x1000)
|
---|
274 | {
|
---|
275 | _snprintf(pszErr, cbErr, "NtQueryInformationProcess returned bad PebBaseAddress: %#llx",
|
---|
276 | (ULONGLONG)BasicInfo.Natural.PebBaseAddress);
|
---|
277 | return ERROR_INVALID_ADDRESS;
|
---|
278 | }
|
---|
279 | ullBasicInfoPeb = (uintptr_t)BasicInfo.Natural.PebBaseAddress;
|
---|
280 | }
|
---|
281 |
|
---|
282 | /*
|
---|
283 | * Get the 32-bit PEB if it's a WOW64 process.
|
---|
284 | * This query should return 0 for non-WOW64 processes, but we quietly
|
---|
285 | * ignore failures and assume non-WOW64 child.
|
---|
286 | */
|
---|
287 | #if K_ARCH_BITS != 64
|
---|
288 | if (!s_fHostIs64Bit)
|
---|
289 | ullPeb32 = ullBasicInfoPeb;
|
---|
290 | else
|
---|
291 | #endif
|
---|
292 | {
|
---|
293 | ULONG_PTR uPeb32Ptr = 0;
|
---|
294 | cbActual1 = 0;
|
---|
295 | rcNt = pfnNtQueryInformationProcess(hProcess, ProcessWow64Information, &uPeb32Ptr, sizeof(uPeb32Ptr), &cbActual1);
|
---|
296 | if (NT_SUCCESS(rcNt) && uPeb32Ptr != 0)
|
---|
297 | {
|
---|
298 | ullPeb32 = uPeb32Ptr;
|
---|
299 | ullPeb64 = ullBasicInfoPeb;
|
---|
300 | #if K_ARCH_BITS != 64
|
---|
301 | assert(ullPeb64 != ullPeb32);
|
---|
302 | if (ullPeb64 == ullPeb32)
|
---|
303 | ullPeb64 = 0;
|
---|
304 | #endif
|
---|
305 | }
|
---|
306 | else
|
---|
307 | {
|
---|
308 | assert(NT_SUCCESS(rcNt));
|
---|
309 | ullPeb64 = ullBasicInfoPeb;
|
---|
310 | }
|
---|
311 | }
|
---|
312 |
|
---|
313 | /*
|
---|
314 | * Read the process parameter pointers.
|
---|
315 | */
|
---|
316 | if (ullPeb32)
|
---|
317 | {
|
---|
318 | DWORD uProcParamPtr = 0;
|
---|
319 | SIZE_T cbRead = 0;
|
---|
320 | if ( MyReadProcessMemory(hProcess, ullPeb32 + s_offProcessParametersInPeb32,
|
---|
321 | &uProcParamPtr, sizeof(uProcParamPtr), &cbRead)
|
---|
322 | && cbRead == sizeof(uProcParamPtr))
|
---|
323 | ullProcParams32 = uProcParamPtr;
|
---|
324 | else
|
---|
325 | {
|
---|
326 | DWORD dwErr = GetLastError();
|
---|
327 | _snprintf(pszErr, cbErr, "Failed to read PEB32!ProcessParameter at %#llx: %u/%#x (%u read)",
|
---|
328 | ullPeb32 + s_offProcessParametersInPeb32, dwErr, dwErr, cbRead);
|
---|
329 | return dwErr ? dwErr : -1;
|
---|
330 | }
|
---|
331 | if (uProcParamPtr < 0x1000)
|
---|
332 | {
|
---|
333 | _snprintf(pszErr, cbErr, "Bad PEB32!ProcessParameter value: %#llx", ullProcParams32);
|
---|
334 | return ERROR_INVALID_ADDRESS;
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | if (ullPeb64)
|
---|
339 | {
|
---|
340 | ULONGLONG uProcParamPtr = 0;
|
---|
341 | SIZE_T cbRead = 0;
|
---|
342 | if ( MyReadProcessMemory(hProcess, ullPeb64 + s_offProcessParametersInPeb64,
|
---|
343 | &uProcParamPtr, sizeof(uProcParamPtr), &cbRead)
|
---|
344 | && cbRead == sizeof(uProcParamPtr))
|
---|
345 | ullProcParams64 = uProcParamPtr;
|
---|
346 | else
|
---|
347 | {
|
---|
348 | DWORD dwErr = GetLastError();
|
---|
349 | _snprintf(pszErr, cbErr, "Failed to read PEB64!ProcessParameter at %p: %u/%#x (%u read)",
|
---|
350 | ullPeb64 + s_offProcessParametersInPeb64, dwErr, dwErr, cbRead);
|
---|
351 | return dwErr ? dwErr : -1;
|
---|
352 | }
|
---|
353 | if (uProcParamPtr < 0x1000)
|
---|
354 | {
|
---|
355 | _snprintf(pszErr, cbErr, "Bad PEB64!ProcessParameter value: %#llx", uProcParamPtr);
|
---|
356 | return ERROR_INVALID_ADDRESS;
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | /*
|
---|
361 | * If we're replacing standard input and standard error but not standard
|
---|
362 | * output, we must read the standard output handle. We ASSUME that in
|
---|
363 | * WOW64 processes the two PEBs have the same value, saving a read.
|
---|
364 | */
|
---|
365 | if (iFirstToInject == 0 && cHandlesToInject == 3 && !pafReplace[1])
|
---|
366 | {
|
---|
367 | if (ullProcParams64)
|
---|
368 | {
|
---|
369 | SIZE_T cbRead = 0;
|
---|
370 | if ( MyReadProcessMemory(hProcess, ullProcParams64 + s_offStandardInputInProcParams64 + sizeof(au64Handles[0]),
|
---|
371 | &au64Handles[1], sizeof(au64Handles[1]), &cbRead)
|
---|
372 | && cbRead == sizeof(au64Handles[1]))
|
---|
373 | au32Handles[1] = (DWORD)au64Handles[1];
|
---|
374 | else
|
---|
375 | {
|
---|
376 | DWORD dwErr = GetLastError();
|
---|
377 | _snprintf(pszErr, cbErr, "Failed to read ProcessParameter64!StandardOutput at %#llx: %u/%#x (%u read)",
|
---|
378 | ullProcParams64 + s_offStandardInputInProcParams64 + sizeof(au64Handles[0]), dwErr, dwErr, cbRead);
|
---|
379 | return dwErr ? dwErr : -1;
|
---|
380 | }
|
---|
381 | }
|
---|
382 | else if (ullProcParams32)
|
---|
383 | {
|
---|
384 | SIZE_T cbRead = 0;
|
---|
385 | if ( !MyReadProcessMemory(hProcess, ullProcParams32 + s_offStandardInputInProcParams32 + sizeof(au32Handles[0]),
|
---|
386 | &au32Handles[1], sizeof(au32Handles[1]), &cbRead)
|
---|
387 | || cbRead != sizeof(au32Handles[1]))
|
---|
388 | {
|
---|
389 | DWORD dwErr = GetLastError();
|
---|
390 | _snprintf(pszErr, cbErr, "Failed to read ProcessParameter32!StandardOutput at %#llx: %u/%#x (%u read)",
|
---|
391 | ullProcParams32 + s_offStandardInputInProcParams32 + sizeof(au32Handles[0]), dwErr, dwErr, cbRead);
|
---|
392 | return dwErr ? dwErr : -1;
|
---|
393 | }
|
---|
394 | }
|
---|
395 | }
|
---|
396 |
|
---|
397 | /*
|
---|
398 | * Duplicate the handles into process, preparing the two handle arrays
|
---|
399 | * that we'll write to the guest afterwards.
|
---|
400 | */
|
---|
401 | for (i = iFirstToInject; i < 3; i++)
|
---|
402 | if (pafReplace[i])
|
---|
403 | {
|
---|
404 | HANDLE hInChild = pahHandles[i];
|
---|
405 | if ( hInChild == NULL
|
---|
406 | || hInChild == INVALID_HANDLE_VALUE
|
---|
407 | || DuplicateHandle(GetCurrentProcess(), pahHandles[i], hProcess, &hInChild,
|
---|
408 | 0 /*fDesiredAccess*/, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
|
---|
409 | {
|
---|
410 | au32Handles[i] = (DWORD)(uintptr_t)hInChild;
|
---|
411 | au64Handles[i] = (uintptr_t)hInChild;
|
---|
412 | }
|
---|
413 | else
|
---|
414 | {
|
---|
415 | DWORD dwErr = GetLastError();
|
---|
416 | _snprintf(pszErr, cbErr, "Failed to duplicate handle %p into the child as %s: %u",
|
---|
417 | pahHandles[i], s_apszNames[i], dwErr);
|
---|
418 | return dwErr ? dwErr : -1;
|
---|
419 | }
|
---|
420 | }
|
---|
421 |
|
---|
422 | /*
|
---|
423 | * Write the handle arrays to the child.
|
---|
424 | *
|
---|
425 | * If we're a WOW64 we need to use NtWow64WriteVirtualMemory64 instead of
|
---|
426 | * WriteProcessMemory because the latter fails with ERROR_NOACCESS (998).
|
---|
427 | * So, we use a wrapper for doing the writing.
|
---|
428 | */
|
---|
429 | if (ullProcParams32)
|
---|
430 | {
|
---|
431 | ULONGLONG ullDst = ullProcParams32 + s_offStandardInputInProcParams32 + iFirstToInject * sizeof(au32Handles[0]);
|
---|
432 | SIZE_T cbToWrite = cHandlesToInject * sizeof(au32Handles[0]);
|
---|
433 | SIZE_T cbWritten = 0;
|
---|
434 | if ( !MyWriteProcessMemory(hProcess, ullDst, &au32Handles[iFirstToInject], cbToWrite, &cbWritten)
|
---|
435 | || cbWritten != cbToWrite)
|
---|
436 | {
|
---|
437 | DWORD dwErr = GetLastError();
|
---|
438 | _snprintf(pszErr, cbErr, "Failed to write handles to ProcessParameter32 (%#llx LB %u): %u/%#x (%u written)",
|
---|
439 | ullDst, cbToWrite, dwErr, dwErr, cbWritten);
|
---|
440 | return dwErr ? dwErr : ERROR_MORE_DATA;
|
---|
441 | }
|
---|
442 | }
|
---|
443 |
|
---|
444 | if (ullProcParams64)
|
---|
445 | {
|
---|
446 | ULONGLONG ullDst = ullProcParams64 + s_offStandardInputInProcParams64 + iFirstToInject * sizeof(au64Handles[0]);
|
---|
447 | SIZE_T cbToWrite = cHandlesToInject * sizeof(au64Handles[0]);
|
---|
448 | SIZE_T cbWritten = 0;
|
---|
449 | if ( !MyWriteProcessMemory(hProcess, ullDst, &au64Handles[iFirstToInject], cbToWrite, &cbWritten)
|
---|
450 | || cbWritten != cbToWrite)
|
---|
451 | {
|
---|
452 | DWORD dwErr = GetLastError();
|
---|
453 | _snprintf(pszErr, cbErr, "Failed to write handles to ProcessParameter64 (%#llx LB %u): %u/%#x (%u written)",
|
---|
454 | ullDst, cbToWrite, dwErr, dwErr, cbWritten);
|
---|
455 | return dwErr ? dwErr : ERROR_MORE_DATA;
|
---|
456 | }
|
---|
457 | }
|
---|
458 |
|
---|
459 | /* Done successfully! */
|
---|
460 | return 0;
|
---|
461 | }
|
---|
462 |
|
---|