VirtualBox

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

Last change on this file since 104381 was 104381, checked in by vboxsync, 4 weeks ago

/Config.kmk, HostDrivers/Support/win: Disable integrity check due to changes in signing. bugref:10657

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

© 2023 Oracle
ContactPrivacy policyTerms of Use