VirtualBox

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

Last change on this file was 101542, checked in by vboxsync, 6 months ago

DIS,VMM,DBGC,IPRT,++: Some disassembler tweaks and TB disassembly work. [build fix, missed bits] bugref:10371 bugref:9898

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

© 2023 Oracle
ContactPrivacy policyTerms of Use