VirtualBox

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

Last change on this file since 52954 was 52954, checked in by vboxsync, 10 years ago

Tighten the checks on the stub process a little bit (part 1).

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