VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp@ 57161

Last change on this file since 57161 was 57161, checked in by vboxsync, 9 years ago

SUPR3HardenedMain-win.cpp: ext-ms-win-devmgmt-policy-l1-1-0.dll and its ext-ms-win-*.dll friends needs the same kind of special handling as we give api-ms-win-*.dll modules, otherwise the w10 file dialog won't load.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 245.4 KB
Line 
1/* $Id: SUPR3HardenedMain-win.cpp 57161 2015-08-03 16:31:15Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main(), windows bits.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include <iprt/nt/nt-and-windows.h>
31#include <AccCtrl.h>
32#include <AclApi.h>
33#ifndef PROCESS_SET_LIMITED_INFORMATION
34# define PROCESS_SET_LIMITED_INFORMATION 0x2000
35#endif
36#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
37# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100)
38# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200)
39# define LOAD_LIBRARY_SEARCH_USER_DIRS UINT32_C(0x400)
40# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800)
41#endif
42
43#include <VBox/sup.h>
44#include <VBox/err.h>
45#include <VBox/dis.h>
46#include <iprt/ctype.h>
47#include <iprt/string.h>
48#include <iprt/initterm.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/thread.h>
52#include <iprt/zero.h>
53
54#include "SUPLibInternal.h"
55#include "win/SUPHardenedVerify-win.h"
56#include "../SUPDrvIOC.h"
57
58#ifndef IMAGE_SCN_TYPE_NOLOAD
59# define IMAGE_SCN_TYPE_NOLOAD 0x00000002
60#endif
61
62
63/*******************************************************************************
64* Defined Constants And Macros *
65*******************************************************************************/
66/** The first argument of a respawed stub when respawned for the first time.
67 * This just needs to be unique enough to avoid most confusion with real
68 * executable names, there are other checks in place to make sure we've respanwed. */
69#define SUPR3_RESPAWN_1_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-2ndchild"
70
71/** The first argument of a respawed stub when respawned for the second time.
72 * This just needs to be unique enough to avoid most confusion with real
73 * executable names, there are other checks in place to make sure we've respanwed. */
74#define SUPR3_RESPAWN_2_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-3rdchild"
75
76/** Unconditional assertion. */
77#define SUPR3HARDENED_ASSERT(a_Expr) \
78 do { \
79 if (!(a_Expr)) \
80 supR3HardenedFatal("%s: %s\n", __FUNCTION__, #a_Expr); \
81 } while (0)
82
83/** Unconditional assertion of NT_SUCCESS. */
84#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
85 do { \
86 NTSTATUS rcNtAssert = (a_Expr); \
87 if (!NT_SUCCESS(rcNtAssert)) \
88 supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, rcNtAssert); \
89 } while (0)
90
91/** Unconditional assertion of a WIN32 API returning non-FALSE. */
92#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
93 do { \
94 BOOL fRcAssert = (a_Expr); \
95 if (fRcAssert == FALSE) \
96 supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, RtlGetLastWin32Error()); \
97 } while (0)
98
99
100/*******************************************************************************
101* Structures and Typedefs *
102*******************************************************************************/
103/**
104 * Security descriptor cleanup structure.
105 */
106typedef struct MYSECURITYCLEANUP
107{
108 union
109 {
110 SID Sid;
111 uint8_t abPadding[SECURITY_MAX_SID_SIZE];
112 } Everyone, Owner, User, Login;
113 union
114 {
115 ACL AclHdr;
116 uint8_t abPadding[1024];
117 } Acl;
118 PSECURITY_DESCRIPTOR pSecDesc;
119} MYSECURITYCLEANUP;
120/** Pointer to security cleanup structure. */
121typedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
122
123
124/**
125 * Image verifier cache entry.
126 */
127typedef struct VERIFIERCACHEENTRY
128{
129 /** Pointer to the next entry with the same hash value. */
130 struct VERIFIERCACHEENTRY * volatile pNext;
131 /** Next entry in the WinVerifyTrust todo list. */
132 struct VERIFIERCACHEENTRY * volatile pNextTodoWvt;
133
134 /** The file handle. */
135 HANDLE hFile;
136 /** If fIndexNumber is set, this is an file system internal file identifier. */
137 LARGE_INTEGER IndexNumber;
138 /** The path hash value. */
139 uint32_t uHash;
140 /** The verification result. */
141 int rc;
142 /** Used for shutting up load and error messages after a while so they don't
143 * flood the the log file and fill up the disk. */
144 uint32_t volatile cHits;
145 /** The validation flags (for WinVerifyTrust retry). */
146 uint32_t fFlags;
147 /** Whether IndexNumber is valid */
148 bool fIndexNumberValid;
149 /** Whether verified by WinVerifyTrust. */
150 bool volatile fWinVerifyTrust;
151 /** cwcPath * sizeof(RTUTF16). */
152 uint16_t cbPath;
153 /** The full path of this entry (variable size). */
154 RTUTF16 wszPath[1];
155} VERIFIERCACHEENTRY;
156/** Pointer to an image verifier path entry. */
157typedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
158
159
160/**
161 * Name of an import DLL that we need to check out.
162 */
163typedef struct VERIFIERCACHEIMPORT
164{
165 /** Pointer to the next DLL in the list. */
166 struct VERIFIERCACHEIMPORT * volatile pNext;
167 /** The length of pwszAltSearchDir if available. */
168 uint32_t cwcAltSearchDir;
169 /** This points the directory containing the DLL needing it, this will be
170 * NULL for a System32 DLL. */
171 PWCHAR pwszAltSearchDir;
172 /** The name of the import DLL (variable length). */
173 char szName[1];
174} VERIFIERCACHEIMPORT;
175/** Pointer to a import DLL that needs checking out. */
176typedef VERIFIERCACHEIMPORT *PVERIFIERCACHEIMPORT;
177
178
179/**
180 * Child requests.
181 */
182typedef enum SUPR3WINCHILDREQ
183{
184 /** Perform child purification and close full access handles (must be zero). */
185 kSupR3WinChildReq_PurifyChildAndCloseHandles = 0,
186 /** Close the events, we're good on our own from here on. */
187 kSupR3WinChildReq_CloseEvents,
188 /** Reporting error. */
189 kSupR3WinChildReq_Error,
190 /** End of valid requests. */
191 kSupR3WinChildReq_End
192} SUPR3WINCHILDREQ;
193
194/**
195 * Child process parameters.
196 */
197typedef struct SUPR3WINPROCPARAMS
198{
199 /** The event semaphore the child will be waiting on. */
200 HANDLE hEvtChild;
201 /** The event semaphore the parent will be waiting on. */
202 HANDLE hEvtParent;
203
204 /** The address of the NTDLL. This is only valid during the very early
205 * initialization as we abuse for thread creation protection. */
206 uintptr_t uNtDllAddr;
207
208 /** The requested operation (set by the child). */
209 SUPR3WINCHILDREQ enmRequest;
210 /** The last status. */
211 int32_t rc;
212 /** The init operation the error relates to if message, kSupInitOp_Invalid if
213 * not message. */
214 SUPINITOP enmWhat;
215 /** Where if message. */
216 char szWhere[80];
217 /** Error message / path name string space. */
218 char szErrorMsg[4096];
219} SUPR3WINPROCPARAMS;
220
221
222/**
223 * Child process data structure for use during child process init setup and
224 * purification.
225 */
226typedef struct SUPR3HARDNTCHILD
227{
228 /** Process handle. */
229 HANDLE hProcess;
230 /** Primary thread handle. */
231 HANDLE hThread;
232 /** Handle to the parent process, if we're the middle (stub) process. */
233 HANDLE hParent;
234 /** The event semaphore the child will be waiting on. */
235 HANDLE hEvtChild;
236 /** The event semaphore the parent will be waiting on. */
237 HANDLE hEvtParent;
238 /** The address of NTDLL in the child. */
239 uintptr_t uNtDllAddr;
240 /** The address of NTDLL in this process. */
241 uintptr_t uNtDllParentAddr;
242 /** Which respawn number this is (1 = stub, 2 = VM). */
243 int iWhich;
244 /** The basic process info. */
245 PROCESS_BASIC_INFORMATION BasicInfo;
246 /** The probable size of the PEB. */
247 size_t cbPeb;
248 /** The pristine process environment block. */
249 PEB Peb;
250 /** The child process parameters. */
251 SUPR3WINPROCPARAMS ProcParams;
252} SUPR3HARDNTCHILD;
253/** Pointer to a child process data structure. */
254typedef SUPR3HARDNTCHILD *PSUPR3HARDNTCHILD;
255
256
257/*******************************************************************************
258* Global Variables *
259*******************************************************************************/
260/** Process parameters. Specified by parent if VM process, see
261 * supR3HardenedVmProcessInit. */
262static SUPR3WINPROCPARAMS g_ProcParams = { NULL, NULL, 0, (SUPR3WINCHILDREQ)0, 0 };
263/** Set if supR3HardenedEarlyProcessInit was invoked. */
264bool g_fSupEarlyProcessInit = false;
265/** Set if the stub device has been opened (stub process only). */
266bool g_fSupStubOpened = false;
267
268/** @name Global variables initialized by suplibHardenedWindowsMain.
269 * @{ */
270/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
271uint32_t g_uNtVerCombined = 0;
272/** Count calls to the special main function for linking santity checks. */
273static uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
274/** The UTF-16 windows path to the executable. */
275RTUTF16 g_wszSupLibHardenedExePath[1024];
276/** The NT path of the executable. */
277SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
278/** The NT path of the application binary directory. */
279SUPSYSROOTDIRBUF g_SupLibHardenedAppBinNtPath;
280/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
281 * not byte). This also gives the length of the exectuable directory path,
282 * including a trailing slash. */
283static uint32_t g_offSupLibHardenedExeNtName;
284/** Set if we need to use the LOAD_LIBRARY_SEARCH_USER_DIRS option. */
285bool g_fSupLibHardenedDllSearchUserDirs = false;
286/** @} */
287
288/** @name Hook related variables.
289 * @{ */
290/** Pointer to the bit of assembly code that will perform the original
291 * NtCreateSection operation. */
292static NTSTATUS (NTAPI * g_pfnNtCreateSectionReal)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
293 PLARGE_INTEGER, ULONG, ULONG, HANDLE);
294/** Pointer to the NtCreateSection function in NtDll (for patching purposes). */
295static uint8_t *g_pbNtCreateSection;
296/** The patched NtCreateSection bytes (for restoring). */
297static uint8_t g_abNtCreateSectionPatch[16];
298/** Pointer to the bit of assembly code that will perform the original
299 * LdrLoadDll operation. */
300static NTSTATUS (NTAPI * g_pfnLdrLoadDllReal)(PWSTR, PULONG, PUNICODE_STRING, PHANDLE);
301/** Pointer to the LdrLoadDll function in NtDll (for patching purposes). */
302static uint8_t *g_pbLdrLoadDll;
303/** The patched LdrLoadDll bytes (for restoring). */
304static uint8_t g_abLdrLoadDllPatch[16];
305
306/** The hash table of verifier cache . */
307static PVERIFIERCACHEENTRY volatile g_apVerifierCache[128];
308/** Queue of cached images which needs WinVerifyTrust to check them. */
309static PVERIFIERCACHEENTRY volatile g_pVerifierCacheTodoWvt = NULL;
310/** Queue of cached images which needs their imports checked. */
311static PVERIFIERCACHEIMPORT volatile g_pVerifierCacheTodoImports = NULL;
312
313/** The windows path to dir \\SystemRoot\\System32 directory (technically
314 * this whatever \KnownDlls\KnownDllPath points to). */
315SUPSYSROOTDIRBUF g_System32WinPath;
316/** @ */
317
318/** Positive if the DLL notification callback has been registered, counts
319 * registration attempts as negative. */
320static int g_cDllNotificationRegistered = 0;
321/** The registration cookie of the DLL notification callback. */
322static PVOID g_pvDllNotificationCookie = NULL;
323
324/** Static error info structure used during init. */
325static RTERRINFOSTATIC g_ErrInfoStatic;
326
327/** In the assembly file. */
328extern "C" uint8_t g_abSupHardReadWriteExecPage[PAGE_SIZE];
329
330/** Whether we've patched our own LdrInitializeThunk or not. We do this to
331 * disable thread creation. */
332static bool g_fSupInitThunkSelfPatched;
333/** The backup of our own LdrInitializeThunk code, for enabling and disabling
334 * thread creation in this process. */
335static uint8_t g_abLdrInitThunkSelfBackup[16];
336
337/** Mask of adversaries that we've detected (SUPHARDNT_ADVERSARY_XXX). */
338static uint32_t g_fSupAdversaries = 0;
339/** @name SUPHARDNT_ADVERSARY_XXX - Adversaries
340 * @{ */
341/** Symantec endpoint protection or similar including SysPlant.sys. */
342#define SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT RT_BIT_32(0)
343/** Symantec Norton 360. */
344#define SUPHARDNT_ADVERSARY_SYMANTEC_N360 RT_BIT_32(1)
345/** Avast! */
346#define SUPHARDNT_ADVERSARY_AVAST RT_BIT_32(2)
347/** TrendMicro OfficeScan and probably others. */
348#define SUPHARDNT_ADVERSARY_TRENDMICRO RT_BIT_32(3)
349/** TrendMicro potentially buggy sakfile.sys. */
350#define SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE RT_BIT_32(4)
351/** McAfee. */
352#define SUPHARDNT_ADVERSARY_MCAFEE RT_BIT_32(5)
353/** Kaspersky or OEMs of it. */
354#define SUPHARDNT_ADVERSARY_KASPERSKY RT_BIT_32(6)
355/** Malwarebytes Anti-Malware (MBAM). */
356#define SUPHARDNT_ADVERSARY_MBAM RT_BIT_32(7)
357/** AVG Internet Security. */
358#define SUPHARDNT_ADVERSARY_AVG RT_BIT_32(8)
359/** Panda Security. */
360#define SUPHARDNT_ADVERSARY_PANDA RT_BIT_32(9)
361/** Microsoft Security Essentials. */
362#define SUPHARDNT_ADVERSARY_MSE RT_BIT_32(10)
363/** Comodo. */
364#define SUPHARDNT_ADVERSARY_COMODO RT_BIT_32(11)
365/** Check Point's Zone Alarm (may include Kaspersky). */
366#define SUPHARDNT_ADVERSARY_ZONE_ALARM RT_BIT_32(12)
367/** Digital guardian. */
368#define SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN RT_BIT_32(13)
369/** Unknown adversary detected while waiting on child. */
370#define SUPHARDNT_ADVERSARY_UNKNOWN RT_BIT_32(31)
371/** @} */
372
373
374/*******************************************************************************
375* Internal Functions *
376*******************************************************************************/
377static NTSTATUS supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
378 bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust,
379 bool *pfQuiet);
380static void supR3HardenedWinRegisterDllNotificationCallback(void);
381static void supR3HardenedWinReInstallHooks(bool fFirst);
382DECLASM(void) supR3HardenedEarlyProcessInitThunk(void);
383
384
385
386/**
387 * Simple wide char search routine.
388 *
389 * @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
390 * NULL if not found.
391 * @param pwszHaystack Pointer to the string that should be searched.
392 * @param wcNeedle The character to search for.
393 */
394static PRTUTF16 suplibHardenedWStrChr(PCRTUTF16 pwszHaystack, RTUTF16 wcNeedle)
395{
396 for (;;)
397 {
398 RTUTF16 wcCur = *pwszHaystack;
399 if (wcCur == wcNeedle)
400 return (PRTUTF16)pwszHaystack;
401 if (wcCur == '\0')
402 return NULL;
403 pwszHaystack++;
404 }
405}
406
407
408/**
409 * Simple wide char string length routine.
410 *
411 * @returns The number of characters in the given string. (Excludes the
412 * terminator.)
413 * @param pwsz The string.
414 */
415static size_t suplibHardenedWStrLen(PCRTUTF16 pwsz)
416{
417 PCRTUTF16 pwszCur = pwsz;
418 while (*pwszCur != '\0')
419 pwszCur++;
420 return pwszCur - pwsz;
421}
422
423
424/**
425 * Our version of GetTickCount.
426 * @returns Millisecond timestamp.
427 */
428static uint64_t supR3HardenedWinGetMilliTS(void)
429{
430 PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)(uintptr_t)0x7ffe0000;
431
432 /* use interrupt time */
433 LARGE_INTEGER Time;
434 do
435 {
436 Time.HighPart = pUserSharedData->InterruptTime.High1Time;
437 Time.LowPart = pUserSharedData->InterruptTime.LowPart;
438 } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart);
439
440 return (uint64_t)Time.QuadPart / 10000;
441}
442
443
444
445/**
446 * Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
447 * and supplies the right flags.
448 *
449 * @returns Module handle on success, NULL on failure.
450 * @param pszName The full path to the DLL.
451 * @param fSystem32Only Whether to only look for imports in the system32
452 * directory. If set to false, the application
453 * directory is also searched.
454 * @param fMainFlags The main flags (giving the location), if the DLL
455 * being loaded is loaded from the app bin
456 * directory and import other DLLs from there. Pass
457 * 0 (= SUPSECMAIN_FLAGS_LOC_APP_BIN) if not
458 * applicable. Ignored if @a fSystem32Only is set.
459 *
460 * This is only needed to load VBoxRT.dll when
461 * executing a testcase from the testcase/ subdir.
462 */
463DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only, uint32_t fMainFlags)
464{
465 WCHAR wszPath[RTPATH_MAX];
466 PRTUTF16 pwszPath = wszPath;
467 int rc = RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszPath, RT_ELEMENTS(wszPath), NULL);
468 if (RT_SUCCESS(rc))
469 {
470 while (*pwszPath)
471 {
472 if (*pwszPath == '/')
473 *pwszPath = '\\';
474 pwszPath++;
475 }
476
477 DWORD fFlags = 0;
478 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
479 {
480 fFlags |= LOAD_LIBRARY_SEARCH_SYSTEM32;
481 if (!fSystem32Only)
482 {
483 fFlags |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
484 if (g_fSupLibHardenedDllSearchUserDirs)
485 fFlags |= LOAD_LIBRARY_SEARCH_USER_DIRS;
486 if ((fMainFlags & SUPSECMAIN_FLAGS_LOC_MASK) != SUPSECMAIN_FLAGS_LOC_APP_BIN)
487 fFlags |= LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
488 }
489 }
490
491 void *pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, fFlags);
492
493 /* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
494 if ( !pvRet
495 && fFlags
496 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
497 && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
498 pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, 0);
499
500 return pvRet;
501 }
502 supR3HardenedFatal("RTStrToUtf16Ex failed on '%s': %Rrc", pszName, rc);
503 return NULL;
504}
505
506
507/**
508 * Gets the internal index number of the file.
509 *
510 * @returns True if we got an index number, false if not.
511 * @param hFile The file in question.
512 * @param pIndexNumber where to return the index number.
513 */
514static bool supR3HardenedWinVerifyCacheGetIndexNumber(HANDLE hFile, PLARGE_INTEGER pIndexNumber)
515{
516 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
517 NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
518 if (NT_SUCCESS(rcNt))
519 rcNt = Ios.Status;
520#ifdef DEBUG_bird
521 if (!NT_SUCCESS(rcNt))
522 __debugbreak();
523#endif
524 return NT_SUCCESS(rcNt) && pIndexNumber->QuadPart != 0;
525}
526
527
528/**
529 * Calculates the hash value for the given UTF-16 path string.
530 *
531 * @returns Hash value.
532 * @param pUniStr String to hash.
533 */
534static uint32_t supR3HardenedWinVerifyCacheHashPath(PCUNICODE_STRING pUniStr)
535{
536 uint32_t uHash = 0;
537 unsigned cwcLeft = pUniStr->Length / sizeof(WCHAR);
538 PRTUTF16 pwc = pUniStr->Buffer;
539
540 while (cwcLeft-- > 0)
541 {
542 RTUTF16 wc = *pwc++;
543 if (wc < 0x80)
544 wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
545 uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
546 }
547 return uHash;
548}
549
550
551/**
552 * Calculates the hash value for a directory + filename combo as if they were
553 * one single string.
554 *
555 * @returns Hash value.
556 * @param pawcDir The directory name.
557 * @param cwcDir The length of the directory name. RTSTR_MAX if
558 * not available.
559 * @param pszName The import name (UTF-8).
560 */
561static uint32_t supR3HardenedWinVerifyCacheHashDirAndFile(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName)
562{
563 uint32_t uHash = 0;
564 while (cwcDir-- > 0)
565 {
566 RTUTF16 wc = *pawcDir++;
567 if (wc < 0x80)
568 wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
569 uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
570 }
571
572 unsigned char ch = '\\';
573 uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
574
575 while ((ch = *pszName++) != '\0')
576 {
577 ch = RT_C_TO_LOWER(ch);
578 uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
579 }
580
581 return uHash;
582}
583
584
585/**
586 * Verify string cache compare function.
587 *
588 * @returns true if the strings match, false if not.
589 * @param pawcLeft The left hand string.
590 * @param pawcRight The right hand string.
591 * @param cwcToCompare The number of chars to compare.
592 */
593static bool supR3HardenedWinVerifyCacheIsMatch(PCRTUTF16 pawcLeft, PCRTUTF16 pawcRight, uint32_t cwcToCompare)
594{
595 /* Try a quick memory compare first. */
596 if (memcmp(pawcLeft, pawcRight, cwcToCompare * sizeof(RTUTF16)) == 0)
597 return true;
598
599 /* Slow char by char compare. */
600 while (cwcToCompare-- > 0)
601 {
602 RTUTF16 wcLeft = *pawcLeft++;
603 RTUTF16 wcRight = *pawcRight++;
604 if (wcLeft != wcRight)
605 {
606 wcLeft = wcLeft != '/' ? RT_C_TO_LOWER(wcLeft) : '\\';
607 wcRight = wcRight != '/' ? RT_C_TO_LOWER(wcRight) : '\\';
608 if (wcLeft != wcRight)
609 return false;
610 }
611 }
612
613 return true;
614}
615
616
617
618/**
619 * Inserts the given verifier result into the cache.
620 *
621 * @param pUniStr The full path of the image.
622 * @param hFile The file handle - must either be entered into
623 * the cache or closed.
624 * @param rc The verifier result.
625 * @param fWinVerifyTrust Whether verified by WinVerifyTrust or not.
626 * @param fFlags The image verification flags.
627 */
628static void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc,
629 bool fWinVerifyTrust, uint32_t fFlags)
630{
631 /*
632 * Allocate and initalize a new entry.
633 */
634 PVERIFIERCACHEENTRY pEntry = (PVERIFIERCACHEENTRY)RTMemAllocZ(sizeof(VERIFIERCACHEENTRY) + pUniStr->Length);
635 if (pEntry)
636 {
637 pEntry->pNext = NULL;
638 pEntry->pNextTodoWvt = NULL;
639 pEntry->hFile = hFile;
640 pEntry->uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
641 pEntry->rc = rc;
642 pEntry->fFlags = fFlags;
643 pEntry->cHits = 0;
644 pEntry->fWinVerifyTrust = fWinVerifyTrust;
645 pEntry->cbPath = pUniStr->Length;
646 memcpy(pEntry->wszPath, pUniStr->Buffer, pUniStr->Length);
647 pEntry->wszPath[pUniStr->Length / sizeof(WCHAR)] = '\0';
648 pEntry->fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &pEntry->IndexNumber);
649
650 /*
651 * Try insert it, careful with concurrent code as well as potential duplicates.
652 */
653 uint32_t iHashTab = pEntry->uHash % RT_ELEMENTS(g_apVerifierCache);
654 VERIFIERCACHEENTRY * volatile *ppEntry = &g_apVerifierCache[iHashTab];
655 for (;;)
656 {
657 if (ASMAtomicCmpXchgPtr(ppEntry, pEntry, NULL))
658 {
659 if (!fWinVerifyTrust)
660 do
661 pEntry->pNextTodoWvt = g_pVerifierCacheTodoWvt;
662 while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pEntry, pEntry->pNextTodoWvt));
663
664 SUP_DPRINTF(("supR3HardenedWinVerifyCacheInsert: %ls\n", pUniStr->Buffer));
665 return;
666 }
667
668 PVERIFIERCACHEENTRY pOther = *ppEntry;
669 if (!pOther)
670 continue;
671 if ( pOther->uHash == pEntry->uHash
672 && pOther->cbPath == pEntry->cbPath
673 && supR3HardenedWinVerifyCacheIsMatch(pOther->wszPath, pEntry->wszPath, pEntry->cbPath / sizeof(RTUTF16)))
674 break;
675 ppEntry = &pOther->pNext;
676 }
677
678 /* Duplicate entry (may happen due to races). */
679 RTMemFree(pEntry);
680 }
681 NtClose(hFile);
682}
683
684
685/**
686 * Looks up an entry in the verifier hash table.
687 *
688 * @return Pointer to the entry on if found, NULL if not.
689 * @param pUniStr The full path of the image.
690 * @param hFile The file handle.
691 */
692static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile)
693{
694 PRTUTF16 const pwszPath = pUniStr->Buffer;
695 uint16_t const cbPath = pUniStr->Length;
696 uint32_t uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
697 uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
698 PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
699 while (pCur)
700 {
701 if ( pCur->uHash == uHash
702 && pCur->cbPath == cbPath
703 && supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pwszPath, cbPath / sizeof(RTUTF16)))
704 {
705
706 if (!pCur->fIndexNumberValid)
707 return pCur;
708 LARGE_INTEGER IndexNumber;
709 bool fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &IndexNumber);
710 if ( fIndexNumberValid
711 && IndexNumber.QuadPart == pCur->IndexNumber.QuadPart)
712 return pCur;
713#ifdef DEBUG_bird
714 __debugbreak();
715#endif
716 }
717 pCur = pCur->pNext;
718 }
719 return NULL;
720}
721
722
723/**
724 * Looks up an import DLL in the verifier hash table.
725 *
726 * @return Pointer to the entry on if found, NULL if not.
727 * @param pawcDir The directory name.
728 * @param cwcDir The length of the directory name.
729 * @param pszName The import name (UTF-8).
730 */
731static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookupImport(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName)
732{
733 uint32_t uHash = supR3HardenedWinVerifyCacheHashDirAndFile(pawcDir, cwcDir, pszName);
734 uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
735 uint32_t const cbPath = (uint32_t)((cwcDir + 1 + strlen(pszName)) * sizeof(RTUTF16));
736 PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
737 while (pCur)
738 {
739 if ( pCur->uHash == uHash
740 && pCur->cbPath == cbPath)
741 {
742 if (supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pawcDir, cwcDir))
743 {
744 if (pCur->wszPath[cwcDir] == '\\' || pCur->wszPath[cwcDir] == '/')
745 {
746 if (RTUtf16ICmpAscii(&pCur->wszPath[cwcDir + 1], pszName))
747 {
748 return pCur;
749 }
750 }
751 }
752 }
753
754 pCur = pCur->pNext;
755 }
756 return NULL;
757}
758
759
760/**
761 * Schedules the import DLLs for verification and entry into the cache.
762 *
763 * @param hLdrMod The loader module which imports should be
764 * scheduled for verification.
765 * @param pwszName The full NT path of the module.
766 */
767DECLHIDDEN(void) supR3HardenedWinVerifyCacheScheduleImports(RTLDRMOD hLdrMod, PCRTUTF16 pwszName)
768{
769 /*
770 * Any imports?
771 */
772 uint32_t cImports;
773 int rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_COUNT, NULL /*pvBits*/, &cImports, sizeof(cImports), NULL);
774 if (RT_SUCCESS(rc))
775 {
776 if (cImports)
777 {
778 /*
779 * Figure out the DLL directory from pwszName.
780 */
781 PCRTUTF16 pawcDir = pwszName;
782 uint32_t cwcDir = 0;
783 uint32_t i = 0;
784 RTUTF16 wc;
785 while ((wc = pawcDir[i++]) != '\0')
786 if ((wc == '\\' || wc == '/' || wc == ':') && cwcDir + 2 != i)
787 cwcDir = i - 1;
788 if ( g_System32NtPath.UniStr.Length / sizeof(WCHAR) == cwcDir
789 && supR3HardenedWinVerifyCacheIsMatch(pawcDir, g_System32NtPath.UniStr.Buffer, cwcDir))
790 pawcDir = NULL;
791
792 /*
793 * Enumerate the imports.
794 */
795 for (i = 0; i < cImports; i++)
796 {
797 union
798 {
799 char szName[256];
800 uint32_t iImport;
801 } uBuf;
802 uBuf.iImport = i;
803 rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_MODULE, NULL /*pvBits*/, &uBuf, sizeof(uBuf), NULL);
804 if (RT_SUCCESS(rc))
805 {
806 /*
807 * Skip kernel32, ntdll and API set stuff.
808 */
809 RTStrToLower(uBuf.szName);
810 if ( RTStrCmp(uBuf.szName, "kernel32.dll") == 0
811 || RTStrCmp(uBuf.szName, "kernelbase.dll") == 0
812 || RTStrCmp(uBuf.szName, "ntdll.dll") == 0
813 || RTStrNCmp(uBuf.szName, RT_STR_TUPLE("api-ms-win-")) == 0
814 || RTStrNCmp(uBuf.szName, RT_STR_TUPLE("ext-ms-win-")) == 0
815 )
816 {
817 continue;
818 }
819
820 /*
821 * Skip to the next one if it's already in the cache.
822 */
823 if (supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
824 g_System32NtPath.UniStr.Length / sizeof(WCHAR),
825 uBuf.szName) != NULL)
826 {
827 SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for system32\n", uBuf.szName));
828 continue;
829 }
830 if (supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedAppBinNtPath.UniStr.Buffer,
831 g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(CHAR),
832 uBuf.szName) != NULL)
833 {
834 SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for appdir\n", uBuf.szName));
835 continue;
836 }
837 if (pawcDir && supR3HardenedWinVerifyCacheLookupImport(pawcDir, cwcDir, uBuf.szName) != NULL)
838 {
839 SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for dll dir\n", uBuf.szName));
840 continue;
841 }
842
843 /* We could skip already scheduled modules, but that'll require serialization and extra work... */
844
845 /*
846 * Add it to the todo list.
847 */
848 SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: Import todo: #%u '%s'.\n", i, uBuf.szName));
849 uint32_t cbName = (uint32_t)strlen(uBuf.szName) + 1;
850 uint32_t cbNameAligned = RT_ALIGN_32(cbName, sizeof(RTUTF16));
851 uint32_t cbNeeded = RT_OFFSETOF(VERIFIERCACHEIMPORT, szName[cbNameAligned])
852 + (pawcDir ? (cwcDir + 1) * sizeof(RTUTF16) : 0);
853 PVERIFIERCACHEIMPORT pImport = (PVERIFIERCACHEIMPORT)RTMemAllocZ(cbNeeded);
854 if (pImport)
855 {
856 /* Init it. */
857 memcpy(pImport->szName, uBuf.szName, cbName);
858 if (!pawcDir)
859 {
860 pImport->cwcAltSearchDir = 0;
861 pImport->pwszAltSearchDir = NULL;
862 }
863 else
864 {
865 pImport->cwcAltSearchDir = cwcDir;
866 pImport->pwszAltSearchDir = (PRTUTF16)&pImport->szName[cbNameAligned];
867 memcpy(pImport->pwszAltSearchDir, pawcDir, cwcDir * sizeof(RTUTF16));
868 pImport->pwszAltSearchDir[cwcDir] = '\0';
869 }
870
871 /* Insert it. */
872 do
873 pImport->pNext = g_pVerifierCacheTodoImports;
874 while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoImports, pImport, pImport->pNext));
875 }
876 }
877 else
878 SUP_DPRINTF(("RTLDRPROP_IMPORT_MODULE failed with rc=%Rrc i=%#x on '%ls'\n", rc, i, pwszName));
879 }
880 }
881 else
882 SUP_DPRINTF(("'%ls' has no imports\n", pwszName));
883 }
884 else
885 SUP_DPRINTF(("RTLDRPROP_IMPORT_COUNT failed with rc=%Rrc on '%ls'\n", rc, pwszName));
886}
887
888
889/**
890 * Processes the list of import todos.
891 */
892static void supR3HardenedWinVerifyCacheProcessImportTodos(void)
893{
894 /*
895 * Work until we've got nothing more todo.
896 */
897 for (;;)
898 {
899 PVERIFIERCACHEIMPORT pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoImports, NULL, PVERIFIERCACHEIMPORT);
900 if (!pTodo)
901 break;
902 do
903 {
904 PVERIFIERCACHEIMPORT pCur = pTodo;
905 pTodo = pTodo->pNext;
906
907 /*
908 * Not in the cached already?
909 */
910 if ( !supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
911 g_System32NtPath.UniStr.Length / sizeof(WCHAR),
912 pCur->szName)
913 && !supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedAppBinNtPath.UniStr.Buffer,
914 g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(WCHAR),
915 pCur->szName)
916 && ( pCur->cwcAltSearchDir == 0
917 || !supR3HardenedWinVerifyCacheLookupImport(pCur->pwszAltSearchDir, pCur->cwcAltSearchDir, pCur->szName)) )
918 {
919 /*
920 * Try locate the imported DLL and open it.
921 */
922 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Processing '%s'...\n", pCur->szName));
923
924 NTSTATUS rcNt;
925 NTSTATUS rcNtRedir = 0x22222222;
926 HANDLE hFile = INVALID_HANDLE_VALUE;
927 RTUTF16 wszPath[260 + 260]; /* Assumes we've limited the import name length to 256. */
928 AssertCompile(sizeof(wszPath) > sizeof(g_System32NtPath));
929
930 /*
931 * Check for DLL isolation / redirection / mapping.
932 */
933 size_t cwcName = 260;
934 PRTUTF16 pwszName = &wszPath[0];
935 int rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
936 if (RT_SUCCESS(rc))
937 {
938 UNICODE_STRING UniStrName;
939 UniStrName.Buffer = wszPath;
940 UniStrName.Length = (USHORT)cwcName * sizeof(WCHAR);
941 UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
942
943 UNICODE_STRING UniStrStatic;
944 UniStrStatic.Buffer = &wszPath[cwcName + 1];
945 UniStrStatic.Length = 0;
946 UniStrStatic.MaximumLength = (USHORT)(sizeof(wszPath) - cwcName * sizeof(WCHAR) - sizeof(WCHAR));
947
948 static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
949 UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
950 PUNICODE_STRING pUniStrResult = NULL;
951
952 rcNtRedir = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
953 &UniStrName,
954 (PUNICODE_STRING)&s_DefaultSuffix,
955 &UniStrStatic,
956 &UniStrDynamic,
957 &pUniStrResult,
958 NULL /*pNewFlags*/,
959 NULL /*pcbFilename*/,
960 NULL /*pcbNeeded*/);
961 if (NT_SUCCESS(rcNtRedir))
962 {
963 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
964 OBJECT_ATTRIBUTES ObjAttr;
965 InitializeObjectAttributes(&ObjAttr, pUniStrResult,
966 OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
967 rcNt = NtCreateFile(&hFile,
968 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
969 &ObjAttr,
970 &Ios,
971 NULL /* Allocation Size*/,
972 FILE_ATTRIBUTE_NORMAL,
973 FILE_SHARE_READ,
974 FILE_OPEN,
975 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
976 NULL /*EaBuffer*/,
977 0 /*EaLength*/);
978 if (NT_SUCCESS(rcNt))
979 rcNt = Ios.Status;
980 if (NT_SUCCESS(rcNt))
981 {
982 /* For accurate logging. */
983 size_t cwcCopy = RT_MIN(pUniStrResult->Length / sizeof(RTUTF16), RT_ELEMENTS(wszPath) - 1);
984 memcpy(wszPath, pUniStrResult->Buffer, cwcCopy * sizeof(RTUTF16));
985 wszPath[cwcCopy] = '\0';
986 }
987 else
988 hFile = INVALID_HANDLE_VALUE;
989 RtlFreeUnicodeString(&UniStrDynamic);
990 }
991 }
992 else
993 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #1 failed: %Rrc\n", rc));
994
995 /*
996 * If not something that gets remapped, do the half normal searching we need.
997 */
998 if (hFile == INVALID_HANDLE_VALUE)
999 {
1000 struct
1001 {
1002 PRTUTF16 pawcDir;
1003 uint32_t cwcDir;
1004 } Tmp, aDirs[] =
1005 {
1006 { g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR) },
1007 { g_SupLibHardenedExeNtPath.UniStr.Buffer, g_SupLibHardenedAppBinNtPath.UniStr.Length / sizeof(WCHAR) },
1008 { pCur->pwszAltSearchDir, pCur->cwcAltSearchDir },
1009 };
1010
1011 /* Search System32 first, unless it's a 'V*' or 'm*' name, the latter for msvcrt. */
1012 if ( pCur->szName[0] == 'v'
1013 || pCur->szName[0] == 'V'
1014 || pCur->szName[0] == 'm'
1015 || pCur->szName[0] == 'M')
1016 {
1017 Tmp = aDirs[0];
1018 aDirs[0] = aDirs[1];
1019 aDirs[1] = Tmp;
1020 }
1021
1022 for (uint32_t i = 0; i < RT_ELEMENTS(aDirs); i++)
1023 {
1024 if (aDirs[i].pawcDir && aDirs[i].cwcDir && aDirs[i].cwcDir < RT_ELEMENTS(wszPath) / 3 * 2)
1025 {
1026 memcpy(wszPath, aDirs[i].pawcDir, aDirs[i].cwcDir * sizeof(RTUTF16));
1027 uint32_t cwc = aDirs[i].cwcDir;
1028 wszPath[cwc++] = '\\';
1029 cwcName = RT_ELEMENTS(wszPath) - cwc;
1030 pwszName = &wszPath[cwc];
1031 rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
1032 if (RT_SUCCESS(rc))
1033 {
1034 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1035 UNICODE_STRING NtName;
1036 NtName.Buffer = wszPath;
1037 NtName.Length = (USHORT)((cwc + cwcName) * sizeof(WCHAR));
1038 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
1039 OBJECT_ATTRIBUTES ObjAttr;
1040 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1041
1042 rcNt = NtCreateFile(&hFile,
1043 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
1044 &ObjAttr,
1045 &Ios,
1046 NULL /* Allocation Size*/,
1047 FILE_ATTRIBUTE_NORMAL,
1048 FILE_SHARE_READ,
1049 FILE_OPEN,
1050 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1051 NULL /*EaBuffer*/,
1052 0 /*EaLength*/);
1053 if (NT_SUCCESS(rcNt))
1054 rcNt = Ios.Status;
1055 if (NT_SUCCESS(rcNt))
1056 break;
1057 hFile = INVALID_HANDLE_VALUE;
1058 }
1059 else
1060 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #2 failed: %Rrc\n", rc));
1061 }
1062 }
1063 }
1064
1065 /*
1066 * If we successfully opened it, verify it and cache the result.
1067 */
1068 if (hFile != INVALID_HANDLE_VALUE)
1069 {
1070 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' -> '%ls' [rcNtRedir=%#x]\n",
1071 pCur->szName, wszPath, rcNtRedir));
1072
1073 ULONG fAccess = 0;
1074 ULONG fProtect = 0;
1075 bool fCallRealApi = false;
1076 rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, false /*fIgnoreArch*/, &fAccess, &fProtect,
1077 &fCallRealApi, "Imports", false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
1078 NtClose(hFile);
1079 }
1080 else
1081 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Failed to locate '%s'\n", pCur->szName));
1082 }
1083 else
1084 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' is in the cache.\n", pCur->szName));
1085
1086 RTMemFree(pCur);
1087 } while (pTodo);
1088 }
1089}
1090
1091
1092/**
1093 * Processes the list of WinVerifyTrust todos.
1094 */
1095static void supR3HardenedWinVerifyCacheProcessWvtTodos(void)
1096{
1097 PVERIFIERCACHEENTRY pReschedule = NULL;
1098 PVERIFIERCACHEENTRY volatile *ppReschedLastNext = NULL;
1099
1100 /*
1101 * Work until we've got nothing more todo.
1102 */
1103 for (;;)
1104 {
1105 if (!supHardenedWinIsWinVerifyTrustCallable())
1106 break;
1107 PVERIFIERCACHEENTRY pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoWvt, NULL, PVERIFIERCACHEENTRY);
1108 if (!pTodo)
1109 break;
1110 do
1111 {
1112 PVERIFIERCACHEENTRY pCur = pTodo;
1113 pTodo = pTodo->pNextTodoWvt;
1114 pCur->pNextTodoWvt = NULL;
1115
1116 if ( !pCur->fWinVerifyTrust
1117 && RT_SUCCESS(pCur->rc))
1118 {
1119 bool fWinVerifyTrust = false;
1120 int rc = supHardenedWinVerifyImageTrust(pCur->hFile, pCur->wszPath, pCur->fFlags, pCur->rc,
1121 &fWinVerifyTrust, NULL /* pErrInfo*/);
1122 if (RT_FAILURE(rc) || fWinVerifyTrust)
1123 {
1124 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
1125 rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
1126 pCur->fWinVerifyTrust = true;
1127 pCur->rc = rc;
1128 }
1129 else
1130 {
1131 /* Retry it at a later time. */
1132 SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls' [rescheduled]\n",
1133 rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
1134 if (!pReschedule)
1135 ppReschedLastNext = &pCur->pNextTodoWvt;
1136 pCur->pNextTodoWvt = pReschedule;
1137 }
1138 }
1139 /* else: already processed. */
1140 } while (pTodo);
1141 }
1142
1143 /*
1144 * Anything to reschedule.
1145 */
1146 if (pReschedule)
1147 {
1148 do
1149 *ppReschedLastNext = g_pVerifierCacheTodoWvt;
1150 while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pReschedule, *ppReschedLastNext));
1151 }
1152}
1153
1154
1155/**
1156 * Screens an image file or file mapped with execute access.
1157 *
1158 * @returns NT status code.
1159 * @param hFile The file handle.
1160 * @param fImage Set if image file mapping being made
1161 * (NtCreateSection thing).
1162 * @param fIgnoreArch Using the DONT_RESOLVE_DLL_REFERENCES flag,
1163 * which also implies that DLL init / term code
1164 * isn't called, so the architecture should be
1165 * ignored.
1166 * @param pfAccess Pointer to the NtCreateSection access flags,
1167 * so we can modify them if necessary.
1168 * @param pfProtect Pointer to the NtCreateSection protection
1169 * flags, so we can modify them if necessary.
1170 * @param pfCallRealApi Whether it's ok to go on to the real API.
1171 * @param pszCaller Who is calling (for debugging / logging).
1172 * @param fAvoidWinVerifyTrust Whether we should avoid WinVerifyTrust.
1173 * @param pfQuiet Where to return whether to be quiet about
1174 * this image in the log (i.e. we've seen it
1175 * lots of times already). Optional.
1176 */
1177static NTSTATUS supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
1178 bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust, bool *pfQuiet)
1179{
1180 *pfCallRealApi = false;
1181 if (pfQuiet)
1182 *pfQuiet = false;
1183
1184 /*
1185 * Query the name of the file, making sure to zero terminator the
1186 * string. (2nd half of buffer is used for error info, see below.)
1187 */
1188 union
1189 {
1190 UNICODE_STRING UniStr;
1191 uint8_t abBuffer[sizeof(UNICODE_STRING) + 2048 * sizeof(WCHAR)];
1192 } uBuf;
1193 RT_ZERO(uBuf);
1194 ULONG cbNameBuf;
1195 NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
1196 if (!NT_SUCCESS(rcNt))
1197 {
1198 supR3HardenedError(VINF_SUCCESS, false,
1199 "supR3HardenedScreenImage/%s: NtQueryObject -> %#x (fImage=%d fProtect=%#x fAccess=%#x)\n",
1200 pszCaller, fImage, *pfProtect, *pfAccess);
1201 return rcNt;
1202 }
1203
1204 if (supHardNtVpIsPossible8dot3Path(uBuf.UniStr.Buffer))
1205 {
1206 uBuf.UniStr.MaximumLength = sizeof(uBuf) - 128;
1207 supHardNtVpFix8dot3Path(&uBuf.UniStr, true /*fPathOnly*/);
1208 }
1209
1210 /*
1211 * Check the cache.
1212 */
1213 PVERIFIERCACHEENTRY pCacheHit = supR3HardenedWinVerifyCacheLookup(&uBuf.UniStr, hFile);
1214 if (pCacheHit)
1215 {
1216 /* Do hit accounting and figure whether we need to be quiet or not. */
1217 uint32_t cHits = ASMAtomicIncU32(&pCacheHit->cHits);
1218 bool const fQuiet = cHits >= 8 && !RT_IS_POWER_OF_TWO(cHits);
1219 if (pfQuiet)
1220 *pfQuiet = fQuiet;
1221
1222 /* If we haven't done the WinVerifyTrust thing, do it if we can. */
1223 if ( !pCacheHit->fWinVerifyTrust
1224 && RT_SUCCESS(pCacheHit->rc)
1225 && supHardenedWinIsWinVerifyTrustCallable() )
1226 {
1227 if (!fAvoidWinVerifyTrust)
1228 {
1229 SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [redoing WinVerifyTrust]\n",
1230 pszCaller, pCacheHit->rc, pCacheHit->wszPath));
1231
1232 bool fWinVerifyTrust = false;
1233 int rc = supHardenedWinVerifyImageTrust(pCacheHit->hFile, pCacheHit->wszPath, pCacheHit->fFlags, pCacheHit->rc,
1234 &fWinVerifyTrust, NULL /* pErrInfo*/);
1235 if (RT_FAILURE(rc) || fWinVerifyTrust)
1236 {
1237 SUP_DPRINTF(("supR3HardenedScreenImage/%s: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
1238 pszCaller, rc, pCacheHit->rc, fWinVerifyTrust, pCacheHit->wszPath));
1239 pCacheHit->fWinVerifyTrust = true;
1240 pCacheHit->rc = rc;
1241 }
1242 else
1243 SUP_DPRINTF(("supR3HardenedScreenImage/%s: WinVerifyTrust not available, rescheduling %ls\n",
1244 pszCaller, pCacheHit->wszPath));
1245 }
1246 else
1247 SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [avoiding WinVerifyTrust]\n",
1248 pszCaller, pCacheHit->rc, pCacheHit->wszPath));
1249 }
1250 else if (!fQuiet || !pCacheHit->fWinVerifyTrust)
1251 SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls%s\n",
1252 pszCaller, pCacheHit->rc, pCacheHit->wszPath, pCacheHit->fWinVerifyTrust ? "" : " [lacks WinVerifyTrust]"));
1253
1254 /* Return the cached value. */
1255 if (RT_SUCCESS(pCacheHit->rc))
1256 {
1257 *pfCallRealApi = true;
1258 return STATUS_SUCCESS;
1259 }
1260
1261 if (!fQuiet)
1262 supR3HardenedError(VINF_SUCCESS, false,
1263 "supR3HardenedScreenImage/%s: cached rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x cHits=%u %ls\n",
1264 pszCaller, pCacheHit->rc, fImage, *pfProtect, *pfAccess, cHits, uBuf.UniStr.Buffer);
1265 return STATUS_TRUST_FAILURE;
1266 }
1267
1268 /*
1269 * On XP the loader might hand us handles with just FILE_EXECUTE and
1270 * SYNCHRONIZE, the means reading will fail later on. Also, we need
1271 * READ_CONTROL access to check the file ownership later on, and non
1272 * of the OS versions seems be giving us that. So, in effect we
1273 * more or less always reopen the file here.
1274 */
1275 HANDLE hMyFile = NULL;
1276 rcNt = NtDuplicateObject(NtCurrentProcess(), hFile, NtCurrentProcess(),
1277 &hMyFile,
1278 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
1279 0 /* Handle attributes*/, 0 /* Options */);
1280 if (!NT_SUCCESS(rcNt))
1281 {
1282 if (rcNt == STATUS_ACCESS_DENIED)
1283 {
1284 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1285 OBJECT_ATTRIBUTES ObjAttr;
1286 InitializeObjectAttributes(&ObjAttr, &uBuf.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1287
1288 rcNt = NtCreateFile(&hMyFile,
1289 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
1290 &ObjAttr,
1291 &Ios,
1292 NULL /* Allocation Size*/,
1293 FILE_ATTRIBUTE_NORMAL,
1294 FILE_SHARE_READ,
1295 FILE_OPEN,
1296 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1297 NULL /*EaBuffer*/,
1298 0 /*EaLength*/);
1299 if (NT_SUCCESS(rcNt))
1300 rcNt = Ios.Status;
1301 if (!NT_SUCCESS(rcNt))
1302 {
1303 supR3HardenedError(VINF_SUCCESS, false,
1304 "supR3HardenedScreenImage/%s: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
1305 pszCaller, rcNt, hFile, uBuf.UniStr.Buffer);
1306 return rcNt;
1307 }
1308
1309 /* Check that we've got the same file. */
1310 LARGE_INTEGER idMyFile, idInFile;
1311 bool fMyValid = supR3HardenedWinVerifyCacheGetIndexNumber(hMyFile, &idMyFile);
1312 bool fInValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &idInFile);
1313 if ( fMyValid
1314 && ( fMyValid != fInValid
1315 || idMyFile.QuadPart != idInFile.QuadPart))
1316 {
1317 supR3HardenedError(VINF_SUCCESS, false,
1318 "supR3HardenedScreenImage/%s: Re-opened has different ID that input: %#llx vx %#llx (%ls)\n",
1319 pszCaller, rcNt, idMyFile.QuadPart, idInFile.QuadPart, uBuf.UniStr.Buffer);
1320 NtClose(hMyFile);
1321 return STATUS_TRUST_FAILURE;
1322 }
1323 }
1324 else
1325 {
1326 SUP_DPRINTF(("supR3HardenedScreenImage/%s: NtDuplicateObject -> %#x\n", pszCaller, rcNt));
1327#ifdef DEBUG
1328
1329 supR3HardenedError(VINF_SUCCESS, false,
1330 "supR3HardenedScreenImage/%s: NtDuplicateObject(,%#x,) failed: %#x\n", pszCaller, hFile, rcNt);
1331#endif
1332 hMyFile = hFile;
1333 }
1334 }
1335
1336 /*
1337 * Special Kludge for Windows XP and W2K3 and their stupid attempts
1338 * at mapping a hidden XML file called c:\Windows\WindowsShell.Manifest
1339 * with executable access. The image bit isn't set, fortunately.
1340 */
1341 if ( !fImage
1342 && uBuf.UniStr.Length > g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)
1343 && memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer,
1344 g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) == 0)
1345 {
1346 PRTUTF16 pwszName = &uBuf.UniStr.Buffer[(g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) / sizeof(WCHAR)];
1347 if (RTUtf16ICmpAscii(pwszName, "WindowsShell.Manifest") == 0)
1348 {
1349 /*
1350 * Drop all executable access to the mapping and let it continue.
1351 */
1352 SUP_DPRINTF(("supR3HardenedScreenImage/%s: Applying the drop-exec-kludge for '%ls'\n", pszCaller, uBuf.UniStr.Buffer));
1353 if (*pfAccess & SECTION_MAP_EXECUTE)
1354 *pfAccess = (*pfAccess & ~SECTION_MAP_EXECUTE) | SECTION_MAP_READ;
1355 if (*pfProtect & PAGE_EXECUTE)
1356 *pfProtect = (*pfProtect & ~PAGE_EXECUTE) | PAGE_READONLY;
1357 *pfProtect = (*pfProtect & ~UINT32_C(0xf0)) | ((*pfProtect & UINT32_C(0xe0)) >> 4);
1358 if (hMyFile != hFile)
1359 NtClose(hMyFile);
1360 *pfCallRealApi = true;
1361 return STATUS_SUCCESS;
1362 }
1363 }
1364
1365#ifndef VBOX_PERMIT_EVEN_MORE
1366 /*
1367 * Check the path. We don't allow DLLs to be loaded from just anywhere:
1368 * 1. System32 - normal code or cat signing, owner TrustedInstaller.
1369 * 2. WinSxS - normal code or cat signing, owner TrustedInstaller.
1370 * 3. VirtualBox - kernel code signing and integrity checks.
1371 * 4. AppPatchDir - normal code or cat signing, owner TrustedInstaller.
1372 * 5. Program Files - normal code or cat signing, owner TrustedInstaller.
1373 * 6. Common Files - normal code or cat signing, owner TrustedInstaller.
1374 * 7. x86 variations of 4 & 5 - ditto.
1375 */
1376 uint32_t fFlags = 0;
1377 if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_System32NtPath.UniStr, true /*fCheckSlash*/))
1378 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1379 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_WinSxSNtPath.UniStr, true /*fCheckSlash*/))
1380 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1381 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_SupLibHardenedAppBinNtPath.UniStr, true /*fCheckSlash*/))
1382 fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
1383# ifdef VBOX_PERMIT_MORE
1384 else if (supHardViIsAppPatchDir(uBuf.UniStr.Buffer, uBuf.UniStr.Length / sizeof(WCHAR)))
1385 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1386 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesNtPath.UniStr, true /*fCheckSlash*/))
1387 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1388 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesNtPath.UniStr, true /*fCheckSlash*/))
1389 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1390# ifdef RT_ARCH_AMD64
1391 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesX86NtPath.UniStr, true /*fCheckSlash*/))
1392 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1393 else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesX86NtPath.UniStr, true /*fCheckSlash*/))
1394 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1395# endif
1396# endif
1397# ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1398 /* Hack to allow profiling our code with Visual Studio. */
1399 else if ( uBuf.UniStr.Length > sizeof(L"\\SamplingRuntime.dll")
1400 && memcmp(uBuf.UniStr.Buffer + (uBuf.UniStr.Length - sizeof(L"\\SamplingRuntime.dll") + sizeof(WCHAR)) / sizeof(WCHAR),
1401 L"\\SamplingRuntime.dll", sizeof(L"\\SamplingRuntime.dll") - sizeof(WCHAR)) == 0 )
1402 {
1403 if (hMyFile != hFile)
1404 NtClose(hMyFile);
1405 *pfCallRealApi = true;
1406 return STATUS_SUCCESS;
1407 }
1408# endif
1409 else
1410 {
1411 supR3HardenedError(VINF_SUCCESS, false,
1412 "supR3HardenedScreenImage/%s: Not a trusted location: '%ls' (fImage=%d fProtect=%#x fAccess=%#x)\n",
1413 pszCaller, uBuf.UniStr.Buffer, fImage, *pfAccess, *pfProtect);
1414 if (hMyFile != hFile)
1415 NtClose(hMyFile);
1416 return STATUS_TRUST_FAILURE;
1417 }
1418
1419#else /* VBOX_PERMIT_EVEN_MORE */
1420 /*
1421 * Require trusted installer + some kind of signature on everything, except
1422 * for the VBox bits where we require kernel code signing and special
1423 * integrity checks.
1424 */
1425 uint32_t fFlags = 0;
1426 if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_SupLibHardenedAppBinNtPath.UniStr, true /*fCheckSlash*/))
1427 fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
1428 else
1429 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
1430#endif /* VBOX_PERMIT_EVEN_MORE */
1431
1432 /*
1433 * Do the verification. For better error message we borrow what's
1434 * left of the path buffer for an RTERRINFO buffer.
1435 */
1436 if (fIgnoreArch)
1437 fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
1438 RTERRINFO ErrInfo;
1439 RTErrInfoInit(&ErrInfo, (char *)&uBuf.abBuffer[cbNameBuf], sizeof(uBuf) - cbNameBuf);
1440
1441 int rc;
1442 bool fWinVerifyTrust = false;
1443 rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, fAvoidWinVerifyTrust, &fWinVerifyTrust, &ErrInfo);
1444 if (RT_FAILURE(rc))
1445 {
1446 supR3HardenedError(VINF_SUCCESS, false,
1447 "supR3HardenedScreenImage/%s: rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x %ls: %s\n",
1448 pszCaller, rc, fImage, *pfAccess, *pfProtect, uBuf.UniStr.Buffer, ErrInfo.pszMsg);
1449 if (hMyFile != hFile)
1450 supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
1451 return STATUS_TRUST_FAILURE;
1452 }
1453
1454 /*
1455 * Insert into the cache.
1456 */
1457 if (hMyFile != hFile)
1458 supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
1459
1460 *pfCallRealApi = true;
1461 return STATUS_SUCCESS;
1462}
1463
1464
1465/**
1466 * Preloads a file into the verify cache if possible.
1467 *
1468 * This is used to avoid known cyclic LoadLibrary issues with WinVerifyTrust.
1469 *
1470 * @param pwszName The name of the DLL to verify.
1471 */
1472DECLHIDDEN(void) supR3HardenedWinVerifyCachePreload(PCRTUTF16 pwszName)
1473{
1474 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1475 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1476
1477 UNICODE_STRING UniStr;
1478 UniStr.Buffer = (PWCHAR)pwszName;
1479 UniStr.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
1480 UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
1481
1482 OBJECT_ATTRIBUTES ObjAttr;
1483 InitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1484
1485 NTSTATUS rcNt = NtCreateFile(&hFile,
1486 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
1487 &ObjAttr,
1488 &Ios,
1489 NULL /* Allocation Size*/,
1490 FILE_ATTRIBUTE_NORMAL,
1491 FILE_SHARE_READ,
1492 FILE_OPEN,
1493 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1494 NULL /*EaBuffer*/,
1495 0 /*EaLength*/);
1496 if (NT_SUCCESS(rcNt))
1497 rcNt = Ios.Status;
1498 if (!NT_SUCCESS(rcNt))
1499 {
1500 SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: Error %#x opening '%ls'.\n", rcNt, pwszName));
1501 return;
1502 }
1503
1504 ULONG fAccess = 0;
1505 ULONG fProtect = 0;
1506 bool fCallRealApi;
1507 //SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: scanning %ls\n", pwszName));
1508 supR3HardenedScreenImage(hFile, false, false /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi, "preload",
1509 false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
1510 //SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: done %ls\n", pwszName));
1511
1512 NtClose(hFile);
1513}
1514
1515
1516
1517/**
1518 * Hook that monitors NtCreateSection calls.
1519 *
1520 * @returns NT status code.
1521 * @param phSection Where to return the section handle.
1522 * @param fAccess The desired access.
1523 * @param pObjAttribs The object attributes (optional).
1524 * @param pcbSection The section size (optional).
1525 * @param fProtect The max section protection.
1526 * @param fAttribs The section attributes.
1527 * @param hFile The file to create a section from (optional).
1528 */
1529static NTSTATUS NTAPI
1530supR3HardenedMonitor_NtCreateSection(PHANDLE phSection, ACCESS_MASK fAccess, POBJECT_ATTRIBUTES pObjAttribs,
1531 PLARGE_INTEGER pcbSection, ULONG fProtect, ULONG fAttribs, HANDLE hFile)
1532{
1533 if ( hFile != NULL
1534 && hFile != INVALID_HANDLE_VALUE)
1535 {
1536 bool const fImage = RT_BOOL(fAttribs & (SEC_IMAGE | SEC_PROTECTED_IMAGE));
1537 bool const fExecMap = RT_BOOL(fAccess & SECTION_MAP_EXECUTE);
1538 bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
1539 | PAGE_EXECUTE_READWRITE));
1540 if (fImage || fExecMap || fExecProt)
1541 {
1542 DWORD dwSavedLastError = RtlGetLastWin32Error();
1543
1544 bool fCallRealApi;
1545 //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 1\n"));
1546 NTSTATUS rcNt = supR3HardenedScreenImage(hFile, fImage, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
1547 "NtCreateSection", true /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
1548 //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 2 rcNt=%#x fCallRealApi=%#x\n", rcNt, fCallRealApi));
1549
1550 RtlRestoreLastWin32Error(dwSavedLastError);
1551
1552 if (!NT_SUCCESS(rcNt))
1553 return rcNt;
1554 Assert(fCallRealApi);
1555 if (!fCallRealApi)
1556 return STATUS_TRUST_FAILURE;
1557
1558 }
1559 }
1560
1561 /*
1562 * Call checked out OK, call the original.
1563 */
1564 return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
1565}
1566
1567
1568/**
1569 * Helper for supR3HardenedMonitor_LdrLoadDll.
1570 *
1571 * @returns NT status code.
1572 * @param pwszPath The path destination buffer.
1573 * @param cwcPath The size of the path buffer.
1574 * @param pUniStrResult The result string.
1575 * @param pOrgName The orignal name (for errors).
1576 * @param pcwc Where to return the actual length.
1577 */
1578static NTSTATUS supR3HardenedCopyRedirectionResult(WCHAR *pwszPath, size_t cwcPath, PUNICODE_STRING pUniStrResult,
1579 PUNICODE_STRING pOrgName, UINT *pcwc)
1580{
1581 UINT cwc;
1582 *pcwc = cwc = pUniStrResult->Length / sizeof(WCHAR);
1583 if (pUniStrResult->Buffer == pwszPath)
1584 pwszPath[cwc] = '\0';
1585 else
1586 {
1587 if (cwc > cwcPath - 1)
1588 {
1589 supR3HardenedError(VINF_SUCCESS, false,
1590 "supR3HardenedMonitor_LdrLoadDll: Name too long: %.*ls -> %.*ls (RtlDosApplyFileIoslationRedirection_Ustr)\n",
1591 pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer,
1592 pUniStrResult->Length / sizeof(WCHAR), pUniStrResult->Buffer);
1593 return STATUS_NAME_TOO_LONG;
1594 }
1595 memcpy(&pwszPath[0], pUniStrResult->Buffer, pUniStrResult->Length);
1596 pwszPath[cwc] = '\0';
1597 }
1598 return STATUS_SUCCESS;
1599}
1600
1601
1602/**
1603 * Hooks that intercepts LdrLoadDll calls.
1604 *
1605 * Two purposes:
1606 * -# Enforce our own search path restrictions.
1607 * -# Prevalidate DLLs about to be loaded so we don't upset the loader data
1608 * by doing it from within the NtCreateSection hook (WinVerifyTrust
1609 * seems to be doing harm there on W7/32).
1610 *
1611 * @returns
1612 * @param pwszSearchPath The search path to use.
1613 * @param pfFlags Flags on input. DLL characteristics or something
1614 * on return?
1615 * @param pName The name of the module.
1616 * @param phMod Where the handle of the loaded DLL is to be
1617 * returned to the caller.
1618 */
1619static NTSTATUS NTAPI
1620supR3HardenedMonitor_LdrLoadDll(PWSTR pwszSearchPath, PULONG pfFlags, PUNICODE_STRING pName, PHANDLE phMod)
1621{
1622 DWORD dwSavedLastError = RtlGetLastWin32Error();
1623 PUNICODE_STRING const pOrgName = pName;
1624 NTSTATUS rcNt;
1625
1626 /*
1627 * Make sure the DLL notification callback is registered. If we could, we
1628 * would've done this during early process init, but due to lack of heap
1629 * and uninitialized loader lock, it's not possible that early on.
1630 *
1631 * The callback protects our NtDll hooks from getting unhooked by
1632 * "friendly" fire from the AV crowd.
1633 */
1634 supR3HardenedWinRegisterDllNotificationCallback();
1635
1636 /*
1637 * Process WinVerifyTrust todo before and after.
1638 */
1639 supR3HardenedWinVerifyCacheProcessWvtTodos();
1640
1641 /*
1642 * Reject things we don't want to deal with.
1643 */
1644 if (!pName || pName->Length == 0)
1645 {
1646 supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: name is NULL or have a zero length.\n");
1647 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x (pName=%p)\n", STATUS_INVALID_PARAMETER, pName));
1648 RtlRestoreLastWin32Error(dwSavedLastError);
1649 return STATUS_INVALID_PARAMETER;
1650 }
1651 /*SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls *pfFlags=%#x pwszSearchPath=%p:%ls\n",
1652 (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
1653 !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));*/
1654
1655 /*
1656 * Reject long paths that's close to the 260 limit without looking.
1657 */
1658 if (pName->Length > 256 * sizeof(WCHAR))
1659 {
1660 supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: too long name: %#x bytes\n", pName->Length);
1661 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
1662 RtlRestoreLastWin32Error(dwSavedLastError);
1663 return STATUS_NAME_TOO_LONG;
1664 }
1665
1666 /*
1667 * Absolute path?
1668 */
1669 NTSTATUS rcNtResolve = STATUS_SUCCESS;
1670 bool fSkipValidation = false;
1671 bool fCheckIfLoaded = false;
1672 WCHAR wszPath[260];
1673 static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
1674 UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
1675 UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
1676 PUNICODE_STRING pUniStrResult = NULL;
1677 UNICODE_STRING ResolvedName;
1678
1679 if ( ( pName->Length >= 4 * sizeof(WCHAR)
1680 && RT_C_IS_ALPHA(pName->Buffer[0])
1681 && pName->Buffer[1] == ':'
1682 && RTPATH_IS_SLASH(pName->Buffer[2]) )
1683 || ( pName->Length >= 1 * sizeof(WCHAR)
1684 && RTPATH_IS_SLASH(pName->Buffer[1]) )
1685 )
1686 {
1687 rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
1688 pName,
1689 (PUNICODE_STRING)&s_DefaultSuffix,
1690 &UniStrStatic,
1691 &UniStrDynamic,
1692 &pUniStrResult,
1693 NULL /*pNewFlags*/,
1694 NULL /*pcbFilename*/,
1695 NULL /*pcbNeeded*/);
1696 if (NT_SUCCESS(rcNtResolve))
1697 {
1698 UINT cwc;
1699 rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
1700 RtlFreeUnicodeString(&UniStrDynamic);
1701 if (!NT_SUCCESS(rcNt))
1702 {
1703 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
1704 RtlRestoreLastWin32Error(dwSavedLastError);
1705 return rcNt;
1706 }
1707
1708 ResolvedName.Buffer = wszPath;
1709 ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
1710 ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
1711
1712 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: '%.*ls' -> '%.*ls' [redir]\n",
1713 (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
1714 ResolvedName.Length / sizeof(WCHAR), ResolvedName.Buffer, rcNt));
1715 pName = &ResolvedName;
1716 }
1717 else
1718 {
1719 memcpy(wszPath, pName->Buffer, pName->Length);
1720 wszPath[pName->Length / sizeof(WCHAR)] = '\0';
1721 }
1722 }
1723 /*
1724 * Not an absolute path. Check if it's one of those special API set DLLs
1725 * or something we're known to use but should be taken from WinSxS.
1726 */
1727 else if ( supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
1728 L"api-ms-win-", 11, false /*fCheckSlash*/)
1729 || supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
1730 L"ext-ms-win-", 11, false /*fCheckSlash*/)
1731 )
1732 {
1733 memcpy(wszPath, pName->Buffer, pName->Length);
1734 wszPath[pName->Length / sizeof(WCHAR)] = '\0';
1735 fSkipValidation = true;
1736 }
1737 /*
1738 * Not an absolute path or special API set. There are two alternatives
1739 * now, either there is no path at all or there is a relative path. We
1740 * will resolve it to an absolute path in either case, failing the call
1741 * if we can't.
1742 */
1743 else
1744 {
1745 PCWCHAR pawcName = pName->Buffer;
1746 uint32_t cwcName = pName->Length / sizeof(WCHAR);
1747 uint32_t offLastSlash = UINT32_MAX;
1748 uint32_t offLastDot = UINT32_MAX;
1749 for (uint32_t i = 0; i < cwcName; i++)
1750 switch (pawcName[i])
1751 {
1752 case '\\':
1753 case '/':
1754 offLastSlash = i;
1755 offLastDot = UINT32_MAX;
1756 break;
1757 case '.':
1758 offLastDot = i;
1759 break;
1760 }
1761
1762 bool const fNeedDllSuffix = offLastDot == UINT32_MAX && offLastSlash == UINT32_MAX;
1763
1764 if (offLastDot != UINT32_MAX && offLastDot == cwcName - 1)
1765 cwcName--;
1766
1767 /*
1768 * Reject relative paths for now as they might be breakout attempts.
1769 */
1770 if (offLastSlash != UINT32_MAX)
1771 {
1772 supR3HardenedError(VINF_SUCCESS, false,
1773 "supR3HardenedMonitor_LdrLoadDll: relative name not permitted: %.*ls\n",
1774 cwcName, pawcName);
1775 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
1776 RtlRestoreLastWin32Error(dwSavedLastError);
1777 return STATUS_OBJECT_NAME_INVALID;
1778 }
1779
1780 /*
1781 * Perform dll redirection to WinSxS such. We using an undocumented
1782 * API here, which as always is a bit risky... ASSUMES that the API
1783 * returns a full DOS path.
1784 */
1785 UINT cwc;
1786 rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
1787 pName,
1788 (PUNICODE_STRING)&s_DefaultSuffix,
1789 &UniStrStatic,
1790 &UniStrDynamic,
1791 &pUniStrResult,
1792 NULL /*pNewFlags*/,
1793 NULL /*pcbFilename*/,
1794 NULL /*pcbNeeded*/);
1795 if (NT_SUCCESS(rcNtResolve))
1796 {
1797 rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
1798 RtlFreeUnicodeString(&UniStrDynamic);
1799 if (!NT_SUCCESS(rcNt))
1800 {
1801 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
1802 RtlRestoreLastWin32Error(dwSavedLastError);
1803 return rcNt;
1804 }
1805 }
1806 else
1807 {
1808 /*
1809 * Search for the DLL. Only System32 is allowed as the target of
1810 * a search on the API level, all VBox calls will have full paths.
1811 * If the DLL is not in System32, we will resort to check if it's
1812 * refering to an already loaded DLL (fCheckIfLoaded).
1813 */
1814 AssertCompile(sizeof(g_System32WinPath.awcBuffer) <= sizeof(wszPath));
1815 cwc = g_System32WinPath.UniStr.Length / sizeof(RTUTF16); Assert(cwc > 2);
1816 if (cwc + 1 + cwcName + fNeedDllSuffix * 4 >= RT_ELEMENTS(wszPath))
1817 {
1818 supR3HardenedError(VINF_SUCCESS, false,
1819 "supR3HardenedMonitor_LdrLoadDll: Name too long (system32): %.*ls\n", cwcName, pawcName);
1820 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
1821 RtlRestoreLastWin32Error(dwSavedLastError);
1822 return STATUS_NAME_TOO_LONG;
1823 }
1824 memcpy(wszPath, g_System32WinPath.UniStr.Buffer, cwc * sizeof(RTUTF16));
1825 wszPath[cwc++] = '\\';
1826 memcpy(&wszPath[cwc], pawcName, cwcName * sizeof(WCHAR));
1827 cwc += cwcName;
1828 if (!fNeedDllSuffix)
1829 wszPath[cwc] = '\0';
1830 else
1831 {
1832 memcpy(&wszPath[cwc], L".dll", 5 * sizeof(WCHAR));
1833 cwc += 4;
1834 }
1835 fCheckIfLoaded = true;
1836 }
1837
1838 ResolvedName.Buffer = wszPath;
1839 ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
1840 ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
1841 pName = &ResolvedName;
1842 }
1843
1844 bool fQuiet = false;
1845 if (!fSkipValidation)
1846 {
1847 /*
1848 * Try open the file. If this fails, never mind, just pass it on to
1849 * the real API as we've replaced any searchable name with a full name
1850 * and the real API can come up with a fitting status code for it.
1851 */
1852 HANDLE hRootDir;
1853 UNICODE_STRING NtPathUniStr;
1854 int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, wszPath, RTSTR_MAX);
1855 if (RT_FAILURE(rc))
1856 {
1857 supR3HardenedError(rc, false,
1858 "supR3HardenedMonitor_LdrLoadDll: RTNtPathFromWinUtf16Ex failed on '%ls': %Rrc\n", wszPath, rc);
1859 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
1860 RtlRestoreLastWin32Error(dwSavedLastError);
1861 return STATUS_OBJECT_NAME_INVALID;
1862 }
1863
1864 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1865 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1866 OBJECT_ATTRIBUTES ObjAttr;
1867 InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
1868
1869 rcNt = NtCreateFile(&hFile,
1870 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
1871 &ObjAttr,
1872 &Ios,
1873 NULL /* Allocation Size*/,
1874 FILE_ATTRIBUTE_NORMAL,
1875 FILE_SHARE_READ,
1876 FILE_OPEN,
1877 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1878 NULL /*EaBuffer*/,
1879 0 /*EaLength*/);
1880 if (NT_SUCCESS(rcNt))
1881 rcNt = Ios.Status;
1882 if (NT_SUCCESS(rcNt))
1883 {
1884 ULONG fAccess = 0;
1885 ULONG fProtect = 0;
1886 bool fCallRealApi = false;
1887 rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, RT_VALID_PTR(pfFlags) && (*pfFlags & 0x2) /*fIgnoreArch*/,
1888 &fAccess, &fProtect, &fCallRealApi,
1889 "LdrLoadDll", false /*fAvoidWinVerifyTrust*/, &fQuiet);
1890 NtClose(hFile);
1891 if (!NT_SUCCESS(rcNt))
1892 {
1893 if (!fQuiet)
1894 {
1895 if (pOrgName != pName)
1896 supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls': rcNt=%#x\n",
1897 wszPath, rcNt);
1898 else
1899 supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls' (%.*ls): rcNt=%#x\n",
1900 wszPath, pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNt);
1901 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
1902 }
1903 RtlRestoreLastWin32Error(dwSavedLastError);
1904 return rcNt;
1905 }
1906
1907 supR3HardenedWinVerifyCacheProcessImportTodos();
1908 }
1909 else
1910 {
1911 DWORD dwErr = RtlGetLastWin32Error();
1912
1913 /*
1914 * Deal with special case where the caller (first case was MS LifeCam)
1915 * is using LoadLibrary instead of GetModuleHandle to find a loaded DLL.
1916 */
1917 NTSTATUS rcNtGetDll = STATUS_SUCCESS;
1918 if ( fCheckIfLoaded
1919 && ( rcNt == STATUS_OBJECT_NAME_NOT_FOUND
1920 || rcNt == STATUS_OBJECT_PATH_NOT_FOUND))
1921 {
1922 rcNtGetDll = LdrGetDllHandle(NULL /*DllPath*/, NULL /*pfFlags*/, pOrgName, phMod);
1923 if (NT_SUCCESS(rcNtGetDll))
1924 {
1925 RtlRestoreLastWin32Error(dwSavedLastError);
1926 return rcNtGetDll;
1927 }
1928 }
1929
1930 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u (NtPath=%.*ls; Input=%.*ls; rcNtGetDll=%#x\n",
1931 wszPath, dwErr, NtPathUniStr.Length / sizeof(RTUTF16), NtPathUniStr.Buffer,
1932 pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtGetDll));
1933 }
1934 RTNtPathFree(&NtPathUniStr, &hRootDir);
1935 }
1936
1937 /*
1938 * Screened successfully enough. Call the real thing.
1939 */
1940 if (!fQuiet)
1941 {
1942 if (pOrgName != pName)
1943 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (Input=%.*ls, rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
1944 (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
1945 (unsigned)pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtResolve,
1946 pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
1947 !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
1948 else
1949 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
1950 (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, rcNtResolve,
1951 pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
1952 !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
1953 }
1954
1955 RtlRestoreLastWin32Error(dwSavedLastError);
1956 rcNt = g_pfnLdrLoadDllReal(pwszSearchPath, pfFlags, pName, phMod);
1957
1958 /*
1959 * Log the result and process pending WinVerifyTrust work if we can.
1960 */
1961 dwSavedLastError = RtlGetLastWin32Error();
1962
1963 if (NT_SUCCESS(rcNt) && phMod)
1964 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x hMod=%p '%ls'\n", rcNt, *phMod, wszPath));
1965 else if (!NT_SUCCESS(rcNt) || !fQuiet)
1966 SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
1967
1968 supR3HardenedWinVerifyCacheProcessWvtTodos();
1969
1970 RtlRestoreLastWin32Error(dwSavedLastError);
1971
1972 return rcNt;
1973}
1974
1975
1976/**
1977 * DLL load and unload notification callback.
1978 *
1979 * This is a safety against our LdrLoadDll hook being replaced by protection
1980 * software. Though, we prefer the LdrLoadDll hook to this one as it allows us
1981 * to call WinVerifyTrust more freely.
1982 *
1983 * @param ulReason The reason we're called, see
1984 * LDR_DLL_NOTIFICATION_REASON_XXX.
1985 * @param pData Reason specific data. (Format is currently the same for
1986 * both load and unload.)
1987 * @param pvUser User parameter (ignored).
1988 *
1989 * @remarks Vista and later.
1990 * @remarks The loader lock is held when we're called, at least on Windows 7.
1991 */
1992static VOID CALLBACK supR3HardenedDllNotificationCallback(ULONG ulReason, PCLDR_DLL_NOTIFICATION_DATA pData, PVOID pvUser)
1993{
1994 NOREF(pvUser);
1995
1996 /*
1997 * Screen the image on load. We will normally get a verification cache
1998 * hit here because of the LdrLoadDll and NtCreateSection hooks, so it
1999 * should be relatively cheap to recheck. In case our NtDll patches
2000 * got re
2001 *
2002 * This ASSUMES that we get informed after the fact as indicated by the
2003 * available documentation.
2004 */
2005 if (ulReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
2006 {
2007 SUP_DPRINTF(("supR3HardenedDllNotificationCallback: load %p LB %#010x %.*ls [fFlags=%#x]\n",
2008 pData->Loaded.DllBase, pData->Loaded.SizeOfImage,
2009 pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2010 pData->Loaded.Flags));
2011
2012 /* Convert the windows path to an NT path and open it. */
2013 HANDLE hRootDir;
2014 UNICODE_STRING NtPathUniStr;
2015 int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, pData->Loaded.FullDllName->Buffer,
2016 pData->Loaded.FullDllName->Length / sizeof(WCHAR));
2017 if (RT_FAILURE(rc))
2018 {
2019 supR3HardenedFatal("supR3HardenedDllNotificationCallback: RTNtPathFromWinUtf16Ex failed on '%.*ls': %Rrc\n",
2020 pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer, rc);
2021 return;
2022 }
2023
2024 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
2025 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2026 OBJECT_ATTRIBUTES ObjAttr;
2027 InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
2028
2029 NTSTATUS rcNt = NtCreateFile(&hFile,
2030 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2031 &ObjAttr,
2032 &Ios,
2033 NULL /* Allocation Size*/,
2034 FILE_ATTRIBUTE_NORMAL,
2035 FILE_SHARE_READ,
2036 FILE_OPEN,
2037 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2038 NULL /*EaBuffer*/,
2039 0 /*EaLength*/);
2040 if (NT_SUCCESS(rcNt))
2041 rcNt = Ios.Status;
2042 if (!NT_SUCCESS(rcNt))
2043 {
2044 supR3HardenedFatal("supR3HardenedDllNotificationCallback: NtCreateFile failed on '%.*ls' / '%.*ls': %#x\n",
2045 pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2046 NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
2047 RTNtPathFree(&NtPathUniStr, &hRootDir);
2048 return;
2049 }
2050
2051 /* Do the screening. */
2052 ULONG fAccess = 0;
2053 ULONG fProtect = 0;
2054 bool fCallRealApi = false;
2055 bool fQuietFailure = false;
2056 rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
2057 "LdrLoadDll", true /*fAvoidWinVerifyTrust*/, &fQuietFailure);
2058 NtClose(hFile);
2059 if (!NT_SUCCESS(rcNt))
2060 {
2061 supR3HardenedFatal("supR3HardenedDllNotificationCallback: supR3HardenedScreenImage failed on '%.*ls' / '%.*ls': %#x\n",
2062 pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2063 NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
2064 RTNtPathFree(&NtPathUniStr, &hRootDir);
2065 return;
2066 }
2067 RTNtPathFree(&NtPathUniStr, &hRootDir);
2068 }
2069 /*
2070 * Log the unload call.
2071 */
2072 else if (ulReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
2073 {
2074 SUP_DPRINTF(("supR3HardenedDllNotificationCallback: Unload %p LB %#010x %.*ls [flags=%#x]\n",
2075 pData->Unloaded.DllBase, pData->Unloaded.SizeOfImage,
2076 pData->Unloaded.FullDllName->Length / sizeof(WCHAR), pData->Unloaded.FullDllName->Buffer,
2077 pData->Unloaded.Flags));
2078 }
2079 /*
2080 * Just log things we don't know and then return without caching anything.
2081 */
2082 else
2083 {
2084 static uint32_t s_cLogEntries = 0;
2085 if (s_cLogEntries++ < 32)
2086 SUP_DPRINTF(("supR3HardenedDllNotificationCallback: ulReason=%u pData=%p\n", ulReason, pData));
2087 return;
2088 }
2089
2090 /*
2091 * Use this opportunity to make sure our NtDll patches are still in place,
2092 * since they may be replaced by indecent protection software solutions.
2093 */
2094 supR3HardenedWinReInstallHooks(false /*fFirstCall */);
2095}
2096
2097
2098/**
2099 * Registers the DLL notification callback if it hasn't already been registered.
2100 */
2101static void supR3HardenedWinRegisterDllNotificationCallback(void)
2102{
2103 /*
2104 * The notification API was added in Vista, so it's an optional (weak) import.
2105 */
2106 if ( LdrRegisterDllNotification != NULL
2107 && g_cDllNotificationRegistered <= 0
2108 && g_cDllNotificationRegistered > -32)
2109 {
2110 NTSTATUS rcNt = LdrRegisterDllNotification(0, supR3HardenedDllNotificationCallback, NULL, &g_pvDllNotificationCookie);
2111 if (NT_SUCCESS(rcNt))
2112 {
2113 SUP_DPRINTF(("Registered Dll notification callback with NTDLL.\n"));
2114 g_cDllNotificationRegistered = 1;
2115 }
2116 else
2117 {
2118 supR3HardenedError(rcNt, false /*fFatal*/, "LdrRegisterDllNotification failed: %#x\n", rcNt);
2119 g_cDllNotificationRegistered--;
2120 }
2121 }
2122}
2123
2124
2125static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
2126{
2127 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
2128 "Failed to install %s monitor: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n "
2129#ifdef RT_ARCH_X86
2130 "(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
2131#endif
2132 ,
2133 pszWhich,
2134 pbPrologue[0], pbPrologue[1], pbPrologue[2], pbPrologue[3],
2135 pbPrologue[4], pbPrologue[5], pbPrologue[6], pbPrologue[7],
2136 pbPrologue[8], pbPrologue[9], pbPrologue[10], pbPrologue[11],
2137 pbPrologue[12], pbPrologue[13], pbPrologue[14], pbPrologue[15]);
2138}
2139
2140
2141/**
2142 * IPRT thread that waits for the parent process to terminate and reacts by
2143 * exiting the current process.
2144 *
2145 * @returns VINF_SUCCESS
2146 * @param hSelf The current thread. Ignored.
2147 * @param pvUser The handle of the parent process.
2148 */
2149static DECLCALLBACK(int) supR3HardenedWinParentWatcherThread(RTTHREAD hSelf, void *pvUser)
2150{
2151 HANDLE hProcWait = (HANDLE)pvUser;
2152 NOREF(hSelf);
2153
2154 /*
2155 * Wait for the parent to terminate.
2156 */
2157 NTSTATUS rcNt;
2158 for (;;)
2159 {
2160 rcNt = NtWaitForSingleObject(hProcWait, TRUE /*Alertable*/, NULL /*pTimeout*/);
2161 if ( rcNt == STATUS_WAIT_0
2162 || rcNt == STATUS_ABANDONED_WAIT_0)
2163 break;
2164 if ( rcNt != STATUS_TIMEOUT
2165 && rcNt != STATUS_USER_APC
2166 && rcNt != STATUS_ALERTED)
2167 supR3HardenedFatal("NtWaitForSingleObject returned %#x\n", rcNt);
2168 }
2169
2170 /*
2171 * Proxy the termination code of the child, if it exited already.
2172 */
2173 PROCESS_BASIC_INFORMATION BasicInfo;
2174 NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
2175 if ( !NT_SUCCESS(rcNt2)
2176 || BasicInfo.ExitStatus == STATUS_PENDING)
2177 BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
2178
2179 NtClose(hProcWait);
2180 SUP_DPRINTF(("supR3HardenedWinParentWatcherThread: Quitting: ExitCode=%#x rcNt=%#x\n", BasicInfo.ExitStatus, rcNt));
2181 suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
2182
2183 return VINF_SUCCESS; /* won't be reached. */
2184}
2185
2186
2187/**
2188 * Creates the parent watcher thread that will make sure this process exits when
2189 * the parent does.
2190 *
2191 * This is a necessary evil to make VBoxNetDhcp and VBoxNetNat termination from
2192 * Main work without too much new magic. It also makes Ctrl-C or similar work
2193 * in on the hardened processes in the windows console.
2194 *
2195 * @param hVBoxRT The VBoxRT.dll handle. We use RTThreadCreate to
2196 * spawn the thread to avoid duplicating thread
2197 * creation and thread naming code from IPRT.
2198 */
2199DECLHIDDEN(void) supR3HardenedWinCreateParentWatcherThread(HMODULE hVBoxRT)
2200{
2201 /*
2202 * Resolve runtime methods that we need.
2203 */
2204 PFNRTTHREADCREATE pfnRTThreadCreate = (PFNRTTHREADCREATE)GetProcAddress(hVBoxRT, "RTThreadCreate");
2205 SUPR3HARDENED_ASSERT(pfnRTThreadCreate != NULL);
2206
2207 /*
2208 * Find the parent process ID.
2209 */
2210 PROCESS_BASIC_INFORMATION BasicInfo;
2211 NTSTATUS rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
2212 if (!NT_SUCCESS(rcNt))
2213 supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: NtQueryInformationProcess failed: %#x\n", rcNt);
2214
2215 /*
2216 * Open the parent process for waiting and exitcode query.
2217 */
2218 OBJECT_ATTRIBUTES ObjAttr;
2219 InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2220
2221 CLIENT_ID ClientId;
2222 ClientId.UniqueProcess = (HANDLE)BasicInfo.InheritedFromUniqueProcessId;
2223 ClientId.UniqueThread = NULL;
2224
2225 HANDLE hParent;
2226 rcNt = NtOpenProcess(&hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
2227 if (!NT_SUCCESS(rcNt))
2228 supR3HardenedFatalMsg("supR3HardenedWinCreateParentWatcherThread", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2229 "NtOpenProcess(%p.0) failed: %#x\n", ClientId.UniqueProcess, rcNt);
2230
2231 /*
2232 * Create the thread that should do the waiting.
2233 */
2234 int rc = pfnRTThreadCreate(NULL, supR3HardenedWinParentWatcherThread, hParent, _64K /* stack */,
2235 RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ParentWatcher");
2236 if (RT_FAILURE(rc))
2237 supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: RTThreadCreate failed: %Rrc\n", rc);
2238}
2239
2240
2241/**
2242 * Checks if the calling thread is the only one in the process.
2243 *
2244 * @returns true if we're positive we're alone, false if not.
2245 */
2246static bool supR3HardenedWinAmIAlone(void)
2247{
2248 ULONG fAmIAlone = 0;
2249 ULONG cbIgn = 0;
2250 NTSTATUS rcNt = NtQueryInformationThread(NtCurrentThread(), ThreadAmILastThread, &fAmIAlone, sizeof(fAmIAlone), &cbIgn);
2251 Assert(NT_SUCCESS(rcNt));
2252 return NT_SUCCESS(rcNt) && fAmIAlone != 0;
2253}
2254
2255
2256/**
2257 * Simplify NtProtectVirtualMemory interface.
2258 *
2259 * Modifies protection for the current process. Caller must know the current
2260 * protection as it's not returned.
2261 *
2262 * @returns NT status code.
2263 * @param pvMem The memory to change protection for.
2264 * @param cbMem The amount of memory to change.
2265 * @param fNewProt The new protection.
2266 */
2267static NTSTATUS supR3HardenedWinProtectMemory(PVOID pvMem, SIZE_T cbMem, ULONG fNewProt)
2268{
2269 ULONG fOldProt = 0;
2270 return NtProtectVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, fNewProt, &fOldProt);
2271}
2272
2273
2274/**
2275 * Installs or reinstalls the NTDLL patches.
2276 */
2277static void supR3HardenedWinReInstallHooks(bool fFirstCall)
2278{
2279 struct
2280 {
2281 size_t cbPatch;
2282 uint8_t const *pabPatch;
2283 uint8_t **ppbApi;
2284 const char *pszName;
2285 } const s_aPatches[] =
2286 {
2287 { sizeof(g_abNtCreateSectionPatch), g_abNtCreateSectionPatch, &g_pbNtCreateSection, "NtCreateSection" },
2288 { sizeof(g_abLdrLoadDllPatch), g_abLdrLoadDllPatch, &g_pbLdrLoadDll, "LdrLoadDll" },
2289 };
2290
2291 ULONG fAmIAlone = ~(ULONG)0;
2292
2293 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPatches); i++)
2294 {
2295 uint8_t *pbApi = *s_aPatches[i].ppbApi;
2296 if (memcmp(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch) != 0)
2297 {
2298 /*
2299 * Log the incident if it's not the initial call.
2300 */
2301 static uint32_t volatile s_cTimes = 0;
2302 if (!fFirstCall && s_cTimes < 128)
2303 {
2304 s_cTimes++;
2305 SUP_DPRINTF(("supR3HardenedWinReInstallHooks: Reinstalling %s (%p: %.*Rhxs).\n",
2306 s_aPatches[i].pszName, pbApi, s_aPatches[i].cbPatch, pbApi));
2307 }
2308
2309 Assert(s_aPatches[i].cbPatch >= 4);
2310
2311 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READWRITE));
2312
2313 /*
2314 * If we're alone, just memcpy the patch in.
2315 */
2316
2317 if (fAmIAlone == ~(ULONG)0)
2318 fAmIAlone = supR3HardenedWinAmIAlone();
2319 if (fAmIAlone)
2320 memcpy(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch);
2321 else
2322 {
2323 /*
2324 * Not alone. Start by injecting a JMP $-2, then waste some
2325 * CPU cycles to get the other threads a good chance of getting
2326 * out of the code before we replace it.
2327 */
2328 RTUINT32U uJmpDollarMinus;
2329 uJmpDollarMinus.au8[0] = 0xeb;
2330 uJmpDollarMinus.au8[1] = 0xfe;
2331 uJmpDollarMinus.au8[2] = pbApi[2];
2332 uJmpDollarMinus.au8[3] = pbApi[3];
2333 ASMAtomicXchgU32((uint32_t volatile *)pbApi, uJmpDollarMinus.u);
2334
2335 NtYieldExecution();
2336 NtYieldExecution();
2337
2338 /* Copy in the tail bytes of the patch, then xchg the jmp $-2. */
2339 if (s_aPatches[i].cbPatch > 4)
2340 memcpy(&pbApi[4], &s_aPatches[i].pabPatch[4], s_aPatches[i].cbPatch - 4);
2341 ASMAtomicXchgU32((uint32_t volatile *)pbApi, *(uint32_t *)s_aPatches[i].pabPatch);
2342 }
2343
2344 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READ));
2345 }
2346 }
2347}
2348
2349
2350/**
2351 * Install hooks for intercepting calls dealing with mapping shared libraries
2352 * into the process.
2353 *
2354 * This allows us to prevent undesirable shared libraries from being loaded.
2355 *
2356 * @remarks We assume we're alone in this process, so no seralizing trickery is
2357 * necessary when installing the patch.
2358 *
2359 * @remarks We would normally just copy the prologue sequence somewhere and add
2360 * a jump back at the end of it. But because we wish to avoid
2361 * allocating executable memory, we need to have preprepared assembly
2362 * "copies". This makes the non-system call patching a little tedious
2363 * and inflexible.
2364 */
2365static void supR3HardenedWinInstallHooks(void)
2366{
2367 NTSTATUS rcNt;
2368
2369 /*
2370 * Disable hard error popups so we can quietly refuse images to be loaded.
2371 */
2372 ULONG fHardErr = 0;
2373 rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr), NULL);
2374 if (!NT_SUCCESS(rcNt))
2375 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2376 "NtQueryInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
2377 if (fHardErr & PROCESS_HARDERR_CRITICAL_ERROR)
2378 {
2379 fHardErr &= ~PROCESS_HARDERR_CRITICAL_ERROR;
2380 rcNt = NtSetInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr));
2381 if (!NT_SUCCESS(rcNt))
2382 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2383 "NtSetInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
2384 }
2385
2386 /*
2387 * Locate the routines first so we can allocate memory that's near enough.
2388 */
2389 PFNRT pfnNtCreateSection = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtCreateSection");
2390 SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
2391 //SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
2392
2393 PFNRT pfnLdrLoadDll = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "LdrLoadDll");
2394 SUPR3HARDENED_ASSERT(pfnLdrLoadDll != NULL);
2395 //SUPR3HARDENED_ASSERT(pfnLdrLoadDll == (FARPROC)LdrLoadDll);
2396
2397 /*
2398 * Exec page setup & management.
2399 */
2400 uint32_t offExecPage = 0;
2401 memset(g_abSupHardReadWriteExecPage, 0xcc, PAGE_SIZE);
2402
2403 /*
2404 * Hook #1 - NtCreateSection.
2405 * Purpose: Validate everything that can be mapped into the process before
2406 * it's mapped and we still have a file handle to work with.
2407 */
2408 uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
2409 g_pbNtCreateSection = pbNtCreateSection;
2410 memcpy(g_abNtCreateSectionPatch, pbNtCreateSection, sizeof(g_abNtCreateSectionPatch));
2411
2412 g_pfnNtCreateSectionReal = NtCreateSection; /* our direct syscall */
2413
2414#ifdef RT_ARCH_AMD64
2415 /*
2416 * Patch 64-bit hosts.
2417 */
2418 /* Pattern #1: XP64/W2K3-64 thru Windows 8.1
2419 0:000> u ntdll!NtCreateSection
2420 ntdll!NtCreateSection:
2421 00000000`779f1750 4c8bd1 mov r10,rcx
2422 00000000`779f1753 b847000000 mov eax,47h
2423 00000000`779f1758 0f05 syscall
2424 00000000`779f175a c3 ret
2425 00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
2426 The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
2427
2428 /* Assemble the patch. */
2429 g_abNtCreateSectionPatch[0] = 0x48; /* mov rax, qword */
2430 g_abNtCreateSectionPatch[1] = 0xb8;
2431 *(uint64_t *)&g_abNtCreateSectionPatch[2] = (uint64_t)supR3HardenedMonitor_NtCreateSection;
2432 g_abNtCreateSectionPatch[10] = 0xff; /* jmp rax */
2433 g_abNtCreateSectionPatch[11] = 0xe0;
2434
2435#else
2436 /*
2437 * Patch 32-bit hosts.
2438 */
2439 /* Pattern #1: XP thru Windows 7
2440 kd> u ntdll!NtCreateSection
2441 ntdll!NtCreateSection:
2442 7c90d160 b832000000 mov eax,32h
2443 7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
2444 7c90d16a ff12 call dword ptr [edx]
2445 7c90d16c c21c00 ret 1Ch
2446 7c90d16f 90 nop
2447 The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
2448
2449 Pattern #2: Windows 8.1
2450 0:000:x86> u ntdll_6a0f0000!NtCreateSection
2451 ntdll_6a0f0000!NtCreateSection:
2452 6a15eabc b854010000 mov eax,154h
2453 6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
2454 6a15eac6 c21c00 ret 1Ch
2455 6a15eac9 8bd4 mov edx,esp
2456 6a15eacb 0f34 sysenter
2457 6a15eacd c3 ret
2458 The variable bit is the value loaded into eax: W81=154h */
2459
2460 /* Assemble the patch. */
2461 g_abNtCreateSectionPatch[0] = 0xe9; /* jmp rel32 */
2462 *(uint32_t *)&g_abNtCreateSectionPatch[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
2463 - (uintptr_t)&pbNtCreateSection[1+4];
2464
2465#endif
2466
2467 /*
2468 * Hook #2 - LdrLoadDll
2469 * Purpose: (a) Enforce LdrLoadDll search path constraints, and (b) pre-validate
2470 * DLLs so we can avoid calling WinVerifyTrust from the first hook,
2471 * and thus avoiding messing up the loader data on some installations.
2472 *
2473 * This differs from the above function in that is no a system call and
2474 * we're at the mercy of the compiler.
2475 */
2476 uint8_t * const pbLdrLoadDll = (uint8_t *)(uintptr_t)pfnLdrLoadDll;
2477 g_pbLdrLoadDll = pbLdrLoadDll;
2478 memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
2479
2480 DISSTATE Dis;
2481 uint32_t cbInstr;
2482 uint32_t offJmpBack = 0;
2483
2484#ifdef RT_ARCH_AMD64
2485 /*
2486 * Patch 64-bit hosts.
2487 */
2488 /* Just use the disassembler to skip 12 bytes or more. */
2489 while (offJmpBack < 12)
2490 {
2491 cbInstr = 1;
2492 int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
2493 if ( RT_FAILURE(rc)
2494 || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))
2495 || (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */) )
2496 supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
2497 offJmpBack += cbInstr;
2498 }
2499
2500 /* Assemble the code for resuming the call.*/
2501 *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
2502
2503 memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
2504 offExecPage += offJmpBack;
2505
2506 g_abSupHardReadWriteExecPage[offExecPage++] = 0xff; /* jmp qword [$+8 wrt RIP] */
2507 g_abSupHardReadWriteExecPage[offExecPage++] = 0x25;
2508 *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = RT_ALIGN_32(offExecPage + 4, 8) - (offExecPage + 4);
2509 offExecPage = RT_ALIGN_32(offExecPage + 4, 8);
2510 *(uint64_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack];
2511 offExecPage = RT_ALIGN_32(offJmpBack + 8, 16);
2512
2513 /* Assemble the LdrLoadDll patch. */
2514 Assert(offJmpBack >= 12);
2515 g_abLdrLoadDllPatch[0] = 0x48; /* mov rax, qword */
2516 g_abLdrLoadDllPatch[1] = 0xb8;
2517 *(uint64_t *)&g_abLdrLoadDllPatch[2] = (uint64_t)supR3HardenedMonitor_LdrLoadDll;
2518 g_abLdrLoadDllPatch[10] = 0xff; /* jmp rax */
2519 g_abLdrLoadDllPatch[11] = 0xe0;
2520
2521#else
2522 /*
2523 * Patch 32-bit hosts.
2524 */
2525 /* Just use the disassembler to skip 5 bytes or more. */
2526 while (offJmpBack < 5)
2527 {
2528 cbInstr = 1;
2529 int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
2530 if ( RT_FAILURE(rc)
2531 || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
2532 supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
2533 offJmpBack += cbInstr;
2534 }
2535
2536 /* Assemble the code for resuming the call.*/
2537 *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
2538
2539 memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
2540 offExecPage += offJmpBack;
2541
2542 g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
2543 *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack]
2544 - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
2545 offExecPage = RT_ALIGN_32(offJmpBack + 4, 16);
2546
2547 /* Assemble the LdrLoadDll patch. */
2548 memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
2549 Assert(offJmpBack >= 5);
2550 g_abLdrLoadDllPatch[0] = 0xe9;
2551 *(uint32_t *)&g_abLdrLoadDllPatch[1] = (uintptr_t)supR3HardenedMonitor_LdrLoadDll - (uintptr_t)&pbLdrLoadDll[1+4];
2552#endif
2553
2554 /*
2555 * Seal the rwx page.
2556 */
2557 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(g_abSupHardReadWriteExecPage, PAGE_SIZE, PAGE_EXECUTE_READ));
2558
2559 /*
2560 * Install the patches.
2561 */
2562 supR3HardenedWinReInstallHooks(true /*fFirstCall*/);
2563}
2564
2565
2566
2567
2568
2569
2570/*
2571 *
2572 * T h r e a d c r e a t i o n c o n t r o l
2573 * T h r e a d c r e a t i o n c o n t r o l
2574 * T h r e a d c r e a t i o n c o n t r o l
2575 *
2576 */
2577
2578
2579/**
2580 * Common code used for child and parent to make new threads exit immediately.
2581 *
2582 * This patches the LdrInitializeThunk code to call NtTerminateThread with
2583 * STATUS_SUCCESS instead of doing the NTDLL initialization.
2584 *
2585 * @returns VBox status code.
2586 * @param hProcess The process to do this to.
2587 * @param pvLdrInitThunk The address of the LdrInitializeThunk code to
2588 * override.
2589 * @param pvNtTerminateThread The address of the NtTerminateThread function in
2590 * the NTDLL instance we're patching. (Must be +/-
2591 * 2GB from the thunk code.)
2592 * @param pabBackup Where to back up the original instruction bytes
2593 * at pvLdrInitThunk.
2594 * @param cbBackup The size of the backup area. Must be 16 bytes.
2595 * @param pErrInfo Where to return extended error information.
2596 * Optional.
2597 */
2598static int supR3HardNtDisableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, void *pvNtTerminateThread,
2599 uint8_t *pabBackup, size_t cbBackup, PRTERRINFO pErrInfo)
2600{
2601 SUP_DPRINTF(("supR3HardNtDisableThreadCreation: pvLdrInitThunk=%p pvNtTerminateThread=%p\n", pvLdrInitThunk, pvNtTerminateThread));
2602 SUPR3HARDENED_ASSERT(cbBackup == 16);
2603 SUPR3HARDENED_ASSERT(RT_ABS((intptr_t)pvLdrInitThunk - (intptr_t)pvNtTerminateThread) < 16*_1M);
2604
2605 /*
2606 * Back up the thunk code.
2607 */
2608 SIZE_T cbIgnored;
2609 NTSTATUS rcNt = NtReadVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
2610 if (!NT_SUCCESS(rcNt))
2611 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2612 "supR3HardNtDisableThreadCreation: NtReadVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2613
2614 /*
2615 * Cook up replacement code that calls NtTerminateThread.
2616 */
2617 uint8_t abReplacement[16];
2618 memcpy(abReplacement, pabBackup, sizeof(abReplacement));
2619
2620#ifdef RT_ARCH_AMD64
2621 abReplacement[0] = 0x31; /* xor ecx, ecx */
2622 abReplacement[1] = 0xc9;
2623 abReplacement[2] = 0x31; /* xor edx, edx */
2624 abReplacement[3] = 0xd2;
2625 abReplacement[4] = 0xe8; /* call near NtTerminateThread */
2626 *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
2627 abReplacement[9] = 0xcc; /* int3 */
2628#elif defined(RT_ARCH_X86)
2629 abReplacement[0] = 0x6a; /* push 0 */
2630 abReplacement[1] = 0x00;
2631 abReplacement[2] = 0x6a; /* push 0 */
2632 abReplacement[3] = 0x00;
2633 abReplacement[4] = 0xe8; /* call near NtTerminateThread */
2634 *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
2635 abReplacement[9] = 0xcc; /* int3 */
2636#else
2637# error "Unsupported arch."
2638#endif
2639
2640 /*
2641 * Install the replacment code.
2642 */
2643 PVOID pvProt = pvLdrInitThunk;
2644 SIZE_T cbProt = cbBackup;
2645 ULONG fOldProt = 0;
2646 rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
2647 if (!NT_SUCCESS(rcNt))
2648 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2649 "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2650
2651 rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abReplacement, sizeof(abReplacement), &cbIgnored);
2652 if (!NT_SUCCESS(rcNt))
2653 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2654 "supR3HardNtDisableThreadCreationEx: NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2655
2656 pvProt = pvLdrInitThunk;
2657 cbProt = cbBackup;
2658 rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
2659 if (!NT_SUCCESS(rcNt))
2660 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2661 "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk/2 failed: %#x", rcNt);
2662
2663 return VINF_SUCCESS;
2664}
2665
2666
2667/**
2668 * Undo the effects of supR3HardNtDisableThreadCreationEx.
2669 *
2670 * @returns VBox status code.
2671 * @param hProcess The process to do this to.
2672 * @param pvLdrInitThunk The address of the LdrInitializeThunk code to
2673 * override.
2674 * @param pabBackup Where to back up the original instruction bytes
2675 * at pvLdrInitThunk.
2676 * @param cbBackup The size of the backup area. Must be 16 bytes.
2677 * @param pErrInfo Where to return extended error information.
2678 * Optional.
2679 */
2680static int supR3HardNtEnableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, uint8_t const *pabBackup, size_t cbBackup,
2681 PRTERRINFO pErrInfo)
2682{
2683 SUP_DPRINTF(("supR3HardNtEnableThreadCreation:\n"));
2684 SUPR3HARDENED_ASSERT(cbBackup == 16);
2685
2686 PVOID pvProt = pvLdrInitThunk;
2687 SIZE_T cbProt = cbBackup;
2688 ULONG fOldProt = 0;
2689 NTSTATUS rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
2690 if (!NT_SUCCESS(rcNt))
2691 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2692 "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2693
2694 SIZE_T cbIgnored;
2695 rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
2696 if (!NT_SUCCESS(rcNt))
2697 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2698 "supR3HardNtEnableThreadCreation: NtWriteVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
2699 rcNt);
2700
2701 pvProt = pvLdrInitThunk;
2702 cbProt = cbBackup;
2703 rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
2704 if (!NT_SUCCESS(rcNt))
2705 return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2706 "supR3HardNtEnableThreadCreation: NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
2707 rcNt);
2708
2709 return VINF_SUCCESS;
2710}
2711
2712
2713/**
2714 * Disable thread creation for the current process.
2715 *
2716 * @remarks Doesn't really disables it, just makes the threads exit immediately
2717 * without executing any real code.
2718 */
2719static void supR3HardenedWinDisableThreadCreation(void)
2720{
2721 /* Cannot use the imported NtTerminateThread as it's pointing to our own
2722 syscall assembly code. */
2723 static PFNRT s_pfnNtTerminateThread = NULL;
2724 if (s_pfnNtTerminateThread == NULL)
2725 s_pfnNtTerminateThread = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtTerminateThread");
2726 SUPR3HARDENED_ASSERT(s_pfnNtTerminateThread);
2727
2728 int rc = supR3HardNtDisableThreadCreationEx(NtCurrentProcess(),
2729 (void *)(uintptr_t)&LdrInitializeThunk,
2730 (void *)(uintptr_t)s_pfnNtTerminateThread,
2731 g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
2732 NULL /* pErrInfo*/);
2733 g_fSupInitThunkSelfPatched = RT_SUCCESS(rc);
2734}
2735
2736
2737/**
2738 * Undoes the effects of supR3HardenedWinDisableThreadCreation.
2739 */
2740DECLHIDDEN(void) supR3HardenedWinEnableThreadCreation(void)
2741{
2742 if (g_fSupInitThunkSelfPatched)
2743 {
2744 int rc = supR3HardNtEnableThreadCreationEx(NtCurrentProcess(),
2745 (void *)(uintptr_t)&LdrInitializeThunk,
2746 g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
2747 RTErrInfoInitStatic(&g_ErrInfoStatic));
2748 if (RT_FAILURE(rc))
2749 supR3HardenedError(rc, true /*fFatal*/, "%s", g_ErrInfoStatic.szMsg);
2750 g_fSupInitThunkSelfPatched = false;
2751 }
2752}
2753
2754
2755
2756
2757/*
2758 *
2759 * R e s p a w n
2760 * R e s p a w n
2761 * R e s p a w n
2762 *
2763 */
2764
2765
2766/**
2767 * Gets the SID of the user associated with the process.
2768 *
2769 * @returns @c true if we've got a login SID, @c false if not.
2770 * @param pSidUser Where to return the user SID.
2771 * @param cbSidUser The size of the user SID buffer.
2772 * @param pSidLogin Where to return the login SID.
2773 * @param cbSidLogin The size of the login SID buffer.
2774 */
2775static bool supR3HardNtChildGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
2776{
2777 HANDLE hToken;
2778 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
2779 union
2780 {
2781 TOKEN_USER UserInfo;
2782 TOKEN_GROUPS Groups;
2783 uint8_t abPadding[4096];
2784 } uBuf;
2785 ULONG cbRet = 0;
2786 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
2787 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
2788
2789 bool fLoginSid = false;
2790 NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
2791 if (NT_SUCCESS(rcNt))
2792 {
2793 for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
2794 if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
2795 {
2796 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
2797 fLoginSid = true;
2798 break;
2799 }
2800 }
2801
2802 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
2803
2804 return fLoginSid;
2805}
2806
2807
2808/**
2809 * Build security attributes for the process or the primary thread (@a fProcess)
2810 *
2811 * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
2812 * to admins, i.e. normal windows users), or by taking ownership and/or
2813 * modifying the DACL. However, it restricts
2814 *
2815 * @param pSecAttrs Where to return the security attributes.
2816 * @param pCleanup Cleanup record.
2817 * @param fProcess Set if it's for the process, clear if it's for
2818 * the primary thread.
2819 */
2820static void supR3HardNtChildInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
2821{
2822 /*
2823 * Safe return values.
2824 */
2825 suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
2826
2827 pSecAttrs->nLength = sizeof(*pSecAttrs);
2828 pSecAttrs->bInheritHandle = FALSE;
2829 pSecAttrs->lpSecurityDescriptor = NULL;
2830
2831/** @todo This isn't at all complete, just sketches... */
2832
2833 /*
2834 * Create an ACL detailing the access of the above groups.
2835 */
2836 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
2837
2838 ULONG fDeny = DELETE | WRITE_DAC | WRITE_OWNER;
2839 ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
2840 ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
2841 if (fProcess)
2842 {
2843 fDeny |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
2844 | PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
2845 | PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
2846 fAllow |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
2847 fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
2848 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
2849 {
2850 fAllow |= PROCESS_QUERY_LIMITED_INFORMATION;
2851 fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
2852 }
2853 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
2854 fAllow |= PROCESS_SET_LIMITED_INFORMATION;
2855 }
2856 else
2857 {
2858 fDeny |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
2859 | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
2860 fAllow |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
2861 fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
2862 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
2863 {
2864 fAllow |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
2865 fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
2866 }
2867
2868 }
2869 fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
2870
2871 /* Deny everyone access to bad bits. */
2872#if 1
2873 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
2874 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
2875 *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
2876 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2877 fDeny, &pCleanup->Everyone.Sid));
2878#endif
2879
2880#if 0
2881 /* Grant some access to the owner - doesn't work. */
2882 SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
2883 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
2884 *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
2885
2886 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2887 fDeny, &pCleanup->Owner.Sid));
2888 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2889 fAllow, &pCleanup->Owner.Sid));
2890#endif
2891
2892#if 1
2893 bool fHasLoginSid = supR3HardNtChildGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
2894 &pCleanup->Login.Sid, sizeof(pCleanup->Login));
2895
2896# if 1
2897 /* Grant minimal access to the user. */
2898 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2899 fDeny, &pCleanup->User.Sid));
2900 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2901 fAllow, &pCleanup->User.Sid));
2902# endif
2903
2904# if 1
2905 /* Grant very limited access to the login sid. */
2906 if (fHasLoginSid)
2907 {
2908 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
2909 fAllowLogin, &pCleanup->Login.Sid));
2910 }
2911# endif
2912
2913#endif
2914
2915 /*
2916 * Create a security descriptor with the above ACL.
2917 */
2918 PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
2919 pCleanup->pSecDesc = pSecDesc;
2920
2921 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
2922 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
2923 FALSE /*fDaclDefaulted*/));
2924 pSecAttrs->lpSecurityDescriptor = pSecDesc;
2925}
2926
2927
2928/**
2929 * Predicate function which tests whether @a ch is a argument separator
2930 * character.
2931 *
2932 * @returns True/false.
2933 * @param ch The character to examine.
2934 */
2935DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
2936{
2937 return ch == ' '
2938 || ch == '\t'
2939 || ch == '\n'
2940 || ch == '\r';
2941}
2942
2943
2944/**
2945 * Construct the new command line.
2946 *
2947 * Since argc/argv are both derived from GetCommandLineW (see
2948 * suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
2949 * conversion and quoting by going to the original source.
2950 *
2951 * The executable name, though, is replaced in case it's not a fullly
2952 * qualified path.
2953 *
2954 * The re-spawn indicator is added immediately after the executable name
2955 * so that we don't get tripped up missing close quote chars in the last
2956 * argument.
2957 *
2958 * @returns Pointer to a command line string (heap).
2959 * @param pUniStr Unicode string structure to initialize to the
2960 * command line. Optional.
2961 * @param iWhich Which respawn we're to check for, 1 being the first
2962 * one, and 2 the second and final.
2963 */
2964static PRTUTF16 supR3HardNtChildConstructCmdLine(PUNICODE_STRING pString, int iWhich)
2965{
2966 SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
2967
2968 /*
2969 * Get the command line and skip the executable name.
2970 */
2971 PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
2972 PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
2973 uint32_t cwcArgs = pCmdLineStr->Length / sizeof(WCHAR);
2974
2975 /* Skip leading space (shouldn't be any, but whatever). */
2976 while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
2977 cwcArgs--, pawcArgs++;
2978 SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
2979
2980 /* Walk to the end of it. */
2981 int fQuoted = false;
2982 do
2983 {
2984 if (*pawcArgs == '"')
2985 {
2986 fQuoted = !fQuoted;
2987 cwcArgs--; pawcArgs++;
2988 }
2989 else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
2990 cwcArgs--, pawcArgs++;
2991 else
2992 {
2993 unsigned cSlashes = 0;
2994 do
2995 {
2996 cSlashes++;
2997 cwcArgs--;
2998 pawcArgs++;
2999 }
3000 while (cwcArgs > 0 && *pawcArgs == '\\');
3001 if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
3002 cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
3003 }
3004 } while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
3005
3006 /* Skip trailing spaces. */
3007 while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
3008 cwcArgs--, pawcArgs++;
3009
3010 /*
3011 * Allocate a new buffer.
3012 */
3013 AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
3014 size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
3015 + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
3016 if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
3017 supR3HardenedFatalMsg("supR3HardNtChildConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
3018 "Command line is too long (%u chars)!", cwcCmdLine);
3019
3020 PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
3021 SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
3022
3023 /*
3024 * Construct the new command line.
3025 */
3026 PRTUTF16 pwszDst = pwszCmdLine;
3027 for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
3028 *pwszDst++ = *pszSrc;
3029
3030 if (cwcArgs)
3031 {
3032 *pwszDst++ = ' ';
3033 suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
3034 pwszDst += cwcArgs;
3035 }
3036
3037 *pwszDst = '\0';
3038 SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
3039
3040 if (pString)
3041 {
3042 pString->Buffer = pwszCmdLine;
3043 pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
3044 pString->MaximumLength = pString->Length + sizeof(WCHAR);
3045 }
3046 return pwszCmdLine;
3047}
3048
3049
3050/**
3051 * Terminates the child process.
3052 *
3053 * @param hProcess The process handle.
3054 * @param pszWhere Who's having child rasing troubles.
3055 * @param rc The status code to report.
3056 * @param pszFormat The message format string.
3057 * @param ... Message format arguments.
3058 */
3059static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
3060{
3061 /*
3062 * Terminate the process ASAP and display error.
3063 */
3064 NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
3065
3066 va_list va;
3067 va_start(va, pszFormat);
3068 supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
3069 va_end(va);
3070
3071 /*
3072 * Wait for the process to really go away.
3073 */
3074 PROCESS_BASIC_INFORMATION BasicInfo;
3075 NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3076 bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
3077 if (!fExitOk)
3078 {
3079 NTSTATUS rcNtWait;
3080 uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
3081 do
3082 {
3083 NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
3084
3085 LARGE_INTEGER Timeout;
3086 Timeout.QuadPart = -20000000; /* 2 second */
3087 rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
3088
3089 rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3090 fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
3091 } while ( !fExitOk
3092 && ( rcNtWait == STATUS_TIMEOUT
3093 || rcNtWait == STATUS_USER_APC
3094 || rcNtWait == STATUS_ALERTED)
3095 && supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
3096 if (fExitOk)
3097 supR3HardenedError(rc, false /*fFatal*/,
3098 "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
3099 rc, rc, rcNtWait, hProcess);
3100 }
3101
3102 /*
3103 * Final error message.
3104 */
3105 va_start(va, pszFormat);
3106 supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
3107 va_end(va);
3108}
3109
3110
3111/**
3112 * Checks the child process when hEvtParent is signalled.
3113 *
3114 * This will read the request data from the child and check it against expected
3115 * request. If an error is signalled, we'll raise it and make sure the child
3116 * terminates before terminating the calling process.
3117 *
3118 * @param pThis The child process data structure.
3119 * @param enmExpectedRequest The expected child request.
3120 * @param pszWhat What we're waiting for.
3121 */
3122static void supR3HardNtChildProcessRequest(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, const char *pszWhat)
3123{
3124 /*
3125 * Read the process parameters from the child.
3126 */
3127 uintptr_t uChildAddr = (uintptr_t)pThis->Peb.ImageBaseAddress
3128 + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3129 SIZE_T cbIgnored = 0;
3130 RT_ZERO(pThis->ProcParams);
3131 NTSTATUS rcNt = NtReadVirtualMemory(pThis->hProcess, (PVOID)uChildAddr,
3132 &pThis->ProcParams, sizeof(pThis->ProcParams), &cbIgnored);
3133 if (!NT_SUCCESS(rcNt))
3134 supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt,
3135 "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
3136
3137 /*
3138 * Is it the expected request?
3139 */
3140 if (pThis->ProcParams.enmRequest == enmExpectedRequest)
3141 return;
3142
3143 /*
3144 * No, not the expected request. If it's an error request, tell the child
3145 * to terminate itself, otherwise we'll have to terminate it.
3146 */
3147 pThis->ProcParams.szErrorMsg[sizeof(pThis->ProcParams.szErrorMsg) - 1] = '\0';
3148 pThis->ProcParams.szWhere[sizeof(pThis->ProcParams.szWhere) - 1] = '\0';
3149 SUP_DPRINTF(("supR3HardenedWinCheckChild: enmRequest=%d rc=%d enmWhat=%d %s: %s\n",
3150 pThis->ProcParams.enmRequest, pThis->ProcParams.rc, pThis->ProcParams.enmWhat,
3151 pThis->ProcParams.szWhere, pThis->ProcParams.szErrorMsg));
3152
3153 if (pThis->ProcParams.enmRequest != kSupR3WinChildReq_Error)
3154 supR3HardenedWinKillChild(pThis, "supR3HardenedWinCheckChild", VERR_INVALID_PARAMETER,
3155 "Unexpected child request #%d. Was expecting #%d (%s).\n",
3156 pThis->ProcParams.enmRequest, enmExpectedRequest, pszWhat);
3157
3158 rcNt = NtSetEvent(pThis->hEvtChild, NULL);
3159 if (!NT_SUCCESS(rcNt))
3160 supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt, "NtSetEvent failed: %#x\n", rcNt);
3161
3162 /* Wait for it to terminate. */
3163 LARGE_INTEGER Timeout;
3164 Timeout.QuadPart = -50000000; /* 5 seconds */
3165 rcNt = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, &Timeout);
3166 if (rcNt != STATUS_WAIT_0)
3167 {
3168 SUP_DPRINTF(("supR3HardNtChildProcessRequest: Child is taking too long to quit (rcWait=%#x), killing it...\n", rcNt));
3169 NtTerminateProcess(pThis->hProcess, DBG_TERMINATE_PROCESS);
3170 }
3171
3172 /*
3173 * Report the error in the same way as it occured in the guest.
3174 */
3175 if (pThis->ProcParams.enmWhat == kSupInitOp_Invalid)
3176 supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, pThis->ProcParams.rc,
3177 "%s", pThis->ProcParams.szErrorMsg);
3178 else
3179 supR3HardenedFatalMsg(pThis->ProcParams.szWhere, pThis->ProcParams.enmWhat, pThis->ProcParams.rc,
3180 "%s", pThis->ProcParams.szErrorMsg);
3181}
3182
3183
3184/**
3185 * Waits for the child to make a certain request or terminate.
3186 *
3187 * The stub process will also wait on it's parent to terminate.
3188 * This call will only return if the child made the expected request.
3189 *
3190 * @param pThis The child process data structure.
3191 * @param enmExpectedRequest The child request to wait for.
3192 * @param cMsTimeout The number of milliseconds to wait (at least).
3193 * @param pszWhat What we're waiting for.
3194 */
3195static void supR3HardNtChildWaitFor(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, RTMSINTERVAL cMsTimeout,
3196 const char *pszWhat)
3197{
3198 /*
3199 * The wait loop.
3200 * Will return when the expected request arrives.
3201 * Will break out when one of the processes terminates.
3202 */
3203 NTSTATUS rcNtWait;
3204 LARGE_INTEGER Timeout;
3205 uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
3206 uint64_t cMsElapsed = 0;
3207 for (;;)
3208 {
3209 /*
3210 * Assemble handles to wait for.
3211 */
3212 ULONG cHandles = 1;
3213 HANDLE ahHandles[3];
3214 ahHandles[0] = pThis->hProcess;
3215 if (pThis->hEvtParent)
3216 ahHandles[cHandles++] = pThis->hEvtParent;
3217 if (pThis->hParent)
3218 ahHandles[cHandles++] = pThis->hParent;
3219
3220 /*
3221 * Do the waiting according to the callers wishes.
3222 */
3223 if ( enmExpectedRequest == kSupR3WinChildReq_End
3224 || cMsTimeout == RT_INDEFINITE_WAIT)
3225 rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*Timeout*/);
3226 else
3227 {
3228 Timeout.QuadPart = -(int64_t)(cMsTimeout - cMsElapsed) * 10000;
3229 rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, &Timeout);
3230 }
3231
3232 /*
3233 * Process child request.
3234 */
3235 if (rcNtWait == STATUS_WAIT_0 + 1 && pThis->hEvtParent != NULL)
3236 {
3237 supR3HardNtChildProcessRequest(pThis, enmExpectedRequest, pszWhat);
3238 SUP_DPRINTF(("supR3HardNtChildWaitFor: Found expected request %d (%s) after %llu ms.\n",
3239 enmExpectedRequest, pszWhat, supR3HardenedWinGetMilliTS() - uMsTsStart));
3240 return; /* Expected request received. */
3241 }
3242
3243 /*
3244 * Process termination?
3245 */
3246 if ( (ULONG)rcNtWait - (ULONG)STATUS_WAIT_0 < cHandles
3247 || (ULONG)rcNtWait - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
3248 break;
3249
3250 /*
3251 * Check sanity.
3252 */
3253 if ( rcNtWait != STATUS_TIMEOUT
3254 && rcNtWait != STATUS_USER_APC
3255 && rcNtWait != STATUS_ALERTED)
3256 supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
3257 "NtWaitForMultipleObjects returned %#x waiting for #%d (%s)\n",
3258 rcNtWait, enmExpectedRequest, pszWhat);
3259
3260 /*
3261 * Calc elapsed time for the next timeout calculation, checking to see
3262 * if we've timed out already.
3263 */
3264 cMsElapsed = supR3HardenedWinGetMilliTS() - uMsTsStart;
3265 if ( cMsElapsed > cMsTimeout
3266 && cMsTimeout != RT_INDEFINITE_WAIT
3267 && enmExpectedRequest != kSupR3WinChildReq_End)
3268 {
3269 if (rcNtWait == STATUS_USER_APC || rcNtWait == STATUS_ALERTED)
3270 cMsElapsed = cMsTimeout - 1; /* try again */
3271 else
3272 {
3273 /* We timed out. */
3274 supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
3275 "Timed out after %llu ms waiting for child request #%d (%s).\n",
3276 cMsElapsed, enmExpectedRequest, pszWhat);
3277 }
3278 }
3279 }
3280
3281 /*
3282 * Proxy the termination code of the child, if it exited already.
3283 */
3284 PROCESS_BASIC_INFORMATION BasicInfo;
3285 NTSTATUS rcNt1 = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3286 NTSTATUS rcNt2 = STATUS_PENDING;
3287 NTSTATUS rcNt3 = STATUS_PENDING;
3288 if ( !NT_SUCCESS(rcNt1)
3289 || BasicInfo.ExitStatus == STATUS_PENDING)
3290 {
3291 rcNt2 = NtTerminateProcess(pThis->hProcess, RTEXITCODE_FAILURE);
3292 Timeout.QuadPart = NT_SUCCESS(rcNt2) ? -20000000 /* 2 sec */ : -1280000 /* 128 ms */;
3293 rcNt3 = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, NULL /*Timeout*/);
3294 BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
3295 }
3296
3297 SUP_DPRINTF(("supR3HardNtChildWaitFor[%d]: Quitting: ExitCode=%#x (rcNtWait=%#x, rcNt1=%#x, rcNt2=%#x, rcNt3=%#x, %llu ms, %s);\n",
3298 pThis->iWhich, BasicInfo.ExitStatus, rcNtWait, rcNt1, rcNt2, rcNt3,
3299 supR3HardenedWinGetMilliTS() - uMsTsStart, pszWhat));
3300 suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
3301}
3302
3303
3304/**
3305 * Closes full access child thread and process handles, making a harmless
3306 * duplicate of the process handle first.
3307 *
3308 * The hProcess member of the child process data structure will be change to the
3309 * harmless handle, while the hThread will be set to NULL.
3310 *
3311 * @param pThis The child process data structure.
3312 */
3313static void supR3HardNtChildCloseFullAccessHandles(PSUPR3HARDNTCHILD pThis)
3314{
3315 /*
3316 * The thread handle.
3317 */
3318 NTSTATUS rcNt = NtClose(pThis->hThread);
3319 if (!NT_SUCCESS(rcNt))
3320 supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt, "NtClose(hThread) failed: %#x", rcNt);
3321 pThis->hThread = NULL;
3322
3323 /*
3324 * Duplicate the process handle into a harmless one.
3325 */
3326 HANDLE hProcWait;
3327 ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
3328 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
3329 fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
3330 else
3331 fRights |= PROCESS_QUERY_INFORMATION;
3332 rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
3333 NtCurrentProcess(), &hProcWait,
3334 fRights, 0 /*HandleAttributes*/, 0);
3335 if (rcNt == STATUS_ACCESS_DENIED)
3336 {
3337 supR3HardenedError(rcNt, false /*fFatal*/,
3338 "supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
3339 fRights, rcNt, SYNCHRONIZE);
3340 rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
3341 NtCurrentProcess(), &hProcWait,
3342 SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
3343 }
3344 if (!NT_SUCCESS(rcNt))
3345 supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt,
3346 "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
3347 /*
3348 * Close the process handle and replace it with the harmless one.
3349 */
3350 rcNt = NtClose(pThis->hProcess);
3351 pThis->hProcess = hProcWait;
3352 if (!NT_SUCCESS(rcNt))
3353 supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
3354 "NtClose failed on child process handle: %#x\n", rcNt);
3355}
3356
3357
3358/**
3359 * This restores the child PEB and tweaks a couple of fields before we do the
3360 * child purification and let the process run normally.
3361 *
3362 * @param pThis The child process data structure.
3363 */
3364static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis)
3365{
3366 /*
3367 * Make a copy of the pre-execution PEB.
3368 */
3369 PEB Peb = pThis->Peb;
3370
3371#if 0
3372 /*
3373 * There should not be any activation context, so if there is, we scratch the memory associated with it.
3374 */
3375 int rc = 0;
3376 if (RT_SUCCESS(rc) && Peb.pShimData && !((uintptr_t)Peb.pShimData & PAGE_OFFSET_MASK))
3377 rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.pShimData, PAGE_SIZE, "pShimData", pErrInfo);
3378 if (RT_SUCCESS(rc) && Peb.ActivationContextData && !((uintptr_t)Peb.ActivationContextData & PAGE_OFFSET_MASK))
3379 rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ActivationContextData, PAGE_SIZE, "ActivationContextData", pErrInfo);
3380 if (RT_SUCCESS(rc) && Peb.ProcessAssemblyStorageMap && !((uintptr_t)Peb.ProcessAssemblyStorageMap & PAGE_OFFSET_MASK))
3381 rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "ProcessAssemblyStorageMap", pErrInfo);
3382 if (RT_SUCCESS(rc) && Peb.SystemDefaultActivationContextData && !((uintptr_t)Peb.SystemDefaultActivationContextData & PAGE_OFFSET_MASK))
3383 rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "SystemDefaultActivationContextData", pErrInfo);
3384 if (RT_SUCCESS(rc) && Peb.SystemAssemblyStorageMap && !((uintptr_t)Peb.SystemAssemblyStorageMap & PAGE_OFFSET_MASK))
3385 rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.SystemAssemblyStorageMap, PAGE_SIZE, "SystemAssemblyStorageMap", pErrInfo);
3386 if (RT_FAILURE(rc))
3387 return rc;
3388#endif
3389
3390 /*
3391 * Clear compatibility and activation related fields.
3392 */
3393 Peb.AppCompatFlags.QuadPart = 0;
3394 Peb.AppCompatFlagsUser.QuadPart = 0;
3395 Peb.pShimData = NULL;
3396 Peb.AppCompatInfo = NULL;
3397#if 0
3398 Peb.ActivationContextData = NULL;
3399 Peb.ProcessAssemblyStorageMap = NULL;
3400 Peb.SystemDefaultActivationContextData = NULL;
3401 Peb.SystemAssemblyStorageMap = NULL;
3402 /*Peb.Diff0.W6.IsProtectedProcess = 1;*/
3403#endif
3404
3405 /*
3406 * Write back the PEB.
3407 */
3408 SIZE_T cbActualMem = pThis->cbPeb;
3409 NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
3410 if (!NT_SUCCESS(rcNt))
3411 supR3HardenedWinKillChild(pThis, "supR3HardNtChildSanitizePeb", rcNt,
3412 "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
3413
3414}
3415
3416
3417/**
3418 * Purifies the child process after very early init has been performed.
3419 *
3420 * @param pThis The child process data structure.
3421 */
3422static void supR3HardNtChildPurify(PSUPR3HARDNTCHILD pThis)
3423{
3424 /*
3425 * We loop until we no longer make any fixes. This is similar to what
3426 * we do (or used to do, really) in the fAvastKludge case of
3427 * supR3HardenedWinInit. We might be up against asynchronous changes,
3428 * which we fudge by waiting a short while before earch purification. This
3429 * is arguably a fragile technique, but it's currently the best we've got.
3430 * Fortunately, most AVs seems to either favor immediate action on initial
3431 * load events or (much better for us) later events like kernel32.
3432 */
3433 uint64_t uMsTsOuterStart = supR3HardenedWinGetMilliTS();
3434 uint32_t cMsFudge = g_fSupAdversaries ? 512 : 256;
3435 uint32_t cTotalFixes = 0;
3436 uint32_t cFixes;
3437 for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
3438 {
3439 /*
3440 * Delay.
3441 */
3442 uint32_t cSleeps = 0;
3443 uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
3444 do
3445 {
3446 NtYieldExecution();
3447 LARGE_INTEGER Time;
3448 Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
3449 NtDelayExecution(FALSE, &Time);
3450 cSleeps++;
3451 } while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
3452 || cSleeps < 8);
3453 SUP_DPRINTF(("supR3HardNtChildPurify: Startup delay kludge #1/%u: %u ms, %u sleeps\n",
3454 iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
3455
3456 /*
3457 * Purify.
3458 */
3459 cFixes = 0;
3460 int rc = supHardenedWinVerifyProcess(pThis->hProcess, pThis->hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION,
3461 g_fSupAdversaries & ( SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE
3462 | SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN)
3463 ? SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW : 0,
3464 &cFixes, RTErrInfoInitStatic(&g_ErrInfoStatic));
3465 if (RT_FAILURE(rc))
3466 supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", rc,
3467 "supHardenedWinVerifyProcess failed with %Rrc: %s", rc, g_ErrInfoStatic.szMsg);
3468 if (cFixes == 0)
3469 {
3470 SUP_DPRINTF(("supR3HardNtChildPurify: Done after %llu ms and %u fixes (loop #%u).\n",
3471 supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cTotalFixes, iLoop));
3472 return; /* We're probably good. */
3473 }
3474 cTotalFixes += cFixes;
3475
3476 if (!g_fSupAdversaries)
3477 g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
3478 cMsFudge = 512;
3479
3480 /*
3481 * Log the KiOpPrefetchPatchCount value if available, hoping it might
3482 * sched some light on spider38's case.
3483 */
3484 ULONG cPatchCount = 0;
3485 NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
3486 &cPatchCount, sizeof(cPatchCount), NULL);
3487 if (NT_SUCCESS(rcNt))
3488 SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
3489 cFixes, g_fSupAdversaries, cPatchCount));
3490 else
3491 SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
3492 }
3493
3494 /*
3495 * We've given up fixing the child process. Probably fighting someone
3496 * that monitors their patches or/and our activities.
3497 */
3498 supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", VERR_TRY_AGAIN,
3499 "Unable to purify child process! After 16 tries over %llu ms, we still %u fix(es) in the last pass.",
3500 supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cFixes);
3501}
3502
3503
3504
3505/**
3506 * Sets up the early process init.
3507 *
3508 * @param pThis The child process data structure.
3509 */
3510static void supR3HardNtChildSetUpChildInit(PSUPR3HARDNTCHILD pThis)
3511{
3512 uintptr_t const uChildExeAddr = (uintptr_t)pThis->Peb.ImageBaseAddress;
3513
3514 /*
3515 * Plant the process parameters. This ASSUMES the handle inheritance is
3516 * performed when creating the child process.
3517 */
3518 RT_ZERO(pThis->ProcParams);
3519 pThis->ProcParams.hEvtChild = pThis->hEvtChild;
3520 pThis->ProcParams.hEvtParent = pThis->hEvtParent;
3521 pThis->ProcParams.uNtDllAddr = pThis->uNtDllAddr;
3522 pThis->ProcParams.enmRequest = kSupR3WinChildReq_Error;
3523 pThis->ProcParams.rc = VINF_SUCCESS;
3524
3525 uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3526 SIZE_T cbIgnored;
3527 NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, (PVOID)uChildAddr, &pThis->ProcParams,
3528 sizeof(pThis->ProcParams), &cbIgnored);
3529 if (!NT_SUCCESS(rcNt))
3530 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3531 "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
3532
3533 /*
3534 * Locate the LdrInitializeThunk address in the child as well as pristine
3535 * code bits for it.
3536 */
3537 PSUPHNTLDRCACHEENTRY pLdrEntry;
3538 int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
3539 if (RT_FAILURE(rc))
3540 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3541 "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
3542
3543 uint8_t *pbChildNtDllBits;
3544 rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, pThis->uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
3545 if (RT_FAILURE(rc))
3546 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3547 "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
3548
3549 RTLDRADDR uLdrInitThunk;
3550 rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
3551 "LdrInitializeThunk", &uLdrInitThunk);
3552 if (RT_FAILURE(rc))
3553 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3554 "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
3555 PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
3556 SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
3557
3558 /*
3559 * Calculate the address of our code in the child process.
3560 */
3561 uintptr_t uEarlyProcInitEP = uChildExeAddr + ( (uintptr_t)&supR3HardenedEarlyProcessInitThunk
3562 - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3563
3564 /*
3565 * Compose the LdrInitializeThunk replacement bytes.
3566 * Note! The amount of code we replace here must be less or equal to what
3567 * the process verification code ignores.
3568 */
3569 uint8_t abNew[16];
3570 memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - pThis->uNtDllAddr), sizeof(abNew));
3571#ifdef RT_ARCH_AMD64
3572 abNew[0] = 0xff;
3573 abNew[1] = 0x25;
3574 *(uint32_t *)&abNew[2] = 0;
3575 *(uint64_t *)&abNew[6] = uEarlyProcInitEP;
3576#elif defined(RT_ARCH_X86)
3577 abNew[0] = 0xe9;
3578 *(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
3579#else
3580# error "Unsupported arch."
3581#endif
3582
3583 /*
3584 * Install the LdrInitializeThunk replacement code in the child process.
3585 */
3586 PVOID pvProt = pvLdrInitThunk;
3587 SIZE_T cbProt = sizeof(abNew);
3588 ULONG fOldProt;
3589 rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
3590 if (!NT_SUCCESS(rcNt))
3591 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3592 "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
3593
3594 rcNt = NtWriteVirtualMemory(pThis->hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
3595 if (!NT_SUCCESS(rcNt))
3596 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3597 "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
3598
3599 pvProt = pvLdrInitThunk;
3600 cbProt = sizeof(abNew);
3601 rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
3602 if (!NT_SUCCESS(rcNt))
3603 supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3604 "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
3605
3606 /* Caller starts child execution. */
3607 SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
3608}
3609
3610
3611
3612/**
3613 * This messes with the child PEB before we trigger the initial image events.
3614 *
3615 * @param pThis The child process data structure.
3616 */
3617static void supR3HardNtChildScrewUpPebForInitialImageEvents(PSUPR3HARDNTCHILD pThis)
3618{
3619 /*
3620 * Not sure if any of the cracker software uses the PEB at this point, but
3621 * just in case they do make some of the PEB fields a little less useful.
3622 */
3623 PEB Peb = pThis->Peb;
3624
3625 /* Make ImageBaseAddress useless. */
3626 Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
3627#ifdef RT_ARCH_AMD64
3628 Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
3629#endif
3630
3631 /*
3632 * Write the PEB.
3633 */
3634 SIZE_T cbActualMem = pThis->cbPeb;
3635 NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
3636 if (!NT_SUCCESS(rcNt))
3637 supR3HardenedWinKillChild(pThis, "supR3HardNtChildScrewUpPebForInitialImageEvents", rcNt,
3638 "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
3639}
3640
3641
3642/**
3643 * Check if the zero terminated NT unicode string is the path to the given
3644 * system32 DLL.
3645 *
3646 * @returns true if it is, false if not.
3647 * @param pUniStr The zero terminated NT unicode string path.
3648 * @param pszName The name of the system32 DLL.
3649 */
3650static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
3651{
3652 if (pUniStr->Length > g_System32NtPath.UniStr.Length)
3653 {
3654 if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
3655 {
3656 if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
3657 {
3658 if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
3659 return true;
3660 }
3661 }
3662 }
3663
3664 return false;
3665}
3666
3667
3668/**
3669 * Worker for supR3HardNtChildGatherData that locates NTDLL in the child
3670 * process.
3671 *
3672 * @param pThis The child process data structure.
3673 */
3674static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis)
3675{
3676 /*
3677 * Find NTDLL in this process first and take that as a starting point.
3678 */
3679 pThis->uNtDllParentAddr = (uintptr_t)GetModuleHandleW(L"ntdll.dll");
3680 SUPR3HARDENED_ASSERT(pThis->uNtDllParentAddr != 0 && !(pThis->uNtDllParentAddr & PAGE_OFFSET_MASK));
3681 pThis->uNtDllAddr = pThis->uNtDllParentAddr;
3682
3683 /*
3684 * Scan the virtual memory of the child.
3685 */
3686 uintptr_t cbAdvance = 0;
3687 uintptr_t uPtrWhere = 0;
3688 for (uint32_t i = 0; i < 1024; i++)
3689 {
3690 /* Query information. */
3691 SIZE_T cbActual = 0;
3692 MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
3693 NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
3694 (void const *)uPtrWhere,
3695 MemoryBasicInformation,
3696 &MemInfo,
3697 sizeof(MemInfo),
3698 &cbActual);
3699 if (!NT_SUCCESS(rcNt))
3700 break;
3701
3702 if ( MemInfo.Type == SEC_IMAGE
3703 || MemInfo.Type == SEC_PROTECTED_IMAGE
3704 || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
3705 {
3706 if (MemInfo.BaseAddress == MemInfo.AllocationBase)
3707 {
3708 /* Get the image name. */
3709 union
3710 {
3711 UNICODE_STRING UniStr;
3712 uint8_t abPadding[4096];
3713 } uBuf;
3714 NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
3715 MemInfo.BaseAddress,
3716 MemorySectionName,
3717 &uBuf,
3718 sizeof(uBuf) - sizeof(WCHAR),
3719 &cbActual);
3720 if (NT_SUCCESS(rcNt))
3721 {
3722 uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
3723 if (supR3HardNtIsNamedSystem32Dll(&uBuf.UniStr, "ntdll.dll"))
3724 {
3725 pThis->uNtDllAddr = (uintptr_t)MemInfo.AllocationBase;
3726 SUP_DPRINTF(("supR3HardNtPuChFindNtdll: uNtDllParentAddr=%p uNtDllChildAddr=%p\n",
3727 pThis->uNtDllParentAddr, pThis->uNtDllAddr));
3728 return;
3729 }
3730 }
3731 }
3732 }
3733
3734 /*
3735 * Advance.
3736 */
3737 cbAdvance = MemInfo.RegionSize;
3738 if (uPtrWhere + cbAdvance <= uPtrWhere)
3739 break;
3740 uPtrWhere += MemInfo.RegionSize;
3741 }
3742
3743 supR3HardenedWinKillChild(pThis, "supR3HardNtChildFindNtdll", VERR_MODULE_NOT_FOUND, "ntdll.dll not found in child process.");
3744}
3745
3746
3747/**
3748 * Gather child data.
3749 *
3750 * @param This The child process data structure.
3751 */
3752static void supR3HardNtChildGatherData(PSUPR3HARDNTCHILD pThis)
3753{
3754 /*
3755 * Basic info.
3756 */
3757 ULONG cbActual = 0;
3758 NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation,
3759 &pThis->BasicInfo, sizeof(pThis->BasicInfo), &cbActual);
3760 if (!NT_SUCCESS(rcNt))
3761 supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
3762 "NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
3763
3764 /*
3765 * If this is the middle (stub) process, we wish to wait for both child
3766 * and parent. So open the parent process. Not fatal if we cannnot.
3767 */
3768 if (pThis->iWhich > 1)
3769 {
3770 PROCESS_BASIC_INFORMATION SelfInfo;
3771 rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &SelfInfo, sizeof(SelfInfo), &cbActual);
3772 if (NT_SUCCESS(rcNt))
3773 {
3774 OBJECT_ATTRIBUTES ObjAttr;
3775 InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
3776
3777 CLIENT_ID ClientId;
3778 ClientId.UniqueProcess = (HANDLE)SelfInfo.InheritedFromUniqueProcessId;
3779 ClientId.UniqueThread = NULL;
3780
3781 rcNt = NtOpenProcess(&pThis->hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
3782#ifdef DEBUG
3783 SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
3784#endif
3785 if (!NT_SUCCESS(rcNt))
3786 {
3787 pThis->hParent = NULL;
3788 SUP_DPRINTF(("supR3HardNtChildGatherData: Failed to open parent process (%#p): %#x\n", ClientId.UniqueProcess, rcNt));
3789 }
3790 }
3791
3792 }
3793
3794 /*
3795 * Process environment block.
3796 */
3797 if (g_uNtVerCombined < SUP_NT_VER_W2K3)
3798 pThis->cbPeb = PEB_SIZE_W51;
3799 else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
3800 pThis->cbPeb = PEB_SIZE_W52;
3801 else if (g_uNtVerCombined < SUP_NT_VER_W70)
3802 pThis->cbPeb = PEB_SIZE_W6;
3803 else if (g_uNtVerCombined < SUP_NT_VER_W80)
3804 pThis->cbPeb = PEB_SIZE_W7;
3805 else if (g_uNtVerCombined < SUP_NT_VER_W81)
3806 pThis->cbPeb = PEB_SIZE_W80;
3807 else
3808 pThis->cbPeb = PEB_SIZE_W81;
3809
3810 SUP_DPRINTF(("supR3HardNtChildGatherData: PebBaseAddress=%p cbPeb=%#x\n",
3811 pThis->BasicInfo.PebBaseAddress, pThis->cbPeb));
3812
3813 SIZE_T cbActualMem;
3814 RT_ZERO(pThis->Peb);
3815 rcNt = NtReadVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &pThis->Peb, sizeof(pThis->Peb), &cbActualMem);
3816 if (!NT_SUCCESS(rcNt))
3817 supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
3818 "NtReadVirtualMemory/Peb failed: %#x", rcNt);
3819
3820 /*
3821 * Locate NtDll.
3822 */
3823 supR3HardNtChildFindNtdll(pThis);
3824}
3825
3826
3827/**
3828 * Does the actually respawning.
3829 *
3830 * @returns Never, will call exit or raise fatal error.
3831 * @param iWhich Which respawn we're to check for, 1 being the
3832 * first one, and 2 the second and final.
3833 */
3834static void supR3HardenedWinDoReSpawn(int iWhich)
3835{
3836 NTSTATUS rcNt;
3837 PPEB pPeb = NtCurrentPeb();
3838 PRTL_USER_PROCESS_PARAMETERS pParentProcParams = pPeb->ProcessParameters;
3839
3840 SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
3841
3842 /*
3843 * Init the child process data structure, creating the child communication
3844 * event sempahores.
3845 */
3846 SUPR3HARDNTCHILD This;
3847 RT_ZERO(This);
3848 This.iWhich = iWhich;
3849
3850 OBJECT_ATTRIBUTES ObjAttrs;
3851 This.hEvtChild = NULL;
3852 InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
3853 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
3854
3855 This.hEvtParent = NULL;
3856 InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
3857 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
3858
3859 /*
3860 * Set up security descriptors.
3861 */
3862 SECURITY_ATTRIBUTES ProcessSecAttrs;
3863 MYSECURITYCLEANUP ProcessSecAttrsCleanup;
3864 supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
3865
3866 SECURITY_ATTRIBUTES ThreadSecAttrs;
3867 MYSECURITYCLEANUP ThreadSecAttrsCleanup;
3868 supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
3869
3870#if 1
3871 /*
3872 * Configure the startup info and creation flags.
3873 */
3874 DWORD dwCreationFlags = CREATE_SUSPENDED;
3875
3876 STARTUPINFOEXW SiEx;
3877 suplibHardenedMemSet(&SiEx, 0, sizeof(SiEx));
3878 if (1)
3879 SiEx.StartupInfo.cb = sizeof(SiEx.StartupInfo);
3880 else
3881 {
3882 SiEx.StartupInfo.cb = sizeof(SiEx);
3883 dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
3884 /** @todo experiment with protected process stuff later on. */
3885 }
3886
3887 SiEx.StartupInfo.dwFlags |= pParentProcParams->WindowFlags & STARTF_USESHOWWINDOW;
3888 SiEx.StartupInfo.wShowWindow = (WORD)pParentProcParams->ShowWindowFlags;
3889
3890 SiEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3891 SiEx.StartupInfo.hStdInput = pParentProcParams->StandardInput;
3892 SiEx.StartupInfo.hStdOutput = pParentProcParams->StandardOutput;
3893 SiEx.StartupInfo.hStdError = pParentProcParams->StandardError;
3894
3895 /*
3896 * Construct the command line and launch the process.
3897 */
3898 PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich);
3899
3900 supR3HardenedWinEnableThreadCreation();
3901 PROCESS_INFORMATION ProcessInfoW32;
3902 if (!CreateProcessW(g_wszSupLibHardenedExePath,
3903 pwszCmdLine,
3904 &ProcessSecAttrs,
3905 &ThreadSecAttrs,
3906 TRUE /*fInheritHandles*/,
3907 dwCreationFlags,
3908 NULL /*pwszzEnvironment*/,
3909 NULL /*pwszCurDir*/,
3910 &SiEx.StartupInfo,
3911 &ProcessInfoW32))
3912 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
3913 "Error relaunching VirtualBox VM process: %u\n"
3914 "Command line: '%ls'",
3915 RtlGetLastWin32Error(), pwszCmdLine);
3916 supR3HardenedWinDisableThreadCreation();
3917
3918 SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n",
3919 iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId));
3920 This.hProcess = ProcessInfoW32.hProcess;
3921 This.hThread = ProcessInfoW32.hThread;
3922
3923#else
3924
3925 /*
3926 * Construct the process parameters.
3927 */
3928 UNICODE_STRING W32ImageName;
3929 W32ImageName.Buffer = g_wszSupLibHardenedExePath; /* Yes the windows name for the process parameters. */
3930 W32ImageName.Length = (USHORT)RTUtf16Len(g_wszSupLibHardenedExePath) * sizeof(WCHAR);
3931 W32ImageName.MaximumLength = W32ImageName.Length + sizeof(WCHAR);
3932
3933 UNICODE_STRING CmdLine;
3934 supR3HardNtChildConstructCmdLine(&CmdLine, iWhich);
3935
3936 PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL;
3937 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateProcessParameters(&pProcParams,
3938 &W32ImageName,
3939 NULL /* DllPath - inherit from this process */,
3940 NULL /* CurrentDirectory - inherit from this process */,
3941 &CmdLine,
3942 NULL /* Environment - inherit from this process */,
3943 NULL /* WindowsTitle - none */,
3944 NULL /* DesktopTitle - none. */,
3945 NULL /* ShellInfo - none. */,
3946 NULL /* RuntimeInfo - none (byte array for MSVCRT file info) */)
3947 );
3948
3949 /** @todo this doesn't work. :-( */
3950 pProcParams->ConsoleHandle = pParentProcParams->ConsoleHandle;
3951 pProcParams->ConsoleFlags = pParentProcParams->ConsoleFlags;
3952 pProcParams->StandardInput = pParentProcParams->StandardInput;
3953 pProcParams->StandardOutput = pParentProcParams->StandardOutput;
3954 pProcParams->StandardError = pParentProcParams->StandardError;
3955
3956 RTL_USER_PROCESS_INFORMATION ProcessInfoNt = { sizeof(ProcessInfoNt) };
3957 rcNt = RtlCreateUserProcess(&g_SupLibHardenedExeNtPath.UniStr,
3958 OBJ_INHERIT | OBJ_CASE_INSENSITIVE /*Attributes*/,
3959 pProcParams,
3960 NULL, //&ProcessSecAttrs,
3961 NULL, //&ThreadSecAttrs,
3962 NtCurrentProcess() /* ParentProcess */,
3963 FALSE /*fInheritHandles*/,
3964 NULL /* DebugPort */,
3965 NULL /* ExceptionPort */,
3966 &ProcessInfoNt);
3967 if (!NT_SUCCESS(rcNt))
3968 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
3969 "Error relaunching VirtualBox VM process: %#x\n"
3970 "Command line: '%ls'",
3971 rcNt, CmdLine.Buffer);
3972
3973 SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [ntdll].\n",
3974 iWhich, ProcessInfo.ClientId.UniqueProcess, ProcessInfo.ClientId.UniqueThread));
3975 RtlDestroyProcessParameters(pProcParams);
3976
3977 This.hProcess = ProcessInfoNt.ProcessHandle;
3978 This.hThread = ProcessInfoNt.ThreadHandle;
3979#endif
3980
3981#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
3982 /*
3983 * Apply anti debugger notification trick to the thread. (Also done in
3984 * supR3HardenedWinInit.) This may fail with STATUS_ACCESS_DENIED and
3985 * maybe other errors. (Unfortunately, recent (SEP 12.1) of symantec's
3986 * sysplant.sys driver will cause process deadlocks and a shutdown/reboot
3987 * denial of service problem if we hide the initial thread, so we postpone
3988 * this action if we've detected SEP.)
3989 */
3990 if (!(g_fSupAdversaries & (SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT | SUPHARDNT_ADVERSARY_SYMANTEC_N360)))
3991 {
3992 rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0);
3993 if (!NT_SUCCESS(rcNt))
3994 SUP_DPRINTF(("supR3HardenedWinReSpawn: NtSetInformationThread/ThreadHideFromDebugger failed: %#x (harmless)\n", rcNt));
3995 }
3996#endif
3997
3998 /*
3999 * Perform very early child initialization.
4000 */
4001 supR3HardNtChildGatherData(&This);
4002 supR3HardNtChildScrewUpPebForInitialImageEvents(&This);
4003 supR3HardNtChildSetUpChildInit(&This);
4004
4005 ULONG cSuspendCount = 0;
4006 rcNt = NtResumeThread(This.hThread, &cSuspendCount);
4007 if (!NT_SUCCESS(rcNt))
4008 supR3HardenedWinKillChild(&This, "supR3HardenedWinDoReSpawn", rcNt, "NtResumeThread failed: %#x", rcNt);
4009
4010 /*
4011 * Santizie the pre-NTDLL child when it's ready.
4012 *
4013 * AV software and other things injecting themselves into the embryonic
4014 * and budding process to intercept API calls and what not. Unfortunately
4015 * this is also the behavior of viruses, malware and other unfriendly
4016 * software, so we won't stand for it. AV software can scan our image
4017 * as they are loaded via kernel hooks, that's sufficient. No need for
4018 * patching half of NTDLL or messing with the import table of the
4019 * process executable.
4020 */
4021 supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_PurifyChildAndCloseHandles, 2000 /*ms*/, "PurifyChildAndCloseHandles");
4022 supR3HardNtChildPurify(&This);
4023 supR3HardNtChildSanitizePeb(&This);
4024
4025 /*
4026 * Close the unrestricted access handles. Since we need to wait on the
4027 * child process, we'll reopen the process with limited access before doing
4028 * away with the process handle returned by CreateProcess.
4029 */
4030 supR3HardNtChildCloseFullAccessHandles(&This);
4031
4032 /*
4033 * Signal the child that we've closed the unrestricted handles and it can
4034 * safely try open the driver.
4035 */
4036 rcNt = NtSetEvent(This.hEvtChild, NULL);
4037 if (!NT_SUCCESS(rcNt))
4038 supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
4039 "NtSetEvent failed on child process handle: %#x\n", rcNt);
4040
4041 /*
4042 * Ditch the loader cache so we don't sit on too much memory while waiting.
4043 */
4044 supR3HardenedWinFlushLoaderCache();
4045 supR3HardenedWinCompactHeaps();
4046
4047 /*
4048 * Enable thread creation at this point so Ctrl-C and Ctrl-Break can be processed.
4049 */
4050 supR3HardenedWinEnableThreadCreation();
4051
4052 /*
4053 * Wait for the child to get to suplibHardenedWindowsMain so we can close the handles.
4054 */
4055 supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_CloseEvents, 60000 /*ms*/, "CloseEvents");
4056
4057 NtClose(This.hEvtChild);
4058 NtClose(This.hEvtParent);
4059 This.hEvtChild = NULL;
4060 This.hEvtParent = NULL;
4061
4062 /*
4063 * Wait for the process to terminate.
4064 */
4065 supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_End, RT_INDEFINITE_WAIT, "the end");
4066 SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
4067}
4068
4069
4070/**
4071 * Logs the content of the given object directory.
4072 *
4073 * @returns true if it exists, false if not.
4074 * @param pszDir The path of the directory to log (ASCII).
4075 */
4076static void supR3HardenedWinLogObjDir(const char *pszDir)
4077{
4078 /*
4079 * Open the driver object directory.
4080 */
4081 RTUTF16 wszDir[128];
4082 int rc = RTUtf16CopyAscii(wszDir, RT_ELEMENTS(wszDir), pszDir);
4083 if (RT_FAILURE(rc))
4084 {
4085 SUP_DPRINTF(("supR3HardenedWinLogObjDir: RTUtf16CopyAscii -> %Rrc on '%s'\n", rc, pszDir));
4086 return;
4087 }
4088
4089 UNICODE_STRING NtDirName;
4090 NtDirName.Buffer = (WCHAR *)wszDir;
4091 NtDirName.Length = (USHORT)(RTUtf16Len(wszDir) * sizeof(WCHAR));
4092 NtDirName.MaximumLength = NtDirName.Length + sizeof(WCHAR);
4093
4094 OBJECT_ATTRIBUTES ObjAttr;
4095 InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4096
4097 HANDLE hDir;
4098 NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
4099 SUP_DPRINTF(("supR3HardenedWinLogObjDir: %ls => %#x\n", wszDir, rcNt));
4100 if (!NT_SUCCESS(rcNt))
4101 return;
4102
4103 /*
4104 * Enumerate it, looking for the driver.
4105 */
4106 ULONG uObjDirCtx = 0;
4107 for (;;)
4108 {
4109 uint32_t abBuffer[_64K + _1K];
4110 ULONG cbActual;
4111 rcNt = NtQueryDirectoryObject(hDir,
4112 abBuffer,
4113 sizeof(abBuffer) - 4, /* minus four for string terminator space. */
4114 FALSE /*ReturnSingleEntry */,
4115 FALSE /*RestartScan*/,
4116 &uObjDirCtx,
4117 &cbActual);
4118 if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
4119 {
4120 SUP_DPRINTF(("supR3HardenedWinLogObjDir: NtQueryDirectoryObject => rcNt=%#x cbActual=%#x\n", rcNt, cbActual));
4121 break;
4122 }
4123
4124 POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
4125 while (pObjDir->Name.Length != 0)
4126 {
4127 WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
4128 SUP_DPRINTF((" %.*ls %.*ls\n",
4129 pObjDir->TypeName.Length / sizeof(WCHAR), pObjDir->TypeName.Buffer,
4130 pObjDir->Name.Length / sizeof(WCHAR), pObjDir->Name.Buffer));
4131
4132 /* Next directory entry. */
4133 pObjDir++;
4134 }
4135 }
4136
4137 /*
4138 * Clean up and return.
4139 */
4140 NtClose(hDir);
4141}
4142
4143
4144/**
4145 * Tries to open VBoxDrvErrorInfo and read extra error info from it.
4146 *
4147 * @returns pszErrorInfo.
4148 * @param pszErrorInfo The destination buffer. Will always be
4149 * terminated.
4150 * @param cbErrorInfo The size of the destination buffer.
4151 * @param pszPrefix What to prefix the error info with, if we got
4152 * anything.
4153 */
4154DECLHIDDEN(char *) supR3HardenedWinReadErrorInfoDevice(char *pszErrorInfo, size_t cbErrorInfo, const char *pszPrefix)
4155{
4156 RT_BZERO(pszErrorInfo, cbErrorInfo);
4157
4158 /*
4159 * Try open the device.
4160 */
4161 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
4162 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4163 UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
4164 OBJECT_ATTRIBUTES ObjAttr;
4165 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4166 NTSTATUS rcNt = NtCreateFile(&hFile,
4167 GENERIC_READ, /* No SYNCHRONIZE. */
4168 &ObjAttr,
4169 &Ios,
4170 NULL /* Allocation Size*/,
4171 FILE_ATTRIBUTE_NORMAL,
4172 FILE_SHARE_READ | FILE_SHARE_WRITE,
4173 FILE_OPEN,
4174 FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
4175 NULL /*EaBuffer*/,
4176 0 /*EaLength*/);
4177 if (NT_SUCCESS(rcNt))
4178 rcNt = Ios.Status;
4179 if (NT_SUCCESS(rcNt))
4180 {
4181 /*
4182 * Try read error info.
4183 */
4184 size_t cchPrefix = strlen(pszPrefix);
4185 if (cchPrefix + 3 < cbErrorInfo)
4186 {
4187 LARGE_INTEGER offRead;
4188 offRead.QuadPart = 0;
4189 rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
4190 &pszErrorInfo[cchPrefix], (ULONG)(cbErrorInfo - cchPrefix - 1), &offRead, NULL);
4191 if (NT_SUCCESS(rcNt))
4192 {
4193 memcpy(pszErrorInfo, pszPrefix, cchPrefix);
4194 pszErrorInfo[cbErrorInfo - 1] = '\0';
4195 SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: '%s'", &pszErrorInfo[cchPrefix]));
4196 }
4197 else
4198 {
4199 *pszErrorInfo = '\0';
4200 if (rcNt != STATUS_END_OF_FILE)
4201 SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtReadFile -> %#x\n", rcNt));
4202 }
4203 }
4204 else
4205 RTStrCopy(pszErrorInfo, cbErrorInfo, "error info buffer too small");
4206 NtClose(hFile);
4207 }
4208 else
4209 SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtCreateFile -> %#x\n", rcNt));
4210
4211 return pszErrorInfo;
4212}
4213
4214
4215
4216/**
4217 * Checks if the driver exists.
4218 *
4219 * This checks whether the driver is present in the /Driver object directory.
4220 * Drivers being initialized or terminated will have an object there
4221 * before/after their devices nodes are created/deleted.
4222 *
4223 * @returns true if it exists, false if not.
4224 * @param pszDriver The driver name.
4225 */
4226static bool supR3HardenedWinDriverExists(const char *pszDriver)
4227{
4228 /*
4229 * Open the driver object directory.
4230 */
4231 UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
4232
4233 OBJECT_ATTRIBUTES ObjAttr;
4234 InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4235
4236 HANDLE hDir;
4237 NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
4238#ifdef VBOX_STRICT
4239 SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
4240#endif
4241 if (!NT_SUCCESS(rcNt))
4242 return true;
4243
4244 /*
4245 * Enumerate it, looking for the driver.
4246 */
4247 bool fFound = true;
4248 ULONG uObjDirCtx = 0;
4249 do
4250 {
4251 uint32_t abBuffer[_64K + _1K];
4252 ULONG cbActual;
4253 rcNt = NtQueryDirectoryObject(hDir,
4254 abBuffer,
4255 sizeof(abBuffer) - 4, /* minus four for string terminator space. */
4256 FALSE /*ReturnSingleEntry */,
4257 FALSE /*RestartScan*/,
4258 &uObjDirCtx,
4259 &cbActual);
4260 if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
4261 break;
4262
4263 POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
4264 while (pObjDir->Name.Length != 0)
4265 {
4266 WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
4267 pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
4268 if ( pObjDir->Name.Length > 1
4269 && RTUtf16ICmpAscii(pObjDir->Name.Buffer, pszDriver) == 0)
4270 {
4271 fFound = true;
4272 break;
4273 }
4274 pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
4275
4276 /* Next directory entry. */
4277 pObjDir++;
4278 }
4279 } while (!fFound);
4280
4281 /*
4282 * Clean up and return.
4283 */
4284 NtClose(hDir);
4285
4286 return fFound;
4287}
4288
4289
4290/**
4291 * Open the stub device before the 2nd respawn.
4292 */
4293static void supR3HardenedWinOpenStubDevice(void)
4294{
4295 if (g_fSupStubOpened)
4296 return;
4297
4298 /*
4299 * Retry if we think driver might still be initializing (STATUS_NO_SUCH_DEVICE + \Drivers\VBoxDrv).
4300 */
4301 static const WCHAR s_wszName[] = SUPDRV_NT_DEVICE_NAME_STUB;
4302 uint64_t const uMsTsStart = supR3HardenedWinGetMilliTS();
4303 NTSTATUS rcNt;
4304 uint32_t iTry;
4305
4306 for (iTry = 0;; iTry++)
4307 {
4308 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
4309 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4310
4311 UNICODE_STRING NtName;
4312 NtName.Buffer = (PWSTR)s_wszName;
4313 NtName.Length = sizeof(s_wszName) - sizeof(WCHAR);
4314 NtName.MaximumLength = sizeof(s_wszName);
4315
4316 OBJECT_ATTRIBUTES ObjAttr;
4317 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4318
4319 rcNt = NtCreateFile(&hFile,
4320 GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */
4321 &ObjAttr,
4322 &Ios,
4323 NULL /* Allocation Size*/,
4324 FILE_ATTRIBUTE_NORMAL,
4325 FILE_SHARE_READ | FILE_SHARE_WRITE,
4326 FILE_OPEN,
4327 FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
4328 NULL /*EaBuffer*/,
4329 0 /*EaLength*/);
4330 if (NT_SUCCESS(rcNt))
4331 rcNt = Ios.Status;
4332
4333 /* The STATUS_NO_SUCH_DEVICE might be returned if the device is not
4334 completely initialized. Delay a little bit and try again. */
4335 if (rcNt != STATUS_NO_SUCH_DEVICE)
4336 break;
4337 if (iTry > 0 && supR3HardenedWinGetMilliTS() - uMsTsStart > 5000) /* 5 sec, at least two tries */
4338 break;
4339 if (!supR3HardenedWinDriverExists("VBoxDrv"))
4340 {
4341 /** @todo Consider starting the VBoxdrv.sys service. Requires 2nd process
4342 * though, rather complicated actually as CreateProcess causes all
4343 * kind of things to happen to this process which would make it hard to
4344 * pass the process verification tests... :-/ */
4345 break;
4346 }
4347
4348 LARGE_INTEGER Time;
4349 if (iTry < 8)
4350 Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
4351 else
4352 Time.QuadPart = -32000000 / 100; /* 32ms in 100ns units, relative time. */
4353 NtDelayExecution(TRUE, &Time);
4354 }
4355
4356 if (NT_SUCCESS(rcNt))
4357 g_fSupStubOpened = true;
4358 else
4359 {
4360 /*
4361 * Report trouble (fatal). For some errors codes we try gather some
4362 * extra information that goes into VBoxStartup.log so that we stand a
4363 * better chance resolving the issue.
4364 */
4365 char szErrorInfo[_4K];
4366 int rc = VERR_OPEN_FAILED;
4367 if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
4368 {
4369 rc = SUP_NT_STATUS_TO_VBOX(rcNt);
4370
4371 /*
4372 * \Windows\ApiPort open trouble. So far only
4373 * STATUS_OBJECT_TYPE_MISMATCH has been observed.
4374 */
4375 if (rc == VERR_SUPDRV_APIPORT_OPEN_ERROR)
4376 {
4377 SUP_DPRINTF(("Error opening VBoxDrvStub: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"));
4378
4379 uint32_t uSessionId = NtCurrentPeb()->SessionId;
4380 SUP_DPRINTF((" SessionID=%#x\n", uSessionId));
4381 char szDir[64];
4382 if (uSessionId == 0)
4383 RTStrCopy(szDir, sizeof(szDir), "\\Windows");
4384 else
4385 {
4386 RTStrPrintf(szDir, sizeof(szDir), "\\Sessions\\%u\\Windows", uSessionId);
4387 supR3HardenedWinLogObjDir(szDir);
4388 }
4389 supR3HardenedWinLogObjDir("\\Windows");
4390 supR3HardenedWinLogObjDir("\\Sessions");
4391
4392 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, rc,
4393 "NtCreateFile(%ls) failed: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"
4394 "\n"
4395 "Error getting %s\\ApiPort in the driver from vboxdrv.\n"
4396 "\n"
4397 "Could be due to security software is redirecting access to it, so please include full "
4398 "details of such software in a bug report. VBoxStartup.log may contain details important "
4399 "to resolving the issue.%s"
4400 , s_wszName, szDir,
4401 supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4402 "\n\nVBoxDrvStub error: "));
4403 }
4404
4405 /*
4406 * Generic VBox failure message.
4407 */
4408 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, rc,
4409 "NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)%s", s_wszName, rc, rcNt,
4410 supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4411 "\nVBoxDrvStub error: "));
4412 }
4413 else
4414 {
4415 const char *pszDefine;
4416 switch (rcNt)
4417 {
4418 case STATUS_NO_SUCH_DEVICE: pszDefine = " STATUS_NO_SUCH_DEVICE"; break;
4419 case STATUS_OBJECT_NAME_NOT_FOUND: pszDefine = " STATUS_OBJECT_NAME_NOT_FOUND"; break;
4420 case STATUS_ACCESS_DENIED: pszDefine = " STATUS_ACCESS_DENIED"; break;
4421 case STATUS_TRUST_FAILURE: pszDefine = " STATUS_TRUST_FAILURE"; break;
4422 default: pszDefine = ""; break;
4423 }
4424
4425 /*
4426 * Problems opening the device is generally due to driver load/
4427 * unload issues. Check whether the driver is loaded and make
4428 * suggestions accordingly.
4429 */
4430/** @todo don't fail during early init, wait till later and try load the driver if missing or at least query the service manager for additional information. */
4431 if ( rcNt == STATUS_NO_SUCH_DEVICE
4432 || rcNt == STATUS_OBJECT_NAME_NOT_FOUND)
4433 {
4434 SUP_DPRINTF(("Error opening VBoxDrvStub: %s\n", pszDefine));
4435 if (supR3HardenedWinDriverExists("VBoxDrv"))
4436 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4437 "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
4438 "\n"
4439 "Driver is probably stuck stopping/starting. Try 'sc.exe query vboxdrv' to get more "
4440 "information about its state. Rebooting may actually help.%s"
4441 , s_wszName, rcNt, pszDefine, iTry,
4442 supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4443 "\nVBoxDrvStub error: "));
4444 else
4445 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4446 "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
4447 "\n"
4448 "Driver is does not appear to be loaded. Try 'sc.exe start vboxdrv', reinstall "
4449 "VirtualBox or reboot.%s"
4450 , s_wszName, rcNt, pszDefine, iTry,
4451 supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4452 "\nVBoxDrvStub error: "));
4453 }
4454
4455 /* Generic NT failure message. */
4456 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4457 "NtCreateFile(%ls) failed: %#x%s (%u retries)%s",
4458 s_wszName, rcNt, pszDefine, iTry,
4459 supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4460 "\nVBoxDrvStub error: "));
4461 }
4462 }
4463}
4464
4465
4466/**
4467 * Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
4468 *
4469 * @returns Program exit code.
4470 */
4471DECLHIDDEN(int) supR3HardenedWinReSpawn(int iWhich)
4472{
4473 /*
4474 * Before the 2nd respawn we set up a child protection deal with the
4475 * support driver via /Devices/VBoxDrvStub. (We tried to do this
4476 * during the early init, but in case we had trouble accessing vboxdrv we
4477 * retry it here where we have kernel32.dll and others to pull in for
4478 * better diagnostics.)
4479 */
4480 if (iWhich == 2)
4481 supR3HardenedWinOpenStubDevice();
4482
4483 /*
4484 * Make sure we're alone in the stub process before creating the VM process
4485 * and that there isn't any debuggers attached.
4486 */
4487 if (iWhich == 2)
4488 {
4489 int rc = supHardNtVpDebugger(NtCurrentProcess(), RTErrInfoInitStatic(&g_ErrInfoStatic));
4490 if (RT_SUCCESS(rc))
4491 rc = supHardNtVpThread(NtCurrentProcess(), NtCurrentThread(), RTErrInfoInitStatic(&g_ErrInfoStatic));
4492 if (RT_FAILURE(rc))
4493 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Integrity, rc, "%s", g_ErrInfoStatic.szMsg);
4494 }
4495
4496
4497 /*
4498 * Respawn the process with kernel protection for the new process.
4499 */
4500 supR3HardenedWinDoReSpawn(iWhich);
4501 SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
4502 return RTEXITCODE_FAILURE;
4503}
4504
4505
4506/**
4507 * Checks if re-spawning is required, replacing the respawn argument if not.
4508 *
4509 * @returns true if required, false if not. In the latter case, the first
4510 * argument in the vector is replaced.
4511 * @param iWhich Which respawn we're to check for, 1 being the
4512 * first one, and 2 the second and final.
4513 * @param cArgs The number of arguments.
4514 * @param papszArgs Pointer to the argument vector.
4515 */
4516DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int iWhich, int cArgs, char **papszArgs)
4517{
4518 SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
4519 SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
4520
4521 if (cArgs < 1)
4522 return true;
4523
4524 if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
4525 {
4526 if (iWhich > 1)
4527 return true;
4528 }
4529 else if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
4530 {
4531 if (iWhich < 2)
4532 return false;
4533 }
4534 else
4535 return true;
4536
4537 /* Replace the argument. */
4538 papszArgs[0] = g_szSupLibHardenedExePath;
4539 return false;
4540}
4541
4542
4543/**
4544 * Initializes the windows verficiation bits and other things we're better off
4545 * doing after main() has passed on it's data.
4546 *
4547 * @param fFlags The main flags.
4548 * @param fAvastKludge Whether to apply the avast kludge.
4549 */
4550DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge)
4551{
4552 NTSTATUS rcNt;
4553
4554#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
4555 /*
4556 * Install a anti debugging hack before we continue. This prevents most
4557 * notifications from ending up in the debugger. (Also applied to the
4558 * child process when respawning.)
4559 */
4560 rcNt = NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, NULL, 0);
4561 if (!NT_SUCCESS(rcNt))
4562 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
4563 "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
4564#endif
4565
4566 /*
4567 * Init the verifier.
4568 */
4569 RTErrInfoInitStatic(&g_ErrInfoStatic);
4570 int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
4571 if (RT_FAILURE(rc))
4572 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
4573 "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
4574
4575 /*
4576 * Get the windows system directory from the KnownDlls dir.
4577 */
4578 HANDLE hSymlink = INVALID_HANDLE_VALUE;
4579 UNICODE_STRING UniStr = RTNT_CONSTANT_UNISTR(L"\\KnownDlls\\KnownDllPath");
4580 OBJECT_ATTRIBUTES ObjAttrs;
4581 InitializeObjectAttributes(&ObjAttrs, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4582 rcNt = NtOpenSymbolicLinkObject(&hSymlink, SYMBOLIC_LINK_QUERY, &ObjAttrs);
4583 if (!NT_SUCCESS(rcNt))
4584 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error opening '%ls': %#x", UniStr.Buffer, rcNt);
4585
4586 g_System32WinPath.UniStr.Buffer = g_System32WinPath.awcBuffer;
4587 g_System32WinPath.UniStr.Length = 0;
4588 g_System32WinPath.UniStr.MaximumLength = sizeof(g_System32WinPath.awcBuffer) - sizeof(RTUTF16);
4589 rcNt = NtQuerySymbolicLinkObject(hSymlink, &g_System32WinPath.UniStr, NULL);
4590 if (!NT_SUCCESS(rcNt))
4591 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error querying '%ls': %#x", UniStr.Buffer, rcNt);
4592 g_System32WinPath.UniStr.Buffer[g_System32WinPath.UniStr.Length / sizeof(RTUTF16)] = '\0';
4593
4594 SUP_DPRINTF(("KnownDllPath: %ls\n", g_System32WinPath.UniStr.Buffer));
4595 NtClose(hSymlink);
4596
4597 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
4598 {
4599 if (fAvastKludge)
4600 {
4601 /*
4602 * Do a self purification to cure avast's weird NtOpenFile write-thru
4603 * change in GetBinaryTypeW change in kernel32. Unfortunately, avast
4604 * uses a system thread to perform the process modifications, which
4605 * means it's hard to make sure it had the chance to make them...
4606 *
4607 * We have to resort to kludge doing yield and sleep fudging for a
4608 * number of milliseconds and schedulings before we can hope that avast
4609 * and similar products have done what they need to do. If we do any
4610 * fixes, we wait for a while again and redo it until we're clean.
4611 *
4612 * This is unfortunately kind of fragile.
4613 */
4614 uint32_t cMsFudge = g_fSupAdversaries ? 512 : 128;
4615 uint32_t cFixes;
4616 for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
4617 {
4618 uint32_t cSleeps = 0;
4619 uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
4620 do
4621 {
4622 NtYieldExecution();
4623 LARGE_INTEGER Time;
4624 Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
4625 NtDelayExecution(FALSE, &Time);
4626 cSleeps++;
4627 } while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
4628 || cSleeps < 8);
4629 SUP_DPRINTF(("supR3HardenedWinInit: Startup delay kludge #2/%u: %u ms, %u sleeps\n",
4630 iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
4631
4632 cFixes = 0;
4633 rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_SELF_PURIFICATION,
4634 0 /*fFlags*/, &cFixes, NULL /*pErrInfo*/);
4635 if (RT_FAILURE(rc) || cFixes == 0)
4636 break;
4637
4638 if (!g_fSupAdversaries)
4639 g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
4640 cMsFudge = 512;
4641
4642 /* Log the KiOpPrefetchPatchCount value if available, hoping it might sched some light on spider38's case. */
4643 ULONG cPatchCount = 0;
4644 rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
4645 &cPatchCount, sizeof(cPatchCount), NULL);
4646 if (NT_SUCCESS(rcNt))
4647 SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
4648 cFixes, g_fSupAdversaries, cPatchCount));
4649 else
4650 SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
4651 }
4652 }
4653
4654 /*
4655 * Install the hooks.
4656 */
4657 supR3HardenedWinInstallHooks();
4658 }
4659
4660#ifndef VBOX_WITH_VISTA_NO_SP
4661 /*
4662 * Complain about Vista w/o service pack if we're launching a VM.
4663 */
4664 if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
4665 && g_uNtVerCombined >= SUP_NT_VER_VISTA
4666 && g_uNtVerCombined < SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
4667 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_NOT_SUPPORTED,
4668 "Window Vista without any service pack installed is not supported. Please install the latest service pack.");
4669#endif
4670}
4671
4672
4673/**
4674 * Modifies the DLL search path for testcases.
4675 *
4676 * This makes sure the application binary path is in the search path. When
4677 * starting a testcase executable in the testcase/ subdirectory this isn't the
4678 * case by default. So, unless we do something about it we won't be able to
4679 * import VBox DLLs.
4680 *
4681 * @param fFlags The main flags (giving the location).
4682 * @param pszAppBinPath The path to the application binary directory
4683 * (windows style).
4684 */
4685DECLHIDDEN(void) supR3HardenedWinModifyDllSearchPath(uint32_t fFlags, const char *pszAppBinPath)
4686{
4687 /*
4688 * For the testcases to work, we must add the app bin directory to the
4689 * DLL search list before the testcase dll is loaded or it won't be
4690 * able to find the VBox DLLs. This is done _after_ VBoxRT.dll is
4691 * initialized and sets its defaults.
4692 */
4693 switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
4694 {
4695 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
4696 break;
4697 default:
4698 return;
4699 }
4700
4701 /*
4702 * Dynamically resolve the two APIs we need (the latter uses forwarders on w7).
4703 */
4704 HMODULE hModKernel32 = GetModuleHandleW(L"kernel32.dll");
4705
4706 typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR);
4707 PFNSETDLLDIRECTORY pfnSetDllDir;
4708 pfnSetDllDir = (PFNSETDLLDIRECTORY)GetProcAddress(hModKernel32, "SetDllDirectoryW");
4709
4710 typedef BOOL (WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD);
4711 PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs;
4712 pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(hModKernel32, "SetDefaultDllDirectories");
4713
4714 if (pfnSetDllDir != NULL)
4715 {
4716 /*
4717 * Convert the path to UTF-16 and try set it.
4718 */
4719 PRTUTF16 pwszAppBinPath = NULL;
4720 int rc = RTStrToUtf16(pszAppBinPath, &pwszAppBinPath);
4721 if (RT_SUCCESS(rc))
4722 {
4723 if (pfnSetDllDir(pwszAppBinPath))
4724 {
4725 SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Set dll dir to '%ls'\n", pwszAppBinPath));
4726 g_fSupLibHardenedDllSearchUserDirs = true;
4727
4728 /*
4729 * We set it alright, on W7 and later we also must modify the
4730 * default DLL search order. See @bugref{6861} for details on
4731 * why we don't do this on Vista (also see init-win.cpp in IPRT).
4732 */
4733 if ( pfnSetDefDllDirs
4734 && g_uNtVerCombined >= SUP_NT_VER_W70)
4735 {
4736 if (pfnSetDefDllDirs( LOAD_LIBRARY_SEARCH_APPLICATION_DIR
4737 | LOAD_LIBRARY_SEARCH_SYSTEM32
4738 | LOAD_LIBRARY_SEARCH_USER_DIRS))
4739 SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Successfully modified search dirs.\n"));
4740 else
4741 supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
4742 pwszAppBinPath, RtlGetLastWin32Error());
4743 }
4744 }
4745 else
4746 supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
4747 pwszAppBinPath, RtlGetLastWin32Error());
4748 RTUtf16Free(pwszAppBinPath);
4749 }
4750 else
4751 supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: RTStrToUtf16(%s) failed: %d\n", pszAppBinPath, rc);
4752 }
4753}
4754
4755
4756/**
4757 * Initializes the application binary directory path.
4758 *
4759 * This is called once or twice.
4760 *
4761 * @param fFlags The main flags (giving the location).
4762 */
4763DECLHIDDEN(void) supR3HardenedWinInitAppBin(uint32_t fFlags)
4764{
4765 USHORT cwc = (USHORT)g_offSupLibHardenedExeNtName - 1;
4766 g_SupLibHardenedAppBinNtPath.UniStr.Buffer = g_SupLibHardenedAppBinNtPath.awcBuffer;
4767 memcpy(g_SupLibHardenedAppBinNtPath.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Buffer, cwc * sizeof(WCHAR));
4768
4769 switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
4770 {
4771 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
4772 break;
4773 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
4774 {
4775 /* Drop one directory level. */
4776 USHORT off = cwc;
4777 WCHAR wc;
4778 while ( off > 1
4779 && (wc = g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 1]) != '\0')
4780 if (wc != '\\' && wc != '/')
4781 off--;
4782 else
4783 {
4784 if (g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 2] == ':')
4785 cwc = off;
4786 else
4787 cwc = off - 1;
4788 break;
4789 }
4790 break;
4791 }
4792 default:
4793 supR3HardenedFatal("supR3HardenedWinInitAppBin: Unknown program binary location: %#x\n", fFlags);
4794 }
4795
4796 g_SupLibHardenedAppBinNtPath.UniStr.Buffer[cwc] = '\0';
4797 g_SupLibHardenedAppBinNtPath.UniStr.Length = cwc * sizeof(WCHAR);
4798 g_SupLibHardenedAppBinNtPath.UniStr.MaximumLength = sizeof(g_SupLibHardenedAppBinNtPath.awcBuffer);
4799 SUP_DPRINTF(("supR3HardenedWinInitAppBin(%#x): '%ls'\n", fFlags, g_SupLibHardenedAppBinNtPath.UniStr.Buffer));
4800}
4801
4802
4803/**
4804 * Converts the Windows command line string (UTF-16) to an array of UTF-8
4805 * arguments suitable for passing to main().
4806 *
4807 * @returns Pointer to the argument array.
4808 * @param pawcCmdLine The UTF-16 windows command line to parse.
4809 * @param cwcCmdLine The length of the command line.
4810 * @param pcArgs Where to return the number of arguments.
4811 */
4812static char **suplibCommandLineToArgvWStub(PCRTUTF16 pawcCmdLine, size_t cwcCmdLine, int *pcArgs)
4813{
4814 /*
4815 * Convert the command line string to UTF-8.
4816 */
4817 char *pszCmdLine = NULL;
4818 SUPR3HARDENED_ASSERT(RT_SUCCESS(RTUtf16ToUtf8Ex(pawcCmdLine, cwcCmdLine, &pszCmdLine, 0, NULL)));
4819
4820 /*
4821 * Parse the command line, carving argument strings out of it.
4822 */
4823 int cArgs = 0;
4824 int cArgsAllocated = 4;
4825 char **papszArgs = (char **)RTMemAllocZ(sizeof(char *) * cArgsAllocated);
4826 char *pszSrc = pszCmdLine;
4827 for (;;)
4828 {
4829 /* skip leading blanks. */
4830 char ch = *pszSrc;
4831 while (suplibCommandLineIsArgSeparator(ch))
4832 ch = *++pszSrc;
4833 if (!ch)
4834 break;
4835
4836 /* Add argument to the vector. */
4837 if (cArgs + 2 >= cArgsAllocated)
4838 {
4839 cArgsAllocated *= 2;
4840 papszArgs = (char **)RTMemRealloc(papszArgs, sizeof(char *) * cArgsAllocated);
4841 }
4842 papszArgs[cArgs++] = pszSrc;
4843 papszArgs[cArgs] = NULL;
4844
4845 /* Unquote and unescape the string. */
4846 char *pszDst = pszSrc++;
4847 bool fQuoted = false;
4848 do
4849 {
4850 if (ch == '"')
4851 fQuoted = !fQuoted;
4852 else if (ch != '\\' || (*pszSrc != '\\' && *pszSrc != '"'))
4853 *pszDst++ = ch;
4854 else
4855 {
4856 unsigned cSlashes = 0;
4857 while ((ch = *pszSrc++) == '\\')
4858 cSlashes++;
4859 if (ch == '"')
4860 {
4861 while (cSlashes >= 2)
4862 {
4863 cSlashes -= 2;
4864 *pszDst++ = '\\';
4865 }
4866 if (cSlashes)
4867 *pszDst++ = '"';
4868 else
4869 fQuoted = !fQuoted;
4870 }
4871 else
4872 {
4873 pszSrc--;
4874 while (cSlashes-- > 0)
4875 *pszDst++ = '\\';
4876 }
4877 }
4878
4879 ch = *pszSrc++;
4880 } while (ch != '\0' && (fQuoted || !suplibCommandLineIsArgSeparator(ch)));
4881
4882 /* Terminate the argument. */
4883 *pszDst = '\0';
4884 if (!ch)
4885 break;
4886 }
4887
4888 *pcArgs = cArgs;
4889 return papszArgs;
4890}
4891
4892
4893/**
4894 * Logs information about a file from a protection product or from Windows.
4895 *
4896 * The purpose here is to better see which version of the product is installed
4897 * and not needing to depend on the user supplying the correct information.
4898 *
4899 * @param pwszFile The NT path to the file.
4900 * @param fAdversarial Set if from a protection product, false if
4901 * system file.
4902 */
4903static void supR3HardenedLogFileInfo(PCRTUTF16 pwszFile, bool fAdversarial)
4904{
4905 /*
4906 * Open the file.
4907 */
4908 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
4909 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4910 UNICODE_STRING UniStrName;
4911 UniStrName.Buffer = (WCHAR *)pwszFile;
4912 UniStrName.Length = (USHORT)(RTUtf16Len(pwszFile) * sizeof(WCHAR));
4913 UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
4914 OBJECT_ATTRIBUTES ObjAttr;
4915 InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4916 NTSTATUS rcNt = NtCreateFile(&hFile,
4917 GENERIC_READ | SYNCHRONIZE,
4918 &ObjAttr,
4919 &Ios,
4920 NULL /* Allocation Size*/,
4921 FILE_ATTRIBUTE_NORMAL,
4922 FILE_SHARE_READ,
4923 FILE_OPEN,
4924 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
4925 NULL /*EaBuffer*/,
4926 0 /*EaLength*/);
4927 if (NT_SUCCESS(rcNt))
4928 rcNt = Ios.Status;
4929 if (NT_SUCCESS(rcNt))
4930 {
4931 SUP_DPRINTF(("%ls:\n", pwszFile));
4932 union
4933 {
4934 uint64_t u64AlignmentInsurance;
4935 FILE_BASIC_INFORMATION BasicInfo;
4936 FILE_STANDARD_INFORMATION StdInfo;
4937 uint8_t abBuf[32768];
4938 RTUTF16 awcBuf[16384];
4939 IMAGE_DOS_HEADER MzHdr;
4940 } u;
4941 RTTIMESPEC TimeSpec;
4942 char szTmp[64];
4943
4944 /*
4945 * Print basic file information available via NtQueryInformationFile.
4946 */
4947 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4948 rcNt = NtQueryInformationFile(hFile, &Ios, &u.BasicInfo, sizeof(u.BasicInfo), FileBasicInformation);
4949 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
4950 {
4951 SUP_DPRINTF((" CreationTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.CreationTime.QuadPart), szTmp, sizeof(szTmp))));
4952 /*SUP_DPRINTF((" LastAccessTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastAccessTime.QuadPart), szTmp, sizeof(szTmp))));*/
4953 SUP_DPRINTF((" LastWriteTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastWriteTime.QuadPart), szTmp, sizeof(szTmp))));
4954 SUP_DPRINTF((" ChangeTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.ChangeTime.QuadPart), szTmp, sizeof(szTmp))));
4955 SUP_DPRINTF((" FileAttributes: %#x\n", u.BasicInfo.FileAttributes));
4956 }
4957 else
4958 SUP_DPRINTF((" FileBasicInformation -> %#x %#x\n", rcNt, Ios.Status));
4959
4960 rcNt = NtQueryInformationFile(hFile, &Ios, &u.StdInfo, sizeof(u.StdInfo), FileStandardInformation);
4961 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
4962 SUP_DPRINTF((" Size: %#llx\n", u.StdInfo.EndOfFile.QuadPart));
4963 else
4964 SUP_DPRINTF((" FileStandardInformation -> %#x %#x\n", rcNt, Ios.Status));
4965
4966 /*
4967 * Read the image header and extract the timestamp and other useful info.
4968 */
4969 RT_ZERO(u);
4970 LARGE_INTEGER offRead;
4971 offRead.QuadPart = 0;
4972 rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
4973 &u, (ULONG)sizeof(u), &offRead, NULL);
4974 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
4975 {
4976 uint32_t offNtHdrs = 0;
4977 if (u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE)
4978 offNtHdrs = u.MzHdr.e_lfanew;
4979 if (offNtHdrs < sizeof(u) - sizeof(IMAGE_NT_HEADERS))
4980 {
4981 PIMAGE_NT_HEADERS64 pNtHdrs64 = (PIMAGE_NT_HEADERS64)&u.abBuf[offNtHdrs];
4982 PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)&u.abBuf[offNtHdrs];
4983 if (pNtHdrs64->Signature == IMAGE_NT_SIGNATURE)
4984 {
4985 SUP_DPRINTF((" NT Headers: %#x\n", offNtHdrs));
4986 SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
4987 SUP_DPRINTF((" Machine: %#x%s\n", pNtHdrs64->FileHeader.Machine,
4988 pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ? " - i386"
4989 : pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ? " - amd64" : ""));
4990 SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
4991 SUP_DPRINTF((" Image Version: %u.%u\n",
4992 pNtHdrs64->OptionalHeader.MajorImageVersion, pNtHdrs64->OptionalHeader.MinorImageVersion));
4993 SUP_DPRINTF((" SizeOfImage: %#x (%u)\n", pNtHdrs64->OptionalHeader.SizeOfImage, pNtHdrs64->OptionalHeader.SizeOfImage));
4994
4995 /*
4996 * Very crude way to extract info from the file version resource.
4997 */
4998 PIMAGE_SECTION_HEADER paSectHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs64->OptionalHeader
4999 + pNtHdrs64->FileHeader.SizeOfOptionalHeader);
5000 IMAGE_DATA_DIRECTORY RsrcDir = { 0, 0 };
5001 if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)
5002 && pNtHdrs64->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
5003 RsrcDir = pNtHdrs64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
5004 else if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
5005 && pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
5006 RsrcDir = pNtHdrs32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
5007 SUP_DPRINTF((" Resource Dir: %#x LB %#x\n", RsrcDir.VirtualAddress, RsrcDir.Size));
5008 if ( RsrcDir.VirtualAddress > offNtHdrs
5009 && RsrcDir.Size > 0
5010 && (uintptr_t)&u + sizeof(u) - (uintptr_t)paSectHdrs
5011 >= pNtHdrs64->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) )
5012 {
5013 offRead.QuadPart = 0;
5014 for (uint32_t i = 0; i < pNtHdrs64->FileHeader.NumberOfSections; i++)
5015 if ( paSectHdrs[i].VirtualAddress - RsrcDir.VirtualAddress < paSectHdrs[i].SizeOfRawData
5016 && paSectHdrs[i].PointerToRawData > offNtHdrs)
5017 {
5018 offRead.QuadPart = paSectHdrs[i].PointerToRawData
5019 + (paSectHdrs[i].VirtualAddress - RsrcDir.VirtualAddress);
5020 break;
5021 }
5022 if (offRead.QuadPart > 0)
5023 {
5024 RT_ZERO(u);
5025 rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
5026 &u, (ULONG)sizeof(u), &offRead, NULL);
5027 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5028 {
5029 static const struct { PCRTUTF16 pwsz; size_t cb; } s_abFields[] =
5030 {
5031#define MY_WIDE_STR_TUPLE(a_sz) { L ## a_sz, sizeof(L ## a_sz) - sizeof(RTUTF16) }
5032 MY_WIDE_STR_TUPLE("ProductName"),
5033 MY_WIDE_STR_TUPLE("ProductVersion"),
5034 MY_WIDE_STR_TUPLE("FileVersion"),
5035 MY_WIDE_STR_TUPLE("SpecialBuild"),
5036 MY_WIDE_STR_TUPLE("PrivateBuild"),
5037 MY_WIDE_STR_TUPLE("FileDescription"),
5038#undef MY_WIDE_STR_TUPLE
5039 };
5040 for (uint32_t i = 0; i < RT_ELEMENTS(s_abFields); i++)
5041 {
5042 size_t cwcLeft = (sizeof(u) - s_abFields[i].cb - 10) / sizeof(RTUTF16);
5043 PCRTUTF16 pwc = u.awcBuf;
5044 RTUTF16 const wcFirst = *s_abFields[i].pwsz;
5045 while (cwcLeft-- > 0)
5046 {
5047 if ( pwc[0] == 1 /* wType == text */
5048 && pwc[1] == wcFirst)
5049 {
5050 if (memcmp(pwc + 1, s_abFields[i].pwsz, s_abFields[i].cb + sizeof(RTUTF16)) == 0)
5051 {
5052 size_t cwcField = s_abFields[i].cb / sizeof(RTUTF16);
5053 pwc += cwcField + 2;
5054 cwcLeft -= cwcField + 2;
5055 for (uint32_t iPadding = 0; iPadding < 3; iPadding++, pwc++, cwcLeft--)
5056 if (*pwc)
5057 break;
5058 int rc = RTUtf16ValidateEncodingEx(pwc, cwcLeft,
5059 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
5060 if (RT_SUCCESS(rc))
5061 SUP_DPRINTF((" %ls:%*s %ls",
5062 s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", pwc));
5063 else
5064 SUP_DPRINTF((" %ls:%*s rc=%Rrc",
5065 s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", rc));
5066
5067 break;
5068 }
5069 }
5070 pwc++;
5071 }
5072 }
5073 }
5074 else
5075 SUP_DPRINTF((" NtReadFile @%#llx -> %#x %#x\n", offRead.QuadPart, rcNt, Ios.Status));
5076 }
5077 else
5078 SUP_DPRINTF((" Resource section not found.\n"));
5079 }
5080 }
5081 else
5082 SUP_DPRINTF((" Nt Headers @%#x: Invalid signature\n", offNtHdrs));
5083 }
5084 else
5085 SUP_DPRINTF((" Nt Headers @%#x: out side buffer\n", offNtHdrs));
5086 }
5087 else
5088 SUP_DPRINTF((" NtReadFile @0 -> %#x %#x\n", rcNt, Ios.Status));
5089 NtClose(hFile);
5090 }
5091}
5092
5093
5094/**
5095 * Scans the Driver directory for drivers which may invade our processes.
5096 *
5097 * @returns Mask of SUPHARDNT_ADVERSARY_XXX flags.
5098 *
5099 * @remarks The enumeration of \Driver normally requires administrator
5100 * privileges. So, the detection we're doing here isn't always gonna
5101 * work just based on that.
5102 *
5103 * @todo Find drivers in \FileSystems as well, then we could detect VrNsdDrv
5104 * from ViRobot APT Shield 2.0.
5105 */
5106static uint32_t supR3HardenedWinFindAdversaries(void)
5107{
5108 static const struct
5109 {
5110 uint32_t fAdversary;
5111 const char *pszDriver;
5112 } s_aDrivers[] =
5113 {
5114 { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, "SysPlant" },
5115
5116 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SRTSPX" },
5117 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymDS" },
5118 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymEvent" },
5119 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymIRON" },
5120 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymNetS" },
5121
5122 { SUPHARDNT_ADVERSARY_AVAST, "aswHwid" },
5123 { SUPHARDNT_ADVERSARY_AVAST, "aswMonFlt" },
5124 { SUPHARDNT_ADVERSARY_AVAST, "aswRdr2" },
5125 { SUPHARDNT_ADVERSARY_AVAST, "aswRvrt" },
5126 { SUPHARDNT_ADVERSARY_AVAST, "aswSnx" },
5127 { SUPHARDNT_ADVERSARY_AVAST, "aswsp" },
5128 { SUPHARDNT_ADVERSARY_AVAST, "aswStm" },
5129 { SUPHARDNT_ADVERSARY_AVAST, "aswVmm" },
5130
5131 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmcomm" },
5132 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmactmon" },
5133 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmevtmgr" },
5134 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmtdi" },
5135 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmebc64" }, /* Titanium internet security, not officescan. */
5136 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmeevw" }, /* Titanium internet security, not officescan. */
5137 { SUPHARDNT_ADVERSARY_TRENDMICRO, "tmciesc" }, /* Titanium internet security, not officescan. */
5138
5139 { SUPHARDNT_ADVERSARY_MCAFEE, "cfwids" },
5140 { SUPHARDNT_ADVERSARY_MCAFEE, "McPvDrv" },
5141 { SUPHARDNT_ADVERSARY_MCAFEE, "mfeapfk" },
5142 { SUPHARDNT_ADVERSARY_MCAFEE, "mfeavfk" },
5143 { SUPHARDNT_ADVERSARY_MCAFEE, "mfefirek" },
5144 { SUPHARDNT_ADVERSARY_MCAFEE, "mfehidk" },
5145 { SUPHARDNT_ADVERSARY_MCAFEE, "mfencbdc" },
5146 { SUPHARDNT_ADVERSARY_MCAFEE, "mfewfpk" },
5147
5148 { SUPHARDNT_ADVERSARY_KASPERSKY, "kl1" },
5149 { SUPHARDNT_ADVERSARY_KASPERSKY, "klflt" },
5150 { SUPHARDNT_ADVERSARY_KASPERSKY, "klif" },
5151 { SUPHARDNT_ADVERSARY_KASPERSKY, "KLIM6" },
5152 { SUPHARDNT_ADVERSARY_KASPERSKY, "klkbdflt" },
5153 { SUPHARDNT_ADVERSARY_KASPERSKY, "klmouflt" },
5154 { SUPHARDNT_ADVERSARY_KASPERSKY, "kltdi" },
5155 { SUPHARDNT_ADVERSARY_KASPERSKY, "kneps" },
5156
5157 { SUPHARDNT_ADVERSARY_MBAM, "MBAMWebAccessControl" },
5158 { SUPHARDNT_ADVERSARY_MBAM, "mbam" },
5159 { SUPHARDNT_ADVERSARY_MBAM, "mbamchameleon" },
5160 { SUPHARDNT_ADVERSARY_MBAM, "mwav" },
5161 { SUPHARDNT_ADVERSARY_MBAM, "mbamswissarmy" },
5162
5163 { SUPHARDNT_ADVERSARY_AVG, "avgfwfd" },
5164 { SUPHARDNT_ADVERSARY_AVG, "avgtdia" },
5165
5166 { SUPHARDNT_ADVERSARY_PANDA, "PSINAflt" },
5167 { SUPHARDNT_ADVERSARY_PANDA, "PSINFile" },
5168 { SUPHARDNT_ADVERSARY_PANDA, "PSINKNC" },
5169 { SUPHARDNT_ADVERSARY_PANDA, "PSINProc" },
5170 { SUPHARDNT_ADVERSARY_PANDA, "PSINProt" },
5171 { SUPHARDNT_ADVERSARY_PANDA, "PSINReg" },
5172 { SUPHARDNT_ADVERSARY_PANDA, "PSKMAD" },
5173 { SUPHARDNT_ADVERSARY_PANDA, "NNSAlpc" },
5174 { SUPHARDNT_ADVERSARY_PANDA, "NNSHttp" },
5175 { SUPHARDNT_ADVERSARY_PANDA, "NNShttps" },
5176 { SUPHARDNT_ADVERSARY_PANDA, "NNSIds" },
5177 { SUPHARDNT_ADVERSARY_PANDA, "NNSNAHSL" },
5178 { SUPHARDNT_ADVERSARY_PANDA, "NNSpicc" },
5179 { SUPHARDNT_ADVERSARY_PANDA, "NNSPihsw" },
5180 { SUPHARDNT_ADVERSARY_PANDA, "NNSPop3" },
5181 { SUPHARDNT_ADVERSARY_PANDA, "NNSProt" },
5182 { SUPHARDNT_ADVERSARY_PANDA, "NNSPrv" },
5183 { SUPHARDNT_ADVERSARY_PANDA, "NNSSmtp" },
5184 { SUPHARDNT_ADVERSARY_PANDA, "NNSStrm" },
5185 { SUPHARDNT_ADVERSARY_PANDA, "NNStlsc" },
5186
5187 { SUPHARDNT_ADVERSARY_MSE, "NisDrv" },
5188
5189 /*{ SUPHARDNT_ADVERSARY_COMODO, "cmdguard" }, file system */
5190 { SUPHARDNT_ADVERSARY_COMODO, "inspect" },
5191 { SUPHARDNT_ADVERSARY_COMODO, "cmdHlp" },
5192
5193 { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN, "dgmaster" }, /* Not verified. */
5194 };
5195
5196 static const struct
5197 {
5198 uint32_t fAdversary;
5199 PCRTUTF16 pwszFile;
5200 } s_aFiles[] =
5201 {
5202 { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\drivers\\SysPlant.sys" },
5203 { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysfer.dll" },
5204 { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysferThunk.dll" },
5205
5206 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ccsetx64.sys" },
5207 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ironx64.sys" },
5208 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtsp64.sys" },
5209 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtspx64.sys" },
5210 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symds64.sys" },
5211 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symefa64.sys" },
5212 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symelam.sys" },
5213 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symnets.sys" },
5214 { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\symevent64x86.sys" },
5215
5216 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswHwid.sys" },
5217 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswMonFlt.sys" },
5218 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRdr2.sys" },
5219 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRvrt.sys" },
5220 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswSnx.sys" },
5221 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswsp.sys" },
5222 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswStm.sys" },
5223 { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswVmm.sys" },
5224
5225 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmcomm.sys" },
5226 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmactmon.sys" },
5227 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmevtmgr.sys" },
5228 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmtdi.sys" },
5229 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmebc64.sys" },
5230 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmeevw.sys" },
5231 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmciesc.sys" },
5232 { SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE, L"\\SystemRoot\\System32\\drivers\\sakfile.sys" }, /* Data Loss Prevention, not officescan. */
5233 { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\sakcd.sys" }, /* Data Loss Prevention, not officescan. */
5234
5235
5236 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\cfwids.sys" },
5237 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\McPvDrv.sys" },
5238 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeapfk.sys" },
5239 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeavfk.sys" },
5240 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfefirek.sys" },
5241 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfehidk.sys" },
5242 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfencbdc.sys" },
5243 { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfewfpk.sys" },
5244
5245 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kl1.sys" },
5246 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klflt.sys" },
5247 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klif.sys" },
5248 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klim6.sys" },
5249 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klkbdflt.sys" },
5250 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klmouflt.sys" },
5251 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kltdi.sys" },
5252 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kneps.sys" },
5253 { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\klfphc.dll" },
5254
5255 { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\MBAMSwissArmy.sys" },
5256 { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mwac.sys" },
5257 { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbamchameleon.sys" },
5258 { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbam.sys" },
5259
5260 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgrkx64.sys" },
5261 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgmfx64.sys" },
5262 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsdrivera.sys" },
5263 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsha.sys" },
5264 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgtdia.sys" },
5265 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgloga.sys" },
5266 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgldx64.sys" },
5267 { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgdiska.sys" },
5268
5269 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINAflt.sys" },
5270 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINFile.sys" },
5271 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINKNC.sys" },
5272 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProc.sys" },
5273 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProt.sys" },
5274 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINReg.sys" },
5275 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSKMAD.sys" },
5276 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSAlpc.sys" },
5277 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSHttp.sys" },
5278 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNShttps.sys" },
5279 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSIds.sys" },
5280 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSNAHSL.sys" },
5281 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSpicc.sys" },
5282 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPihsw.sys" },
5283 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPop3.sys" },
5284 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSProt.sys" },
5285 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPrv.sys" },
5286 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSSmtp.sys" },
5287 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSStrm.sys" },
5288 { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNStlsc.sys" },
5289
5290 { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\MpFilter.sys" },
5291 { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\NisDrvWFP.sys" },
5292
5293 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdguard.sys" },
5294 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmderd.sys" },
5295 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\inspect.sys" },
5296 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdhlp.sys" },
5297 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cfrmd.sys" },
5298 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\hmd.sys" },
5299 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\guard64.dll" },
5300 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdvrt64.dll" },
5301 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdkbd64.dll" },
5302 { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdcsr.dll" },
5303
5304 { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\drivers\\vsdatant.sys" },
5305 { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\AntiTheftCredentialProvider.dll" },
5306
5307 { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN, L"\\SystemRoot\\System32\\drivers\\dgmaster.sys" },
5308 };
5309
5310 uint32_t fFound = 0;
5311
5312 /*
5313 * Open the driver object directory.
5314 */
5315 UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
5316
5317 OBJECT_ATTRIBUTES ObjAttr;
5318 InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
5319
5320 HANDLE hDir;
5321 NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
5322#ifdef VBOX_STRICT
5323 SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
5324#endif
5325 if (NT_SUCCESS(rcNt))
5326 {
5327 /*
5328 * Enumerate it, looking for the driver.
5329 */
5330 ULONG uObjDirCtx = 0;
5331 for (;;)
5332 {
5333 uint32_t abBuffer[_64K + _1K];
5334 ULONG cbActual;
5335 rcNt = NtQueryDirectoryObject(hDir,
5336 abBuffer,
5337 sizeof(abBuffer) - 4, /* minus four for string terminator space. */
5338 FALSE /*ReturnSingleEntry */,
5339 FALSE /*RestartScan*/,
5340 &uObjDirCtx,
5341 &cbActual);
5342 if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
5343 break;
5344
5345 POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
5346 while (pObjDir->Name.Length != 0)
5347 {
5348 WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
5349 pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
5350
5351 for (uint32_t i = 0; i < RT_ELEMENTS(s_aDrivers); i++)
5352 if (RTUtf16ICmpAscii(pObjDir->Name.Buffer, s_aDrivers[i].pszDriver) == 0)
5353 {
5354 fFound |= s_aDrivers[i].fAdversary;
5355 SUP_DPRINTF(("Found driver %s (%#x)\n", s_aDrivers[i].pszDriver, s_aDrivers[i].fAdversary));
5356 break;
5357 }
5358
5359 pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
5360
5361 /* Next directory entry. */
5362 pObjDir++;
5363 }
5364 }
5365
5366 NtClose(hDir);
5367 }
5368 else
5369 SUP_DPRINTF(("NtOpenDirectoryObject failed on \\Driver: %#x\n", rcNt));
5370
5371 /*
5372 * Look for files.
5373 */
5374 for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
5375 {
5376 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
5377 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
5378 UNICODE_STRING UniStrName;
5379 UniStrName.Buffer = (WCHAR *)s_aFiles[i].pwszFile;
5380 UniStrName.Length = (USHORT)(RTUtf16Len(s_aFiles[i].pwszFile) * sizeof(WCHAR));
5381 UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
5382 InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
5383 rcNt = NtCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &ObjAttr, &Ios, NULL /* Allocation Size*/,
5384 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
5385 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*EaBuffer*/, 0 /*EaLength*/);
5386 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5387 {
5388 fFound |= s_aFiles[i].fAdversary;
5389 NtClose(hFile);
5390 }
5391 }
5392
5393 /*
5394 * Log details.
5395 */
5396 SUP_DPRINTF(("supR3HardenedWinFindAdversaries: %#x\n", fFound));
5397 for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
5398 if (fFound & s_aFiles[i].fAdversary)
5399 supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, true /* fAdversarial */);
5400
5401 return fFound;
5402}
5403
5404
5405extern "C" int main(int argc, char **argv, char **envp);
5406
5407/**
5408 * The executable entry point.
5409 *
5410 * This is normally taken care of by the C runtime library, but we don't want to
5411 * get involved with anything as complicated like the CRT in this setup. So, we
5412 * it everything ourselves, including parameter parsing.
5413 */
5414extern "C" void __stdcall suplibHardenedWindowsMain(void)
5415{
5416 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
5417
5418 g_cSuplibHardenedWindowsMainCalls++;
5419 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED;
5420
5421 /*
5422 * Initialize the NTDLL API wrappers. This aims at bypassing patched NTDLL
5423 * in all the processes leading up the VM process.
5424 */
5425 supR3HardenedWinInitImports();
5426 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED;
5427
5428 /*
5429 * Notify the parent process that we're probably capable of reporting our
5430 * own errors.
5431 */
5432 if (g_ProcParams.hEvtParent || g_ProcParams.hEvtChild)
5433 {
5434 SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit);
5435
5436 g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents;
5437 NtSetEvent(g_ProcParams.hEvtParent, NULL);
5438
5439 NtClose(g_ProcParams.hEvtParent);
5440 NtClose(g_ProcParams.hEvtChild);
5441 g_ProcParams.hEvtParent = NULL;
5442 g_ProcParams.hEvtChild = NULL;
5443 }
5444 else
5445 SUPR3HARDENED_ASSERT(!g_fSupEarlyProcessInit);
5446
5447 /*
5448 * After having resolved imports we patch the LdrInitializeThunk code so
5449 * that it's more difficult to invade our privacy by CreateRemoteThread.
5450 * We'll re-enable this after opening the driver or temporarily while respawning.
5451 */
5452 supR3HardenedWinDisableThreadCreation();
5453
5454 /*
5455 * Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
5456 * SUPHardenedVerfiyImage-win.cpp.)
5457 */
5458 supR3HardenedWinInitVersion();
5459 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERSION_INITIALIZED;
5460
5461 /*
5462 * Convert the arguments to UTF-8 and open the log file if specified.
5463 * This must be done as early as possible since the code below may fail.
5464 */
5465 PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
5466 int cArgs;
5467 char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs);
5468
5469 supR3HardenedOpenLog(&cArgs, papszArgs);
5470
5471 /*
5472 * Log information about important system files.
5473 */
5474 supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\ntdll.dll", false /* fAdversarial */);
5475 supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\kernel32.dll", false /* fAdversarial */);
5476 supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\KernelBase.dll", false /* fAdversarial */);
5477 supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\apisetschema.dll", false /* fAdversarial */);
5478
5479 /*
5480 * Scan the system for adversaries, logging information about them.
5481 */
5482 g_fSupAdversaries = supR3HardenedWinFindAdversaries();
5483
5484 /*
5485 * Get the executable name, make sure it's the long version.
5486 */
5487 DWORD cwcExecName = GetModuleFileNameW(GetModuleHandleW(NULL), g_wszSupLibHardenedExePath,
5488 RT_ELEMENTS(g_wszSupLibHardenedExePath));
5489 if (cwcExecName >= RT_ELEMENTS(g_wszSupLibHardenedExePath))
5490 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, VERR_BUFFER_OVERFLOW,
5491 "The executable path is too long.");
5492
5493 RTUTF16 wszLong[RT_ELEMENTS(g_wszSupLibHardenedExePath)];
5494 DWORD cwcLong = GetLongPathNameW(g_wszSupLibHardenedExePath, wszLong, RT_ELEMENTS(wszLong));
5495 if (cwcLong > 0)
5496 {
5497 memcpy(g_wszSupLibHardenedExePath, wszLong, (cwcLong + 1) * sizeof(RTUTF16));
5498 cwcExecName = cwcLong;
5499 }
5500
5501 /* The NT version of it. */
5502 HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
5503 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
5504 if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
5505 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(RtlGetLastWin32Error()),
5506 "Error opening the executable: %u (%ls).", RtlGetLastWin32Error());
5507 RT_ZERO(g_SupLibHardenedExeNtPath);
5508 ULONG cbIgn;
5509 NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &g_SupLibHardenedExeNtPath,
5510 sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbIgn);
5511 if (!NT_SUCCESS(rcNt))
5512 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
5513 "NtQueryObject -> %#x (on %ls)\n", rcNt, g_wszSupLibHardenedExePath);
5514 NtClose(hFile);
5515
5516 /* The NT executable name offset / dir path length. */
5517 g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
5518 while ( g_offSupLibHardenedExeNtName > 1
5519 && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
5520 g_offSupLibHardenedExeNtName--;
5521
5522 /*
5523 * Preliminary app binary path init. May change when SUPR3HardenedMain is
5524 * called (via main below).
5525 */
5526 supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
5527
5528 /*
5529 * If we've done early init already, register the DLL load notification
5530 * callback and reinstall the NtDll patches.
5531 */
5532 if (g_fSupEarlyProcessInit)
5533 {
5534 supR3HardenedWinRegisterDllNotificationCallback();
5535 supR3HardenedWinReInstallHooks(false /*fFirstCall */);
5536 }
5537
5538 /*
5539 * Call the C/C++ main function.
5540 */
5541 SUP_DPRINTF(("Calling main()\n"));
5542 rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
5543
5544 /*
5545 * Exit the process (never return).
5546 */
5547 SUP_DPRINTF(("Terminating the normal way: rcExit=%d\n", rcExit));
5548 suplibHardenedExit(rcExit);
5549}
5550
5551
5552/**
5553 * Reports an error to the parent process via the process parameter structure.
5554 *
5555 * @param pszWhere Where this error occured, if fatal message. NULL
5556 * if not message.
5557 * @param enmWhat Which init operation went wrong if fatal
5558 * message. kSupInitOp_Invalid if not message.
5559 * @param rc The status code to report.
5560 * @param pszFormat The format string.
5561 * @param va The format arguments.
5562 */
5563DECLHIDDEN(void) supR3HardenedWinReportErrorToParent(const char *pszWhere, SUPINITOP enmWhat, int rc,
5564 const char *pszFormat, va_list va)
5565{
5566 if (pszWhere)
5567 RTStrCopy(g_ProcParams.szWhere, sizeof(g_ProcParams.szWhere), pszWhere);
5568 else
5569 g_ProcParams.szWhere[0] = '\0';
5570 RTStrPrintfV(g_ProcParams.szErrorMsg, sizeof(g_ProcParams.szErrorMsg), pszFormat, va);
5571 g_ProcParams.enmWhat = enmWhat;
5572 g_ProcParams.rc = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc;
5573 g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
5574
5575 NtClearEvent(g_ProcParams.hEvtChild);
5576 NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL);
5577 if (NT_SUCCESS(rcNt))
5578 {
5579 LARGE_INTEGER Timeout;
5580 Timeout.QuadPart = -300000000; /* 30 second */
5581 NTSTATUS rcNt = NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
5582 }
5583}
5584
5585
5586/**
5587 * Routine called by the supR3HardenedEarlyProcessInitThunk assembly routine
5588 * when LdrInitializeThunk is executed in during process initialization.
5589 *
5590 * This initializes the Stub and VM processes, hooking NTDLL APIs and opening
5591 * the device driver before any other DLLs gets loaded into the process. This
5592 * greately reduces and controls the trusted code base of the process compared
5593 * to opening the driver from SUPR3HardenedMain. It also avoids issues with so
5594 * call protection software that is in the habit of patching half of the ntdll
5595 * and kernel32 APIs in the process, making it almost indistinguishable from
5596 * software that is up to no good. Once we've opened vboxdrv, the process
5597 * should be locked down so thighly that only kernel software and csrss can mess
5598 * with the process.
5599 */
5600DECLASM(uintptr_t) supR3HardenedEarlyProcessInit(void)
5601{
5602 /*
5603 * When the first thread gets here we wait for the parent to continue with
5604 * the process purifications. The primary thread must execute for image
5605 * load notifications to trigger, at least in more recent windows versions.
5606 * The old trick of starting a different thread that terminates immediately
5607 * thus doesn't work.
5608 *
5609 * We are not allowed to modify any data at this point because it will be
5610 * reset by the child process purification the parent does when we stop. To
5611 * sabotage thread creation during purification, and to avoid unnecessary
5612 * work for the parent, we reset g_ProcParams before signalling the parent
5613 * here.
5614 */
5615 if (g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
5616 {
5617 NtTerminateThread(0, 0);
5618 return 0x22; /* crash */
5619 }
5620
5621 /* Retrieve the data we need. */
5622 uintptr_t uNtDllAddr = ASMAtomicXchgPtrT(&g_ProcParams.uNtDllAddr, 0, uintptr_t);
5623 if (!RT_VALID_PTR(uNtDllAddr))
5624 {
5625 NtTerminateThread(0, 0);
5626 return 0x23; /* crash */
5627 }
5628
5629 HANDLE hEvtChild = g_ProcParams.hEvtChild;
5630 HANDLE hEvtParent = g_ProcParams.hEvtParent;
5631 if ( hEvtChild == NULL
5632 || hEvtChild == RTNT_INVALID_HANDLE_VALUE
5633 || hEvtParent == NULL
5634 || hEvtParent == RTNT_INVALID_HANDLE_VALUE)
5635 {
5636 NtTerminateThread(0, 0);
5637 return 0x24; /* crash */
5638 }
5639
5640 /* Resolve the APIs we need. */
5641 PFNNTWAITFORSINGLEOBJECT pfnNtWaitForSingleObject;
5642 PFNNTSETEVENT pfnNtSetEvent;
5643 supR3HardenedWinGetVeryEarlyImports(uNtDllAddr, &pfnNtWaitForSingleObject, &pfnNtSetEvent);
5644
5645 /* Signal the parent that we're ready for purification. */
5646 RT_ZERO(g_ProcParams);
5647 g_ProcParams.enmRequest = kSupR3WinChildReq_PurifyChildAndCloseHandles;
5648 NTSTATUS rcNt = pfnNtSetEvent(hEvtParent, NULL);
5649 if (rcNt != STATUS_SUCCESS)
5650 return 0x33; /* crash */
5651
5652 /* Wait up to 2 mins for the parent to exorcise evil. */
5653 LARGE_INTEGER Timeout;
5654 Timeout.QuadPart = -1200000000; /* 120 second */
5655 rcNt = pfnNtWaitForSingleObject(hEvtChild, FALSE /*Alertable*/, &Timeout);
5656 if (rcNt != STATUS_SUCCESS)
5657 return 0x34; /* crash */
5658
5659 /*
5660 * We're good to go, work global state and restore process parameters.
5661 * Note that we will not restore uNtDllAddr since that is our first defence
5662 * against unwanted threads (see above).
5663 */
5664 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED;
5665 g_fSupEarlyProcessInit = true;
5666
5667 g_ProcParams.hEvtChild = hEvtChild;
5668 g_ProcParams.hEvtParent = hEvtParent;
5669 g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
5670 g_ProcParams.rc = VINF_SUCCESS;
5671
5672 /*
5673 * Initialize the NTDLL imports that we consider usable before the
5674 * process has been initialized.
5675 */
5676 supR3HardenedWinInitImportsEarly(uNtDllAddr);
5677 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED;
5678
5679 /*
5680 * Init g_uNtVerCombined as well as we can at this point.
5681 */
5682 supR3HardenedWinInitVersion();
5683
5684 /*
5685 * Convert the arguments to UTF-8 so we can open the log file if specified.
5686 * We may have to normalize the pointer on older windows version (not w7/64 +).
5687 * Note! This leaks memory at present.
5688 */
5689 PRTL_USER_PROCESS_PARAMETERS pUserProcParams = NtCurrentPeb()->ProcessParameters;
5690 UNICODE_STRING CmdLineStr = pUserProcParams->CommandLine;
5691 if ( CmdLineStr.Buffer != NULL
5692 && !(pUserProcParams->Flags & RTL_USER_PROCESS_PARAMS_FLAG_NORMALIZED) )
5693 CmdLineStr.Buffer = (WCHAR *)((uintptr_t)CmdLineStr.Buffer + (uintptr_t)pUserProcParams);
5694 int cArgs;
5695 char **papszArgs = suplibCommandLineToArgvWStub(CmdLineStr.Buffer, CmdLineStr.Length / sizeof(WCHAR), &cArgs);
5696 supR3HardenedOpenLog(&cArgs, papszArgs);
5697 SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", uNtDllAddr));
5698
5699 /*
5700 * Set up the direct system calls so we can more easily hook NtCreateSection.
5701 */
5702 supR3HardenedWinInitSyscalls(true /*fReportErrors*/);
5703
5704 /*
5705 * Determine the executable path and name. Will NOT determine the windows style
5706 * executable path here as we don't need it.
5707 */
5708 SIZE_T cbActual = 0;
5709 rcNt = NtQueryVirtualMemory(NtCurrentProcess(), &g_ProcParams, MemorySectionName, &g_SupLibHardenedExeNtPath,
5710 sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbActual);
5711 if ( !NT_SUCCESS(rcNt)
5712 || g_SupLibHardenedExeNtPath.UniStr.Length == 0
5713 || g_SupLibHardenedExeNtPath.UniStr.Length & 1)
5714 supR3HardenedFatal("NtQueryVirtualMemory/MemorySectionName failed in supR3HardenedVmProcessInit: %#x\n", rcNt);
5715
5716 /* The NT executable name offset / dir path length. */
5717 g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
5718 while ( g_offSupLibHardenedExeNtName > 1
5719 && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
5720 g_offSupLibHardenedExeNtName--;
5721
5722 /*
5723 * Preliminary app binary path init. May change when SUPR3HardenedMain is called.
5724 */
5725 supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
5726
5727 /*
5728 * Initialize the image verification stuff (hooks LdrLoadDll and NtCreateSection).
5729 */
5730 supR3HardenedWinInit(0, false /*fAvastKludge*/);
5731
5732 /*
5733 * Open the driver.
5734 */
5735 if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
5736 {
5737 SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv stub...\n"));
5738 supR3HardenedWinOpenStubDevice();
5739 }
5740 else if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
5741 {
5742 SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv...\n"));
5743 supR3HardenedMainOpenDevice();
5744 }
5745 else
5746 supR3HardenedFatal("Unexpected first argument '%s'!\n", papszArgs[0]);
5747 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_DEVICE_OPENED;
5748
5749 /*
5750 * Reinstall the NtDll patches since there is a slight possibility that
5751 * someone undid them while we where busy opening the device.
5752 */
5753 supR3HardenedWinReInstallHooks(false /*fFirstCall */);
5754
5755 /*
5756 * Restore the LdrInitializeThunk code so we can initialize the process
5757 * normally when we return.
5758 */
5759 SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n"));
5760 PSUPHNTLDRCACHEENTRY pLdrEntry;
5761 int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
5762 if (RT_FAILURE(rc))
5763 supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
5764
5765 uint8_t *pbBits;
5766 rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
5767 if (RT_FAILURE(rc))
5768 supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
5769
5770 RTLDRADDR uValue;
5771 rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
5772 if (RT_FAILURE(rc))
5773 supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
5774
5775 PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
5776 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
5777 memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - uNtDllAddr), 16);
5778 SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
5779
5780 SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n"));
5781 return (uintptr_t)pvLdrInitThunk;
5782}
5783
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette