VirtualBox

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

Last change on this file since 54371 was 54139, checked in by vboxsync, 10 years ago

SUPR3HardenedMain-win.cpp: Workaround for a process deadlock caused by our favourite Symantec driver (SysPlant.sys) that would lead to the system getting stuck during reboot and shutdown.

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

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