VirtualBox

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

最後變更 在這個檔案從106945是 106897,由 vboxsync 提交於 5 月 前

SUPHardNt: Rough and untested port of the C code to win.arm64 so the extpack builds [SCM fix]. VBP-1447

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

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