VirtualBox

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

Revision 67550, 267.5 KB checked in by vboxsync, 4 months ago (diff)

HostDrivers/Support/SUPR3HardenedMain-win: bugref:8810: version tweak

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

www.oracle.com
ContactPrivacy policyTerms of Use