VirtualBox

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

Last change on this file since 59810 was 59810, checked in by vboxsync, 9 years ago

supR3HardenedWinInitVersion: Don't call RtlGetVersion during early init, it may touch NTDLL BSS data and cause VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH when opening our kernel driver. Problem started with windows 10 build 14267.

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

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