VirtualBox

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

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

SUPHardNt: Log info about another security product.

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