VirtualBox

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

最後變更 在這個檔案從107881是 106998,由 vboxsync 提交於 4 月 前

SUPHardNt: Fixed two more bugs from r165898, first more mixing of TAFs and X.509 certs, second that SUPR3HARDNTPATCH should be a struct not a union. jiraref:VBP-1442

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

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