VirtualBox

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

Last change on this file since 94265 was 93270, checked in by vboxsync, 3 years ago

SUPHardNt: The supR3HardenedMonitor_NtCreateSection and supR3HardenedMonitor_LdrLoadDll needs to have -guard:cf disabled so we can call the real functions w/o getting into trouble. bugref:10162

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette