VirtualBox

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

Last change on this file since 67979 was 67979, checked in by vboxsync, 7 years ago

Added RTUTf16NICmpAscii.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use