VirtualBox

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

Last change on this file since 85873 was 85873, checked in by vboxsync, 4 years ago

SUPHardNt: fix potential warning.

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

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