summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp')
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp5719
1 files changed, 5719 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
new file mode 100644
index 00000000..369e68c0
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
@@ -0,0 +1,5719 @@
+/* $Id: SUPDrv-win.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - Windows NT specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef IPRT_NT_MAP_TO_ZW
+# define IPRT_NT_MAP_TO_ZW
+#endif
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "../SUPDrvInternal.h"
+#include <excpt.h>
+#include <ntimage.h>
+
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/ctype.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/power.h>
+#include <iprt/rand.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+
+#include <iprt/asm-amd64-x86.h>
+
+#ifdef VBOX_WITH_HARDENING
+# include "SUPHardenedVerify-win.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxDrv"
+/** The Pool tag (VBox). */
+#define SUPDRV_NT_POOL_TAG 'xoBV'
+
+/** NT device name for user access. */
+#define DEVICE_NAME_NT_USR L"\\Device\\VBoxDrvU"
+#ifdef VBOX_WITH_HARDENING
+/** Macro for checking for deflecting calls to the stub device. */
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) \
+ do { if ((a_pDevObj) == g_pDevObjStub) \
+ return supdrvNtCompleteRequest(STATUS_ACCESS_DENIED, a_pIrp); \
+ } while (0)
+/** Macro for checking for deflecting calls to the stub and error info
+ * devices. */
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(a_pDevObj, a_pIrp) \
+ do { if ((a_pDevObj) == g_pDevObjStub || (a_pDevObj) == g_pDevObjErrorInfo) \
+ return supdrvNtCompleteRequest(STATUS_ACCESS_DENIED, a_pIrp); \
+ } while (0)
+#else
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) do {} while (0)
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(a_pDevObj, a_pIrp) do {} while (0)
+#endif
+
+/** Enables the fast I/O control code path. */
+#define VBOXDRV_WITH_FAST_IO
+
+/** Enables generating UID from NT SIDs so the GMM can share free memory
+ * among VMs running as the same user. */
+#define VBOXDRV_WITH_SID_TO_UID_MAPPING
+
+/* Missing if we're compiling against older WDKs. */
+#ifndef NonPagedPoolNx
+# define NonPagedPoolNx ((POOL_TYPE)512)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+/**
+ * SID to User ID mapping.
+ *
+ * This is used to generate a RTUID value for a NT security identifier.
+ * Normally, the UID is the hash of the SID string, but due to collisions it may
+ * differ. See g_NtUserIdHashTree and g_NtUserIdUidTree.
+ */
+typedef struct SUPDRVNTUSERID
+{
+ /** Hash tree node, key: RTStrHash1 of szSid. */
+ AVLLU32NODECORE HashCore;
+ /** UID three node, key: The UID. */
+ AVLU32NODECORE UidCore;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The length of the SID string. */
+ uint16_t cchSid;
+ /** The SID string for the user. */
+ char szSid[RT_FLEXIBLE_ARRAY];
+} SUPDRVNTUSERID;
+/** Pointer to a SID to UID mapping. */
+typedef SUPDRVNTUSERID *PSUPDRVNTUSERID;
+#endif
+
+/**
+ * Device extension used by VBoxDrvU.
+ */
+typedef struct SUPDRVDEVEXTUSR
+{
+ /** Global cookie (same location as in SUPDRVDEVEXT, different value). */
+ uint32_t u32Cookie;
+ /** Pointer to the main driver extension. */
+ PSUPDRVDEVEXT pMainDrvExt;
+} SUPDRVDEVEXTUSR;
+AssertCompileMembersAtSameOffset(SUPDRVDEVEXT, u32Cookie, SUPDRVDEVEXTUSR, u32Cookie);
+/** Pointer to the VBoxDrvU device extension. */
+typedef SUPDRVDEVEXTUSR *PSUPDRVDEVEXTUSR;
+/** Value of SUPDRVDEVEXTUSR::u32Cookie. */
+#define SUPDRVDEVEXTUSR_COOKIE UINT32_C(0x12345678)
+
+/** Get the main device extension. */
+#define SUPDRVNT_GET_DEVEXT(pDevObj) \
+ ( pDevObj != g_pDevObjUsr \
+ ? (PSUPDRVDEVEXT)pDevObj->DeviceExtension \
+ : ((PSUPDRVDEVEXTUSR)pDevObj->DeviceExtension)->pMainDrvExt )
+
+#ifdef VBOX_WITH_HARDENING
+
+/**
+ * Device extension used by VBoxDrvStub.
+ */
+typedef struct SUPDRVDEVEXTSTUB
+{
+ /** Common header. */
+ SUPDRVDEVEXTUSR Common;
+} SUPDRVDEVEXTSTUB;
+/** Pointer to the VBoxDrvStub device extension. */
+typedef SUPDRVDEVEXTSTUB *PSUPDRVDEVEXTSTUB;
+/** Value of SUPDRVDEVEXTSTUB::Common.u32Cookie. */
+#define SUPDRVDEVEXTSTUB_COOKIE UINT32_C(0x90abcdef)
+
+
+/**
+ * Device extension used by VBoxDrvErrorInfo.
+ */
+typedef struct SUPDRVDEVEXTERRORINFO
+{
+ /** Common header. */
+ SUPDRVDEVEXTUSR Common;
+} SUPDRVDEVEXTERRORINFO;
+/** Pointer to the VBoxDrvErrorInfo device extension. */
+typedef SUPDRVDEVEXTERRORINFO *PSUPDRVDEVEXTERRORINFO;
+/** Value of SUPDRVDEVEXTERRORINFO::Common.u32Cookie. */
+#define SUPDRVDEVEXTERRORINFO_COOKIE UINT32_C(0xBadC0ca0)
+
+/**
+ * Error info for a failed VBoxDrv or VBoxDrvStub open attempt.
+ */
+typedef struct SUPDRVNTERRORINFO
+{
+ /** The list entry (in g_ErrorInfoHead). */
+ RTLISTNODE ListEntry;
+ /** The ID of the process this error info belongs to. */
+ HANDLE hProcessId;
+ /** The ID of the thread owning this info. */
+ HANDLE hThreadId;
+ /** Milliseconds createion timestamp (for cleaning up). */
+ uint64_t uCreatedMsTs;
+ /** Number of bytes of valid info. */
+ uint32_t cchErrorInfo;
+ /** The error info. */
+ char szErrorInfo[16384 - sizeof(RTLISTNODE) - sizeof(HANDLE)*2 - sizeof(uint64_t) - sizeof(uint32_t) - 0x20];
+} SUPDRVNTERRORINFO;
+/** Pointer to error info. */
+typedef SUPDRVNTERRORINFO *PSUPDRVNTERRORINFO;
+
+
+/**
+ * The kind of process we're protecting.
+ */
+typedef enum SUPDRVNTPROTECTKIND
+{
+ kSupDrvNtProtectKind_Invalid = 0,
+
+ /** Stub process protection while performing process verification.
+ * Next: StubSpawning (or free) */
+ kSupDrvNtProtectKind_StubUnverified,
+ /** Stub process protection before it creates the VM process.
+ * Next: StubParent, StubDead. */
+ kSupDrvNtProtectKind_StubSpawning,
+ /** Stub process protection while having a VM process as child.
+ * Next: StubDead */
+ kSupDrvNtProtectKind_StubParent,
+ /** Dead stub process. */
+ kSupDrvNtProtectKind_StubDead,
+
+ /** Potential VM process.
+ * Next: VmProcessConfirmed, VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessUnconfirmed,
+ /** Confirmed VM process.
+ * Next: VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessConfirmed,
+ /** Dead VM process. */
+ kSupDrvNtProtectKind_VmProcessDead,
+
+ /** End of valid protection kinds. */
+ kSupDrvNtProtectKind_End
+} SUPDRVNTPROTECTKIND;
+
+/**
+ * A NT process protection structure.
+ */
+typedef struct SUPDRVNTPROTECT
+{
+ /** The AVL node core structure. The process ID is the pid. */
+ AVLPVNODECORE AvlCore;
+ /** Magic value (SUPDRVNTPROTECT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The kind of process we're protecting. */
+ SUPDRVNTPROTECTKIND volatile enmProcessKind;
+ /** Whether this structure is in the tree. */
+ bool fInTree : 1;
+ /** 7,: Hack to allow the supid themes service duplicate handle privileges to
+ * our process. */
+ bool fThemesFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstThreadCreateHandle : 1;
+ /** 8.1: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fCsrssFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle duplicated by CSRSS
+ * during process creation. Only applicable to VmProcessUnconfirmed. On
+ * 32-bit systems we allow two as ZoneAlarm's system call hooks has been
+ * observed to do some seemingly unnecessary duplication work. */
+ int32_t volatile cCsrssFirstProcessDuplicateHandle;
+
+ /** The parent PID for VM processes, otherwise NULL. */
+ HANDLE hParentPid;
+ /** The TID of the thread opening VBoxDrv or VBoxDrvStub, NULL if not opened. */
+ HANDLE hOpenTid;
+ /** The PID of the CSRSS process associated with this process. */
+ HANDLE hCsrssPid;
+ /** Pointer to the CSRSS process structure (referenced). */
+ PEPROCESS pCsrssProcess;
+ /** State dependent data. */
+ union
+ {
+ /** A stub process in the StubParent state will keep a reference to a child
+ * while it's in the VmProcessUnconfirmed state so that it can be cleaned up
+ * correctly if things doesn't work out. */
+ struct SUPDRVNTPROTECT *pChild;
+ /** A process in the VmProcessUnconfirmed state will keep a weak
+ * reference to the parent's protection structure so it can clean up the pChild
+ * reference the parent has to it. */
+ struct SUPDRVNTPROTECT *pParent;
+ } u;
+} SUPDRVNTPROTECT;
+/** Pointer to a NT process protection record. */
+typedef SUPDRVNTPROTECT *PSUPDRVNTPROTECT;
+/** The SUPDRVNTPROTECT::u32Magic value (Robert A. Heinlein). */
+# define SUPDRVNTPROTECT_MAGIC UINT32_C(0x19070707)
+/** The SUPDRVNTPROTECT::u32Magic value of a dead structure. */
+# define SUPDRVNTPROTECT_MAGIC_DEAD UINT32_C(0x19880508)
+
+/** Pointer to ObGetObjectType. */
+typedef POBJECT_TYPE (NTAPI *PFNOBGETOBJECTTYPE)(PVOID);
+/** Pointer to ObRegisterCallbacks. */
+typedef NTSTATUS (NTAPI *PFNOBREGISTERCALLBACKS)(POB_CALLBACK_REGISTRATION, PVOID *);
+/** Pointer to ObUnregisterCallbacks. */
+typedef VOID (NTAPI *PFNOBUNREGISTERCALLBACKS)(PVOID);
+/** Pointer to PsSetCreateProcessNotifyRoutineEx. */
+typedef NTSTATUS (NTAPI *PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)(PCREATE_PROCESS_NOTIFY_ROUTINE_EX, BOOLEAN);
+/** Pointer to PsReferenceProcessFilePointer. */
+typedef NTSTATUS (NTAPI *PFNPSREFERENCEPROCESSFILEPOINTER)(PEPROCESS, PFILE_OBJECT *);
+/** Pointer to PsIsProtectedProcessLight. */
+typedef BOOLEAN (NTAPI *PFNPSISPROTECTEDPROCESSLIGHT)(PEPROCESS);
+/** Pointer to ZwAlpcCreatePort. */
+typedef NTSTATUS (NTAPI *PFNZWALPCCREATEPORT)(PHANDLE, POBJECT_ATTRIBUTES, struct _ALPC_PORT_ATTRIBUTES *);
+
+#endif /* VBOX_WITH_HARDENINIG */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj);
+static NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtCleanup(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+#ifdef VBOXDRV_WITH_FAST_IO
+static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
+ PVOID pvOutput, ULONG cbOutput, ULONG uCmd,
+ PIO_STATUS_BLOCK pIoStatus, PDEVICE_OBJECT pDevObj);
+#endif
+static NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
+static NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2);
+static NTSTATUS _stdcall VBoxDrvNtRead(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+static NTSTATUS VBoxDrvNtErr2NtStatus(int rc);
+#ifdef VBOX_WITH_HARDENING
+static NTSTATUS supdrvNtProtectInit(void);
+static void supdrvNtProtectTerm(void);
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid,
+ SUPDRVNTPROTECTKIND enmProcessKind, bool fLink);
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect);
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid);
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect);
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect);
+
+static bool supdrvNtIsDebuggerAttached(void);
+static void supdrvNtErrorInfoCleanupProcess(HANDLE hProcessId);
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Exported Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The non-paged pool type to use, NonPagedPool or NonPagedPoolNx. */
+static POOL_TYPE g_enmNonPagedPoolType = NonPagedPool;
+/** Pointer to the system device instance. */
+static PDEVICE_OBJECT g_pDevObjSys = NULL;
+/** Pointer to the user device instance. */
+static PDEVICE_OBJECT g_pDevObjUsr = NULL;
+#ifdef VBOXDRV_WITH_FAST_IO
+/** Fast I/O dispatch table. */
+static FAST_IO_DISPATCH const g_VBoxDrvFastIoDispatch =
+{
+ /* .SizeOfFastIoDispatch = */ sizeof(g_VBoxDrvFastIoDispatch),
+ /* .FastIoCheckIfPossible = */ NULL,
+ /* .FastIoRead = */ NULL,
+ /* .FastIoWrite = */ NULL,
+ /* .FastIoQueryBasicInfo = */ NULL,
+ /* .FastIoQueryStandardInfo = */ NULL,
+ /* .FastIoLock = */ NULL,
+ /* .FastIoUnlockSingle = */ NULL,
+ /* .FastIoUnlockAll = */ NULL,
+ /* .FastIoUnlockAllByKey = */ NULL,
+ /* .FastIoDeviceControl = */ VBoxDrvNtFastIoDeviceControl,
+ /* .AcquireFileForNtCreateSection = */ NULL,
+ /* .ReleaseFileForNtCreateSection = */ NULL,
+ /* .FastIoDetachDevice = */ NULL,
+ /* .FastIoQueryNetworkOpenInfo = */ NULL,
+ /* .AcquireForModWrite = */ NULL,
+ /* .MdlRead = */ NULL,
+ /* .MdlReadComplete = */ NULL,
+ /* .PrepareMdlWrite = */ NULL,
+ /* .MdlWriteComplete = */ NULL,
+ /* .FastIoReadCompressed = */ NULL,
+ /* .FastIoWriteCompressed = */ NULL,
+ /* .MdlReadCompleteCompressed = */ NULL,
+ /* .MdlWriteCompleteCompressed = */ NULL,
+ /* .FastIoQueryOpen = */ NULL,
+ /* .ReleaseForModWrite = */ NULL,
+ /* .AcquireForCcFlush = */ NULL,
+ /* .ReleaseForCcFlush = */ NULL,
+};
+#endif /* VBOXDRV_WITH_FAST_IO */
+
+/** Default ZERO value. */
+static ULONG g_fOptDefaultZero = 0;
+/** Registry values.
+ * We wrap these in a struct to ensure they are followed by a little zero
+ * padding in order to limit the chance of trouble on unpatched systems. */
+struct
+{
+ /** The ForceAsync registry value. */
+ ULONG fOptForceAsyncTsc;
+ /** Padding. */
+ uint64_t au64Padding[2];
+} g_Options = { FALSE, 0, 0 };
+/** Registry query table for RtlQueryRegistryValues. */
+static RTL_QUERY_REGISTRY_TABLE g_aRegValues[] =
+{
+ {
+ /* .QueryRoutine = */ NULL,
+ /* .Flags = */ RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK,
+ /* .Name = */ L"ForceAsyncTsc",
+ /* .EntryContext = */ &g_Options.fOptForceAsyncTsc,
+ /* .DefaultType = */ (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD,
+ /* .DefaultData = */ &g_fOptDefaultZero,
+ /* .DefaultLength = */ sizeof(g_fOptDefaultZero),
+ },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 } /* terminator entry. */
+};
+
+/** Pointer to KeQueryMaximumGroupCount. */
+static PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnKeQueryMaximumGroupCount = NULL;
+/** Pointer to KeGetProcessorIndexFromNumber. */
+static PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnKeGetProcessorIndexFromNumber = NULL;
+/** Pointer to KeGetProcessorNumberFromIndex. */
+static PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnKeGetProcessorNumberFromIndex = NULL;
+
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+/** Spinlock protecting g_NtUserIdHashTree and g_NtUserIdUidTree. */
+static RTSPINLOCK g_hNtUserIdLock = NIL_RTSPINLOCK;
+/** AVL tree of SUPDRVNTUSERID structures by hash value. */
+static PAVLLU32NODECORE g_NtUserIdHashTree = NULL;
+/** AVL tree of SUPDRVNTUSERID structures by UID. */
+static PAVLU32NODECORE g_NtUserIdUidTree = NULL;
+#endif
+
+#ifdef VBOX_WITH_HARDENING
+/** Pointer to the stub device instance. */
+static PDEVICE_OBJECT g_pDevObjStub = NULL;
+/** Spinlock protecting g_NtProtectTree as well as the releasing of protection
+ * structures. */
+static RTSPINLOCK g_hNtProtectLock = NIL_RTSPINLOCK;
+/** AVL tree of SUPDRVNTPROTECT structures. */
+static AVLPVTREE g_NtProtectTree = NULL;
+/** Cookie returned by ObRegisterCallbacks for the callbacks. */
+static PVOID g_pvObCallbacksCookie = NULL;
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
+uint32_t g_uNtVerCombined = 0;
+/** Pointer to ObGetObjectType if available.. */
+static PFNOBGETOBJECTTYPE g_pfnObGetObjectType = NULL;
+/** Pointer to ObRegisterCallbacks if available.. */
+static PFNOBREGISTERCALLBACKS g_pfnObRegisterCallbacks = NULL;
+/** Pointer to ObUnregisterCallbacks if available.. */
+static PFNOBUNREGISTERCALLBACKS g_pfnObUnRegisterCallbacks = NULL;
+/** Pointer to PsSetCreateProcessNotifyRoutineEx if available.. */
+static PFNPSSETCREATEPROCESSNOTIFYROUTINEEX g_pfnPsSetCreateProcessNotifyRoutineEx = NULL;
+/** Pointer to PsReferenceProcessFilePointer if available. */
+static PFNPSREFERENCEPROCESSFILEPOINTER g_pfnPsReferenceProcessFilePointer = NULL;
+/** Pointer to PsIsProtectedProcessLight. */
+static PFNPSISPROTECTEDPROCESSLIGHT g_pfnPsIsProtectedProcessLight = NULL;
+/** Pointer to ZwAlpcCreatePort. */
+static PFNZWALPCCREATEPORT g_pfnZwAlpcCreatePort = NULL;
+
+# ifdef RT_ARCH_AMD64
+extern "C" {
+/** Pointer to KiServiceLinkage (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64). */
+PFNRT g_pfnKiServiceLinkage = NULL;
+/** Pointer to KiServiceInternal (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64) */
+PFNRT g_pfnKiServiceInternal = NULL;
+}
+# endif
+/** The primary ALPC port object type. (LpcPortObjectType at init time.) */
+static POBJECT_TYPE g_pAlpcPortObjectType1 = NULL;
+/** The secondary ALPC port object type. (Sampled at runtime.) */
+static POBJECT_TYPE volatile g_pAlpcPortObjectType2 = NULL;
+
+/** Pointer to the error information device instance. */
+static PDEVICE_OBJECT g_pDevObjErrorInfo = NULL;
+/** Fast mutex semaphore protecting the error info list. */
+static RTSEMMUTEX g_hErrorInfoLock = NIL_RTSEMMUTEX;
+/** Head of the error info (SUPDRVNTERRORINFO). */
+static RTLISTANCHOR g_ErrorInfoHead;
+
+#endif
+
+
+/**
+ * Takes care of creating the devices and their symbolic links.
+ *
+ * @returns NT status code.
+ * @param pDrvObj Pointer to driver object.
+ */
+static NTSTATUS vboxdrvNtCreateDevices(PDRIVER_OBJECT pDrvObj)
+{
+ /*
+ * System device.
+ */
+ UNICODE_STRING DevName;
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_SYS);
+ NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjSys);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * User device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_USR);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTUSR), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjUsr);
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
+ pDevExtUsr->pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtUsr->u32Cookie = SUPDRVDEVEXTUSR_COOKIE;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Hardened stub device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_STUB);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTSTUB), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjStub);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtStub->Common.u32Cookie = SUPDRVDEVEXTSTUB_COOKIE;
+
+ /*
+ * Hardened error information device.
+ */
+ RtlInitUnicodeString(&DevName, SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTERRORINFO), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE,
+ &g_pDevObjErrorInfo);
+ if (NT_SUCCESS(rcNt))
+ {
+ g_pDevObjErrorInfo->Flags |= DO_BUFFERED_IO;
+
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTERRORINFO pDevExtErrInf = (PSUPDRVDEVEXTERRORINFO)g_pDevObjStub->DeviceExtension;
+ pDevExtErrInf->Common.pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtErrInf->Common.u32Cookie = SUPDRVDEVEXTERRORINFO_COOKIE;
+
+#endif
+ /* Done. */
+ return rcNt;
+#ifdef VBOX_WITH_HARDENING
+ }
+
+ /* Bail out. */
+ IoDeleteDevice(g_pDevObjErrorInfo);
+ g_pDevObjErrorInfo = NULL;
+ }
+ }
+
+ /* Bail out. */
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjUsr = NULL;
+ }
+ IoDeleteDevice(g_pDevObjUsr);
+ g_pDevObjUsr = NULL;
+#endif
+ }
+ IoDeleteDevice(g_pDevObjSys);
+ g_pDevObjSys = NULL;
+ }
+ return rcNt;
+}
+
+/**
+ * Destroys the devices and links created by vboxdrvNtCreateDevices.
+ */
+static void vboxdrvNtDestroyDevices(void)
+{
+ if (g_pDevObjUsr)
+ {
+ PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
+ pDevExtUsr->pMainDrvExt = NULL;
+ }
+#ifdef VBOX_WITH_HARDENING
+ if (g_pDevObjStub)
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = NULL;
+ }
+ if (g_pDevObjErrorInfo)
+ {
+ PSUPDRVDEVEXTERRORINFO pDevExtErrorInfo = (PSUPDRVDEVEXTERRORINFO)g_pDevObjStub->DeviceExtension;
+ pDevExtErrorInfo->Common.pMainDrvExt = NULL;
+ }
+#endif
+
+#ifdef VBOX_WITH_HARDENING
+ IoDeleteDevice(g_pDevObjErrorInfo);
+ g_pDevObjErrorInfo = NULL;
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjStub = NULL;
+#endif
+ IoDeleteDevice(g_pDevObjUsr);
+ g_pDevObjUsr = NULL;
+ IoDeleteDevice(g_pDevObjSys);
+ g_pDevObjSys = NULL;
+}
+
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+
+ /*
+ * Sanity checks.
+ */
+#ifdef VBOXDRV_WITH_FAST_IO
+ if (g_VBoxDrvFastIoDispatch.FastIoDeviceControl != VBoxDrvNtFastIoDeviceControl)
+ {
+ DbgPrint("VBoxDrv: FastIoDeviceControl=%p instead of %p\n",
+ g_VBoxDrvFastIoDispatch.FastIoDeviceControl, VBoxDrvNtFastIoDeviceControl);
+ return STATUS_INTERNAL_ERROR;
+ }
+#endif
+
+ /*
+ * Figure out if we can use NonPagedPoolNx or not.
+ */
+ ULONG ulMajorVersion, ulMinorVersion, ulBuildNumber;
+ PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL);
+ if (ulMajorVersion > 6 || (ulMajorVersion == 6 && ulMinorVersion >= 2)) /* >= 6.2 (W8)*/
+ g_enmNonPagedPoolType = NonPagedPoolNx;
+
+ /*
+ * Query options first so any overflows on unpatched machines will do less
+ * harm (see MS11-011 / 2393802 / 2011-03-18).
+ *
+ * Unfortunately, pRegPath isn't documented as zero terminated, even if it
+ * quite likely always is, so we have to make a copy here.
+ */
+ NTSTATUS rcNt;
+ PWSTR pwszCopy = (PWSTR)ExAllocatePoolWithTag(g_enmNonPagedPoolType, pRegPath->Length + sizeof(WCHAR), 'VBox');
+ if (pwszCopy)
+ {
+ memcpy(pwszCopy, pRegPath->Buffer, pRegPath->Length);
+ pwszCopy[pRegPath->Length / sizeof(WCHAR)] = '\0';
+ rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, pwszCopy,
+ g_aRegValues, NULL /*pvContext*/, NULL /*pvEnv*/);
+ ExFreePoolWithTag(pwszCopy, 'VBox');
+ /* Probably safe to ignore rcNt here. */
+ }
+
+ /*
+ * Resolve methods we want but isn't available everywhere.
+ */
+ UNICODE_STRING RoutineName;
+ RtlInitUnicodeString(&RoutineName, L"KeQueryMaximumGroupCount");
+ g_pfnKeQueryMaximumGroupCount = (PFNKEQUERYMAXIMUMGROUPCOUNT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"KeGetProcessorIndexFromNumber");
+ g_pfnKeGetProcessorIndexFromNumber = (PFNKEGETPROCESSORINDEXFROMNUMBER)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"KeGetProcessorNumberFromIndex");
+ g_pfnKeGetProcessorNumberFromIndex = (PFNKEGETPROCESSORNUMBERFROMINDEX)MmGetSystemRoutineAddress(&RoutineName);
+
+ Assert( (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeGetProcessorIndexFromNumber != NULL)
+ && (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeQueryMaximumGroupCount != NULL)); /* all or nothing. */
+
+ /*
+ * Initialize the runtime (IPRT).
+ */
+ int vrc = RTR0Init(0);
+ if (RT_SUCCESS(vrc))
+ {
+ Log(("VBoxDrv::DriverEntry\n"));
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Initialize process protection.
+ */
+ rcNt = supdrvNtProtectInit();
+ if (NT_SUCCESS(rcNt))
+#endif
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ /*
+ * Create the spinlock for the SID -> UID mappings.
+ */
+ vrc = RTSpinlockCreate(&g_hNtUserIdLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "NtUserId");
+ if (RT_SUCCESS(vrc))
+#endif
+ {
+ /*
+ * Create device.
+ * (That means creating a device object and a symbolic link so the DOS
+ * subsystems (OS/2, win32, ++) can access the device.)
+ */
+ rcNt = vboxdrvNtCreateDevices(pDrvObj);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Initialize the device extension.
+ */
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
+
+ vrc = supdrvInitDevExt(pDevExt, sizeof(SUPDRVSESSION));
+ if (!vrc)
+ {
+ /*
+ * Setup the driver entry points in pDrvObj.
+ */
+ pDrvObj->DriverUnload = VBoxDrvNtUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxDrvNtCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLEANUP] = VBoxDrvNtCleanup;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxDrvNtClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxDrvNtDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxDrvNtInternalDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxDrvNtRead;
+ pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxDrvNtNotSupportedStub;
+
+#ifdef VBOXDRV_WITH_FAST_IO
+ /* Fast I/O to speed up guest execution roundtrips. */
+ pDrvObj->FastIoDispatch = (PFAST_IO_DISPATCH)&g_VBoxDrvFastIoDispatch;
+#endif
+
+ /*
+ * Register ourselves for power state changes. We don't
+ * currently care if this fails.
+ */
+ UNICODE_STRING CallbackName;
+ RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
+
+ OBJECT_ATTRIBUTES Attr;
+ InitializeObjectAttributes(&Attr, &CallbackName, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ rcNt = ExCreateCallback(&pDevExt->pObjPowerCallback, &Attr, TRUE, TRUE);
+ if (rcNt == STATUS_SUCCESS)
+ pDevExt->hPowerCallback = ExRegisterCallback(pDevExt->pObjPowerCallback,
+ VBoxPowerDispatchCallback,
+ g_pDevObjSys);
+
+ /*
+ * Done! Returning success!
+ */
+ Log(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n"));
+ return STATUS_SUCCESS;
+ }
+
+ /*
+ * Failed. Clean up.
+ */
+ Log(("supdrvInitDevExit failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+
+ vboxdrvNtDestroyDevices();
+ }
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ RTSpinlockDestroy(g_hNtUserIdLock);
+ g_hNtUserIdLock = NIL_RTSPINLOCK;
+#endif
+ }
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+#endif
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
+ }
+ RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+ RTR0Term();
+ }
+ else
+ {
+ Log(("RTR0Init failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
+ }
+ if (NT_SUCCESS(rcNt))
+ rcNt = STATUS_INVALID_PARAMETER;
+ return rcNt;
+}
+
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj)
+{
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+
+ Log(("VBoxDrvNtUnload at irql %d\n", KeGetCurrentIrql()));
+
+ /* Clean up the power callback registration. */
+ if (pDevExt->hPowerCallback)
+ ExUnregisterCallback(pDevExt->hPowerCallback);
+ if (pDevExt->pObjPowerCallback)
+ ObDereferenceObject(pDevExt->pObjPowerCallback);
+
+ /*
+ * We ASSUME that it's not possible to unload a driver with open handles.
+ */
+ supdrvDeleteDevExt(pDevExt);
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ RTSpinlockDestroy(g_hNtUserIdLock);
+ g_hNtUserIdLock = NIL_RTSPINLOCK;
+#endif
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
+ RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+ RTR0Term();
+ vboxdrvNtDestroyDevices();
+
+ NOREF(pDrvObj);
+}
+
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+
+/**
+ * Worker for supdrvNtUserIdMakeForSession.
+ */
+static bool supdrvNtUserIdMakeUid(PSUPDRVNTUSERID pNtUserId)
+{
+ pNtUserId->UidCore.Key = pNtUserId->HashCore.Key;
+ for (uint32_t cTries = 0; cTries < _4K; cTries++)
+ {
+ bool fRc = RTAvlU32Insert(&g_NtUserIdUidTree, &pNtUserId->UidCore);
+ if (fRc)
+ return true;
+ pNtUserId->UidCore.Key += pNtUserId->cchSid | 1;
+ }
+ return false;
+}
+
+
+/**
+ * Try create a RTUID value for the session.
+ *
+ * @returns VBox status code.
+ * @param pSession The session to try set SUPDRVSESSION::Uid for.
+ */
+static int supdrvNtUserIdMakeForSession(PSUPDRVSESSION pSession)
+{
+ /*
+ * Get the current security context and query the User SID for it.
+ */
+ SECURITY_SUBJECT_CONTEXT Ctx = { NULL, SecurityIdentification, NULL, NULL };
+ SeCaptureSubjectContext(&Ctx);
+
+ int rc;
+ TOKEN_USER *pTokenUser = NULL;
+ NTSTATUS rcNt = SeQueryInformationToken(SeQuerySubjectContextToken(&Ctx) /* or always PrimaryToken?*/,
+ TokenUser, (PVOID *)&pTokenUser);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Convert the user SID to a string to make it easier to handle, then prepare
+ * a user ID entry for it as that way we can combine lookup and insertion and
+ * avoid needing to deal with races.
+ */
+ UNICODE_STRING UniStr = RTNT_NULL_UNISTR();
+ rcNt = RtlConvertSidToUnicodeString(&UniStr, pTokenUser->User.Sid, TRUE /*AllocateDesitnationString*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ size_t cchSid = 0;
+ rc = RTUtf16CalcUtf8LenEx(UniStr.Buffer, UniStr.Length / sizeof(RTUTF16), &cchSid);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTUSERID const pNtUserIdNew = (PSUPDRVNTUSERID)RTMemAlloc(RT_UOFFSETOF_DYN(SUPDRVNTUSERID, szSid[cchSid + 1]));
+ if (pNtUserIdNew)
+ {
+ char *pszSid = pNtUserIdNew->szSid;
+ rc = RTUtf16ToUtf8Ex(UniStr.Buffer, UniStr.Length / sizeof(RTUTF16), &pszSid, cchSid + 1, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pNtUserIdNew->HashCore.Key = RTStrHash1(pNtUserIdNew->szSid);
+ pNtUserIdNew->cchSid = (uint16_t)cchSid;
+ pNtUserIdNew->cRefs = 1;
+ Log5Func(("pNtUserId=%p cchSid=%u hash=%#x '%s'\n", pNtUserIdNew, cchSid, pNtUserIdNew->HashCore.Key, pszSid));
+
+ /*
+ * Do the lookup / insert.
+ */
+ RTSpinlockAcquire(g_hNtUserIdLock);
+ AssertCompileMemberOffset(SUPDRVNTUSERID, HashCore, 0);
+ PSUPDRVNTUSERID pNtUserId = (PSUPDRVNTUSERID)RTAvllU32Get(&g_NtUserIdHashTree, pNtUserIdNew->HashCore.Key);
+ if (pNtUserId)
+ {
+ /* Match the strings till we reach the end of the collision list. */
+ PSUPDRVNTUSERID const pNtUserIdHead = pNtUserId;
+ while ( pNtUserId
+ && ( pNtUserId->cchSid != cchSid
+ || memcmp(pNtUserId->szSid, pNtUserId->szSid, cchSid) != 0))
+ pNtUserId = (PSUPDRVNTUSERID)pNtUserId->HashCore.pList;
+ if (pNtUserId)
+ {
+ /* Found matching: Retain reference and free the new entry we prepared. */
+ uint32_t const cRefs = ASMAtomicIncU32(&pNtUserId->cRefs);
+ Assert(cRefs < _16K); RT_NOREF(cRefs);
+ RTSpinlockRelease(g_hNtUserIdLock);
+ Log5Func(("Using %p / %#x instead\n", pNtUserId, pNtUserId->UidCore.Key));
+ }
+ else
+ {
+ /* No match: Try insert prepared entry after the head node. */
+ if (supdrvNtUserIdMakeUid(pNtUserIdNew))
+ {
+ pNtUserIdNew->HashCore.pList = pNtUserIdHead->HashCore.pList;
+ pNtUserIdHead->HashCore.pList = &pNtUserIdNew->HashCore;
+ pNtUserId = pNtUserIdNew;
+ }
+ RTSpinlockRelease(g_hNtUserIdLock);
+ if (pNtUserId)
+ Log5Func(("Using %p / %#x (the prepared one)\n", pNtUserId, pNtUserId->UidCore.Key));
+ else
+ LogRelFunc(("supdrvNtUserIdMakeForSession: failed to insert new\n"));
+ }
+ }
+ else
+ {
+ /* No matching hash: Try insert the prepared entry. */
+ pNtUserIdNew->UidCore.Key = pNtUserIdNew->HashCore.Key;
+ if (supdrvNtUserIdMakeUid(pNtUserIdNew))
+ {
+ RTAvllU32Insert(&g_NtUserIdHashTree, &pNtUserIdNew->HashCore);
+ pNtUserId = pNtUserIdNew;
+ }
+ RTSpinlockRelease(g_hNtUserIdLock);
+ if (pNtUserId)
+ Log5Func(("Using %p / %#x (the prepared one, no conflict)\n", pNtUserId, pNtUserId->UidCore.Key));
+ else
+ LogRelFunc(("failed to insert!! WTF!?!\n"));
+ }
+
+ if (pNtUserId != pNtUserIdNew)
+ RTMemFree(pNtUserIdNew);
+
+ /*
+ * Update the session info.
+ */
+ pSession->pNtUserId = pNtUserId;
+ pSession->Uid = pNtUserId ? (RTUID)pNtUserId->UidCore.Key : NIL_RTUID;
+ }
+ else
+ RTMemFree(pNtUserIdNew);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ RtlFreeUnicodeString(&UniStr);
+ }
+ else
+ {
+ rc = RTErrConvertFromNtStatus(rcNt);
+ LogFunc(("RtlConvertSidToUnicodeString failed: %#x / %Rrc\n", rcNt, rc));
+ }
+ ExFreePool(pTokenUser);
+ }
+ else
+ {
+ rc = RTErrConvertFromNtStatus(rcNt);
+ LogFunc(("SeQueryInformationToken failed: %#x / %Rrc\n", rcNt, rc));
+ }
+
+ SeReleaseSubjectContext(&Ctx);
+ return rc;
+}
+
+
+/**
+ * Releases a reference to @a pNtUserId.
+ *
+ * @param pNtUserId The NT user ID entry to release.
+ */
+static void supdrvNtUserIdRelease(PSUPDRVNTUSERID pNtUserId)
+{
+ if (pNtUserId)
+ {
+ uint32_t const cRefs = ASMAtomicDecU32(&pNtUserId->cRefs);
+ Log5Func(("%p / %#x: cRefs=%d\n", pNtUserId, pNtUserId->cRefs));
+ Assert(cRefs < _8K);
+ if (cRefs == 0)
+ {
+ RTSpinlockAcquire(g_hNtUserIdLock);
+ if (pNtUserId->cRefs == 0)
+ {
+ PAVLLU32NODECORE pAssert1 = RTAvllU32RemoveNode(&g_NtUserIdHashTree, &pNtUserId->HashCore);
+ PAVLU32NODECORE pAssert2 = RTAvlU32Remove(&g_NtUserIdUidTree, pNtUserId->UidCore.Key);
+
+ RTSpinlockRelease(g_hNtUserIdLock);
+
+ Assert(pAssert1 == &pNtUserId->HashCore);
+ Assert(pAssert2 == &pNtUserId->UidCore);
+ RT_NOREF(pAssert1, pAssert2);
+
+ RTMemFree(pNtUserId);
+ }
+ else
+ RTSpinlockRelease(g_hNtUserIdLock);
+ }
+ }
+}
+
+#endif /* VBOXDRV_WITH_SID_TO_UID_MAPPING */
+
+/**
+ * For simplifying request completion into a simple return statement, extended
+ * version.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param uInfo Extra info value.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) supdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
+{
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = uInfo;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * For simplifying request completion into a simple return statement.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) supdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
+{
+ return supdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
+}
+
+
+/**
+ * Create (i.e. Open) file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+
+ /*
+ * We are not remotely similar to a directory...
+ * (But this is possible.)
+ */
+ if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+ return supdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
+
+ /*
+ * Don't create a session for kernel clients, they'll close the handle
+ * immediately and work with the file object via
+ * VBoxDrvNtInternalDeviceControl. The first request will be one to
+ * create a session.
+ */
+ NTSTATUS rcNt;
+ if (pIrp->RequestorMode == KernelMode)
+ {
+ if (pDevObj == g_pDevObjSys)
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ rcNt = STATUS_ACCESS_DENIED;
+ }
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Anyone can open the error device.
+ */
+ else if (pDevObj == g_pDevObjErrorInfo)
+ {
+ pFileObj->FsContext = NULL;
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+#endif
+ else
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ /*
+ * Make sure no debuggers are attached to non-user processes.
+ */
+ if ( pDevObj != g_pDevObjUsr
+ && supdrvNtIsDebuggerAttached())
+ {
+ LogRel(("vboxdrv: Process %p is being debugged, access to vboxdrv / vboxdrvu declined.\n",
+ PsGetProcessId(PsGetCurrentProcess())));
+ rcNt = STATUS_TRUST_FAILURE;
+ }
+ else
+#endif
+ {
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Access to the stub device is only granted to processes which
+ * passes verification.
+ *
+ * Note! The stub device has no need for a SUPDRVSESSION structure,
+ * so the it uses the SUPDRVNTPROTECT directly instead.
+ */
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = NULL;
+ rc = supdrvNtProtectCreate(&pNtProtect, PsGetProcessId(PsGetCurrentProcess()),
+ kSupDrvNtProtectKind_StubUnverified, true /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvNtProtectFindAssociatedCsrss(pNtProtect);
+ if (RT_SUCCESS(rc))
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ LogRel(("vboxdrv: Declined %p access to VBoxDrvStub: rc=%d\n", PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ /*
+ * Unrestricted access is only granted to a process in the
+ * VmProcessUnconfirmed state that checks out correctly and is
+ * allowed to transition to VmProcessConfirmed. Again, only one
+ * session per process.
+ */
+ else if (pDevObj != g_pDevObjUsr)
+ {
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(PsGetCurrentProcess()));
+ if (pNtProtect)
+ {
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a session. */
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/,
+ &pSession);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+#endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pSession->pNtProtect = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+
+ /* No second attempt. */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ LogRel(("vboxdrv: supdrvCreateSession failed for process %p: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ LogRel(("vboxdrv: Process %p failed process verification: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process (enmProcessKind=%d).\n",
+ PsGetProcessId(PsGetCurrentProcess()), pNtProtect->enmProcessKind));
+ rc = VERR_SUPDRV_NOT_BUDDING_VM_PROCESS_2;
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process.\n", PsGetProcessId(PsGetCurrentProcess())));
+ rc = VERR_SUPDRV_NOT_BUDDING_VM_PROCESS_1;
+ }
+ }
+ /*
+ * Call common code to create an unprivileged session.
+ */
+ else
+ {
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, false /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+#endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pSession; /* Keeps reference. No race. */
+ pSession->pNtProtect = NULL;
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+ }
+
+#else /* !VBOX_WITH_HARDENING */
+ /*
+ * Call common code to create a session.
+ */
+ pFileObj->FsContext = NULL;
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+# ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ rc = supdrvNtUserIdMakeForSession(pSession);
+ if (RT_SUCCESS(rc))
+# endif
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ }
+#endif /* !VBOX_WITH_HARDENING */
+
+ /* bail out */
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+ }
+ }
+
+ Assert(!NT_SUCCESS(rcNt));
+ pFileObj->FsContext = NULL;
+ return supdrvNtCompleteRequest(rcNt, pIrp); /* Note. the IoStatus is completely ignored on error. */
+}
+
+
+/**
+ * Clean up file handle entry point.
+ *
+ * This is called when the last handle reference is released, or something like
+ * that. In the case of IoGetDeviceObjectPointer, this is called as it closes
+ * the handle, however it will go on using the file object afterwards...
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtCleanup(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else if (pDevObj == g_pDevObjErrorInfo)
+ supdrvNtErrorInfoCleanupProcess(PsGetCurrentProcessId());
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
+}
+
+
+/**
+ * Close file entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtClose: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else if (pDevObj == g_pDevObjErrorInfo)
+ supdrvNtErrorInfoCleanupProcess(PsGetCurrentProcessId());
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
+}
+
+
+#ifdef VBOXDRV_WITH_FAST_IO
+/**
+ * Fast I/O device control callback.
+ *
+ * This performs no buffering, neither on the way in or out.
+ *
+ * @returns TRUE if handled, FALSE if the normal I/O control routine should be
+ * called.
+ * @param pFileObj The file object.
+ * @param fWait Whether it's a blocking call
+ * @param pvInput The input buffer as specified by the user.
+ * @param cbInput The size of the input buffer.
+ * @param pvOutput The output buffer as specfied by the user.
+ * @param cbOutput The size of the output buffer.
+ * @param uCmd The I/O command/function being invoked.
+ * @param pIoStatus Where to return the status of the operation.
+ * @param pDevObj The device object..
+ */
+static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
+ PVOID pvOutput, ULONG cbOutput, ULONG uCmd,
+ PIO_STATUS_BLOCK pIoStatus, PDEVICE_OBJECT pDevObj)
+{
+ RT_NOREF1(fWait);
+
+ /*
+ * Only the normal devices, not the stub or error info ones.
+ */
+ if (pDevObj != g_pDevObjSys && pDevObj != g_pDevObjUsr)
+ {
+ pIoStatus->Status = STATUS_NOT_SUPPORTED;
+ pIoStatus->Information = 0;
+ return TRUE;
+ }
+
+ /*
+ * Check the input a little bit and get a the session references.
+ */
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ if (!pSession)
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ return TRUE;
+ }
+
+ if (pSession->fUnrestricted)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+#endif
+
+ /*
+ * Deal with the 2-3 high-speed IOCtl that takes their arguments from
+ * the session and iCmd, and does not return anything.
+ */
+ if ( (uCmd & 3) == METHOD_NEITHER
+ && (uint32_t)((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2) < (uint32_t)32)
+ {
+ int rc = supdrvIOCtlFast((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2,
+ (unsigned)(uintptr_t)pvOutput/* VMCPU id */,
+ pDevExt, pSession);
+ pIoStatus->Status = RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
+ pIoStatus->Information = 0; /* Could be used to pass rc if we liked. */
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+ }
+
+ /*
+ * The normal path.
+ */
+ NTSTATUS rcNt;
+ unsigned cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtFastIoDeviceControl(%p): ioctl=%#x pvIn=%p cbIn=%#x pvOut=%p cbOut=%#x pSession=%p\n",
+ pDevExt, uCmd, pvInput, cbInput, pvOutput, cbOutput, pSession));
+
+# ifdef RT_ARCH_AMD64
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(NULL))
+# endif
+ {
+ /*
+ * In this fast I/O device control path we have to do our own buffering.
+ */
+ /* Verify that the I/O control function matches our pattern. */
+ if ((uCmd & 0x3) == METHOD_BUFFERED)
+ {
+ /* Get the header so we can validate it a little bit against the
+ parameters before allocating any memory kernel for the reqest. */
+ SUPREQHDR Hdr;
+ if (cbInput >= sizeof(Hdr) && cbOutput >= sizeof(Hdr))
+ {
+ __try
+ {
+ RtlCopyMemory(&Hdr, pvInput, sizeof(Hdr));
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ Hdr.cbIn = Hdr.cbOut = 0; /* shut up MSC */
+ }
+ }
+ else
+ {
+ Hdr.cbIn = Hdr.cbOut = 0; /* shut up MSC */
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Verify that the sizes in the request header are correct. */
+ ULONG cbBuf = RT_MAX(cbInput, cbOutput);
+ if ( cbInput == Hdr.cbIn
+ && cbOutput == Hdr.cbOut
+ && cbBuf < _1M*16)
+ {
+ /* Allocate a buffer and copy all the input into it. */
+ PSUPREQHDR pHdr = (PSUPREQHDR)ExAllocatePoolWithTag(g_enmNonPagedPoolType, cbBuf, 'VBox');
+ if (pHdr)
+ {
+ __try
+ {
+ RtlCopyMemory(pHdr, pvInput, cbInput);
+ if (cbInput < cbBuf)
+ RtlZeroMemory((uint8_t *)pHdr + cbInput, cbBuf - cbInput);
+ if (!memcmp(pHdr, &Hdr, sizeof(Hdr)))
+ rcNt = STATUS_SUCCESS;
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Now call the common code to do the real work.
+ */
+ rc = supdrvIOCtl(uCmd, pDevExt, pSession, pHdr, cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy back the result.
+ */
+ cbOut = pHdr->cbOut;
+ if (cbOut > cbOutput)
+ {
+ cbOut = cbOutput;
+ OSDBGPRINT(("VBoxDrvNtFastIoDeviceControl: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, uCmd));
+ }
+ if (cbOut)
+ {
+ __try
+ {
+ RtlCopyMemory(pvOutput, pHdr, cbOut);
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ }
+ else
+ rcNt = STATUS_SUCCESS;
+ }
+ else if (rc == VERR_INVALID_PARAMETER)
+ rcNt = STATUS_INVALID_PARAMETER;
+ else
+ rcNt = STATUS_NOT_SUPPORTED;
+ Log2(("VBoxDrvNtFastIoDeviceControl: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ else
+ Log(("VBoxDrvNtFastIoDeviceControl: Error reading %u bytes of user memory at %p (uCmd=%#x)\n",
+ cbInput, pvInput, uCmd));
+ ExFreePoolWithTag(pHdr, 'VBox');
+ }
+ else
+ rcNt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ uCmd, Hdr.cbIn, Hdr.cbOut, cbInput, cbOutput));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: not buffered request (%#x) - not supported\n", uCmd));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+# ifdef RT_ARCH_AMD64
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+# endif
+
+ /* complete the request. */
+ pIoStatus->Status = rcNt;
+ pIoStatus->Information = cbOut;
+ supdrvSessionRelease(pSession);
+ return TRUE; /* handled. */
+}
+#endif /* VBOXDRV_WITH_FAST_IO */
+
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(pDevObj, pIrp);
+
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pStack->FileObject->FsContext);
+
+ if (!RT_VALID_PTR(pSession))
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+
+ /*
+ * Deal with the 2-3 high-speed IOCtl that takes their arguments from
+ * the session and iCmd, and does not return anything.
+ */
+ if (pSession->fUnrestricted)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+ }
+#endif
+
+ ULONG uCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
+ if ( (uCmd & 3) == METHOD_NEITHER
+ && (uint32_t)((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2) < (uint32_t)32)
+ {
+ int rc = supdrvIOCtlFast((uCmd - SUP_IOCTL_FAST_DO_FIRST) >> 2,
+ (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */,
+ pDevExt, pSession);
+
+ /* Complete the I/O request. */
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
+ }
+ }
+
+ return VBoxDrvNtDeviceControlSlow(pDevExt, pSession, pIrp, pStack);
+}
+
+
+/**
+ * Worker for VBoxDrvNtDeviceControl that takes the slow IOCtl functions.
+ *
+ * @returns NT status code.
+ *
+ * @param pDevExt Device extension.
+ * @param pSession The session.
+ * @param pIrp Request packet.
+ * @param pStack The stack location containing the DeviceControl parameters.
+ */
+static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack)
+{
+ NTSTATUS rcNt;
+ uint32_t cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
+ pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
+
+#ifdef RT_ARCH_AMD64
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(pIrp))
+#endif
+ {
+ /* Verify that it's a buffered CTL. */
+ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
+ {
+ /* Verify that the sizes in the request header are correct. */
+ PSUPREQHDR pHdr = (PSUPREQHDR)pIrp->AssociatedIrp.SystemBuffer;
+ if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
+ && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
+ && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
+ {
+ /* Zero extra output bytes to make sure we don't leak anything. */
+ if (pHdr->cbIn < pHdr->cbOut)
+ RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
+
+ /*
+ * Do the job.
+ */
+ rc = supdrvIOCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
+ RT_MAX(pHdr->cbIn, pHdr->cbOut));
+ if (!rc)
+ {
+ rcNt = STATUS_SUCCESS;
+ cbOut = pHdr->cbOut;
+ if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
+ {
+ cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
+ OSDBGPRINT(("VBoxDrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
+ }
+ }
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ Log2(("VBoxDrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+#ifdef RT_ARCH_AMD64
+ else
+ {
+ Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+#endif
+
+ /* complete the request. */
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = cbOut;
+ supdrvSessionRelease(pSession);
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Internal Device I/O Control entry point, used for IDC.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_OR_ERROR_INFO_DEV(pDevObj, pIrp);
+
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack ? pStack->FileObject : NULL;
+ PSUPDRVSESSION pSession = pFileObj ? (PSUPDRVSESSION)pFileObj->FsContext : NULL;
+ NTSTATUS rcNt;
+ unsigned cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtInternalDeviceControl(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
+ pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
+
+ /* Verify that it's a buffered CTL. */
+ if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
+ {
+ /* Verify the pDevExt in the session. */
+ if ( pStack->Parameters.DeviceIoControl.IoControlCode != SUPDRV_IDC_REQ_CONNECT
+ ? RT_VALID_PTR(pSession) && pSession->pDevExt == pDevExt
+ : !pSession
+ )
+ {
+ /* Verify that the size in the request header is correct. */
+ PSUPDRVIDCREQHDR pHdr = (PSUPDRVIDCREQHDR)pIrp->AssociatedIrp.SystemBuffer;
+ if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
+ && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cb
+ && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cb)
+ {
+ /*
+ * Call the generic code.
+ *
+ * Note! Connect and disconnect requires some extra attention
+ * in order to get the session handling right.
+ */
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_DISCONNECT)
+ pFileObj->FsContext = NULL;
+
+ rc = supdrvIDC(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr);
+ if (!rc)
+ {
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_CONNECT)
+ pFileObj->FsContext = ((PSUPDRVIDCREQCONNECT)pHdr)->u.Out.pSession;
+
+ rcNt = STATUS_SUCCESS;
+ cbOut = pHdr->cb;
+ }
+ else
+ {
+ rcNt = STATUS_INVALID_PARAMETER;
+ if (pStack->Parameters.DeviceIoControl.IoControlCode == SUPDRV_IDC_REQ_DISCONNECT)
+ pFileObj->FsContext = pSession;
+ }
+ Log2(("VBoxDrvNtInternalDeviceControl: returns %#x/rc=%#x\n", rcNt, rc));
+ }
+ else
+ {
+ Log(("VBoxDrvNtInternalDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx Irp=%#lx/%#lx!\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode,
+ pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cb : 0,
+ pStack->Parameters.DeviceIoControl.InputBufferLength,
+ pStack->Parameters.DeviceIoControl.OutputBufferLength));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ else
+ {
+ Log(("VBoxDrvNtInternalDeviceControl: not buffered request (%#x) - not supported\n",
+ pStack->Parameters.DeviceIoControl.IoControlCode));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+
+ /* complete the request. */
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = cbOut;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Implementation of the read major function for VBoxDrvErrorInfo.
+ *
+ * This is a stub function for the other devices.
+ *
+ * @returns NT status code.
+ * @param pDevObj The device object.
+ * @param pIrp The I/O request packet.
+ */
+NTSTATUS _stdcall VBoxDrvNtRead(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtRead\n"));
+ RT_NOREF1(pDevObj);
+
+ NTSTATUS rcNt;
+ pIrp->IoStatus.Information = 0;
+
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * VBoxDrvErrorInfo?
+ */
+ if (pDevObj == g_pDevObjErrorInfo)
+ {
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ if ( pStack
+ && (pIrp->Flags & IRP_BUFFERED_IO))
+ {
+ /*
+ * Look up the process error information.
+ */
+ HANDLE hCurThreadId = PsGetCurrentThreadId();
+ HANDLE hCurProcessId = PsGetCurrentProcessId();
+ int rc = RTSemMutexRequestNoResume(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTERRORINFO pMatch = NULL;
+ PSUPDRVNTERRORINFO pCur;
+ RTListForEach(&g_ErrorInfoHead, pCur, SUPDRVNTERRORINFO, ListEntry)
+ {
+ if ( pCur->hProcessId == hCurProcessId
+ && pCur->hThreadId == hCurThreadId)
+ {
+ pMatch = pCur;
+ break;
+ }
+ }
+
+ /*
+ * Did we find error info and is the caller requesting data within it?
+ * If so, check the destination buffer and copy the data into it.
+ */
+ if ( pMatch
+ && pStack->Parameters.Read.ByteOffset.QuadPart < pMatch->cchErrorInfo
+ && pStack->Parameters.Read.ByteOffset.QuadPart >= 0)
+ {
+ PVOID pvDstBuf = pIrp->AssociatedIrp.SystemBuffer;
+ if (pvDstBuf)
+ {
+ uint32_t offRead = (uint32_t)pStack->Parameters.Read.ByteOffset.QuadPart;
+ uint32_t cbToRead = pMatch->cchErrorInfo - offRead;
+ if (cbToRead < pStack->Parameters.Read.Length)
+ RT_BZERO((uint8_t *)pvDstBuf + cbToRead, pStack->Parameters.Read.Length - cbToRead);
+ else
+ cbToRead = pStack->Parameters.Read.Length;
+ memcpy(pvDstBuf, &pMatch->szErrorInfo[offRead], cbToRead);
+ pIrp->IoStatus.Information = cbToRead;
+
+ rcNt = STATUS_SUCCESS;
+ }
+ else
+ rcNt = STATUS_INVALID_ADDRESS;
+ }
+ /*
+ * End of file. Free the info.
+ */
+ else if (pMatch)
+ {
+ RTListNodeRemove(&pMatch->ListEntry);
+ RTMemFree(pMatch);
+ rcNt = STATUS_END_OF_FILE;
+ }
+ /*
+ * We found no error info. Return EOF.
+ */
+ else
+ rcNt = STATUS_END_OF_FILE;
+
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+ else
+ rcNt = STATUS_UNSUCCESSFUL;
+
+ /* Paranoia: Clear the buffer on failure. */
+ if (!NT_SUCCESS(rcNt))
+ {
+ PVOID pvDstBuf = pIrp->AssociatedIrp.SystemBuffer;
+ if ( pvDstBuf
+ && pStack->Parameters.Read.Length)
+ RT_BZERO(pvDstBuf, pStack->Parameters.Read.Length);
+ }
+ }
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ else
+#endif /* VBOX_WITH_HARDENING */
+ {
+ /*
+ * Stub.
+ */
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+
+ /*
+ * Complete the request.
+ */
+ pIrp->IoStatus.Status = rcNt;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * Stub function for functions we don't implemented.
+ *
+ * @returns STATUS_NOT_SUPPORTED
+ * @param pDevObj Device object.
+ * @param pIrp IRP.
+ */
+NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ Log(("VBoxDrvNtNotSupportedStub\n"));
+ NOREF(pDevObj);
+
+ pIrp->IoStatus.Information = 0;
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+/**
+ * ExRegisterCallback handler for power events
+ *
+ * @param pCallbackContext User supplied parameter (pDevObj)
+ * @param pvArgument1 First argument
+ * @param pvArgument2 Second argument
+ */
+VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pvArgument1, PVOID pvArgument2)
+{
+ /*PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pCallbackContext;*/ RT_NOREF1(pCallbackContext);
+ Log(("VBoxPowerDispatchCallback: %x %x\n", pvArgument1, pvArgument2));
+
+ /* Power change imminent? */
+ if ((uintptr_t)pvArgument1 == PO_CB_SYSTEM_STATE_LOCK)
+ {
+ if (pvArgument2 == NULL)
+ Log(("VBoxPowerDispatchCallback: about to go into suspend mode!\n"));
+ else
+ Log(("VBoxPowerDispatchCallback: resumed!\n"));
+
+ /* Inform any clients that have registered themselves with IPRT. */
+ RTPowerSignalEvent(pvArgument2 == NULL ? RTPOWEREVENT_SUSPEND : RTPOWEREVENT_RESUME);
+ }
+}
+
+
+/**
+ * Called to clean up the session structure before it's freed.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that's being cleaned up.
+ */
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+#ifdef VBOX_WITH_HARDENING
+ if (pSession->pNtProtect)
+ {
+ supdrvNtProtectRelease(pSession->pNtProtect);
+ pSession->pNtProtect = NULL;
+ }
+ RT_NOREF1(pDevExt);
+#else
+ RT_NOREF2(pDevExt, pSession);
+#endif
+#ifdef VBOXDRV_WITH_SID_TO_UID_MAPPING
+ if (pSession->pNtUserId)
+ {
+ supdrvNtUserIdRelease(pSession->pNtUserId);
+ pSession->pNtUserId = NULL;
+ }
+#endif
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+size_t VBOXCALL supdrvOSGipGetGroupTableSize(PSUPDRVDEVEXT pDevExt)
+{
+ NOREF(pDevExt);
+ uint32_t cMaxCpus = RTMpGetCount();
+ uint32_t cGroups = RTMpGetMaxCpuGroupCount();
+
+ return cGroups * RT_UOFFSETOF(SUPGIPCPUGROUP, aiCpuSetIdxs)
+ + RT_SIZEOFMEMB(SUPGIPCPUGROUP, aiCpuSetIdxs[0]) * cMaxCpus;
+}
+
+
+int VBOXCALL supdrvOSInitGipGroupTable(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, size_t cbGipCpuGroups)
+{
+ Assert(cbGipCpuGroups > 0); NOREF(cbGipCpuGroups); NOREF(pDevExt);
+
+ unsigned const cGroups = RTMpGetMaxCpuGroupCount();
+ AssertReturn(cGroups > 0 && cGroups < RT_ELEMENTS(pGip->aoffCpuGroup), VERR_INTERNAL_ERROR_2);
+ pGip->cPossibleCpuGroups = cGroups;
+
+ PSUPGIPCPUGROUP pGroup = (PSUPGIPCPUGROUP)&pGip->aCPUs[pGip->cCpus];
+ for (uint32_t idxGroup = 0; idxGroup < cGroups; idxGroup++)
+ {
+ uint32_t cActive = 0;
+ uint32_t const cMax = RTMpGetCpuGroupCounts(idxGroup, &cActive);
+ uint32_t const cbNeeded = RT_UOFFSETOF_DYN(SUPGIPCPUGROUP, aiCpuSetIdxs[cMax]);
+ uintptr_t const offGroup = (uintptr_t)pGroup - (uintptr_t)pGip;
+ AssertReturn(cbNeeded <= cbGipCpuGroups, VERR_INTERNAL_ERROR_3);
+ AssertReturn(cActive <= cMax, VERR_INTERNAL_ERROR_4);
+ AssertReturn(offGroup == (uint32_t)offGroup, VERR_INTERNAL_ERROR_5);
+
+ pGip->aoffCpuGroup[idxGroup] = offGroup;
+ pGroup->cMembers = cActive;
+ pGroup->cMaxMembers = cMax;
+ for (uint32_t idxMember = 0; idxMember < cMax; idxMember++)
+ {
+ pGroup->aiCpuSetIdxs[idxMember] = RTMpSetIndexFromCpuGroupMember(idxGroup, idxMember);
+ Assert((unsigned)pGroup->aiCpuSetIdxs[idxMember] < pGip->cPossibleCpus);
+ }
+
+ /* advance. */
+ cbGipCpuGroups -= cbNeeded;
+ pGroup = (PSUPGIPCPUGROUP)&pGroup->aiCpuSetIdxs[cMax];
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+void VBOXCALL supdrvOSGipInitGroupBitsForCpu(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu)
+{
+ NOREF(pDevExt);
+
+ /*
+ * Translate the CPU index into a group and member.
+ */
+ PROCESSOR_NUMBER ProcNum = { 0, (UCHAR)pGipCpu->iCpuSet, 0 };
+ if (g_pfnKeGetProcessorNumberFromIndex)
+ {
+ NTSTATUS rcNt = g_pfnKeGetProcessorNumberFromIndex(pGipCpu->iCpuSet, &ProcNum);
+ if (NT_SUCCESS(rcNt))
+ Assert(ProcNum.Group < g_pfnKeQueryMaximumGroupCount());
+ else
+ {
+ AssertFailed();
+ ProcNum.Group = 0;
+ ProcNum.Number = pGipCpu->iCpuSet;
+ }
+ }
+ pGipCpu->iCpuGroup = ProcNum.Group;
+ pGipCpu->iCpuGroupMember = ProcNum.Number;
+
+ /*
+ * Update the group info. Just do this wholesale for now (doesn't scale well).
+ */
+ for (uint32_t idxGroup = 0; idxGroup < pGip->cPossibleCpuGroups; idxGroup++)
+ {
+ uint32_t offGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offGroup != UINT32_MAX)
+ {
+ PSUPGIPCPUGROUP pGroup = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offGroup);
+ uint32_t cActive = 0;
+ uint32_t cMax = RTMpGetCpuGroupCounts(idxGroup, &cActive);
+
+ AssertStmt(cMax == pGroup->cMaxMembers, cMax = pGroup->cMaxMembers);
+ AssertStmt(cActive <= cMax, cActive = cMax);
+ if (pGroup->cMembers != cActive)
+ ASMAtomicWriteU16(&pGroup->cMembers, cActive);
+
+ for (uint32_t idxMember = 0; idxMember < cMax; idxMember++)
+ {
+ int idxCpuSet = RTMpSetIndexFromCpuGroupMember(idxGroup, idxMember);
+ AssertMsg((unsigned)idxCpuSet < pGip->cPossibleCpus,
+ ("%d vs %d for %u.%u\n", idxCpuSet, pGip->cPossibleCpus, idxGroup, idxMember));
+
+ if (pGroup->aiCpuSetIdxs[idxMember] != idxCpuSet)
+ ASMAtomicWriteS16(&pGroup->aiCpuSetIdxs[idxMember], idxCpuSet);
+ }
+ }
+ }
+}
+
+
+/**
+ * Initializes any OS specific object creator fields.
+ */
+void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+}
+
+
+/**
+ * Checks if the session can access the object.
+ *
+ * @returns true if a decision has been made.
+ * @returns false if the default access policy should be applied.
+ *
+ * @param pObj The object in question.
+ * @param pSession The session wanting to access the object.
+ * @param pszObjName The object name, can be NULL.
+ * @param prc Where to store the result when returning true.
+ */
+bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+ NOREF(pObj);
+ NOREF(pSession);
+ NOREF(pszObjName);
+ NOREF(prc);
+ return false;
+}
+
+
+/**
+ * Force async tsc mode (stub).
+ */
+bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt)
+{
+ RT_NOREF1(pDevExt);
+ return g_Options.fOptForceAsyncTsc != 0;
+}
+
+
+/**
+ * Whether the host takes CPUs offline during a suspend/resume operation.
+ */
+bool VBOXCALL supdrvOSAreCpusOfflinedOnSuspend(void)
+{
+ return false;
+}
+
+
+/**
+ * Whether the hardware TSC has been synchronized by the OS.
+ */
+bool VBOXCALL supdrvOSAreTscDeltasInSync(void)
+{
+ /* If IPRT didn't find KeIpiGenericCall we pretend windows(, the firmware,
+ or whoever) always configures TSCs perfectly. */
+ return !RTMpOnPairIsConcurrentExecSupported();
+}
+
+
+#define MY_SystemLoadGdiDriverInSystemSpaceInformation 54
+#define MY_SystemUnloadGdiDriverInformation 27
+
+typedef struct MYSYSTEMGDIDRIVERINFO
+{
+ UNICODE_STRING Name; /**< In: image file name. */
+ PVOID ImageAddress; /**< Out: the load address. */
+ PVOID SectionPointer; /**< Out: section object. */
+ PVOID EntryPointer; /**< Out: entry point address. */
+ PVOID ExportSectionPointer; /**< Out: export directory/section. */
+ ULONG ImageLength; /**< Out: SizeOfImage. */
+} MYSYSTEMGDIDRIVERINFO;
+
+extern "C" __declspec(dllimport) NTSTATUS NTAPI ZwSetSystemInformation(ULONG, PVOID, ULONG);
+
+int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ pImage->pvNtSectionObj = NULL;
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+
+#ifdef VBOX_WITHOUT_NATIVE_R0_LOADER
+# ifndef RT_ARCH_X86
+# error "VBOX_WITHOUT_NATIVE_R0_LOADER is only safe on x86."
+# endif
+ NOREF(pDevExt); NOREF(pszFilename); NOREF(pImage);
+ return VERR_NOT_SUPPORTED;
+
+#else
+ /*
+ * Convert the filename from DOS UTF-8 to NT UTF-16.
+ */
+ size_t cwcFilename;
+ int rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ PRTUTF16 pwcsFilename = (PRTUTF16)RTMemTmpAlloc((4 + cwcFilename + 1) * sizeof(RTUTF16));
+ if (!pwcsFilename)
+ return VERR_NO_TMP_MEMORY;
+
+ pwcsFilename[0] = '\\';
+ pwcsFilename[1] = '?';
+ pwcsFilename[2] = '?';
+ pwcsFilename[3] = '\\';
+ PRTUTF16 pwcsTmp = &pwcsFilename[4];
+ rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwcsTmp, cwcFilename + 1, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try load it.
+ */
+ MYSYSTEMGDIDRIVERINFO Info;
+ RtlInitUnicodeString(&Info.Name, pwcsFilename);
+ Info.ImageAddress = NULL;
+ Info.SectionPointer = NULL;
+ Info.EntryPointer = NULL;
+ Info.ExportSectionPointer = NULL;
+ Info.ImageLength = 0;
+
+ NTSTATUS rcNt = ZwSetSystemInformation(MY_SystemLoadGdiDriverInSystemSpaceInformation, &Info, sizeof(Info));
+ if (NT_SUCCESS(rcNt))
+ {
+ pImage->pvImage = Info.ImageAddress;
+ pImage->pvNtSectionObj = Info.SectionPointer;
+ Log(("ImageAddress=%p SectionPointer=%p ImageLength=%#x cbImageBits=%#x rcNt=%#x '%ls'\n",
+ Info.ImageAddress, Info.SectionPointer, Info.ImageLength, pImage->cbImageBits, rcNt, Info.Name.Buffer));
+# ifdef DEBUG_bird
+ SUPR0Printf("ImageAddress=%p SectionPointer=%p ImageLength=%#x cbImageBits=%#x rcNt=%#x '%ls'\n",
+ Info.ImageAddress, Info.SectionPointer, Info.ImageLength, pImage->cbImageBits, rcNt, Info.Name.Buffer);
+# endif
+ if (pImage->cbImageBits == Info.ImageLength)
+ {
+ /*
+ * Lock down the entire image, just to be on the safe side.
+ */
+ rc = RTR0MemObjLockKernel(&pImage->hMemLock, pImage->pvImage, pImage->cbImageBits, RTMEM_PROT_READ);
+ if (RT_FAILURE(rc))
+ {
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+ supdrvOSLdrUnload(pDevExt, pImage);
+ }
+ }
+ else
+ {
+ supdrvOSLdrUnload(pDevExt, pImage);
+ rc = VERR_LDR_MISMATCH_NATIVE;
+ }
+ }
+ else
+ {
+ Log(("rcNt=%#x '%ls'\n", rcNt, pwcsFilename));
+ SUPR0Printf("VBoxDrv: rcNt=%x '%ws'\n", rcNt, pwcsFilename);
+ switch (rcNt)
+ {
+ case /* 0xc0000003 */ STATUS_INVALID_INFO_CLASS:
+# ifdef RT_ARCH_AMD64
+ /* Unwind will crash and BSOD, so no fallback here! */
+ rc = VERR_NOT_IMPLEMENTED;
+# else
+ /*
+ * Use the old way of loading the modules.
+ *
+ * Note! We do *NOT* try class 26 because it will probably
+ * not work correctly on terminal servers and such.
+ */
+ rc = VERR_NOT_SUPPORTED;
+# endif
+ break;
+ case /* 0xc0000034 */ STATUS_OBJECT_NAME_NOT_FOUND:
+ rc = VERR_MODULE_NOT_FOUND;
+ break;
+ case /* 0xC0000263 */ STATUS_DRIVER_ENTRYPOINT_NOT_FOUND:
+ rc = VERR_LDR_IMPORTED_SYMBOL_NOT_FOUND;
+ break;
+ case /* 0xC0000428 */ STATUS_INVALID_IMAGE_HASH:
+ rc = VERR_LDR_IMAGE_HASH;
+ break;
+ case /* 0xC000010E */ STATUS_IMAGE_ALREADY_LOADED:
+ Log(("WARNING: see @bugref{4853} for cause of this failure on Windows 7 x64\n"));
+ rc = VERR_ALREADY_LOADED;
+ break;
+ default:
+ rc = VERR_LDR_GENERAL_FAILURE;
+ break;
+ }
+
+ pImage->pvNtSectionObj = NULL;
+ }
+ }
+
+ RTMemTmpFree(pwcsFilename);
+ NOREF(pDevExt);
+ return rc;
+#endif
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyOpened(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
+{
+ NOREF(pDevExt); NOREF(pImage); NOREF(pszFilename);
+}
+
+
+void VBOXCALL supdrvOSLdrNotifyUnloaded(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ NOREF(pDevExt); NOREF(pImage);
+}
+
+
+/**
+ * Common worker for supdrvOSLdrQuerySymbol and supdrvOSLdrValidatePointer.
+ *
+ * @note Similar code in rtR0DbgKrnlNtParseModule.
+ */
+static int supdrvOSLdrValidatePointerOrQuerySymbol(PSUPDRVLDRIMAGE pImage, void *pv, const char *pszSymbol,
+ size_t cchSymbol, void **ppvSymbol)
+{
+ AssertReturn(pImage->pvNtSectionObj, VERR_INVALID_STATE);
+ Assert(pszSymbol || !ppvSymbol);
+
+ /*
+ * Locate the export directory in the loaded image.
+ */
+ uint8_t const *pbMapping = (uint8_t const *)pImage->pvImage;
+ uint32_t const cbMapping = pImage->cbImageBits;
+ uint32_t const uRvaToValidate = (uint32_t)((uintptr_t)pv - (uintptr_t)pbMapping);
+ AssertReturn(uRvaToValidate < cbMapping || ppvSymbol, VERR_INTERNAL_ERROR_3);
+
+ uint32_t const offNtHdrs = *(uint16_t *)pbMapping == IMAGE_DOS_SIGNATURE
+ ? ((IMAGE_DOS_HEADER const *)pbMapping)->e_lfanew
+ : 0;
+ AssertLogRelReturn(offNtHdrs + sizeof(IMAGE_NT_HEADERS) < cbMapping, VERR_INTERNAL_ERROR_5);
+
+ IMAGE_NT_HEADERS const *pNtHdrs = (IMAGE_NT_HEADERS const *)((uintptr_t)pbMapping + offNtHdrs);
+ AssertLogRelReturn(pNtHdrs->Signature == IMAGE_NT_SIGNATURE, VERR_INVALID_EXE_SIGNATURE);
+ AssertLogRelReturn(pNtHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC, VERR_BAD_EXE_FORMAT);
+ AssertLogRelReturn(pNtHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES, VERR_BAD_EXE_FORMAT);
+
+ uint32_t const offEndSectHdrs = offNtHdrs
+ + sizeof(*pNtHdrs)
+ + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ AssertReturn(offEndSectHdrs < cbMapping, VERR_BAD_EXE_FORMAT);
+
+ /*
+ * Find the export directory.
+ */
+ IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ if (!ExpDir.Size)
+ {
+ SUPR0Printf("SUPDrv: No exports in %s!\n", pImage->szName);
+ return ppvSymbol ? VERR_SYMBOL_NOT_FOUND : VERR_NOT_FOUND;
+ }
+ AssertReturn( ExpDir.Size >= sizeof(IMAGE_EXPORT_DIRECTORY)
+ && ExpDir.VirtualAddress >= offEndSectHdrs
+ && ExpDir.VirtualAddress < cbMapping
+ && ExpDir.VirtualAddress + ExpDir.Size <= cbMapping, VERR_BAD_EXE_FORMAT);
+
+ IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pbMapping[ExpDir.VirtualAddress];
+
+ uint32_t const cNamedExports = pExpDir->NumberOfNames;
+ AssertReturn(cNamedExports < _1M, VERR_BAD_EXE_FORMAT);
+ AssertReturn(pExpDir->NumberOfFunctions < _1M, VERR_BAD_EXE_FORMAT);
+ if (pExpDir->NumberOfFunctions == 0 || cNamedExports == 0)
+ {
+ SUPR0Printf("SUPDrv: No exports in %s!\n", pImage->szName);
+ return ppvSymbol ? VERR_SYMBOL_NOT_FOUND : VERR_NOT_FOUND;
+ }
+
+ uint32_t const cExports = RT_MAX(cNamedExports, pExpDir->NumberOfFunctions);
+
+ AssertReturn( pExpDir->AddressOfFunctions >= offEndSectHdrs
+ && pExpDir->AddressOfFunctions < cbMapping
+ && pExpDir->AddressOfFunctions + cExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint32_t const * const paoffExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfFunctions];
+
+ AssertReturn( pExpDir->AddressOfNames >= offEndSectHdrs
+ && pExpDir->AddressOfNames < cbMapping
+ && pExpDir->AddressOfNames + cNamedExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint32_t const * const paoffNamedExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfNames];
+
+ AssertReturn( pExpDir->AddressOfNameOrdinals >= offEndSectHdrs
+ && pExpDir->AddressOfNameOrdinals < cbMapping
+ && pExpDir->AddressOfNameOrdinals + cNamedExports * sizeof(uint32_t) <= cbMapping,
+ VERR_BAD_EXE_FORMAT);
+ uint16_t const * const pau16NameOrdinals = (uint16_t const *)&pbMapping[pExpDir->AddressOfNameOrdinals];
+
+ /*
+ * Validate the entrypoint RVA by scanning the export table.
+ */
+ uint32_t iExportOrdinal = UINT32_MAX;
+ if (!ppvSymbol)
+ {
+ for (uint32_t i = 0; i < cExports; i++)
+ if (paoffExports[i] == uRvaToValidate)
+ {
+ iExportOrdinal = i;
+ break;
+ }
+ if (iExportOrdinal == UINT32_MAX)
+ {
+ SUPR0Printf("SUPDrv: No export with rva %#x (%s) in %s!\n", uRvaToValidate, pszSymbol, pImage->szName);
+ return VERR_NOT_FOUND;
+ }
+ }
+
+ /*
+ * Can we validate the symbol name too or should we find a name?
+ * If so, just do a linear search.
+ */
+ if (pszSymbol && (RT_C_IS_UPPER(*pszSymbol) || ppvSymbol))
+ {
+ for (uint32_t i = 0; i < cNamedExports; i++)
+ {
+ uint32_t const offName = paoffNamedExports[i];
+ AssertReturn(offName < cbMapping, VERR_BAD_EXE_FORMAT);
+ uint32_t const cchMaxName = cbMapping - offName;
+ const char * const pszName = (const char *)&pbMapping[offName];
+ const char * const pszEnd = (const char *)memchr(pszName, '\0', cchMaxName);
+ AssertReturn(pszEnd, VERR_BAD_EXE_FORMAT);
+
+ if ( cchSymbol == (size_t)(pszEnd - pszName)
+ && memcmp(pszName, pszSymbol, cchSymbol) == 0)
+ {
+ if (ppvSymbol)
+ {
+ iExportOrdinal = pau16NameOrdinals[i];
+ if ( iExportOrdinal < cExports
+ && paoffExports[iExportOrdinal] < cbMapping)
+ {
+ *ppvSymbol = (void *)(paoffExports[iExportOrdinal] + pbMapping);
+ return VINF_SUCCESS;
+ }
+ }
+ else if (pau16NameOrdinals[i] == iExportOrdinal)
+ return VINF_SUCCESS;
+ else
+ SUPR0Printf("SUPDrv: Different exports found for %s and rva %#x in %s: %#x vs %#x\n",
+ pszSymbol, uRvaToValidate, pImage->szName, pau16NameOrdinals[i], iExportOrdinal);
+ return VERR_LDR_BAD_FIXUP;
+ }
+ }
+ if (!ppvSymbol)
+ SUPR0Printf("SUPDrv: No export named %s (%#x) in %s!\n", pszSymbol, uRvaToValidate, pImage->szName);
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv,
+ const uint8_t *pbImageBits, const char *pszSymbol)
+{
+ RT_NOREF(pDevExt, pbImageBits);
+ return supdrvOSLdrValidatePointerOrQuerySymbol(pImage, pv, pszSymbol, pszSymbol ? strlen(pszSymbol) : 0, NULL);
+}
+
+
+int VBOXCALL supdrvOSLdrQuerySymbol(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage,
+ const char *pszSymbol, size_t cchSymbol, void **ppvSymbol)
+{
+ RT_NOREF(pDevExt);
+ AssertReturn(ppvSymbol, VERR_INVALID_PARAMETER);
+ AssertReturn(pszSymbol, VERR_INVALID_PARAMETER);
+ return supdrvOSLdrValidatePointerOrQuerySymbol(pImage, NULL, pszSymbol, cchSymbol, ppvSymbol);
+}
+
+
+/**
+ * memcmp + errormsg + log.
+ *
+ * @returns Same as memcmp.
+ * @param pImage The image.
+ * @param pbImageBits The image bits ring-3 uploads.
+ * @param uRva The RVA to start comparing at.
+ * @param cb The number of bytes to compare.
+ * @param pReq The load request.
+ */
+static int supdrvNtCompare(PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, uint32_t uRva, uint32_t cb, PSUPLDRLOAD pReq)
+{
+ int iDiff = memcmp((uint8_t const *)pImage->pvImage + uRva, pbImageBits + uRva, cb);
+ if (iDiff)
+ {
+ uint32_t cbLeft = cb;
+ const uint8_t *pbNativeBits = (const uint8_t *)pImage->pvImage;
+ for (size_t off = uRva; cbLeft > 0; off++, cbLeft--)
+ if (pbNativeBits[off] != pbImageBits[off])
+ {
+ /* Note! We need to copy image bits into a temporary stack buffer here as we'd
+ otherwise risk overwriting them while formatting the error message. */
+ uint8_t abBytes[64];
+ memcpy(abBytes, &pbImageBits[off], RT_MIN(64, cbLeft));
+ supdrvLdrLoadError(VERR_LDR_MISMATCH_NATIVE, pReq,
+ "Mismatch at %#x (%p) of %s loaded at %p:\n"
+ "ntld: %.*Rhxs\n"
+ "iprt: %.*Rhxs",
+ off, &pbNativeBits[off], pImage->szName, pImage->pvImage,
+ RT_MIN(64, cbLeft), &pbNativeBits[off],
+ RT_MIN(64, cbLeft), &abBytes[0]);
+ SUPR0Printf("VBoxDrv: %s", pReq->u.Out.szError);
+ break;
+ }
+ }
+ return iDiff;
+}
+
+/** Image compare exclusion regions. */
+typedef struct SUPDRVNTEXCLREGIONS
+{
+ /** Number of regions. */
+ uint32_t cRegions;
+ /** The regions. */
+ struct SUPDRVNTEXCLREGION
+ {
+ uint32_t uRva;
+ uint32_t cb;
+ } aRegions[20];
+} SUPDRVNTEXCLREGIONS;
+
+/**
+ * Adds an exclusion region to the collection.
+ */
+static bool supdrvNtAddExclRegion(SUPDRVNTEXCLREGIONS *pRegions, uint32_t uRvaRegion, uint32_t cbRegion)
+{
+ uint32_t const cRegions = pRegions->cRegions;
+ AssertReturn(cRegions + 1 <= RT_ELEMENTS(pRegions->aRegions), false);
+ uint32_t i = 0;
+ for (; i < cRegions; i++)
+ if (uRvaRegion < pRegions->aRegions[i].uRva)
+ break;
+ if (i != cRegions)
+ memmove(&pRegions->aRegions[i + 1], &pRegions->aRegions[i], (cRegions - i) * sizeof(pRegions->aRegions[0]));
+ pRegions->aRegions[i].uRva = uRvaRegion;
+ pRegions->aRegions[i].cb = cbRegion;
+ pRegions->cRegions++;
+ return true;
+}
+
+
+int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
+{
+ NOREF(pDevExt);
+ if (pImage->pvNtSectionObj)
+ {
+ /*
+ * Usually, the entire image matches exactly.
+ */
+ if (!memcmp(pImage->pvImage, pbImageBits, pImage->cbImageBits))
+ return VINF_SUCCESS;
+
+ /*
+ * On Windows 10 the ImageBase member of the optional header is sometimes
+ * updated with the actual load address and sometimes not.
+ * On older windows versions (builds <= 9200?), a user mode address is
+ * sometimes found in the image base field after upgrading to VC++ 14.2.
+ */
+ uint32_t const offNtHdrs = *(uint16_t *)pbImageBits == IMAGE_DOS_SIGNATURE
+ ? ((IMAGE_DOS_HEADER const *)pbImageBits)->e_lfanew
+ : 0;
+ AssertLogRelReturn(offNtHdrs + sizeof(IMAGE_NT_HEADERS) < pImage->cbImageBits, VERR_INTERNAL_ERROR_5);
+ IMAGE_NT_HEADERS const *pNtHdrsIprt = (IMAGE_NT_HEADERS const *)(pbImageBits + offNtHdrs);
+ IMAGE_NT_HEADERS const *pNtHdrsNtLd = (IMAGE_NT_HEADERS const *)((uintptr_t)pImage->pvImage + offNtHdrs);
+
+ uint32_t const offImageBase = offNtHdrs + RT_UOFFSETOF(IMAGE_NT_HEADERS, OptionalHeader.ImageBase);
+ uint32_t const cbImageBase = RT_SIZEOFMEMB(IMAGE_NT_HEADERS, OptionalHeader.ImageBase);
+ if ( pNtHdrsNtLd->OptionalHeader.ImageBase != pNtHdrsIprt->OptionalHeader.ImageBase
+ && pNtHdrsIprt->Signature == IMAGE_NT_SIGNATURE
+ && pNtHdrsIprt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC
+ && !memcmp(pImage->pvImage, pbImageBits, offImageBase)
+ && !memcmp((uint8_t const *)pImage->pvImage + offImageBase + cbImageBase,
+ pbImageBits + offImageBase + cbImageBase,
+ pImage->cbImageBits - offImageBase - cbImageBase))
+ return VINF_SUCCESS;
+
+ /*
+ * On Windows Server 2003 (sp2 x86) both import thunk tables are fixed
+ * up and we typically get a mismatch in the INIT section.
+ *
+ * So, lets see if everything matches when excluding the
+ * OriginalFirstThunk tables and (maybe) the ImageBase member.
+ * For simplicity the max number of exclusion regions is set to 16.
+ */
+ if ( pNtHdrsIprt->Signature == IMAGE_NT_SIGNATURE
+ && pNtHdrsIprt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC
+ && pNtHdrsIprt->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size >= sizeof(IMAGE_IMPORT_DESCRIPTOR)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress > sizeof(IMAGE_NT_HEADERS)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress < pImage->cbImageBits
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size >= sizeof(IMAGE_LOAD_CONFIG_DIRECTORY)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress > sizeof(IMAGE_NT_HEADERS)
+ && pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress < pImage->cbImageBits)
+ {
+ SUPDRVNTEXCLREGIONS ExcludeRegions;
+ ExcludeRegions.cRegions = 0;
+
+ /* ImageBase: */
+ if (pNtHdrsNtLd->OptionalHeader.ImageBase != pNtHdrsIprt->OptionalHeader.ImageBase)
+ supdrvNtAddExclRegion(&ExcludeRegions, offImageBase, cbImageBase);
+
+ /* Imports: */
+ uint32_t cImpsLeft = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
+ / sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ uint32_t offImps = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ AssertLogRelReturn(offImps + cImpsLeft * sizeof(IMAGE_IMPORT_DESCRIPTOR) <= pImage->cbImageBits, VERR_INTERNAL_ERROR_3);
+ IMAGE_IMPORT_DESCRIPTOR const *pImp = (IMAGE_IMPORT_DESCRIPTOR const *)(pbImageBits + offImps);
+ while ( cImpsLeft-- > 0
+ && ExcludeRegions.cRegions < RT_ELEMENTS(ExcludeRegions.aRegions))
+ {
+ uint32_t uRvaThunk = pImp->OriginalFirstThunk;
+ if ( uRvaThunk > sizeof(IMAGE_NT_HEADERS)
+ && uRvaThunk <= pImage->cbImageBits - sizeof(IMAGE_THUNK_DATA)
+ && uRvaThunk != pImp->FirstThunk)
+ {
+ /* Find the size of the thunk table. */
+ IMAGE_THUNK_DATA const *paThunk = (IMAGE_THUNK_DATA const *)(pbImageBits + uRvaThunk);
+ uint32_t cMaxThunks = (pImage->cbImageBits - uRvaThunk) / sizeof(IMAGE_THUNK_DATA);
+ uint32_t cThunks = 0;
+ while (cThunks < cMaxThunks && paThunk[cThunks].u1.Function != 0)
+ cThunks++;
+ supdrvNtAddExclRegion(&ExcludeRegions, uRvaThunk, cThunks * sizeof(IMAGE_THUNK_DATA));
+ }
+
+#if 0 /* Useful for VMMR0 hacking, not for production use. See also SUPDrvLdr.cpp. */
+ /* Exclude the other thunk table if ntoskrnl.exe. */
+ uint32_t uRvaName = pImp->Name;
+ if ( uRvaName > sizeof(IMAGE_NT_HEADERS)
+ && uRvaName < pImage->cbImageBits - sizeof("ntoskrnl.exe")
+ && memcmp(&pbImageBits[uRvaName], RT_STR_TUPLE("ntoskrnl.exe")) == 0)
+ {
+ uRvaThunk = pImp->FirstThunk;
+ if ( uRvaThunk > sizeof(IMAGE_NT_HEADERS)
+ && uRvaThunk <= pImage->cbImageBits - sizeof(IMAGE_THUNK_DATA))
+ {
+ /* Find the size of the thunk table. */
+ IMAGE_THUNK_DATA const *paThunk = (IMAGE_THUNK_DATA const *)(pbImageBits + uRvaThunk);
+ uint32_t cMaxThunks = (pImage->cbImageBits - uRvaThunk) / sizeof(IMAGE_THUNK_DATA);
+ uint32_t cThunks = 0;
+ while (cThunks < cMaxThunks && paThunk[cThunks].u1.Function != 0)
+ cThunks++;
+ supdrvNtAddExclRegion(&ExcludeRegions, uRvaThunk, cThunks * sizeof(IMAGE_THUNK_DATA));
+ }
+ }
+#endif
+
+ /* advance */
+ pImp++;
+ }
+
+ /* Exclude the security cookie if present. */
+ uint32_t const cbCfg = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size;
+ uint32_t const offCfg = pNtHdrsIprt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress;
+ IMAGE_LOAD_CONFIG_DIRECTORY const * const pCfg = (IMAGE_LOAD_CONFIG_DIRECTORY const *)&pbImageBits[offCfg];
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie)
+ && pCfg->SecurityCookie != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->SecurityCookie - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /* Also exclude the GuardCFCheckFunctionPointer and GuardCFDispatchFunctionPointer pointer variables. */
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardCFCheckFunctionPointer)
+ && pCfg->GuardCFCheckFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardCFCheckFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardCFDispatchFunctionPointer)
+ && pCfg->GuardCFDispatchFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardCFDispatchFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /* Ditto for the XFG variants: */
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardXFGCheckFunctionPointer)
+ && pCfg->GuardXFGCheckFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardXFGCheckFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+ if ( pCfg->Size >= RT_UOFFSET_AFTER(IMAGE_LOAD_CONFIG_DIRECTORY, GuardXFGDispatchFunctionPointer)
+ && pCfg->GuardXFGDispatchFunctionPointer != NULL)
+ supdrvNtAddExclRegion(&ExcludeRegions, (uintptr_t)pCfg->GuardXFGDispatchFunctionPointer - (uintptr_t)pImage->pvImage, sizeof(void *));
+
+ /** @todo What about GuardRFVerifyStackPointerFunctionPointer and
+ * GuardRFFailureRoutineFunctionPointer? Ignore for now as the compiler we're
+ * using (19.26.28805) sets them to zero from what I can tell. */
+
+ /*
+ * Ok, do the comparison.
+ */
+ int iDiff = 0;
+ uint32_t uRvaNext = 0;
+ for (unsigned i = 0; !iDiff && i < ExcludeRegions.cRegions; i++)
+ {
+ if (uRvaNext < ExcludeRegions.aRegions[i].uRva)
+ iDiff = supdrvNtCompare(pImage, pbImageBits, uRvaNext, ExcludeRegions.aRegions[i].uRva - uRvaNext, pReq);
+ uRvaNext = ExcludeRegions.aRegions[i].uRva + ExcludeRegions.aRegions[i].cb;
+ }
+ if (!iDiff && uRvaNext < pImage->cbImageBits)
+ iDiff = supdrvNtCompare(pImage, pbImageBits, uRvaNext, pImage->cbImageBits - uRvaNext, pReq);
+ if (!iDiff)
+ {
+ /*
+ * If there is a cookie init export, call it.
+ *
+ * This typically just does:
+ * __security_cookie = (rdtsc ^ &__security_cookie) & 0xffffffffffff;
+ * __security_cookie_complement = ~__security_cookie;
+ */
+ PFNRT pfnModuleInitSecurityCookie = NULL;
+ int rcSym = supdrvOSLdrQuerySymbol(pDevExt, pImage, RT_STR_TUPLE("ModuleInitSecurityCookie"),
+ (void **)&pfnModuleInitSecurityCookie);
+ if (RT_SUCCESS(rcSym) && pfnModuleInitSecurityCookie)
+ pfnModuleInitSecurityCookie();
+
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ supdrvNtCompare(pImage, pbImageBits, 0, pImage->cbImageBits, pReq);
+ return VERR_LDR_MISMATCH_NATIVE;
+ }
+ return supdrvLdrLoadError(VERR_INTERNAL_ERROR_4, pReq, "No NT section object! Impossible!");
+}
+
+
+void VBOXCALL supdrvOSLdrUnload(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ if (pImage->pvNtSectionObj)
+ {
+ if (pImage->hMemLock != NIL_RTR0MEMOBJ)
+ {
+ RTR0MemObjFree(pImage->hMemLock, false /*fFreeMappings*/);
+ pImage->hMemLock = NIL_RTR0MEMOBJ;
+ }
+
+ NTSTATUS rcNt = ZwSetSystemInformation(MY_SystemUnloadGdiDriverInformation,
+ &pImage->pvNtSectionObj, sizeof(pImage->pvNtSectionObj));
+ if (rcNt != STATUS_SUCCESS)
+ SUPR0Printf("VBoxDrv: failed to unload '%s', rcNt=%#x\n", pImage->szName, rcNt);
+ pImage->pvNtSectionObj = NULL;
+ }
+ NOREF(pDevExt);
+}
+
+
+void VBOXCALL supdrvOSLdrRetainWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+void VBOXCALL supdrvOSLdrReleaseWrapperModule(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
+{
+ RT_NOREF(pDevExt, pImage);
+ AssertFailed();
+}
+
+
+#ifdef SUPDRV_WITH_MSR_PROBER
+
+#if 1
+/** @todo make this selectable. */
+# define AMD_MSR_PASSCODE 0x9c5a203a
+#else
+# define ASMRdMsrEx(a, b, c) ASMRdMsr(a)
+# define ASMWrMsrEx(a, b, c) ASMWrMsr(a,c)
+#endif
+
+
+/**
+ * Argument package used by supdrvOSMsrProberRead and supdrvOSMsrProberWrite.
+ */
+typedef struct SUPDRVNTMSPROBERARGS
+{
+ uint32_t uMsr;
+ uint64_t uValue;
+ bool fGp;
+} SUPDRVNTMSPROBERARGS;
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberRead.} */
+static DECLCALLBACK(void) supdrvNtMsProberReadOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ /*
+ * rdmsr and wrmsr faults can be caught even with interrupts disabled.
+ * (At least on 32-bit XP.)
+ */
+ SUPDRVNTMSPROBERARGS *pArgs = (SUPDRVNTMSPROBERARGS *)pvUser1; NOREF(idCpu); NOREF(pvUser2);
+ RTCCUINTREG fOldFlags = ASMIntDisableFlags();
+ __try
+ {
+ pArgs->uValue = ASMRdMsrEx(pArgs->uMsr, AMD_MSR_PASSCODE);
+ pArgs->fGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pArgs->fGp = true;
+ pArgs->uValue = 0;
+ }
+ ASMSetFlags(fOldFlags);
+}
+
+
+int VBOXCALL supdrvOSMsrProberRead(uint32_t uMsr, RTCPUID idCpu, uint64_t *puValue)
+{
+ SUPDRVNTMSPROBERARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue = 0;
+ Args.fGp = true;
+
+ if (idCpu == NIL_RTCPUID)
+ supdrvNtMsProberReadOnCpu(idCpu, &Args, NULL);
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvNtMsProberReadOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.fGp)
+ return VERR_ACCESS_DENIED;
+ *puValue = Args.uValue;
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberWrite.} */
+static DECLCALLBACK(void) supdrvNtMsProberWriteOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ /*
+ * rdmsr and wrmsr faults can be caught even with interrupts disabled.
+ * (At least on 32-bit XP.)
+ */
+ SUPDRVNTMSPROBERARGS *pArgs = (SUPDRVNTMSPROBERARGS *)pvUser1; NOREF(idCpu); NOREF(pvUser2);
+ RTCCUINTREG fOldFlags = ASMIntDisableFlags();
+ __try
+ {
+ ASMWrMsrEx(pArgs->uMsr, AMD_MSR_PASSCODE, pArgs->uValue);
+ pArgs->fGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pArgs->fGp = true;
+ }
+ ASMSetFlags(fOldFlags);
+}
+
+int VBOXCALL supdrvOSMsrProberWrite(uint32_t uMsr, RTCPUID idCpu, uint64_t uValue)
+{
+ SUPDRVNTMSPROBERARGS Args;
+ Args.uMsr = uMsr;
+ Args.uValue = uValue;
+ Args.fGp = true;
+
+ if (idCpu == NIL_RTCPUID)
+ supdrvNtMsProberWriteOnCpu(idCpu, &Args, NULL);
+ else
+ {
+ int rc = RTMpOnSpecific(idCpu, supdrvNtMsProberWriteOnCpu, &Args, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (Args.fGp)
+ return VERR_ACCESS_DENIED;
+ return VINF_SUCCESS;
+}
+
+/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberModify.} */
+static DECLCALLBACK(void) supdrvNtMsProberModifyOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PSUPMSRPROBER pReq = (PSUPMSRPROBER)pvUser1;
+ register uint32_t uMsr = pReq->u.In.uMsr;
+ bool const fFaster = pReq->u.In.enmOp == SUPMSRPROBEROP_MODIFY_FASTER;
+ uint64_t uBefore = 0;
+ uint64_t uWritten = 0;
+ uint64_t uAfter = 0;
+ bool fBeforeGp = true;
+ bool fModifyGp = true;
+ bool fAfterGp = true;
+ bool fRestoreGp = true;
+ RTCCUINTREG fOldFlags;
+ RT_NOREF2(idCpu, pvUser2);
+
+ /*
+ * Do the job.
+ */
+ fOldFlags = ASMIntDisableFlags();
+ ASMCompilerBarrier(); /* paranoia */
+ if (!fFaster)
+ ASMWriteBackAndInvalidateCaches();
+
+ __try
+ {
+ uBefore = ASMRdMsrEx(uMsr, AMD_MSR_PASSCODE);
+ fBeforeGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fBeforeGp = true;
+ }
+ if (!fBeforeGp)
+ {
+ register uint64_t uRestore = uBefore;
+
+ /* Modify. */
+ uWritten = uRestore;
+ uWritten &= pReq->u.In.uArgs.Modify.fAndMask;
+ uWritten |= pReq->u.In.uArgs.Modify.fOrMask;
+ __try
+ {
+ ASMWrMsrEx(uMsr, AMD_MSR_PASSCODE, uWritten);
+ fModifyGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fModifyGp = true;
+ }
+
+ /* Read modified value. */
+ __try
+ {
+ uAfter = ASMRdMsrEx(uMsr, AMD_MSR_PASSCODE);
+ fAfterGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fAfterGp = true;
+ }
+
+ /* Restore original value. */
+ __try
+ {
+ ASMWrMsrEx(uMsr, AMD_MSR_PASSCODE, uRestore);
+ fRestoreGp = false;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fRestoreGp = true;
+ }
+
+ /* Invalid everything we can. */
+ if (!fFaster)
+ {
+ ASMWriteBackAndInvalidateCaches();
+ ASMReloadCR3();
+ ASMNopPause();
+ }
+ }
+
+ ASMCompilerBarrier(); /* paranoia */
+ ASMSetFlags(fOldFlags);
+
+ /*
+ * Write out the results.
+ */
+ pReq->u.Out.uResults.Modify.uBefore = uBefore;
+ pReq->u.Out.uResults.Modify.uWritten = uWritten;
+ pReq->u.Out.uResults.Modify.uAfter = uAfter;
+ pReq->u.Out.uResults.Modify.fBeforeGp = fBeforeGp;
+ pReq->u.Out.uResults.Modify.fModifyGp = fModifyGp;
+ pReq->u.Out.uResults.Modify.fAfterGp = fAfterGp;
+ pReq->u.Out.uResults.Modify.fRestoreGp = fRestoreGp;
+ RT_ZERO(pReq->u.Out.uResults.Modify.afReserved);
+}
+
+
+int VBOXCALL supdrvOSMsrProberModify(RTCPUID idCpu, PSUPMSRPROBER pReq)
+{
+ if (idCpu == NIL_RTCPUID)
+ {
+ supdrvNtMsProberModifyOnCpu(idCpu, pReq, NULL);
+ return VINF_SUCCESS;
+ }
+ return RTMpOnSpecific(idCpu, supdrvNtMsProberModifyOnCpu, pReq, NULL);
+}
+
+#endif /* SUPDRV_WITH_MSR_PROBER */
+
+
+/**
+ * Converts an IPRT error code to an nt status code.
+ *
+ * @returns corresponding nt status code.
+ * @param rc IPRT error status code.
+ */
+static NTSTATUS VBoxDrvNtErr2NtStatus(int rc)
+{
+ switch (rc)
+ {
+ case VINF_SUCCESS: return STATUS_SUCCESS;
+ case VERR_GENERAL_FAILURE: return STATUS_NOT_SUPPORTED;
+ case VERR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
+ case VERR_INVALID_MAGIC: return STATUS_UNKNOWN_REVISION;
+ case VERR_INVALID_HANDLE: return STATUS_INVALID_HANDLE;
+ case VERR_INVALID_POINTER: return STATUS_INVALID_ADDRESS;
+ case VERR_LOCK_FAILED: return STATUS_NOT_LOCKED;
+ case VERR_ALREADY_LOADED: return STATUS_IMAGE_ALREADY_LOADED;
+ case VERR_PERMISSION_DENIED: return STATUS_ACCESS_DENIED;
+ case VERR_VERSION_MISMATCH: return STATUS_REVISION_MISMATCH;
+ }
+
+ if (rc < 0)
+ {
+ if (((uint32_t)rc & UINT32_C(0xffff0000)) == UINT32_C(0xffff0000))
+ return (NTSTATUS)( ((uint32_t)rc & UINT32_C(0xffff)) | SUP_NT_STATUS_BASE );
+ }
+ return STATUS_UNSUCCESSFUL;
+}
+
+
+SUPR0DECL(int) SUPR0PrintfV(const char *pszFormat, va_list va)
+{
+ char szMsg[384];
+ size_t cch = RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+
+ RTLogWriteDebugger(szMsg, cch);
+ return 0;
+}
+
+
+SUPR0DECL(uint32_t) SUPR0GetKernelFeatures(void)
+{
+ return 0;
+}
+
+
+SUPR0DECL(bool) SUPR0FpuBegin(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+ return false;
+}
+
+
+SUPR0DECL(void) SUPR0FpuEnd(bool fCtxHook)
+{
+ RT_NOREF(fCtxHook);
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlSetupForHandle(PSUPDRVSESSION pSession, intptr_t hHandle, uint32_t fFlags, PSUPR0IOCTLCTX *ppCtx)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
+ *ppCtx = NULL;
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Turn the partition handle into a file object and related device object
+ * so that we can issue direct I/O control calls to the pair later.
+ */
+ PFILE_OBJECT pFileObject = NULL;
+ OBJECT_HANDLE_INFORMATION HandleInfo = { 0, 0 };
+ NTSTATUS rcNt = ObReferenceObjectByHandle((HANDLE)hHandle, /*FILE_WRITE_DATA*/0, *IoFileObjectType,
+ UserMode, (void **)&pFileObject, &HandleInfo);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrConvertFromNtStatus(rcNt);
+ AssertPtrReturn(pFileObject, VERR_INTERNAL_ERROR_3);
+
+ PDEVICE_OBJECT pDevObject = IoGetRelatedDeviceObject(pFileObject);
+ AssertMsgReturnStmt(RT_VALID_PTR(pDevObject), ("pDevObject=%p\n", pDevObject),
+ ObDereferenceObject(pFileObject), VERR_INTERNAL_ERROR_2);
+
+ /*
+ * Allocate a context structure and fill it in.
+ */
+ PSUPR0IOCTLCTX pCtx = (PSUPR0IOCTLCTX)RTMemAllocZ(sizeof(*pCtx));
+ if (pCtx)
+ {
+ pCtx->u32Magic = SUPR0IOCTLCTX_MAGIC;
+ pCtx->cRefs = 1;
+ pCtx->pFileObject = pFileObject;
+ pCtx->pDeviceObject = pDevObject;
+
+ PDRIVER_OBJECT pDrvObject = pDevObject->DriverObject;
+ if ( RT_VALID_PTR(pDrvObject->FastIoDispatch)
+ && RT_VALID_PTR(pDrvObject->FastIoDispatch->FastIoDeviceControl))
+ pCtx->pfnFastIoDeviceControl = pDrvObject->FastIoDispatch->FastIoDeviceControl;
+ else
+ pCtx->pfnFastIoDeviceControl = NULL;
+ *ppCtx = pCtx;
+ return VINF_SUCCESS;
+ }
+
+ ObDereferenceObject(pFileObject);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * I/O control destructor for NT.
+ *
+ * @param pCtx The context to destroy.
+ */
+static void supdrvNtIoCtlContextDestroy(PSUPR0IOCTLCTX pCtx)
+{
+ PFILE_OBJECT pFileObject = pCtx->pFileObject;
+ pCtx->pfnFastIoDeviceControl = NULL;
+ pCtx->pFileObject = NULL;
+ pCtx->pDeviceObject = NULL;
+ ASMAtomicWriteU32(&pCtx->u32Magic, ~SUPR0IOCTLCTX_MAGIC);
+
+ if (RT_VALID_PTR(pFileObject))
+ ObDereferenceObject(pFileObject);
+ RTMemFree(pCtx);
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlCleanup(PSUPR0IOCTLCTX pCtx)
+{
+ if (pCtx != NULL)
+ {
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->u32Magic == SUPR0IOCTLCTX_MAGIC, VERR_INVALID_PARAMETER);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+ }
+ return VINF_SUCCESS;
+}
+
+
+SUPR0DECL(int) SUPR0IoCtlPerform(PSUPR0IOCTLCTX pCtx, uintptr_t uFunction,
+ void *pvInput, RTR3PTR pvInputUser, size_t cbInput,
+ void *pvOutput, RTR3PTR pvOutputUser, size_t cbOutput,
+ int32_t *piNativeRc)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->u32Magic == SUPR0IOCTLCTX_MAGIC, VERR_INVALID_PARAMETER);
+
+ /* Reference the context. */
+ uint32_t cRefs = ASMAtomicIncU32(&pCtx->cRefs);
+ Assert(cRefs > 1 && cRefs < _4K);
+
+ /*
+ * Try fast I/O control path first.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ if (pCtx->pfnFastIoDeviceControl)
+ {
+ /* Must pass user addresses here as that's what's being expected. */
+ BOOLEAN fHandled = pCtx->pfnFastIoDeviceControl(pCtx->pFileObject,
+ TRUE /*Wait*/,
+ (void *)pvInputUser, (ULONG)cbInput,
+ (void *)pvOutputUser, (ULONG)cbOutput,
+ uFunction,
+ &Ios,
+ pCtx->pDeviceObject);
+ if (fHandled)
+ {
+ /* Relase the context. */
+ cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+
+ /* Set/convert status and return. */
+ if (piNativeRc)
+ {
+ *piNativeRc = Ios.Status;
+ return VINF_SUCCESS;
+ }
+ if (NT_SUCCESS(Ios.Status))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(Ios.Status);
+ }
+
+ /*
+ * Fall back on IRP if not handled.
+ *
+ * Note! Perhaps we should rather fail, because VID.SYS will crash getting
+ * the partition ID with the code below. It tries to zero the output
+ * buffer as if it were as system buffer...
+ */
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * For directly accessed buffers we must supply user mode addresses or
+ * we'll fail ProbeForWrite validation.
+ */
+ switch (uFunction & 3)
+ {
+ case METHOD_BUFFERED:
+ /* For buffered accesses, we can supply kernel buffers. */
+ break;
+
+ case METHOD_IN_DIRECT:
+ pvInput = (void *)pvInputUser;
+ break;
+
+ case METHOD_NEITHER:
+ pvInput = (void *)pvInputUser;
+ RT_FALL_THRU();
+
+ case METHOD_OUT_DIRECT:
+ pvOutput = (void *)pvOutputUser;
+ break;
+ }
+
+ /*
+ * Build the request.
+ */
+ int rc;
+ KEVENT Event;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ PIRP pIrp = IoBuildDeviceIoControlRequest(uFunction, pCtx->pDeviceObject,
+ pvInput, (ULONG)cbInput, pvOutput, (ULONG)cbOutput,
+ FALSE /* InternalDeviceControl */, &Event, &Ios);
+ if (pIrp)
+ {
+ IoGetNextIrpStackLocation(pIrp)->FileObject = pCtx->pFileObject;
+
+ /*
+ * Make the call.
+ */
+ NTSTATUS rcNt = IoCallDriver(pCtx->pDeviceObject, pIrp);
+ if (rcNt == STATUS_PENDING)
+ {
+ rcNt = KeWaitForSingleObject(&Event, /* Object */
+ Executive, /* WaitReason */
+ KernelMode, /* WaitMode */
+ FALSE, /* Alertable */
+ NULL); /* TimeOut */
+ AssertMsg(rcNt == STATUS_SUCCESS, ("rcNt=%#x\n", rcNt));
+ rcNt = Ios.Status;
+ }
+ else if (NT_SUCCESS(rcNt) && Ios.Status != STATUS_SUCCESS)
+ rcNt = Ios.Status;
+
+ /* Set/convert return code. */
+ if (piNativeRc)
+ {
+ *piNativeRc = rcNt;
+ rc = VINF_SUCCESS;
+ }
+ else if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ {
+ if (piNativeRc)
+ *piNativeRc = STATUS_NO_MEMORY;
+ rc = VERR_NO_MEMORY;
+ }
+
+ /* Relase the context. */
+ cRefs = ASMAtomicDecU32(&pCtx->cRefs);
+ Assert(cRefs < _4K);
+ if (cRefs == 0)
+ supdrvNtIoCtlContextDestroy(pCtx);
+
+ return rc;
+}
+
+
+#ifdef VBOX_WITH_HARDENING
+
+/** @name Identifying Special Processes: CSRSS.EXE
+ * @{ */
+
+
+/**
+ * Checks if the process is a system32 process by the given name.
+ *
+ * @returns true / false.
+ * @param pProcess The process to check.
+ * @param pszName The lower case process name (no path!).
+ */
+static bool supdrvNtProtectIsSystem32ProcessMatch(PEPROCESS pProcess, const char *pszName)
+{
+ Assert(strlen(pszName) < 16); /* see buffer below */
+
+ /*
+ * This test works on XP+.
+ */
+ const char *pszImageFile = (const char *)PsGetProcessImageFileName(pProcess);
+ if (!pszImageFile)
+ return false;
+
+ if (RTStrICmp(pszImageFile, pszName) != 0)
+ return false;
+
+ /*
+ * This test requires a Vista+ API.
+ */
+ if (g_pfnPsReferenceProcessFilePointer)
+ {
+ PFILE_OBJECT pFile = NULL;
+ NTSTATUS rcNt = g_pfnPsReferenceProcessFilePointer(pProcess, &pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ union
+ {
+ OBJECT_NAME_INFORMATION Info;
+ uint8_t abBuffer[sizeof(g_System32NtPath) + 16 * sizeof(WCHAR)];
+ } Buf;
+ ULONG cbIgn;
+ rcNt = ObQueryNameString(pFile, &Buf.Info, sizeof(Buf) - sizeof(WCHAR), &cbIgn);
+ ObDereferenceObject(pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ /* Terminate the name. */
+ PRTUTF16 pwszName = Buf.Info.Name.Buffer;
+ pwszName[Buf.Info.Name.Length / sizeof(RTUTF16)] = '\0';
+
+ /* Match the name against the system32 directory path. */
+ uint32_t cbSystem32 = g_System32NtPath.UniStr.Length;
+ if (Buf.Info.Name.Length < cbSystem32)
+ return false;
+ if (memcmp(pwszName, g_System32NtPath.UniStr.Buffer, cbSystem32))
+ return false;
+ pwszName += cbSystem32 / sizeof(RTUTF16);
+ if (*pwszName++ != '\\')
+ return false;
+
+ /* Compare the name. */
+ const char *pszRight = pszName;
+ for (;;)
+ {
+ WCHAR wchLeft = *pwszName++;
+ char chRight = *pszRight++;
+ Assert(chRight == RT_C_TO_LOWER(chRight));
+
+ if ( wchLeft != chRight
+ && RT_C_TO_LOWER(wchLeft) != chRight)
+ return false;
+ if (!chRight)
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the current process is likely to be CSRSS.
+ *
+ * @returns true/false.
+ * @param pProcess The process.
+ */
+static bool supdrvNtProtectIsCsrssByProcess(PEPROCESS pProcess)
+{
+ /*
+ * On Windows 8.1 CSRSS.EXE is a protected process.
+ */
+ if (g_pfnPsIsProtectedProcessLight)
+ {
+ if (!g_pfnPsIsProtectedProcessLight(pProcess))
+ return false;
+ }
+
+ /*
+ * The name tests.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pProcess, "csrss.exe"))
+ return false;
+
+ /** @todo Could extend the CSRSS.EXE check with that the TokenUser of the
+ * current process must be "NT AUTHORITY\SYSTEM" (S-1-5-18). */
+
+ return true;
+}
+
+
+/**
+ * Helper for supdrvNtProtectGetAlpcPortObjectType that tries out a name.
+ *
+ * @returns true if done, false if not.
+ * @param pwszPortNm The port path.
+ * @param ppObjType The object type return variable, updated when
+ * returning true.
+ */
+static bool supdrvNtProtectGetAlpcPortObjectType2(PCRTUTF16 pwszPortNm, POBJECT_TYPE *ppObjType)
+{
+ bool fDone = false;
+
+ UNICODE_STRING UniStrPortNm;
+ UniStrPortNm.Buffer = (WCHAR *)pwszPortNm;
+ UniStrPortNm.Length = (USHORT)(RTUtf16Len(pwszPortNm) * sizeof(WCHAR));
+ UniStrPortNm.MaximumLength = UniStrPortNm.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &UniStrPortNm, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ HANDLE hPort;
+ NTSTATUS rcNt = g_pfnZwAlpcCreatePort(&hPort, &ObjAttr, NULL /*pPortAttribs*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ PVOID pvObject;
+ rcNt = ObReferenceObjectByHandle(hPort, 0 /*DesiredAccess*/, NULL /*pObjectType*/,
+ KernelMode, &pvObject, NULL /*pHandleInfo*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ POBJECT_TYPE pObjType = g_pfnObGetObjectType(pvObject);
+ if (pObjType)
+ {
+ SUPR0Printf("vboxdrv: ALPC Port Object Type %p (vs %p)\n", pObjType, *ppObjType);
+ *ppObjType = pObjType;
+ fDone = true;
+ }
+ ObDereferenceObject(pvObject);
+ }
+ NtClose(hPort);
+ }
+ return fDone;
+}
+
+
+/**
+ * Attempts to retrieve the ALPC Port object type.
+ *
+ * We've had at least three reports that using LpcPortObjectType when trying to
+ * get at the ApiPort object results in STATUS_OBJECT_TYPE_MISMATCH errors.
+ * It's not known who has modified LpcPortObjectType or AlpcPortObjectType (not
+ * exported) so that it differs from the actual ApiPort type, or maybe this
+ * unknown entity is intercepting our attempt to reference the port and
+ * tries to mislead us. The paranoid explanataion is of course that some evil
+ * root kit like software is messing with the OS, however, it's possible that
+ * this is valid kernel behavior that 99.8% of our users and 100% of the
+ * developers are not triggering for some reason.
+ *
+ * The code here creates an ALPC port object and gets it's type. It will cache
+ * the result in g_pAlpcPortObjectType2 on success.
+ *
+ * @returns Object type.
+ * @param uSessionId The session id.
+ * @param pszSessionId The session id formatted as a string.
+ */
+static POBJECT_TYPE supdrvNtProtectGetAlpcPortObjectType(uint32_t uSessionId, const char *pszSessionId)
+{
+ POBJECT_TYPE pObjType = *LpcPortObjectType;
+
+ if ( g_pfnZwAlpcCreatePort
+ && g_pfnObGetObjectType)
+ {
+ int rc;
+ ssize_t cchTmp; NOREF(cchTmp);
+ char szTmp[16];
+ RTUTF16 wszPortNm[128];
+ size_t offRand;
+
+ /*
+ * First attempt is in the session directory.
+ */
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\Sessions\\");
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), pszSessionId);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\VBoxDrv-");
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "-");
+ offRand = RTUtf16Len(wszPortNm);
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ bool fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ if (!fDone)
+ {
+ wszPortNm[offRand] = '\0';
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0); Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ }
+ if (!fDone)
+ {
+ /*
+ * Try base names.
+ */
+ if (uSessionId == 0)
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\BaseNamedObjects\\VBoxDrv-");
+ else
+ {
+ rc = RTUtf16CopyAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\Sessions\\");
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), pszSessionId);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "\\BaseNamedObjects\\VBoxDrv-");
+ }
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), "-");
+ offRand = RTUtf16Len(wszPortNm);
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ if (!fDone)
+ {
+ wszPortNm[offRand] = '\0';
+ cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), RTRandU32(), 16, 0, 0, 0);
+ Assert(cchTmp > 0);
+ rc |= RTUtf16CatAscii(wszPortNm, RT_ELEMENTS(wszPortNm), szTmp);
+ AssertRCSuccess(rc);
+
+ fDone = supdrvNtProtectGetAlpcPortObjectType2(wszPortNm, &pObjType);
+ }
+ }
+
+ /* Cache the result in g_pAlpcPortObjectType2. */
+ if ( g_pAlpcPortObjectType2 == NULL
+ && pObjType != g_pAlpcPortObjectType1
+ && fDone)
+ g_pAlpcPortObjectType2 = pObjType;
+
+ }
+
+ return pObjType;
+}
+
+
+/**
+ * Called in the context of VBoxDrvNtCreate to determin the CSRSS for the
+ * current process.
+ *
+ * The Client/Server Runtime Subsystem (CSRSS) process needs to be allowed some
+ * additional access right so we need to make 101% sure we correctly identify
+ * the CSRSS process a process is associated with.
+ *
+ * @returns IPRT status code.
+ * @param pNtProtect The NT protected process structure. The
+ * hCsrssPid member will be updated on success.
+ */
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect)
+{
+ Assert(pNtProtect->AvlCore.Key == PsGetCurrentProcessId());
+ Assert(pNtProtect->pCsrssProcess == NULL);
+ Assert(pNtProtect->hCsrssPid == NULL);
+
+ /*
+ * We'll try use the ApiPort LPC object for the session we're in to track
+ * down the CSRSS process. So, we start by constructing a path to it.
+ */
+ int rc;
+ uint32_t uSessionId = PsGetProcessSessionId(PsGetCurrentProcess());
+ char szSessionId[16];
+ WCHAR wszApiPort[48];
+ if (uSessionId == 0)
+ {
+ szSessionId[0] = '0';
+ szSessionId[1] = '\0';
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ }
+ else
+ {
+ ssize_t cchTmp = RTStrFormatU32(szSessionId, sizeof(szSessionId), uSessionId, 10, 0, 0, 0);
+ AssertReturn(cchTmp > 0, (int)cchTmp);
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Sessions\\");
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), szSessionId);
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ }
+ AssertRCReturn(rc, rc);
+
+ UNICODE_STRING ApiPortStr;
+ ApiPortStr.Buffer = wszApiPort;
+ ApiPortStr.Length = (USHORT)(RTUtf16Len(wszApiPort) * sizeof(RTUTF16));
+ ApiPortStr.MaximumLength = ApiPortStr.Length + sizeof(RTUTF16);
+
+ /*
+ * The object cannot be opened, but we can reference it by name.
+ */
+ void *pvApiPortObj = NULL;
+ NTSTATUS rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ g_pAlpcPortObjectType1,
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ && g_pAlpcPortObjectType2 != NULL)
+ rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ g_pAlpcPortObjectType2,
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ && g_pfnObGetObjectType
+ && g_pfnZwAlpcCreatePort)
+ rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ supdrvNtProtectGetAlpcPortObjectType(uSessionId, szSessionId),
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUPR0Printf("vboxdrv: Error opening '%ls': %#x\n", wszApiPort, rcNt);
+ return rcNt == STATUS_OBJECT_TYPE_MISMATCH ? VERR_SUPDRV_APIPORT_OPEN_ERROR_TYPE : VERR_SUPDRV_APIPORT_OPEN_ERROR;
+ }
+
+ /*
+ * Query the processes in the system so we can locate CSRSS.EXE candidates.
+ * Note! Attempts at using SystemSessionProcessInformation failed with
+ * STATUS_ACCESS_VIOLATION.
+ * Note! The 32 bytes on the size of to counteract the allocation header
+ * that rtR0MemAllocEx slaps on everything.
+ */
+ ULONG cbNeeded = _64K - 32;
+ uint32_t cbBuf;
+ uint8_t *pbBuf = NULL;
+ do
+ {
+ cbBuf = RT_ALIGN(cbNeeded + _4K, _64K) - 32;
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ break;
+
+ cbNeeded = 0;
+#if 0 /* doesn't work. */
+ SYSTEM_SESSION_PROCESS_INFORMATION Req;
+ Req.SessionId = uSessionId;
+ Req.BufferLength = cbBuf;
+ Req.Buffer = pbBuf;
+ rcNt = NtQuerySystemInformation(SystemSessionProcessInformation, &Req, sizeof(Req), &cbNeeded);
+#else
+ rcNt = NtQuerySystemInformation(SystemProcessInformation, pbBuf, cbBuf, &cbNeeded);
+#endif
+ if (NT_SUCCESS(rcNt))
+ break;
+
+ RTMemFree(pbBuf);
+ pbBuf = NULL;
+ } while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbNeeded < 32U*_1M);
+
+ if ( pbBuf
+ && NT_SUCCESS(rcNt)
+ && cbNeeded >= sizeof(SYSTEM_PROCESS_INFORMATION))
+ {
+ /*
+ * Walk the returned data and look for the process associated with the
+ * ApiPort object. The ApiPort object keeps the EPROCESS address of
+ * the owner process (i.e. CSRSS) relatively early in the structure. On
+ * 64-bit windows 8.1 it's at offset 0x18. So, obtain the EPROCESS
+ * pointer to likely CSRSS processes and check for a match in the first
+ * 0x40 bytes of the ApiPort object.
+ */
+ rc = VERR_SUPDRV_CSRSS_NOT_FOUND;
+ for (uint32_t offBuf = 0; offBuf <= cbNeeded - sizeof(SYSTEM_PROCESS_INFORMATION);)
+ {
+ PRTNT_SYSTEM_PROCESS_INFORMATION pProcInfo = (PRTNT_SYSTEM_PROCESS_INFORMATION)&pbBuf[offBuf];
+ if ( pProcInfo->ProcessName.Length == 9 * sizeof(WCHAR)
+ && pProcInfo->NumberOfThreads > 2 /* Very low guess. */
+ && pProcInfo->HandleCount > 32 /* Very low guess, I hope. */
+ && (uintptr_t)pProcInfo->ProcessName.Buffer - (uintptr_t)pbBuf < cbNeeded
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[0]) == 'c'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[1]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[2]) == 'r'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[3]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[4]) == 's'
+ && pProcInfo->ProcessName.Buffer[5] == '.'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[6]) == 'e'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[7]) == 'x'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[8]) == 'e' )
+ {
+
+ /* Get the process structure and perform some more thorough
+ process checks. */
+ PEPROCESS pProcess;
+ rcNt = PsLookupProcessByProcessId(pProcInfo->UniqueProcessId, &pProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (supdrvNtProtectIsCsrssByProcess(pProcess))
+ {
+ if (PsGetProcessSessionId(pProcess) == uSessionId)
+ {
+ /* Final test, check the ApiPort.
+ Note! The old LPC (pre Vista) objects has the PID
+ much earlier in the structure. Might be
+ worth looking for it instead. */
+ bool fThatsIt = false;
+ __try
+ {
+ PEPROCESS *ppPortProc = (PEPROCESS *)pvApiPortObj;
+ uint32_t cTests = g_uNtVerCombined >= SUP_NT_VER_VISTA ? 16 : 38; /* ALPC since Vista. */
+ do
+ {
+ fThatsIt = *ppPortProc == pProcess;
+ ppPortProc++;
+ } while (!fThatsIt && --cTests > 0);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fThatsIt = false;
+ }
+ if (fThatsIt)
+ {
+ /* Ok, we found it! Keep the process structure
+ reference as well as the PID so we can
+ safely identify it later on. */
+ pNtProtect->hCsrssPid = pProcInfo->UniqueProcessId;
+ pNtProtect->pCsrssProcess = pProcess;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ ObDereferenceObject(pProcess);
+ }
+ }
+
+ /* Advance. */
+ if (!pProcInfo->NextEntryOffset)
+ break;
+ offBuf += pProcInfo->NextEntryOffset;
+ }
+ }
+ else
+ rc = VERR_SUPDRV_SESSION_PROCESS_ENUM_ERROR;
+ RTMemFree(pbBuf);
+ ObDereferenceObject(pvApiPortObj);
+ return rc;
+}
+
+
+/**
+ * Checks that the given process is the CSRSS process associated with protected
+ * process.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pCsrss The process structure of the alleged CSRSS.EXE
+ * process.
+ */
+static bool supdrvNtProtectIsAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pCsrss)
+{
+ if (pNtProtect->pCsrssProcess == pCsrss)
+ {
+ if (pNtProtect->hCsrssPid == PsGetProcessId(pCsrss))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Checks if the given process is the stupid themes service.
+ *
+ * The caller does some screening of access masks and what not. We do the rest.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pAnnoyingProcess The process structure of an process that might
+ * happen to be the annoying themes process.
+ */
+static bool supdrvNtProtectIsFrigginThemesService(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pAnnoyingProcess)
+{
+ RT_NOREF1(pNtProtect);
+
+ /*
+ * Check the process name.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pAnnoyingProcess, "svchost.exe"))
+ return false;
+
+ /** @todo Come up with more checks. */
+
+ return true;
+}
+
+
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+/**
+ * Checks if the given process is one of the whitelisted debuggers.
+ *
+ * @returns true / false.
+ * @param pProcess The process to check.
+ */
+static bool supdrvNtProtectIsWhitelistedDebugger(PEPROCESS pProcess)
+{
+ const char *pszImageFile = (const char *)PsGetProcessImageFileName(pProcess);
+ if (!pszImageFile)
+ return false;
+
+ if (pszImageFile[0] == 'w' || pszImageFile[0] == 'W')
+ {
+ if (RTStrICmp(pszImageFile, "windbg.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "werfault.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "werfaultsecure.exe") == 0)
+ return true;
+ }
+ else if (pszImageFile[0] == 'd' || pszImageFile[0] == 'D')
+ {
+ if (RTStrICmp(pszImageFile, "drwtsn32.exe") == 0)
+ return true;
+ if (RTStrICmp(pszImageFile, "dwwin.exe") == 0)
+ return true;
+ }
+
+ return false;
+}
+#endif /* VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/** @} */
+
+
+/** @name Process Creation Callbacks.
+ * @{ */
+
+
+/**
+ * Cleans up VBoxDrv or VBoxDrvStub error info not collected by the dead process.
+ *
+ * @param hProcessId The ID of the dead process.
+ */
+static void supdrvNtErrorInfoCleanupProcess(HANDLE hProcessId)
+{
+ int rc = RTSemMutexRequestNoResume(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ PSUPDRVNTERRORINFO pCur, pNext;
+ RTListForEachSafe(&g_ErrorInfoHead, pCur, pNext, SUPDRVNTERRORINFO, ListEntry)
+ {
+ if (pCur->hProcessId == hProcessId)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ }
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+}
+
+
+/**
+ * Common worker used by the process creation hooks as well as the process
+ * handle creation hooks to check if a VM process is being created.
+ *
+ * @returns true if likely to be a VM process, false if not.
+ * @param pNtStub The NT protection structure for the possible
+ * stub process.
+ * @param hParentPid The parent pid.
+ * @param hChildPid The child pid.
+ */
+static bool supdrvNtProtectIsSpawningStubProcess(PSUPDRVNTPROTECT pNtStub, HANDLE hParentPid, HANDLE hChildPid)
+{
+ bool fRc = false;
+ if (pNtStub->AvlCore.Key == hParentPid) /* paranoia */
+ {
+ if (pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ /* Compare short names. */
+ PEPROCESS pStubProcess;
+ NTSTATUS rcNt = PsLookupProcessByProcessId(hParentPid, &pStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ PEPROCESS pChildProcess;
+ rcNt = PsLookupProcessByProcessId(hChildPid, &pChildProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ const char *pszStub = (const char *)PsGetProcessImageFileName(pStubProcess);
+ const char *pszChild = (const char *)PsGetProcessImageFileName(pChildProcess);
+ fRc = pszStub != NULL
+ && pszChild != NULL
+ && strcmp(pszStub, pszChild) == 0;
+
+ /** @todo check that the full image names matches. */
+
+ ObDereferenceObject(pChildProcess);
+ }
+ ObDereferenceObject(pStubProcess);
+ }
+ }
+ }
+ return fRc;
+}
+
+
+/**
+ * Common code used by the notifies to protect a child process.
+ *
+ * @returns VBox status code.
+ * @param pNtStub The NT protect structure for the parent.
+ * @param hChildPid The child pid.
+ */
+static int supdrvNtProtectProtectNewStubChild(PSUPDRVNTPROTECT pNtParent, HANDLE hChildPid)
+{
+ /*
+ * Create a child protection struction.
+ */
+ PSUPDRVNTPROTECT pNtChild;
+ int rc = supdrvNtProtectCreate(&pNtChild, hChildPid, kSupDrvNtProtectKind_VmProcessUnconfirmed, false /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ pNtChild->fFirstProcessCreateHandle = true;
+ pNtChild->fFirstThreadCreateHandle = true;
+ pNtChild->fCsrssFirstProcessCreateHandle = true;
+ pNtChild->cCsrssFirstProcessDuplicateHandle = ARCH_BITS == 32 ? 2 : 1;
+ pNtChild->fThemesFirstProcessCreateHandle = true;
+ pNtChild->hParentPid = pNtParent->AvlCore.Key;
+ pNtChild->hCsrssPid = pNtParent->hCsrssPid;
+ pNtChild->pCsrssProcess = pNtParent->pCsrssProcess;
+ if (pNtChild->pCsrssProcess)
+ ObReferenceObject(pNtChild->pCsrssProcess);
+
+ /*
+ * Take the spinlock, recheck parent conditions and link things.
+ */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtParent->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtChild->AvlCore);
+ if (fSuccess)
+ {
+ pNtChild->fInTree = true;
+ pNtParent->u.pChild = pNtChild; /* Parent keeps the initial reference. */
+ pNtParent->enmProcessKind = kSupDrvNtProtectKind_StubParent;
+ pNtChild->u.pParent = pNtParent;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ pNtChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtChild);
+ }
+ return rc;
+}
+
+
+/**
+ * Common process termination code.
+ *
+ * Transitions protected process to the dead states, protecting against handle
+ * PID reuse (esp. with unconfirmed VM processes) and handle cleanup issues.
+ *
+ * @param hDeadPid The PID of the dead process.
+ */
+static void supdrvNtProtectUnprotectDeadProcess(HANDLE hDeadPid)
+{
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hDeadPid);
+ if (pNtProtect)
+ {
+ PSUPDRVNTPROTECT pNtChild = NULL;
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /*
+ * If this is an unconfirmed VM process, we must release the reference
+ * the parent structure holds.
+ */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ PSUPDRVNTPROTECT pNtParent = pNtProtect->u.pParent;
+ AssertRelease(pNtParent); AssertRelease(pNtParent->u.pChild == pNtProtect);
+ pNtParent->u.pChild = NULL;
+ pNtProtect->u.pParent = NULL;
+ pNtChild = pNtProtect;
+ }
+ /*
+ * If this is a stub exitting before the VM process gets confirmed,
+ * release the protection of the potential VM process as this is not
+ * the prescribed behavior.
+ */
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtProtect->u.pChild)
+ {
+ pNtChild = pNtProtect->u.pChild;
+ pNtProtect->u.pChild = NULL;
+ pNtChild->u.pParent = NULL;
+ pNtChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /*
+ * Transition it to the dead state to prevent it from opening the
+ * support driver again or be posthumously abused as a vm process parent.
+ */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubSpawning
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_StubDead;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtProtect);
+ supdrvNtProtectRelease(pNtChild);
+
+ /*
+ * Do session cleanups.
+ */
+ AssertReturnVoid((HANDLE)(uintptr_t)RTProcSelf() == hDeadPid);
+ if (g_pDevObjSys)
+ {
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, (RTPROCESS)(uintptr_t)hDeadPid,
+ RTR0ProcHandleSelf(), NULL);
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+ }
+}
+
+
+/**
+ * Common worker for the process creation callback that verifies a new child
+ * being created by the handle creation callback code.
+ *
+ * @param pNtStub The parent.
+ * @param pNtVm The child.
+ * @param fCallerChecks The result of any additional tests the caller made.
+ * This is in order to avoid duplicating the failure
+ * path code.
+ */
+static void supdrvNtProtectVerifyNewChildProtection(PSUPDRVNTPROTECT pNtStub, PSUPDRVNTPROTECT pNtVm, bool fCallerChecks)
+{
+ if ( fCallerChecks
+ && pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtVm->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtVm->u.pParent == pNtStub
+ && pNtStub->u.pChild == pNtVm)
+ {
+ /* Fine, reset the CSRSS hack (fixes ViRobot APT Shield 2.0 issue). */
+ pNtVm->fFirstProcessCreateHandle = true;
+ return;
+ }
+
+ LogRel(("vboxdrv: Misdetected vm stub; hParentPid=%p hChildPid=%p\n", pNtStub->AvlCore.Key, pNtVm->AvlCore.Key));
+ if (pNtStub->enmProcessKind != kSupDrvNtProtectKind_VmProcessConfirmed)
+ supdrvNtProtectUnprotectDeadProcess(pNtVm->AvlCore.Key);
+}
+
+
+/**
+ * Old style callback (since forever).
+ *
+ * @param hParentPid The parent PID.
+ * @param hNewPid The PID of the new child.
+ * @param fCreated TRUE if it's a creation notification,
+ * FALSE if termination.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotify(HANDLE hParentPid, HANDLE hNewPid, BOOLEAN fCreated)
+{
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (fCreated)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ else
+ {
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm, true);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ {
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+ supdrvNtErrorInfoCleanupProcess(hNewPid);
+ }
+}
+
+
+/**
+ * New style callback (Vista SP1+ / w2k8).
+ *
+ * @param pNewProcess The new process.
+ * @param hNewPid The PID of the new process.
+ * @param pInfo Process creation details. NULL if process
+ * termination notification.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotifyEx(PEPROCESS pNewProcess, HANDLE hNewPid, PPS_CREATE_NOTIFY_INFO pInfo)
+{
+ RT_NOREF1(pNewProcess);
+
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (pInfo)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(pInfo->CreatingThreadId.UniqueProcess);
+
+ Log(("vboxdrv/NewProcessEx: ctx=%04zx/%p pid=%04zx ppid=%04zx ctor=%04zx/%04zx rcNt=%#x %.*ls\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ hNewPid, pInfo->ParentProcessId,
+ pInfo->CreatingThreadId.UniqueProcess, pInfo->CreatingThreadId.UniqueThread, pInfo->CreationStatus,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? (size_t)pInfo->ImageFileName->Length / 2 : 0,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? pInfo->ImageFileName->Buffer : NULL));
+
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ /* Parent must be creator. */
+ if (pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, pInfo->ParentProcessId, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ }
+ else
+ {
+ /* Parent must be creator (as above). */
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm,
+ pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ {
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+ supdrvNtErrorInfoCleanupProcess(hNewPid);
+ }
+}
+
+/** @} */
+
+
+/** @name Process Handle Callbacks.
+ * @{ */
+
+/** Process rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOW_PROCESS_RIGHTS \
+ ( PROCESS_TERMINATE \
+ | PROCESS_VM_READ \
+ | PROCESS_QUERY_INFORMATION \
+ | PROCESS_QUERY_LIMITED_INFORMATION \
+ | PROCESS_SUSPEND_RESUME \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+
+/** Evil process rights. */
+# define SUPDRV_NT_EVIL_PROCESS_RIGHTS \
+ ( PROCESS_CREATE_THREAD \
+ | PROCESS_SET_SESSIONID /*?*/ \
+ | PROCESS_VM_OPERATION \
+ | PROCESS_VM_WRITE \
+ | PROCESS_DUP_HANDLE \
+ | PROCESS_CREATE_PROCESS /*?*/ \
+ | PROCESS_SET_QUOTA /*?*/ \
+ | PROCESS_SET_INFORMATION \
+ | PROCESS_SET_LIMITED_INFORMATION /*?*/ \
+ | 0)
+AssertCompile((SUPDRV_NT_ALLOW_PROCESS_RIGHTS & SUPDRV_NT_EVIL_PROCESS_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ProcessHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ /*
+ * Protected? Kludge required for NtOpenProcess calls comming in before
+ * the create process hook triggers on Windows 8.1 (possibly others too).
+ */
+ HANDLE hObjPid = PsGetProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hObjPid);
+ if (!pNtProtect)
+ {
+ HANDLE hParentPid = PsGetProcessInheritedFromUniqueProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hObjPid))
+ {
+ supdrvNtProtectProtectNewStubChild(pNtStub, hObjPid);
+ pNtProtect = supdrvNtProtectLookup(hObjPid);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ /*
+ * Ok, it's a protected process. Strip rights as required or possible.
+ */
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOW_PROCESS_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing itself. */
+ if ((PEPROCESS)pOpInfo->Object == PsGetCurrentProcess())
+ {
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstProcessCreateHandle = false;
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ }
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Allow debuggers full access. */
+ else if (supdrvNtProtectIsWhitelistedDebugger(PsGetCurrentProcess()))
+ {
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstProcessCreateHandle = false;
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s [debugger]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ }
+#endif
+ else
+ {
+ ACCESS_MASK const fDesiredAccess = pOpInfo->Parameters->CreateHandleInformation.DesiredAccess;
+
+ /* Special case 1 on Vista, 7 & 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here.
+
+ Special case 2 on 8.1:
+ The CreateProcess code requires additional rights for
+ something, we'll drop these in the stub code. */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess())
+ && ExGetPreviousMode() != KernelMode)
+ {
+ if ( !pOpInfo->KernelHandle
+ && fDesiredAccess == s_fCsrssStupidDesires)
+ {
+ if (g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3))
+ fAllowedRights |= s_fCsrssStupidDesires;
+ else
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_SET_INFORMATION
+ | PROCESS_SET_LIMITED_INFORMATION
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstProcessCreateHandle = false;
+ }
+
+ /* Special case 3 on 8.1:
+ The interaction between the CreateProcess code and CSRSS.EXE
+ has changed to the better with Windows 8.1. CSRSS.EXE no
+ longer duplicates the process (thread too) handle, but opens
+ it, thus allowing us to do our job. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fCsrssFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ pNtProtect->fCsrssFirstProcessCreateHandle = false;
+ if (fDesiredAccess == s_fCsrssStupidDesires)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for CreateProcess/VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ /* Special case 4, Windows 7, Vista, possibly 8, but not 8.1:
+ The Themes service requires PROCESS_DUP_HANDLE access to our
+ process or we won't get any menus and dialogs will be half
+ unreadable. This is _very_ unfortunate and more work will
+ go into making this more secure. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && fDesiredAccess == 0x1478 /* 6.1.7600.16385 (win7_rtm.090713-1255) */
+ && pNtProtect->fThemesFirstProcessCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsFrigginThemesService(pNtProtect, PsGetCurrentProcess()) )
+ {
+ pNtProtect->fThemesFirstProcessCreateHandle = true; /* Only once! */
+ fAllowedRights |= PROCESS_DUP_HANDLE;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ /* Special case 6a, Windows 10+: AudioDG.exe opens the process with the
+ PROCESS_SET_LIMITED_INFORMATION right. It seems like it need it for
+ some myserious and weirdly placed cpu set management of our process.
+ I'd love to understand what that's all about...
+ Currently playing safe and only grand this right, however limited, to
+ audiodg.exe. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(10, 0)
+ && ( fDesiredAccess == PROCESS_SET_LIMITED_INFORMATION
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION) /* expected fix #1 */
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION) /* expected fix #2 */
+ )
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsSystem32ProcessMatch(PsGetCurrentProcess(), "audiodg.exe") )
+ {
+ fAllowedRights |= PROCESS_SET_LIMITED_INFORMATION;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p/pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ fDesiredAccess, pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ fAllowedRights, fDesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()), ExGetPreviousMode() ));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing itself. */
+ if ( (PEPROCESS)pOpInfo->Object == PsGetCurrentProcess()
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pOpInfo->Object)
+ {
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ ACCESS_MASK const fDesiredAccess = pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess;
+
+ /* Special case 5 on Vista, 7 & 8:
+ This is the CSRSS.EXE end of special case #1. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->cCsrssFirstProcessDuplicateHandle > 0
+ && pOpInfo->KernelHandle == 0
+ && fDesiredAccess == s_fCsrssStupidDesires
+ && pNtProtect->hParentPid
+ == PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()))
+ {
+ if (ASMAtomicDecS32(&pNtProtect->cCsrssFirstProcessDuplicateHandle) >= 0)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS, PROCESS_DUP_HANDLE */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for launching VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ /* Special case 6b, Windows 10+: AudioDG.exe duplicates the handle it opened above. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(10, 0)
+ && ( fDesiredAccess == PROCESS_SET_LIMITED_INFORMATION
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION) /* expected fix #1 */
+ || fDesiredAccess == (PROCESS_SET_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION) /* expected fix #2 */
+ )
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsSystem32ProcessMatch(PsGetCurrentProcess(), "audiodg.exe") )
+ {
+ fAllowedRights |= PROCESS_SET_LIMITED_INFORMATION;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ fDesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE
+ ? pOpInfo->Parameters->CreateHandleInformation.GrantedAccess
+ : pOpInfo->Parameters->DuplicateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | PROCESS_UNKNOWN_4000 /* Seen set on win 8.1 */
+ /*| PROCESS_UNKNOWN_8000 */ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOW_PROCESS_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOW_PROCESS_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+
+/** @} */
+
+
+/** @name Thread Handle Callbacks
+ * @{ */
+
+/* From ntifs.h */
+extern "C" NTKERNELAPI PEPROCESS __stdcall IoThreadToProcess(PETHREAD);
+
+/** Thread rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOWED_THREAD_RIGHTS \
+ ( THREAD_TERMINATE \
+ | THREAD_GET_CONTEXT \
+ | THREAD_QUERY_INFORMATION \
+ | THREAD_QUERY_LIMITED_INFORMATION \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+/** @todo consider THREAD_SET_LIMITED_INFORMATION & THREAD_RESUME */
+
+/** Evil thread rights.
+ * @remarks THREAD_RESUME is not included as it seems to be forced upon us by
+ * Windows 8.1, at least for some processes. We dont' actively
+ * allow it though, just tollerate it when forced to. */
+# define SUPDRV_NT_EVIL_THREAD_RIGHTS \
+ ( THREAD_SUSPEND_RESUME \
+ | THREAD_SET_CONTEXT \
+ | THREAD_SET_INFORMATION \
+ | THREAD_SET_LIMITED_INFORMATION /*?*/ \
+ | THREAD_SET_THREAD_TOKEN /*?*/ \
+ | THREAD_IMPERSONATE /*?*/ \
+ | THREAD_DIRECT_IMPERSONATION /*?*/ \
+ /*| THREAD_RESUME - see remarks. */ \
+ | 0)
+AssertCompile((SUPDRV_NT_EVIL_THREAD_RIGHTS & SUPDRV_NT_ALLOWED_THREAD_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ThreadHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ PEPROCESS pProcess = IoThreadToProcess((PETHREAD)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(pProcess));
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOWED_THREAD_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if (pProcess == PsGetCurrentProcess())
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] self\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Allow debuggers full access. */
+ else if (supdrvNtProtectIsWhitelistedDebugger(PsGetCurrentProcess()))
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s [debugger]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+#endif
+ else
+ {
+ /* Special case 1 on Vista, 7, 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstThreadCreateHandle
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess()) )
+ {
+ if ( !pOpInfo->KernelHandle
+ && pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ fAllowedRights |= s_fCsrssStupidDesires;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+
+ /* Special case 2 on 8.1, possibly also Vista, 7, 8:
+ When creating a process like VBoxTestOGL from the VM process,
+ CSRSS.EXE will try talk to the calling thread and, it
+ appears, impersonate it. We unfortunately need to allow
+ this or there will be no 3D support. Typical DbgPrint:
+ "SXS: BasepCreateActCtx() Calling csrss server failed. Status = 0xc00000a5" */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()), ExGetPreviousMode()));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if ( pProcess == PsGetCurrentProcess()
+ && (PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pProcess)
+ {
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] self\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ /* Special case 3 on Vista, 7, 8:
+ This is the follow up to special case 1. */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && pOpInfo->KernelHandle == 0
+ && ExGetPreviousMode() == UserMode
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s\n",
+ pOpInfo->KernelHandle ? "k" : "", PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ThreadHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL); RT_NOREF1(pvUser);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Parameters->CreateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | THREAD_RESUME /* This seems to be force upon us too with 8.1. */
+ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOWED_THREAD_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOWED_THREAD_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+
+/** @} */
+
+
+/**
+ * Creates a new process protection structure.
+ *
+ * @returns VBox status code.
+ * @param ppNtProtect Where to return the pointer to the structure
+ * on success.
+ * @param hPid The process ID of the process to protect.
+ * @param enmProcessKind The kind of process we're protecting.
+ * @param fLink Whether to link the structure into the tree.
+ */
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid, SUPDRVNTPROTECTKIND enmProcessKind, bool fLink)
+{
+ AssertReturn(g_hNtProtectLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)RTMemAllocZ(sizeof(*pNtProtect));
+ if (!pNtProtect)
+ return VERR_NO_MEMORY;
+
+ pNtProtect->AvlCore.Key = hPid;
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC;
+ pNtProtect->cRefs = 1;
+ pNtProtect->enmProcessKind = enmProcessKind;
+ pNtProtect->hParentPid = NULL;
+ pNtProtect->hOpenTid = NULL;
+ pNtProtect->hCsrssPid = NULL;
+ pNtProtect->pCsrssProcess = NULL;
+
+ if (fLink)
+ {
+ RTSpinlockAcquire(g_hNtProtectLock);
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtProtect->AvlCore);
+ pNtProtect->fInTree = fSuccess;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ if (!fSuccess)
+ {
+ /* Duplicate entry, fail. */
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC_DEAD;
+ LogRel(("supdrvNtProtectCreate: Duplicate (%#x).\n", pNtProtect->AvlCore.Key));
+ RTMemFree(pNtProtect);
+ return VERR_DUPLICATE;
+ }
+ }
+
+ *ppNtProtect = pNtProtect;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a reference to a NT protection structure.
+ *
+ * @param pNtProtect The NT protection structure.
+ */
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect)
+{
+ if (!pNtProtect)
+ return;
+ AssertReturnVoid(pNtProtect->u32Magic == SUPDRVNTPROTECT_MAGIC);
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+ uint32_t cRefs = ASMAtomicDecU32(&pNtProtect->cRefs);
+ if (cRefs != 0)
+ RTSpinlockRelease(g_hNtProtectLock);
+ else
+ {
+ /*
+ * That was the last reference. Remove it from the tree, invalidate it
+ * and free the resources associated with it. Also, release any
+ * child/parent references related to this protection structure.
+ */
+ ASMAtomicWriteU32(&pNtProtect->u32Magic, SUPDRVNTPROTECT_MAGIC_DEAD);
+ if (pNtProtect->fInTree)
+ {
+ PSUPDRVNTPROTECT pRemoved = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pNtProtect->AvlCore.Key);
+ Assert(pRemoved == pNtProtect); RT_NOREF_PV(pRemoved);
+ pNtProtect->fInTree = false;
+ }
+
+ PSUPDRVNTPROTECT pChild = NULL;
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent)
+ {
+ pChild = pNtProtect->u.pChild;
+ if (pChild)
+ {
+ pNtProtect->u.pChild = NULL;
+ pChild->u.pParent = NULL;
+ pChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ uint32_t cChildRefs = ASMAtomicDecU32(&pChild->cRefs);
+ if (!cChildRefs)
+ {
+ Assert(pChild->fInTree);
+ if (pChild->fInTree)
+ {
+ PSUPDRVNTPROTECT pRemovedChild = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pChild->AvlCore.Key);
+ Assert(pRemovedChild == pChild); RT_NOREF_PV(pRemovedChild);
+ pChild->fInTree = false;
+ }
+ }
+ else
+ pChild = NULL;
+ }
+ }
+ else
+ AssertRelease(pNtProtect->enmProcessKind != kSupDrvNtProtectKind_VmProcessUnconfirmed);
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ if (pNtProtect->pCsrssProcess)
+ {
+ ObDereferenceObject(pNtProtect->pCsrssProcess);
+ pNtProtect->pCsrssProcess = NULL;
+ }
+
+ RTMemFree(pNtProtect);
+ if (pChild)
+ RTMemFree(pChild);
+ }
+}
+
+
+/**
+ * Looks up a PID in the NT protect tree.
+ *
+ * @returns Pointer to a NT protection structure (with a referenced) on success,
+ * NULL if not found.
+ * @param hPid The process ID.
+ */
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid)
+{
+ RTSpinlockAcquire(g_hNtProtectLock);
+ PSUPDRVNTPROTECT pFound = (PSUPDRVNTPROTECT)RTAvlPVGet(&g_NtProtectTree, hPid);
+ if (pFound)
+ ASMAtomicIncU32(&pFound->cRefs);
+ RTSpinlockRelease(g_hNtProtectLock);
+ return pFound;
+}
+
+
+/**
+ * Validates a few facts about the stub process when the VM process opens
+ * vboxdrv.
+ *
+ * This makes sure the stub process is still around and that it has neither
+ * debugger nor extra threads in it.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The unconfirmed VM process currently trying to
+ * open vboxdrv.
+ * @param pErrInfo Additional error information.
+ */
+static int supdrvNtProtectVerifyStubForVmProcess(PSUPDRVNTPROTECT pNtProtect, PRTERRINFO pErrInfo)
+{
+ /*
+ * Grab a reference to the parent stub process.
+ */
+ SUPDRVNTPROTECTKIND enmStub = kSupDrvNtProtectKind_Invalid;
+ PSUPDRVNTPROTECT pNtStub = NULL;
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ pNtStub = pNtProtect->u.pParent; /* weak reference. */
+ if (pNtStub)
+ {
+ enmStub = pNtStub->enmProcessKind;
+ if (enmStub == kSupDrvNtProtectKind_StubParent)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pNtStub->cRefs);
+ Assert(cRefs > 0 && cRefs < 1024); RT_NOREF_PV(cRefs);
+ }
+ else
+ pNtStub = NULL;
+ }
+ }
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ /*
+ * We require the stub process to be present.
+ */
+ if (!pNtStub)
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_NOT_FOUND, "Missing stub process (enmStub=%d).", enmStub);
+
+ /*
+ * Open the parent process and thread so we can check for debuggers and unwanted threads.
+ */
+ int rc;
+ PEPROCESS pStubProcess;
+ NTSTATUS rcNt = PsLookupProcessByProcessId(pNtStub->AvlCore.Key, &pStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ HANDLE hStubProcess;
+ rcNt = ObOpenObjectByPointer(pStubProcess, OBJ_KERNEL_HANDLE, NULL /*PassedAccessState*/,
+ 0 /*DesiredAccess*/, *PsProcessType, KernelMode, &hStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ PETHREAD pStubThread;
+ rcNt = PsLookupThreadByThreadId(pNtStub->hOpenTid, &pStubThread);
+ if (NT_SUCCESS(rcNt))
+ {
+ HANDLE hStubThread;
+ rcNt = ObOpenObjectByPointer(pStubThread, OBJ_KERNEL_HANDLE, NULL /*PassedAccessState*/,
+ 0 /*DesiredAccess*/, *PsThreadType, KernelMode, &hStubThread);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Do some simple sanity checking.
+ */
+ rc = supHardNtVpDebugger(hStubProcess, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpThread(hStubProcess, hStubThread, pErrInfo);
+
+ /* Clean up. */
+ rcNt = NtClose(hStubThread); AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_THREAD_OPEN_ERROR,
+ "Error opening stub thread %p (tid %p, pid %p): %#x",
+ pStubThread, pNtStub->hOpenTid, pNtStub->AvlCore.Key, rcNt);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_THREAD_NOT_FOUND,
+ "Failed to locate thread %p in %p: %#x", pNtStub->hOpenTid, pNtStub->AvlCore.Key, rcNt);
+ rcNt = NtClose(hStubProcess); AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_OPEN_ERROR,
+ "Error opening stub process %p (pid %p): %#x", pStubProcess, pNtStub->AvlCore.Key, rcNt);
+ ObDereferenceObject(pStubProcess);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_STUB_NOT_FOUND,
+ "Failed to locate stub process %p: %#x", pNtStub->AvlCore.Key, rcNt);
+
+ supdrvNtProtectRelease(pNtStub);
+ return rc;
+}
+
+
+static const char *supdrvNtProtectHandleTypeIndexToName(ULONG idxType, char *pszName, size_t cbName)
+{
+ /*
+ * Query the object types.
+ */
+ uint32_t cbBuf = _8K;
+ uint8_t *pbBuf = (uint8_t *)RTMemAllocZ(_8K);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQueryObject(NULL, ObjectTypesInformation, pbBuf, cbBuf, &cbNeeded);
+ while (rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + 256, _64K);
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAllocZ(cbBuf);
+ if (pbBuf)
+ rcNt = NtQueryObject(NULL, ObjectTypesInformation, pbBuf, cbBuf, &cbNeeded);
+ else
+ break;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ Assert(cbNeeded <= cbBuf);
+
+ POBJECT_TYPES_INFORMATION pObjTypes = (OBJECT_TYPES_INFORMATION *)pbBuf;
+ POBJECT_TYPE_INFORMATION pCurType = &pObjTypes->FirstType;
+ ULONG cLeft = pObjTypes->NumberOfTypes;
+ while (cLeft-- > 0 && (uintptr_t)&pCurType[1] - (uintptr_t)pbBuf < cbNeeded)
+ {
+ if (pCurType->TypeIndex == idxType)
+ {
+ PCRTUTF16 const pwszSrc = pCurType->TypeName.Buffer;
+ AssertBreak(pwszSrc);
+ size_t idxName = pCurType->TypeName.Length / sizeof(RTUTF16);
+ AssertBreak(idxName > 0);
+ AssertBreak(idxName < 128);
+ if (idxName >= cbName)
+ idxName = cbName - 1;
+ pszName[idxName] = '\0';
+ while (idxName-- > 0)
+ pszName[idxName] = (char )pwszSrc[idxName];
+ RTMemFree(pbBuf);
+ return pszName;
+ }
+
+ /* next */
+ pCurType = (POBJECT_TYPE_INFORMATION)( (uintptr_t)pCurType->TypeName.Buffer
+ + RT_ALIGN_32(pCurType->TypeName.MaximumLength, sizeof(uintptr_t)));
+ }
+ }
+
+ RTMemFree(pbBuf);
+ return "unknown";
+}
+
+
+/**
+ * Worker for supdrvNtProtectVerifyProcess that verifies the handles to a VM
+ * process and its thread.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure for getting information
+ * about special processes.
+ * @param pErrInfo Where to return additional error details.
+ */
+static int supdrvNtProtectRestrictHandlesToProcessAndThread(PSUPDRVNTPROTECT pNtProtect, PRTERRINFO pErrInfo)
+{
+ /*
+ * What to protect.
+ */
+ PEPROCESS pProtectedProcess = PsGetCurrentProcess();
+ HANDLE hProtectedPid = PsGetProcessId(pProtectedProcess);
+ PETHREAD pProtectedThread = PsGetCurrentThread();
+ AssertReturn(pNtProtect->AvlCore.Key == hProtectedPid, VERR_INTERNAL_ERROR_5);
+
+ /*
+ * Take a snapshot of all the handles in the system.
+ * Note! The 32 bytes on the size of to counteract the allocation header
+ * that rtR0MemAllocEx slaps on everything.
+ */
+ uint32_t cbBuf = _256K - 32;
+ uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ if (!NT_SUCCESS(rcNt))
+ {
+ while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbBuf <= 32U*_1M)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + _4K, _64K) - 32;
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Error allocating %zu bytes for querying handles.", cbBuf);
+ rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMemFree(pbBuf);
+ return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
+ "NtQuerySystemInformation/SystemExtendedHandleInformation failed: %#x\n", rcNt);
+ }
+ }
+
+ /*
+ * Walk the information and look for handles to the two objects we're protecting.
+ */
+ int rc = VINF_SUCCESS;
+# ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ HANDLE idLastDebugger = (HANDLE)~(uintptr_t)0;
+# endif
+
+ uint32_t cCsrssProcessHandles = 0;
+ uint32_t cSystemProcessHandles = 0;
+ uint32_t cEvilProcessHandles = 0;
+ uint32_t cBenignProcessHandles = 0;
+
+ uint32_t cCsrssThreadHandles = 0;
+ uint32_t cEvilThreadHandles = 0;
+ uint32_t cBenignThreadHandles = 0;
+
+ uint32_t cEvilInheritableHandles = 0;
+ uint32_t cBenignInheritableHandles = 0;
+ char szTmpName[32];
+
+ SYSTEM_HANDLE_INFORMATION_EX const *pInfo = (SYSTEM_HANDLE_INFORMATION_EX const *)pbBuf;
+ ULONG_PTR i = pInfo->NumberOfHandles;
+ AssertRelease(RT_UOFFSETOF_DYN(SYSTEM_HANDLE_INFORMATION_EX, Handles[i]) == cbNeeded);
+ while (i-- > 0)
+ {
+ const char *pszType;
+ SYSTEM_HANDLE_ENTRY_INFO_EX const *pHandleInfo = &pInfo->Handles[i];
+ if (pHandleInfo->Object == pProtectedProcess)
+ {
+ /* Handles within the protected process are fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_PROCESS_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignProcessHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil process handle.
+ See the special cases in the hook code. */
+ if ( cCsrssProcessHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssProcessHandles++;
+ continue;
+ }
+
+ /* The system process is allowed having two open process handle in
+ Windows 8.1 and later, and one in earlier. This is probably a
+ little overly paranoid as I think we can safely trust the
+ system process... */
+ if ( cSystemProcessHandles < (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3) ? UINT32_C(2) : UINT32_C(1))
+ && pHandleInfo->UniqueProcessId == PsGetProcessId(PsInitialSystemProcess))
+ {
+ cSystemProcessHandles++;
+ continue;
+ }
+
+ cEvilProcessHandles++;
+ pszType = "process";
+ }
+ else if (pHandleInfo->Object == pProtectedThread)
+ {
+ /* Handles within the protected process is fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_THREAD_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignThreadHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil handle to the primary thread
+ for LPC purposes. See the hook for special case. */
+ if ( cCsrssThreadHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssThreadHandles++;
+ continue;
+ }
+
+ cEvilThreadHandles++;
+ pszType = "thread";
+ }
+ else if ( (pHandleInfo->HandleAttributes & OBJ_INHERIT)
+ && pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ /* No handles should be marked inheritable, except files and two events.
+ Handles to NT 'directory' objects are especially evil, because of
+ KnownDlls faking. See bugref{10294} for details.
+
+ Correlating the ObjectTypeIndex to a type is complicated, so instead
+ we try referecing the handle and check the type that way. So, only
+ file and events objects are allowed to be marked inheritable at the
+ moment. Add more in whitelist fashion if needed. */
+ void *pvObject = NULL;
+ rcNt = ObReferenceObjectByHandle(pHandleInfo->HandleValue, 0, *IoFileObjectType, KernelMode, &pvObject, NULL);
+ if (rcNt == STATUS_OBJECT_TYPE_MISMATCH)
+ rcNt = ObReferenceObjectByHandle(pHandleInfo->HandleValue, 0, *ExEventObjectType, KernelMode, &pvObject, NULL);
+ if (NT_SUCCESS(rcNt))
+ {
+ ObDereferenceObject(pvObject);
+ cBenignInheritableHandles++;
+ continue;
+ }
+
+ if (rcNt != STATUS_OBJECT_TYPE_MISMATCH)
+ {
+ cBenignInheritableHandles++;
+ continue;
+ }
+
+ cEvilInheritableHandles++;
+ pszType = supdrvNtProtectHandleTypeIndexToName(pHandleInfo->ObjectTypeIndex, szTmpName, sizeof(szTmpName));
+ }
+ else
+ continue;
+
+# ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /* Ignore whitelisted debuggers. */
+ if (pHandleInfo->UniqueProcessId == idLastDebugger)
+ continue;
+ PEPROCESS pDbgProc;
+ rcNt = PsLookupProcessByProcessId(pHandleInfo->UniqueProcessId, &pDbgProc);
+ if (NT_SUCCESS(rcNt))
+ {
+ bool fIsDebugger = supdrvNtProtectIsWhitelistedDebugger(pDbgProc);
+ ObDereferenceObject(pDbgProc);
+ if (fIsDebugger)
+ {
+ idLastDebugger = pHandleInfo->UniqueProcessId;
+ continue;
+ }
+ }
+# endif
+
+ /* Found evil handle. Currently ignoring on pre-Vista. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if ( g_uNtVerCombined >= SUP_NT_VER_VISTA
+# else
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0)
+# endif
+ || g_pfnObRegisterCallbacks)
+ {
+ LogRel(("vboxdrv: Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)\n",
+ pHandleInfo->UniqueProcessId, pHandleInfo->HandleValue,
+ pHandleInfo->GrantedAccess, pHandleInfo->HandleAttributes, pszType, pHandleInfo->ObjectTypeIndex));
+ rc = RTErrInfoAddF(pErrInfo, VERR_SUPDRV_HARDENING_EVIL_HANDLE,
+ *pErrInfo->pszMsg
+ ? "\nFound evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)"
+ : "Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s (%u)",
+ pHandleInfo->UniqueProcessId, pHandleInfo->HandleValue,
+ pHandleInfo->GrantedAccess, pHandleInfo->HandleAttributes, pszType, pHandleInfo->ObjectTypeIndex);
+
+ /* Try add the process name. */
+ PEPROCESS pOffendingProcess;
+ rcNt = PsLookupProcessByProcessId(pHandleInfo->UniqueProcessId, &pOffendingProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ const char *pszName = (const char *)PsGetProcessImageFileName(pOffendingProcess);
+ if (pszName && *pszName)
+ rc = RTErrInfoAddF(pErrInfo, rc, " [%s]", pszName);
+
+ ObDereferenceObject(pOffendingProcess);
+ }
+ }
+ }
+
+ RTMemFree(pbBuf);
+ return rc;
+}
+
+
+/**
+ * Checks if the current process checks out as a VM process stub.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure. This is upgraded to a
+ * final protection kind (state) on success.
+ */
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect)
+{
+ AssertReturn(PsGetProcessId(PsGetCurrentProcess()) == pNtProtect->AvlCore.Key, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Do the verification. The handle restriction checks are only preformed
+ * on VM processes.
+ */
+ int rc = VINF_SUCCESS;
+ PSUPDRVNTERRORINFO pErrorInfo = (PSUPDRVNTERRORINFO)RTMemAllocZ(sizeof(*pErrorInfo));
+ if (RT_SUCCESS(rc))
+ {
+ pErrorInfo->hProcessId = PsGetCurrentProcessId();
+ pErrorInfo->hThreadId = PsGetCurrentThreadId();
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, pErrorInfo->szErrorInfo, sizeof(pErrorInfo->szErrorInfo));
+
+ if (pNtProtect->enmProcessKind >= kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ rc = supdrvNtProtectRestrictHandlesToProcessAndThread(pNtProtect, &ErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_VERIFY_ONLY, 0 /*fFlags*/,
+ NULL /*pcFixes*/, &ErrInfo);
+ if (RT_SUCCESS(rc) && pNtProtect->enmProcessKind >= kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ rc = supdrvNtProtectVerifyStubForVmProcess(pNtProtect, &ErrInfo);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /*
+ * Upgrade and return.
+ */
+ HANDLE hOpenTid = PsGetCurrentThreadId();
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /* Stub process verficiation is pretty much straight forward. */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ {
+ pNtProtect->enmProcessKind = RT_SUCCESS(rc) ? kSupDrvNtProtectKind_StubSpawning : kSupDrvNtProtectKind_StubDead;
+ pNtProtect->hOpenTid = hOpenTid;
+ }
+ /* The VM process verification is a little bit more complicated
+ because we need to drop the parent process reference as well. */
+ else if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ AssertRelease(pNtProtect->cRefs >= 2); /* Parent + Caller */
+ PSUPDRVNTPROTECT pParent = pNtProtect->u.pParent;
+ AssertRelease(pParent);
+ AssertRelease(pParent->u.pParent == pNtProtect);
+ AssertRelease(pParent->enmProcessKind == kSupDrvNtProtectKind_StubParent);
+ pParent->u.pParent = NULL;
+
+ pNtProtect->u.pParent = NULL;
+ ASMAtomicDecU32(&pNtProtect->cRefs);
+
+ if (RT_SUCCESS(rc))
+ {
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessConfirmed;
+ pNtProtect->hOpenTid = hOpenTid;
+ }
+ else
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /* Since the stub and VM processes are only supposed to have one thread,
+ we're not supposed to be subject to any races from within the processes.
+
+ There is a race between VM process verification and the stub process
+ exiting, though. We require the stub process to be alive until the new
+ VM process has made it thru the validation. So, when the stub
+ terminates the notification handler will change the state of both stub
+ and VM process to dead.
+
+ Also, I'm not entirely certain where the process
+ termination notification is triggered from, so that can theorically
+ create a race in both cases. */
+ else
+ {
+ AssertReleaseMsg( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubDead
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessDead,
+ ("enmProcessKind=%d rc=%Rrc\n", pNtProtect->enmProcessKind, rc));
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE; /* There should be no races here. */
+ }
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ /*
+ * Free error info on success, keep it on failure.
+ */
+ if (RT_SUCCESS(rc))
+ RTMemFree(pErrorInfo);
+ else if (pErrorInfo)
+ {
+ pErrorInfo->cchErrorInfo = (uint32_t)strlen(pErrorInfo->szErrorInfo);
+ if (!pErrorInfo->cchErrorInfo)
+ pErrorInfo->cchErrorInfo = (uint32_t)RTStrPrintf(pErrorInfo->szErrorInfo, sizeof(pErrorInfo->szErrorInfo),
+ "supdrvNtProtectVerifyProcess: rc=%d", rc);
+ RTLogWriteDebugger(pErrorInfo->szErrorInfo, pErrorInfo->cchErrorInfo);
+
+ int rc2 = RTSemMutexRequest(g_hErrorInfoLock, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc2))
+ {
+ pErrorInfo->uCreatedMsTs = RTTimeMilliTS();
+
+ /* Free old entries. */
+ PSUPDRVNTERRORINFO pCur;
+ while ( (pCur = RTListGetFirst(&g_ErrorInfoHead, SUPDRVNTERRORINFO, ListEntry)) != NULL
+ && (int64_t)(pErrorInfo->uCreatedMsTs - pCur->uCreatedMsTs) > 60000 /*60sec*/)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+
+ /* Insert our new entry. */
+ RTListAppend(&g_ErrorInfoHead, &pErrorInfo->ListEntry);
+
+ RTSemMutexRelease(g_hErrorInfoLock);
+ }
+ else
+ RTMemFree(pErrorInfo);
+ }
+
+ return rc;
+}
+
+
+# ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+
+/**
+ * Checks if the current process is being debugged.
+ * @return @c true if debugged, @c false if not.
+ */
+static bool supdrvNtIsDebuggerAttached(void)
+{
+ return PsIsProcessBeingDebugged(PsGetCurrentProcess()) != FALSE;
+}
+
+# endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/**
+ * Terminates the hardening bits.
+ */
+static void supdrvNtProtectTerm(void)
+{
+ /*
+ * Stop intercepting process and thread handle creation calls.
+ */
+ if (g_pvObCallbacksCookie)
+ {
+ g_pfnObUnRegisterCallbacks(g_pvObCallbacksCookie);
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Stop intercepting process creation and termination notifications.
+ */
+ NTSTATUS rcNt;
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ AssertMsg(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+
+ Assert(g_NtProtectTree == NULL);
+
+ /*
+ * Clean up globals.
+ */
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+
+ RTSemMutexDestroy(g_hErrorInfoLock);
+ g_hErrorInfoLock = NIL_RTSEMMUTEX;
+
+ PSUPDRVNTERRORINFO pCur;
+ while ((pCur = RTListGetFirst(&g_ErrorInfoHead, SUPDRVNTERRORINFO, ListEntry)) != NULL)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+
+ supHardenedWinTermImageVerifier();
+}
+
+# ifdef RT_ARCH_X86
+DECLASM(void) supdrvNtQueryVirtualMemory_0xAF(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB0(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB1(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB2(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB3(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB4(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB5(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB6(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB7(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB8(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB9(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBA(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBB(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBC(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBD(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBE(void);
+# elif defined(RT_ARCH_AMD64)
+DECLASM(void) supdrvNtQueryVirtualMemory_0x1F(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x20(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x21(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x22(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x23(void);
+extern "C" NTSYSAPI NTSTATUS NTAPI ZwRequestWaitReplyPort(HANDLE, PVOID, PVOID);
+# endif
+
+
+/**
+ * Initalizes the hardening bits.
+ *
+ * @returns NT status code.
+ */
+static NTSTATUS supdrvNtProtectInit(void)
+{
+ /*
+ * Initialize the globals.
+ */
+
+ /* The NT version. */
+ ULONG uMajor, uMinor, uBuild;
+ PsGetVersion(&uMajor, &uMinor, &uBuild, NULL);
+ g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(uMajor, uMinor, uBuild, 0, 0);
+
+ /* Resolve methods we want but isn't available everywhere. */
+ UNICODE_STRING RoutineName;
+
+ RtlInitUnicodeString(&RoutineName, L"ObGetObjectType");
+ g_pfnObGetObjectType = (PFNOBGETOBJECTTYPE)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ObRegisterCallbacks");
+ g_pfnObRegisterCallbacks = (PFNOBREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ObUnRegisterCallbacks");
+ g_pfnObUnRegisterCallbacks = (PFNOBUNREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsSetCreateProcessNotifyRoutineEx");
+ g_pfnPsSetCreateProcessNotifyRoutineEx = (PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsReferenceProcessFilePointer");
+ g_pfnPsReferenceProcessFilePointer = (PFNPSREFERENCEPROCESSFILEPOINTER)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsIsProtectedProcessLight");
+ g_pfnPsIsProtectedProcessLight = (PFNPSISPROTECTEDPROCESSLIGHT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ZwAlpcCreatePort");
+ g_pfnZwAlpcCreatePort = (PFNZWALPCCREATEPORT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ZwQueryVirtualMemory"); /* Yes, using Zw version here. */
+ g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)MmGetSystemRoutineAddress(&RoutineName);
+ if (!g_pfnNtQueryVirtualMemory && g_uNtVerCombined < SUP_NT_VER_VISTA)
+ {
+ /* XP & W2K3 doesn't have this function exported, so we've cooked up a
+ few alternative in the assembly helper file that uses the code in
+ ZwReadFile with a different eax value. We figure the syscall number
+ by inspecting ZwQueryVolumeInformationFile as it's the next number. */
+# ifdef RT_ARCH_X86
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwQueryVolumeInformationFile;
+ if (*pbCode == 0xb8) /* mov eax, dword */
+ switch (*(uint32_t const *)&pbCode[1])
+ {
+ case 0xb0: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xAF; break; /* just in case */
+ case 0xb1: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB0; break; /* just in case */
+ case 0xb2: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB1; break; /* just in case */
+ case 0xb3: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* XP SP3 */
+ case 0xb4: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* just in case */
+ case 0xb5: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB3; break; /* just in case */
+ case 0xb6: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB4; break; /* just in case */
+ case 0xb7: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB5; break; /* just in case */
+ case 0xb8: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB6; break; /* just in case */
+ case 0xb9: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB7; break; /* just in case */
+ case 0xba: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB8; break; /* just in case */
+ case 0xbb: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBA; break; /* W2K3 R2 SP2 */
+ case 0xbc: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBB; break; /* just in case */
+ case 0xbd: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBC; break; /* just in case */
+ case 0xbe: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBD; break; /* just in case */
+ case 0xbf: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBE; break; /* just in case */
+ }
+# elif defined(RT_ARCH_AMD64)
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwRequestWaitReplyPort;
+ if ( pbCode[ 0] == 0x48 /* mov rax, rsp */
+ && pbCode[ 1] == 0x8b
+ && pbCode[ 2] == 0xc4
+ && pbCode[ 3] == 0xfa /* cli */
+ && pbCode[ 4] == 0x48 /* sub rsp, 10h */
+ && pbCode[ 5] == 0x83
+ && pbCode[ 6] == 0xec
+ && pbCode[ 7] == 0x10
+ && pbCode[ 8] == 0x50 /* push rax */
+ && pbCode[ 9] == 0x9c /* pushfq */
+ && pbCode[10] == 0x6a /* push 10 */
+ && pbCode[11] == 0x10
+ && pbCode[12] == 0x48 /* lea rax, [nt!KiServiceLinkage] */
+ && pbCode[13] == 0x8d
+ && pbCode[14] == 0x05
+ && pbCode[19] == 0x50 /* push rax */
+ && pbCode[20] == 0xb8 /* mov eax,1fh <- the syscall no. */
+ /*&& pbCode[21] == 0x1f*/
+ && pbCode[22] == 0x00
+ && pbCode[23] == 0x00
+ && pbCode[24] == 0x00
+ && pbCode[25] == 0xe9 /* jmp KiServiceInternal */
+ )
+ {
+ uint8_t const *pbKiServiceInternal = &pbCode[30] + *(int32_t const *)&pbCode[26];
+ uint8_t const *pbKiServiceLinkage = &pbCode[19] + *(int32_t const *)&pbCode[15];
+ if (*pbKiServiceLinkage == 0xc3)
+ {
+ g_pfnKiServiceInternal = (PFNRT)pbKiServiceInternal;
+ g_pfnKiServiceLinkage = (PFNRT)pbKiServiceLinkage;
+ switch (pbCode[21])
+ {
+ case 0x1e: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x1F; break;
+ case 0x1f: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x20; break;
+ case 0x20: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x21; break;
+ case 0x21: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x22; break;
+ case 0x22: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x23; break;
+ }
+ }
+ }
+# endif
+ }
+ if (!g_pfnNtQueryVirtualMemory)
+ {
+ LogRel(("vboxdrv: Cannot locate ZwQueryVirtualMemory in ntoskrnl, nor were we able to cook up a replacement.\n"));
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+# ifdef VBOX_STRICT
+ if ( g_uNtVerCombined >= SUP_NT_VER_W70
+ && ( g_pfnObGetObjectType == NULL
+ || g_pfnZwAlpcCreatePort == NULL) )
+ {
+ LogRel(("vboxdrv: g_pfnObGetObjectType=%p g_pfnZwAlpcCreatePort=%p.\n", g_pfnObGetObjectType, g_pfnZwAlpcCreatePort));
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+# endif
+
+ /* LPC object type. */
+ g_pAlpcPortObjectType1 = *LpcPortObjectType;
+
+ /* The spinlock protecting our structures. */
+ int rc = RTSpinlockCreate(&g_hNtProtectLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "NtProtectLock");
+ if (RT_FAILURE(rc))
+ return VBoxDrvNtErr2NtStatus(rc);
+ g_NtProtectTree = NULL;
+
+ NTSTATUS rcNt;
+
+ /* The mutex protecting the error information. */
+ RTListInit(&g_ErrorInfoHead);
+ rc = RTSemMutexCreate(&g_hErrorInfoLock);
+ if (RT_SUCCESS(rc))
+ {
+ /* Image stuff + certificates. */
+ rc = supHardenedWinInitImageVerifier(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Intercept process creation and termination.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, FALSE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, FALSE /*fRemove*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Intercept process and thread handle creation calls.
+ * The preferred method is only available on Vista SP1+.
+ */
+ if (g_pfnObRegisterCallbacks && g_pfnObUnRegisterCallbacks)
+ {
+ static OB_OPERATION_REGISTRATION s_aObOperations[] =
+ {
+ {
+ 0, /* PsProcessType - imported, need runtime init, better do it explicitly. */
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ProcessHandlePre,
+ supdrvNtProtectCallback_ProcessHandlePost,
+ },
+ {
+ 0, /* PsThreadType - imported, need runtime init, better do it explicitly. */
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ThreadHandlePre,
+ supdrvNtProtectCallback_ThreadHandlePost,
+ },
+ };
+ s_aObOperations[0].ObjectType = PsProcessType;
+ s_aObOperations[1].ObjectType = PsThreadType;
+
+ static OB_CALLBACK_REGISTRATION s_ObCallbackReg =
+ {
+ /* .Version = */ OB_FLT_REGISTRATION_VERSION,
+ /* .OperationRegistrationCount = */ RT_ELEMENTS(s_aObOperations),
+ /* .Altitude.Length = */ { 0,
+ /* .Altitude.MaximumLength = */ 0,
+ /* .Altitude.Buffer = */ NULL },
+ /* .RegistrationContext = */ NULL,
+ /* .OperationRegistration = */ &s_aObOperations[0]
+ };
+ static WCHAR const *s_apwszAltitudes[] = /** @todo get a valid number */
+ {
+ L"48596.98940", L"46935.19485", L"49739.39704", L"40334.74976",
+ L"66667.98940", L"69888.19485", L"69889.39704", L"60364.74976",
+ L"85780.98940", L"88978.19485", L"89939.39704", L"80320.74976",
+ L"329879.98940", L"326787.19485", L"328915.39704", L"320314.74976",
+ };
+
+ rcNt = STATUS_FLT_INSTANCE_ALTITUDE_COLLISION;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszAltitudes) && rcNt == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION; i++)
+ {
+ s_ObCallbackReg.Altitude.Buffer = (WCHAR *)s_apwszAltitudes[i];
+ s_ObCallbackReg.Altitude.Length = (uint16_t)RTUtf16Len(s_apwszAltitudes[i]) * sizeof(WCHAR);
+ s_ObCallbackReg.Altitude.MaximumLength = s_ObCallbackReg.Altitude.Length + sizeof(WCHAR);
+
+ rcNt = g_pfnObRegisterCallbacks(&s_ObCallbackReg, &g_pvObCallbacksCookie);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Happy ending.
+ */
+ return STATUS_SUCCESS;
+ }
+ }
+ LogRel(("vboxdrv: ObRegisterCallbacks failed with rcNt=%#x\n", rcNt));
+ g_pvObCallbacksCookie = NULL;
+ }
+ else
+ {
+ /*
+ * For the time being, we do not implement extra process
+ * protection on pre-Vista-SP1 systems as they are lacking
+ * necessary KPIs. XP is end of life, we do not wish to
+ * spend more time on it, so we don't put up a fuss there.
+ * Vista users without SP1 can install SP1 (or later), darn it,
+ * so refuse to load.
+ */
+ /** @todo Hack up an XP solution - will require hooking kernel APIs or doing bad
+ * stuff to a couple of object types. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
+# else
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
+# endif
+ {
+ DbgPrint("vboxdrv: ObRegisterCallbacks was not found. Please make sure you got the latest updates and service packs installed\n");
+ rcNt = STATUS_SXS_VERSION_CONFLICT;
+ }
+ else
+ {
+ Log(("vboxdrv: ObRegisterCallbacks was not found; ignored pre-Vista\n"));
+ return rcNt = STATUS_SUCCESS;
+ }
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Drop process create/term notifications.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ }
+ else
+ LogRel(("vboxdrv: PsSetCreateProcessNotifyRoutine%s failed with rcNt=%#x\n",
+ g_pfnPsSetCreateProcessNotifyRoutineEx ? "Ex" : "", rcNt));
+ supHardenedWinTermImageVerifier();
+ }
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+
+ RTSemMutexDestroy(g_hErrorInfoLock);
+ g_hErrorInfoLock = NIL_RTSEMMUTEX;
+ }
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+ return rcNt;
+}
+
+#endif /* VBOX_WITH_HARDENING */
+