VirtualBox

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

Last change on this file since 78351 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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