VirtualBox

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

Last change on this file since 80212 was 80212, checked in by vboxsync, 5 years ago

SUPHardNt: Hack for fending off unwanted APCs during early process initialization, preventing them from tripping over when we're evicted code they need (executable memory allocations). We only allow the LdrInitializeThunk APC to go thru. bugdbref:29744598

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