SUPR3HardenedMain-win.cpp revision 40839c441cb305d84420565f7ca25403d8177413
/* $Id$ */
/** @file
* VirtualBox Support Library - Hardened main(), windows bits.
*/
/*
* Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <AccCtrl.h>
#include <AclApi.h>
#ifndef PROCESS_SET_LIMITED_INFORMATION
# define PROCESS_SET_LIMITED_INFORMATION 0x2000
#endif
# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
#endif
#include <iprt/initterm.h>
#include "SUPLibInternal.h"
#include "win/SUPHardenedVerify-win.h"
#include "../SUPDrvIOC.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The first argument of a respawed stub argument.
* This just needs to be unique enough to avoid most confusion with real
* executable names, there are other checks in place to make sure we've respanwed. */
#define SUPR3_RESPAWN_ARG0 "81954AF5-4D2F-31EB-A142-B7AF187A1C41-suplib-2ndchild"
/** Unconditional assertion. */
#define SUPR3HARDENED_ASSERT(a_Expr) \
do { \
if (!(a_Expr)) \
} while (0)
/** Unconditional assertion of NT_SUCCESS. */
#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
do { \
if (!NT_SUCCESS(rcNtAssert)) \
} while (0)
/** Unconditional assertion of a WIN32 API returning non-FALSE. */
#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
do { \
} while (0)
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Security descriptor cleanup structure.
*/
typedef struct MYSECURITYCLEANUP
{
union
{
union
{
} Acl;
/** Pointer to security cleanup structure. */
typedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
/**
* Image verifier cache entry.
*/
typedef struct VERIFIERCACHEENTRY
{
/** Pointer to the next entry with the same hash value. */
struct VERIFIERCACHEENTRY * volatile pNext;
/** The file handle. */
/** If fIndexNumber is set, this is an file system internal file identifier. */
/** The path hash value. */
/** The verification result. */
int rc;
/** Whether IndexNumber is valid */
bool fIndexNumberValid;
/** cwcPath * sizeof(RTUTF16). */
/** The full path of this entry (variable size). */
/** Pointer to an image verifier path entry. */
typedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** @name Global variables initialized by suplibHardenedWindowsMain.
* @{ */
/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
uint32_t g_uNtVerCombined = 0;
/** Count calls to the special main function for linking santity checks. */
static uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
/** The UTF-16 windows path to the executable. */
/** The NT path of the executable. */
/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
* not byte). This also gives the length of the exectuable directory path,
* including a trailing slash. */
/** @} */
/** @name Hook related variables.
* @{ */
/** The jump back address of the patched NtCreateSection. */
/** Pointer to the bit of assembly code that will perform the original
* NtCreateSection operation. */
/** The hash table of verifier cache . */
/** @ */
/** Static error info structure used during init. */
static RTERRINFOSTATIC g_ErrInfoStatic;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef RT_ARCH_AMD64
# include "NtCreateSection-template-amd64-syscall-type-1.h"
#endif
#ifdef RT_ARCH_X86
# include "NtCreateSection-template-x86-syscall-type-1.h"
#endif
/**
* Simple wide char search routine.
*
* @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
* NULL if not found.
* @param pwszHaystack Pointer to the string that should be searched.
* @param wcNeedle The character to search for.
*/
{
for (;;)
{
return (PRTUTF16)pwszHaystack;
if (wcCur == '\0')
return NULL;
pwszHaystack++;
}
}
/**
* Simple wide char string length routine.
*
* @returns The number of characters in the given string. (Excludes the
* terminator.)
* @param pwsz The string.
*/
{
while (*pwszCur != '\0')
pwszCur++;
}
/**
* Allocate zero filled memory on the heap.
*
* @returns Pointer to the memory. Will never return NULL, triggers a fatal
* error instead.
* @param cb The number of bytes to allocate.
*/
{
if (!pv)
return pv;
}
/**
* Reallocates memory on the heap.
*
* @returns Pointer to the resized memory block. Will never return NULL,
* triggers a fatal error instead.
* @param pvOld The old memory block.
* @param cbNew The new block size.
*/
{
if (!pvOld)
return suplibHardenedAllocZ(cbNew);
if (!pv)
return pv;
}
/**
* Frees memory allocated by suplibHardenedAlloc, suplibHardenedAllocZ or
* suplibHardenedReAlloc.
*
* @param pv Pointer to the memeory to be freed.
*/
{
if (pv)
}
/**
* Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
* and supplies the right flags.
*
* @returns Module handle on success, NULL on failure.
* @param pszName The full path to the DLL.
* @param fSystem32Only Whether to only look for imports in the system32
* directory. If set to false, the application
* directory is also searched.
*/
{
if (RT_SUCCESS(rc))
{
while (*pwszPath)
{
if (*pwszPath == '/')
*pwszPath = '\\';
pwszPath++;
}
{
if (!fSystem32Only)
}
/* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
if ( !pvRet
&& fFlags
&& GetLastError() == ERROR_INVALID_PARAMETER)
return pvRet;
}
return NULL;
}
/**
* Gets the internal index number of the file.
*
* @returns True if we got an index number, false if not.
* @param hFile The file in question.
* @param pIndexNumber where to return the index number.
*/
{
NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
if (NT_SUCCESS(rcNt))
#ifdef DEBUG_bird
if (!NT_SUCCESS(rcNt))
__debugbreak();
#endif
}
/**
* Calculates the hash value for the given UTF-16 string.
*
* @returns Hash value.
* @param pUniStr String to hash.
*/
{
while (cwcLeft-- > 0)
{
}
return uHash;
}
/**
* Inserts the given verifier result into the cache.
*
* @param pUniStr The full path of the image.
* @param hFile The file handle - must either be entered into
* the cache or closed.
* @param rc The verifier result.
* @param fCacheable Whether this is a cacheable result. Passed in
* here instead of being handled by the caller to
* save code duplication.
*/
static void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc, bool fCacheable)
{
/*
* Don't cache anything until we've got the WinVerifyTrust API up and running.
*/
&& fCacheable)
{
/*
* Allocate and initalize a new entry.
*/
if (pEntry)
{
/*
* Try insert it, careful with concurrent code as well as potential duplicates.
*/
for (;;)
{
return;
if (!pOther)
continue;
break;
}
/* Duplicate entry. */
#ifdef DEBUG_bird
__debugbreak();
#endif
}
}
}
/**
* Looks up an entry in the verifier hash table.
*
* @return Pointer to the entry on if found, NULL if not.
* @param pUniStr The full path of the image.
* @param hFile The file handle.
*/
static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile)
{
while (pCur)
{
{
if (!pCur->fIndexNumberValid)
return pCur;
if ( fIndexNumberValid
return pCur;
#ifdef DEBUG_bird
__debugbreak();
#endif
}
}
return NULL;
}
/**
* Hook that monitors NtCreateSection calls.
*
* @returns NT status code.
* @param phSection Where to return the section handle.
* @param fAccess The desired access.
* @param pObjAttribs The object attributes (optional).
* @param pcbSection The section size (optional).
* @param fProtect The max section protection.
* @param fAttribs The section attributes.
* @param hFile The file to create a section from (optional).
*/
supR3HardenedMonitor_NtCreateSection(PHANDLE phSection, ACCESS_MASK fAccess, POBJECT_ATTRIBUTES pObjAttribs,
{
&& hFile != INVALID_HANDLE_VALUE)
{
bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
{
/*
* Query the name of the file, making sure to zero terminator the
* string. (2nd half of buffer is used for error info, see below.)
*/
union
{
} uBuf;
NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
if (!NT_SUCCESS(rcNt))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_NtCreateSection: NtQueryObject -> %#x (fImage=%d fExecMap=%d fExecProt=%d)\n",
return rcNt;
}
/*
* Check the cache.
*/
if (pCacheHit)
{
SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: cache hit (%Rrc) on %ls\n", pCacheHit->rc, pCacheHit->wszPath));
return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_NtCreateSection: cached rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls\n",
return STATUS_TRUST_FAILURE;
}
/*
* On XP the loader might hand us handles with just FILE_EXECUTE and
* SYNCRHONIZE, the means reading will fail later on. So, we might
* have to reopen the file here in order to validate it - annoying.
*/
&hMyFile,
0 /* Handle attributes*/, 0 /* Options */);
if (!NT_SUCCESS(rcNt))
{
if (rcNt == STATUS_ACCESS_DENIED)
{
InitializeObjectAttributes(&ObjAttr, &uBuf.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
if (!NT_SUCCESS(rcNt))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_NtCreateSection: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
return rcNt;
}
}
else
{
#ifdef DEBUG
supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_NtCreateSection: NtDuplicateObject(,%#x,) failed: %#x\n", hFile, rcNt);
#endif
}
}
/*
* Special Kludge for Windows XP and W2K3 and their stupid attempts
* at mapping a hidden XML file called c:\Windows\WindowsShell.Manifest
* with executable access. The image bit isn't set, fortunately.
*/
if ( !fImage
{
PRTUTF16 pwszName = &uBuf.UniStr.Buffer[(g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) / sizeof(WCHAR)];
{
/*
* Drop all executable access to the mapping and let it continue.
*/
SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: Applying the drop-exec-kludge for '%ls'\n", uBuf.UniStr.Buffer));
if (fAccess & SECTION_MAP_EXECUTE)
if (fProtect & PAGE_EXECUTE)
return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
}
}
/*
* Check the path. We don't allow DLLs to be loaded from just anywhere:
* 1. System32 - normal code or cat signing.
* 2. WinSxS - normal code or cat signing.
* 3. VirtualBox - kernel code signing and integrity checks.
*/
bool fSystem32 = false;
{
fSystem32 = true;
}
g_offSupLibHardenedExeNtName * sizeof(WCHAR)) == 0)
else
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_NtCreateSection: Not a trusted location: '%ls' (fImage=%d fExecMap=%d fExecProt=%d)\n",
return STATUS_TRUST_FAILURE;
}
/*
* Do the verification. For better error message we borrow what's
* left of the path buffer for an RTERRINFO buffer.
*/
bool fCacheable = true;
int rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, &fCacheable, &ErrInfo);
if (RT_FAILURE(rc))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_NtCreateSection: rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls: %s\n",
return STATUS_TRUST_FAILURE;
}
}
}
/*
* Call checked out OK, call the original.
*/
return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
}
#ifdef RT_ARCH_AMD64
/**
* Tries to allocate memory between @a uStart and @a uEnd.
*
* @returns Pointer to the memory on success. NULL on failure.
* @param uStart The start address.
* @param uEnd The end address. This is lower than @a uStart
* if @a iDirection is negative, and higher if
* positive.
* @param iDirection The search direction.
* @param cbAlloc The number of bytes to allocate.
*/
static void *supR3HardenedWinAllocHookMemory(uintptr_t uStart, uintptr_t uEnd, intptr_t iDirection, size_t cbAlloc)
{
/*
* Make uEnd the last valid return address.
*/
if (iDirection > 0)
{
}
else
/*
* Search for free memory.
*/
for (;;)
{
/*
* Examine the memory at this address, if it's free, try make the allocation here.
*/
(void *)uCur,
&MemInfo,
sizeof(MemInfo),
&cbIgn));
{
for (;;)
{
void *pvMem = VirtualAllocEx(hProc, (void *)uCur, cbAlloc, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pvMem)
{
if ( iDirection > 0
return pvMem;
}
/* Advance within the free area and try again? */
if ( iDirection > 0
break;
}
}
/*
* Advance to the next memory region.
*/
if (iDirection > 0)
{
break;
}
else
{
break;
}
}
return NULL;
}
#endif
/**
* Install hooks for intercepting calls dealing with mapping shared libraries
* into the process.
*
* This allows us to prevent undesirable shared libraries from being loaded.
*
* @remarks We assume we're alone in this process, so no seralizing trickery is
* necessary when installing the patch.
*/
DECLHIDDEN(void) supR3HardenedWinInstallHooks(void)
{
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
/*
* Install a anti debugging hack before we continue. This prevents most
* notifications from ending up in the debugger.
*/
if (!NT_SUCCESS(rcNt))
"NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
#endif
/*
* Locate the routine.
*/
#ifdef RT_ARCH_AMD64
/*
* For 64-bit hosts we need some memory within a +/-2GB range of the
* actual function to be able to patch it.
*/
-1, cbMem);
if (!pvMem)
{
1, cbMem);
if (!pvMem)
"Failed to allocate memory within the +/-2GB range from NTDLL.\n");
}
/*
* Patch 64-bit hosts.
*/
0:000> u ntdll!NtCreateSection
ntdll!NtCreateSection:
00000000`779f1750 4c8bd1 mov r10,rcx
00000000`779f1753 b847000000 mov eax,47h
00000000`779f1758 0f05 syscall
00000000`779f175a c3 ret
00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
)
{
switch (pbNtCreateSection[4])
{
# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
# include "NtCreateSection-template-amd64-syscall-type-1.h"
}
}
if (pfnCallReal)
{
pbNtCreateSection[0] = 0xff;
*(uint32_t *)&pbNtCreateSection[2] = (uint32_t)((uintptr_t)puJmpTab - (uintptr_t)&pbNtCreateSection[2+4]);
return;
}
#else
/*
* Patch 32-bit hosts.
*/
/* Pattern #1: XP thru Windows 7
kd> u ntdll!NtCreateSection
ntdll!NtCreateSection:
7c90d160 b832000000 mov eax,32h
7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c90d16a ff12 call dword ptr [edx]
7c90d16c c21c00 ret 1Ch
7c90d16f 90 nop
The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
Pattern #2: Windows 8.1
0:000:x86> u ntdll_6a0f0000!NtCreateSection
ntdll_6a0f0000!NtCreateSection:
6a15eabc b854010000 mov eax,154h
6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
6a15eac6 c21c00 ret 1Ch
6a15eac9 8bd4 mov edx,esp
6a15eacb 0f34 sysenter
6a15eacd c3 ret
The variable bit is the value loaded into eax: W81=154h
Note! One nice thing here is that we can share code pattern #1. */
)
)
{
{
# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
# include "NtCreateSection-template-x86-syscall-type-1.h"
}
}
if (pfnCallReal)
{
pbNtCreateSection[0] = 0xe9;
return;
}
#endif
"Failed to install NtCreateSection monitor: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n "
#ifdef RT_ARCH_X86
"(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
#endif
,
}
/**
* Verifies the process integrity.
*/
DECLHIDDEN(void) supR3HardenedWinVerifyProcess(void)
{
int rc = supHardenedWinVerifyProcess(GetCurrentProcess(), GetCurrentThread(), &g_ErrInfoStatic.Core);
if (RT_FAILURE(rc))
}
/**
* Gets the SID of the user associated with the process.
*
* @returns @c true if we've got a login SID, @c false if not.
* @param pSidUser Where to return the user SID.
* @param cbSidUser The size of the user SID buffer.
* @param pSidLogin Where to return the login SID.
* @param cbSidLogin The size of the login SID buffer.
*/
static bool supR3HardenedGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
{
union
{
} uBuf;
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
bool fLoginSid = false;
if (NT_SUCCESS(rcNt))
{
{
fLoginSid = true;
break;
}
}
return fLoginSid;
}
/**
* Build security attributes for the process or the primary thread (@a fProcess)
*
* Process DACLs can be bypassed using the SeDebugPrivilege (generally available
* modifying the DACL. However, it restricts
*
* @param pSecAttrs Where to return the security attributes.
* @param pCleanup Cleanup record.
* @param fProcess Set if it's for the process, clear if it's for
* the primary thread.
*/
static void supR3HardenedInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
{
/*
* Safe return values.
*/
/** @todo This isn't at all complete, just sketches... */
/*
* Create an ACL detailing the access of the above groups.
*/
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
if (fProcess)
{
{
}
}
else
{
fDeny |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
{
}
}
/* Deny everyone access to bad bits. */
#if 1
#endif
#if 0
/* Grant some access to the owner - doesn't work. */
#endif
#if 1
# if 1
/* Grant minimal access to the user. */
# endif
# if 1
/* Grant very limited access to the login sid. */
if (fHasLoginSid)
{
}
# endif
#endif
/*
* Create a security descriptor with the above ACL.
*/
PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)suplibHardenedAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
FALSE /*fDaclDefaulted*/));
}
/**
* Predicate function which tests whether @a ch is a argument separator
* character.
*
* @param ch The character to examine.
*/
{
return ch == ' '
|| ch == '\t'
|| ch == '\n'
|| ch == '\r';
}
/**
* GetCommandLineW (see suplibHardenedWindowsMain), we skip the argument
* by argument UTF-8 -> UTF-16 conversion and quoting by going to the
* original source.
*
* The executable name, though, is replaced in case it's not a fullly
* qualified path.
*
* The re-spawn indicator is added immediately after the executable name
* so that we don't get tripped up missing close quote chars in the last
* argument.
*
* @returns Pointer to a command line string (heap).
*/
static PRTUTF16 supR3HardenedWinConstructCmdLine(void)
{
/*
* Get the command line and skip the executable name.
*/
/* Skip leading space (shouldn't be any, but whatever). */
while (suplibCommandLineIsArgSeparator(*pwszArgs))
pwszArgs++;
/* Walk to the end of it. */
int fQuoted = false;
do
{
if (*pwszArgs == '"')
{
pwszArgs++;
}
pwszArgs++;
else
{
unsigned cSlashes = 0;
do
cSlashes++;
while (*++pwszArgs == '\\');
pwszArgs++; /* odd number of slashes == escaped quote */
}
/* Skip trailing spaces. */
while (suplibCommandLineIsArgSeparator(*pwszArgs))
pwszArgs++;
/*
* Allocate a new buffer.
*/
size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_ARG0) - 1) / sizeof(SUPR3_RESPAWN_ARG0[0]) /* Respawn exe name. */
PRTUTF16 pwszCmdLine = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0 /* dwFlags*/, (cwcCmdLine + 1) * sizeof(RTUTF16));
/*
* Construct the new command line.
*/
if (cwcArgs)
{
*pwszDst++ = ' ';
}
*pwszDst = '\0';
return pwszCmdLine;
}
/**
* Does the actually respawning.
*
* @returns Exit code (if we get that far).
*/
static int supR3HardenedWinDoReSpawn(void)
{
/*
* Configure the startup info and creation flags.
*/
DWORD dwCreationFlags = 0;
if (1)
else
{
/** @todo experiment with protected process stuff later on. */
}
/*
* Set up security descriptors.
*/
/*
* Construct the command line and launch the process.
*/
TRUE /*fInheritHandles*/,
NULL /*pwszzEnvironment*/,
NULL /*pwszCurDir*/,
&ProcessInfo))
"Error relaunching VirtualBox VM process: %u\n"
"Command line: '%ls'",
GetLastError(), pwszCmdLine);
/*
* Close the unrestricted access handles. Since we need to wait on the
* child process, we'll reopen the process with limited access before doing
* away with the process handle returned by CreateProcess.
*/
else
if (!DuplicateHandle(GetCurrentProcess(),
FALSE /*fInheritHandle*/,
0))
{
/* This is unacceptable, kill the process. */
supR3HardenedError(dwErr, false /*fFatal*/, "DuplicateHandle failed on child process handle: %u\n", dwErr);
&& dwExit != STILL_ACTIVE;
if (!fExitOk)
{
do
{
&& dwExit != STILL_ACTIVE;
} while ( !fExitOk
if (fExitOk)
"DuplicateHandle failed and we failed to kill child: dwErr=%u dwWait=%u err=%u hProcess=%p\n",
}
"DuplicateHandle failed on child process handle: %u\n", dwErr);
}
/*
* Wait for the process to terminate and proxy the termination code.
*/
for (;;)
{
if ( dwWait == WAIT_OBJECT_0
|| dwWait == WAIT_ABANDONED_0)
break;
if ( dwWait != WAIT_TIMEOUT
&& dwWait != WAIT_IO_COMPLETION)
}
|| dwExit == STILL_ACTIVE)
}
/**
* Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
*
* @returns Program exit code.
*/
DECLHIDDEN(int) supR3HardenedWinReSpawn(void)
{
/*
* Open the stub device.
*/
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
if (!NT_SUCCESS(rcNt))
{
int rc = VERR_OPEN_FAILED;
else
}
/*
* Respawn the process with kernel protection for the new process.
*/
return supR3HardenedWinDoReSpawn();
}
/**
* Checks if re-spawning is required, replacing the respawn argument if not.
*
* @returns true if required, false if not. In the latter case, the first
* argument in the vector is replaced.
* @param cArgs The number of arguments.
* @param papszArgs Pointer to the argument vector.
*/
{
if (cArgs < 1)
return true;
return true;
/* Replace the argument. */
return false;
}
/**
* Initializes the windows verficiation bits.
* @param fFlags The main flags.
*/
{
if (RT_FAILURE(rc))
#ifndef VBOX_WITH_VISTA_NO_SP
/*
* Complain about Vista w/o service pack if we're launching a VM.
*/
if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
"Window Vista without any service pack installed is not supported. Please install the latest service pack.");
#endif
}
/**
* Converts the Windows command line string (UTF-16) to an array of UTF-8
* arguments suitable for passing to main().
*
* @returns Pointer to the argument array.
* @param pwszCmdLine The UTF-16 windows command line to parse.
* @param pcArgs Where to return the number of arguments.
*/
{
/*
* Convert the command line string to UTF-8.
*/
int cbNeeded = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, pwszCmdLine, -1, NULL /*pszDst*/, 0 /*cbDst*/,
SUPR3HARDENED_ASSERT(cbNeeded > 0);
/*
* Parse the command line, carving argument strings out of it.
*/
int cArgs = 0;
int cArgsAllocated = 4;
char *pszSrc = pszCmdLine;
for (;;)
{
/* skip leading blanks. */
while (suplibCommandLineIsArgSeparator(ch))
if (!ch)
break;
/* Add argument to the vector. */
{
cArgsAllocated *= 2;
}
/* Unquote and unescape the string. */
bool fQuoted = false;
do
{
if (ch == '"')
else
{
unsigned cSlashes = 0;
cSlashes++;
if (ch == '"')
{
while (cSlashes >= 2)
{
cSlashes -= 2;
*pszDst++ = '\\';
}
if (cSlashes)
*pszDst++ = '"';
else
}
else
{
pszSrc--;
while (cSlashes-- > 0)
*pszDst++ = '\\';
}
}
/* Terminate the argument. */
*pszDst = '\0';
if (!ch)
break;
}
return papszArgs;
}
/**
* The executable entry point.
*
* This is normally taken care of by the C runtime library, but we don't want to
* get involved with anything as complicated like the CRT in this setup. So, we
* it everything ourselves, including parameter parsing.
*/
{
/*
* Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
*/
/*
* Get the executable name.
*/
"The executable path is too long.");
/* The NT version. */
HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(GetLastError()),
"Error opening the executable: %u (%ls).", GetLastError());
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
/* The NT executable name offset / dir path length. */
while ( g_offSupLibHardenedExeNtName > 1
/*
* Convert the arguments to UTF-8 and call the C/C++ main function.
*/
int cArgs;
/*
* Exit the process (never return).
*/
}