VirtualBox

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

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

Doxygen: @todo fixes

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

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