SUPDrv-win.cpp revision ce6849778032d3a6cc99c3a78db50734704bb8e8
/* $Id$ */
/** @file
* VBoxDrv - The VirtualBox Support Driver - Windows NT specifics.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define IPRT_NT_MAP_TO_ZW
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "../SUPDrvInternal.h"
#include <excpt.h>
#include <ntimage.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.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. */
do { if ((a_pDevObj) == g_pDevObjStub) \
} while (0)
/** Macro for checking for deflecting calls to the stub and error info
* devices. */
} while (0)
#else
# 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
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Device extension used by VBoxDrvU.
*/
typedef struct SUPDRVDEVEXTUSR
{
/** Global cookie (same location as in SUPDRVDEVEXT, different value). */
/** Pointer to the main driver extension. */
/** Pointer to the VBoxDrvU device extension. */
typedef SUPDRVDEVEXTUSR *PSUPDRVDEVEXTUSR;
/** Value of SUPDRVDEVEXTUSR::u32Cookie. */
/** Get the main device extension. */
#define SUPDRVNT_GET_DEVEXT(pDevObj) \
( pDevObj != g_pDevObjUsr \
#ifdef VBOX_WITH_HARDENING
/**
* Device extension used by VBoxDrvStub.
*/
typedef struct SUPDRVDEVEXTSTUB
{
/** Common header. */
/** Pointer to the VBoxDrvStub device extension. */
typedef SUPDRVDEVEXTSTUB *PSUPDRVDEVEXTSTUB;
/** Value of SUPDRVDEVEXTSTUB::Common.u32Cookie. */
/**
* Device extension used by VBoxDrvErrorInfo.
*/
typedef struct SUPDRVDEVEXTERRORINFO
{
/** Common header. */
/** Pointer to the VBoxDrvErrorInfo device extension. */
typedef SUPDRVDEVEXTERRORINFO *PSUPDRVDEVEXTERRORINFO;
/** Value of SUPDRVDEVEXTERRORINFO::Common.u32Cookie. */
/**
* Error info for a failed VBoxDrv or VBoxDrvStub open attempt.
*/
typedef struct SUPDRVNTERRORINFO
{
/** The list entry (in g_ErrorInfoHead). */
/** The ID of the process this error info belongs to. */
/** The ID of the thread owning this info. */
/** Milliseconds createion timestamp (for cleaning up). */
/** Number of bytes of valid info. */
/** The error info. */
char szErrorInfo[2048];
/** Pointer to error info. */
typedef SUPDRVNTERRORINFO *PSUPDRVNTERRORINFO;
/**
* The kind of process we're protecting.
*/
typedef enum SUPDRVNTPROTECTKIND
{
/** Stub process protection while performing process verification.
* Next: StubSpawning (or free) */
/** Stub process protection before it creates the VM process.
* Next: StubParent, StubDead. */
/** Stub process protection while having a VM process as child.
* Next: StubDead */
/** Dead stub process. */
/** Potential VM process.
* Next: VmProcessConfirmed, VmProcessDead. */
/** Confirmed VM process.
* Next: VmProcessDead. */
/** Dead VM process. */
/** End of valid protection kinds. */
/**
* A NT process protection structure.
*/
typedef struct SUPDRVNTPROTECT
{
/** The AVL node core structure. The process ID is the pid. */
/** Magic value (SUPDRVNTPROTECT_MAGIC). */
/** Reference counter. */
/** The kind of process we're protecting. */
SUPDRVNTPROTECTKIND volatile enmProcessKind;
/** 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. */
/** The TID of the thread opening VBoxDrv or VBoxDrvStub, NULL if not opened. */
/** The PID of the CSRSS process associated with this process. */
/** Pointer to the CSRSS process structure (referenced). */
/** 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;
/** Pointer to a NT process protection record. */
typedef SUPDRVNTPROTECT *PSUPDRVNTPROTECT;
/** The SUPDRVNTPROTECT::u32Magic value (Robert A. Heinlein). */
/** The SUPDRVNTPROTECT::u32Magic value of a dead structure. */
/** Pointer to ObGetObjectType. */
/** Pointer to ObRegisterCallbacks. */
/** Pointer to ObUnregisterCallbacks. */
/** Pointer to PsSetCreateProcessNotifyRoutineEx. */
typedef NTSTATUS (NTAPI *PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)(PCREATE_PROCESS_NOTIFY_ROUTINE_EX, BOOLEAN);
/** Pointer to PsReferenceProcessFilePointer. */
/** Pointer to PsIsProtectedProcessLight. */
/** Pointer to ZwAlpcCreatePort. */
typedef NTSTATUS (NTAPI *PFNZWALPCCREATEPORT)(PHANDLE, POBJECT_ATTRIBUTES, struct _ALPC_PORT_ATTRIBUTES *);
#endif /* VBOX_WITH_HARDENINIG */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef VBOXDRV_WITH_FAST_IO
static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
#endif
static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
static VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2);
#ifdef VBOX_WITH_HARDENING
static NTSTATUS supdrvNtProtectInit(void);
static void supdrvNtProtectTerm(void);
static bool supdrvNtIsDebuggerAttached(void);
#endif
/*******************************************************************************
* Exported Functions *
*******************************************************************************/
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Pointer to the system device instance. */
/** Pointer to the user device instance. */
#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 */
#ifdef VBOX_WITH_HARDENING
/** Pointer to the stub device instance. */
/** Spinlock protecting g_NtProtectTree as well as the releasing of protection
* structures. */
/** AVL tree of SUPDRVNTPROTECT structures. */
/** Cookie returned by ObRegisterCallbacks for the callbacks. */
/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
uint32_t g_uNtVerCombined = 0;
/** Pointer to ObGetObjectType if available.. */
/** Pointer to ObRegisterCallbacks if available.. */
/** Pointer to ObUnregisterCallbacks if available.. */
/** Pointer to PsSetCreateProcessNotifyRoutineEx if available.. */
/** Pointer to PsReferenceProcessFilePointer if available. */
/** Pointer to PsIsProtectedProcessLight. */
/** Pointer to ZwAlpcCreatePort. */
# ifdef RT_ARCH_AMD64
extern "C" {
/** Pointer to KiServiceLinkage (used to fake missing ZwQueryVirtualMemory on
* XP64 / W2K3-64). */
/** Pointer to KiServiceInternal (used to fake missing ZwQueryVirtualMemory on
* XP64 / W2K3-64) */
}
# endif
/** The primary ALPC port object type. (LpcPortObjectType at init time.) */
/** The secondary ALPC port object type. (Sampled at runtime.) */
/** Pointer to the error information device instance. */
/** Fast mutex semaphore protecting the error info list. */
/** 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.
*/
{
/*
* System device.
*/
NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjSys);
if (NT_SUCCESS(rcNt))
{
/*
* User device.
*/
rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTUSR), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjUsr);
if (NT_SUCCESS(rcNt))
{
#ifdef VBOX_WITH_HARDENING
/*
* Hardened stub device.
*/
rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTSTUB), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjStub);
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
/*
* Hardened error information device.
*/
rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTERRORINFO), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE,
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
#endif
/* Done. */
return rcNt;
#ifdef VBOX_WITH_HARDENING
}
/* Bail out. */
}
}
/* Bail out. */
g_pDevObjUsr = NULL;
}
#endif
g_pDevObjUsr = NULL;
}
g_pDevObjSys = NULL;
}
return rcNt;
}
/**
* Destroys the devices and links created by vboxdrvNtCreateDevices.
*/
static void vboxdrvNtDestroyDevices(void)
{
if (g_pDevObjUsr)
{
}
#ifdef VBOX_WITH_HARDENING
if (g_pDevObjStub)
{
}
if (g_pDevObjErrorInfo)
{
}
#endif
#ifdef VBOX_WITH_HARDENING
#endif
g_pDevObjUsr = NULL;
g_pDevObjSys = NULL;
}
/**
* Driver entry point.
*
* @returns appropriate status code.
* @param pDrvObj Pointer to driver object.
* @param pRegPath Registry base path.
*/
{
/*
* Sanity checks.
*/
#ifdef VBOXDRV_WITH_FAST_IO
{
DbgPrint("VBoxDrv: FastIoDeviceControl=%p instead of %p\n",
return STATUS_INTERNAL_ERROR;
}
#endif
/*
* Initialize the runtime (IPRT).
*/
if (RT_SUCCESS(vrc))
{
Log(("VBoxDrv::DriverEntry\n"));
#ifdef VBOX_WITH_HARDENING
/*
* Initialize process protection.
*/
rcNt = supdrvNtProtectInit();
if (NT_SUCCESS(rcNt))
#endif
{
/*
* Create device.
* (That means creating a device object and a symbolic link so the DOS
* subsystems (OS/2, win32, ++) can access the device.)
*/
if (NT_SUCCESS(rcNt))
{
/*
* Initialize the device extension.
*/
if (!vrc)
{
/*
* Setup the driver entry points in pDrvObj.
*/
#ifdef VBOXDRV_WITH_FAST_IO
/* Fast I/O to speed up guest execution roundtrips. */
#endif
/*
* Register ourselves for power state changes. We don't
* currently care if this fails.
*/
if (rcNt == STATUS_SUCCESS)
/*
* Done! Returning success!
*/
Log(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n"));
return STATUS_SUCCESS;
}
}
#ifdef VBOX_WITH_HARDENING
#endif
}
RTR0Term();
}
else
{
}
if (NT_SUCCESS(rcNt))
return rcNt;
}
/**
* Unload the driver.
*
* @param pDrvObj Driver object.
*/
{
/* Clean up the power callback registration. */
if (pDevExt->hPowerCallback)
if (pDevExt->pObjPowerCallback)
/*
* We ASSUME that it's not possible to unload a driver with open handles.
*/
#ifdef VBOX_WITH_HARDENING
#endif
RTR0Term();
}
/**
* 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.
*/
{
return rcNt;
}
/**
* For simplifying request completion into a simple return statement.
*
* @returns rcNt
* @param rcNt The status code.
* @param pIrp The IRP.
*/
{
}
/**
* Create (i.e. Open) file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
/*
* We are not remotely similar to a directory...
* (But this is possible.)
*/
/*
* 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.
*/
{
if (pDevObj == g_pDevObjSys)
}
#ifdef VBOX_WITH_HARDENING
/*
* Anyone can open the error device.
*/
else if (pDevObj == g_pDevObjErrorInfo)
{
}
#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
{
LogRel(("vboxdrv: Process %p is being debugged, access to vboxdrv / vboxdrvu declined.\n",
}
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)
{
kSupDrvNtProtectKind_StubUnverified, true /*fLink*/);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
}
}
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)
{
if (pNtProtect)
{
{
if (RT_SUCCESS(rc))
{
/* Create a session. */
&pSession);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
}
}
/* No second attempt. */
LogRel(("vboxdrv: supdrvCreateSession failed for process %p: rc=%d.\n",
}
else
LogRel(("vboxdrv: Process %p failed process verification: rc=%d.\n",
}
else
{
LogRel(("vboxdrv: %p is not a budding VM process (enmProcessKind=%d).\n",
}
}
else
{
}
}
/*
* Call common code to create an unprivileged session.
*/
else
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
}
}
}
#else /* !VBOX_WITH_HARDENING */
/*
* Call common code to create a session.
*/
rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/, &pSession);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
#endif /* !VBOX_WITH_HARDENING */
/* bail out */
}
}
return supdrvNtCompleteRequest(rcNt, pIrp); /* Note. the IoStatus is completely ignored on error. */
}
/**
* Clean up file handle entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
#ifdef VBOX_WITH_HARDENING
if (pDevObj == g_pDevObjStub)
{
if (pNtProtect)
{
}
}
else if (pDevObj == g_pDevObjErrorInfo)
else
#endif
{
if (pSession)
{
}
}
}
/**
* Close file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
#ifdef VBOX_WITH_HARDENING
if (pDevObj == g_pDevObjStub)
{
if (pNtProtect)
{
}
}
else if (pDevObj == g_pDevObjErrorInfo)
else
#endif
{
if (pSession)
{
}
}
}
#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 uFunction The function.
* @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,
{
/*
* Only the normal devices, not the stub or error info ones.
*/
{
pIoStatus->Information = 0;
return TRUE;
}
/*
* Check the input a little bit and get a the session references.
*/
if (!pSession)
{
pIoStatus->Information = 0;
return TRUE;
}
if (pSession->fUnrestricted)
{
#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
if (supdrvNtIsDebuggerAttached())
{
pIoStatus->Information = 0;
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 == SUP_IOCTL_FAST_DO_RAW_RUN
|| uCmd == SUP_IOCTL_FAST_DO_HM_RUN
|| uCmd == SUP_IOCTL_FAST_DO_NOP)
{
return TRUE;
}
}
/*
* The normal path.
*/
unsigned cbOut = 0;
int rc = 0;
Log2(("VBoxDrvNtFastIoDeviceControl(%p): ioctl=%#x pvIn=%p cbIn=%#x pvOut=%p cbOut=%#x pSession=%p\n",
# 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. */
{
/* Get the header so we can validate it a little bit against the
parameters before allocating any memory kernel for the reqest. */
{
{
}
{
rcNt = GetExceptionCode();
}
}
else
if (NT_SUCCESS(rcNt))
{
/* Verify that the sizes in the request header are correct. */
{
/* Allocate a buffer and copy all the input into it. */
if (pHdr)
{
{
}
{
rcNt = GetExceptionCode();
}
}
else
if (NT_SUCCESS(rcNt))
{
/*
* Now call the common code to do the real work.
*/
if (RT_SUCCESS(rc))
{
/*
* Copy back the result.
*/
{
OSDBGPRINT(("VBoxDrvNtFastIoDeviceControl: too much output! %#x > %#x; uCmd=%#x!\n",
}
if (cbOut)
{
{
}
{
rcNt = GetExceptionCode();
}
}
else
}
else if (rc == VERR_INVALID_PARAMETER)
else
}
}
else
{
Log(("VBoxDrvNtFastIoDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
}
}
}
else
{
}
}
# ifdef RT_ARCH_AMD64
else
{
Log(("VBoxDrvNtFastIoDeviceControl: WOW64 req - not supported\n"));
}
# endif
/* complete the request. */
return TRUE; /* handled. */
}
#endif /* VBOXDRV_WITH_FAST_IO */
/**
* Device I/O Control entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
if (!RT_VALID_PTR(pSession))
/*
* 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())
{
}
#endif
if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
|| ulCmd == SUP_IOCTL_FAST_DO_NOP)
{
int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
/* Complete the I/O request. */
}
}
}
/**
* Worker for VBoxDrvNtDeviceControl that takes the slow IOCtl functions.
*
* @returns NT status code.
*
* @param pDevObj Device object.
* @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)
{
unsigned cbOut = 0;
int rc = 0;
Log2(("VBoxDrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
#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. */
{
/* Verify that the sizes in the request header are correct. */
{
/* Zero extra output bytes to make sure we don't leak anything. */
/*
* Do the job.
*/
if (!rc)
{
{
OSDBGPRINT(("VBoxDrvLinuxIOCtl: too much output! %#x > %#x; uCmd=%#x!\n",
}
}
else
}
else
{
Log(("VBoxDrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
}
}
else
{
Log(("VBoxDrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
}
}
#ifdef RT_ARCH_AMD64
else
{
Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
}
#endif
/* complete the request. */
return rcNt;
}
/**
* Internal Device I/O Control entry point, used for IDC.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
unsigned cbOut = 0;
int rc = 0;
Log2(("VBoxDrvNtInternalDeviceControl(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
/* Verify that it's a buffered CTL. */
{
/* Verify the pDevExt in the session. */
: !pSession
)
{
/* Verify that the size in the request header is correct. */
{
/*
* Call the generic code.
*
* Note! Connect and disconnect requires some extra attention
* in order to get the session handling right.
*/
if (!rc)
{
}
else
{
}
}
else
{
Log(("VBoxDrvNtInternalDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx Irp=%#lx/%#lx!\n",
}
}
else
}
else
{
Log(("VBoxDrvNtInternalDeviceControl: not buffered request (%#x) - not supported\n",
}
/* complete the request. */
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.
*/
{
Log(("VBoxDrvNtRead\n"));
#ifdef VBOX_WITH_HARDENING
/*
* VBoxDrvErrorInfo?
*/
if (pDevObj == g_pDevObjErrorInfo)
{
if ( pStack
{
/*
* Look up the process error information.
*/
if (RT_SUCCESS(rc))
{
{
break;
}
/*
* Did we find error info and is the caller requesting data within it?
* If so, cehck the destination buffer and copy the data into it.
*/
if ( pCur
{
if (pvDstBuf)
{
{
}
}
else
}
/*
* End of file. Free the info.
*/
else if (pCur)
{
}
/*
* We found no error info. Return EOF.
*/
else
}
else
}
else
}
else
#endif /* VBOX_WITH_HARDENING */
{
/*
* Stub.
*/
}
/*
* Complete the request.
*/
return rcNt;
}
/**
* Stub function for functions we don't implemented.
*
* @returns STATUS_NOT_SUPPORTED
* @param pDevObj Device object.
* @param pIrp IRP.
*/
{
Log(("VBoxDrvNtNotSupportedStub\n"));
return STATUS_NOT_SUPPORTED;
}
/**
* ExRegisterCallback handler for power events
*
* @param pCallbackContext User supplied parameter (pDevObj)
* @param pArgument1 First argument
* @param pArgument2 Second argument
*/
{
/* Power change imminent? */
if ((unsigned)pArgument1 == PO_CB_SYSTEM_STATE_LOCK)
{
if ((unsigned)pArgument2 == 0)
Log(("VBoxPowerDispatchCallback: about to go into suspend mode!\n"));
else
Log(("VBoxPowerDispatchCallback: resumed!\n"));
/* Inform any clients that have registered themselves with IPRT. */
}
}
/**
* 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.
*/
{
#ifdef VBOX_WITH_HARDENING
if (pSession->pNtProtect)
{
}
#endif
}
void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
{
}
void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
{
}
/**
* Initializes any OS specific object creator fields.
*/
{
}
/**
* 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)
{
return false;
}
/**
* Force async tsc mode (stub).
*/
{
return false;
}
#define MY_SystemUnloadGdiDriverInformation 27
typedef struct MYSYSTEMGDIDRIVERINFO
{
int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
{
# ifndef RT_ARCH_X86
# error "VBOX_WITHOUT_NATIVE_R0_LOADER is only safe on x86."
# endif
return VERR_NOT_SUPPORTED;
#else
/*
* Convert the filename from DOS UTF-8 to NT UTF-16.
*/
if (RT_FAILURE(rc))
return rc;
if (!pwcsFilename)
return VERR_NO_TMP_MEMORY;
pwcsFilename[0] = '\\';
if (RT_SUCCESS(rc))
{
/*
* Try load it.
*/
Info.ImageLength = 0;
NTSTATUS rcNt = ZwSetSystemInformation(MY_SystemLoadGdiDriverInSystemSpaceInformation, &Info, sizeof(Info));
if (NT_SUCCESS(rcNt))
{
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 '%ws'\n",
Info.ImageAddress, Info.SectionPointer, Info.ImageLength, pImage->cbImageBits, rcNt, Info.Name.Buffer);
# endif
{
/*
* 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))
{
}
}
else
{
}
}
else
{
switch (rcNt)
{
case /* 0xc0000003 */ STATUS_INVALID_INFO_CLASS:
# ifdef RT_ARCH_AMD64
/* Unwind will crash and BSOD, so no fallback here! */
# 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.
*/
# endif
break;
case /* 0xc0000034 */ STATUS_OBJECT_NAME_NOT_FOUND:
break;
case /* 0xC0000263 */ STATUS_DRIVER_ENTRYPOINT_NOT_FOUND:
break;
case /* 0xC0000428 */ STATUS_INVALID_IMAGE_HASH:
break;
case /* 0xC000010E */ STATUS_IMAGE_ALREADY_LOADED:
Log(("WARNING: see @bugref{4853} for cause of this failure on Windows 7 x64\n"));
break;
default:
break;
}
}
}
return rc;
#endif
}
{
}
int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
{
return VINF_SUCCESS;
}
/**
* memcmp + 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.
*/
static int supdrvNtCompare(PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, uint32_t uRva, uint32_t cb)
{
if (iDiff)
{
{
char szBytes[128];
break;
}
}
return iDiff;
}
int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits, PSUPLDRLOAD pReq)
{
if (pImage->pvNtSectionObj)
{
/*
* Usually, the entire image matches exactly.
*/
return VINF_SUCCESS;
/*
* However, 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. To make life simpler, set the max number
* of imports to 16 and just record and sort the locations that needs
* to be excluded from the comparison.
*/
IMAGE_NT_HEADERS const *pNtHdrs;
: 0));
&& pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size >= sizeof(IMAGE_IMPORT_DESCRIPTOR)
&& pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress > sizeof(IMAGE_NT_HEADERS)
&& pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress < pImage->cbImageBits
)
{
struct MyRegion
{
} aExcludeRgns[16];
unsigned cExcludeRgns = 0;
/ sizeof(IMAGE_IMPORT_DESCRIPTOR);
IMAGE_IMPORT_DESCRIPTOR const *pImp;
while ( cImpsLeft-- > 0
{
if ( uRvaThunk > sizeof(IMAGE_NT_HEADERS)
{
/* Find the size of the thunk table. */
cThunks++;
/* Ordered table insert. */
unsigned i = 0;
for (; i < cExcludeRgns; i++)
break;
if (i != cExcludeRgns)
cExcludeRgns++;
}
/* advance */
pImp++;
}
/*
* Ok, do the comparison.
*/
int iDiff = 0;
for (unsigned i = 0; !iDiff && i < cExcludeRgns; i++)
{
}
if (!iDiff)
return VINF_SUCCESS;
}
else
return VERR_LDR_MISMATCH_NATIVE;
}
return VERR_INTERNAL_ERROR_4;
}
{
if (pImage->pvNtSectionObj)
{
{
}
if (rcNt != STATUS_SUCCESS)
}
}
#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
{
bool fGp;
/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberRead.} */
{
/*
* rdmsr and wrmsr faults can be caught even with interrupts disabled.
* (At least on 32-bit XP.)
*/
{
}
{
}
}
{
if (idCpu == NIL_RTCPUID)
else
{
if (RT_FAILURE(rc))
return rc;
}
return VERR_ACCESS_DENIED;
return VINF_SUCCESS;
}
/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberWrite.} */
{
/*
* rdmsr and wrmsr faults can be caught even with interrupts disabled.
* (At least on 32-bit XP.)
*/
{
}
{
}
}
{
if (idCpu == NIL_RTCPUID)
else
{
if (RT_FAILURE(rc))
return rc;
}
return VERR_ACCESS_DENIED;
return VINF_SUCCESS;
}
/** @callback_method_impl{FNRTMPWORKER, Worker for supdrvOSMsrProberModify.} */
{
bool fBeforeGp = true;
bool fModifyGp = true;
bool fAfterGp = true;
bool fRestoreGp = true;
/*
* Do the job.
*/
ASMCompilerBarrier(); /* paranoia */
if (!fFaster)
{
fBeforeGp = false;
}
{
fBeforeGp = true;
}
if (!fBeforeGp)
{
/* Modify. */
{
fModifyGp = false;
}
{
fModifyGp = true;
}
/* Read modified value. */
{
fAfterGp = false;
}
{
fAfterGp = true;
}
/* Restore original value. */
{
fRestoreGp = false;
}
{
fRestoreGp = true;
}
/* Invalid everything we can. */
if (!fFaster)
{
ASMReloadCR3();
ASMNopPause();
}
}
ASMCompilerBarrier(); /* paranoia */
/*
* Write out the results.
*/
}
{
if (idCpu == NIL_RTCPUID)
{
return VINF_SUCCESS;
}
}
#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.
*/
{
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)
{
}
return STATUS_UNSUCCESSFUL;
}
#if 0 /* See alternative in SUPDrvA-win.asm */
/**
* Alternative version of SUPR0Printf for Windows.
*
* @returns 0.
* @param pszFormat The format string.
*/
{
char szMsg[512];
return 0;
}
#endif
/**
* Returns configuration flags of the host kernel.
*/
{
return 0;
}
#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!).
*/
{
/*
* This test works on XP+.
*/
if (!pszImageFile)
return false;
return false;
/*
* This test requires a Vista+ API.
*/
{
if (!NT_SUCCESS(rcNt))
return false;
union
{
} Buf;
if (!NT_SUCCESS(rcNt))
return false;
/* Terminate the name. */
/* Match the name against the system32 directory path. */
return false;
return false;
if (*pwszName++ != '\\')
return false;
/* Compare the name. */
for (;;)
{
return false;
if (!chRight)
break;
}
}
return true;
}
/**
* Checks if the current process is likely to be CSRSS.
*
* @param pProcess The process.
*/
{
/*
* On Windows 8.1 CSRSS.EXE is a protected process.
*/
{
return false;
}
/*
* The name tests.
*/
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.
*/
{
bool fDone = false;
InitializeObjectAttributes(&ObjAttr, &UniStrPortNm, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
if (pObjType)
{
fDone = true;
}
}
}
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)
{
{
int rc;
char szTmp[16];
/*
* First attempt is in the session directory.
*/
cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
if (!fDone)
{
}
if (!fDone)
{
/*
* Try base names.
*/
if (uSessionId == 0)
else
{
}
cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), (uint32_t)(uintptr_t)PsGetProcessId(PsGetCurrentProcess()), 16, 0, 0, 0);
if (!fDone)
{
}
}
/* Cache the result in g_pAlpcPortObjectType2. */
if ( g_pAlpcPortObjectType2 == NULL
&& fDone)
}
return pObjType;
}
/**
* Called in the context of VBoxDrvNtCreate to determin the CSRSS for the
* current process.
*
* 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.
*/
{
/*
* 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;
char szSessionId[16];
if (uSessionId == 0)
{
szSessionId[0] = '0';
}
else
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
}
/*
* The object cannot be opened, but we can reference it by name.
*/
void *pvApiPortObj = NULL;
0,
NULL /*pAccessState*/,
NULL /*pvParseContext*/,
&pvApiPortObj);
if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
&& g_pAlpcPortObjectType2 != NULL)
0,
NULL /*pAccessState*/,
NULL /*pvParseContext*/,
&pvApiPortObj);
if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
0,
NULL /*pAccessState*/,
NULL /*pvParseContext*/,
&pvApiPortObj);
if (!NT_SUCCESS(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.
*/
do
{
if (!pbBuf)
break;
cbNeeded = 0;
#if 0 /* doesn't work. */
#else
#endif
if (NT_SUCCESS(rcNt))
break;
} while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
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.
*/
{
{
/* Get the process structure and perform some more thorough
process checks. */
if (NT_SUCCESS(rcNt))
{
{
{
/* 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;
{
do
{
ppPortProc++;
}
{
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. */
rc = VINF_SUCCESS;
break;
}
}
}
}
}
/* Advance. */
if (!pProcInfo->NextEntryOffset)
break;
}
}
else
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.
*/
{
{
{
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)
{
/*
* Check the process name.
*/
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.
*/
{
if (!pszImageFile)
return false;
{
return true;
return true;
return true;
}
{
return true;
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.
*/
{
if (RT_SUCCESS(rc))
{
{
{
}
}
}
}
/**
* 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;
{
{
/* Compare short names. */
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
/** @todo check that the full image names matches. */
}
}
}
}
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.
*/
{
/*
* Create a child protection struction.
*/
int rc = supdrvNtProtectCreate(&pNtChild, hChildPid, kSupDrvNtProtectKind_VmProcessUnconfirmed, false /*fLink*/);
if (RT_SUCCESS(rc))
{
pNtChild->fFirstProcessCreateHandle = true;
pNtChild->fFirstThreadCreateHandle = true;
pNtChild->fCsrssFirstProcessCreateHandle = true;
pNtChild->fThemesFirstProcessCreateHandle = true;
if (pNtChild->pCsrssProcess)
/*
* Take the spinlock, recheck parent conditions and link things.
*/
{
if (fSuccess)
{
return VINF_SUCCESS;
}
}
else
}
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.
*/
{
if (pNtProtect)
{
/*
* If this is an unconfirmed VM process, we must release the reference
* the parent structure holds.
*/
{
}
/*
* 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.
*/
&& pNtProtect->u.pChild)
{
}
/*
* Transition it to the dead state to prevent it from opening the
* support driver again or be posthumously abused as a vm process parent.
*/
/*
* Do session cleanups.
*/
if (g_pDevObjSys)
{
RTR0ProcHandleSelf(), NULL);
if (pSession)
{
}
}
}
}
/**
* 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
{
/* 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));
}
/**
* 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.
*/
{
/*
* Is it a new process that needs protection?
*/
if (fCreated)
{
if (pNtStub)
{
if (!pNtVm)
{
}
else
{
}
}
}
/*
* Process termination, do clean ups.
*/
else
{
}
}
/**
* 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.
*/
supdrvNtProtectCallback_ProcessCreateNotifyEx(PEPROCESS pNewProcess, HANDLE hNewPid, PPS_CREATE_NOTIFY_INFO pInfo)
{
/*
* Is it a new process that needs protection?
*/
if (pInfo)
{
Log(("vboxdrv/NewProcessEx: ctx=%04zx/%p pid=%04zx ppid=%04zx ctor=%04zx/%04zx rcNt=%#x %.*ls\n",
pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? (size_t)pInfo->ImageFileName->Length / 2 : 0,
if (pNtStub)
{
if (!pNtVm)
{
/* Parent must be creator. */
{
}
}
else
{
/* Parent must be creator (as above). */
}
}
}
/*
* Process termination, do clean ups.
*/
else
{
}
}
/** @} */
/** @name Process Handle Callbacks.
* @{ */
/** Process rights that we allow for handles to stub and VM processes. */
# define SUPDRV_NT_ALLOW_PROCESS_RIGHTS \
| PROCESS_VM_READ \
| DELETE \
| READ_CONTROL \
| SYNCHRONIZE)
/** Evil process rights. */
# define SUPDRV_NT_EVIL_PROCESS_RIGHTS \
| PROCESS_SET_SESSIONID /*?*/ \
| PROCESS_VM_WRITE \
| PROCESS_CREATE_PROCESS /*?*/ \
| PROCESS_SET_QUOTA /*?*/ \
| PROCESS_SET_LIMITED_INFORMATION /*?*/ \
| 0)
{
Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
/*
* Protected? Kludge required for NtOpenProcess calls comming in before
* the create process hook triggers on Windows 8.1 (possibly others too).
*/
if (!pNtProtect)
{
if (pNtStub)
{
{
}
}
}
if (pNtProtect)
{
/*
* Ok, it's a protected process. Strip rights as required or possible.
*/
{
/* Don't restrict the process accessing itself. */
{
pNtProtect->fFirstProcessCreateHandle = false;
Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s\n",
}
#ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
/* Allow debuggers full access. */
else if (supdrvNtProtectIsWhitelistedDebugger(PsGetCurrentProcess()))
{
pNtProtect->fFirstProcessCreateHandle = false;
Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s [debugger]\n",
}
#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.
Special case 2 on 8.1:
The CreateProcess code requires additional rights for
something, we'll drop these in the stub code. */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() != KernelMode)
{
if ( !pOpInfo->KernelHandle
{
else
| 0;
}
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. */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() == UserMode
{
pNtProtect->fCsrssFirstProcessCreateHandle = false;
{
/* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
PROCESS_CREATE_PROCESS */
| PROCESS_DUP_HANDLE /* Needed for CreateProcess/VBoxTestOGL. */
| 0;
}
}
/* 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. */
&& pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == 0x1478 /* 6.1.7600.16385 (win7_rtm.090713-1255) */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() == UserMode
{
}
Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p wants %#x to %p/pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
}
}
else
{
/* Don't restrict the process accessing itself. */
{
Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
}
else
{
/* Special case 5 on Vista, 7 & 8:
This is the CSRSS.EXE end of special case #1. */
&& pOpInfo->KernelHandle == 0
&& pNtProtect->hParentPid
&& ExGetPreviousMode() == UserMode
{
{
/* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
PROCESS_CREATE_PROCESS, PROCESS_DUP_HANDLE */
| PROCESS_DUP_HANDLE /* Needed for launching VBoxTestOGL. */
| 0;
}
}
Log(("vboxdrv/ProcessHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
}
}
}
return OB_PREOP_SUCCESS;
}
{
Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
if ( pOpInfo->CallContext
{
| 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",
}
}
/** @} */
/** @name Thread Handle Callbacks
* @{ */
/* From ntifs.h */
/** Thread rights that we allow for handles to stub and VM processes. */
# define SUPDRV_NT_ALLOWED_THREAD_RIGHTS \
( THREAD_TERMINATE \
| 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_SET_LIMITED_INFORMATION /*?*/ \
| THREAD_SET_THREAD_TOKEN /*?*/ \
| THREAD_IMPERSONATE /*?*/ \
| THREAD_DIRECT_IMPERSONATION /*?*/ \
/*| THREAD_RESUME - see remarks. */ \
| 0)
{
Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
if (pNtProtect)
{
{
/* 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",
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",
}
#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. */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() == UserMode
{
if ( !pOpInfo->KernelHandle
{
}
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" */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() == UserMode
{
//fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
}
Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p wants %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s [prev=%#x]\n",
}
}
else
{
/* Don't restrict the process accessing its own threads. */
if ( pProcess == PsGetCurrentProcess()
{
Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] self\n",
}
else
{
/* Special case 3 on Vista, 7, 8:
This is the follow up to special case 1. */
&& pOpInfo->KernelHandle == 0
&& ExGetPreviousMode() == UserMode
{
//fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
}
Log(("vboxdrv/ThreadHandlePre: %sctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s\n",
}
}
}
return OB_PREOP_SUCCESS;
}
{
Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
if ( pOpInfo->CallContext
{
| 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));
}
}
/** @} */
/**
* 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)
{
if (!pNtProtect)
return VERR_NO_MEMORY;
if (fLink)
{
if (!fSuccess)
{
/* Duplicate entry, fail. */
return VERR_DUPLICATE;
}
}
return VINF_SUCCESS;
}
/**
* Releases a reference to a NT protection structure.
*
* @param pNtProtect The NT protection structure.
*/
{
if (!pNtProtect)
return;
if (cRefs != 0)
else
{
/*
* That was the last reference. Remove it from the tree, invalidate it
* and free the resources associated with it. Also, release any
*/
PSUPDRVNTPROTECT pRemoved = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pNtProtect->AvlCore.Key);
{
if (pChild)
{
if (!cChildRefs)
else
}
}
else
if (pNtProtect->pCsrssProcess)
{
}
if (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.
*/
{
if (pFound)
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.
*/
{
/*
* Grab a reference to the parent stub process.
*/
{
if (pNtStub)
{
{
}
else
}
}
/*
* 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;
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
/*
* Do some simple sanity checking.
*/
if (RT_SUCCESS(rc))
/* Clean up. */
}
else
"Error opening stub thread %p (tid %p, pid %p): %#x",
}
else
}
else
}
else
return rc;
}
/**
* 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.
*/
/*
* 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.
*/
if (!NT_SUCCESS(rcNt))
{
while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
{
if (!pbBuf)
return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Error allocating %zu bytes for querying handles.", cbBuf);
}
if (!NT_SUCCESS(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
# endif
while (i-- > 0)
{
const char *pszType;
{
/* Handles within the protected process are fine. */
{
continue;
}
/* CSRSS is allowed to have one evil process handle.
See the special cases in the hook code. */
if ( cCsrssProcessHandles < 1
{
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... */
{
continue;
}
pszType = "process";
}
{
/* Handles within the protected process is fine. */
{
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
{
continue;
}
pszType = "thread";
}
else
continue;
# ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
/* Ignore whitelisted debuggers. */
continue;
if (NT_SUCCESS(rcNt))
{
if (fIsDebugger)
{
continue;
}
}
# endif
/* Found evil handle. Currently ignoring on pre-Vista. */
# ifndef VBOX_WITH_VISTA_NO_SP
if ( g_uNtVerCombined >= SUP_NT_VER_VISTA
# else
# endif
{
LogRel(("vboxdrv: Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s\n",
? "\nFound evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s"
: "Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s",
/* Try add the process name. */
if (NT_SUCCESS(rcNt))
{
}
}
}
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.
*/
{
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;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_VERIFY_ONLY,
}
}
else
rc = VERR_NO_MEMORY;
/*
* Upgrade and return.
*/
/* Stub process verficiation is pretty much straight forward. */
{
pNtProtect->enmProcessKind = RT_SUCCESS(rc) ? kSupDrvNtProtectKind_StubSpawning : kSupDrvNtProtectKind_StubDead;
}
/* The VM process verification is a little bit more complicated
because we need to drop the parent process reference as well. */
{
if (RT_SUCCESS(rc))
{
}
else
}
/* 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
{
if (RT_SUCCESS(rc))
}
/*
* Free error info on success, keep it on failure.
*/
if (RT_SUCCESS(rc))
else if (pErrorInfo)
{
if (!pErrorInfo->cchErrorInfo)
pErrorInfo->cchErrorInfo = (uint32_t)RTStrPrintf(pErrorInfo->szErrorInfo, sizeof(pErrorInfo->szErrorInfo),
"supdrvNtProtectVerifyProcess: rc=%d", rc);
if (RT_FAILURE(rc))
if (RT_SUCCESS(rc2))
{
/* Free old entries. */
{
}
/* Insert our new entry. */
}
else
}
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)
{
}
# endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
/**
* Terminates the hardening bits.
*/
static void supdrvNtProtectTerm(void)
{
/*
* Stop intercepting process and thread handle creation calls.
*/
{
}
/*
* Stop intercepting process creation and termination notifications.
*/
rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
else
rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
/*
* Clean up globals.
*/
{
}
}
# 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);
# endif
/**
* Initalizes the hardening bits.
*
* @returns NT status code.
*/
static NTSTATUS supdrvNtProtectInit(void)
{
/*
* Initialize the globals.
*/
/* The NT version. */
/* Resolve methods we want but isn't available everywhere. */
g_pfnPsSetCreateProcessNotifyRoutineEx = (PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)MmGetSystemRoutineAddress(&RoutineName);
g_pfnPsReferenceProcessFilePointer = (PFNPSREFERENCEPROCESSFILEPOINTER)MmGetSystemRoutineAddress(&RoutineName);
g_pfnPsIsProtectedProcessLight = (PFNPSISPROTECTEDPROCESSLIGHT)MmGetSystemRoutineAddress(&RoutineName);
{
/* 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
{
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)
/*&& pbCode[21] == 0x1f*/
)
{
if (*pbKiServiceLinkage == 0xc3)
{
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
}
{
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. */
/* The spinlock protecting our structures. */
if (RT_FAILURE(rc))
return VBoxDrvNtErr2NtStatus(rc);
/* The mutex protecting the error information. */
if (RT_SUCCESS(rc))
{
/* Image stuff + certificates. */
if (RT_SUCCESS(rc))
{
/*
* Intercept process creation and termination.
*/
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+.
*/
{
static OB_OPERATION_REGISTRATION s_aObOperations[] =
{
{
},
{
},
};
static OB_CALLBACK_REGISTRATION s_ObCallbackReg =
{
/* .Version = */ OB_FLT_REGISTRATION_VERSION,
/* .Altitude.Length = */ 0,
/* .Altitude.MaximumLength = */ 0,
/* .Altitude.Buffer = */ NULL,
/* .RegistrationContext = */ NULL,
/* .OperationRegistration = */ &s_aObOperations[0]
};
{
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",
};
for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszAltitudes) && rcNt == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION; i++)
{
if (NT_SUCCESS(rcNt))
{
/*
* Happy ending.
*/
return STATUS_SUCCESS;
}
}
}
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
# endif
{
DbgPrint("vboxdrv: ObRegisterCallbacks was not found. Please make sure you got the latest updates and service packs installed\n");
}
else
{
Log(("vboxdrv: ObRegisterCallbacks was not found; ignored pre-Vista\n"));
return rcNt = STATUS_SUCCESS;
}
}
/*
*/
g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
else
}
else
LogRel(("vboxdrv: PsSetCreateProcessNotifyRoutine%s failed with rcNt=%#x\n",
}
else
}
else
return rcNt;
}
#endif /* VBOX_WITH_HARDENING */