VirtualBox

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

Last change on this file since 85844 was 85121, checked in by vboxsync, 5 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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