VirtualBox

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

Last change on this file since 99220 was 99220, checked in by vboxsync, 14 months ago

Disassember,*: Start separating the disassembler into a architecture specific and common part, bugref:10394

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

© 2023 Oracle
ContactPrivacy policyTerms of Use