VirtualBox

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

Last change on this file was 104384, checked in by vboxsync, 2 weeks ago

/Config.kmk,SUPHardNt: s/VBOX_WITHOUT_HARDENING_INTEGRITY_CHECK/VBOX_WITHOUT_WINDOWS_KERNEL_CODE_SIGNING_CERT/; s/SUPHNTVI_F_TRUSTED_INSTALLER_OWNER/SUPHNTVI_F_TRUSTED_INSTALLER_OR_SIMILAR_OWNER/; some more comments, a @todo and dialing back the changes a little. bugref:10657

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

© 2023 Oracle
ContactPrivacy policyTerms of Use