VirtualBox

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

Last change on this file since 64883 was 64532, checked in by vboxsync, 8 years ago

more typos

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

© 2023 Oracle
ContactPrivacy policyTerms of Use