VirtualBox

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

Revision 67981, 277.4 KB checked in by vboxsync, 3 months ago (diff)

Windows 7+

  • 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    bool fNeedUncChecking = false;
1571    if (   hFile != NULL
1572        && hFile != INVALID_HANDLE_VALUE)
1573    {
1574        bool const fImage    = RT_BOOL(fAttribs & (SEC_IMAGE | SEC_PROTECTED_IMAGE));
1575        bool const fExecMap  = RT_BOOL(fAccess & SECTION_MAP_EXECUTE);
1576        bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
1577                                                   | PAGE_EXECUTE_READWRITE));
1578        if (fImage || fExecMap || fExecProt)
1579        {
1580            fNeedUncChecking = true;
1581            DWORD dwSavedLastError = RtlGetLastWin32Error();
1582
1583            bool fCallRealApi;
1584            //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 1\n"));
1585            NTSTATUS rcNt = supR3HardenedScreenImage(hFile, fImage, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
1586                                                     "NtCreateSection", true /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
1587            //SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 2 rcNt=%#x fCallRealApi=%#x\n", rcNt, fCallRealApi));
1588
1589            RtlRestoreLastWin32Error(dwSavedLastError);
1590
1591            if (!NT_SUCCESS(rcNt))
1592                return rcNt;
1593            Assert(fCallRealApi);
1594            if (!fCallRealApi)
1595                return STATUS_TRUST_FAILURE;
1596
1597        }
1598    }
1599
1600    /*
1601     * Call checked out OK, call the original.
1602     */
1603    NTSTATUS rcNtReal = g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
1604
1605    /*
1606     * Check that the image that got mapped bear some resemblance to the one that was
1607     * requested.  Apparently there are ways to trick the NT cache manager to map a
1608     * file different from hFile into memory using local UNC accesses.
1609     */
1610    if (   NT_SUCCESS(rcNtReal)
1611        && fNeedUncChecking)
1612    {
1613        DWORD dwSavedLastError = RtlGetLastWin32Error();
1614
1615        bool fOkay = false;
1616
1617        /* To get the name of the file backing the section, we unfortunately have to map it. */
1618        SIZE_T   cbView   = 0;
1619        PVOID    pvTmpMap = NULL;
1620        NTSTATUS rcNt = NtMapViewOfSection(*phSection, NtCurrentProcess(), &pvTmpMap, 0, 0, NULL /*poffSection*/, &cbView,
1621                                           ViewUnmap, MEM_TOP_DOWN, PAGE_EXECUTE);
1622        if (NT_SUCCESS(rcNt))
1623        {
1624            /* Query the name. */
1625            union
1626            {
1627                UNICODE_STRING  UniStr;
1628                RTUTF16         awcBuf[512];
1629            } uBuf;
1630            RT_ZERO(uBuf);
1631            SIZE_T   cbActual = 0;
1632            NTSTATUS rcNtQuery = NtQueryVirtualMemory(NtCurrentProcess(), pvTmpMap, MemorySectionName,
1633                                                      &uBuf, sizeof(uBuf) - sizeof(RTUTF16), &cbActual);
1634
1635            /* Unmap the view. */
1636            rcNt = NtUnmapViewOfSection(NtCurrentProcess(), pvTmpMap);
1637            if (!NT_SUCCESS(rcNt))
1638                SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtUnmapViewOfSection failed on %p (hSection=%p, hFile=%p) with %#x!\n",
1639                             pvTmpMap, *phSection, hFile, rcNt));
1640
1641            /* Process the name query result. */
1642            if (NT_SUCCESS(rcNtQuery))
1643            {
1644                static UNICODE_STRING const s_UncPrefix = RTNT_CONSTANT_UNISTR(L"\\Device\\Mup");
1645                if (!supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &s_UncPrefix, true /*fCheckSlash*/))
1646                    fOkay = true;
1647                else
1648                    supR3HardenedError(VINF_SUCCESS, false,
1649                                       "supR3HardenedMonitor_NtCreateSection: Image section with UNC path is not trusted: '%.*ls'\n",
1650                                       uBuf.UniStr.Length / sizeof(RTUTF16), uBuf.UniStr.Buffer);
1651            }
1652            else
1653                SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtQueryVirtualMemory failed on %p (hFile=%p) with %#x -> STATUS_TRUST_FAILURE\n",
1654                             *phSection, hFile, rcNt));
1655        }
1656        else
1657            SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: NtMapViewOfSection failed on %p (hFile=%p) with %#x -> STATUS_TRUST_FAILURE\n",
1658                         *phSection, hFile, rcNt));
1659        if (!fOkay)
1660        {
1661            NtClose(*phSection);
1662            *phSection = INVALID_HANDLE_VALUE;
1663            RtlRestoreLastWin32Error(dwSavedLastError);
1664            return STATUS_TRUST_FAILURE;
1665        }
1666
1667        RtlRestoreLastWin32Error(dwSavedLastError);
1668    }
1669    return rcNtReal;
1670}
1671
1672
1673/**
1674 * Checks if the given name is a valid ApiSet name.
1675 *
1676 * This is only called on likely looking names.
1677 *
1678 * @returns true if ApiSet name, false if not.
1679 * @param   pName               The name to check out.
1680 */
1681static bool supR3HardenedIsApiSetDll(PUNICODE_STRING pName)
1682{
1683    /*
1684     * API added in Windows 8, or so they say.
1685     */
1686    if (ApiSetQueryApiSetPresence != NULL)
1687    {
1688        BOOLEAN fPresent = FALSE;
1689        NTSTATUS rcNt = ApiSetQueryApiSetPresence(pName, &fPresent);
1690        SUP_DPRINTF(("supR3HardenedIsApiSetDll: ApiSetQueryApiSetPresence(%.*ls) -> %#x, fPresent=%d\n",
1691                     pName->Length / sizeof(WCHAR), pName->Buffer, rcNt, fPresent));
1692        return fPresent != 0;
1693    }
1694
1695    /*
1696     * Fallback needed for Windows 7.  Fortunately, there aren't too many fake DLLs here.
1697     */
1698    if (   g_uNtVerCombined >= SUP_NT_VER_W70
1699        && (   supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
1700                                              L"api-ms-win-", 11, false /*fCheckSlash*/)
1701            || supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
1702                                              L"ext-ms-win-", 11, false /*fCheckSlash*/) ))
1703    {
1704#define MY_ENTRY(a) { a, sizeof(a) - 1 }
1705        static const struct { const char *psz; size_t cch; } s_aKnownSets[] =
1706        {
1707            MY_ENTRY("api-ms-win-core-console-l1-1-0 "),
1708            MY_ENTRY("api-ms-win-core-datetime-l1-1-0"),
1709            MY_ENTRY("api-ms-win-core-debug-l1-1-0"),
1710            MY_ENTRY("api-ms-win-core-delayload-l1-1-0"),
1711            MY_ENTRY("api-ms-win-core-errorhandling-l1-1-0"),
1712            MY_ENTRY("api-ms-win-core-fibers-l1-1-0"),
1713            MY_ENTRY("api-ms-win-core-file-l1-1-0"),
1714            MY_ENTRY("api-ms-win-core-handle-l1-1-0"),
1715            MY_ENTRY("api-ms-win-core-heap-l1-1-0"),
1716            MY_ENTRY("api-ms-win-core-interlocked-l1-1-0"),
1717            MY_ENTRY("api-ms-win-core-io-l1-1-0"),
1718            MY_ENTRY("api-ms-win-core-libraryloader-l1-1-0"),
1719            MY_ENTRY("api-ms-win-core-localization-l1-1-0"),
1720            MY_ENTRY("api-ms-win-core-localregistry-l1-1-0"),
1721            MY_ENTRY("api-ms-win-core-memory-l1-1-0"),
1722            MY_ENTRY("api-ms-win-core-misc-l1-1-0"),
1723            MY_ENTRY("api-ms-win-core-namedpipe-l1-1-0"),
1724            MY_ENTRY("api-ms-win-core-processenvironment-l1-1-0"),
1725            MY_ENTRY("api-ms-win-core-processthreads-l1-1-0"),
1726            MY_ENTRY("api-ms-win-core-profile-l1-1-0"),
1727            MY_ENTRY("api-ms-win-core-rtlsupport-l1-1-0"),
1728            MY_ENTRY("api-ms-win-core-string-l1-1-0"),
1729            MY_ENTRY("api-ms-win-core-synch-l1-1-0"),
1730            MY_ENTRY("api-ms-win-core-sysinfo-l1-1-0"),
1731            MY_ENTRY("api-ms-win-core-threadpool-l1-1-0"),
1732            MY_ENTRY("api-ms-win-core-ums-l1-1-0"),
1733            MY_ENTRY("api-ms-win-core-util-l1-1-0"),
1734            MY_ENTRY("api-ms-win-core-xstate-l1-1-0"),
1735            MY_ENTRY("api-ms-win-security-base-l1-1-0"),
1736            MY_ENTRY("api-ms-win-security-lsalookup-l1-1-0"),
1737            MY_ENTRY("api-ms-win-security-sddl-l1-1-0"),
1738            MY_ENTRY("api-ms-win-service-core-l1-1-0"),
1739            MY_ENTRY("api-ms-win-service-management-l1-1-0"),
1740            MY_ENTRY("api-ms-win-service-management-l2-1-0"),
1741            MY_ENTRY("api-ms-win-service-winsvc-l1-1-0"),
1742        };
1743#undef MY_ENTRY
1744
1745        /* drop the dll suffix if present. */
1746        PCRTUTF16 pawcName = pName->Buffer;
1747        size_t    cwcName  = pName->Length / sizeof(WCHAR);
1748        if (   cwcName > 5
1749            && (pawcName[cwcName - 1] == 'l' || pawcName[cwcName - 1] == 'L')
1750            && (pawcName[cwcName - 2] == 'l' || pawcName[cwcName - 2] == 'L')
1751            && (pawcName[cwcName - 3] == 'd' || pawcName[cwcName - 3] == 'D')
1752            &&  pawcName[cwcName - 4] == '.')
1753            cwcName -= 4;
1754
1755        /* Search the table. */
1756        for (size_t i = 0; i < RT_ELEMENTS(s_aKnownSets); i++)
1757            if (   cwcName == s_aKnownSets[i].cch
1758                && RTUtf16NICmpAscii(pawcName, s_aKnownSets[i].psz, cwcName) == 0)
1759            {
1760                SUP_DPRINTF(("supR3HardenedIsApiSetDll: '%.*ls' -> true\n", pName->Length / sizeof(WCHAR)));
1761                return true;
1762            }
1763
1764        SUP_DPRINTF(("supR3HardenedIsApiSetDll: Warning! '%.*ls' looks like an API set, but it's not in the list!\n",
1765                     pName->Length / sizeof(WCHAR), pName->Buffer));
1766    }
1767
1768    SUP_DPRINTF(("supR3HardenedIsApiSetDll: '%.*ls' -> false\n", pName->Length / sizeof(WCHAR)));
1769    return false;
1770}
1771
1772
1773/**
1774 * Checks whether the given unicode string contains a path separator and at
1775 * least one dash.
1776 *
1777 * This is used to check for likely ApiSet name.  So far, all the pseudo DLL
1778 * names include multiple dashes, so we use that as a criteria for recognizing
1779 * them.  By happy coincident, most regular DLLs doesn't include dashes.
1780 *
1781 * @returns true if it contains path separator, false if only a name.
1782 * @param   pPath               The path to check.
1783 */
1784static bool supR3HardenedHasDashButNoPath(PUNICODE_STRING pPath)
1785{
1786    size_t    cDashes = 0;
1787    size_t    cwcLeft = pPath->Length / sizeof(WCHAR);
1788    PCRTUTF16 pwc     = pPath->Buffer;
1789    while (cwcLeft-- > 0)
1790    {
1791        RTUTF16 wc = *pwc++;
1792        switch (wc)
1793        {
1794            default:
1795                break;
1796
1797            case '-':
1798                cDashes++;
1799                break;
1800
1801            case '\\':
1802            case '/':
1803            case ':':
1804                return false;
1805        }
1806    }
1807    return cDashes > 0;
1808}
1809
1810
1811/**
1812 * Helper for supR3HardenedMonitor_LdrLoadDll.
1813 *
1814 * @returns NT status code.
1815 * @param   pwszPath        The path destination buffer.
1816 * @param   cwcPath         The size of the path buffer.
1817 * @param   pUniStrResult   The result string.
1818 * @param   pOrgName        The orignal name (for errors).
1819 * @param   pcwc            Where to return the actual length.
1820 */
1821static NTSTATUS supR3HardenedCopyRedirectionResult(WCHAR *pwszPath, size_t cwcPath, PUNICODE_STRING pUniStrResult,
1822                                                   PUNICODE_STRING pOrgName, UINT *pcwc)
1823{
1824    UINT cwc;
1825    *pcwc = cwc = pUniStrResult->Length / sizeof(WCHAR);
1826    if (pUniStrResult->Buffer == pwszPath)
1827        pwszPath[cwc] = '\0';
1828    else
1829    {
1830        if (cwc > cwcPath - 1)
1831        {
1832            supR3HardenedError(VINF_SUCCESS, false,
1833                               "supR3HardenedMonitor_LdrLoadDll: Name too long: %.*ls -> %.*ls (RtlDosApplyFileIoslationRedirection_Ustr)\n",
1834                               pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer,
1835                               pUniStrResult->Length / sizeof(WCHAR), pUniStrResult->Buffer);
1836            return STATUS_NAME_TOO_LONG;
1837        }
1838        memcpy(&pwszPath[0], pUniStrResult->Buffer, pUniStrResult->Length);
1839        pwszPath[cwc] = '\0';
1840    }
1841    return STATUS_SUCCESS;
1842}
1843
1844
1845/**
1846 * Helper for supR3HardenedMonitor_LdrLoadDll that compares the name part of the
1847 * input path against a ASCII name string of a given length.
1848 *
1849 * @returns true if the name part matches
1850 * @param   pPath               The LdrLoadDll input path.
1851 * @param   pszName             The name to try match it with.
1852 * @param   cchName             The name length.
1853 */
1854static bool supR3HardenedIsFilenameMatchDll(PUNICODE_STRING pPath, const char *pszName, size_t cchName)
1855{
1856    if (pPath->Length < cchName * 2)
1857        return false;
1858    PCRTUTF16 pwszTmp = &pPath->Buffer[pPath->Length / sizeof(RTUTF16) - cchName];
1859    if (   pPath->Length != cchName
1860        && pwszTmp[-1] != '\\'
1861        && pwszTmp[-1] != '/')
1862        return false;
1863    return RTUtf16ICmpAscii(pwszTmp, pszName) == 0;
1864}
1865
1866
1867/**
1868 * Hooks that intercepts LdrLoadDll calls.
1869 *
1870 * Two purposes:
1871 *      -# Enforce our own search path restrictions.
1872 *      -# Prevalidate DLLs about to be loaded so we don't upset the loader data
1873 *         by doing it from within the NtCreateSection hook (WinVerifyTrust
1874 *         seems to be doing harm there on W7/32).
1875 *
1876 * @returns
1877 * @param   pwszSearchPath      The search path to use.
1878 * @param   pfFlags             Flags on input. DLL characteristics or something
1879 *                              on return?
1880 * @param   pName               The name of the module.
1881 * @param   phMod               Where the handle of the loaded DLL is to be
1882 *                              returned to the caller.
1883 */
1884static NTSTATUS NTAPI
1885supR3HardenedMonitor_LdrLoadDll(PWSTR pwszSearchPath, PULONG pfFlags, PUNICODE_STRING pName, PHANDLE phMod)
1886{
1887    DWORD                   dwSavedLastError = RtlGetLastWin32Error();
1888    PUNICODE_STRING const   pOrgName = pName;
1889    NTSTATUS                rcNt;
1890
1891    /*
1892     * Make sure the DLL notification callback is registered.  If we could, we
1893     * would've done this during early process init, but due to lack of heap
1894     * and uninitialized loader lock, it's not possible that early on.
1895     *
1896     * The callback protects our NtDll hooks from getting unhooked by
1897     * "friendly" fire from the AV crowd.
1898     */
1899    supR3HardenedWinRegisterDllNotificationCallback();
1900
1901    /*
1902     * Process WinVerifyTrust todo before and after.
1903     */
1904    supR3HardenedWinVerifyCacheProcessWvtTodos();
1905
1906    /*
1907     * Reject things we don't want to deal with.
1908     */
1909    if (!pName || pName->Length == 0)
1910    {
1911        supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: name is NULL or have a zero length.\n");
1912        SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x (pName=%p)\n", STATUS_INVALID_PARAMETER, pName));
1913        RtlRestoreLastWin32Error(dwSavedLastError);
1914        return STATUS_INVALID_PARAMETER;
1915    }
1916    PCWCHAR const  pawcOrgName = pName->Buffer;
1917    uint32_t const cwcOrgName  = pName->Length / sizeof(WCHAR);
1918
1919    /*SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls *pfFlags=%#x pwszSearchPath=%p:%ls\n",
1920                 (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
1921                 !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));*/
1922
1923    /*
1924     * Reject long paths that's close to the 260 limit without looking.
1925     */
1926    if (cwcOrgName > 256)
1927    {
1928        supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: too long name: %#x bytes\n", pName->Length);
1929        SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
1930        RtlRestoreLastWin32Error(dwSavedLastError);
1931        return STATUS_NAME_TOO_LONG;
1932    }
1933
1934    /*
1935     * Reject all UNC-like paths as we cannot trust non-local files at all.
1936     * Note! We may have to relax this to deal with long path specifications and NT pass thrus.
1937     */
1938    if (   cwcOrgName >= 3
1939        && RTPATH_IS_SLASH(pawcOrgName[0])
1940        && RTPATH_IS_SLASH(pawcOrgName[1])
1941        && !RTPATH_IS_SLASH(pawcOrgName[2]))
1942    {
1943        supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting UNC name '%.*ls'\n", cwcOrgName, pawcOrgName);
1944        SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_REDIRECTOR_NOT_STARTED));
1945        RtlRestoreLastWin32Error(dwSavedLastError);
1946        return STATUS_REDIRECTOR_NOT_STARTED;
1947    }
1948
1949    /*
1950     * Reject PGHook.dll as it creates a thread from its DllMain that breaks
1951     * our preconditions respawning the 2nd process, resulting in
1952     * VERR_SUP_VP_THREAD_NOT_ALONE.   The DLL is being loaded by a user APC
1953     * scheduled during kernel32.dll load notification from a kernel driver,
1954     * so failing the load attempt should not upset anyone.
1955     */
1956    if (g_enmSupR3HardenedMainState == SUPR3HARDENEDMAINSTATE_WIN_EARLY_STUB_DEVICE_OPENED)
1957    {
1958        static const struct { const char *psz; size_t cch; } s_aUnwantedEarlyDlls[] =
1959        {
1960            { RT_STR_TUPLE("PGHook.dll") },
1961        };
1962        for (unsigned i = 0; i < RT_ELEMENTS(s_aUnwantedEarlyDlls); i++)
1963            if (supR3HardenedIsFilenameMatchDll(pName, s_aUnwantedEarlyDlls[i].psz, s_aUnwantedEarlyDlls[i].cch))
1964            {
1965                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",
1966                             pName->Length / sizeof(RTUTF16), pName->Buffer));
1967                return STATUS_TOO_MANY_THREADS;
1968            }
1969    }
1970
1971    /*
1972     * Resolve the path, copying the result into wszPath
1973     */
1974    NTSTATUS        rcNtResolve     = STATUS_SUCCESS;
1975    bool            fSkipValidation = false;
1976    bool            fCheckIfLoaded  = false;
1977    WCHAR           wszPath[260];
1978    static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
1979    UNICODE_STRING  UniStrStatic   = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
1980    UNICODE_STRING  UniStrDynamic  = { 0, 0, NULL };
1981    PUNICODE_STRING pUniStrResult  = NULL;
1982    UNICODE_STRING  ResolvedName;
1983
1984    /*
1985     * Process the name a little, checking if it needs a DLL suffix and is pathless.
1986     */
1987    uint32_t        offLastSlash = UINT32_MAX;
1988    uint32_t        offLastDot   = UINT32_MAX;
1989    for (uint32_t i = 0; i < cwcOrgName; i++)
1990        switch (pawcOrgName[i])
1991        {
1992            case '\\':
1993            case '/':
1994                offLastSlash = i;
1995                offLastDot = UINT32_MAX;
1996                break;
1997            case '.':
1998                offLastDot = i;
1999                break;
2000        }
2001    bool const fNeedDllSuffix = offLastDot == UINT32_MAX;
2002    //bool const fTrailingDot   = offLastDot == cwcOrgName - 1;
2003
2004    /*
2005     * Absolute path?
2006     */
2007    if (   (   cwcOrgName >= 4
2008            && RT_C_IS_ALPHA(pawcOrgName[0])
2009            && pawcOrgName[1] == ':'
2010            && RTPATH_IS_SLASH(pawcOrgName[2]) )
2011        || (   cwcOrgName >= 1
2012            && RTPATH_IS_SLASH(pawcOrgName[0]) )
2013       )
2014    {
2015        rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
2016                                                               pName,
2017                                                               (PUNICODE_STRING)&s_DefaultSuffix,
2018                                                               &UniStrStatic,
2019                                                               &UniStrDynamic,
2020                                                               &pUniStrResult,
2021                                                               NULL /*pNewFlags*/,
2022                                                               NULL /*pcbFilename*/,
2023                                                               NULL /*pcbNeeded*/);
2024        if (NT_SUCCESS(rcNtResolve))
2025        {
2026            UINT cwc;
2027            rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
2028            RtlFreeUnicodeString(&UniStrDynamic);
2029            if (!NT_SUCCESS(rcNt))
2030            {
2031                SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
2032                RtlRestoreLastWin32Error(dwSavedLastError);
2033                return rcNt;
2034            }
2035
2036            ResolvedName.Buffer = wszPath;
2037            ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
2038            ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
2039
2040            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: '%.*ls' -> '%.*ls' [redir]\n",
2041                         (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
2042                         ResolvedName.Length / sizeof(WCHAR), ResolvedName.Buffer, rcNt));
2043            pName = &ResolvedName;
2044        }
2045        else
2046        {
2047            /* Copy the path. */
2048            memcpy(wszPath, pawcOrgName, cwcOrgName * sizeof(WCHAR));
2049            if (!fNeedDllSuffix)
2050                wszPath[cwcOrgName] = '\0';
2051            else
2052            {
2053                if (cwcOrgName + 4 >= RT_ELEMENTS(wszPath))
2054                {
2055                    supR3HardenedError(VINF_SUCCESS, false,
2056                                       "supR3HardenedMonitor_LdrLoadDll: Name too long (abs): %.*ls\n", cwcOrgName, pawcOrgName);
2057                    SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
2058                    RtlRestoreLastWin32Error(dwSavedLastError);
2059                    return STATUS_NAME_TOO_LONG;
2060                }
2061                memcpy(&wszPath[cwcOrgName], L".dll", 5 * sizeof(WCHAR));
2062            }
2063        }
2064    }
2065    /*
2066     * Not an absolute path.  Check if it's one of those special API set DLLs
2067     * or something we're known to use but should be taken from WinSxS.
2068     */
2069    else if (   supR3HardenedHasDashButNoPath(pName)
2070             && supR3HardenedIsApiSetDll(pName))
2071    {
2072        memcpy(wszPath, pName->Buffer, pName->Length);
2073        wszPath[pName->Length / sizeof(WCHAR)] = '\0';
2074        fSkipValidation = true;
2075    }
2076    /*
2077     * Not an absolute path or special API set.  There are two alternatives
2078     * now, either there is no path at all or there is a relative path.  We
2079     * will resolve it to an absolute path in either case, failing the call
2080     * if we can't.
2081     */
2082    else
2083    {
2084        /*
2085         * Reject relative paths for now as they might be breakout attempts.
2086         */
2087        if (offLastSlash != UINT32_MAX)
2088        {
2089            supR3HardenedError(VINF_SUCCESS, false,
2090                               "supR3HardenedMonitor_LdrLoadDll: relative name not permitted: %.*ls\n",
2091                               cwcOrgName, pawcOrgName);
2092            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
2093            RtlRestoreLastWin32Error(dwSavedLastError);
2094            return STATUS_OBJECT_NAME_INVALID;
2095        }
2096
2097        /*
2098         * Perform dll redirection to WinSxS such.  We using an undocumented
2099         * API here, which as always is a bit risky...  ASSUMES that the API
2100         * returns a full DOS path.
2101         */
2102        UINT cwc;
2103        rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
2104                                                               pName,
2105                                                               (PUNICODE_STRING)&s_DefaultSuffix,
2106                                                               &UniStrStatic,
2107                                                               &UniStrDynamic,
2108                                                               &pUniStrResult,
2109                                                               NULL /*pNewFlags*/,
2110                                                               NULL /*pcbFilename*/,
2111                                                               NULL /*pcbNeeded*/);
2112        if (NT_SUCCESS(rcNtResolve))
2113        {
2114            rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
2115            RtlFreeUnicodeString(&UniStrDynamic);
2116            if (!NT_SUCCESS(rcNt))
2117            {
2118                SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
2119                RtlRestoreLastWin32Error(dwSavedLastError);
2120                return rcNt;
2121            }
2122        }
2123        else
2124        {
2125            /*
2126             * Search for the DLL.  Only System32 is allowed as the target of
2127             * a search on the API level, all VBox calls will have full paths.
2128             * If the DLL is not in System32, we will resort to check if it's
2129             * refering to an already loaded DLL (fCheckIfLoaded).
2130             */
2131            AssertCompile(sizeof(g_System32WinPath.awcBuffer) <= sizeof(wszPath));
2132            cwc = g_System32WinPath.UniStr.Length / sizeof(RTUTF16); Assert(cwc > 2);
2133            if (cwc + 1 + cwcOrgName + fNeedDllSuffix * 4 >= RT_ELEMENTS(wszPath))
2134            {
2135                supR3HardenedError(VINF_SUCCESS, false,
2136                                   "supR3HardenedMonitor_LdrLoadDll: Name too long (system32): %.*ls\n", cwcOrgName, pawcOrgName);
2137                SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
2138                RtlRestoreLastWin32Error(dwSavedLastError);
2139                return STATUS_NAME_TOO_LONG;
2140            }
2141            memcpy(wszPath, g_System32WinPath.UniStr.Buffer, cwc * sizeof(RTUTF16));
2142            wszPath[cwc++] = '\\';
2143            memcpy(&wszPath[cwc], pawcOrgName, cwcOrgName * sizeof(WCHAR));
2144            cwc += cwcOrgName;
2145            if (!fNeedDllSuffix)
2146                wszPath[cwc] = '\0';
2147            else
2148            {
2149                memcpy(&wszPath[cwc], L".dll", 5 * sizeof(WCHAR));
2150                cwc += 4;
2151            }
2152            fCheckIfLoaded = true;
2153        }
2154
2155        ResolvedName.Buffer = wszPath;
2156        ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
2157        ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
2158        pName = &ResolvedName;
2159    }
2160
2161#ifndef IN_SUP_R3_STATIC
2162    /*
2163     * Reject blacklisted DLLs based on input name.
2164     */
2165    for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
2166        if (supR3HardenedIsFilenameMatchDll(pName, g_aSupNtViBlacklistedDlls[i].psz, g_aSupNtViBlacklistedDlls[i].cch))
2167        {
2168            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: Refusing to load blacklisted DLL: '%.*ls'\n",
2169                         pName->Length / sizeof(RTUTF16), pName->Buffer));
2170            RtlRestoreLastWin32Error(dwSavedLastError);
2171            return STATUS_TOO_MANY_THREADS;
2172        }
2173#endif
2174
2175    bool fQuiet = false;
2176    if (!fSkipValidation)
2177    {
2178        /*
2179         * Try open the file.  If this fails, never mind, just pass it on to
2180         * the real API as we've replaced any searchable name with a full name
2181         * and the real API can come up with a fitting status code for it.
2182         */
2183        HANDLE          hRootDir;
2184        UNICODE_STRING  NtPathUniStr;
2185        int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, wszPath, RTSTR_MAX);
2186        if (RT_FAILURE(rc))
2187        {
2188            supR3HardenedError(rc, false,
2189                               "supR3HardenedMonitor_LdrLoadDll: RTNtPathFromWinUtf16Ex failed on '%ls': %Rrc\n", wszPath, rc);
2190            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
2191            RtlRestoreLastWin32Error(dwSavedLastError);
2192            return STATUS_OBJECT_NAME_INVALID;
2193        }
2194
2195        HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
2196        IO_STATUS_BLOCK     Ios   = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2197        OBJECT_ATTRIBUTES   ObjAttr;
2198        InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
2199
2200        rcNt = NtCreateFile(&hFile,
2201                            FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2202                            &ObjAttr,
2203                            &Ios,
2204                            NULL /* Allocation Size*/,
2205                            FILE_ATTRIBUTE_NORMAL,
2206                            FILE_SHARE_READ,
2207                            FILE_OPEN,
2208                            FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2209                            NULL /*EaBuffer*/,
2210                            0 /*EaLength*/);
2211        if (NT_SUCCESS(rcNt))
2212            rcNt = Ios.Status;
2213        if (NT_SUCCESS(rcNt))
2214        {
2215            ULONG fAccess = 0;
2216            ULONG fProtect = 0;
2217            bool  fCallRealApi = false;
2218            rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, RT_VALID_PTR(pfFlags) && (*pfFlags & 0x2) /*fIgnoreArch*/,
2219                                            &fAccess, &fProtect, &fCallRealApi,
2220                                            "LdrLoadDll", false /*fAvoidWinVerifyTrust*/, &fQuiet);
2221            NtClose(hFile);
2222            if (!NT_SUCCESS(rcNt))
2223            {
2224                if (!fQuiet)
2225                {
2226                    if (pOrgName != pName)
2227                        supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls': rcNt=%#x\n",
2228                                           wszPath, rcNt);
2229                    else
2230                        supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls' (%.*ls): rcNt=%#x\n",
2231                                           wszPath, pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNt);
2232                    SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
2233                }
2234                RtlRestoreLastWin32Error(dwSavedLastError);
2235                return rcNt;
2236            }
2237
2238            supR3HardenedWinVerifyCacheProcessImportTodos();
2239        }
2240        else
2241        {
2242            DWORD dwErr = RtlGetLastWin32Error();
2243
2244            /*
2245             * Deal with special case where the caller (first case was MS LifeCam)
2246             * is using LoadLibrary instead of GetModuleHandle to find a loaded DLL.
2247             */
2248            NTSTATUS rcNtGetDll = STATUS_SUCCESS;
2249            if (   fCheckIfLoaded
2250                 && (   rcNt == STATUS_OBJECT_NAME_NOT_FOUND
2251                     || rcNt == STATUS_OBJECT_PATH_NOT_FOUND))
2252            {
2253                rcNtGetDll = LdrGetDllHandle(NULL /*DllPath*/, NULL /*pfFlags*/, pOrgName, phMod);
2254                if (NT_SUCCESS(rcNtGetDll))
2255                {
2256                    RTNtPathFree(&NtPathUniStr, &hRootDir);
2257                    RtlRestoreLastWin32Error(dwSavedLastError);
2258                    return rcNtGetDll;
2259                }
2260            }
2261
2262            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u (NtPath=%.*ls; Input=%.*ls; rcNtGetDll=%#x\n",
2263                         wszPath, dwErr, NtPathUniStr.Length / sizeof(RTUTF16), NtPathUniStr.Buffer,
2264                         pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtGetDll));
2265
2266            RTNtPathFree(&NtPathUniStr, &hRootDir);
2267            RtlRestoreLastWin32Error(dwSavedLastError);
2268            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
2269            return rcNt;
2270        }
2271        RTNtPathFree(&NtPathUniStr, &hRootDir);
2272    }
2273
2274    /*
2275     * Screened successfully enough.  Call the real thing.
2276     */
2277    if (!fQuiet)
2278    {
2279        if (pOrgName != pName)
2280            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (Input=%.*ls, rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
2281                         (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
2282                         (unsigned)pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtResolve,
2283                         pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
2284                         !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
2285        else
2286            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
2287                         (unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, rcNtResolve,
2288                         pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
2289                         !((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
2290    }
2291
2292    RtlRestoreLastWin32Error(dwSavedLastError);
2293    rcNt = g_pfnLdrLoadDllReal(pwszSearchPath, pfFlags, pName, phMod);
2294
2295    /*
2296     * Log the result and process pending WinVerifyTrust work if we can.
2297     */
2298    dwSavedLastError = RtlGetLastWin32Error();
2299
2300    if (NT_SUCCESS(rcNt) && phMod)
2301        SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x hMod=%p '%ls'\n", rcNt, *phMod, wszPath));
2302    else if (!NT_SUCCESS(rcNt) || !fQuiet)
2303        SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
2304
2305    supR3HardenedWinVerifyCacheProcessWvtTodos();
2306
2307    RtlRestoreLastWin32Error(dwSavedLastError);
2308
2309    return rcNt;
2310}
2311
2312
2313/**
2314 * DLL load and unload notification callback.
2315 *
2316 * This is a safety against our LdrLoadDll hook being replaced by protection
2317 * software.  Though, we prefer the LdrLoadDll hook to this one as it allows us
2318 * to call WinVerifyTrust more freely.
2319 *
2320 * @param   ulReason    The reason we're called, see
2321 *                      LDR_DLL_NOTIFICATION_REASON_XXX.
2322 * @param   pData       Reason specific data.  (Format is currently the same for
2323 *                      both load and unload.)
2324 * @param   pvUser      User parameter (ignored).
2325 *
2326 * @remarks Vista and later.
2327 * @remarks The loader lock is held when we're called, at least on Windows 7.
2328 */
2329static VOID CALLBACK supR3HardenedDllNotificationCallback(ULONG ulReason, PCLDR_DLL_NOTIFICATION_DATA pData, PVOID pvUser)
2330{
2331    NOREF(pvUser);
2332
2333    /*
2334     * Screen the image on load.  We will normally get a verification cache
2335     * hit here because of the LdrLoadDll and NtCreateSection hooks, so it
2336     * should be relatively cheap to recheck.  In case our NtDll patches
2337     * got re
2338     *
2339     * This ASSUMES that we get informed after the fact as indicated by the
2340     * available documentation.
2341     */
2342    if (ulReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
2343    {
2344        SUP_DPRINTF(("supR3HardenedDllNotificationCallback: load   %p LB %#010x %.*ls [fFlags=%#x]\n",
2345                     pData->Loaded.DllBase, pData->Loaded.SizeOfImage,
2346                     pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2347                     pData->Loaded.Flags));
2348
2349        /* Convert the windows path to an NT path and open it. */
2350        HANDLE          hRootDir;
2351        UNICODE_STRING  NtPathUniStr;
2352        int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, pData->Loaded.FullDllName->Buffer,
2353                                        pData->Loaded.FullDllName->Length / sizeof(WCHAR));
2354        if (RT_FAILURE(rc))
2355        {
2356            supR3HardenedFatal("supR3HardenedDllNotificationCallback: RTNtPathFromWinUtf16Ex failed on '%.*ls': %Rrc\n",
2357                               pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer, rc);
2358            return;
2359        }
2360
2361        HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
2362        IO_STATUS_BLOCK     Ios   = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2363        OBJECT_ATTRIBUTES   ObjAttr;
2364        InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
2365
2366        NTSTATUS rcNt = NtCreateFile(&hFile,
2367                                     FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2368                                     &ObjAttr,
2369                                     &Ios,
2370                                     NULL /* Allocation Size*/,
2371                                     FILE_ATTRIBUTE_NORMAL,
2372                                     FILE_SHARE_READ,
2373                                     FILE_OPEN,
2374                                     FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2375                                     NULL /*EaBuffer*/,
2376                                     0 /*EaLength*/);
2377        if (NT_SUCCESS(rcNt))
2378            rcNt = Ios.Status;
2379        if (!NT_SUCCESS(rcNt))
2380        {
2381            supR3HardenedFatal("supR3HardenedDllNotificationCallback: NtCreateFile failed on '%.*ls' / '%.*ls': %#x\n",
2382                               pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2383                               NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
2384            /* not reached */
2385        }
2386
2387        /* Do the screening. */
2388        ULONG fAccess = 0;
2389        ULONG fProtect = 0;
2390        bool  fCallRealApi = false;
2391        bool  fQuietFailure = false;
2392        rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
2393                                        "LdrLoadDll", true /*fAvoidWinVerifyTrust*/, &fQuietFailure);
2394        NtClose(hFile);
2395        if (!NT_SUCCESS(rcNt))
2396        {
2397            supR3HardenedFatal("supR3HardenedDllNotificationCallback: supR3HardenedScreenImage failed on '%.*ls' / '%.*ls': %#x\n",
2398                               pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
2399                               NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
2400            /* not reached */
2401        }
2402        RTNtPathFree(&NtPathUniStr, &hRootDir);
2403    }
2404    /*
2405     * Log the unload call.
2406     */
2407    else if (ulReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
2408    {
2409        SUP_DPRINTF(("supR3HardenedDllNotificationCallback: Unload %p LB %#010x %.*ls [flags=%#x]\n",
2410                     pData->Unloaded.DllBase, pData->Unloaded.SizeOfImage,
2411                     pData->Unloaded.FullDllName->Length / sizeof(WCHAR), pData->Unloaded.FullDllName->Buffer,
2412                     pData->Unloaded.Flags));
2413    }
2414    /*
2415     * Just log things we don't know and then return without caching anything.
2416     */
2417    else
2418    {
2419        static uint32_t s_cLogEntries = 0;
2420        if (s_cLogEntries++ < 32)
2421            SUP_DPRINTF(("supR3HardenedDllNotificationCallback: ulReason=%u pData=%p\n", ulReason, pData));
2422        return;
2423    }
2424
2425    /*
2426     * Use this opportunity to make sure our NtDll patches are still in place,
2427     * since they may be replaced by indecent protection software solutions.
2428     */
2429    supR3HardenedWinReInstallHooks(false /*fFirstCall */);
2430}
2431
2432
2433/**
2434 * Registers the DLL notification callback if it hasn't already been registered.
2435 */
2436static void supR3HardenedWinRegisterDllNotificationCallback(void)
2437{
2438    /*
2439     * The notification API was added in Vista, so it's an optional (weak) import.
2440     */
2441    if (   LdrRegisterDllNotification != NULL
2442        && g_cDllNotificationRegistered <= 0
2443        && g_cDllNotificationRegistered > -32)
2444    {
2445        NTSTATUS rcNt = LdrRegisterDllNotification(0, supR3HardenedDllNotificationCallback, NULL, &g_pvDllNotificationCookie);
2446        if (NT_SUCCESS(rcNt))
2447        {
2448            SUP_DPRINTF(("Registered Dll notification callback with NTDLL.\n"));
2449            g_cDllNotificationRegistered = 1;
2450        }
2451        else
2452        {
2453            supR3HardenedError(rcNt, false /*fFatal*/, "LdrRegisterDllNotification failed: %#x\n", rcNt);
2454            g_cDllNotificationRegistered--;
2455        }
2456    }
2457}
2458
2459
2460static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
2461{
2462    supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
2463                          "Failed to install %s monitor: %x %x %x %x  %x %x %x %x  %x %x %x %x  %x %x %x %x\n "
2464#ifdef RT_ARCH_X86
2465                          "(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
2466#endif
2467                          ,
2468                          pszWhich,
2469                          pbPrologue[0],  pbPrologue[1],  pbPrologue[2],  pbPrologue[3],
2470                          pbPrologue[4],  pbPrologue[5],  pbPrologue[6],  pbPrologue[7],
2471                          pbPrologue[8],  pbPrologue[9],  pbPrologue[10], pbPrologue[11],
2472                          pbPrologue[12], pbPrologue[13], pbPrologue[14], pbPrologue[15]);
2473}
2474
2475
2476/**
2477 * IPRT thread that waits for the parent process to terminate and reacts by
2478 * exiting the current process.
2479 *
2480 * @returns VINF_SUCCESS
2481 * @param   hSelf               The current thread.  Ignored.
2482 * @param   pvUser              The handle of the parent process.
2483 */
2484static DECLCALLBACK(int) supR3HardenedWinParentWatcherThread(RTTHREAD hSelf, void *pvUser)
2485{
2486    HANDLE hProcWait = (HANDLE)pvUser;
2487    NOREF(hSelf);
2488
2489    /*
2490     * Wait for the parent to terminate.
2491     */
2492    NTSTATUS rcNt;
2493    for (;;)
2494    {
2495        rcNt = NtWaitForSingleObject(hProcWait, TRUE /*Alertable*/, NULL /*pTimeout*/);
2496        if (   rcNt == STATUS_WAIT_0
2497            || rcNt == STATUS_ABANDONED_WAIT_0)
2498            break;
2499        if (   rcNt != STATUS_TIMEOUT
2500            && rcNt != STATUS_USER_APC
2501            && rcNt != STATUS_ALERTED)
2502            supR3HardenedFatal("NtWaitForSingleObject returned %#x\n", rcNt);
2503    }
2504
2505    /*
2506     * Proxy the termination code of the child, if it exited already.
2507     */
2508    PROCESS_BASIC_INFORMATION BasicInfo;
2509    NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
2510    if (   !NT_SUCCESS(rcNt2)
2511        || BasicInfo.ExitStatus == STATUS_PENDING)
2512        BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
2513
2514    NtClose(hProcWait);
2515    SUP_DPRINTF(("supR3HardenedWinParentWatcherThread: Quitting: ExitCode=%#x rcNt=%#x\n", BasicInfo.ExitStatus, rcNt));
2516    suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
2517    /* not reached */
2518}
2519
2520
2521/**
2522 * Creates the parent watcher thread that will make sure this process exits when
2523 * the parent does.
2524 *
2525 * This is a necessary evil to make VBoxNetDhcp and VBoxNetNat termination from
2526 * Main work without too much new magic.  It also makes Ctrl-C or similar work
2527 * in on the hardened processes in the windows console.
2528 *
2529 * @param   hVBoxRT             The VBoxRT.dll handle.  We use RTThreadCreate to
2530 *                              spawn the thread to avoid duplicating thread
2531 *                              creation and thread naming code from IPRT.
2532 */
2533DECLHIDDEN(void) supR3HardenedWinCreateParentWatcherThread(HMODULE hVBoxRT)
2534{
2535    /*
2536     * Resolve runtime methods that we need.
2537     */
2538    PFNRTTHREADCREATE pfnRTThreadCreate = (PFNRTTHREADCREATE)GetProcAddress(hVBoxRT, "RTThreadCreate");
2539    SUPR3HARDENED_ASSERT(pfnRTThreadCreate != NULL);
2540
2541    /*
2542     * Find the parent process ID.
2543     */
2544    PROCESS_BASIC_INFORMATION BasicInfo;
2545    NTSTATUS rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
2546    if (!NT_SUCCESS(rcNt))
2547        supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: NtQueryInformationProcess failed: %#x\n", rcNt);
2548
2549    /*
2550     * Open the parent process for waiting and exitcode query.
2551     */
2552    OBJECT_ATTRIBUTES ObjAttr;
2553    InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2554
2555    CLIENT_ID ClientId;
2556    ClientId.UniqueProcess = (HANDLE)BasicInfo.InheritedFromUniqueProcessId;
2557    ClientId.UniqueThread  = NULL;
2558
2559    HANDLE hParent;
2560    rcNt = NtOpenProcess(&hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
2561    if (!NT_SUCCESS(rcNt))
2562        supR3HardenedFatalMsg("supR3HardenedWinCreateParentWatcherThread", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2563                              "NtOpenProcess(%p.0) failed: %#x\n", ClientId.UniqueProcess, rcNt);
2564
2565    /*
2566     * Create the thread that should do the waiting.
2567     */
2568    int rc = pfnRTThreadCreate(NULL, supR3HardenedWinParentWatcherThread, hParent, _64K /* stack */,
2569                               RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ParentWatcher");
2570    if (RT_FAILURE(rc))
2571        supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: RTThreadCreate failed: %Rrc\n", rc);
2572}
2573
2574
2575/**
2576 * Checks if the calling thread is the only one in the process.
2577 *
2578 * @returns true if we're positive we're alone, false if not.
2579 */
2580static bool supR3HardenedWinAmIAlone(void)
2581{
2582    ULONG    fAmIAlone = 0;
2583    ULONG    cbIgn     = 0;
2584    NTSTATUS rcNt = NtQueryInformationThread(NtCurrentThread(), ThreadAmILastThread, &fAmIAlone, sizeof(fAmIAlone), &cbIgn);
2585    Assert(NT_SUCCESS(rcNt));
2586    return NT_SUCCESS(rcNt) && fAmIAlone != 0;
2587}
2588
2589
2590/**
2591 * Simplify NtProtectVirtualMemory interface.
2592 *
2593 * Modifies protection for the current process.  Caller must know the current
2594 * protection as it's not returned.
2595 *
2596 * @returns NT status code.
2597 * @param   pvMem               The memory to change protection for.
2598 * @param   cbMem               The amount of memory to change.
2599 * @param   fNewProt            The new protection.
2600 */
2601static NTSTATUS supR3HardenedWinProtectMemory(PVOID pvMem, SIZE_T cbMem, ULONG fNewProt)
2602{
2603    ULONG fOldProt = 0;
2604    return NtProtectVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, fNewProt, &fOldProt);
2605}
2606
2607
2608/**
2609 * Installs or reinstalls the NTDLL patches.
2610 */
2611static void supR3HardenedWinReInstallHooks(bool fFirstCall)
2612{
2613    struct
2614    {
2615        size_t          cbPatch;
2616        uint8_t const  *pabPatch;
2617        uint8_t       **ppbApi;
2618        const char     *pszName;
2619    } const s_aPatches[] =
2620    {
2621        { sizeof(g_abNtCreateSectionPatch), g_abNtCreateSectionPatch, &g_pbNtCreateSection, "NtCreateSection" },
2622        { sizeof(g_abLdrLoadDllPatch),      g_abLdrLoadDllPatch,      &g_pbLdrLoadDll,      "LdrLoadDll"      },
2623    };
2624
2625    ULONG fAmIAlone = ~(ULONG)0;
2626
2627    for (uint32_t i = 0; i < RT_ELEMENTS(s_aPatches); i++)
2628    {
2629        uint8_t *pbApi = *s_aPatches[i].ppbApi;
2630        if (memcmp(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch) != 0)
2631        {
2632            /*
2633             * Log the incident if it's not the initial call.
2634             */
2635            static uint32_t volatile s_cTimes = 0;
2636            if (!fFirstCall && s_cTimes < 128)
2637            {
2638                s_cTimes++;
2639                SUP_DPRINTF(("supR3HardenedWinReInstallHooks: Reinstalling %s (%p: %.*Rhxs).\n",
2640                             s_aPatches[i].pszName, pbApi, s_aPatches[i].cbPatch, pbApi));
2641            }
2642
2643            Assert(s_aPatches[i].cbPatch >= 4);
2644
2645            SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READWRITE));
2646
2647            /*
2648             * If we're alone, just memcpy the patch in.
2649             */
2650
2651            if (fAmIAlone == ~(ULONG)0)
2652                fAmIAlone = supR3HardenedWinAmIAlone();
2653            if (fAmIAlone)
2654                memcpy(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch);
2655            else
2656            {
2657                /*
2658                 * Not alone.  Start by injecting a JMP $-2, then waste some
2659                 * CPU cycles to get the other threads a good chance of getting
2660                 * out of the code before we replace it.
2661                 */
2662                RTUINT32U uJmpDollarMinus;
2663                uJmpDollarMinus.au8[0] = 0xeb;
2664                uJmpDollarMinus.au8[1] = 0xfe;
2665                uJmpDollarMinus.au8[2] = pbApi[2];
2666                uJmpDollarMinus.au8[3] = pbApi[3];
2667                ASMAtomicXchgU32((uint32_t volatile *)pbApi, uJmpDollarMinus.u);
2668
2669                NtYieldExecution();
2670                NtYieldExecution();
2671
2672                /* Copy in the tail bytes of the patch, then xchg the jmp $-2. */
2673                if (s_aPatches[i].cbPatch > 4)
2674                    memcpy(&pbApi[4], &s_aPatches[i].pabPatch[4], s_aPatches[i].cbPatch - 4);
2675                ASMAtomicXchgU32((uint32_t volatile *)pbApi, *(uint32_t *)s_aPatches[i].pabPatch);
2676            }
2677
2678            SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READ));
2679        }
2680    }
2681}
2682
2683
2684/**
2685 * Install hooks for intercepting calls dealing with mapping shared libraries
2686 * into the process.
2687 *
2688 * This allows us to prevent undesirable shared libraries from being loaded.
2689 *
2690 * @remarks We assume we're alone in this process, so no seralizing trickery is
2691 *          necessary when installing the patch.
2692 *
2693 * @remarks We would normally just copy the prologue sequence somewhere and add
2694 *          a jump back at the end of it. But because we wish to avoid
2695 *          allocating executable memory, we need to have preprepared assembly
2696 *          "copies".  This makes the non-system call patching a little tedious
2697 *            and inflexible.
2698 */
2699static void supR3HardenedWinInstallHooks(void)
2700{
2701    NTSTATUS rcNt;
2702
2703    /*
2704     * Disable hard error popups so we can quietly refuse images to be loaded.
2705     */
2706    ULONG fHardErr = 0;
2707    rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr), NULL);
2708    if (!NT_SUCCESS(rcNt))
2709        supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2710                              "NtQueryInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
2711    if (fHardErr & PROCESS_HARDERR_CRITICAL_ERROR)
2712    {
2713        fHardErr &= ~PROCESS_HARDERR_CRITICAL_ERROR;
2714        rcNt = NtSetInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr));
2715        if (!NT_SUCCESS(rcNt))
2716            supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
2717                                  "NtSetInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
2718    }
2719
2720    /*
2721     * Locate the routines first so we can allocate memory that's near enough.
2722     */
2723    PFNRT pfnNtCreateSection = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtCreateSection");
2724    SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
2725    //SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
2726
2727    PFNRT pfnLdrLoadDll = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "LdrLoadDll");
2728    SUPR3HARDENED_ASSERT(pfnLdrLoadDll != NULL);
2729    //SUPR3HARDENED_ASSERT(pfnLdrLoadDll == (FARPROC)LdrLoadDll);
2730
2731    /*
2732     * Exec page setup & management.
2733     */
2734    uint32_t offExecPage = 0;
2735    memset(g_abSupHardReadWriteExecPage, 0xcc, PAGE_SIZE);
2736
2737    /*
2738     * Hook #1 - NtCreateSection.
2739     * Purpose: Validate everything that can be mapped into the process before
2740     *          it's mapped and we still have a file handle to work with.
2741     */
2742    uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
2743    g_pbNtCreateSection = pbNtCreateSection;
2744    memcpy(g_abNtCreateSectionPatch, pbNtCreateSection, sizeof(g_abNtCreateSectionPatch));
2745
2746    g_pfnNtCreateSectionReal = NtCreateSection; /* our direct syscall */
2747
2748#ifdef RT_ARCH_AMD64
2749    /*
2750     * Patch 64-bit hosts.
2751     */
2752    /* Pattern #1: XP64/W2K3-64 thru Windows 8.1
2753       0:000> u ntdll!NtCreateSection
2754       ntdll!NtCreateSection:
2755       00000000`779f1750 4c8bd1          mov     r10,rcx
2756       00000000`779f1753 b847000000      mov     eax,47h
2757       00000000`779f1758 0f05            syscall
2758       00000000`779f175a c3              ret
2759       00000000`779f175b 0f1f440000      nop     dword ptr [rax+rax]
2760       The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
2761
2762    /* Assemble the patch. */
2763    g_abNtCreateSectionPatch[0]  = 0x48; /* mov rax, qword */
2764    g_abNtCreateSectionPatch[1]  = 0xb8;
2765    *(uint64_t *)&g_abNtCreateSectionPatch[2] = (uint64_t)supR3HardenedMonitor_NtCreateSection;
2766    g_abNtCreateSectionPatch[10] = 0xff; /* jmp rax */
2767    g_abNtCreateSectionPatch[11] = 0xe0;
2768
2769#else
2770    /*
2771     * Patch 32-bit hosts.
2772     */
2773    /* Pattern #1: XP thru Windows 7
2774            kd> u ntdll!NtCreateSection
2775            ntdll!NtCreateSection:
2776            7c90d160 b832000000      mov     eax,32h
2777            7c90d165 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
2778            7c90d16a ff12            call    dword ptr [edx]
2779            7c90d16c c21c00          ret     1Ch
2780            7c90d16f 90              nop
2781       The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
2782
2783       Pattern #2: Windows 8.1
2784            0:000:x86> u ntdll_6a0f0000!NtCreateSection
2785            ntdll_6a0f0000!NtCreateSection:
2786            6a15eabc b854010000      mov     eax,154h
2787            6a15eac1 e803000000      call    ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
2788            6a15eac6 c21c00          ret     1Ch
2789            6a15eac9 8bd4            mov     edx,esp
2790            6a15eacb 0f34            sysenter
2791            6a15eacd c3              ret
2792       The variable bit is the value loaded into eax: W81=154h */
2793
2794    /* Assemble the patch. */
2795    g_abNtCreateSectionPatch[0] = 0xe9;  /* jmp rel32 */
2796    *(uint32_t *)&g_abNtCreateSectionPatch[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
2797                                              - (uintptr_t)&pbNtCreateSection[1+4];
2798
2799#endif
2800
2801    /*
2802     * Hook #2 - LdrLoadDll
2803     * Purpose: (a) Enforce LdrLoadDll search path constraints, and (b) pre-validate
2804     *          DLLs so we can avoid calling WinVerifyTrust from the first hook,
2805     *          and thus avoiding messing up the loader data on some installations.
2806     *
2807     * This differs from the above function in that is no a system call and
2808     * we're at the mercy of the compiler.
2809     */
2810    uint8_t * const pbLdrLoadDll = (uint8_t *)(uintptr_t)pfnLdrLoadDll;
2811    g_pbLdrLoadDll = pbLdrLoadDll;
2812    memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
2813
2814    DISSTATE Dis;
2815    uint32_t cbInstr;
2816    uint32_t offJmpBack = 0;
2817
2818#ifdef RT_ARCH_AMD64
2819    /*
2820     * Patch 64-bit hosts.
2821     */
2822    /* Just use the disassembler to skip 12 bytes or more. */
2823    while (offJmpBack < 12)
2824    {
2825        cbInstr = 1;
2826        int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
2827        if (   RT_FAILURE(rc)
2828            || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))
2829            || (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */) )
2830            supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
2831        offJmpBack += cbInstr;
2832    }
2833
2834    /* Assemble the code for resuming the call.*/
2835    *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
2836
2837    memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
2838    offExecPage += offJmpBack;
2839
2840    g_abSupHardReadWriteExecPage[offExecPage++] = 0xff; /* jmp qword [$+8 wrt RIP] */
2841    g_abSupHardReadWriteExecPage[offExecPage++] = 0x25;
2842    *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = RT_ALIGN_32(offExecPage + 4, 8) - (offExecPage + 4);
2843    offExecPage = RT_ALIGN_32(offExecPage + 4, 8);
2844    *(uint64_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack];
2845    offExecPage = RT_ALIGN_32(offExecPage + 8, 16);
2846
2847    /* Assemble the LdrLoadDll patch. */
2848    Assert(offJmpBack >= 12);
2849    g_abLdrLoadDllPatch[0]  = 0x48; /* mov rax, qword */
2850    g_abLdrLoadDllPatch[1]  = 0xb8;
2851    *(uint64_t *)&g_abLdrLoadDllPatch[2] = (uint64_t)supR3HardenedMonitor_LdrLoadDll;
2852    g_abLdrLoadDllPatch[10] = 0xff; /* jmp rax */
2853    g_abLdrLoadDllPatch[11] = 0xe0;
2854
2855#else
2856    /*
2857     * Patch 32-bit hosts.
2858     */
2859    /* Just use the disassembler to skip 5 bytes or more. */
2860    while (offJmpBack < 5)
2861    {
2862        cbInstr = 1;
2863        int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
2864        if (   RT_FAILURE(rc)
2865            || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
2866            supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
2867        offJmpBack += cbInstr;
2868    }
2869
2870    /* Assemble the code for resuming the call.*/
2871    *(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
2872
2873    memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
2874    offExecPage += offJmpBack;
2875
2876    g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
2877    *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack]
2878                                                            - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
2879    offExecPage = RT_ALIGN_32(offExecPage + 4, 16);
2880
2881    /* Assemble the LdrLoadDll patch. */
2882    memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
2883    Assert(offJmpBack >= 5);
2884    g_abLdrLoadDllPatch[0] = 0xe9;
2885    *(uint32_t *)&g_abLdrLoadDllPatch[1] = (uintptr_t)supR3HardenedMonitor_LdrLoadDll - (uintptr_t)&pbLdrLoadDll[1+4];
2886#endif
2887
2888    /*
2889     * Seal the rwx page.
2890     */
2891    SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(g_abSupHardReadWriteExecPage, PAGE_SIZE, PAGE_EXECUTE_READ));
2892
2893    /*
2894     * Install the patches.
2895     */
2896    supR3HardenedWinReInstallHooks(true /*fFirstCall*/);
2897}
2898
2899
2900
2901
2902
2903
2904/*
2905 *
2906 * T h r e a d   c r e a t i o n   c o n t r o l
2907 * T h r e a d   c r e a t i o n   c o n t r o l
2908 * T h r e a d   c r e a t i o n   c o n t r o l
2909 *
2910 */
2911
2912
2913/**
2914 * Common code used for child and parent to make new threads exit immediately.
2915 *
2916 * This patches the LdrInitializeThunk code to call NtTerminateThread with
2917 * STATUS_SUCCESS instead of doing the NTDLL initialization.
2918 *
2919 * @returns VBox status code.
2920 * @param   hProcess            The process to do this to.
2921 * @param   pvLdrInitThunk      The address of the LdrInitializeThunk code to
2922 *                              override.
2923 * @param   pvNtTerminateThread The address of the NtTerminateThread function in
2924 *                              the NTDLL instance we're patching.  (Must be +/-
2925 *                              2GB from the thunk code.)
2926 * @param   pabBackup           Where to back up the original instruction bytes
2927 *                              at pvLdrInitThunk.
2928 * @param   cbBackup            The size of the backup area. Must be 16 bytes.
2929 * @param   pErrInfo            Where to return extended error information.
2930 *                              Optional.
2931 */
2932static int supR3HardNtDisableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, void *pvNtTerminateThread,
2933                                              uint8_t *pabBackup, size_t cbBackup, PRTERRINFO pErrInfo)
2934{
2935    SUP_DPRINTF(("supR3HardNtDisableThreadCreation: pvLdrInitThunk=%p pvNtTerminateThread=%p\n", pvLdrInitThunk, pvNtTerminateThread));
2936    SUPR3HARDENED_ASSERT(cbBackup == 16);
2937    SUPR3HARDENED_ASSERT(RT_ABS((intptr_t)pvLdrInitThunk - (intptr_t)pvNtTerminateThread) < 16*_1M);
2938
2939    /*
2940     * Back up the thunk code.
2941     */
2942    SIZE_T  cbIgnored;
2943    NTSTATUS rcNt = NtReadVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
2944    if (!NT_SUCCESS(rcNt))
2945        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2946                             "supR3HardNtDisableThreadCreation: NtReadVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2947
2948    /*
2949     * Cook up replacement code that calls NtTerminateThread.
2950     */
2951    uint8_t abReplacement[16];
2952    memcpy(abReplacement, pabBackup, sizeof(abReplacement));
2953
2954#ifdef RT_ARCH_AMD64
2955    abReplacement[0] = 0x31;    /* xor ecx, ecx */
2956    abReplacement[1] = 0xc9;
2957    abReplacement[2] = 0x31;    /* xor edx, edx */
2958    abReplacement[3] = 0xd2;
2959    abReplacement[4] = 0xe8;    /* call near NtTerminateThread */
2960    *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
2961    abReplacement[9] = 0xcc;    /* int3 */
2962#elif defined(RT_ARCH_X86)
2963    abReplacement[0] = 0x6a;    /* push 0 */
2964    abReplacement[1] = 0x00;
2965    abReplacement[2] = 0x6a;    /* push 0 */
2966    abReplacement[3] = 0x00;
2967    abReplacement[4] = 0xe8;    /* call near NtTerminateThread */
2968    *(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
2969    abReplacement[9] = 0xcc;    /* int3 */
2970#else
2971# error "Unsupported arch."
2972#endif
2973
2974    /*
2975     * Install the replacment code.
2976     */
2977    PVOID  pvProt   = pvLdrInitThunk;
2978    SIZE_T cbProt   = cbBackup;
2979    ULONG  fOldProt = 0;
2980    rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
2981    if (!NT_SUCCESS(rcNt))
2982        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2983                             "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2984
2985    rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abReplacement, sizeof(abReplacement), &cbIgnored);
2986    if (!NT_SUCCESS(rcNt))
2987        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2988                             "supR3HardNtDisableThreadCreationEx: NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
2989
2990    pvProt   = pvLdrInitThunk;
2991    cbProt   = cbBackup;
2992    rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
2993    if (!NT_SUCCESS(rcNt))
2994        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2995                             "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk/2 failed: %#x", rcNt);
2996
2997    return VINF_SUCCESS;
2998}
2999
3000
3001/**
3002 * Undo the effects of supR3HardNtDisableThreadCreationEx.
3003 *
3004 * @returns VBox status code.
3005 * @param   hProcess            The process to do this to.
3006 * @param   pvLdrInitThunk      The address of the LdrInitializeThunk code to
3007 *                              override.
3008 * @param   pabBackup           Where to back up the original instruction bytes
3009 *                              at pvLdrInitThunk.
3010 * @param   cbBackup            The size of the backup area. Must be 16 bytes.
3011 * @param   pErrInfo            Where to return extended error information.
3012 *                              Optional.
3013 */
3014static int supR3HardNtEnableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, uint8_t const *pabBackup, size_t cbBackup,
3015                                             PRTERRINFO pErrInfo)
3016{
3017    SUP_DPRINTF(("supR3HardNtEnableThreadCreation:\n"));
3018    SUPR3HARDENED_ASSERT(cbBackup == 16);
3019
3020    PVOID  pvProt   = pvLdrInitThunk;
3021    SIZE_T cbProt   = cbBackup;
3022    ULONG  fOldProt = 0;
3023    NTSTATUS rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
3024    if (!NT_SUCCESS(rcNt))
3025        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
3026                             "supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
3027
3028    SIZE_T cbIgnored;
3029    rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
3030    if (!NT_SUCCESS(rcNt))
3031        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
3032                             "supR3HardNtEnableThreadCreation: NtWriteVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
3033                             rcNt);
3034
3035    pvProt   = pvLdrInitThunk;
3036    cbProt   = cbBackup;
3037    rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
3038    if (!NT_SUCCESS(rcNt))
3039        return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
3040                             "supR3HardNtEnableThreadCreation: NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
3041                             rcNt);
3042
3043    return VINF_SUCCESS;
3044}
3045
3046
3047/**
3048 * Disable thread creation for the current process.
3049 *
3050 * @remarks Doesn't really disables it, just makes the threads exit immediately
3051 *          without executing any real code.
3052 */
3053static void supR3HardenedWinDisableThreadCreation(void)
3054{
3055    /* Cannot use the imported NtTerminateThread as it's pointing to our own
3056       syscall assembly code. */
3057    static PFNRT s_pfnNtTerminateThread = NULL;
3058    if (s_pfnNtTerminateThread == NULL)
3059        s_pfnNtTerminateThread = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtTerminateThread");
3060    SUPR3HARDENED_ASSERT(s_pfnNtTerminateThread);
3061
3062    int rc = supR3HardNtDisableThreadCreationEx(NtCurrentProcess(),
3063                                                (void *)(uintptr_t)&LdrInitializeThunk,
3064                                                (void *)(uintptr_t)s_pfnNtTerminateThread,
3065                                                g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
3066                                                NULL /* pErrInfo*/);
3067    g_fSupInitThunkSelfPatched = RT_SUCCESS(rc);
3068}
3069
3070
3071/**
3072 * Undoes the effects of supR3HardenedWinDisableThreadCreation.
3073 */
3074DECLHIDDEN(void) supR3HardenedWinEnableThreadCreation(void)
3075{
3076    if (g_fSupInitThunkSelfPatched)
3077    {
3078        int rc = supR3HardNtEnableThreadCreationEx(NtCurrentProcess(),
3079                                                   (void *)(uintptr_t)&LdrInitializeThunk,
3080                                                   g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
3081                                                   RTErrInfoInitStatic(&g_ErrInfoStatic));
3082        if (RT_FAILURE(rc))
3083            supR3HardenedError(rc, true /*fFatal*/, "%s", g_ErrInfoStatic.szMsg);
3084        g_fSupInitThunkSelfPatched = false;
3085    }
3086}
3087
3088
3089
3090
3091/*
3092 *
3093 * R e s p a w n
3094 * R e s p a w n
3095 * R e s p a w n
3096 *
3097 */
3098
3099
3100/**
3101 * Gets the SID of the user associated with the process.
3102 *
3103 * @returns @c true if we've got a login SID, @c false if not.
3104 * @param   pSidUser            Where to return the user SID.
3105 * @param   cbSidUser           The size of the user SID buffer.
3106 * @param   pSidLogin           Where to return the login SID.
3107 * @param   cbSidLogin          The size of the login SID buffer.
3108 */
3109static bool supR3HardNtChildGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
3110{
3111    HANDLE hToken;
3112    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
3113    union
3114    {
3115        TOKEN_USER      UserInfo;
3116        TOKEN_GROUPS    Groups;
3117        uint8_t         abPadding[4096];
3118    } uBuf;
3119    ULONG cbRet = 0;
3120    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
3121    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
3122
3123    bool fLoginSid = false;
3124    NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
3125    if (NT_SUCCESS(rcNt))
3126    {
3127        for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
3128            if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
3129            {
3130                SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
3131                fLoginSid = true;
3132                break;
3133            }
3134    }
3135
3136    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
3137
3138    return fLoginSid;
3139}
3140
3141
3142/**
3143 * Build security attributes for the process or the primary thread (@a fProcess)
3144 *
3145 * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
3146 * to admins, i.e. normal windows users), or by taking ownership and/or
3147 * modifying the DACL.  However, it restricts
3148 *
3149 * @param   pSecAttrs           Where to return the security attributes.
3150 * @param   pCleanup            Cleanup record.
3151 * @param   fProcess            Set if it's for the process, clear if it's for
3152 *                              the primary thread.
3153 */
3154static void supR3HardNtChildInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
3155{
3156    /*
3157     * Safe return values.
3158     */
3159    suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
3160
3161    pSecAttrs->nLength              = sizeof(*pSecAttrs);
3162    pSecAttrs->bInheritHandle       = FALSE;
3163    pSecAttrs->lpSecurityDescriptor = NULL;
3164
3165/** @todo This isn't at all complete, just sketches... */
3166
3167    /*
3168     * Create an ACL detailing the access of the above groups.
3169     */
3170    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
3171
3172    ULONG fDeny  = DELETE | WRITE_DAC | WRITE_OWNER;
3173    ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
3174    ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
3175    if (fProcess)
3176    {
3177        fDeny       |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
3178                    |  PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
3179                    |  PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
3180        fAllow      |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
3181        fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
3182        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
3183        {
3184            fAllow      |= PROCESS_QUERY_LIMITED_INFORMATION;
3185            fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
3186        }
3187        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
3188            fAllow  |= PROCESS_SET_LIMITED_INFORMATION;
3189    }
3190    else
3191    {
3192        fDeny       |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
3193                    |  THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
3194        fAllow      |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
3195        fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
3196        if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
3197        {
3198            fAllow      |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
3199            fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
3200        }
3201
3202    }
3203    fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
3204
3205    /* Deny everyone access to bad bits. */
3206#if 1
3207    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
3208    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
3209    *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
3210    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3211                                                          fDeny, &pCleanup->Everyone.Sid));
3212#endif
3213
3214#if 0
3215    /* Grant some access to the owner - doesn't work. */
3216    SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
3217    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
3218    *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
3219
3220    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3221                                                          fDeny, &pCleanup->Owner.Sid));
3222    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3223                                                           fAllow, &pCleanup->Owner.Sid));
3224#endif
3225
3226#if 1
3227    bool fHasLoginSid = supR3HardNtChildGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
3228                                                          &pCleanup->Login.Sid, sizeof(pCleanup->Login));
3229
3230# if 1
3231    /* Grant minimal access to the user. */
3232    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3233                                                          fDeny, &pCleanup->User.Sid));
3234    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3235                                                           fAllow, &pCleanup->User.Sid));
3236# endif
3237
3238# if 1
3239    /* Grant very limited access to the login sid. */
3240    if (fHasLoginSid)
3241    {
3242        SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
3243                                                               fAllowLogin, &pCleanup->Login.Sid));
3244    }
3245# endif
3246
3247#endif
3248
3249    /*
3250     * Create a security descriptor with the above ACL.
3251     */
3252    PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
3253    pCleanup->pSecDesc = pSecDesc;
3254
3255    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
3256    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
3257                                                                 FALSE /*fDaclDefaulted*/));
3258    pSecAttrs->lpSecurityDescriptor = pSecDesc;
3259}
3260
3261
3262/**
3263 * Predicate function which tests whether @a ch is a argument separator
3264 * character.
3265 *
3266 * @returns True/false.
3267 * @param   ch                  The character to examine.
3268 */
3269DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
3270{
3271    return ch == ' '
3272        || ch == '\t'
3273        || ch == '\n'
3274        || ch == '\r';
3275}
3276
3277
3278/**
3279 * Construct the new command line.
3280 *
3281 * Since argc/argv are both derived from GetCommandLineW (see
3282 * suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
3283 * conversion and quoting by going to the original source.
3284 *
3285 * The executable name, though, is replaced in case it's not a fullly
3286 * qualified path.
3287 *
3288 * The re-spawn indicator is added immediately after the executable name
3289 * so that we don't get tripped up missing close quote chars in the last
3290 * argument.
3291 *
3292 * @returns Pointer to a command line string (heap).
3293 * @param   pString         Unicode string structure to initialize to the
3294 *                          command line. Optional.
3295 * @param   iWhich          Which respawn we're to check for, 1 being the first
3296 *                          one, and 2 the second and final.
3297 */
3298static PRTUTF16 supR3HardNtChildConstructCmdLine(PUNICODE_STRING pString, int iWhich)
3299{
3300    SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
3301
3302    /*
3303     * Get the command line and skip the executable name.
3304     */
3305    PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
3306    PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
3307    uint32_t  cwcArgs  = pCmdLineStr->Length / sizeof(WCHAR);
3308
3309    /* Skip leading space (shouldn't be any, but whatever). */
3310    while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
3311        cwcArgs--, pawcArgs++;
3312    SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
3313
3314    /* Walk to the end of it. */
3315    int fQuoted = false;
3316    do
3317    {
3318        if (*pawcArgs == '"')
3319        {
3320            fQuoted = !fQuoted;
3321            cwcArgs--; pawcArgs++;
3322        }
3323        else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
3324            cwcArgs--, pawcArgs++;
3325        else
3326        {
3327            unsigned cSlashes = 0;
3328            do
3329            {
3330                cSlashes++;
3331                cwcArgs--;
3332                pawcArgs++;
3333            }
3334            while (cwcArgs > 0 && *pawcArgs == '\\');
3335            if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
3336                cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
3337        }
3338    } while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
3339
3340    /* Skip trailing spaces. */
3341    while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
3342        cwcArgs--, pawcArgs++;
3343
3344    /*
3345     * Allocate a new buffer.
3346     */
3347    AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
3348    size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
3349                      + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
3350    if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
3351        supR3HardenedFatalMsg("supR3HardNtChildConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
3352                              "Command line is too long (%u chars)!", cwcCmdLine);
3353
3354    PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
3355    SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
3356
3357    /*
3358     * Construct the new command line.
3359     */
3360    PRTUTF16 pwszDst = pwszCmdLine;
3361    for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
3362        *pwszDst++ = *pszSrc;
3363
3364    if (cwcArgs)
3365    {
3366        *pwszDst++ = ' ';
3367        suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
3368        pwszDst += cwcArgs;
3369    }
3370
3371    *pwszDst = '\0';
3372    SUPR3HARDENED_ASSERT((uintptr_t)(pwszDst - pwszCmdLine) == cwcCmdLine);
3373
3374    if (pString)
3375    {
3376        pString->Buffer = pwszCmdLine;
3377        pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
3378        pString->MaximumLength = pString->Length + sizeof(WCHAR);
3379    }
3380    return pwszCmdLine;
3381}
3382
3383
3384/**
3385 * Terminates the child process.
3386 *
3387 * @param   hProcess            The process handle.
3388 * @param   pszWhere            Who's having child rasing troubles.
3389 * @param   rc                  The status code to report.
3390 * @param   pszFormat           The message format string.
3391 * @param   ...                 Message format arguments.
3392 */
3393static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
3394{
3395    /*
3396     * Terminate the process ASAP and display error.
3397     */
3398    NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
3399
3400    va_list va;
3401    va_start(va, pszFormat);
3402    supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
3403    va_end(va);
3404
3405    /*
3406     * Wait for the process to really go away.
3407     */
3408    PROCESS_BASIC_INFORMATION BasicInfo;
3409    NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3410    bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
3411    if (!fExitOk)
3412    {
3413        NTSTATUS rcNtWait;
3414        uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
3415        do
3416        {
3417            NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
3418
3419            LARGE_INTEGER Timeout;
3420            Timeout.QuadPart = -20000000; /* 2 second */
3421            rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
3422
3423            rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3424            fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
3425        } while (   !fExitOk
3426                 && (   rcNtWait == STATUS_TIMEOUT
3427                     || rcNtWait == STATUS_USER_APC
3428                     || rcNtWait == STATUS_ALERTED)
3429                 && supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
3430        if (fExitOk)
3431            supR3HardenedError(rc, false /*fFatal*/,
3432                               "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
3433                               rc, rc, rcNtWait, hProcess);
3434    }
3435
3436    /*
3437     * Final error message.
3438     */
3439    va_start(va, pszFormat);
3440    supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
3441    /* not reached */
3442}
3443
3444
3445/**
3446 * Checks the child process when hEvtParent is signalled.
3447 *
3448 * This will read the request data from the child and check it against expected
3449 * request.  If an error is signalled, we'll raise it and make sure the child
3450 * terminates before terminating the calling process.
3451 *
3452 * @param   pThis               The child process data structure.
3453 * @param   enmExpectedRequest  The expected child request.
3454 * @param   pszWhat             What we're waiting for.
3455 */
3456static void supR3HardNtChildProcessRequest(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, const char *pszWhat)
3457{
3458    /*
3459     * Read the process parameters from the child.
3460     */
3461    uintptr_t           uChildAddr = (uintptr_t)pThis->Peb.ImageBaseAddress
3462                                   + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3463    SIZE_T              cbIgnored  = 0;
3464    RT_ZERO(pThis->ProcParams);
3465    NTSTATUS rcNt = NtReadVirtualMemory(pThis->hProcess, (PVOID)uChildAddr,
3466                                        &pThis->ProcParams, sizeof(pThis->ProcParams), &cbIgnored);
3467    if (!NT_SUCCESS(rcNt))
3468        supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt,
3469                                  "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
3470
3471    /*
3472     * Is it the expected request?
3473     */
3474    if (pThis->ProcParams.enmRequest == enmExpectedRequest)
3475        return;
3476
3477    /*
3478     * No, not the expected request. If it's an error request, tell the child
3479     * to terminate itself, otherwise we'll have to terminate it.
3480     */
3481    pThis->ProcParams.szErrorMsg[sizeof(pThis->ProcParams.szErrorMsg) - 1] = '\0';
3482    pThis->ProcParams.szWhere[sizeof(pThis->ProcParams.szWhere) - 1] = '\0';
3483    SUP_DPRINTF(("supR3HardenedWinCheckChild: enmRequest=%d rc=%d enmWhat=%d %s: %s\n",
3484                 pThis->ProcParams.enmRequest, pThis->ProcParams.rc, pThis->ProcParams.enmWhat,
3485                 pThis->ProcParams.szWhere, pThis->ProcParams.szErrorMsg));
3486
3487    if (pThis->ProcParams.enmRequest != kSupR3WinChildReq_Error)
3488        supR3HardenedWinKillChild(pThis, "supR3HardenedWinCheckChild", VERR_INVALID_PARAMETER,
3489                                  "Unexpected child request #%d. Was expecting #%d (%s).\n",
3490                                  pThis->ProcParams.enmRequest, enmExpectedRequest, pszWhat);
3491
3492    rcNt = NtSetEvent(pThis->hEvtChild, NULL);
3493    if (!NT_SUCCESS(rcNt))
3494        supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt, "NtSetEvent failed: %#x\n", rcNt);
3495
3496    /* Wait for it to terminate. */
3497    LARGE_INTEGER Timeout;
3498    Timeout.QuadPart = -50000000; /* 5 seconds */
3499    rcNt = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, &Timeout);
3500    if (rcNt != STATUS_WAIT_0)
3501    {
3502        SUP_DPRINTF(("supR3HardNtChildProcessRequest: Child is taking too long to quit (rcWait=%#x), killing it...\n", rcNt));
3503        NtTerminateProcess(pThis->hProcess, DBG_TERMINATE_PROCESS);
3504    }
3505
3506    /*
3507     * Report the error in the same way as it occured in the guest.
3508     */
3509    if (pThis->ProcParams.enmWhat == kSupInitOp_Invalid)
3510        supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, pThis->ProcParams.rc,
3511                              "%s", pThis->ProcParams.szErrorMsg);
3512    else
3513        supR3HardenedFatalMsg(pThis->ProcParams.szWhere, pThis->ProcParams.enmWhat, pThis->ProcParams.rc,
3514                              "%s", pThis->ProcParams.szErrorMsg);
3515}
3516
3517
3518/**
3519 * Waits for the child to make a certain request or terminate.
3520 *
3521 * The stub process will also wait on it's parent to terminate.
3522 * This call will only return if the child made the expected request.
3523 *
3524 * @param   pThis               The child process data structure.
3525 * @param   enmExpectedRequest  The child request to wait for.
3526 * @param   cMsTimeout          The number of milliseconds to wait (at least).
3527 * @param   pszWhat             What we're waiting for.
3528 */
3529static void supR3HardNtChildWaitFor(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, RTMSINTERVAL cMsTimeout,
3530                                    const char *pszWhat)
3531{
3532    /*
3533     * The wait loop.
3534     * Will return when the expected request arrives.
3535     * Will break out when one of the processes terminates.
3536     */
3537    NTSTATUS      rcNtWait;
3538    LARGE_INTEGER Timeout;
3539    uint64_t      uMsTsStart = supR3HardenedWinGetMilliTS();
3540    uint64_t      cMsElapsed = 0;
3541    for (;;)
3542    {
3543        /*
3544         * Assemble handles to wait for.
3545         */
3546        ULONG  cHandles = 1;
3547        HANDLE ahHandles[3];
3548        ahHandles[0] = pThis->hProcess;
3549        if (pThis->hEvtParent)
3550            ahHandles[cHandles++] = pThis->hEvtParent;
3551        if (pThis->hParent)
3552            ahHandles[cHandles++] = pThis->hParent;
3553
3554        /*
3555         * Do the waiting according to the callers wishes.
3556         */
3557        if (   enmExpectedRequest == kSupR3WinChildReq_End
3558            || cMsTimeout  == RT_INDEFINITE_WAIT)
3559            rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*Timeout*/);
3560        else
3561        {
3562            Timeout.QuadPart = -(int64_t)(cMsTimeout - cMsElapsed) * 10000;
3563            rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, &Timeout);
3564        }
3565
3566        /*
3567         * Process child request.
3568         */
3569        if (rcNtWait == STATUS_WAIT_0 + 1 && pThis->hEvtParent != NULL)
3570        {
3571            supR3HardNtChildProcessRequest(pThis, enmExpectedRequest, pszWhat);
3572            SUP_DPRINTF(("supR3HardNtChildWaitFor: Found expected request %d (%s) after %llu ms.\n",
3573                         enmExpectedRequest, pszWhat, supR3HardenedWinGetMilliTS() - uMsTsStart));
3574            return; /* Expected request received. */
3575        }
3576
3577        /*
3578         * Process termination?
3579         */
3580        if (   (ULONG)rcNtWait - (ULONG)STATUS_WAIT_0           < cHandles
3581            || (ULONG)rcNtWait - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
3582            break;
3583
3584        /*
3585         * Check sanity.
3586         */
3587        if (   rcNtWait != STATUS_TIMEOUT
3588            && rcNtWait != STATUS_USER_APC
3589            && rcNtWait != STATUS_ALERTED)
3590            supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
3591                                      "NtWaitForMultipleObjects returned %#x waiting for #%d (%s)\n",
3592                                      rcNtWait, enmExpectedRequest, pszWhat);
3593
3594        /*
3595         * Calc elapsed time for the next timeout calculation, checking to see
3596         * if we've timed out already.
3597         */
3598        cMsElapsed = supR3HardenedWinGetMilliTS() - uMsTsStart;
3599        if (   cMsElapsed > cMsTimeout
3600            && cMsTimeout != RT_INDEFINITE_WAIT
3601            && enmExpectedRequest != kSupR3WinChildReq_End)
3602        {
3603            if (rcNtWait == STATUS_USER_APC || rcNtWait == STATUS_ALERTED)
3604                cMsElapsed = cMsTimeout - 1; /* try again */
3605            else
3606            {
3607                /* We timed out. */
3608                supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
3609                                          "Timed out after %llu ms waiting for child request #%d (%s).\n",
3610                                          cMsElapsed, enmExpectedRequest, pszWhat);
3611            }
3612        }
3613    }
3614
3615    /*
3616     * Proxy the termination code of the child, if it exited already.
3617     */
3618    PROCESS_BASIC_INFORMATION BasicInfo;
3619    NTSTATUS rcNt1 = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
3620    NTSTATUS rcNt2 = STATUS_PENDING;
3621    NTSTATUS rcNt3 = STATUS_PENDING;
3622    if (   !NT_SUCCESS(rcNt1)
3623        || BasicInfo.ExitStatus == STATUS_PENDING)
3624    {
3625        rcNt2 = NtTerminateProcess(pThis->hProcess, RTEXITCODE_FAILURE);
3626        Timeout.QuadPart = NT_SUCCESS(rcNt2) ? -20000000 /* 2 sec */ : -1280000 /* 128 ms */;
3627        rcNt3 = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, NULL /*Timeout*/);
3628        BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
3629    }
3630
3631    SUP_DPRINTF(("supR3HardNtChildWaitFor[%d]: Quitting: ExitCode=%#x (rcNtWait=%#x, rcNt1=%#x, rcNt2=%#x, rcNt3=%#x, %llu ms, %s);\n",
3632                 pThis->iWhich, BasicInfo.ExitStatus, rcNtWait, rcNt1, rcNt2, rcNt3,
3633                 supR3HardenedWinGetMilliTS() - uMsTsStart, pszWhat));
3634    suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
3635}
3636
3637
3638/**
3639 * Closes full access child thread and process handles, making a harmless
3640 * duplicate of the process handle first.
3641 *
3642 * The hProcess member of the child process data structure will be change to the
3643 * harmless handle, while the hThread will be set to NULL.
3644 *
3645 * @param   pThis               The child process data structure.
3646 */
3647static void supR3HardNtChildCloseFullAccessHandles(PSUPR3HARDNTCHILD pThis)
3648{
3649    /*
3650     * The thread handle.
3651     */
3652    NTSTATUS rcNt = NtClose(pThis->hThread);
3653    if (!NT_SUCCESS(rcNt))
3654        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt, "NtClose(hThread) failed: %#x", rcNt);
3655    pThis->hThread = NULL;
3656
3657    /*
3658     * Duplicate the process handle into a harmless one.
3659     */
3660    HANDLE hProcWait;
3661    ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
3662    if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
3663        fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
3664    else
3665        fRights |= PROCESS_QUERY_INFORMATION;
3666    rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
3667                             NtCurrentProcess(), &hProcWait,
3668                             fRights, 0 /*HandleAttributes*/, 0);
3669    if (rcNt == STATUS_ACCESS_DENIED)
3670    {
3671        supR3HardenedError(rcNt, false /*fFatal*/,
3672                           "supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
3673                           fRights, rcNt, SYNCHRONIZE);
3674        rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
3675                                 NtCurrentProcess(), &hProcWait,
3676                                 SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
3677    }
3678    if (!NT_SUCCESS(rcNt))
3679        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt,
3680                                  "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
3681    /*
3682     * Close the process handle and replace it with the harmless one.
3683     */
3684    rcNt = NtClose(pThis->hProcess);
3685    pThis->hProcess = hProcWait;
3686    if (!NT_SUCCESS(rcNt))
3687        supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
3688                                  "NtClose failed on child process handle: %#x\n", rcNt);
3689}
3690
3691
3692/**
3693 * This restores the child PEB and tweaks a couple of fields before we do the
3694 * child purification and let the process run normally.
3695 *
3696 * @param   pThis               The child process data structure.
3697 */
3698static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis)
3699{
3700    /*
3701     * Make a copy of the pre-execution PEB.
3702     */
3703    PEB Peb = pThis->Peb;
3704
3705#if 0
3706    /*
3707     * There should not be any activation context, so if there is, we scratch the memory associated with it.
3708     */
3709    int rc = 0;
3710    if (RT_SUCCESS(rc) && Peb.pShimData && !((uintptr_t)Peb.pShimData & PAGE_OFFSET_MASK))
3711        rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.pShimData, PAGE_SIZE, "pShimData", pErrInfo);
3712    if (RT_SUCCESS(rc) && Peb.ActivationContextData && !((uintptr_t)Peb.ActivationContextData & PAGE_OFFSET_MASK))
3713        rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ActivationContextData, PAGE_SIZE, "ActivationContextData", pErrInfo);
3714    if (RT_SUCCESS(rc) && Peb.ProcessAssemblyStorageMap && !((uintptr_t)Peb.ProcessAssemblyStorageMap & PAGE_OFFSET_MASK))
3715        rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "ProcessAssemblyStorageMap", pErrInfo);
3716    if (RT_SUCCESS(rc) && Peb.SystemDefaultActivationContextData && !((uintptr_t)Peb.SystemDefaultActivationContextData & PAGE_OFFSET_MASK))
3717        rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "SystemDefaultActivationContextData", pErrInfo);
3718    if (RT_SUCCESS(rc) && Peb.SystemAssemblyStorageMap && !((uintptr_t)Peb.SystemAssemblyStorageMap & PAGE_OFFSET_MASK))
3719        rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.SystemAssemblyStorageMap, PAGE_SIZE, "SystemAssemblyStorageMap", pErrInfo);
3720    if (RT_FAILURE(rc))
3721        return rc;
3722#endif
3723
3724    /*
3725     * Clear compatibility and activation related fields.
3726     */
3727    Peb.AppCompatFlags.QuadPart             = 0;
3728    Peb.AppCompatFlagsUser.QuadPart         = 0;
3729    Peb.pShimData                           = NULL;
3730    Peb.AppCompatInfo                       = NULL;
3731#if 0
3732    Peb.ActivationContextData               = NULL;
3733    Peb.ProcessAssemblyStorageMap           = NULL;
3734    Peb.SystemDefaultActivationContextData  = NULL;
3735    Peb.SystemAssemblyStorageMap            = NULL;
3736    /*Peb.Diff0.W6.IsProtectedProcess = 1;*/
3737#endif
3738
3739    /*
3740     * Write back the PEB.
3741     */
3742    SIZE_T cbActualMem = pThis->cbPeb;
3743    NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
3744    if (!NT_SUCCESS(rcNt))
3745        supR3HardenedWinKillChild(pThis, "supR3HardNtChildSanitizePeb", rcNt,
3746                                  "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
3747
3748}
3749
3750
3751/**
3752 * Purifies the child process after very early init has been performed.
3753 *
3754 * @param   pThis               The child process data structure.
3755 */
3756static void supR3HardNtChildPurify(PSUPR3HARDNTCHILD pThis)
3757{
3758    /*
3759     * We loop until we no longer make any fixes.  This is similar to what
3760     * we do (or used to do, really) in the fAvastKludge case of
3761     * supR3HardenedWinInit.  We might be up against asynchronous changes,
3762     * which we fudge by waiting a short while before earch purification. This
3763     * is arguably a fragile technique, but it's currently the best we've got.
3764     * Fortunately, most AVs seems to either favor immediate action on initial
3765     * load events or (much better for us) later events like kernel32.
3766     */
3767    uint64_t uMsTsOuterStart = supR3HardenedWinGetMilliTS();
3768    uint32_t cMsFudge        = g_fSupAdversaries ? 512 : 256;
3769    uint32_t cTotalFixes     = 0;
3770    uint32_t cFixes          = 0; /* (MSC wrongly thinks this maybe used uninitialized) */
3771    for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
3772    {
3773        /*
3774         * Delay.
3775         */
3776        uint32_t cSleeps = 0;
3777        uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
3778        do
3779        {
3780            NtYieldExecution();
3781            LARGE_INTEGER Time;
3782            Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
3783            NtDelayExecution(FALSE, &Time);
3784            cSleeps++;
3785        } while (   supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
3786                 || cSleeps < 8);
3787        SUP_DPRINTF(("supR3HardNtChildPurify: Startup delay kludge #1/%u: %u ms, %u sleeps\n",
3788                     iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
3789
3790        /*
3791         * Purify.
3792         */
3793        cFixes = 0;
3794        int rc = supHardenedWinVerifyProcess(pThis->hProcess, pThis->hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION,
3795                                             g_fSupAdversaries & (  SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE
3796                                                                  | SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD)
3797                                             ? SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW : 0,
3798                                             &cFixes, RTErrInfoInitStatic(&g_ErrInfoStatic));
3799        if (RT_FAILURE(rc))
3800            supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", rc,
3801                                      "supHardenedWinVerifyProcess failed with %Rrc: %s", rc, g_ErrInfoStatic.szMsg);
3802        if (cFixes == 0)
3803        {
3804            SUP_DPRINTF(("supR3HardNtChildPurify: Done after %llu ms and %u fixes (loop #%u).\n",
3805                         supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cTotalFixes, iLoop));
3806            return; /* We're probably good. */
3807        }
3808        cTotalFixes += cFixes;
3809
3810        if (!g_fSupAdversaries)
3811            g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
3812        cMsFudge = 512;
3813
3814        /*
3815         * Log the KiOpPrefetchPatchCount value if available, hoping it might
3816         * sched some light on spider38's case.
3817         */
3818        ULONG cPatchCount = 0;
3819        NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
3820                                                 &cPatchCount, sizeof(cPatchCount), NULL);
3821        if (NT_SUCCESS(rcNt))
3822            SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
3823                         cFixes, g_fSupAdversaries, cPatchCount));
3824        else
3825            SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
3826    }
3827
3828    /*
3829     * We've given up fixing the child process.  Probably fighting someone
3830     * that monitors their patches or/and our activities.
3831     */
3832    supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", VERR_TRY_AGAIN,
3833                              "Unable to purify child process! After 16 tries over %llu ms, we still %u fix(es) in the last pass.",
3834                              supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cFixes);
3835}
3836
3837
3838
3839/**
3840 * Sets up the early process init.
3841 *
3842 * @param   pThis               The child process data structure.
3843 */
3844static void supR3HardNtChildSetUpChildInit(PSUPR3HARDNTCHILD pThis)
3845{
3846    uintptr_t const uChildExeAddr = (uintptr_t)pThis->Peb.ImageBaseAddress;
3847
3848    /*
3849     * Plant the process parameters.  This ASSUMES the handle inheritance is
3850     * performed when creating the child process.
3851     */
3852    RT_ZERO(pThis->ProcParams);
3853    pThis->ProcParams.hEvtChild  = pThis->hEvtChild;
3854    pThis->ProcParams.hEvtParent = pThis->hEvtParent;
3855    pThis->ProcParams.uNtDllAddr = pThis->uNtDllAddr;
3856    pThis->ProcParams.enmRequest = kSupR3WinChildReq_Error;
3857    pThis->ProcParams.rc         = VINF_SUCCESS;
3858
3859    uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3860    SIZE_T    cbIgnored;
3861    NTSTATUS  rcNt = NtWriteVirtualMemory(pThis->hProcess, (PVOID)uChildAddr, &pThis->ProcParams,
3862                                          sizeof(pThis->ProcParams), &cbIgnored);
3863    if (!NT_SUCCESS(rcNt))
3864        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3865                                  "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
3866
3867    /*
3868     * Locate the LdrInitializeThunk address in the child as well as pristine
3869     * code bits for it.
3870     */
3871    PSUPHNTLDRCACHEENTRY pLdrEntry;
3872    int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry, NULL /*pErrInfo*/);
3873    if (RT_FAILURE(rc))
3874        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3875                                  "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
3876
3877    uint8_t *pbChildNtDllBits;
3878    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, pThis->uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
3879    if (RT_FAILURE(rc))
3880        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3881                                  "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
3882
3883    RTLDRADDR uLdrInitThunk;
3884    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
3885                          "LdrInitializeThunk", &uLdrInitThunk);
3886    if (RT_FAILURE(rc))
3887        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
3888                                  "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
3889    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
3890    SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
3891
3892    /*
3893     * Calculate the address of our code in the child process.
3894     */
3895    uintptr_t uEarlyProcInitEP = uChildExeAddr + (  (uintptr_t)&supR3HardenedEarlyProcessInitThunk
3896                                                  - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
3897
3898    /*
3899     * Compose the LdrInitializeThunk replacement bytes.
3900     * Note! The amount of code we replace here must be less or equal to what
3901     *       the process verification code ignores.
3902     */
3903    uint8_t abNew[16];
3904    memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - pThis->uNtDllAddr), sizeof(abNew));
3905#ifdef RT_ARCH_AMD64
3906    abNew[0] = 0xff;
3907    abNew[1] = 0x25;
3908    *(uint32_t *)&abNew[2] = 0;
3909    *(uint64_t *)&abNew[6] = uEarlyProcInitEP;
3910#elif defined(RT_ARCH_X86)
3911    abNew[0] = 0xe9;
3912    *(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
3913#else
3914# error "Unsupported arch."
3915#endif
3916
3917    /*
3918     * Install the LdrInitializeThunk replacement code in the child process.
3919     */
3920    PVOID   pvProt = pvLdrInitThunk;
3921    SIZE_T  cbProt = sizeof(abNew);
3922    ULONG   fOldProt;
3923    rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
3924    if (!NT_SUCCESS(rcNt))
3925        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3926                                  "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
3927
3928    rcNt = NtWriteVirtualMemory(pThis->hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
3929    if (!NT_SUCCESS(rcNt))
3930        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3931                                  "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
3932
3933    pvProt = pvLdrInitThunk;
3934    cbProt = sizeof(abNew);
3935    rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
3936    if (!NT_SUCCESS(rcNt))
3937        supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
3938                                  "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
3939
3940    /* Caller starts child execution. */
3941    SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
3942}
3943
3944
3945
3946/**
3947 * This messes with the child PEB before we trigger the initial image events.
3948 *
3949 * @param   pThis               The child process data structure.
3950 */
3951static void supR3HardNtChildScrewUpPebForInitialImageEvents(PSUPR3HARDNTCHILD pThis)
3952{
3953    /*
3954     * Not sure if any of the cracker software uses the PEB at this point, but
3955     * just in case they do make some of the PEB fields a little less useful.
3956     */
3957    PEB Peb = pThis->Peb;
3958
3959    /* Make ImageBaseAddress useless. */
3960    Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
3961#ifdef RT_ARCH_AMD64
3962    Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
3963#endif
3964
3965    /*
3966     * Write the PEB.
3967     */
3968    SIZE_T cbActualMem = pThis->cbPeb;
3969    NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
3970    if (!NT_SUCCESS(rcNt))
3971        supR3HardenedWinKillChild(pThis, "supR3HardNtChildScrewUpPebForInitialImageEvents", rcNt,
3972                                  "NtWriteVirtualMemory/Peb failed: %#x", rcNt);
3973}
3974
3975
3976/**
3977 * Check if the zero terminated NT unicode string is the path to the given
3978 * system32 DLL.
3979 *
3980 * @returns true if it is, false if not.
3981 * @param   pUniStr             The zero terminated NT unicode string path.
3982 * @param   pszName             The name of the system32 DLL.
3983 */
3984static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
3985{
3986    if (pUniStr->Length > g_System32NtPath.UniStr.Length)
3987    {
3988        if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
3989        {
3990            if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
3991            {
3992                if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
3993                    return true;
3994            }
3995        }
3996    }
3997
3998    return false;
3999}
4000
4001
4002/**
4003 * Worker for supR3HardNtChildGatherData that locates NTDLL in the child
4004 * process.
4005 *
4006 * @param   pThis               The child process data structure.
4007 */
4008static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis)
4009{
4010    /*
4011     * Find NTDLL in this process first and take that as a starting point.
4012     */
4013    pThis->uNtDllParentAddr = (uintptr_t)GetModuleHandleW(L"ntdll.dll");
4014    SUPR3HARDENED_ASSERT(pThis->uNtDllParentAddr != 0 && !(pThis->uNtDllParentAddr & PAGE_OFFSET_MASK));
4015    pThis->uNtDllAddr = pThis->uNtDllParentAddr;
4016
4017    /*
4018     * Scan the virtual memory of the child.
4019     */
4020    uintptr_t   cbAdvance = 0;
4021    uintptr_t   uPtrWhere = 0;
4022    for (uint32_t i = 0; i < 1024; i++)
4023    {
4024        /* Query information. */
4025        SIZE_T                      cbActual = 0;
4026        MEMORY_BASIC_INFORMATION    MemInfo  = { 0, 0, 0, 0, 0, 0, 0 };
4027        NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
4028                                             (void const *)uPtrWhere,
4029                                             MemoryBasicInformation,
4030                                             &MemInfo,
4031                                             sizeof(MemInfo),
4032                                             &cbActual);
4033        if (!NT_SUCCESS(rcNt))
4034            break;
4035
4036        if (   MemInfo.Type == SEC_IMAGE
4037            || MemInfo.Type == SEC_PROTECTED_IMAGE
4038            || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
4039        {
4040            if (MemInfo.BaseAddress == MemInfo.AllocationBase)
4041            {
4042                /* Get the image name. */
4043                union
4044                {
4045                    UNICODE_STRING UniStr;
4046                    uint8_t abPadding[4096];
4047                } uBuf;
4048                NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
4049                                                     MemInfo.BaseAddress,
4050                                                     MemorySectionName,
4051                                                     &uBuf,
4052                                                     sizeof(uBuf) - sizeof(WCHAR),
4053                                                     &cbActual);
4054                if (NT_SUCCESS(rcNt))
4055                {
4056                    uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
4057                    if (supR3HardNtIsNamedSystem32Dll(&uBuf.UniStr, "ntdll.dll"))
4058                    {
4059                        pThis->uNtDllAddr = (uintptr_t)MemInfo.AllocationBase;
4060                        SUP_DPRINTF(("supR3HardNtPuChFindNtdll: uNtDllParentAddr=%p uNtDllChildAddr=%p\n",
4061                                     pThis->uNtDllParentAddr, pThis->uNtDllAddr));
4062                        return;
4063                    }
4064                }
4065            }
4066        }
4067
4068        /*
4069         * Advance.
4070         */
4071        cbAdvance = MemInfo.RegionSize;
4072        if (uPtrWhere + cbAdvance <= uPtrWhere)
4073            break;
4074        uPtrWhere += MemInfo.RegionSize;
4075    }
4076
4077    supR3HardenedWinKillChild(pThis, "supR3HardNtChildFindNtdll", VERR_MODULE_NOT_FOUND, "ntdll.dll not found in child process.");
4078}
4079
4080
4081/**
4082 * Gather child data.
4083 *
4084 * @param   pThis               The child process data structure.
4085 */
4086static void supR3HardNtChildGatherData(PSUPR3HARDNTCHILD pThis)
4087{
4088    /*
4089     * Basic info.
4090     */
4091    ULONG cbActual = 0;
4092    NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation,
4093                                              &pThis->BasicInfo, sizeof(pThis->BasicInfo), &cbActual);
4094    if (!NT_SUCCESS(rcNt))
4095        supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
4096                                  "NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
4097
4098    /*
4099     * If this is the middle (stub) process, we wish to wait for both child
4100     * and parent.  So open the parent process.  Not fatal if we cannnot.
4101     */
4102    if (pThis->iWhich > 1)
4103    {
4104        PROCESS_BASIC_INFORMATION SelfInfo;
4105        rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &SelfInfo, sizeof(SelfInfo), &cbActual);
4106        if (NT_SUCCESS(rcNt))
4107        {
4108            OBJECT_ATTRIBUTES ObjAttr;
4109            InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4110
4111            CLIENT_ID ClientId;
4112            ClientId.UniqueProcess = (HANDLE)SelfInfo.InheritedFromUniqueProcessId;
4113            ClientId.UniqueThread  = NULL;
4114
4115            rcNt = NtOpenProcess(&pThis->hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
4116#ifdef DEBUG
4117            SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
4118#endif
4119            if (!NT_SUCCESS(rcNt))
4120            {
4121                pThis->hParent = NULL;
4122                SUP_DPRINTF(("supR3HardNtChildGatherData: Failed to open parent process (%#p): %#x\n", ClientId.UniqueProcess, rcNt));
4123            }
4124        }
4125
4126    }
4127
4128    /*
4129     * Process environment block.
4130     */
4131    if (g_uNtVerCombined < SUP_NT_VER_W2K3)
4132        pThis->cbPeb = PEB_SIZE_W51;
4133    else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
4134        pThis->cbPeb = PEB_SIZE_W52;
4135    else if (g_uNtVerCombined < SUP_NT_VER_W70)
4136        pThis->cbPeb = PEB_SIZE_W6;
4137    else if (g_uNtVerCombined < SUP_NT_VER_W80)
4138        pThis->cbPeb = PEB_SIZE_W7;
4139    else if (g_uNtVerCombined < SUP_NT_VER_W81)
4140        pThis->cbPeb = PEB_SIZE_W80;
4141    else
4142        pThis->cbPeb = PEB_SIZE_W81;
4143
4144    SUP_DPRINTF(("supR3HardNtChildGatherData: PebBaseAddress=%p cbPeb=%#x\n",
4145                 pThis->BasicInfo.PebBaseAddress, pThis->cbPeb));
4146
4147    SIZE_T cbActualMem;
4148    RT_ZERO(pThis->Peb);
4149    rcNt = NtReadVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &pThis->Peb, sizeof(pThis->Peb), &cbActualMem);
4150    if (!NT_SUCCESS(rcNt))
4151        supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
4152                                  "NtReadVirtualMemory/Peb failed: %#x", rcNt);
4153
4154    /*
4155     * Locate NtDll.
4156     */
4157    supR3HardNtChildFindNtdll(pThis);
4158}
4159
4160
4161/**
4162 * Does the actually respawning.
4163 *
4164 * @returns Never, will call exit or raise fatal error.
4165 * @param   iWhich              Which respawn we're to check for, 1 being the
4166 *                              first one, and 2 the second and final.
4167 */
4168static DECL_NO_RETURN(void) supR3HardenedWinDoReSpawn(int iWhich)
4169{
4170    NTSTATUS                        rcNt;
4171    PPEB                            pPeb              = NtCurrentPeb();
4172    PRTL_USER_PROCESS_PARAMETERS    pParentProcParams = pPeb->ProcessParameters;
4173
4174    SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
4175
4176    /*
4177     * Init the child process data structure, creating the child communication
4178     * event sempahores.
4179     */
4180    SUPR3HARDNTCHILD This;
4181    RT_ZERO(This);
4182    This.iWhich = iWhich;
4183
4184    OBJECT_ATTRIBUTES ObjAttrs;
4185    This.hEvtChild = NULL;
4186    InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4187    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
4188
4189    This.hEvtParent = NULL;
4190    InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4191    SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
4192
4193    /*
4194     * Set up security descriptors.
4195     */
4196    SECURITY_ATTRIBUTES ProcessSecAttrs;
4197    MYSECURITYCLEANUP   ProcessSecAttrsCleanup;
4198    supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
4199
4200    SECURITY_ATTRIBUTES ThreadSecAttrs;
4201    MYSECURITYCLEANUP   ThreadSecAttrsCleanup;
4202    supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
4203
4204#if 1
4205    /*
4206     * Configure the startup info and creation flags.
4207     */
4208    DWORD dwCreationFlags = CREATE_SUSPENDED;
4209
4210    STARTUPINFOEXW SiEx;
4211    suplibHardenedMemSet(&SiEx, 0, sizeof(SiEx));
4212    if (1)
4213        SiEx.StartupInfo.cb = sizeof(SiEx.StartupInfo);
4214    else
4215    {
4216        SiEx.StartupInfo.cb = sizeof(SiEx);
4217        dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
4218        /** @todo experiment with protected process stuff later on. */
4219    }
4220
4221    SiEx.StartupInfo.dwFlags |= pParentProcParams->WindowFlags & STARTF_USESHOWWINDOW;
4222    SiEx.StartupInfo.wShowWindow = (WORD)pParentProcParams->ShowWindowFlags;
4223
4224    SiEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4225    SiEx.StartupInfo.hStdInput  = pParentProcParams->StandardInput;
4226    SiEx.StartupInfo.hStdOutput = pParentProcParams->StandardOutput;
4227    SiEx.StartupInfo.hStdError  = pParentProcParams->StandardError;
4228
4229    /*
4230     * Construct the command line and launch the process.
4231     */
4232    PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich);
4233
4234    supR3HardenedWinEnableThreadCreation();
4235    PROCESS_INFORMATION ProcessInfoW32;
4236    if (!CreateProcessW(g_wszSupLibHardenedExePath,
4237                        pwszCmdLine,
4238                        &ProcessSecAttrs,
4239                        &ThreadSecAttrs,
4240                        TRUE /*fInheritHandles*/,
4241                        dwCreationFlags,
4242                        NULL /*pwszzEnvironment*/,
4243                        NULL /*pwszCurDir*/,
4244                        &SiEx.StartupInfo,
4245                        &ProcessInfoW32))
4246        supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
4247                              "Error relaunching VirtualBox VM process: %u\n"
4248                              "Command line: '%ls'",
4249                              RtlGetLastWin32Error(), pwszCmdLine);
4250    supR3HardenedWinDisableThreadCreation();
4251
4252    SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n",
4253                 iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId));
4254    This.hProcess = ProcessInfoW32.hProcess;
4255    This.hThread  = ProcessInfoW32.hThread;
4256
4257#else
4258
4259    /*
4260     * Construct the process parameters.
4261     */
4262    UNICODE_STRING W32ImageName;
4263    W32ImageName.Buffer = g_wszSupLibHardenedExePath; /* Yes the windows name for the process parameters. */
4264    W32ImageName.Length = (USHORT)RTUtf16Len(g_wszSupLibHardenedExePath) * sizeof(WCHAR);
4265    W32ImageName.MaximumLength = W32ImageName.Length + sizeof(WCHAR);
4266
4267    UNICODE_STRING CmdLine;
4268    supR3HardNtChildConstructCmdLine(&CmdLine, iWhich);
4269
4270    PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL;
4271    SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateProcessParameters(&pProcParams,
4272                                                               &W32ImageName,
4273                                                               NULL /* DllPath - inherit from this process */,
4274                                                               NULL /* CurrentDirectory - inherit from this process */,
4275                                                               &CmdLine,
4276                                                               NULL /* Environment - inherit from this process */,
4277                                                               NULL /* WindowsTitle - none */,
4278                                                               NULL /* DesktopTitle - none. */,
4279                                                               NULL /* ShellInfo - none. */,
4280                                                               NULL /* RuntimeInfo - none (byte array for MSVCRT file info) */)
4281                                    );
4282
4283    /** @todo this doesn't work. :-( */
4284    pProcParams->ConsoleHandle  = pParentProcParams->ConsoleHandle;
4285    pProcParams->ConsoleFlags   = pParentProcParams->ConsoleFlags;
4286    pProcParams->StandardInput  = pParentProcParams->StandardInput;
4287    pProcParams->StandardOutput = pParentProcParams->StandardOutput;
4288    pProcParams->StandardError  = pParentProcParams->StandardError;
4289
4290    RTL_USER_PROCESS_INFORMATION ProcessInfoNt = { sizeof(ProcessInfoNt) };
4291    rcNt = RtlCreateUserProcess(&g_SupLibHardenedExeNtPath.UniStr,
4292                                OBJ_INHERIT | OBJ_CASE_INSENSITIVE /*Attributes*/,
4293                                pProcParams,
4294                                NULL, //&ProcessSecAttrs,
4295                                NULL, //&ThreadSecAttrs,
4296                                NtCurrentProcess() /* ParentProcess */,
4297                                FALSE /*fInheritHandles*/,
4298                                NULL /* DebugPort */,
4299                                NULL /* ExceptionPort */,
4300                                &ProcessInfoNt);
4301    if (!NT_SUCCESS(rcNt))
4302        supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
4303                              "Error relaunching VirtualBox VM process: %#x\n"
4304                              "Command line: '%ls'",
4305                              rcNt, CmdLine.Buffer);
4306
4307    SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [ntdll].\n",
4308                 iWhich, ProcessInfo.ClientId.UniqueProcess, ProcessInfo.ClientId.UniqueThread));
4309    RtlDestroyProcessParameters(pProcParams);
4310
4311    This.hProcess = ProcessInfoNt.ProcessHandle;
4312    This.hThread  = ProcessInfoNt.ThreadHandle;
4313#endif
4314
4315#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
4316    /*
4317     * Apply anti debugger notification trick to the thread.  (Also done in
4318     * supR3HardenedWinInit.)  This may fail with STATUS_ACCESS_DENIED and
4319     * maybe other errors.  (Unfortunately, recent (SEP 12.1) of symantec's
4320     * sysplant.sys driver will cause process deadlocks and a shutdown/reboot
4321     * denial of service problem if we hide the initial thread, so we postpone
4322     * this action if we've detected SEP.)
4323     */
4324    if (!(g_fSupAdversaries & (SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT | SUPHARDNT_ADVERSARY_SYMANTEC_N360)))
4325    {
4326        rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0);
4327        if (!NT_SUCCESS(rcNt))
4328            SUP_DPRINTF(("supR3HardenedWinReSpawn: NtSetInformationThread/ThreadHideFromDebugger failed: %#x (harmless)\n", rcNt));
4329    }
4330#endif
4331
4332    /*
4333     * Perform very early child initialization.
4334     */
4335    supR3HardNtChildGatherData(&This);
4336    supR3HardNtChildScrewUpPebForInitialImageEvents(&This);
4337    supR3HardNtChildSetUpChildInit(&This);
4338
4339    ULONG cSuspendCount = 0;
4340    rcNt = NtResumeThread(This.hThread, &cSuspendCount);
4341    if (!NT_SUCCESS(rcNt))
4342        supR3HardenedWinKillChild(&This, "supR3HardenedWinDoReSpawn", rcNt, "NtResumeThread failed: %#x", rcNt);
4343
4344    /*
4345     * Santizie the pre-NTDLL child when it's ready.
4346     *
4347     * AV software and other things injecting themselves into the embryonic
4348     * and budding process to intercept API calls and what not.  Unfortunately
4349     * this is also the behavior of viruses, malware and other unfriendly
4350     * software, so we won't stand for it.  AV software can scan our image
4351     * as they are loaded via kernel hooks, that's sufficient.  No need for
4352     * patching half of NTDLL or messing with the import table of the
4353     * process executable.
4354     */
4355    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_PurifyChildAndCloseHandles, 2000 /*ms*/, "PurifyChildAndCloseHandles");
4356    supR3HardNtChildPurify(&This);
4357    supR3HardNtChildSanitizePeb(&This);
4358
4359    /*
4360     * Close the unrestricted access handles.  Since we need to wait on the
4361     * child process, we'll reopen the process with limited access before doing
4362     * away with the process handle returned by CreateProcess.
4363     */
4364    supR3HardNtChildCloseFullAccessHandles(&This);
4365
4366    /*
4367     * Signal the child that we've closed the unrestricted handles and it can
4368     * safely try open the driver.
4369     */
4370    rcNt = NtSetEvent(This.hEvtChild, NULL);
4371    if (!NT_SUCCESS(rcNt))
4372        supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
4373                                  "NtSetEvent failed on child process handle: %#x\n", rcNt);
4374
4375    /*
4376     * Ditch the loader cache so we don't sit on too much memory while waiting.
4377     */
4378    supR3HardenedWinFlushLoaderCache();
4379    supR3HardenedWinCompactHeaps();
4380
4381    /*
4382     * Enable thread creation at this point so Ctrl-C and Ctrl-Break can be processed.
4383     */
4384    supR3HardenedWinEnableThreadCreation();
4385
4386    /*
4387     * Wait for the child to get to suplibHardenedWindowsMain so we can close the handles.
4388     */
4389    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_CloseEvents, 60000 /*ms*/, "CloseEvents");
4390
4391    NtClose(This.hEvtChild);
4392    NtClose(This.hEvtParent);
4393    This.hEvtChild  = NULL;
4394    This.hEvtParent = NULL;
4395
4396    /*
4397     * Wait for the process to terminate.
4398     */
4399    supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_End, RT_INDEFINITE_WAIT, "the end");
4400    supR3HardenedFatal("supR3HardenedWinDoReSpawn: supR3HardNtChildWaitFor unexpectedly returned!\n");
4401    /* not reached*/
4402}
4403
4404
4405/**
4406 * Logs the content of the given object directory.
4407 *
4408 * @returns true if it exists, false if not.
4409 * @param   pszDir             The path of the directory to log (ASCII).
4410 */
4411static void supR3HardenedWinLogObjDir(const char *pszDir)
4412{
4413    /*
4414     * Open the driver object directory.
4415     */
4416    RTUTF16 wszDir[128];
4417    int rc = RTUtf16CopyAscii(wszDir, RT_ELEMENTS(wszDir), pszDir);
4418    if (RT_FAILURE(rc))
4419    {
4420        SUP_DPRINTF(("supR3HardenedWinLogObjDir: RTUtf16CopyAscii -> %Rrc on '%s'\n", rc, pszDir));
4421        return;
4422    }
4423
4424    UNICODE_STRING NtDirName;
4425    NtDirName.Buffer = (WCHAR *)wszDir;
4426    NtDirName.Length = (USHORT)(RTUtf16Len(wszDir) * sizeof(WCHAR));
4427    NtDirName.MaximumLength = NtDirName.Length + sizeof(WCHAR);
4428
4429    OBJECT_ATTRIBUTES ObjAttr;
4430    InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4431
4432    HANDLE hDir;
4433    NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
4434    SUP_DPRINTF(("supR3HardenedWinLogObjDir: %ls => %#x\n", wszDir, rcNt));
4435    if (!NT_SUCCESS(rcNt))
4436        return;
4437
4438    /*
4439     * Enumerate it, looking for the driver.
4440     */
4441    ULONG uObjDirCtx = 0;
4442    for (;;)
4443    {
4444        uint32_t    abBuffer[_64K + _1K];
4445        ULONG       cbActual;
4446        rcNt = NtQueryDirectoryObject(hDir,
4447                                      abBuffer,
4448                                      sizeof(abBuffer) - 4, /* minus four for string terminator space. */
4449                                      FALSE /*ReturnSingleEntry */,
4450                                      FALSE /*RestartScan*/,
4451                                      &uObjDirCtx,
4452                                      &cbActual);
4453        if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
4454        {
4455            SUP_DPRINTF(("supR3HardenedWinLogObjDir: NtQueryDirectoryObject => rcNt=%#x cbActual=%#x\n", rcNt, cbActual));
4456            break;
4457        }
4458
4459        POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
4460        while (pObjDir->Name.Length != 0)
4461        {
4462            SUP_DPRINTF(("  %.*ls  %.*ls\n",
4463                         pObjDir->TypeName.Length / sizeof(WCHAR), pObjDir->TypeName.Buffer,
4464                         pObjDir->Name.Length / sizeof(WCHAR), pObjDir->Name.Buffer));
4465
4466            /* Next directory entry. */
4467            pObjDir++;
4468        }
4469    }
4470
4471    /*
4472     * Clean up and return.
4473     */
4474    NtClose(hDir);
4475}
4476
4477
4478/**
4479 * Tries to open VBoxDrvErrorInfo and read extra error info from it.
4480 *
4481 * @returns pszErrorInfo.
4482 * @param   pszErrorInfo        The destination buffer.  Will always be
4483 *                              terminated.
4484 * @param   cbErrorInfo         The size of the destination buffer.
4485 * @param   pszPrefix           What to prefix the error info with, if we got
4486 *                              anything.
4487 */
4488DECLHIDDEN(char *) supR3HardenedWinReadErrorInfoDevice(char *pszErrorInfo, size_t cbErrorInfo, const char *pszPrefix)
4489{
4490    RT_BZERO(pszErrorInfo, cbErrorInfo);
4491
4492    /*
4493     * Try open the device.
4494     */
4495    HANDLE              hFile  = RTNT_INVALID_HANDLE_VALUE;
4496    IO_STATUS_BLOCK     Ios    = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4497    UNICODE_STRING      NtName = RTNT_CONSTANT_UNISTR(SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
4498    OBJECT_ATTRIBUTES   ObjAttr;
4499    InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4500    NTSTATUS rcNt = NtCreateFile(&hFile,
4501                                 GENERIC_READ, /* No SYNCHRONIZE. */
4502                                 &ObjAttr,
4503                                 &Ios,
4504                                 NULL /* Allocation Size*/,
4505                                 FILE_ATTRIBUTE_NORMAL,
4506                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
4507                                 FILE_OPEN,
4508                                 FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
4509                                 NULL /*EaBuffer*/,
4510                                 0 /*EaLength*/);
4511    if (NT_SUCCESS(rcNt))
4512        rcNt = Ios.Status;
4513    if (NT_SUCCESS(rcNt))
4514    {
4515        /*
4516         * Try read error info.
4517         */
4518        size_t cchPrefix = strlen(pszPrefix);
4519        if (cchPrefix + 3 < cbErrorInfo)
4520        {
4521            LARGE_INTEGER offRead;
4522            offRead.QuadPart = 0;
4523            rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
4524                              &pszErrorInfo[cchPrefix], (ULONG)(cbErrorInfo - cchPrefix - 1), &offRead, NULL);
4525            if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status) && Ios.Information > 0)
4526            {
4527                memcpy(pszErrorInfo, pszPrefix, cchPrefix);
4528                pszErrorInfo[RT_MIN(cbErrorInfo - 1, cchPrefix + Ios.Information)] = '\0';
4529                SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: '%s'", &pszErrorInfo[cchPrefix]));
4530            }
4531            else
4532            {
4533                *pszErrorInfo = '\0';
4534                if (rcNt != STATUS_END_OF_FILE || Ios.Status != STATUS_END_OF_FILE)
4535                    SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtReadFile -> %#x / %#x / %p\n",
4536                                 rcNt, Ios.Status, Ios.Information));
4537            }
4538        }
4539        else
4540            RTStrCopy(pszErrorInfo, cbErrorInfo, "error info buffer too small");
4541        NtClose(hFile);
4542    }
4543    else
4544        SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtCreateFile -> %#x\n", rcNt));
4545
4546    return pszErrorInfo;
4547}
4548
4549
4550
4551/**
4552 * Checks if the driver exists.
4553 *
4554 * This checks whether the driver is present in the /Driver object directory.
4555 * Drivers being initialized or terminated will have an object there
4556 * before/after their devices nodes are created/deleted.
4557 *
4558 * @returns true if it exists, false if not.
4559 * @param   pszDriver           The driver name.
4560 */
4561static bool supR3HardenedWinDriverExists(const char *pszDriver)
4562{
4563    /*
4564     * Open the driver object directory.
4565     */
4566    UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
4567
4568    OBJECT_ATTRIBUTES ObjAttr;
4569    InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4570
4571    HANDLE hDir;
4572    NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
4573#ifdef VBOX_STRICT
4574    SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
4575#endif
4576    if (!NT_SUCCESS(rcNt))
4577        return true;
4578
4579    /*
4580     * Enumerate it, looking for the driver.
4581     */
4582    bool  fFound = true;
4583    ULONG uObjDirCtx = 0;
4584    do
4585    {
4586        uint32_t    abBuffer[_64K + _1K];
4587        ULONG       cbActual;
4588        rcNt = NtQueryDirectoryObject(hDir,
4589                                      abBuffer,
4590                                      sizeof(abBuffer) - 4, /* minus four for string terminator space. */
4591                                      FALSE /*ReturnSingleEntry */,
4592                                      FALSE /*RestartScan*/,
4593                                      &uObjDirCtx,
4594                                      &cbActual);
4595        if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
4596            break;
4597
4598        POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
4599        while (pObjDir->Name.Length != 0)
4600        {
4601            WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
4602            pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
4603            if (   pObjDir->Name.Length > 1
4604                && RTUtf16ICmpAscii(pObjDir->Name.Buffer, pszDriver) == 0)
4605            {
4606                fFound = true;
4607                break;
4608            }
4609            pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
4610
4611            /* Next directory entry. */
4612            pObjDir++;
4613        }
4614    } while (!fFound);
4615
4616    /*
4617     * Clean up and return.
4618     */
4619    NtClose(hDir);
4620
4621    return fFound;
4622}
4623
4624
4625/**
4626 * Open the stub device before the 2nd respawn.
4627 */
4628static void supR3HardenedWinOpenStubDevice(void)
4629{
4630    if (g_fSupStubOpened)
4631        return;
4632
4633    /*
4634     * Retry if we think driver might still be initializing (STATUS_NO_SUCH_DEVICE + \Drivers\VBoxDrv).
4635     */
4636    static const WCHAR  s_wszName[] = SUPDRV_NT_DEVICE_NAME_STUB;
4637    uint64_t const      uMsTsStart = supR3HardenedWinGetMilliTS();
4638    NTSTATUS            rcNt;
4639    uint32_t            iTry;
4640
4641    for (iTry = 0;; iTry++)
4642    {
4643        HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
4644        IO_STATUS_BLOCK     Ios   = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4645
4646        UNICODE_STRING      NtName;
4647        NtName.Buffer        = (PWSTR)s_wszName;
4648        NtName.Length        = sizeof(s_wszName) - sizeof(WCHAR);
4649        NtName.MaximumLength = sizeof(s_wszName);
4650
4651        OBJECT_ATTRIBUTES   ObjAttr;
4652        InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4653
4654        rcNt = NtCreateFile(&hFile,
4655                            GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */
4656                            &ObjAttr,
4657                            &Ios,
4658                            NULL /* Allocation Size*/,
4659                            FILE_ATTRIBUTE_NORMAL,
4660                            FILE_SHARE_READ | FILE_SHARE_WRITE,
4661                            FILE_OPEN,
4662                            FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
4663                            NULL /*EaBuffer*/,
4664                            0 /*EaLength*/);
4665        if (NT_SUCCESS(rcNt))
4666            rcNt = Ios.Status;
4667
4668        /* The STATUS_NO_SUCH_DEVICE might be returned if the device is not
4669           completely initialized.  Delay a little bit and try again. */
4670        if (rcNt != STATUS_NO_SUCH_DEVICE)
4671            break;
4672        if (iTry > 0 && supR3HardenedWinGetMilliTS() - uMsTsStart > 5000)  /* 5 sec, at least two tries */
4673            break;
4674        if (!supR3HardenedWinDriverExists("VBoxDrv"))
4675        {
4676            /** @todo Consider starting the VBoxdrv.sys service. Requires 2nd process
4677             *        though, rather complicated actually as CreateProcess causes all
4678             *        kind of things to happen to this process which would make it hard to
4679             *        pass the process verification tests... :-/ */
4680            break;
4681        }
4682
4683        LARGE_INTEGER Time;
4684        if (iTry < 8)
4685            Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
4686        else
4687            Time.QuadPart = -32000000 / 100; /* 32ms in 100ns units, relative time. */
4688        NtDelayExecution(TRUE, &Time);
4689    }
4690
4691    if (NT_SUCCESS(rcNt))
4692        g_fSupStubOpened = true;
4693    else
4694    {
4695        /*
4696         * Report trouble (fatal).  For some errors codes we try gather some
4697         * extra information that goes into VBoxStartup.log so that we stand a
4698         * better chance resolving the issue.
4699         */
4700        char szErrorInfo[16384];
4701        int rc = VERR_OPEN_FAILED;
4702        if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
4703        {
4704            rc = SUP_NT_STATUS_TO_VBOX(rcNt);
4705
4706            /*
4707             * \Windows\ApiPort open trouble.  So far only
4708             * STATUS_OBJECT_TYPE_MISMATCH has been observed.
4709             */
4710            if (rc == VERR_SUPDRV_APIPORT_OPEN_ERROR)
4711            {
4712                SUP_DPRINTF(("Error opening VBoxDrvStub: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"));
4713
4714                uint32_t uSessionId = NtCurrentPeb()->SessionId;
4715                SUP_DPRINTF(("  SessionID=%#x\n", uSessionId));
4716                char szDir[64];
4717                if (uSessionId == 0)
4718                    RTStrCopy(szDir, sizeof(szDir), "\\Windows");
4719                else
4720                {
4721                    RTStrPrintf(szDir, sizeof(szDir), "\\Sessions\\%u\\Windows", uSessionId);
4722                    supR3HardenedWinLogObjDir(szDir);
4723                }
4724                supR3HardenedWinLogObjDir("\\Windows");
4725                supR3HardenedWinLogObjDir("\\Sessions");
4726
4727                supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, rc,
4728                                      "NtCreateFile(%ls) failed: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"
4729                                      "\n"
4730                                      "Error getting %s\\ApiPort in the driver from vboxdrv.\n"
4731                                      "\n"
4732                                      "Could be due to security software is redirecting access to it, so please include full "
4733                                      "details of such software in a bug report. VBoxStartup.log may contain details important "
4734                                      "to resolving the issue.%s"
4735                                      , s_wszName, szDir,
4736                                      supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4737                                                                          "\n\nVBoxDrvStub error: "));
4738            }
4739
4740            /*
4741             * Generic VBox failure message.
4742             */
4743            supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, rc,
4744                                  "NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)%s", s_wszName, rc, rcNt,
4745                                  supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4746                                                                      "\nVBoxDrvStub error: "));
4747        }
4748        else
4749        {
4750            const char *pszDefine;
4751            switch (rcNt)
4752            {
4753                case STATUS_NO_SUCH_DEVICE:         pszDefine = " STATUS_NO_SUCH_DEVICE"; break;
4754                case STATUS_OBJECT_NAME_NOT_FOUND:  pszDefine = " STATUS_OBJECT_NAME_NOT_FOUND"; break;
4755                case STATUS_ACCESS_DENIED:          pszDefine = " STATUS_ACCESS_DENIED"; break;
4756                case STATUS_TRUST_FAILURE:          pszDefine = " STATUS_TRUST_FAILURE"; break;
4757                default:                            pszDefine = ""; break;
4758            }
4759
4760            /*
4761             * Problems opening the device is generally due to driver load/
4762             * unload issues.  Check whether the driver is loaded and make
4763             * suggestions accordingly.
4764             */
4765/** @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. */
4766            if (   rcNt == STATUS_NO_SUCH_DEVICE
4767                || rcNt == STATUS_OBJECT_NAME_NOT_FOUND)
4768            {
4769                SUP_DPRINTF(("Error opening VBoxDrvStub: %s\n", pszDefine));
4770                if (supR3HardenedWinDriverExists("VBoxDrv"))
4771                    supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4772                                          "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
4773                                          "\n"
4774                                          "Driver is probably stuck stopping/starting. Try 'sc.exe query vboxdrv' to get more "
4775                                          "information about its state. Rebooting may actually help.%s"
4776                                          , s_wszName, rcNt, pszDefine, iTry,
4777                                          supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4778                                                                              "\nVBoxDrvStub error: "));
4779                else
4780                    supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4781                                          "NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
4782                                          "\n"
4783                                          "Driver is does not appear to be loaded. Try 'sc.exe start vboxdrv', reinstall "
4784                                          "VirtualBox or reboot.%s"
4785                                          , s_wszName, rcNt, pszDefine, iTry,
4786                                          supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4787                                                                              "\nVBoxDrvStub error: "));
4788            }
4789
4790            /* Generic NT failure message. */
4791            supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
4792                                  "NtCreateFile(%ls) failed: %#x%s (%u retries)%s",
4793                                  s_wszName, rcNt, pszDefine, iTry,
4794                                  supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
4795                                                                      "\nVBoxDrvStub error: "));
4796        }
4797    }
4798}
4799
4800
4801/**
4802 * Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
4803 *
4804 * @returns Program exit code.
4805 */
4806DECLHIDDEN(int) supR3HardenedWinReSpawn(int iWhich)
4807{
4808    /*
4809     * Before the 2nd respawn we set up a child protection deal with the
4810     * support driver via /Devices/VBoxDrvStub.  (We tried to do this
4811     * during the early init, but in case we had trouble accessing vboxdrv we
4812     * retry it here where we have kernel32.dll and others to pull in for
4813     * better diagnostics.)
4814     */
4815    if (iWhich == 2)
4816        supR3HardenedWinOpenStubDevice();
4817
4818    /*
4819     * Make sure we're alone in the stub process before creating the VM process
4820     * and that there aren't any debuggers attached.
4821     */
4822    if (iWhich == 2)
4823    {
4824        int rc = supHardNtVpDebugger(NtCurrentProcess(), RTErrInfoInitStatic(&g_ErrInfoStatic));
4825        if (RT_SUCCESS(rc))
4826            rc = supHardNtVpThread(NtCurrentProcess(), NtCurrentThread(), RTErrInfoInitStatic(&g_ErrInfoStatic));
4827        if (RT_FAILURE(rc))
4828            supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Integrity, rc, "%s", g_ErrInfoStatic.szMsg);
4829    }
4830
4831
4832    /*
4833     * Respawn the process with kernel protection for the new process.
4834     */
4835    supR3HardenedWinDoReSpawn(iWhich);
4836    /* not reached! */
4837}
4838
4839
4840/**
4841 * Checks if re-spawning is required, replacing the respawn argument if not.
4842 *
4843 * @returns true if required, false if not. In the latter case, the first
4844 *          argument in the vector is replaced.
4845 * @param   iWhich              Which respawn we're to check for, 1 being the
4846 *                              first one, and 2 the second and final.
4847 * @param   cArgs               The number of arguments.
4848 * @param   papszArgs           Pointer to the argument vector.
4849 */
4850DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int iWhich, int cArgs, char **papszArgs)
4851{
4852    SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
4853    SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
4854
4855    if (cArgs < 1)
4856        return true;
4857
4858    if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
4859    {
4860        if (iWhich > 1)
4861            return true;
4862    }
4863    else if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
4864    {
4865        if (iWhich < 2)
4866            return false;
4867    }
4868    else
4869        return true;
4870
4871    /* Replace the argument. */
4872    papszArgs[0] = g_szSupLibHardenedExePath;
4873    return false;
4874}
4875
4876
4877/**
4878 * Initializes the windows verficiation bits and other things we're better off
4879 * doing after main() has passed on it's data.
4880 *
4881 * @param   fFlags          The main flags.
4882 * @param   fAvastKludge    Whether to apply the avast kludge.
4883 */
4884DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge)
4885{
4886    NTSTATUS rcNt;
4887
4888#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
4889    /*
4890     * Install a anti debugging hack before we continue.  This prevents most
4891     * notifications from ending up in the debugger. (Also applied to the
4892     * child process when respawning.)
4893     */
4894    rcNt = NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, NULL, 0);
4895    if (!NT_SUCCESS(rcNt))
4896        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
4897                              "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
4898#endif
4899
4900    /*
4901     * Init the verifier.
4902     */
4903    RTErrInfoInitStatic(&g_ErrInfoStatic);
4904    int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
4905    if (RT_FAILURE(rc))
4906        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
4907                              "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
4908
4909    /*
4910     * Get the windows system directory from the KnownDlls dir.
4911     */
4912    HANDLE              hSymlink = INVALID_HANDLE_VALUE;
4913    UNICODE_STRING      UniStr = RTNT_CONSTANT_UNISTR(L"\\KnownDlls\\KnownDllPath");
4914    OBJECT_ATTRIBUTES   ObjAttrs;
4915    InitializeObjectAttributes(&ObjAttrs, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
4916    rcNt = NtOpenSymbolicLinkObject(&hSymlink, SYMBOLIC_LINK_QUERY, &ObjAttrs);
4917    if (!NT_SUCCESS(rcNt))
4918        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error opening '%ls': %#x", UniStr.Buffer, rcNt);
4919
4920    g_System32WinPath.UniStr.Buffer = g_System32WinPath.awcBuffer;
4921    g_System32WinPath.UniStr.Length = 0;
4922    g_System32WinPath.UniStr.MaximumLength = sizeof(g_System32WinPath.awcBuffer) - sizeof(RTUTF16);
4923    rcNt = NtQuerySymbolicLinkObject(hSymlink, &g_System32WinPath.UniStr, NULL);
4924    if (!NT_SUCCESS(rcNt))
4925        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error querying '%ls': %#x", UniStr.Buffer, rcNt);
4926    g_System32WinPath.UniStr.Buffer[g_System32WinPath.UniStr.Length / sizeof(RTUTF16)] = '\0';
4927
4928    SUP_DPRINTF(("KnownDllPath: %ls\n", g_System32WinPath.UniStr.Buffer));
4929    NtClose(hSymlink);
4930
4931    if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
4932    {
4933        if (fAvastKludge)
4934        {
4935            /*
4936             * Do a self purification to cure avast's weird NtOpenFile write-thru
4937             * change in GetBinaryTypeW change in kernel32.  Unfortunately, avast
4938             * uses a system thread to perform the process modifications, which
4939             * means it's hard to make sure it had the chance to make them...
4940             *
4941             * We have to resort to kludge doing yield and sleep fudging for a
4942             * number of milliseconds and schedulings before we can hope that avast
4943             * and similar products have done what they need to do.  If we do any
4944             * fixes, we wait for a while again and redo it until we're clean.
4945             *
4946             * This is unfortunately kind of fragile.
4947             */
4948            uint32_t cMsFudge = g_fSupAdversaries ? 512 : 128;
4949            uint32_t cFixes;
4950            for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
4951            {
4952                uint32_t cSleeps = 0;
4953                uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
4954                do
4955                {
4956                    NtYieldExecution();
4957                    LARGE_INTEGER Time;
4958                    Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
4959                    NtDelayExecution(FALSE, &Time);
4960                    cSleeps++;
4961                } while (   supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
4962                         || cSleeps < 8);
4963                SUP_DPRINTF(("supR3HardenedWinInit: Startup delay kludge #2/%u: %u ms, %u sleeps\n",
4964                             iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
4965
4966                cFixes = 0;
4967                rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_SELF_PURIFICATION,
4968                                                 0 /*fFlags*/, &cFixes, NULL /*pErrInfo*/);
4969                if (RT_FAILURE(rc) || cFixes == 0)
4970                    break;
4971
4972                if (!g_fSupAdversaries)
4973                    g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
4974                cMsFudge = 512;
4975
4976                /* Log the KiOpPrefetchPatchCount value if available, hoping it might sched some light on spider38's case. */
4977                ULONG cPatchCount = 0;
4978                rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
4979                                                &cPatchCount, sizeof(cPatchCount), NULL);
4980                if (NT_SUCCESS(rcNt))
4981                    SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
4982                                 cFixes, g_fSupAdversaries, cPatchCount));
4983                else
4984                    SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
4985            }
4986        }
4987
4988        /*
4989         * Install the hooks.
4990         */
4991        supR3HardenedWinInstallHooks();
4992    }
4993
4994#ifndef VBOX_WITH_VISTA_NO_SP
4995    /*
4996     * Complain about Vista w/o service pack if we're launching a VM.
4997     */
4998    if (   !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
4999        && g_uNtVerCombined >= SUP_NT_VER_VISTA
5000        && g_uNtVerCombined <  SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
5001        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_NOT_SUPPORTED,
5002                              "Window Vista without any service pack installed is not supported. Please install the latest service pack.");
5003#endif
5004}
5005
5006
5007/**
5008 * Modifies the DLL search path for testcases.
5009 *
5010 * This makes sure the application binary path is in the search path.  When
5011 * starting a testcase executable in the testcase/ subdirectory this isn't the
5012 * case by default.  So, unless we do something about it we won't be able to
5013 * import VBox DLLs.
5014 *
5015 * @param   fFlags          The main flags (giving the location).
5016 * @param   pszAppBinPath   The path to the application binary directory
5017 *                          (windows style).
5018 */
5019DECLHIDDEN(void) supR3HardenedWinModifyDllSearchPath(uint32_t fFlags, const char *pszAppBinPath)
5020{
5021    /*
5022     * For the testcases to work, we must add the app bin directory to the
5023     * DLL search list before the testcase dll is loaded or it won't be
5024     * able to find the VBox DLLs.  This is done _after_ VBoxRT.dll is
5025     * initialized and sets its defaults.
5026     */
5027    switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
5028    {
5029        case SUPSECMAIN_FLAGS_LOC_TESTCASE:
5030            break;
5031        default:
5032            return;
5033    }
5034
5035    /*
5036     * Dynamically resolve the two APIs we need (the latter uses forwarders on w7).
5037     */
5038    HMODULE hModKernel32 = GetModuleHandleW(L"kernel32.dll");
5039
5040    typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR);
5041    PFNSETDLLDIRECTORY pfnSetDllDir;
5042    pfnSetDllDir     = (PFNSETDLLDIRECTORY)GetProcAddress(hModKernel32, "SetDllDirectoryW");
5043
5044    typedef BOOL (WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD);
5045    PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs;
5046    pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(hModKernel32, "SetDefaultDllDirectories");
5047
5048    if (pfnSetDllDir != NULL)
5049    {
5050        /*
5051         * Convert the path to UTF-16 and try set it.
5052         */
5053        PRTUTF16 pwszAppBinPath = NULL;
5054        int rc = RTStrToUtf16(pszAppBinPath, &pwszAppBinPath);
5055        if (RT_SUCCESS(rc))
5056        {
5057            if (pfnSetDllDir(pwszAppBinPath))
5058            {
5059                SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Set dll dir to '%ls'\n", pwszAppBinPath));
5060                g_fSupLibHardenedDllSearchUserDirs = true;
5061
5062                /*
5063                 * We set it alright, on W7 and later we also must modify the
5064                 * default DLL search order.  See @bugref{6861} for details on
5065                 * why we don't do this on Vista (also see init-win.cpp in IPRT).
5066                 */
5067                if (   pfnSetDefDllDirs
5068                    && g_uNtVerCombined >= SUP_NT_VER_W70)
5069                {
5070                    if (pfnSetDefDllDirs(  LOAD_LIBRARY_SEARCH_APPLICATION_DIR
5071                                         | LOAD_LIBRARY_SEARCH_SYSTEM32
5072                                         | LOAD_LIBRARY_SEARCH_USER_DIRS))
5073                        SUP_DPRINTF(("supR3HardenedWinModifyDllSearchPath: Successfully modified search dirs.\n"));
5074                    else
5075                        supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
5076                                           pwszAppBinPath, RtlGetLastWin32Error());
5077                }
5078            }
5079            else
5080                supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: SetDllDirectoryW(%ls) failed: %d\n",
5081                                   pwszAppBinPath, RtlGetLastWin32Error());
5082            RTUtf16Free(pwszAppBinPath);
5083        }
5084        else
5085            supR3HardenedFatal("supR3HardenedWinModifyDllSearchPath: RTStrToUtf16(%s) failed: %d\n", pszAppBinPath, rc);
5086    }
5087}
5088
5089
5090/**
5091 * Initializes the application binary directory path.
5092 *
5093 * This is called once or twice.
5094 *
5095 * @param   fFlags          The main flags (giving the location).
5096 */
5097DECLHIDDEN(void) supR3HardenedWinInitAppBin(uint32_t fFlags)
5098{
5099    USHORT cwc = (USHORT)g_offSupLibHardenedExeNtName - 1;
5100    g_SupLibHardenedAppBinNtPath.UniStr.Buffer = g_SupLibHardenedAppBinNtPath.awcBuffer;
5101    memcpy(g_SupLibHardenedAppBinNtPath.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Buffer, cwc * sizeof(WCHAR));
5102
5103    switch (fFlags & SUPSECMAIN_FLAGS_LOC_MASK)
5104    {
5105        case SUPSECMAIN_FLAGS_LOC_APP_BIN:
5106            break;
5107        case SUPSECMAIN_FLAGS_LOC_TESTCASE:
5108        {
5109            /* Drop one directory level. */
5110            USHORT off = cwc;
5111            WCHAR  wc;
5112            while (   off > 1
5113                   && (wc = g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 1]) != '\0')
5114                if (wc != '\\' && wc != '/')
5115                    off--;
5116                else
5117                {
5118                    if (g_SupLibHardenedAppBinNtPath.UniStr.Buffer[off - 2] == ':')
5119                        cwc = off;
5120                    else
5121                        cwc = off - 1;
5122                    break;
5123                }
5124            break;
5125        }
5126        default:
5127            supR3HardenedFatal("supR3HardenedWinInitAppBin: Unknown program binary location: %#x\n", fFlags);
5128    }
5129
5130    g_SupLibHardenedAppBinNtPath.UniStr.Buffer[cwc]   = '\0';
5131    g_SupLibHardenedAppBinNtPath.UniStr.Length        = cwc * sizeof(WCHAR);
5132    g_SupLibHardenedAppBinNtPath.UniStr.MaximumLength = sizeof(g_SupLibHardenedAppBinNtPath.awcBuffer);
5133    SUP_DPRINTF(("supR3HardenedWinInitAppBin(%#x): '%ls'\n", fFlags, g_SupLibHardenedAppBinNtPath.UniStr.Buffer));
5134}
5135
5136
5137/**
5138 * Converts the Windows command line string (UTF-16) to an array of UTF-8
5139 * arguments suitable for passing to main().
5140 *
5141 * @returns Pointer to the argument array.
5142 * @param   pawcCmdLine         The UTF-16 windows command line to parse.
5143 * @param   cwcCmdLine          The length of the command line.
5144 * @param   pcArgs              Where to return the number of arguments.
5145 */
5146static char **suplibCommandLineToArgvWStub(PCRTUTF16 pawcCmdLine, size_t cwcCmdLine, int *pcArgs)
5147{
5148    /*
5149     * Convert the command line string to UTF-8.
5150     */
5151    char *pszCmdLine = NULL;
5152    SUPR3HARDENED_ASSERT(RT_SUCCESS(RTUtf16ToUtf8Ex(pawcCmdLine, cwcCmdLine, &pszCmdLine, 0, NULL)));
5153
5154    /*
5155     * Parse the command line, carving argument strings out of it.
5156     */
5157    int    cArgs          = 0;
5158    int    cArgsAllocated = 4;
5159    char **papszArgs      = (char **)RTMemAllocZ(sizeof(char *) * cArgsAllocated);
5160    char  *pszSrc         = pszCmdLine;
5161    for (;;)
5162    {
5163        /* skip leading blanks. */
5164        char ch = *pszSrc;
5165        while (suplibCommandLineIsArgSeparator(ch))
5166            ch = *++pszSrc;
5167        if (!ch)
5168            break;
5169
5170        /* Add argument to the vector. */
5171        if (cArgs + 2 >= cArgsAllocated)
5172        {
5173            cArgsAllocated *= 2;
5174            papszArgs = (char **)RTMemRealloc(papszArgs, sizeof(char *) * cArgsAllocated);
5175        }
5176        papszArgs[cArgs++] = pszSrc;
5177        papszArgs[cArgs]   = NULL;
5178
5179        /* Unquote and unescape the string. */
5180        char *pszDst = pszSrc++;
5181        bool fQuoted = false;
5182        do
5183        {
5184            if (ch == '"')
5185                fQuoted = !fQuoted;
5186            else if (ch != '\\' || (*pszSrc != '\\' && *pszSrc != '"'))
5187                *pszDst++ = ch;
5188            else
5189            {
5190                unsigned cSlashes = 0;
5191                while ((ch = *pszSrc++) == '\\')
5192                    cSlashes++;
5193                if (ch == '"')
5194                {
5195                    while (cSlashes >= 2)
5196                    {
5197                        cSlashes -= 2;
5198                        *pszDst++ = '\\';
5199                    }
5200                    if (cSlashes)
5201                        *pszDst++ = '"';
5202                    else
5203                        fQuoted = !fQuoted;
5204                }
5205                else
5206                {
5207                    pszSrc--;
5208                    while (cSlashes-- > 0)
5209                        *pszDst++ = '\\';
5210                }
5211            }
5212
5213            ch = *pszSrc++;
5214        } while (ch != '\0' && (fQuoted || !suplibCommandLineIsArgSeparator(ch)));
5215
5216        /* Terminate the argument. */
5217        *pszDst = '\0';
5218        if (!ch)
5219            break;
5220    }
5221
5222    *pcArgs = cArgs;
5223    return papszArgs;
5224}
5225
5226
5227/**
5228 * Worker for supR3HardenedFindVersionRsrcOffset.
5229 *
5230 * @returns RVA the version resource data, UINT32_MAX if not found.
5231 * @param   pRootDir            The root resource directory.  Expects data to
5232 *                              follow.
5233 * @param   cbBuf               The amount of data at pRootDir.
5234 * @param   offData             The offset to the data entry.
5235 * @param   pcbData             Where to return the size of the data.
5236 */
5237static uint32_t supR3HardenedGetRvaFromRsrcDataEntry(PIMAGE_RESOURCE_DIRECTORY pRootDir, uint32_t cbBuf, uint32_t offData,
5238                                                     uint32_t *pcbData)
5239{
5240    if (   offData <= cbBuf
5241        && offData + sizeof(IMAGE_RESOURCE_DATA_ENTRY) <= cbBuf)
5242    {
5243        PIMAGE_RESOURCE_DATA_ENTRY pRsrcData = (PIMAGE_RESOURCE_DATA_ENTRY)((uintptr_t)pRootDir + offData);
5244        SUP_DPRINTF(("    [Raw version resource data: %#x LB %#x, codepage %#x (reserved %#x)]\n",
5245                     pRsrcData->OffsetToData, pRsrcData->Size, pRsrcData->CodePage, pRsrcData->Reserved));
5246        if (pRsrcData->Size > 0)
5247        {
5248            *pcbData = pRsrcData->Size;
5249            return pRsrcData->OffsetToData;
5250        }
5251    }
5252    else
5253        SUP_DPRINTF(("    Version resource data (%#x) is outside the buffer (%#x)! :-(\n", offData, cbBuf));
5254
5255    *pcbData = 0;
5256    return UINT32_MAX;
5257}
5258
5259
5260/** @def SUP_RSRC_DPRINTF
5261 * Dedicated debug printf for resource directory parsing.
5262 * @sa SUP_DPRINTF
5263 */
5264#if 0 /* more details */
5265# define SUP_RSRC_DPRINTF(a) SUP_DPRINTF(a)
5266#else
5267# define SUP_RSRC_DPRINTF(a) do { } while (0)
5268#endif
5269
5270/**
5271 * Scans the resource directory for a version resource.
5272 *
5273 * @returns RVA of the version resource data, UINT32_MAX if not found.
5274 * @param   pRootDir            The root resource directory.  Expects data to
5275 *                              follow.
5276 * @param   cbBuf               The amount of data at pRootDir.
5277 * @param   pcbData             Where to return the size of the version data.
5278 */
5279static uint32_t supR3HardenedFindVersionRsrcRva(PIMAGE_RESOURCE_DIRECTORY pRootDir, uint32_t cbBuf, uint32_t *pcbData)
5280{
5281    SUP_RSRC_DPRINTF(("    ResDir: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
5282                      pRootDir->Characteristics,
5283                      pRootDir->TimeDateStamp,
5284                      pRootDir->MajorVersion,
5285                      pRootDir->MinorVersion,
5286                      pRootDir->NumberOfNamedEntries,
5287                      pRootDir->NumberOfIdEntries));
5288
5289    PIMAGE_RESOURCE_DIRECTORY_ENTRY paEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRootDir + 1);
5290    unsigned cMaxEntries = (cbBuf - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
5291    unsigned cEntries    = pRootDir->NumberOfNamedEntries + pRootDir->NumberOfIdEntries;
5292    if (cEntries > cMaxEntries)
5293        cEntries = cMaxEntries;
5294    for (unsigned i = 0; i < cEntries; i++)
5295    {
5296        if (!paEntries[i].NameIsString)
5297        {
5298            if (!paEntries[i].DataIsDirectory)
5299                SUP_RSRC_DPRINTF(("    #%u:   ID: #%#06x  Data: %#010x\n",
5300                                i, paEntries[i].Id, paEntries[i].OffsetToData));
5301            else
5302                SUP_RSRC_DPRINTF(("    #%u:   ID: #%#06x  Dir: %#010x\n",
5303                                i, paEntries[i].Id, paEntries[i].OffsetToDirectory));
5304        }
5305        else
5306        {
5307            if (!paEntries[i].DataIsDirectory)
5308                SUP_RSRC_DPRINTF(("    #%u: Name: #%#06x  Data: %#010x\n",
5309                                i, paEntries[i].NameOffset, paEntries[i].OffsetToData));
5310            else
5311                SUP_RSRC_DPRINTF(("    #%u: Name: #%#06x  Dir: %#010x\n",
5312                                i, paEntries[i].NameOffset, paEntries[i].OffsetToDirectory));
5313        }
5314
5315        /*
5316         * Look for the version resource type.  Skip to the next entry if not found.
5317         */
5318        if (paEntries[i].NameIsString)
5319            continue;
5320        if (paEntries[i].Id != 0x10 /*RT_VERSION*/)
5321            continue;
5322        if (!paEntries[i].DataIsDirectory)
5323        {
5324            SUP_DPRINTF(("    #%u:   ID: #%#06x  Data: %#010x - WEIRD!\n", i, paEntries[i].Id, paEntries[i].OffsetToData));
5325            continue;
5326        }
5327        SUP_RSRC_DPRINTF(("    Version resource dir entry #%u: dir offset: %#x (cbBuf=%#x)\n",
5328                          i, paEntries[i].OffsetToDirectory, cbBuf));
5329
5330        /*
5331         * Locate the sub-resource directory for it.
5332         */
5333        if (paEntries[i].OffsetToDirectory >= cbBuf)
5334        {
5335            SUP_DPRINTF(("    Version resource dir is outside the buffer! :-(\n"));
5336            continue;
5337        }
5338        uint32_t cbMax = cbBuf - paEntries[i].OffsetToDirectory;
5339        if (cbMax < sizeof(IMAGE_RESOURCE_DIRECTORY) + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))
5340        {
5341            SUP_DPRINTF(("    Version resource dir entry #0 is outside the buffer! :-(\n"));
5342            continue;
5343        }
5344        PIMAGE_RESOURCE_DIRECTORY pVerDir = (PIMAGE_RESOURCE_DIRECTORY)((uintptr_t)pRootDir + paEntries[i].OffsetToDirectory);
5345        SUP_RSRC_DPRINTF(("    VerDir: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
5346                          pVerDir->Characteristics,
5347                          pVerDir->TimeDateStamp,
5348                          pVerDir->MajorVersion,
5349                          pVerDir->MinorVersion,
5350                          pVerDir->NumberOfNamedEntries,
5351                          pVerDir->NumberOfIdEntries));
5352        PIMAGE_RESOURCE_DIRECTORY_ENTRY paVerEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVerDir + 1);
5353        unsigned cMaxVerEntries = (cbMax - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
5354        unsigned cVerEntries    = pVerDir->NumberOfNamedEntries + pVerDir->NumberOfIdEntries;
5355        if (cVerEntries > cMaxVerEntries)
5356            cVerEntries = cMaxVerEntries;
5357        for (unsigned iVer = 0; iVer < cVerEntries; iVer++)
5358        {
5359            if (!paVerEntries[iVer].NameIsString)
5360            {
5361                if (!paVerEntries[iVer].DataIsDirectory)
5362                    SUP_RSRC_DPRINTF(("      #%u:   ID: #%#06x  Data: %#010x\n",
5363                                      iVer, paVerEntries[iVer].Id, paVerEntries[iVer].OffsetToData));
5364                else
5365                    SUP_RSRC_DPRINTF(("      #%u:   ID: #%#06x  Dir: %#010x\n",
5366                                      iVer, paVerEntries[iVer].Id, paVerEntries[iVer].OffsetToDirectory));
5367            }
5368            else
5369            {
5370                if (!paVerEntries[iVer].DataIsDirectory)
5371                    SUP_RSRC_DPRINTF(("      #%u: Name: #%#06x  Data: %#010x\n",
5372                                      iVer, paVerEntries[iVer].NameOffset, paVerEntries[iVer].OffsetToData));
5373                else
5374                    SUP_RSRC_DPRINTF(("      #%u: Name: #%#06x  Dir: %#010x\n",
5375                                      iVer, paVerEntries[iVer].NameOffset, paVerEntries[iVer].OffsetToDirectory));
5376            }
5377            if (!paVerEntries[iVer].DataIsDirectory)
5378            {
5379                SUP_DPRINTF(("    [Version info resource found at %#x! (ID/Name: #%#x)]\n",
5380                             paVerEntries[iVer].OffsetToData, paVerEntries[iVer].Name));
5381                return supR3HardenedGetRvaFromRsrcDataEntry(pRootDir, cbBuf, paVerEntries[iVer].OffsetToData, pcbData);
5382            }
5383
5384            /*
5385             * Check out the next directory level.
5386             */
5387            if (paVerEntries[iVer].OffsetToDirectory >= cbBuf)
5388            {
5389                SUP_DPRINTF(("    Version resource subdir is outside the buffer! :-(\n"));
5390                continue;
5391            }
5392            cbMax = cbBuf - paVerEntries[iVer].OffsetToDirectory;
5393            if (cbMax < sizeof(IMAGE_RESOURCE_DIRECTORY) + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))
5394            {
5395                SUP_DPRINTF(("    Version resource subdir entry #0 is outside the buffer! :-(\n"));
5396                continue;
5397            }
5398            PIMAGE_RESOURCE_DIRECTORY pVerSubDir = (PIMAGE_RESOURCE_DIRECTORY)((uintptr_t)pRootDir + paVerEntries[iVer].OffsetToDirectory);
5399            SUP_RSRC_DPRINTF(("      VerSubDir#%u: Char=%#x Time=%#x Ver=%d%d #NamedEntries=%#x #IdEntries=%#x\n",
5400                              iVer,
5401                              pVerSubDir->Characteristics,
5402                              pVerSubDir->TimeDateStamp,
5403                              pVerSubDir->MajorVersion,
5404                              pVerSubDir->MinorVersion,
5405                              pVerSubDir->NumberOfNamedEntries,
5406                              pVerSubDir->NumberOfIdEntries));
5407            PIMAGE_RESOURCE_DIRECTORY_ENTRY paVerSubEntries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pVerSubDir + 1);
5408            unsigned cMaxVerSubEntries = (cbMax - sizeof(IMAGE_RESOURCE_DIRECTORY)) / sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
5409            unsigned cVerSubEntries    = pVerSubDir->NumberOfNamedEntries + pVerSubDir->NumberOfIdEntries;
5410            if (cVerSubEntries > cMaxVerSubEntries)
5411                cVerSubEntries = cMaxVerSubEntries;
5412            for (unsigned iVerSub = 0; iVerSub < cVerSubEntries; iVerSub++)
5413            {
5414                if (!paVerSubEntries[iVerSub].NameIsString)
5415                {
5416                    if (!paVerSubEntries[iVerSub].DataIsDirectory)
5417                        SUP_RSRC_DPRINTF(("        #%u:   ID: #%#06x  Data: %#010x\n",
5418                                          iVerSub, paVerSubEntries[iVerSub].Id, paVerSubEntries[iVerSub].OffsetToData));
5419                    else
5420                        SUP_RSRC_DPRINTF(("        #%u:   ID: #%#06x  Dir: %#010x\n",
5421                                          iVerSub, paVerSubEntries[iVerSub].Id, paVerSubEntries[iVerSub].OffsetToDirectory));
5422                }
5423                else
5424                {
5425                    if (!paVerSubEntries[iVerSub].DataIsDirectory)
5426                        SUP_RSRC_DPRINTF(("        #%u: Name: #%#06x  Data: %#010x\n",
5427                                          iVerSub, paVerSubEntries[iVerSub].NameOffset, paVerSubEntries[iVerSub].OffsetToData));
5428                    else
5429                        SUP_RSRC_DPRINTF(("        #%u: Name: #%#06x  Dir: %#010x\n",
5430                                          iVerSub, paVerSubEntries[iVerSub].NameOffset, paVerSubEntries[iVerSub].OffsetToDirectory));
5431                }
5432                if (!paVerSubEntries[iVerSub].DataIsDirectory)
5433                {
5434                    SUP_DPRINTF(("    [Version info resource found at %#x! (ID/Name: %#x; SubID/SubName: %#x)]\n",
5435                                 paVerSubEntries[iVerSub].OffsetToData, paVerEntries[iVer].Name, paVerSubEntries[iVerSub].Name));
5436                    return supR3HardenedGetRvaFromRsrcDataEntry(pRootDir, cbBuf, paVerSubEntries[iVerSub].OffsetToData, pcbData);
5437                }
5438            }
5439        }
5440    }
5441
5442    *pcbData = 0;
5443    return UINT32_MAX;
5444}
5445
5446
5447/**
5448 * Logs information about a file from a protection product or from Windows,
5449 * optionally returning the file version.
5450 *
5451 * The purpose here is to better see which version of the product is installed
5452 * and not needing to depend on the user supplying the correct information.
5453 *
5454 * @param   pwszFile        The NT path to the file.
5455 * @param   pwszFileVersion Where to return the file version, if found. NULL if
5456 *                          not interested.
5457 * @param   cwcFileVersion  The size of the file version buffer (UTF-16 units).
5458 */
5459static void supR3HardenedLogFileInfo(PCRTUTF16 pwszFile, PRTUTF16 pwszFileVersion, size_t cwcFileVersion)
5460{
5461    /*
5462     * Make sure the file version is always set when we return.
5463     */
5464    if (pwszFileVersion && cwcFileVersion)
5465        *pwszFileVersion = '\0';
5466
5467    /*
5468     * Open the file.
5469     */
5470    HANDLE              hFile  = RTNT_INVALID_HANDLE_VALUE;
5471    IO_STATUS_BLOCK     Ios    = RTNT_IO_STATUS_BLOCK_INITIALIZER;
5472    UNICODE_STRING      UniStrName;
5473    UniStrName.Buffer = (WCHAR *)pwszFile;
5474    UniStrName.Length = (USHORT)(RTUtf16Len(pwszFile) * sizeof(WCHAR));
5475    UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
5476    OBJECT_ATTRIBUTES   ObjAttr;
5477    InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
5478    NTSTATUS rcNt = NtCreateFile(&hFile,
5479                                 GENERIC_READ | SYNCHRONIZE,
5480                                 &ObjAttr,
5481                                 &Ios,
5482                                 NULL /* Allocation Size*/,
5483                                 FILE_ATTRIBUTE_NORMAL,
5484                                 FILE_SHARE_READ,
5485                                 FILE_OPEN,
5486                                 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
5487                                 NULL /*EaBuffer*/,
5488                                 0 /*EaLength*/);
5489    if (NT_SUCCESS(rcNt))
5490        rcNt = Ios.Status;
5491    if (NT_SUCCESS(rcNt))
5492    {
5493        SUP_DPRINTF(("%ls:\n", pwszFile));
5494        union
5495        {
5496            uint64_t                    u64AlignmentInsurance;
5497            FILE_BASIC_INFORMATION      BasicInfo;
5498            FILE_STANDARD_INFORMATION   StdInfo;
5499            uint8_t                     abBuf[32768];
5500            RTUTF16                     awcBuf[16384];
5501            IMAGE_DOS_HEADER            MzHdr;
5502            IMAGE_RESOURCE_DIRECTORY    ResDir;
5503        } u;
5504        RTTIMESPEC  TimeSpec;
5505        char        szTmp[64];
5506
5507        /*
5508         * Print basic file information available via NtQueryInformationFile.
5509         */
5510        IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
5511        rcNt = NtQueryInformationFile(hFile, &Ios, &u.BasicInfo, sizeof(u.BasicInfo), FileBasicInformation);
5512        if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5513        {
5514            SUP_DPRINTF(("    CreationTime:    %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.CreationTime.QuadPart), szTmp, sizeof(szTmp))));
5515            /*SUP_DPRINTF(("    LastAccessTime:  %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastAccessTime.QuadPart), szTmp, sizeof(szTmp))));*/
5516            SUP_DPRINTF(("    LastWriteTime:   %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastWriteTime.QuadPart), szTmp, sizeof(szTmp))));
5517            SUP_DPRINTF(("    ChangeTime:      %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.ChangeTime.QuadPart), szTmp, sizeof(szTmp))));
5518            SUP_DPRINTF(("    FileAttributes:  %#x\n", u.BasicInfo.FileAttributes));
5519        }
5520        else
5521            SUP_DPRINTF(("    FileBasicInformation -> %#x %#x\n", rcNt, Ios.Status));
5522
5523        rcNt = NtQueryInformationFile(hFile, &Ios, &u.StdInfo, sizeof(u.StdInfo), FileStandardInformation);
5524        if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5525            SUP_DPRINTF(("    Size:            %#llx\n", u.StdInfo.EndOfFile.QuadPart));
5526        else
5527            SUP_DPRINTF(("    FileStandardInformation -> %#x %#x\n", rcNt, Ios.Status));
5528
5529        /*
5530         * Read the image header and extract the timestamp and other useful info.
5531         */
5532        RT_ZERO(u);
5533        LARGE_INTEGER offRead;
5534        offRead.QuadPart = 0;
5535        rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
5536                          &u, (ULONG)sizeof(u), &offRead, NULL);
5537        if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5538        {
5539            uint32_t offNtHdrs = 0;
5540            if (u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE)
5541                offNtHdrs = u.MzHdr.e_lfanew;
5542            if (offNtHdrs < sizeof(u) - sizeof(IMAGE_NT_HEADERS))
5543            {
5544                PIMAGE_NT_HEADERS64 pNtHdrs64 = (PIMAGE_NT_HEADERS64)&u.abBuf[offNtHdrs];
5545                PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)&u.abBuf[offNtHdrs];
5546                if (pNtHdrs64->Signature == IMAGE_NT_SIGNATURE)
5547                {
5548                    SUP_DPRINTF(("    NT Headers:      %#x\n", offNtHdrs));
5549                    SUP_DPRINTF(("    Timestamp:       %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
5550                    SUP_DPRINTF(("    Machine:         %#x%s\n", pNtHdrs64->FileHeader.Machine,
5551                                 pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ? " - i386"
5552                                 : pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ? " - amd64" : ""));
5553                    SUP_DPRINTF(("    Timestamp:       %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
5554                    SUP_DPRINTF(("    Image Version:   %u.%u\n",
5555                                 pNtHdrs64->OptionalHeader.MajorImageVersion, pNtHdrs64->OptionalHeader.MinorImageVersion));
5556                    SUP_DPRINTF(("    SizeOfImage:     %#x (%u)\n", pNtHdrs64->OptionalHeader.SizeOfImage, pNtHdrs64->OptionalHeader.SizeOfImage));
5557
5558                    /*
5559                     * Very crude way to extract info from the file version resource.
5560                     */
5561                    PIMAGE_SECTION_HEADER paSectHdrs = (PIMAGE_SECTION_HEADER)(  (uintptr_t)&pNtHdrs64->OptionalHeader
5562                                                                               + pNtHdrs64->FileHeader.SizeOfOptionalHeader);
5563                    IMAGE_DATA_DIRECTORY  RsrcDir = { 0, 0 };
5564                    if (   pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)
5565                        && pNtHdrs64->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
5566                        RsrcDir = pNtHdrs64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
5567                    else if (   pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
5568                             && pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
5569                        RsrcDir = pNtHdrs32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
5570                    SUP_DPRINTF(("    Resource Dir:    %#x LB %#x\n", RsrcDir.VirtualAddress, RsrcDir.Size));
5571                    if (   RsrcDir.VirtualAddress > offNtHdrs
5572                        && RsrcDir.Size > 0
5573                        &&    (uintptr_t)&u + sizeof(u) - (uintptr_t)paSectHdrs
5574                           >= pNtHdrs64->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) )
5575                    {
5576                        uint32_t uRvaRsrcSect = 0;
5577                        uint32_t cbRsrcSect   = 0;
5578                        uint32_t offRsrcSect  = 0;
5579                        offRead.QuadPart = 0;
5580                        for (uint32_t i = 0; i < pNtHdrs64->FileHeader.NumberOfSections; i++)
5581                        {
5582                            uRvaRsrcSect = paSectHdrs[i].VirtualAddress;
5583                            cbRsrcSect   = paSectHdrs[i].Misc.VirtualSize;
5584                            offRsrcSect  = paSectHdrs[i].PointerToRawData;
5585                            if (   RsrcDir.VirtualAddress - uRvaRsrcSect < cbRsrcSect
5586                                && offRsrcSect > offNtHdrs)
5587                            {
5588                                offRead.QuadPart = offRsrcSect + (RsrcDir.VirtualAddress - uRvaRsrcSect);
5589                                break;
5590                            }
5591                        }
5592                        if (offRead.QuadPart > 0)
5593                        {
5594                            RT_ZERO(u);
5595                            rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
5596                                              &u, (ULONG)sizeof(u), &offRead, NULL);
5597                            PCRTUTF16 pwcVersionData = &u.awcBuf[0];
5598                            size_t    cbVersionData  = sizeof(u);
5599
5600                            if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5601                            {
5602                                /* Make it less crude by try find the version resource data. */
5603                                uint32_t  cbVersion;
5604                                uint32_t  uRvaVersion = supR3HardenedFindVersionRsrcRva(&u.ResDir, sizeof(u), &cbVersion);
5605                                NOREF(uRvaVersion);
5606                                if (   uRvaVersion != UINT32_MAX
5607                                    && cbVersion < cbRsrcSect
5608                                    && uRvaVersion - uRvaRsrcSect <= cbRsrcSect - cbVersion)
5609                                {
5610                                    uint32_t const offVersion = uRvaVersion - uRvaRsrcSect;
5611                                    if (   offVersion < sizeof(u)
5612                                        && offVersion + cbVersion <= sizeof(u))
5613                                    {
5614                                        pwcVersionData = (PCRTUTF16)&u.abBuf[offVersion];
5615                                        cbVersionData  = cbVersion;
5616                                    }
5617                                    else
5618                                    {
5619                                        offRead.QuadPart = offVersion + offRsrcSect;
5620                                        RT_ZERO(u);
5621                                        rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
5622                                                          &u, (ULONG)sizeof(u), &offRead, NULL);
5623                                        pwcVersionData = &u.awcBuf[0];
5624                                        cbVersionData  = RT_MIN(cbVersion, sizeof(u));
5625                                    }
5626                                }
5627                            }
5628
5629                            if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
5630                            {
5631                                static const struct { PCRTUTF16 pwsz; size_t cb; bool fRet; } s_abFields[] =
5632                                {
5633#define MY_WIDE_STR_TUPLE(a_sz, a_fRet) { L ## a_sz, sizeof(L ## a_sz) - sizeof(RTUTF16), a_fRet }
5634                                    MY_WIDE_STR_TUPLE("ProductName",        false),
5635                                    MY_WIDE_STR_TUPLE("ProductVersion",     false),
5636                                    MY_WIDE_STR_TUPLE("FileVersion",        true),
5637                                    MY_WIDE_STR_TUPLE("SpecialBuild",       false),
5638                                    MY_WIDE_STR_TUPLE("PrivateBuild",       false),
5639                                    MY_WIDE_STR_TUPLE("FileDescription",    false),
5640#undef MY_WIDE_STR_TUPLE
5641                                };
5642                                for (uint32_t i = 0; i < RT_ELEMENTS(s_abFields); i++)
5643                                {
5644                                    if (cbVersionData <= s_abFields[i].cb + 10)
5645                                        continue;
5646                                    size_t          cwcLeft = (cbVersionData - s_abFields[i].cb - 10) / sizeof(RTUTF16);
5647                                    PCRTUTF16       pwc     = pwcVersionData;
5648                                    RTUTF16 const   wcFirst = *s_abFields[i].pwsz;
5649                                    while (cwcLeft-- > 0)
5650                                    {
5651                                        if (   pwc[0] == 1 /* wType == text */
5652                                            && pwc[1] == wcFirst)
5653                                        {
5654                                            if (memcmp(pwc + 1, s_abFields[i].pwsz, s_abFields[i].cb + sizeof(RTUTF16)) == 0)
5655                                            {
5656                                                size_t cwcField = s_abFields[i].cb / sizeof(RTUTF16);
5657                                                pwc     += cwcField + 2;
5658                                                cwcLeft -= cwcField + 2;
5659                                                for (uint32_t iPadding = 0; iPadding < 3; iPadding++, pwc++, cwcLeft--)
5660                                                    if (*pwc)
5661                                                        break;
5662                                                int rc = RTUtf16ValidateEncodingEx(pwc, cwcLeft,
5663                                                                                   RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
5664                                                if (RT_SUCCESS(rc))
5665                                                {
5666                                                    SUP_DPRINTF(("    %ls:%*s %ls",
5667                                                                 s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", pwc));
5668                                                    if (   s_abFields[i].fRet
5669                                                        && pwszFileVersion
5670                                                        && cwcFileVersion > 1)
5671                                                        RTUtf16Copy(pwszFileVersion, cwcFileVersion, pwc);
5672                                                }
5673                                                else
5674                                                    SUP_DPRINTF(("    %ls:%*s rc=%Rrc",
5675                                                                 s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", rc));
5676
5677                                                break;
5678                                            }
5679                                        }
5680                                        pwc++;
5681                                    }
5682                                }
5683                            }
5684                            else
5685                                SUP_DPRINTF(("    NtReadFile @%#llx -> %#x %#x\n", offRead.QuadPart, rcNt, Ios.Status));
5686                        }
5687                        else
5688                            SUP_DPRINTF(("    Resource section not found.\n"));
5689                    }
5690                }
5691                else
5692                    SUP_DPRINTF(("    Nt Headers @%#x: Invalid signature\n", offNtHdrs));
5693            }
5694            else
5695                SUP_DPRINTF(("    Nt Headers @%#x: out side buffer\n", offNtHdrs));
5696        }
5697        else
5698            SUP_DPRINTF(("    NtReadFile @0 -> %#x %#x\n", rcNt, Ios.Status));
5699        NtClose(hFile);
5700    }
5701}
5702
5703
5704/**
5705 * Scans the Driver directory for drivers which may invade our processes.
5706 *
5707 * @returns Mask of SUPHARDNT_ADVERSARY_XXX flags.
5708 *
5709 * @remarks The enumeration of \\Driver normally requires administrator
5710 *          privileges.  So, the detection we're doing here isn't always gonna
5711 *          work just based on that.
5712 *
5713 * @todo    Find drivers in \\FileSystems as well, then we could detect VrNsdDrv
5714 *          from ViRobot APT Shield 2.0.
5715 */
5716static uint32_t supR3HardenedWinFindAdversaries(void)
5717{
5718    static const struct
5719    {
5720        uint32_t    fAdversary;
5721        const char *pszDriver;
5722    } s_aDrivers[] =
5723    {
5724        { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT,    "SysPlant" },
5725
5726        { SUPHARDNT_ADVERSARY_SYMANTEC_N360,        "SRTSPX" },
5727        { SUPHARDNT_ADVERSARY_SYMANTEC_N360,        "SymDS" },
5728        { SUPHARDNT_ADVERSARY_SYMANTEC_N360,        "SymEvent" },
5729        { SUPHARDNT_ADVERSARY_SYMANTEC_N360,        "SymIRON" },
5730        { SUPHARDNT_ADVERSARY_SYMANTEC_N360,        "SymNetS" },
5731
5732        { SUPHARDNT_ADVERSARY_AVAST,                "aswHwid" },
5733        { SUPHARDNT_ADVERSARY_AVAST,                "aswMonFlt" },
5734        { SUPHARDNT_ADVERSARY_AVAST,                "aswRdr2" },
5735        { SUPHARDNT_ADVERSARY_AVAST,                "aswRvrt" },
5736        { SUPHARDNT_ADVERSARY_AVAST,                "aswSnx" },
5737        { SUPHARDNT_ADVERSARY_AVAST,                "aswsp" },
5738        { SUPHARDNT_ADVERSARY_AVAST,                "aswStm" },
5739        { SUPHARDNT_ADVERSARY_AVAST,                "aswVmm" },
5740
5741        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmcomm" },
5742        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmactmon" },
5743        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmevtmgr" },
5744        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmtdi" },
5745        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmebc64" },  /* Titanium internet security, not officescan. */
5746        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmeevw" },   /* Titanium internet security, not officescan. */
5747        { SUPHARDNT_ADVERSARY_TRENDMICRO,           "tmciesc" },  /* Titanium internet security, not officescan. */
5748
5749        { SUPHARDNT_ADVERSARY_MCAFEE,               "cfwids" },
5750        { SUPHARDNT_ADVERSARY_MCAFEE,               "McPvDrv" },
5751        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfeapfk" },
5752        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfeavfk" },
5753        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfefirek" },
5754        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfehidk" },
5755        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfencbdc" },
5756        { SUPHARDNT_ADVERSARY_MCAFEE,               "mfewfpk" },
5757
5758        { SUPHARDNT_ADVERSARY_KASPERSKY,            "kl1" },
5759        { SUPHARDNT_ADVERSARY_KASPERSKY,            "klflt" },
5760        { SUPHARDNT_ADVERSARY_KASPERSKY,            "klif" },
5761        { SUPHARDNT_ADVERSARY_KASPERSKY,            "KLIM6" },
5762        { SUPHARDNT_ADVERSARY_KASPERSKY,            "klkbdflt" },
5763        { SUPHARDNT_ADVERSARY_KASPERSKY,            "klmouflt" },
5764        { SUPHARDNT_ADVERSARY_KASPERSKY,            "kltdi" },
5765        { SUPHARDNT_ADVERSARY_KASPERSKY,            "kneps" },
5766
5767        { SUPHARDNT_ADVERSARY_MBAM,                 "MBAMWebAccessControl" },
5768        { SUPHARDNT_ADVERSARY_MBAM,                 "mbam" },
5769        { SUPHARDNT_ADVERSARY_MBAM,                 "mbamchameleon" },
5770        { SUPHARDNT_ADVERSARY_MBAM,                 "mwav" },
5771        { SUPHARDNT_ADVERSARY_MBAM,                 "mbamswissarmy" },
5772
5773        { SUPHARDNT_ADVERSARY_AVG,                  "avgfwfd" },
5774        { SUPHARDNT_ADVERSARY_AVG,                  "avgtdia" },
5775
5776        { SUPHARDNT_ADVERSARY_PANDA,                "PSINAflt" },
5777        { SUPHARDNT_ADVERSARY_PANDA,                "PSINFile" },
5778        { SUPHARDNT_ADVERSARY_PANDA,                "PSINKNC" },
5779        { SUPHARDNT_ADVERSARY_PANDA,                "PSINProc" },
5780        { SUPHARDNT_ADVERSARY_PANDA,                "PSINProt" },
5781        { SUPHARDNT_ADVERSARY_PANDA,                "PSINReg" },
5782        { SUPHARDNT_ADVERSARY_PANDA,                "PSKMAD" },
5783        { SUPHARDNT_ADVERSARY_PANDA,                "NNSAlpc" },
5784        { SUPHARDNT_ADVERSARY_PANDA,                "NNSHttp" },
5785        { SUPHARDNT_ADVERSARY_PANDA,                "NNShttps" },
5786        { SUPHARDNT_ADVERSARY_PANDA,                "NNSIds" },
5787        { SUPHARDNT_ADVERSARY_PANDA,                "NNSNAHSL" },
5788        { SUPHARDNT_ADVERSARY_PANDA,                "NNSpicc" },
5789        { SUPHARDNT_ADVERSARY_PANDA,                "NNSPihsw" },
5790        { SUPHARDNT_ADVERSARY_PANDA,                "NNSPop3" },
5791        { SUPHARDNT_ADVERSARY_PANDA,                "NNSProt" },
5792        { SUPHARDNT_ADVERSARY_PANDA,                "NNSPrv" },
5793        { SUPHARDNT_ADVERSARY_PANDA,                "NNSSmtp" },
5794        { SUPHARDNT_ADVERSARY_PANDA,                "NNSStrm" },
5795        { SUPHARDNT_ADVERSARY_PANDA,                "NNStlsc" },
5796
5797        { SUPHARDNT_ADVERSARY_MSE,                  "NisDrv" },
5798
5799        /*{ SUPHARDNT_ADVERSARY_COMODO, "cmdguard" }, file system */
5800        { SUPHARDNT_ADVERSARY_COMODO,               "inspect" },
5801        { SUPHARDNT_ADVERSARY_COMODO,               "cmdHlp" },
5802
5803        { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD, "dgmaster" },
5804
5805        { SUPHARDNT_ADVERSARY_CYLANCE,              "cyprotectdrv" }, /* Not verified. */
5806
5807        { SUPHARDNT_ADVERSARY_BEYONDTRUST,          "privman" }, /* Not verified. */
5808
5809        { SUPHARDNT_ADVERSARY_AVECTO,               "PGDriver" },
5810    };
5811
5812    static const struct
5813    {
5814        uint32_t    fAdversary;
5815        PCRTUTF16   pwszFile;
5816    } s_aFiles[] =
5817    {
5818        { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\drivers\\SysPlant.sys" },
5819        { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysfer.dll" },
5820        { SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT, L"\\SystemRoot\\System32\\sysferThunk.dll" },
5821
5822        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ccsetx64.sys" },
5823        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ironx64.sys" },
5824        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtsp64.sys" },
5825        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtspx64.sys" },
5826        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symds64.sys" },
5827        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symefa64.sys" },
5828        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symelam.sys" },
5829        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symnets.sys" },
5830        { SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\symevent64x86.sys" },
5831
5832        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswHwid.sys" },
5833        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswMonFlt.sys" },
5834        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRdr2.sys" },
5835        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRvrt.sys" },
5836        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswSnx.sys" },
5837        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswsp.sys" },
5838        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswStm.sys" },
5839        { SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswVmm.sys" },
5840
5841        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmcomm.sys" },
5842        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmactmon.sys" },
5843        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmevtmgr.sys" },
5844        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmtdi.sys" },
5845        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmebc64.sys" },
5846        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmeevw.sys" },
5847        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmciesc.sys" },
5848        { SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE, L"\\SystemRoot\\System32\\drivers\\sakfile.sys" },  /* Data Loss Prevention, not officescan. */
5849        { SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\sakcd.sys" },  /* Data Loss Prevention, not officescan. */
5850
5851
5852        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\cfwids.sys" },
5853        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\McPvDrv.sys" },
5854        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeapfk.sys" },
5855        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeavfk.sys" },
5856        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfefirek.sys" },
5857        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfehidk.sys" },
5858        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfencbdc.sys" },
5859        { SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfewfpk.sys" },
5860
5861        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kl1.sys" },
5862        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klflt.sys" },
5863        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klif.sys" },
5864        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klim6.sys" },
5865        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klkbdflt.sys" },
5866        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klmouflt.sys" },
5867        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kltdi.sys" },
5868        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kneps.sys" },
5869        { SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\klfphc.dll" },
5870
5871        { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\MBAMSwissArmy.sys" },
5872        { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mwac.sys" },
5873        { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbamchameleon.sys" },
5874        { SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbam.sys" },
5875
5876        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgrkx64.sys" },
5877        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgmfx64.sys" },
5878        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsdrivera.sys" },
5879        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsha.sys" },
5880        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgtdia.sys" },
5881        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgloga.sys" },
5882        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgldx64.sys" },
5883        { SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgdiska.sys" },
5884
5885        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINAflt.sys" },
5886        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINFile.sys" },
5887        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINKNC.sys" },
5888        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProc.sys" },
5889        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProt.sys" },
5890        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINReg.sys" },
5891        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSKMAD.sys" },
5892        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSAlpc.sys" },
5893        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSHttp.sys" },
5894        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNShttps.sys" },
5895        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSIds.sys" },
5896        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSNAHSL.sys" },
5897        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSpicc.sys" },
5898        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPihsw.sys" },
5899        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPop3.sys" },
5900        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSProt.sys" },
5901        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPrv.sys" },
5902        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSSmtp.sys" },
5903        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSStrm.sys" },
5904        { SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNStlsc.sys" },
5905
5906        { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\MpFilter.sys" },
5907        { SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\NisDrvWFP.sys" },
5908
5909        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdguard.sys" },
5910        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmderd.sys" },
5911        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\inspect.sys" },
5912        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdhlp.sys" },
5913        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cfrmd.sys" },
5914        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\hmd.sys" },
5915        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\guard64.dll" },
5916        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdvrt64.dll" },
5917        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdkbd64.dll" },
5918        { SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdcsr.dll" },
5919
5920        { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\drivers\\vsdatant.sys" },
5921        { SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\AntiTheftCredentialProvider.dll" },
5922
5923        { SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD, L"\\SystemRoot\\System32\\drivers\\dgmaster.sys" },
5924
5925        { SUPHARDNT_ADVERSARY_CYLANCE, L"\\SystemRoot\\System32\\drivers\\cyprotectdrv32.sys" },
5926        { SUPHARDNT_ADVERSARY_CYLANCE, L"\\SystemRoot\\System32\\drivers\\cyprotectdrv64.sys" },
5927
5928        { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\drivers\\privman.sys" },
5929        { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\privman64.dll" },
5930        { SUPHARDNT_ADVERSARY_BEYONDTRUST, L"\\SystemRoot\\System32\\privman32.dll" },
5931
5932        { SUPHARDNT_ADVERSARY_AVECTO, L"\\SystemRoot\\System32\\drivers\\PGDriver.sys" },
5933    };
5934
5935    uint32_t fFound = 0;
5936
5937    /*
5938     * Open the driver object directory.
5939     */
5940    UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
5941
5942    OBJECT_ATTRIBUTES ObjAttr;
5943    InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
5944
5945    HANDLE hDir;
5946    NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
5947#ifdef VBOX_STRICT
5948    if (rcNt != STATUS_ACCESS_DENIED) /* non-admin */
5949        SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
5950#endif
5951    if (NT_SUCCESS(rcNt))
5952    {
5953        /*
5954         * Enumerate it, looking for the driver.
5955         */
5956        ULONG    uObjDirCtx = 0;
5957        for (;;)
5958        {
5959            uint32_t    abBuffer[_64K + _1K];
5960            ULONG       cbActual;
5961            rcNt = NtQueryDirectoryObject(hDir,
5962                                          abBuffer,
5963                                          sizeof(abBuffer) - 4, /* minus four for string terminator space. */
5964                                          FALSE /*ReturnSingleEntry */,
5965                                          FALSE /*RestartScan*/,
5966                                          &uObjDirCtx,
5967                                          &cbActual);
5968            if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
5969                break;
5970
5971            POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
5972            while (pObjDir->Name.Length != 0)
5973            {
5974                WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
5975                pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
5976
5977                for (uint32_t i = 0; i < RT_ELEMENTS(s_aDrivers); i++)
5978                    if (RTUtf16ICmpAscii(pObjDir->Name.Buffer, s_aDrivers[i].pszDriver) == 0)
5979                    {
5980                        fFound |= s_aDrivers[i].fAdversary;
5981                        SUP_DPRINTF(("Found driver %s (%#x)\n", s_aDrivers[i].pszDriver, s_aDrivers[i].fAdversary));
5982                        break;
5983                    }
5984
5985                pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
5986
5987                /* Next directory entry. */
5988                pObjDir++;
5989            }
5990        }
5991
5992        NtClose(hDir);
5993    }
5994    else
5995        SUP_DPRINTF(("NtOpenDirectoryObject failed on \\Driver: %#x\n", rcNt));
5996
5997    /*
5998     * Look for files.
5999     */
6000    for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
6001    {
6002        HANDLE              hFile  = RTNT_INVALID_HANDLE_VALUE;
6003        IO_STATUS_BLOCK     Ios    = RTNT_IO_STATUS_BLOCK_INITIALIZER;
6004        UNICODE_STRING      UniStrName;
6005        UniStrName.Buffer = (WCHAR *)s_aFiles[i].pwszFile;
6006        UniStrName.Length = (USHORT)(RTUtf16Len(s_aFiles[i].pwszFile) * sizeof(WCHAR));
6007        UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
6008        InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
6009        rcNt = NtCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &ObjAttr, &Ios, NULL /* Allocation Size*/,
6010                            FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
6011                            FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*EaBuffer*/, 0 /*EaLength*/);
6012        if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
6013        {
6014            fFound |= s_aFiles[i].fAdversary;
6015            NtClose(hFile);
6016        }
6017    }
6018
6019    /*
6020     * Log details and upgrade select adversaries.
6021     */
6022    SUP_DPRINTF(("supR3HardenedWinFindAdversaries: %#x\n", fFound));
6023    for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
6024        if (s_aFiles[i].fAdversary & fFound)
6025        {
6026            if (!(s_aFiles[i].fAdversary & SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD))
6027                supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, NULL, 0);
6028            else
6029            {
6030                /*
6031                 * See if it's a newer version of the driver which doesn't BSODs when we free
6032                 * its memory.  To use RTStrVersionCompare we do a rough UTF-16 -> ASCII conversion.
6033                 */
6034                union
6035                {
6036                    char    szFileVersion[64];
6037                    RTUTF16 wszFileVersion[32];
6038                } uBuf;
6039                supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, uBuf.wszFileVersion, RT_ELEMENTS(uBuf.wszFileVersion));
6040                if (uBuf.wszFileVersion[0])
6041                {
6042                    for (uint32_t off = 0; off < RT_ELEMENTS(uBuf.wszFileVersion); off++)
6043                    {
6044                        RTUTF16 wch = uBuf.wszFileVersion[off];
6045                        uBuf.szFileVersion[off] = (char)wch;
6046                        if (!wch)
6047                            break;
6048                    }
6049                    uBuf.szFileVersion[RT_ELEMENTS(uBuf.wszFileVersion)] = '\0';
6050#define VER_IN_RANGE(a_pszFirst, a_pszLast) \
6051    (RTStrVersionCompare(uBuf.szFileVersion, a_pszFirst) >= 0 && RTStrVersionCompare(uBuf.szFileVersion, a_pszLast) <= 0)
6052                    if (   VER_IN_RANGE("7.3.2.0000", "999999999.9.9.9999")
6053                        || VER_IN_RANGE("7.3.1.1000", "7.3.1.3000")
6054                        || VER_IN_RANGE("7.3.0.3000", "7.3.0.999999999")
6055                        || VER_IN_RANGE("7.2.1.3000", "7.2.999999999.999999999") )
6056                    {
6057                        uint32_t const fOldFound = fFound;
6058                        fFound = (fOldFound & ~SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_OLD)
6059                               |               SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN_NEW;
6060                        SUP_DPRINTF(("supR3HardenedWinFindAdversaries: Found newer version: %#x -> %#x\n", fOldFound, fFound));
6061                    }
6062                }
6063            }
6064        }
6065
6066    return fFound;
6067}
6068
6069
6070extern "C" int main(int argc, char **argv, char **envp);
6071
6072/**
6073 * The executable entry point.
6074 *
6075 * This is normally taken care of by the C runtime library, but we don't want to
6076 * get involved with anything as complicated like the CRT in this setup.  So, we
6077 * it everything ourselves, including parameter parsing.
6078 */
6079extern "C" void __stdcall suplibHardenedWindowsMain(void)
6080{
6081    RTEXITCODE rcExit = RTEXITCODE_FAILURE;
6082
6083    g_cSuplibHardenedWindowsMainCalls++;
6084    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED;
6085
6086    /*
6087     * Initialize the NTDLL API wrappers. This aims at bypassing patched NTDLL
6088     * in all the processes leading up the VM process.
6089     */
6090    supR3HardenedWinInitImports();
6091    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED;
6092
6093    /*
6094     * Notify the parent process that we're probably capable of reporting our
6095     * own errors.
6096     */
6097    if (g_ProcParams.hEvtParent || g_ProcParams.hEvtChild)
6098    {
6099        SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit);
6100
6101        g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents;
6102        NtSetEvent(g_ProcParams.hEvtParent, NULL);
6103
6104        NtClose(g_ProcParams.hEvtParent);
6105        NtClose(g_ProcParams.hEvtChild);
6106        g_ProcParams.hEvtParent = NULL;
6107        g_ProcParams.hEvtChild  = NULL;
6108    }
6109    else
6110        SUPR3HARDENED_ASSERT(!g_fSupEarlyProcessInit);
6111
6112    /*
6113     * After having resolved imports we patch the LdrInitializeThunk code so
6114     * that it's more difficult to invade our privacy by CreateRemoteThread.
6115     * We'll re-enable this after opening the driver or temporarily while respawning.
6116     */
6117    supR3HardenedWinDisableThreadCreation();
6118
6119    /*
6120     * Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
6121     * SUPHardenedVerfiyImage-win.cpp.)
6122     */
6123    supR3HardenedWinInitVersion(false /*fEarly*/);
6124    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERSION_INITIALIZED;
6125
6126    /*
6127     * Convert the arguments to UTF-8 and open the log file if specified.
6128     * This must be done as early as possible since the code below may fail.
6129     */
6130    PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
6131    int    cArgs;
6132    char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs);
6133
6134    supR3HardenedOpenLog(&cArgs, papszArgs);
6135
6136    /*
6137     * Log information about important system files.
6138     */
6139    supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\ntdll.dll",          NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
6140    supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\kernel32.dll",       NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
6141    supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\KernelBase.dll",     NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
6142    supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\apisetschema.dll",   NULL /*pwszFileVersion*/, 0 /*cwcFileVersion*/);
6143
6144    /*
6145     * Scan the system for adversaries, logging information about them.
6146     */
6147    g_fSupAdversaries = supR3HardenedWinFindAdversaries();
6148
6149    /*
6150     * Get the executable name, make sure it's the long version.
6151     */
6152    DWORD cwcExecName = GetModuleFileNameW(GetModuleHandleW(NULL), g_wszSupLibHardenedExePath,
6153                                           RT_ELEMENTS(g_wszSupLibHardenedExePath));
6154    if (cwcExecName >= RT_ELEMENTS(g_wszSupLibHardenedExePath))
6155        supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, VERR_BUFFER_OVERFLOW,
6156                              "The executable path is too long.");
6157
6158    RTUTF16 wszLong[RT_ELEMENTS(g_wszSupLibHardenedExePath)];
6159    DWORD cwcLong = GetLongPathNameW(g_wszSupLibHardenedExePath, wszLong, RT_ELEMENTS(wszLong));
6160    if (cwcLong > 0)
6161    {
6162        memcpy(g_wszSupLibHardenedExePath, wszLong, (cwcLong + 1) * sizeof(RTUTF16));
6163        cwcExecName = cwcLong;
6164    }
6165
6166    /* The NT version of it. */
6167    HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
6168                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
6169    if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
6170        supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(RtlGetLastWin32Error()),
6171                              "Error opening the executable: %u (%ls).", RtlGetLastWin32Error());
6172    RT_ZERO(g_SupLibHardenedExeNtPath);
6173    ULONG cbIgn;
6174    NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &g_SupLibHardenedExeNtPath,
6175                                  sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbIgn);
6176    if (!NT_SUCCESS(rcNt))
6177        supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
6178                              "NtQueryObject -> %#x (on %ls)\n", rcNt, g_wszSupLibHardenedExePath);
6179    NtClose(hFile);
6180
6181    /* The NT executable name offset / dir path length. */
6182    g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
6183    while (   g_offSupLibHardenedExeNtName > 1
6184           && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
6185        g_offSupLibHardenedExeNtName--;
6186
6187    /*
6188     * Preliminary app binary path init.  May change when SUPR3HardenedMain is
6189     * called (via main below).
6190     */
6191    supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
6192
6193    /*
6194     * If we've done early init already, register the DLL load notification
6195     * callback and reinstall the NtDll patches.
6196     */
6197    if (g_fSupEarlyProcessInit)
6198    {
6199        supR3HardenedWinRegisterDllNotificationCallback();
6200        supR3HardenedWinReInstallHooks(false /*fFirstCall */);
6201    }
6202
6203    /*
6204     * Call the C/C++ main function.
6205     */
6206    SUP_DPRINTF(("Calling main()\n"));
6207    rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
6208
6209    /*
6210     * Exit the process (never return).
6211     */
6212    SUP_DPRINTF(("Terminating the normal way: rcExit=%d\n", rcExit));
6213    suplibHardenedExit(rcExit);
6214}
6215
6216
6217/**
6218 * Reports an error to the parent process via the process parameter structure.
6219 *
6220 * @param   pszWhere            Where this error occured, if fatal message. NULL
6221 *                              if not message.
6222 * @param   enmWhat             Which init operation went wrong if fatal
6223 *                              message. kSupInitOp_Invalid if not message.
6224 * @param   rc                  The status code to report.
6225 * @param   pszFormat           The format string.
6226 * @param   va                  The format arguments.
6227 */
6228DECLHIDDEN(void) supR3HardenedWinReportErrorToParent(const char *pszWhere, SUPINITOP enmWhat, int rc,
6229                                                     const char *pszFormat, va_list va)
6230{
6231    if (pszWhere)
6232        RTStrCopy(g_ProcParams.szWhere, sizeof(g_ProcParams.szWhere), pszWhere);
6233    else
6234        g_ProcParams.szWhere[0] = '\0';
6235    RTStrPrintfV(g_ProcParams.szErrorMsg, sizeof(g_ProcParams.szErrorMsg), pszFormat, va);
6236    g_ProcParams.enmWhat = enmWhat;
6237    g_ProcParams.rc      = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc;
6238    g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
6239
6240    NtClearEvent(g_ProcParams.hEvtChild);
6241    NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL);
6242    if (NT_SUCCESS(rcNt))
6243    {
6244        LARGE_INTEGER Timeout;
6245        Timeout.QuadPart = -300000000; /* 30 second */
6246        /*NTSTATUS rcNt =*/ NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
6247    }
6248}
6249
6250
6251/**
6252 * Routine called by the supR3HardenedEarlyProcessInitThunk assembly routine
6253 * when LdrInitializeThunk is executed during process initialization.
6254 *
6255 * This initializes the Stub and VM processes, hooking NTDLL APIs and opening
6256 * the device driver before any other DLLs gets loaded into the process.  This
6257 * greately reduces and controls the trusted code base of the process compared
6258 * to opening the driver from SUPR3HardenedMain.  It also avoids issues with so
6259 * call protection software that is in the habit of patching half of the ntdll
6260 * and kernel32 APIs in the process, making it almost indistinguishable from
6261 * software that is up to no good.  Once we've opened vboxdrv, the process
6262 * should be locked down so thighly that only kernel software and csrss can mess
6263 * with the process.
6264 */
6265DECLASM(uintptr_t) supR3HardenedEarlyProcessInit(void)
6266{
6267    /*
6268     * When the first thread gets here we wait for the parent to continue with
6269     * the process purifications.  The primary thread must execute for image
6270     * load notifications to trigger, at least in more recent windows versions.
6271     * The old trick of starting a different thread that terminates immediately
6272     * thus doesn't work.
6273     *
6274     * We are not allowed to modify any data at this point because it will be
6275     * reset by the child process purification the parent does when we stop. To
6276     * sabotage thread creation during purification, and to avoid unnecessary
6277     * work for the parent, we reset g_ProcParams before signalling the parent
6278     * here.
6279     */
6280    if (g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
6281    {
6282        NtTerminateThread(0, 0);
6283        return 0x22; /* crash */
6284    }
6285
6286    /* Retrieve the data we need. */
6287    uintptr_t uNtDllAddr = ASMAtomicXchgPtrT(&g_ProcParams.uNtDllAddr, 0, uintptr_t);
6288    if (!RT_VALID_PTR(uNtDllAddr))
6289    {
6290        NtTerminateThread(0, 0);
6291        return 0x23; /* crash */
6292    }
6293
6294    HANDLE hEvtChild  = g_ProcParams.hEvtChild;
6295    HANDLE hEvtParent = g_ProcParams.hEvtParent;
6296    if (   hEvtChild  == NULL
6297        || hEvtChild  == RTNT_INVALID_HANDLE_VALUE
6298        || hEvtParent == NULL
6299        || hEvtParent == RTNT_INVALID_HANDLE_VALUE)
6300    {
6301        NtTerminateThread(0, 0);
6302        return 0x24; /* crash */
6303    }
6304
6305    /* Resolve the APIs we need. */
6306    PFNNTWAITFORSINGLEOBJECT    pfnNtWaitForSingleObject;
6307    PFNNTSETEVENT               pfnNtSetEvent;
6308    supR3HardenedWinGetVeryEarlyImports(uNtDllAddr, &pfnNtWaitForSingleObject, &pfnNtSetEvent);
6309
6310    /* Signal the parent that we're ready for purification. */
6311    RT_ZERO(g_ProcParams);
6312    g_ProcParams.enmRequest = kSupR3WinChildReq_PurifyChildAndCloseHandles;
6313    NTSTATUS rcNt = pfnNtSetEvent(hEvtParent, NULL);
6314    if (rcNt != STATUS_SUCCESS)
6315        return 0x33; /* crash */
6316
6317    /* Wait up to 2 mins for the parent to exorcise evil. */
6318    LARGE_INTEGER Timeout;
6319    Timeout.QuadPart = -1200000000; /* 120 second */
6320    rcNt = pfnNtWaitForSingleObject(hEvtChild, FALSE /*Alertable*/, &Timeout);
6321    if (rcNt != STATUS_SUCCESS)
6322        return 0x34; /* crash */
6323
6324    /*
6325     * We're good to go, work global state and restore process parameters.
6326     * Note that we will not restore uNtDllAddr since that is our first defence
6327     * against unwanted threads (see above).
6328     */
6329    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED;
6330    g_fSupEarlyProcessInit      = true;
6331
6332    g_ProcParams.hEvtChild      = hEvtChild;
6333    g_ProcParams.hEvtParent     = hEvtParent;
6334    g_ProcParams.enmRequest     = kSupR3WinChildReq_Error;
6335    g_ProcParams.rc             = VINF_SUCCESS;
6336
6337    /*
6338     * Initialize the NTDLL imports that we consider usable before the
6339     * process has been initialized.
6340     */
6341    supR3HardenedWinInitImportsEarly(uNtDllAddr);
6342    g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED;
6343
6344    /*
6345     * Init g_uNtVerCombined as well as we can at this point.
6346     */
6347    supR3HardenedWinInitVersion(true /*fEarly*/);
6348
6349    /*
6350     * Convert the arguments to UTF-8 so we can open the log file if specified.
6351     * We may have to normalize the pointer on older windows version (not w7/64 +).
6352     * Note! This leaks memory at present.
6353     */
6354    PRTL_USER_PROCESS_PARAMETERS pUserProcParams = NtCurrentPeb()->ProcessParameters;
6355    UNICODE_STRING CmdLineStr = pUserProcParams->CommandLine;
6356    if (   CmdLineStr.Buffer != NULL
6357        && !(pUserProcParams->Flags & RTL_USER_PROCESS_PARAMS_FLAG_NORMALIZED) )
6358        CmdLineStr.Buffer = (WCHAR *)((uintptr_t)CmdLineStr.Buffer + (uintptr_t)pUserProcParams);
6359    int    cArgs;
6360    char **papszArgs = suplibCommandLineToArgvWStub(CmdLineStr.Buffer, CmdLineStr.Length / sizeof(WCHAR), &cArgs);
6361    supR3HardenedOpenLog(&cArgs, papszArgs);
6362    SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p g_uNtVerCombined=%#x\n", uNtDllAddr, g_uNtVerCombined));
6363
6364    /*
6365     * Set up the direct system calls so we can more easily hook NtCreateSection.
6366     */
6367    RTERRINFOSTATIC ErrInfo;
6368    supR3HardenedWinInitSyscalls(true /*fReportErrors*/, RTErrInfoInitStatic(&ErrInfo));
6369
6370    /*
6371     * Determine the executable path and name.  Will NOT determine the windows style
6372     * executable path here as we don't need it.
6373     */
6374    SIZE_T cbActual = 0;
6375    rcNt = NtQueryVirtualMemory(NtCurrentProcess(), &g_ProcParams, MemorySectionName, &g_SupLibHardenedExeNtPath,
6376                                sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbActual);
6377    if (   !NT_SUCCESS(rcNt)
6378        || g_SupLibHardenedExeNtPath.UniStr.Length == 0
6379        || g_SupLibHardenedExeNtPath.UniStr.Length & 1)
6380        supR3HardenedFatal("NtQueryVirtualMemory/MemorySectionName failed in supR3HardenedVmProcessInit: %#x\n", rcNt);
6381
6382    /* The NT executable name offset / dir path length. */
6383    g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
6384    while (   g_offSupLibHardenedExeNtName > 1
6385           && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
6386        g_offSupLibHardenedExeNtName--;
6387
6388    /*
6389     * Preliminary app binary path init.  May change when SUPR3HardenedMain is called.
6390     */
6391    supR3HardenedWinInitAppBin(SUPSECMAIN_FLAGS_LOC_APP_BIN);
6392
6393    /*
6394     * Initialize the image verification stuff (hooks LdrLoadDll and NtCreateSection).
6395     */
6396    supR3HardenedWinInit(0, false /*fAvastKludge*/);
6397
6398    /*
6399     * Open the driver.
6400     */
6401    if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
6402    {
6403        SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv stub...\n"));
6404        supR3HardenedWinOpenStubDevice();
6405        g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_STUB_DEVICE_OPENED;
6406    }
6407    else if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
6408    {
6409        SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv...\n"));
6410        supR3HardenedMainOpenDevice();
6411        g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_REAL_DEVICE_OPENED;
6412    }
6413    else
6414        supR3HardenedFatal("Unexpected first argument '%s'!\n", papszArgs[0]);
6415
6416    /*
6417     * Reinstall the NtDll patches since there is a slight possibility that
6418     * someone undid them while we where busy opening the device.
6419     */
6420    supR3HardenedWinReInstallHooks(false /*fFirstCall */);
6421
6422    /*
6423     * Restore the LdrInitializeThunk code so we can initialize the process
6424     * normally when we return.
6425     */
6426    SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n"));
6427    PSUPHNTLDRCACHEENTRY pLdrEntry;
6428    int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry, RTErrInfoInitStatic(&ErrInfo));
6429    if (RT_FAILURE(rc))
6430        supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheOpen failed on NTDLL: %Rrc %s\n",
6431                           rc, ErrInfo.Core.pszMsg);
6432
6433    uint8_t *pbBits;
6434    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, uNtDllAddr, NULL, NULL, RTErrInfoInitStatic(&ErrInfo));
6435    if (RT_FAILURE(rc))
6436        supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc %s\n",
6437                           rc, ErrInfo.Core.pszMsg);
6438
6439    RTLDRADDR uValue;
6440    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
6441    if (RT_FAILURE(rc))
6442        supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
6443
6444    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
6445    SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
6446    memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - uNtDllAddr), 16);
6447    SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
6448
6449    SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n"));
6450    return (uintptr_t)pvLdrInitThunk;
6451}
6452
Note: See TracBrowser for help on using the repository browser.

www.oracle.com
ContactPrivacy policyTerms of Use