VirtualBox

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

Last change on this file since 51936 was 51936, checked in by vboxsync, 10 years ago

One more define.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.5 KB
Line 
1/* $Id: SUPR3HardenedMain-win.cpp 51936 2014-07-08 15:31:55Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main(), windows bits.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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* Header Files *
29*******************************************************************************/
30#include <iprt/nt/nt-and-windows.h>
31#include <AccCtrl.h>
32#include <AclApi.h>
33#ifndef PROCESS_SET_LIMITED_INFORMATION
34# define PROCESS_SET_LIMITED_INFORMATION 0x2000
35#endif
36#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
37# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
38# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
39#endif
40
41#include <VBox/sup.h>
42#include <VBox/err.h>
43#include <iprt/ctype.h>
44#include <iprt/string.h>
45#include <iprt/initterm.h>
46#include <iprt/param.h>
47
48#include "SUPLibInternal.h"
49#include "win/SUPHardenedVerify-win.h"
50#include "../SUPDrvIOC.h"
51
52
53/*******************************************************************************
54* Defined Constants And Macros *
55*******************************************************************************/
56/** The first argument of a respawed stub argument.
57 * This just needs to be unique enough to avoid most confusion with real
58 * executable names, there are other checks in place to make sure we've respanwed. */
59#define SUPR3_RESPAWN_ARG0 "81954AF5-4D2F-31EB-A142-B7AF187A1C41-suplib-2ndchild"
60
61/** Unconditional assertion. */
62#define SUPR3HARDENED_ASSERT(a_Expr) \
63 do { \
64 if (!(a_Expr)) \
65 supR3HardenedFatal("%s: %s", __FUNCTION__, #a_Expr); \
66 } while (0)
67
68/** Unconditional assertion of NT_SUCCESS. */
69#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
70 do { \
71 NTSTATUS rcNtAssert = (a_Expr); \
72 if (!NT_SUCCESS(rcNtAssert)) \
73 supR3HardenedFatal("%s: %s -> %#x", __FUNCTION__, #a_Expr, rcNtAssert); \
74 } while (0)
75
76/** Unconditional assertion of a WIN32 API returning non-FALSE. */
77#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
78 do { \
79 BOOL fRcAssert = (a_Expr); \
80 if (fRcAssert == FALSE) \
81 supR3HardenedFatal("%s: %s -> %#x", __FUNCTION__, #a_Expr, GetLastError()); \
82 } while (0)
83
84
85/*******************************************************************************
86* Structures and Typedefs *
87*******************************************************************************/
88/**
89 * Security descriptor cleanup structure.
90 */
91typedef struct MYSECURITYCLEANUP
92{
93 union
94 {
95 SID Sid;
96 uint8_t abPadding[SECURITY_MAX_SID_SIZE];
97 } Everyone, Owner, User, Login;
98 union
99 {
100 ACL AclHdr;
101 uint8_t abPadding[1024];
102 } Acl;
103 PSECURITY_DESCRIPTOR pSecDesc;
104} MYSECURITYCLEANUP;
105/** Pointer to security cleanup structure. */
106typedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
107
108
109/**
110 * Image verifier cache entry.
111 */
112typedef struct VERIFIERCACHEENTRY
113{
114 /** Pointer to the next entry with the same hash value. */
115 struct VERIFIERCACHEENTRY * volatile pNext;
116 /** The file handle. */
117 HANDLE hFile;
118 /** If fIndexNumber is set, this is an file system internal file identifier. */
119 LARGE_INTEGER IndexNumber;
120 /** The path hash value. */
121 uint32_t uHash;
122 /** The verification result. */
123 int rc;
124 /** Whether IndexNumber is valid */
125 bool fIndexNumberValid;
126 /** cwcPath * sizeof(RTUTF16). */
127 uint16_t cbPath;
128 /** The full path of this entry (variable size). */
129 RTUTF16 wszPath[1];
130} VERIFIERCACHEENTRY;
131/** Pointer to an image verifier path entry. */
132typedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
133
134
135/*******************************************************************************
136* Global Variables *
137*******************************************************************************/
138/** @name Global variables initialized by suplibHardenedWindowsMain.
139 * @{ */
140/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
141uint32_t g_uNtVerCombined = 0;
142/** Count calls to the special main function for linking santity checks. */
143static uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
144/** The UTF-16 windows path to the executable. */
145RTUTF16 g_wszSupLibHardenedExePath[1024];
146/** The NT path of the executable. */
147SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
148/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
149 * not byte). This also gives the length of the exectuable directory path,
150 * including a trailing slash. */
151uint32_t g_offSupLibHardenedExeNtName;
152/** @} */
153
154/** @name Hook related variables.
155 * @{ */
156/** The jump back address of the patched NtCreateSection. */
157extern "C" PFNRT g_pfnNtCreateSectionJmpBack = NULL;
158/** Pointer to the bit of assembly code that will perform the original
159 * NtCreateSection operation. */
160static NTSTATUS (NTAPI * g_pfnNtCreateSectionReal)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
161 PLARGE_INTEGER, ULONG, ULONG, HANDLE);
162/** The hash table of verifier cache . */
163static VERIFIERCACHEENTRY * volatile g_apVerifierCache[128];
164/** @ */
165
166/** Static error info structure used during init. */
167static RTERRINFOSTATIC g_ErrInfoStatic;
168
169
170/*******************************************************************************
171* Internal Functions *
172*******************************************************************************/
173#ifdef RT_ARCH_AMD64
174# define SYSCALL(a_Num) DECLASM(void) RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num)(void)
175# include "NtCreateSection-template-amd64-syscall-type-1.h"
176# undef SYSCALL
177#endif
178#ifdef RT_ARCH_X86
179# define SYSCALL(a_Num) DECLASM(void) RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num)(void)
180# include "NtCreateSection-template-x86-syscall-type-1.h"
181# undef SYSCALL
182#endif
183
184
185
186/**
187 * Simple wide char search routine.
188 *
189 * @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
190 * NULL if not found.
191 * @param pwszHaystack Pointer to the string that should be searched.
192 * @param wcNeedle The character to search for.
193 */
194static PRTUTF16 suplibHardenedWStrChr(PCRTUTF16 pwszHaystack, RTUTF16 wcNeedle)
195{
196 for (;;)
197 {
198 RTUTF16 wcCur = *pwszHaystack;
199 if (wcCur == wcNeedle)
200 return (PRTUTF16)pwszHaystack;
201 if (wcCur == '\0')
202 return NULL;
203 pwszHaystack++;
204 }
205}
206
207
208/**
209 * Simple wide char string length routine.
210 *
211 * @returns The number of characters in the given string. (Excludes the
212 * terminator.)
213 * @param pwsz The string.
214 */
215static size_t suplibHardenedWStrLen(PCRTUTF16 pwsz)
216{
217 PCRTUTF16 pwszCur = pwsz;
218 while (*pwszCur != '\0')
219 pwszCur++;
220 return pwszCur - pwsz;
221}
222
223
224/**
225 * Allocate zero filled memory on the heap.
226 *
227 * @returns Pointer to the memory. Will never return NULL, triggers a fatal
228 * error instead.
229 * @param cb The number of bytes to allocate.
230 */
231DECLHIDDEN(void *) suplibHardenedAllocZ(size_t cb)
232{
233 void *pv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
234 if (!pv)
235 supR3HardenedFatal("HeapAlloc failed to allocate %zu bytes.\n", cb);
236 return pv;
237}
238
239
240/**
241 * Reallocates memory on the heap.
242 *
243 * @returns Pointer to the resized memory block. Will never return NULL,
244 * triggers a fatal error instead.
245 * @param pvOld The old memory block.
246 * @param cbNew The new block size.
247 */
248DECLHIDDEN(void *) suplibHardenedReAlloc(void *pvOld, size_t cbNew)
249{
250 if (!pvOld)
251 return suplibHardenedAllocZ(cbNew);
252 void *pv = HeapReAlloc(GetProcessHeap(), 0 /*dwFlags*/, pvOld, cbNew);
253 if (!pv)
254 supR3HardenedFatal("HeapReAlloc failed to allocate %zu bytes.\n", cbNew);
255 return pv;
256}
257
258
259/**
260 * Frees memory allocated by suplibHardenedAlloc, suplibHardenedAllocZ or
261 * suplibHardenedReAlloc.
262 *
263 * @param pv Pointer to the memeory to be freed.
264 */
265DECLHIDDEN(void) suplibHardenedFree(void *pv)
266{
267 if (pv)
268 HeapFree(GetProcessHeap(), 0 /* dwFlags*/, pv);
269}
270
271
272/**
273 * Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
274 * and supplies the right flags.
275 *
276 * @returns Module handle on success, NULL on failure.
277 * @param pszName The full path to the DLL.
278 * @param fSystem32Only Whether to only look for imports in the system32
279 * directory. If set to false, the application
280 * directory is also searched.
281 */
282DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only)
283{
284 WCHAR wszPath[RTPATH_MAX];
285 PRTUTF16 pwszPath = wszPath;
286 int rc = RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszPath, RT_ELEMENTS(wszPath), NULL);
287 if (RT_SUCCESS(rc))
288 {
289 while (*pwszPath)
290 {
291 if (*pwszPath == '/')
292 *pwszPath = '\\';
293 pwszPath++;
294 }
295
296 DWORD fFlags = 0;
297 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
298 {
299 fFlags |= LOAD_LIBRARY_SEARCH_SYSTEM32;
300 if (!fSystem32Only)
301 fFlags |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
302 }
303
304 void *pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, fFlags);
305
306 /* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
307 if ( !pvRet
308 && fFlags
309 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
310 && GetLastError() == ERROR_INVALID_PARAMETER)
311 pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, 0);
312
313 return pvRet;
314 }
315 supR3HardenedFatal("RTStrToUtf16Ex failed on '%s': %Rrc", pszName, rc);
316 return NULL;
317}
318
319
320/**
321 * Gets the internal index number of the file.
322 *
323 * @returns True if we got an index number, false if not.
324 * @param hFile The file in question.
325 * @param pIndexNumber where to return the index number.
326 */
327static bool supR3HardenedWinVerifyCacheGetIndexNumber(HANDLE hFile, PLARGE_INTEGER pIndexNumber)
328{
329 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
330 NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
331 if (NT_SUCCESS(rcNt))
332 rcNt = Ios.Status;
333#ifdef DEBUG_bird
334 if (!NT_SUCCESS(rcNt))
335 __debugbreak();
336#endif
337 return NT_SUCCESS(rcNt) && pIndexNumber->QuadPart != 0;
338}
339
340
341/**
342 * Calculates the hash value for the given UTF-16 string.
343 *
344 * @returns Hash value.
345 * @param pUniStr String to hash.
346 */
347static uint32_t supR3HardenedWinVerifyCacheHashPath(PCUNICODE_STRING pUniStr)
348{
349 uint32_t uHash = 0;
350 unsigned cwcLeft = pUniStr->Length / sizeof(WCHAR);
351 PRTUTF16 pwc = pUniStr->Buffer;
352
353 while (cwcLeft-- > 0)
354 {
355 RTUTF16 wc = *pwc++;
356 uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
357 }
358 return uHash;
359}
360
361
362/**
363 * Inserts the given verifier result into the cache.
364 *
365 * @param pUniStr The full path of the image.
366 * @param hFile The file handle - must either be entered into
367 * the cache or closed.
368 * @param rc The verifier result.
369 * @param fCacheable Whether this is a cacheable result. Passed in
370 * here instead of being handled by the caller to
371 * save code duplication.
372 */
373static void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc, bool fCacheable)
374{
375 /*
376 * Don't cache anything until we've got the WinVerifyTrust API up and running.
377 */
378 if ( g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_VERIFY_TRUST_READY
379 && fCacheable)
380 {
381 /*
382 * Allocate and initalize a new entry.
383 */
384 PVERIFIERCACHEENTRY pEntry = (PVERIFIERCACHEENTRY)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
385 sizeof(VERIFIERCACHEENTRY) + pUniStr->Length);
386 if (pEntry)
387 {
388 pEntry->pNext = NULL;
389 pEntry->hFile = hFile;
390 pEntry->rc = rc;
391 pEntry->uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
392 pEntry->cbPath = pUniStr->Length;
393 memcpy(pEntry->wszPath, pUniStr->Buffer, pUniStr->Length);
394 pEntry->wszPath[pUniStr->Length / sizeof(WCHAR)] = '\0';
395 pEntry->fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &pEntry->IndexNumber);
396
397 /*
398 * Try insert it, careful with concurrent code as well as potential duplicates.
399 */
400 uint32_t iHashTab = pEntry->uHash % RT_ELEMENTS(g_apVerifierCache);
401 VERIFIERCACHEENTRY * volatile *ppEntry = &g_apVerifierCache[iHashTab];
402 for (;;)
403 {
404 if (ASMAtomicCmpXchgPtr(ppEntry, pEntry, NULL))
405 return;
406 PVERIFIERCACHEENTRY pOther = *ppEntry;
407 if (!pOther)
408 continue;
409 if ( pOther->uHash == pEntry->uHash
410 && pOther->cbPath == pEntry->cbPath
411 && memcmp(pOther->wszPath, pEntry->wszPath, pEntry->cbPath) == 0)
412 break;
413 ppEntry = &pOther->pNext;
414 }
415
416 /* Duplicate entry. */
417#ifdef DEBUG_bird
418 __debugbreak();
419#endif
420 HeapFree(GetProcessHeap(), 0 /* dwFlags*/, pEntry);
421 }
422 }
423 NtClose(hFile);
424}
425
426
427/**
428 * Looks up an entry in the verifier hash table.
429 *
430 * @return Pointer to the entry on if found, NULL if not.
431 * @param pUniStr The full path of the image.
432 * @param hFile The file handle.
433 */
434static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile)
435{
436 PRTUTF16 const pwszPath = pUniStr->Buffer;
437 uint16_t const cbPath = pUniStr->Length;
438 uint32_t uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
439 uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
440 PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
441 while (pCur)
442 {
443 if ( pCur->uHash == uHash
444 && pCur->cbPath == cbPath
445 && memcmp(pCur->wszPath, pwszPath, cbPath) == 0)
446 {
447
448 if (!pCur->fIndexNumberValid)
449 return pCur;
450 LARGE_INTEGER IndexNumber;
451 bool fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &IndexNumber);
452 if ( fIndexNumberValid
453 && IndexNumber.QuadPart == pCur->IndexNumber.QuadPart)
454 return pCur;
455#ifdef DEBUG_bird
456 __debugbreak();
457#endif
458 }
459 pCur = pCur->pNext;
460 }
461 return NULL;
462}
463
464
465/**
466 * Hook that monitors NtCreateSection calls.
467 *
468 * @returns NT status code.
469 * @param phSection Where to return the section handle.
470 * @param fAccess The desired access.
471 * @param pObjAttribs The object attributes (optional).
472 * @param pcbSection The section size (optional).
473 * @param fProtect The max section protection.
474 * @param fAttribs The section attributes.
475 * @param hFile The file to create a section from (optional).
476 */
477static NTSTATUS NTAPI
478supR3HardenedMonitor_NtCreateSection(PHANDLE phSection, ACCESS_MASK fAccess, POBJECT_ATTRIBUTES pObjAttribs,
479 PLARGE_INTEGER pcbSection, ULONG fProtect, ULONG fAttribs, HANDLE hFile)
480{
481 if ( hFile != NULL
482 && hFile != INVALID_HANDLE_VALUE)
483 {
484 bool const fImage = RT_BOOL(fAttribs & (SEC_IMAGE | SEC_PROTECTED_IMAGE));
485 bool const fExecMap = RT_BOOL(fAccess & SECTION_MAP_EXECUTE);
486 bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
487 | PAGE_EXECUTE_READWRITE));
488 if (fImage || fExecMap || fExecProt)
489 {
490 /*
491 * Query the name of the file, making sure to zero terminator the
492 * string. (2nd half of buffer is used for error info, see below.)
493 */
494 union
495 {
496 UNICODE_STRING UniStr;
497 uint8_t abBuffer[sizeof(UNICODE_STRING) + 2048 * sizeof(WCHAR)];
498 } uBuf;
499 RT_ZERO(uBuf);
500 ULONG cbNameBuf;
501 NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
502 if (!NT_SUCCESS(rcNt))
503 {
504 supR3HardenedError(VINF_SUCCESS, false,
505 "supR3HardenedMonitor_NtCreateSection: NtQueryObject -> %#x (fImage=%d fExecMap=%d fExecProt=%d)\n",
506 fImage, fExecMap, fExecProt);
507 return rcNt;
508 }
509
510 /*
511 * Check the cache.
512 */
513 PVERIFIERCACHEENTRY pCacheHit = supR3HardenedWinVerifyCacheLookup(&uBuf.UniStr, hFile);
514 if (pCacheHit)
515 {
516 SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: cache hit (%Rrc) on %ls\n", pCacheHit->rc, pCacheHit->wszPath));
517 if (RT_SUCCESS(pCacheHit->rc))
518 return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
519 supR3HardenedError(VINF_SUCCESS, false,
520 "supR3HardenedMonitor_NtCreateSection: cached rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls\n",
521 pCacheHit->rc, fImage, fExecMap, fExecProt, uBuf.UniStr.Buffer);
522 return STATUS_TRUST_FAILURE;
523 }
524
525 /*
526 * On XP the loader might hand us handles with just FILE_EXECUTE and
527 * SYNCRHONIZE, the means reading will fail later on. So, we might
528 * have to reopen the file here in order to validate it - annoying.
529 */
530 HANDLE hMyFile = NULL;
531 rcNt = NtDuplicateObject(NtCurrentProcess(), hFile, NtCurrentProcess(),
532 &hMyFile,
533 FILE_READ_DATA | SYNCHRONIZE,
534 0 /* Handle attributes*/, 0 /* Options */);
535 if (!NT_SUCCESS(rcNt))
536 {
537 if (rcNt == STATUS_ACCESS_DENIED)
538 {
539 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
540 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
541
542 OBJECT_ATTRIBUTES ObjAttr;
543 InitializeObjectAttributes(&ObjAttr, &uBuf.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
544
545 rcNt = NtCreateFile(&hMyFile,
546 FILE_READ_DATA | SYNCHRONIZE,
547 &ObjAttr,
548 &Ios,
549 NULL /* Allocation Size*/,
550 FILE_ATTRIBUTE_NORMAL,
551 FILE_SHARE_READ,
552 FILE_OPEN,
553 FILE_NON_DIRECTORY_FILE,
554 NULL /*EaBuffer*/,
555 0 /*EaLength*/);
556 if (NT_SUCCESS(rcNt))
557 rcNt = Ios.Status;
558 if (!NT_SUCCESS(rcNt))
559 {
560 supR3HardenedError(VINF_SUCCESS, false,
561 "supR3HardenedMonitor_NtCreateSection: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
562 rcNt, hFile, uBuf.UniStr.Buffer);
563 return rcNt;
564 }
565 }
566 else
567 {
568#ifdef DEBUG
569
570 supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_NtCreateSection: NtDuplicateObject(,%#x,) failed: %#x\n", hFile, rcNt);
571#endif
572 hMyFile = hFile;
573 }
574 }
575
576 /*
577 * Special Kludge for Windows XP and W2K3 and their stupid attempts
578 * at mapping a hidden XML file called c:\Windows\WindowsShell.Manifest
579 * with executable access. The image bit isn't set, fortunately.
580 */
581 if ( !fImage
582 && uBuf.UniStr.Length > g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)
583 && memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer,
584 g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) == 0)
585 {
586 PRTUTF16 pwszName = &uBuf.UniStr.Buffer[(g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) / sizeof(WCHAR)];
587 if (RTUtf16ICmpAscii(pwszName, "WindowsShell.Manifest") == 0)
588 {
589 /*
590 * Drop all executable access to the mapping and let it continue.
591 */
592 SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: Applying the drop-exec-kludge for '%ls'\n", uBuf.UniStr.Buffer));
593 if (fAccess & SECTION_MAP_EXECUTE)
594 fAccess = (fAccess & ~SECTION_MAP_EXECUTE) | SECTION_MAP_READ;
595 if (fProtect & PAGE_EXECUTE)
596 fProtect = (fProtect & ~PAGE_EXECUTE) | PAGE_READONLY;
597 fProtect = (fProtect & ~UINT32_C(0xf0)) | ((fProtect & UINT32_C(0xe0)) >> 4);
598 return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
599 }
600 }
601
602 /*
603 * Check the path. We don't allow DLLs to be loaded from just anywhere:
604 * 1. System32 - normal code or cat signing.
605 * 2. WinSxS - normal code or cat signing.
606 * 3. VirtualBox - kernel code signing and integrity checks.
607 */
608 bool fSystem32 = false;
609 Assert(g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] == '\\');
610 uint32_t fFlags = 0;
611 if ( uBuf.UniStr.Length > g_System32NtPath.UniStr.Length
612 && memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0
613 && uBuf.UniStr.Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
614 {
615 fSystem32 = true;
616 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION;
617 }
618 else if ( uBuf.UniStr.Length > g_WinSxSNtPath.UniStr.Length
619 && memcmp(uBuf.UniStr.Buffer, g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length) == 0
620 && uBuf.UniStr.Buffer[g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
621 fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION;
622 else if ( uBuf.UniStr.Length > g_offSupLibHardenedExeNtName
623 && memcmp(uBuf.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Buffer,
624 g_offSupLibHardenedExeNtName * sizeof(WCHAR)) == 0)
625 fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
626 else
627 {
628 supR3HardenedError(VINF_SUCCESS, false,
629 "supR3HardenedMonitor_NtCreateSection: Not a trusted location: '%ls' (fImage=%d fExecMap=%d fExecProt=%d)\n",
630 uBuf.UniStr.Buffer, fImage, fExecMap, fExecProt);
631 if (hMyFile != hFile)
632 NtClose(hMyFile);
633 return STATUS_TRUST_FAILURE;
634 }
635
636 /*
637 * Do the verification. For better error message we borrow what's
638 * left of the path buffer for an RTERRINFO buffer.
639 */
640 RTERRINFO ErrInfo;
641 RTErrInfoInit(&ErrInfo, (char *)&uBuf.abBuffer[cbNameBuf], sizeof(uBuf) - cbNameBuf);
642
643 bool fCacheable = true;
644 int rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, &fCacheable, &ErrInfo);
645 if (RT_FAILURE(rc))
646 {
647 supR3HardenedError(VINF_SUCCESS, false,
648 "supR3HardenedMonitor_NtCreateSection: rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls: %s\n",
649 rc, fImage, fExecMap, fExecProt, uBuf.UniStr.Buffer, ErrInfo.pszMsg);
650 if (hMyFile != hFile)
651 NtClose(hMyFile);
652 return STATUS_TRUST_FAILURE;
653 }
654 if (hMyFile != hFile)
655 supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fCacheable);
656 }
657 }
658
659 /*
660 * Call checked out OK, call the original.
661 */
662 return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
663}
664
665
666#ifdef RT_ARCH_AMD64
667/**
668 * Tries to allocate memory between @a uStart and @a uEnd.
669 *
670 * @returns Pointer to the memory on success. NULL on failure.
671 * @param uStart The start address.
672 * @param uEnd The end address. This is lower than @a uStart
673 * if @a iDirection is negative, and higher if
674 * positive.
675 * @param iDirection The search direction.
676 * @param cbAlloc The number of bytes to allocate.
677 */
678static void *supR3HardenedWinAllocHookMemory(uintptr_t uStart, uintptr_t uEnd, intptr_t iDirection, size_t cbAlloc)
679{
680 size_t const cbAllocGranularity = _64K;
681 size_t const uAllocGranularityMask = ~(cbAllocGranularity - 1);
682 HANDLE const hProc = GetCurrentProcess();
683
684 /*
685 * Make uEnd the last valid return address.
686 */
687 if (iDirection > 0)
688 {
689 SUPR3HARDENED_ASSERT(uEnd > cbAlloc);
690 uEnd -= cbAlloc;
691 uEnd &= uAllocGranularityMask;
692 }
693 else
694 uEnd = RT_ALIGN_Z(uEnd, cbAllocGranularity);
695
696 /*
697 * Search for free memory.
698 */
699 uintptr_t uCur = uStart & uAllocGranularityMask;
700 for (;;)
701 {
702 /*
703 * Examine the memory at this address, if it's free, try make the allocation here.
704 */
705 SIZE_T cbIgn;
706 MEMORY_BASIC_INFORMATION MemInfo;
707 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryVirtualMemory(hProc,
708 (void *)uCur,
709 MemoryBasicInformation,
710 &MemInfo,
711 sizeof(MemInfo),
712 &cbIgn));
713 if ( MemInfo.State == MEM_FREE
714 && MemInfo.RegionSize >= cbAlloc)
715 {
716 for (;;)
717 {
718 SUPR3HARDENED_ASSERT((uintptr_t)MemInfo.BaseAddress <= uCur);
719 void *pvMem = VirtualAllocEx(hProc, (void *)uCur, cbAlloc, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
720 if (pvMem)
721 {
722 if ( iDirection > 0
723 ? (uintptr_t)pvMem >= uStart
724 && (uintptr_t)pvMem <= uEnd
725 : (uintptr_t)pvMem >= uEnd
726 && (uintptr_t)pvMem <= uStart)
727 return pvMem;
728 VirtualFreeEx(hProc, pvMem, cbAlloc, MEM_RELEASE);
729 }
730
731 /* Advance within the free area and try again? */
732 uintptr_t uNext = iDirection > 0 ? uCur + cbAllocGranularity : uCur - cbAllocGranularity;
733 uNext &= uAllocGranularityMask;
734 if ( iDirection > 0
735 ? uNext <= uCur
736 || uNext > uEnd
737 || uNext - (uintptr_t)MemInfo.BaseAddress > MemInfo.RegionSize
738 || MemInfo.RegionSize - (uNext - (uintptr_t)MemInfo.BaseAddress) < cbAlloc
739 : uNext >= uCur
740 || uNext < uEnd
741 || uNext < (uintptr_t)MemInfo.BaseAddress)
742 break;
743 uCur = uNext;
744 }
745 }
746
747 /*
748 * Advance to the next memory region.
749 */
750 if (iDirection > 0)
751 {
752 uCur = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize;
753 uCur = RT_ALIGN_Z(uCur, cbAllocGranularity);
754 if (uCur >= uEnd)
755 break;
756 }
757 else
758 {
759 uCur = (uintptr_t)(MemInfo.AllocationBase ? MemInfo.AllocationBase : MemInfo.BaseAddress);
760 if (uCur > uEnd)
761 uCur -= cbAlloc;
762 uCur &= uAllocGranularityMask;
763 if (uCur < uEnd)
764 break;
765 }
766 }
767 return NULL;
768}
769#endif
770
771
772/**
773 * Install hooks for intercepting calls dealing with mapping shared libraries
774 * into the process.
775 *
776 * This allows us to prevent undesirable shared libraries from being loaded.
777 *
778 * @remarks We assume we're alone in this process, so no seralizing trickery is
779 * necessary when installing the patch.
780 */
781DECLHIDDEN(void) supR3HardenedWinInstallHooks(void)
782{
783#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
784 /*
785 * Install a anti debugging hack before we continue. This prevents most
786 * notifications from ending up in the debugger.
787 */
788 NTSTATUS rcNt = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
789 if (!NT_SUCCESS(rcNt))
790 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
791 "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
792#endif
793
794 /*
795 * Locate the routine.
796 */
797 HMODULE hmodNtDll = GetModuleHandleW(L"NTDLL");
798 SUPR3HARDENED_ASSERT(hmodNtDll != NULL);
799
800 FARPROC pfnNtCreateSection = GetProcAddress(hmodNtDll, "NtCreateSection");
801 SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
802 SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
803
804 uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
805
806#ifdef RT_ARCH_AMD64
807 /*
808 * For 64-bit hosts we need some memory within a +/-2GB range of the
809 * actual function to be able to patch it.
810 */
811 size_t cbMem = _4K;
812 void *pvMem = supR3HardenedWinAllocHookMemory((uintptr_t)pfnNtCreateSection,
813 (uintptr_t)pfnNtCreateSection - _2G + PAGE_SIZE,
814 -1, cbMem);
815 if (!pvMem)
816 {
817 pvMem = supR3HardenedWinAllocHookMemory((uintptr_t)pfnNtCreateSection,
818 (uintptr_t)pfnNtCreateSection + _2G - PAGE_SIZE,
819 1, cbMem);
820 if (!pvMem)
821 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
822 "Failed to allocate memory within the +/-2GB range from NTDLL.\n");
823 }
824 uintptr_t *puJmpTab = (uintptr_t *)pvMem;
825
826 /*
827 * Patch 64-bit hosts.
828 */
829 PFNRT pfnCallReal = NULL;
830 uint8_t offJmpBack = UINT8_MAX;
831
832 /* Pattern #1: XP64/W2K3-64 thru Windows 8.1
833 0:000> u ntdll!NtCreateSection
834 ntdll!NtCreateSection:
835 00000000`779f1750 4c8bd1 mov r10,rcx
836 00000000`779f1753 b847000000 mov eax,47h
837 00000000`779f1758 0f05 syscall
838 00000000`779f175a c3 ret
839 00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
840 The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
841 if ( pbNtCreateSection[ 0] == 0x4c /* mov r10, rcx */
842 && pbNtCreateSection[ 1] == 0x8b
843 && pbNtCreateSection[ 2] == 0xd1
844 && pbNtCreateSection[ 3] == 0xb8 /* mov eax, 000000xxh */
845 && pbNtCreateSection[ 5] == 0x00
846 && pbNtCreateSection[ 6] == 0x00
847 && pbNtCreateSection[ 7] == 0x00
848 && pbNtCreateSection[ 8] == 0x0f /* syscall */
849 && pbNtCreateSection[ 9] == 0x05
850 && pbNtCreateSection[10] == 0xc3 /* ret */
851 )
852 {
853 offJmpBack = 8; /* the 3rd instruction (syscall). */
854 switch (pbNtCreateSection[4])
855 {
856# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
857# include "NtCreateSection-template-amd64-syscall-type-1.h"
858# undef SYSCALL
859 }
860 }
861
862 if (pfnCallReal)
863 {
864 g_pfnNtCreateSectionJmpBack = (PFNRT)(uintptr_t)(pbNtCreateSection + offJmpBack);
865 *(PFNRT *)&g_pfnNtCreateSectionReal = pfnCallReal;
866 *puJmpTab = (uintptr_t)supR3HardenedMonitor_NtCreateSection;
867
868 DWORD dwOldProt;
869 SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
870 PAGE_EXECUTE_READWRITE, &dwOldProt));
871 pbNtCreateSection[0] = 0xff;
872 pbNtCreateSection[1] = 0x25;
873 *(uint32_t *)&pbNtCreateSection[2] = (uint32_t)((uintptr_t)puJmpTab - (uintptr_t)&pbNtCreateSection[2+4]);
874
875 SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
876 PAGE_EXECUTE_READ, &dwOldProt));
877 return;
878 }
879
880#else
881 /*
882 * Patch 32-bit hosts.
883 */
884 PFNRT pfnCallReal = NULL;
885 uint8_t offJmpBack = UINT8_MAX;
886
887 /* Pattern #1: XP thru Windows 7
888 kd> u ntdll!NtCreateSection
889 ntdll!NtCreateSection:
890 7c90d160 b832000000 mov eax,32h
891 7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
892 7c90d16a ff12 call dword ptr [edx]
893 7c90d16c c21c00 ret 1Ch
894 7c90d16f 90 nop
895 The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
896
897 Pattern #2: Windows 8.1
898 0:000:x86> u ntdll_6a0f0000!NtCreateSection
899 ntdll_6a0f0000!NtCreateSection:
900 6a15eabc b854010000 mov eax,154h
901 6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
902 6a15eac6 c21c00 ret 1Ch
903 6a15eac9 8bd4 mov edx,esp
904 6a15eacb 0f34 sysenter
905 6a15eacd c3 ret
906 The variable bit is the value loaded into eax: W81=154h
907 Note! One nice thing here is that we can share code pattern #1. */
908
909 if ( pbNtCreateSection[ 0] == 0xb8 /* mov eax, 000000xxh*/
910 && pbNtCreateSection[ 2] <= 0x02
911 && pbNtCreateSection[ 3] == 0x00
912 && pbNtCreateSection[ 4] == 0x00
913 ( ( pbNtCreateSection[ 5] == 0xba /* mov edx, offset SharedUserData!SystemCallStub */
914 && pbNtCreateSection[ 6] == 0x00
915 && pbNtCreateSection[ 7] == 0x03
916 && pbNtCreateSection[ 8] == 0xfe
917 && pbNtCreateSection[ 9] == 0x7f
918 && pbNtCreateSection[10] == 0xff /* call [edx] */
919 && pbNtCreateSection[11] == 0x12
920 && pbNtCreateSection[12] == 0xc2 /* ret 1ch */
921 && pbNtCreateSection[13] == 0x1c
922 && pbNtCreateSection[14] == 0x00)
923
924 || ( pbNtCreateSection[ 5] == 0xe8 /* call [$+3] */
925 && RT_ABS(*(int32_t *)&pbNtCreateSection[6]) < 0x10
926 && pbNtCreateSection[10] == 0xc2 /* ret 1ch */
927 && pbNtCreateSection[11] == 0x1c
928 && pbNtCreateSection[12] == 0x00 )
929 )
930 )
931 {
932 offJmpBack = 5; /* the 2nd instruction. */
933 switch (*(uint32_t const *)&pbNtCreateSection[1])
934 {
935# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
936# include "NtCreateSection-template-x86-syscall-type-1.h"
937# undef SYSCALL
938 }
939 }
940
941 if (pfnCallReal)
942 {
943 g_pfnNtCreateSectionJmpBack = (PFNRT)(uintptr_t)(pbNtCreateSection + offJmpBack);
944 *(PFNRT *)&g_pfnNtCreateSectionReal = pfnCallReal;
945
946 DWORD dwOldProt;
947 SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
948 PAGE_EXECUTE_READWRITE, &dwOldProt));
949 pbNtCreateSection[0] = 0xe9;
950 *(uint32_t *)&pbNtCreateSection[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
951 - (uintptr_t)&pbNtCreateSection[1+4];
952
953 SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
954 PAGE_EXECUTE_READ, &dwOldProt));
955 return;
956 }
957#endif
958
959 supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
960 "Failed to install NtCreateSection monitor: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n "
961#ifdef RT_ARCH_X86
962 "(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
963#endif
964 ,
965 pbNtCreateSection[0], pbNtCreateSection[1], pbNtCreateSection[2], pbNtCreateSection[3],
966 pbNtCreateSection[4], pbNtCreateSection[5], pbNtCreateSection[6], pbNtCreateSection[7],
967 pbNtCreateSection[8], pbNtCreateSection[9], pbNtCreateSection[10], pbNtCreateSection[11],
968 pbNtCreateSection[12], pbNtCreateSection[13], pbNtCreateSection[14], pbNtCreateSection[15]);
969}
970
971
972/**
973 * Verifies the process integrity.
974 */
975DECLHIDDEN(void) supR3HardenedWinVerifyProcess(void)
976{
977 RTErrInfoInitStatic(&g_ErrInfoStatic);
978 int rc = supHardenedWinVerifyProcess(GetCurrentProcess(), GetCurrentThread(), &g_ErrInfoStatic.Core);
979 if (RT_FAILURE(rc))
980 supR3HardenedFatalMsg("supR3HardenedWinVerifyProcess", kSupInitOp_Integrity, rc,
981 "Failed to verify process integrity: %s", g_ErrInfoStatic.szMsg);
982}
983
984
985/**
986 * Gets the SID of the user associated with the process.
987 *
988 * @returns @c true if we've got a login SID, @c false if not.
989 * @param pSidUser Where to return the user SID.
990 * @param cbSidUser The size of the user SID buffer.
991 * @param pSidLogin Where to return the login SID.
992 * @param cbSidLogin The size of the login SID buffer.
993 */
994static bool supR3HardenedGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
995{
996 HANDLE hToken;
997 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
998 union
999 {
1000 TOKEN_USER UserInfo;
1001 TOKEN_GROUPS Groups;
1002 uint8_t abPadding[4096];
1003 } uBuf;
1004 ULONG cbRet = 0;
1005 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
1006 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
1007
1008 bool fLoginSid = false;
1009 NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
1010 if (NT_SUCCESS(rcNt))
1011 {
1012 for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
1013 if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
1014 {
1015 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
1016 fLoginSid = true;
1017 break;
1018 }
1019 }
1020
1021 SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
1022
1023 return fLoginSid;
1024}
1025
1026
1027/**
1028 * Build security attributes for the process or the primary thread (@a fProcess)
1029 *
1030 * Process DACLs can be bypassed using the SeDebugPrivilege (generally available
1031 * to admins, i.e. normal windows users), or by taking ownership and/or
1032 * modifying the DACL. However, it restricts
1033 *
1034 * @param pSecAttrs Where to return the security attributes.
1035 * @param pCleanup Cleanup record.
1036 * @param fProcess Set if it's for the process, clear if it's for
1037 * the primary thread.
1038 */
1039static void supR3HardenedInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
1040{
1041 /*
1042 * Safe return values.
1043 */
1044 suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
1045
1046 pSecAttrs->nLength = sizeof(*pSecAttrs);
1047 pSecAttrs->bInheritHandle = FALSE;
1048 pSecAttrs->lpSecurityDescriptor = NULL;
1049
1050/** @todo This isn't at all complete, just sketches... */
1051
1052 /*
1053 * Create an ACL detailing the access of the above groups.
1054 */
1055 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
1056
1057 ULONG fDeny = DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL;
1058 ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
1059 ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
1060 if (fProcess)
1061 {
1062 fDeny |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
1063 | PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
1064 | PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
1065 fAllow |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
1066 fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
1067 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
1068 {
1069 fAllow |= PROCESS_QUERY_LIMITED_INFORMATION;
1070 fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
1071 }
1072 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
1073 fAllow |= PROCESS_SET_LIMITED_INFORMATION;
1074 }
1075 else
1076 {
1077 fDeny |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
1078 | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
1079 fAllow |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
1080 fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
1081 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
1082 {
1083 fAllow |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
1084 fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
1085 }
1086
1087 }
1088 fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
1089
1090 /* Deny everyone access to bad bits. */
1091#if 1
1092 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
1093 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
1094 *RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
1095 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1096 fDeny, &pCleanup->Everyone.Sid));
1097#endif
1098
1099#if 0
1100 /* Grant some access to the owner - doesn't work. */
1101 SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
1102 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
1103 *RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
1104
1105 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1106 fDeny, &pCleanup->Owner.Sid));
1107 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1108 fAllow, &pCleanup->Owner.Sid));
1109#endif
1110
1111#if 1
1112 bool fHasLoginSid = supR3HardenedGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
1113 &pCleanup->Login.Sid, sizeof(pCleanup->Login));
1114
1115# if 1
1116 /* Grant minimal access to the user. */
1117 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1118 fDeny, &pCleanup->User.Sid));
1119 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1120 fAllow, &pCleanup->User.Sid));
1121# endif
1122
1123# if 1
1124 /* Grant very limited access to the login sid. */
1125 if (fHasLoginSid)
1126 {
1127 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
1128 fAllowLogin, &pCleanup->Login.Sid));
1129 }
1130# endif
1131
1132#endif
1133
1134 /*
1135 * Create a security descriptor with the above ACL.
1136 */
1137 PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)suplibHardenedAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
1138 pCleanup->pSecDesc = pSecDesc;
1139
1140 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
1141 SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
1142 FALSE /*fDaclDefaulted*/));
1143 pSecAttrs->lpSecurityDescriptor = pSecDesc;
1144}
1145
1146
1147/**
1148 * Predicate function which tests whether @a ch is a argument separator
1149 * character.
1150 *
1151 * @returns True/false.
1152 * @param ch The character to examine.
1153 */
1154DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
1155{
1156 return ch == ' '
1157 || ch == '\t'
1158 || ch == '\n'
1159 || ch == '\r';
1160}
1161
1162
1163/**
1164 * Construct the new command line. Since argc/argv are both derived from
1165 * GetCommandLineW (see suplibHardenedWindowsMain), we skip the argument
1166 * by argument UTF-8 -> UTF-16 conversion and quoting by going to the
1167 * original source.
1168 *
1169 * The executable name, though, is replaced in case it's not a fullly
1170 * qualified path.
1171 *
1172 * The re-spawn indicator is added immediately after the executable name
1173 * so that we don't get tripped up missing close quote chars in the last
1174 * argument.
1175 *
1176 * @returns Pointer to a command line string (heap).
1177 */
1178static PRTUTF16 supR3HardenedWinConstructCmdLine(void)
1179{
1180 /*
1181 * Get the command line and skip the executable name.
1182 */
1183 PCRTUTF16 pwszArgs = GetCommandLineW();
1184
1185 /* Skip leading space (shouldn't be any, but whatever). */
1186 while (suplibCommandLineIsArgSeparator(*pwszArgs))
1187 pwszArgs++;
1188 SUPR3HARDENED_ASSERT(*pwszArgs != '\0');
1189
1190 /* Walk to the end of it. */
1191 int fQuoted = false;
1192 do
1193 {
1194 if (*pwszArgs == '"')
1195 {
1196 fQuoted = !fQuoted;
1197 pwszArgs++;
1198 }
1199 else if (*pwszArgs != '\\' || (pwszArgs[1] != '\\' && pwszArgs[1] != '"'))
1200 pwszArgs++;
1201 else
1202 {
1203 unsigned cSlashes = 0;
1204 do
1205 cSlashes++;
1206 while (*++pwszArgs == '\\');
1207 if (*pwszArgs == '"' && (cSlashes & 1))
1208 pwszArgs++; /* odd number of slashes == escaped quote */
1209 }
1210 } while (*pwszArgs && (fQuoted || !suplibCommandLineIsArgSeparator(*pwszArgs)));
1211
1212 /* Skip trailing spaces. */
1213 while (suplibCommandLineIsArgSeparator(*pwszArgs))
1214 pwszArgs++;
1215
1216 /*
1217 * Allocate a new buffer.
1218 */
1219 size_t cwcArgs = suplibHardenedWStrLen(pwszArgs);
1220 size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_ARG0) - 1) / sizeof(SUPR3_RESPAWN_ARG0[0]) /* Respawn exe name. */
1221 + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
1222 PRTUTF16 pwszCmdLine = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0 /* dwFlags*/, (cwcCmdLine + 1) * sizeof(RTUTF16));
1223 SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
1224
1225 /*
1226 * Construct the new command line.
1227 */
1228 PRTUTF16 pwszDst = pwszCmdLine;
1229 for (const char *pszSrc = SUPR3_RESPAWN_ARG0; *pszSrc; pszSrc++)
1230 *pwszDst++ = *pszSrc;
1231
1232 if (cwcArgs)
1233 {
1234 *pwszDst++ = ' ';
1235 suplibHardenedMemCopy(pwszDst, pwszArgs, cwcArgs * sizeof(RTUTF16));
1236 pwszDst += cwcArgs;
1237 }
1238
1239 *pwszDst = '\0';
1240 SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
1241
1242 return pwszCmdLine;
1243}
1244
1245
1246/**
1247 * Does the actually respawning.
1248 *
1249 * @returns Exit code (if we get that far).
1250 */
1251static int supR3HardenedWinDoReSpawn(void)
1252{
1253 SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
1254
1255 /*
1256 * Configure the startup info and creation flags.
1257 */
1258 DWORD dwCreationFlags = 0;
1259
1260 STARTUPINFOEXW SiEx;
1261 suplibHardenedMemSet(&SiEx, 0, sizeof(SiEx));
1262 if (1)
1263 SiEx.StartupInfo.cb = sizeof(SiEx.StartupInfo);
1264 else
1265 {
1266 SiEx.StartupInfo.cb = sizeof(SiEx);
1267 dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
1268 /** @todo experiment with protected process stuff later on. */
1269 }
1270
1271 SiEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
1272 SiEx.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1273 SiEx.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1274 SiEx.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1275
1276 /*
1277 * Set up security descriptors.
1278 */
1279 SECURITY_ATTRIBUTES ProcessSecAttrs;
1280 MYSECURITYCLEANUP ProcessSecAttrsCleanup;
1281 supR3HardenedInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
1282
1283 SECURITY_ATTRIBUTES ThreadSecAttrs;
1284 MYSECURITYCLEANUP ThreadSecAttrsCleanup;
1285 supR3HardenedInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
1286
1287 /*
1288 * Construct the command line and launch the process.
1289 */
1290 PRTUTF16 pwszCmdLine = supR3HardenedWinConstructCmdLine();
1291
1292 PROCESS_INFORMATION ProcessInfo;
1293 if (!CreateProcessW(g_wszSupLibHardenedExePath,
1294 pwszCmdLine,
1295 &ProcessSecAttrs,
1296 &ThreadSecAttrs,
1297 TRUE /*fInheritHandles*/,
1298 dwCreationFlags,
1299 NULL /*pwszzEnvironment*/,
1300 NULL /*pwszCurDir*/,
1301 &SiEx.StartupInfo,
1302 &ProcessInfo))
1303 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
1304 "Error relaunching VirtualBox VM process: %u\n"
1305 "Command line: '%ls'",
1306 GetLastError(), pwszCmdLine);
1307
1308 /*
1309 * Close the unrestricted access handles. Since we need to wait on the
1310 * child process, we'll reopen the process with limited access before doing
1311 * away with the process handle returned by CreateProcess.
1312 */
1313 SUPR3HARDENED_ASSERT(CloseHandle(ProcessInfo.hThread));
1314 ProcessInfo.hThread = NULL;
1315
1316 HANDLE hProcWait;
1317 DWORD dwRights = SYNCHRONIZE;
1318 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
1319 dwRights |= PROCESS_QUERY_LIMITED_INFORMATION;
1320 else
1321 dwRights |= PROCESS_QUERY_INFORMATION;
1322 if (!DuplicateHandle(GetCurrentProcess(),
1323 ProcessInfo.hProcess,
1324 GetCurrentProcess(),
1325 &hProcWait,
1326 SYNCHRONIZE,
1327 FALSE /*fInheritHandle*/,
1328 0))
1329 {
1330 /* This is unacceptable, kill the process. */
1331 DWORD dwErr = GetLastError();
1332 TerminateProcess(ProcessInfo.hProcess, RTEXITCODE_FAILURE);
1333 supR3HardenedError(dwErr, false /*fFatal*/, "DuplicateHandle failed on child process handle: %u\n", dwErr);
1334
1335 DWORD dwExit;
1336 BOOL fExitOk = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit)
1337 && dwExit != STILL_ACTIVE;
1338 if (!fExitOk)
1339 {
1340 DWORD dwStartTick = GetTickCount();
1341 DWORD dwWait;
1342 do
1343 {
1344 TerminateProcess(ProcessInfo.hProcess, RTEXITCODE_FAILURE);
1345 dwWait = WaitForSingleObject(ProcessInfo.hProcess, 1000);
1346 fExitOk = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit)
1347 && dwExit != STILL_ACTIVE;
1348 } while ( !fExitOk
1349 && (dwWait == WAIT_TIMEOUT || dwWait == WAIT_IO_COMPLETION)
1350 && GetTickCount() - dwStartTick < 60 * 1000);
1351 if (fExitOk)
1352 supR3HardenedError(dwErr, false /*fFatal*/,
1353 "DuplicateHandle failed and we failed to kill child: dwErr=%u dwWait=%u err=%u hProcess=%p\n",
1354 dwErr, dwWait, GetLastError(), ProcessInfo.hProcess);
1355 }
1356 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
1357 "DuplicateHandle failed on child process handle: %u\n", dwErr);
1358 }
1359
1360 SUPR3HARDENED_ASSERT(CloseHandle(ProcessInfo.hProcess));
1361 ProcessInfo.hProcess = NULL;
1362
1363 /*
1364 * Wait for the process to terminate and proxy the termination code.
1365 */
1366 for (;;)
1367 {
1368 SetLastError(NO_ERROR);
1369 DWORD dwWait = WaitForSingleObject(hProcWait, INFINITE);
1370 if ( dwWait == WAIT_OBJECT_0
1371 || dwWait == WAIT_ABANDONED_0)
1372 break;
1373 if ( dwWait != WAIT_TIMEOUT
1374 && dwWait != WAIT_IO_COMPLETION)
1375 supR3HardenedFatal("WaitForSingleObject returned %#x (last error %#x)\n", dwWait, GetLastError());
1376 }
1377
1378 DWORD dwExit;
1379 if ( !GetExitCodeProcess(hProcWait, &dwExit)
1380 || dwExit == STILL_ACTIVE)
1381 dwExit = RTEXITCODE_FAILURE;
1382
1383 CloseHandle(hProcWait);
1384 suplibHardenedExit((RTEXITCODE)dwExit);
1385}
1386
1387
1388/**
1389 * Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
1390 *
1391 * @returns Program exit code.
1392 */
1393DECLHIDDEN(int) supR3HardenedWinReSpawn(void)
1394{
1395 /*
1396 * Open the stub device.
1397 */
1398 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1399 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1400
1401 static const WCHAR s_wszName[] = L"\\Device\\VBoxDrvStub";
1402 UNICODE_STRING NtName;
1403 NtName.Buffer = (PWSTR)s_wszName;
1404 NtName.Length = sizeof(s_wszName) - sizeof(WCHAR);
1405 NtName.MaximumLength = sizeof(s_wszName);
1406
1407 OBJECT_ATTRIBUTES ObjAttr;
1408 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1409
1410 NTSTATUS rcNt = NtCreateFile(&hFile,
1411 GENERIC_READ | GENERIC_WRITE,
1412 &ObjAttr,
1413 &Ios,
1414 NULL /* Allocation Size*/,
1415 FILE_ATTRIBUTE_NORMAL,
1416 FILE_SHARE_READ | FILE_SHARE_WRITE,
1417 FILE_OPEN,
1418 FILE_NON_DIRECTORY_FILE,
1419 NULL /*EaBuffer*/,
1420 0 /*EaLength*/);
1421 if (NT_SUCCESS(rcNt))
1422 rcNt = Ios.Status;
1423 if (!NT_SUCCESS(rcNt))
1424 {
1425 int rc = VERR_OPEN_FAILED;
1426 if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
1427 rc = SUP_NT_STATUS_TO_VBOX(rcNt);
1428 else
1429 {
1430 const char *pszDefine;
1431 switch (rcNt)
1432 {
1433 case STATUS_NO_SUCH_DEVICE: pszDefine = " STATUS_NO_SUCH_DEVICE"; break;
1434 case STATUS_OBJECT_NAME_NOT_FOUND: pszDefine = " STATUS_OBJECT_NAME_NOT_FOUND"; break;
1435 case STATUS_ACCESS_DENIED: pszDefine = " STATUS_ACCESS_DENIED"; break;
1436 case STATUS_TRUST_FAILURE: pszDefine = " STATUS_TRUST_FAILURE"; break;
1437 default: pszDefine = ""; break;
1438 }
1439 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
1440 "NtCreateFile(%ls) failed: %#x%s\n", s_wszName, rcNt, pszDefine);
1441 }
1442 supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, rc,
1443 "NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)\n", s_wszName, rc, rcNt);
1444 }
1445
1446 /*
1447 * Respawn the process with kernel protection for the new process.
1448 */
1449 return supR3HardenedWinDoReSpawn();
1450}
1451
1452
1453/**
1454 * Checks if re-spawning is required, replacing the respawn argument if not.
1455 *
1456 * @returns true if required, false if not. In the latter case, the first
1457 * argument in the vector is replaced.
1458 * @param cArgs The number of arguments.
1459 * @param papszArgs Pointer to the argument vector.
1460 */
1461DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int cArgs, char **papszArgs)
1462{
1463 SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
1464
1465 if (cArgs < 1)
1466 return true;
1467 if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_ARG0))
1468 return true;
1469
1470 /* Replace the argument. */
1471 papszArgs[0] = g_szSupLibHardenedExePath;
1472 return false;
1473}
1474
1475
1476/**
1477 * Initializes the windows verficiation bits.
1478 * @param fFlags The main flags.
1479 */
1480DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags)
1481{
1482 RTErrInfoInitStatic(&g_ErrInfoStatic);
1483 int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
1484 if (RT_FAILURE(rc))
1485 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
1486 "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
1487 supR3HardenedWinInstallHooks();
1488
1489#ifndef VBOX_WITH_VISTA_NO_SP
1490 /*
1491 * Complain about Vista w/o service pack if we're launching a VM.
1492 */
1493 if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
1494 && g_uNtVerCombined >= SUP_NT_VER_VISTA
1495 && g_uNtVerCombined < SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
1496 supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_NOT_SUPPORTED,
1497 "Window Vista without any service pack installed is not supported. Please install the latest service pack.");
1498#endif
1499}
1500
1501
1502/**
1503 * Converts the Windows command line string (UTF-16) to an array of UTF-8
1504 * arguments suitable for passing to main().
1505 *
1506 * @returns Pointer to the argument array.
1507 * @param pwszCmdLine The UTF-16 windows command line to parse.
1508 * @param pcArgs Where to return the number of arguments.
1509 */
1510static char **suplibCommandLineToArgvWStub(PCRTUTF16 pwszCmdLine, int *pcArgs)
1511{
1512 /*
1513 * Convert the command line string to UTF-8.
1514 */
1515 int cbNeeded = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, pwszCmdLine, -1, NULL /*pszDst*/, 0 /*cbDst*/,
1516 NULL /*pchDefChar*/, NULL /* pfUsedDefChar */);
1517 SUPR3HARDENED_ASSERT(cbNeeded > 0);
1518 int cbAllocated = cbNeeded + 16;
1519 char *pszCmdLine = (char *)suplibHardenedAllocZ(cbAllocated);
1520
1521 SUPR3HARDENED_ASSERT(WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, pwszCmdLine, -1,
1522 pszCmdLine, cbAllocated - 1,
1523 NULL /*pchDefChar*/, NULL /* pfUsedDefChar */) == cbNeeded);
1524
1525 /*
1526 * Parse the command line, carving argument strings out of it.
1527 */
1528 int cArgs = 0;
1529 int cArgsAllocated = 4;
1530 char **papszArgs = (char **)suplibHardenedAllocZ(sizeof(char *) * cArgsAllocated);
1531 char *pszSrc = pszCmdLine;
1532 for (;;)
1533 {
1534 /* skip leading blanks. */
1535 char ch = *pszSrc;
1536 while (suplibCommandLineIsArgSeparator(ch))
1537 ch = *++pszSrc;
1538 if (!ch)
1539 break;
1540
1541 /* Add argument to the vector. */
1542 if (cArgs + 2 >= cArgsAllocated)
1543 {
1544 cArgsAllocated *= 2;
1545 papszArgs = (char **)suplibHardenedReAlloc(papszArgs, sizeof(char *) * cArgsAllocated);
1546 }
1547 papszArgs[cArgs++] = pszSrc;
1548 papszArgs[cArgs] = NULL;
1549
1550 /* Unquote and unescape the string. */
1551 char *pszDst = pszSrc++;
1552 bool fQuoted = false;
1553 do
1554 {
1555 if (ch == '"')
1556 fQuoted = !fQuoted;
1557 else if (ch != '\\' || (*pszSrc != '\\' && *pszSrc != '"'))
1558 *pszDst++ = ch;
1559 else
1560 {
1561 unsigned cSlashes = 0;
1562 while ((ch = *pszSrc++) == '\\')
1563 cSlashes++;
1564 if (ch == '"')
1565 {
1566 while (cSlashes >= 2)
1567 {
1568 cSlashes -= 2;
1569 *pszDst++ = '\\';
1570 }
1571 if (cSlashes)
1572 *pszDst++ = '"';
1573 else
1574 fQuoted = !fQuoted;
1575 }
1576 else
1577 {
1578 pszSrc--;
1579 while (cSlashes-- > 0)
1580 *pszDst++ = '\\';
1581 }
1582 }
1583
1584 ch = *pszSrc++;
1585 } while (ch != '\0' && (fQuoted || !suplibCommandLineIsArgSeparator(ch)));
1586
1587 /* Terminate the argument. */
1588 *pszDst = '\0';
1589 if (!ch)
1590 break;
1591 }
1592
1593 *pcArgs = cArgs;
1594 return papszArgs;
1595}
1596
1597
1598extern "C" int main(int argc, char **argv, char **envp);
1599
1600/**
1601 * The executable entry point.
1602 *
1603 * This is normally taken care of by the C runtime library, but we don't want to
1604 * get involved with anything as complicated like the CRT in this setup. So, we
1605 * it everything ourselves, including parameter parsing.
1606 */
1607extern "C" void __stdcall suplibHardenedWindowsMain(void)
1608{
1609 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
1610
1611 g_cSuplibHardenedWindowsMainCalls++;
1612
1613 /*
1614 * Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
1615 * SUPHardenedVerfiyImage-win.cpp.)
1616 */
1617 supR3HardenedWinInitVersion();
1618
1619 /*
1620 * Get the executable name.
1621 */
1622 DWORD cwcExecName = GetModuleFileNameW(GetModuleHandle(NULL), g_wszSupLibHardenedExePath,
1623 RT_ELEMENTS(g_wszSupLibHardenedExePath));
1624 if (cwcExecName >= RT_ELEMENTS(g_wszSupLibHardenedExePath))
1625 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, VERR_BUFFER_OVERFLOW,
1626 "The executable path is too long.");
1627
1628 /* The NT version. */
1629 HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
1630 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1631 if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
1632 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(GetLastError()),
1633 "Error opening the executable: %u (%ls).", GetLastError());
1634 RT_ZERO(g_SupLibHardenedExeNtPath);
1635 ULONG cbIgn;
1636 NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &g_SupLibHardenedExeNtPath,
1637 sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbIgn);
1638 if (!NT_SUCCESS(rcNt))
1639 supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
1640 "NtQueryObject -> %#x (on %ls)\n", rcNt, g_wszSupLibHardenedExePath);
1641 CloseHandle(hFile);
1642
1643 /* The NT executable name offset / dir path length. */
1644 g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
1645 while ( g_offSupLibHardenedExeNtName > 1
1646 && g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
1647 g_offSupLibHardenedExeNtName--;
1648
1649 /*
1650 * Convert the arguments to UTF-8 and call the C/C++ main function.
1651 */
1652 int cArgs;
1653 char **papszArgs = suplibCommandLineToArgvWStub(GetCommandLineW(), &cArgs);
1654
1655 rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
1656
1657 /*
1658 * Exit the process (never return).
1659 */
1660 suplibHardenedExit(rcExit);
1661}
1662
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