SUPHardenedVerifyProcess-win.cpp revision f71c6ce3d63d12af823dfc6df1c57ce9928b6a3a
1ef682c649d3f26392a8b7a091337adb78ddd87dvboxsync/* $Id$ */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/** @file
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
1ef682c649d3f26392a8b7a091337adb78ddd87dvboxsync */
1ef682c649d3f26392a8b7a091337adb78ddd87dvboxsync
1ef682c649d3f26392a8b7a091337adb78ddd87dvboxsync/*
1ef682c649d3f26392a8b7a091337adb78ddd87dvboxsync * Copyright (C) 2006-2014 Oracle Corporation
b2640405e06105d868b5fc8f7b676bb680884380vboxsync *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * available from http://www.virtualbox.org. This file is free software;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * you can redistribute it and/or modify it under the terms of the GNU
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * General Public License (GPL) as published by the Free Software
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * The contents of this file may alternatively be used under the terms
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * of the Common Development and Distribution License Version 1.0
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * VirtualBox OSE distribution, in which case the provisions of the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * CDDL are applicable instead of those of the GPL.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * You may elect to license modified versions of this file under the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * terms and conditions of either the GPL or the CDDL or both.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/*******************************************************************************
b2640405e06105d868b5fc8f7b676bb680884380vboxsync* Header Files *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync*******************************************************************************/
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef IN_RING0
b2640405e06105d868b5fc8f7b676bb680884380vboxsync# define IPRT_NT_MAP_TO_ZW
b2640405e06105d868b5fc8f7b676bb680884380vboxsync# include <iprt/nt/nt.h>
8ad79874169cc981a694a15e8a806b9a39133673vboxsync# include <ntimage.h>
8ad79874169cc981a694a15e8a806b9a39133673vboxsync#else
8ad79874169cc981a694a15e8a806b9a39133673vboxsync# include <iprt/nt/nt-and-windows.h>
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#include <VBox/sup.h>
543d2dd34b3036927165be4f72c0cccd85daaa33vboxsync#include <VBox/err.h>
543d2dd34b3036927165be4f72c0cccd85daaa33vboxsync#include <iprt/ctype.h>
543d2dd34b3036927165be4f72c0cccd85daaa33vboxsync#include <iprt/param.h>
543d2dd34b3036927165be4f72c0cccd85daaa33vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef IN_RING0
b2640405e06105d868b5fc8f7b676bb680884380vboxsync# include "SUPDrvInternal.h"
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#else
b2640405e06105d868b5fc8f7b676bb680884380vboxsync# include "SUPLibInternal.h"
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#include "win/SUPHardenedVerify-win.h"
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync/*******************************************************************************
b2640405e06105d868b5fc8f7b676bb680884380vboxsync* Structures and Typedefs *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync*******************************************************************************/
8ad79874169cc981a694a15e8a806b9a39133673vboxsync/**
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Virtual address space region.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsynctypedef struct SUPHNTVPREGION
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The RVA of the region. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t uRva;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The size of the region. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t cb;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The protection of the region. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t fProt;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync} SUPHNTVPREGION;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/** Pointer to a virtual address space region. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsynctypedef SUPHNTVPREGION *PSUPHNTVPREGION;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
8ad79874169cc981a694a15e8a806b9a39133673vboxsync/**
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Virtual address space image information.
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsynctypedef struct SUPHNTVPIMAGE
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The base address of the image. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uintptr_t uImageBase;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync /** The size of the image mapping. */
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync uintptr_t cbImage;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The name from the allowed lists. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync const char *pszName;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync struct
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The full unicode name. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync UNICODE_STRING UniStr;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Buffer space. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync WCHAR awcBuffer[260];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync } Name;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The number of mapping regions. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t cRegions;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Mapping regions. */
8ad79874169cc981a694a15e8a806b9a39133673vboxsync SUPHNTVPREGION aRegions[16];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The image characteristics from the FileHeader. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint16_t fImageCharecteristics;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The DLL characteristics from the OptionalHeader. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint16_t fDllCharecteristics;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Set if this is the DLL. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync bool fDll;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Set if the image is NTDLL an the verficiation code needs to watch out for
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * the NtCreateSection patch. */
8ad79874169cc981a694a15e8a806b9a39133673vboxsync bool fNtCreateSectionPatch;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Whether the API set schema hack needs to be applied when verifying memory
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync * content. The hack means that we only check if the 1st section is mapped. */
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync bool fApiSetSchemaOnlySection1;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync} SUPHNTVPIMAGE;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/** Pointer to image info from the virtual address space scan. */
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsynctypedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/**
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Virtual address space scanning state.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsynctypedef struct SUPHNTVPSTATE
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** The result. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync int rcResult;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Number of images in aImages. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t cImages;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Images found in the process.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * The array is large enough to hold the executable, all allowed DLLs, and one
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * more so we can get the image name of the first unwanted DLL. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync SUPHNTVPIMAGE aImages[1 + 6 + 1
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync + 16
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
b2640405e06105d868b5fc8f7b676bb680884380vboxsync ];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Memory compare scratch buffer.*/
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint8_t abMemory[_4K];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** File compare scratch buffer.*/
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint8_t abFile[_4K];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Section headers for use when comparing file and loaded image. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync IMAGE_SECTION_HEADER aSecHdrs[16];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /** Pointer to the error info. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync PRTERRINFO pErrInfo;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync} SUPHNTVPSTATE;
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync/** Pointer to stat information of a virtual address space scan. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsynctypedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/*******************************************************************************
b2640405e06105d868b5fc8f7b676bb680884380vboxsync* Global Variables *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync*******************************************************************************/
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/**
8ad79874169cc981a694a15e8a806b9a39133673vboxsync * System DLLs allowed to be loaded into the process.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @remarks supHardNtVpCheckDlls assumes these are lower case.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync */
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsyncstatic const char *g_apszSupNtVpAllowedDlls[] =
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "ntdll.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "kernel32.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "kernelbase.dll",
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "apphelp.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "apisetschema.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "sfc.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "psapi.dll",
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "msvcrt.dll",
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync "advapi32.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "sechost.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "rpcrt4.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "SamplingRuntime.dll",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
8ad79874169cc981a694a15e8a806b9a39133673vboxsync};
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync/**
2347f07aa55c4c0035118a2a1634e5187a3ffdf4vboxsync * VBox executables allowed to start VMs.
eb4f1fa4c357485330370c0eaba27e5a2af7d9c4vboxsync * @remarks Remember to keep in sync with SUPR3HardenedVerify.cpp.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
8ad79874169cc981a694a15e8a806b9a39133673vboxsyncstatic const char *g_apszSupNtVpAllowedVmExes[] =
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "VBoxHeadless.exe",
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "VirtualBox.exe",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "VBoxSDL.exe",
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync "VBoxNetDHCP.exe",
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync "VBoxNetNAT.exe",
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstMicro.exe",
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync "tstPDMAsyncCompletion.exe",
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "tstPDMAsyncCompletionStress.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstVMM.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstVMREQ.exe",
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync "tstCFGM.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstIntNet-1.exe",
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync "tstMMHyperHeap.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstR0ThreadPreemptionDriver.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstRTR0MemUserKernelDriver.exe",
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync "tstRTR0SemMutexDriver.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstRTR0TimerDriver.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync "tstSSM.exe",
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync};
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync * ring-0, in ring-3 it's just a slightly confusing define. */
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync#ifdef IN_RING0
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsyncPFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync#else
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync#endif
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
b0a3d0ec5780199a2f379da63c59ccf48f1a73b9vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync/**
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * Fills in error information.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync *
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @returns @a rc.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @param pErrInfo Pointer to the extended error info structure.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * Can be NULL.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @param pszErr Where to return error details.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @param cbErr Size of the buffer @a pszErr points to.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @param rc The status to return.
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync * @param pszMsg The format string for the message.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @param ... The arguments for the format string.
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync */
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsyncstatic int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_list va;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef IN_RING3
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_start(va, pszMsg);
8ad79874169cc981a694a15e8a806b9a39133673vboxsync supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_end(va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
8ad79874169cc981a694a15e8a806b9a39133673vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_start(va, pszMsg);
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync va_end(va);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return rc;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync}
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync/**
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * Fills in error information.
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync *
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync * @returns @a rc.
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync * @param pThis The process validator instance.
8ad79874169cc981a694a15e8a806b9a39133673vboxsync * @param pszErr Where to return error details.
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync * @param cbErr Size of the buffer @a pszErr points to.
69dc1a8b612032236f9f04033e7c53ce7a478f17vboxsync * @param rc The status to return.
485e602154df33e5466e0dcca16d8f97914ce41dvboxsync * @param pszMsg The format string for the message.
485e602154df33e5466e0dcca16d8f97914ce41dvboxsync * @param ... The arguments for the format string.
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync */
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsyncstatic int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync{
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync va_list va;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef IN_RING3
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_start(va, pszMsg);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync va_end(va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
8ad79874169cc981a694a15e8a806b9a39133673vboxsync
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync va_start(va, pszMsg);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync#ifdef IN_RING0
b2640405e06105d868b5fc8f7b676bb680884380vboxsync RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync pThis->rcResult = rc;
eafe2940026f325feeaa433c9ea44823431ec5ffvboxsync#else
eafe2940026f325feeaa433c9ea44823431ec5ffvboxsync if (RT_SUCCESS(pThis->rcResult))
8ad79874169cc981a694a15e8a806b9a39133673vboxsync {
8ad79874169cc981a694a15e8a806b9a39133673vboxsync RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pThis->rcResult = rc;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync }
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync else
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync {
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync va_end(va);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync return pThis->rcResult;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync}
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsyncstatic NTSTATUS supHardNtVpReadFile(HANDLE hFile, uint64_t off, void *pvBuf, size_t cbRead)
8ad79874169cc981a694a15e8a806b9a39133673vboxsync{
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync if ((ULONG)cbRead != cbRead)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return STATUS_INTEGER_OVERFLOW;
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NTSTATUS rcNt = NtReadFile(hFile,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NULL /*hEvent*/,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NULL /*ApcRoutine*/,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NULL /*ApcContext*/,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync &Ios,
8ad79874169cc981a694a15e8a806b9a39133673vboxsync pvBuf,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync (ULONG)cbRead,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync (PLARGE_INTEGER)&off,
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NULL);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if (NT_SUCCESS(rcNt))
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync rcNt = Ios.Status;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync return rcNt;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync}
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsyncstatic NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync{
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync#ifdef IN_RING0
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync /* ASSUMES hProcess is the current process. */
8ad79874169cc981a694a15e8a806b9a39133673vboxsync /** @todo use MmCopyVirtualMemory where available! */
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if (RT_SUCCESS(rc))
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync return STATUS_SUCCESS;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return STATUS_ACCESS_DENIED;
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync#else
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync SIZE_T cbIgn;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync rcNt = STATUS_IO_DEVICE_ERROR;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return rcNt;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync#endif
b2640405e06105d868b5fc8f7b676bb680884380vboxsync}
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsyncstatic int supHardNtVpFileMemCompare(PSUPHNTVPSTATE pThis, const void *pvFile, const void *pvMemory, size_t cbToCompare,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync PSUPHNTVPIMAGE pImage, uint32_t uRva)
8ad79874169cc981a694a15e8a806b9a39133673vboxsync{
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (suplibHardenedMemComp(pvFile, pvMemory, cbToCompare) == 0)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return VINF_SUCCESS;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /* Find the exact location. */
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync const uint8_t *pbFile = (const uint8_t *)pvFile;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync const uint8_t *pbMemory = (const uint8_t *)pvMemory;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync while (cbToCompare > 0 && *pbFile == *pbMemory)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync {
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync cbToCompare--;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync pbFile++;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync pbMemory++;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync uRva++;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync }
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync "%s: memory compare at %#x failed: %#x != %#x\n", pImage->pszName, uRva, *pbFile, *pbMemory);
8ad79874169cc981a694a15e8a806b9a39133673vboxsync}
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
69dc1a8b612032236f9f04033e7c53ce7a478f17vboxsync
485e602154df33e5466e0dcca16d8f97914ce41dvboxsyncstatic int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
485e602154df33e5466e0dcca16d8f97914ce41dvboxsync uint32_t uRva, uint32_t cb, uint32_t fProt)
3383321ffc6907012f92f16b26b026908de7fe7fvboxsync{
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync uint32_t const cbOrg = cb;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if (!cb)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return VINF_SUCCESS;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync for (uint32_t i = 0; i < pImage->cRegions; i++)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (offRegion < pImage->aRegions[i].cb)
8ad79874169cc981a694a15e8a806b9a39133673vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if ( pImage->aRegions[i].fProt != fProt
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync && ( fProt != PAGE_READWRITE
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
eafe2940026f325feeaa433c9ea44823431ec5ffvboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
eafe2940026f325feeaa433c9ea44823431ec5ffvboxsync "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
8ad79874169cc981a694a15e8a806b9a39133673vboxsync pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
8ad79874169cc981a694a15e8a806b9a39133673vboxsync if (cbLeft >= cb)
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync return VINF_SUCCESS;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync cb -= cbLeft;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync uRva += cbLeft;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync#if 0 /* This shouldn't ever be necessary. */
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if ( i + 1 < pImage->cRegions
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync && uRva < pImage->aRegions[i + 1].uRva)
8ad79874169cc981a694a15e8a806b9a39133673vboxsync {
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync cbLeft = pImage->aRegions[i + 1].uRva - uRva;
0c802efc285bf77b849eaf660a9c18a0e7f62445vboxsync if (cbLeft >= cb)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return VINF_SUCCESS;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync cb -= cbLeft;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uRva += cbLeft;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync }
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync}
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync
8ad79874169cc981a694a15e8a806b9a39133673vboxsync/**
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Compares process memory with the disk content.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync *
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @returns VBox status code.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @param pThis The process scanning state structure (for the
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * two scratch buffers).
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @param pImage The image data collected during the address
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * space scan.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @param hProcess Handle to the process.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @param hFile Handle to the image file.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * @param pErrInfo Pointer to error info structure. Optional.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsyncstatic int supHardNtVpVerifyImageCompareMemory(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, HANDLE hFile,
8ad79874169cc981a694a15e8a806b9a39133673vboxsync PRTERRINFO pErrInfo)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync{
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync /*
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync * Read and find the file headers.
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync NTSTATUS rcNt = supHardNtVpReadFile(hFile, 0, pThis->abFile, sizeof(pThis->abFile));
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync if (!NT_SUCCESS(rcNt))
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Error reading image header: %#x", pImage->pszName, rcNt);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t offNtHdrs = 0;
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync offNtHdrs = pDosHdr->e_lfanew;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /*
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Do basic header validation.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#ifdef RT_ARCH_AMD64
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#else
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync#endif
bda3dd52e256c637c703aecf163ecc5b682a43a4vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Unexpected optional header size: %#x",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "%s: Unexpected data dirs: %#x", pImage->pszName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync /*
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync * Before we start comparing things, store what we need to know from the headers.
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync */
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "%s: Too many section headers: %#x", pImage->pszName, cSections);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync suplibHardenedMemCopy(pThis->aSecHdrs, pNtHdrs + 1, cSections * sizeof(IMAGE_SECTION_HEADER));
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uintptr_t const uImageBase = pNtHdrs->OptionalHeader.ImageBase;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (uImageBase & PAGE_OFFSET_MASK)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync "%s: Invalid image base: %p", pImage->pszName, uImageBase);
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t const cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync pImage->pszName, cbImage, pImage->cbImage);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync uint32_t const cbSectAlign = pNtHdrs->OptionalHeader.SectionAlignment;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync || cbSectAlign < PAGE_SIZE
b2640405e06105d868b5fc8f7b676bb680884380vboxsync || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t const cbFileAlign = pNtHdrs->OptionalHeader.FileAlignment;
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
8ad79874169cc981a694a15e8a806b9a39133673vboxsync "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync pImage->pszName, cbFileAlign, cbSectAlign);
8ad79874169cc981a694a15e8a806b9a39133673vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t const cbHeaders = pNtHdrs->OptionalHeader.SizeOfHeaders;
8ad79874169cc981a694a15e8a806b9a39133673vboxsync uint32_t const cbMinHdrs = offNtHdrs + sizeof(*pNtHdrs) + sizeof(IMAGE_SECTION_HEADER) * cSections;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (cbHeaders < cbMinHdrs)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Headers are too small: %#x < %#x (cSections=%#x)",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, cbHeaders, cbMinHdrs, cSections);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (cbHdrsFile > sizeof(pThis->abFile))
8ad79874169cc981a694a15e8a806b9a39133673vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync /*
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync * Compare the file header with the loaded bits. The loader will fiddle
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync * with image base, changing it to the actual load address.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync int rc;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (!pImage->fApiSetSchemaOnlySection1)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync rcNt = supHardNtVpReadMem(hProcess, pImage->uImageBase, pThis->abMemory, cbHdrsFile);
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync if (!NT_SUCCESS(rcNt))
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Error reading image header from memory: %#x", pImage->pszName, rcNt);
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync if (uImageBase != pImage->uImageBase)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pNtHdrs->OptionalHeader.ImageBase = pImage->uImageBase;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync rc = supHardNtVpFileMemCompare(pThis, pThis->abFile, pThis->abMemory, cbHeaders, pImage, 0 /*uRva*/);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (RT_FAILURE(rc))
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync return rc;
ce95f18112057771f68abe58df709edd7c467db1vboxsync rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
ce95f18112057771f68abe58df709edd7c467db1vboxsync if (RT_FAILURE(rc))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return rc;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /*
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Save some header fields we might be using later on.
b2640405e06105d868b5fc8f7b676bb680884380vboxsync */
8ad79874169cc981a694a15e8a806b9a39133673vboxsync pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync pImage->fDllCharecteristics = pNtHdrs->OptionalHeader.DllCharacteristics;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
8ad79874169cc981a694a15e8a806b9a39133673vboxsync /*
b2640405e06105d868b5fc8f7b676bb680884380vboxsync * Validate sections and check them against the mapping regions.
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync */
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync uint32_t uRva = cbHdrsFile;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync for (uint32_t i = 0; i < cSections; i++)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /* Validate the section. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync if (cbMap > cbImage || uRva + cbMap > cbImage)
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
083344b49cc7370da15d3cb7e3a9c9cb2d8dfbb0vboxsync "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
b2640405e06105d868b5fc8f7b676bb680884380vboxsync "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, i, cbFile, cbMap, uSectRva);
8ad79874169cc981a694a15e8a806b9a39133673vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync /* Validate the protection. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (pImage->fApiSetSchemaOnlySection1)
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->uImageBase -= uSectRva;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->cbImage += uSectRva;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->aRegions[i].uRva = uSectRva;
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uint32_t fProt;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync {
b2640405e06105d868b5fc8f7b676bb680884380vboxsync case IMAGE_SCN_MEM_READ:
b2640405e06105d868b5fc8f7b676bb680884380vboxsync fProt = PAGE_READONLY;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync break;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
b2640405e06105d868b5fc8f7b676bb680884380vboxsync fProt = PAGE_READWRITE;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (!suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync fProt = PAGE_READONLY;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync break;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync fProt = PAGE_EXECUTE_READ;
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync break;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync case IMAGE_SCN_MEM_EXECUTE:
b2640405e06105d868b5fc8f7b676bb680884380vboxsync fProt = PAGE_EXECUTE;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync break;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync default:
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
b2640405e06105d868b5fc8f7b676bb680884380vboxsync pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
b2640405e06105d868b5fc8f7b676bb680884380vboxsync }
8ad79874169cc981a694a15e8a806b9a39133673vboxsync rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
b2640405e06105d868b5fc8f7b676bb680884380vboxsync if (RT_FAILURE(rc))
b2640405e06105d868b5fc8f7b676bb680884380vboxsync return rc;
b2640405e06105d868b5fc8f7b676bb680884380vboxsync }
b2640405e06105d868b5fc8f7b676bb680884380vboxsync
cf2d13234ccc8755e2610badbdab5dc0b7bfb1dcvboxsync /* Advance the RVA. */
b2640405e06105d868b5fc8f7b676bb680884380vboxsync uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
a57b3586d0f1a87e0024e318f3c8dad382113611vboxsync }
/*
* Check the mapping regions with the image to make sure someone didn't
* fill executable code into some gap in the image.
*/
/** @todo not vital. */
/*
* Compare executable code. If we're not loaded at the link address, we
* need to load base relocations and apply them while making the compare.
* A special case
*/
/** @todo not vital. */
return VINF_SUCCESS;
}
/**
* Verifies the signature of the given image on disk, then checks if the memory
* mapping matches what we verified.
*
* @returns VBox status code.
* @param pThis The process scanning state structure (for the
* two scratch buffers).
* @param pImage The image data collected during the address
* space scan.
* @param hProcess Handle to the process.
* @param hFile Handle to the image file.
*/
static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess)
{
/*
* Open the image.
*/
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &pImage->Name.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
#ifdef IN_RING0
ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
#endif
NTSTATUS rcNt = NtCreateFile(&hFile,
GENERIC_READ,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (!NT_SUCCESS(rcNt))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
"Error opening image for scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
/*
* Validate the signature, then make an attempt at comparing memory and
* disk content.
*/
int rc = supHardenedWinVerifyImageByHandle(hFile, pImage->Name.UniStr.Buffer,
pImage->fDll ? 0 : SUPHNTVI_F_REQUIRE_BUILD_CERT,
NULL /*pfCacheable*/, pThis->pErrInfo);
if (RT_SUCCESS(rc))
rc = supHardNtVpVerifyImageCompareMemory(pThis, pImage, hProcess, hFile, pThis->pErrInfo);
/*
* Clean up and return.
*/
rcNt = NtClose(hFile);
if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_FILE_CLOSE_ERROR,
"Error closing image after scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
return rc;
}
/**
* Verifies that there is only one thread in the process.
*
* @returns VBox status code.
* @param hProcess The process.
* @param hThread The thread.
* @param pErrInfo Pointer to error info structure. Optional.
*/
static int supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
{
/*
* Use the ThreadAmILastThread request to check that there is only one
* thread in the process.
*/
ULONG cbIgn = 0;
ULONG fAmI = 0;
NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
if (!NT_SUCCESS(rcNt))
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
"NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
if (!fAmI)
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
"More than one thread in process");
/** @todo Would be nice to verify the relation ship between hProcess and hThread
* as well... */
return VINF_SUCCESS;
}
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
/**
* Verifies that there isn't a debugger attached to the process.
*
* @returns VBox status code.
* @param hProcess The process.
* @param pErrInfo Pointer to error info structure. Optional.
*/
static int supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
{
/*
* Use the ProcessDebugPort request to check there is no debugger
* currently attached to the process.
*/
ULONG cbIgn = 0;
uintptr_t uPtr = ~(uintptr_t)0;
NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
ProcessDebugPort,
&uPtr, sizeof(uPtr), &cbIgn);
if (!NT_SUCCESS(rcNt))
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
"NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
if (uPtr != 0)
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
"Debugger attached (%#zx)", uPtr);
return VINF_SUCCESS;
}
#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
/**
* Allocates and initalizes a process stat structure for process virtual memory
* scanning.
*
* @returns Pointer to the state structure on success, NULL on failure.
* @param pErrInfo Pointer to error info structure. Optional.
*/
static PSUPHNTVPSTATE supHardNtVpCreateState(PRTERRINFO pErrInfo)
{
/*
* Allocate the memory.
*/
PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)suplibHardenedAllocZ(sizeof(*pThis));
if (pThis)
{
pThis->rcResult = VINF_SUCCESS;
pThis->pErrInfo = pErrInfo;
return pThis;
}
supHardNtVpSetInfo1(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
return NULL;
}
/**
* Matches two UNICODE_STRING structures in a case sensitive fashion.
*
* @returns true if equal, false if not.
* @param pUniStr1 The first unicode string.
* @param pUniStr2 The first unicode string.
*/
static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
{
if (pUniStr1->Length != pUniStr2->Length)
return false;
return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
}
/**
* Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
*
* @returns true / false
* @param pszName1 The ASCII name.
* @param pwszName2 The UTF-16 name.
*/
static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
{
for (;;)
{
char ch1 = *pszName1++;
RTUTF16 wc2 = *pwszName2++;
if (ch1 != wc2)
{
ch1 = RT_C_TO_LOWER(ch1);
wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
if (ch1 != wc2)
return false;
}
if (!ch1)
return true;
}
}
/**
* Records an additional memory region for an image.
*
* @returns VBox status code.
* @param pThis The process scanning state structure.
* @param pImage The new image structure. Only the unicode name
* buffer is valid.
* @param pMemInfo The memory information for the image.
*/
static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
{
/*
* Extract the final component.
*/
unsigned cwcDirName = pImage->Name.UniStr.Length / sizeof(WCHAR);
PCRTUTF16 pwszFilename = &pImage->Name.UniStr.Buffer[cwcDirName];
while ( cwcDirName > 0
&& pwszFilename[-1] != '\\'
&& pwszFilename[-1] != '/'
&& pwszFilename[-1] != ':')
{
pwszFilename--;
cwcDirName--;
}
if (!*pwszFilename)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
"Empty filename (len=%u) for image at %p.", pImage->Name.UniStr.Length, pMemInfo->BaseAddress);
/*
* Drop trailing slashes from the directory name.
*/
while ( cwcDirName > 0
&& ( pImage->Name.UniStr.Buffer[cwcDirName - 1] == '\\'
|| pImage->Name.UniStr.Buffer[cwcDirName - 1] == '/'))
cwcDirName--;
/*
* Match it against known DLLs.
*/
pImage->pszName = NULL;
for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
{
pImage->pszName = g_apszSupNtVpAllowedDlls[i];
pImage->fDll = true;
#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
/* The directory name must match the one we've got for System32. */
if ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
|| suplibHardenedMemComp(pImage->Name.UniStr.Buffer,
g_System32NtPath.UniStr.Buffer,
cwcDirName * sizeof(WCHAR)))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
"Expected %ls to be loaded from %ls.",
pImage->Name.UniStr.Buffer, g_System32NtPath.UniStr.Buffer);
#endif
break;
}
if (!pImage->pszName)
{
/*
* Not a known DLL, executable?
*/
for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
{
pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
pImage->fDll = false;
break;
}
}
if (!pImage->pszName)
{
if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
&& ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
|| supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
|| supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)) )
{
supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
"Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
"You or your admin need to add and exception to the Application and Device Control (ADC) "
"component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
"See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
, pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
}
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
"Unknown image file %ls at %p.", pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
}
/*
* Since it's a new image, we expect to be at the start of the mapping now.
*/
if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
"Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
/*
* Check for size/rva overflow.
*/
if (pMemInfo->RegionSize >= _2G)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
"Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
/*
* Fill in details from the memory info.
*/
pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
pImage->cbImage = pMemInfo->RegionSize;
pImage->cRegions = 1;
pImage->aRegions[0].uRva = 0;
pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
pImage->aRegions[0].fProt = pMemInfo->Protect;
return VINF_SUCCESS;
}
/**
* Records an additional memory region for an image.
*
* @returns VBox status code.
* @param pThis The process scanning state structure.
* @param pImage The image.
* @param pMemInfo The memory information for the region.
*/
static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
{
/*
* Make sure the base address matches.
*/
if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
"Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
pMemInfo->BaseAddress, pMemInfo->RegionSize);
/*
* Check for size and rva overflows.
*/
uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
if (pMemInfo->RegionSize >= _2G)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
"Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
if (uRva >= _2G)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
"Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
/*
* Record the region.
*/
uint32_t iRegion = pImage->cRegions;
if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
"Too many regions for %s.", pImage->pszName);
pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
pImage->cRegions++;
return VINF_SUCCESS;
}
/**
* Scans the virtual memory of the process.
*
* This collects the locations of DLLs and the EXE, and verifies that executable
* memory is only associated with these.
*
* @returns VBox status code.
* @param pThis The process scanning state structure. Details
* about images are added to this.
* @param hProcess The process to verify.
*/
static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
{
uint32_t cXpExceptions = 0;
uintptr_t cbAdvance = 0;
uintptr_t uPtrWhere = 0;
for (uint32_t i = 0; i < 1024; i++)
{
SIZE_T cbActual = 0;
MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
(void const *)uPtrWhere,
MemoryBasicInformation,
&MemInfo,
sizeof(MemInfo),
&cbActual);
if (!NT_SUCCESS(rcNt))
{
if (rcNt == STATUS_INVALID_PARAMETER)
return pThis->rcResult;
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
"NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
}
/*
* Record images.
*/
if ( MemInfo.Type == SEC_IMAGE
|| MemInfo.Type == SEC_PROTECTED_IMAGE
|| MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
{
uint32_t iImg = pThis->cImages;
rcNt = g_pfnNtQueryVirtualMemory(hProcess,
(void const *)uPtrWhere,
MemorySectionName,
&pThis->aImages[iImg].Name,
sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
&cbActual);
if (!NT_SUCCESS(rcNt))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
"NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
/* New or existing image? */
bool fNew = true;
uint32_t iSearch = iImg;
while (iSearch-- > 0)
if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
{
int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
if (RT_FAILURE(rc))
return rc;
fNew = false;
break;
}
else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
"Unexpected base address match");
if (fNew)
{
int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
if (RT_SUCCESS(rc))
{
pThis->cImages++;
if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
"Internal error: aImages is full.\n");
}
#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
return rc;
#else
else
return rc;
#endif
}
}
/*
* XP, W2K3: Ignore the CSRSS read-only region as best we can.
*/
else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
== PAGE_EXECUTE_READ
&& cXpExceptions == 0
&& (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
/* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
&& g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
cXpExceptions++;
/*
* Executable memory?
*/
#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
{
supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
"Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
uPtrWhere,
MemInfo.BaseAddress,
MemInfo.RegionSize,
MemInfo.Type,
MemInfo.Protect,
MemInfo.State,
MemInfo.AllocationBase,
MemInfo.AllocationProtect);
# ifdef IN_RING3
/* Continue add more information about the problematic process. */
# else
return pThis->rcResult;
# endif
}
#endif
/*
* Advance.
*/
cbAdvance = MemInfo.RegionSize;
if (uPtrWhere + cbAdvance <= uPtrWhere)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
"Empty region at %p.", uPtrWhere);
uPtrWhere += MemInfo.RegionSize;
}
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
"Too many virtual memory regions.\n");
}
/**
* Check the integrity of the executable of the process.
*
* @returns VBox status code.
* @param pThis The process scanning state structure. Details
* about images are added to this.
* @param hProcess The process to verify.
*/
static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis, HANDLE hProcess)
{
/*
* Make sure there is exactly one executable image.
*/
unsigned cExecs = 0;
unsigned iExe = ~0U;
unsigned i = pThis->cImages;
while (i-- > 0)
{
if (!pThis->aImages[i].fDll)
{
cExecs++;
iExe = i;
}
}
if (cExecs == 0)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
"No executable mapping found in the virtual address space.");
if (cExecs != 1)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
"Found more than one executable mapping in the virtual address space.");
PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
/*
* Check that it matches the executable image of the process.
*/
int rc;
ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
PUNICODE_STRING pUniStr = (PUNICODE_STRING)suplibHardenedAllocZ(cbUniStr);
if (!pUniStr)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
"Error allocating %zu bytes for process name.", cbUniStr);
ULONG cbIgn = 0;
NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
if (NT_SUCCESS(rcNt))
{
if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
rc = VINF_SUCCESS;
else
{
pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
"Process image name does not match the exectuable we found: %ls vs %ls.",
pUniStr->Buffer, pImage->Name.UniStr.Buffer);
}
}
else
rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
"NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
suplibHardenedFree(pUniStr);
if (RT_FAILURE(rc))
return rc;
/*
* Validate the signing of the executable image.
* This will load the fDllCharecteristics and fImageCharecteristics members we use below.
*/
rc = supHardNtVpVerifyImage(pThis, pImage, hProcess);
if (RT_FAILURE(rc))
return rc;
/*
* Check linking requirements.
*/
SECTION_IMAGE_INFORMATION ImageInfo;
rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
if (!NT_SUCCESS(rcNt))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
"NtQueryInformationProcess/ProcessImageInformation failed: %#x", rcNt);
if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
"EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
ImageInfo.DllCharacteristics);
if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
"EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
ImageInfo.DllCharacteristics);
if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
"EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
ImageInfo.DllCharacteristics);
if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
"EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
"EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
return VINF_SUCCESS;
}
/**
* Check the integrity of the DLLs found in the process.
*
* @returns VBox status code.
* @param pThis The process scanning state structure. Details
* about images are added to this.
* @param hProcess The process to verify.
*/
static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis, HANDLE hProcess)
{
/*
* Check for duplicate entries.
*/
uint32_t i = pThis->cImages;
while (i-- > 1)
{
const char *pszName = pThis->aImages[i].pszName;
uint32_t j = i;
while (j-- > 0)
if (pThis->aImages[j].pszName == pszName)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
"Duplicate image entries for %s: %ls and %ls",
pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
}
/*
* Check that both ntdll and kernel32 are present.
* ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
*/
uint32_t iNtDll = UINT32_MAX;
uint32_t iKernel32 = UINT32_MAX;
uint32_t iApiSetSchema = UINT32_MAX;
i = pThis->cImages;
while (i-- > 0)
if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
iNtDll = i;
else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
iKernel32 = i;
else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "apisetschema.dll") == 0)
iApiSetSchema = i;
if (iNtDll == UINT32_MAX)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
"The process has no NTDLL.DLL.");
if (iKernel32 == UINT32_MAX)
return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
"The process has no KERNEL32.DLL.");
/*
* Verify that the DLLs are correctly signed (by MS).
*/
i = pThis->cImages;
while (i-- > 0)
{
pThis->aImages[i].fNtCreateSectionPatch = i == iNtDll;
pThis->aImages[i].fApiSetSchemaOnlySection1 = i == iApiSetSchema && pThis->aImages[i].cRegions == 1;
int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i], hProcess);
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Verifies the given process.
*
* The following requirements are checked:
* - The process only has one thread, the calling thread.
* - The process has no debugger attached.
* - The executable image of the process is verified to be signed with
* certificate known to this code at build time.
* - The executable image is one of a predefined set.
* - The process has only a very limited set of system DLLs loaded.
* - The system DLLs signatures check out fine.
* - The only executable memory in the process belongs to the system DLLs and
* the executable image.
*
* @returns VBox status code.
* @param hProcess The process to verify.
* @param hThread A thread in the process (the caller).
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
{
int rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
if (RT_SUCCESS(rc))
rc = supHardNtVpDebugger(hProcess, pErrInfo);
#endif
if (RT_SUCCESS(rc))
{
PSUPHNTVPSTATE pThis = supHardNtVpCreateState(pErrInfo);
if (pThis)
{
rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
if (RT_SUCCESS(rc))
rc = supHardNtVpCheckExe(pThis, hProcess);
if (RT_SUCCESS(rc))
rc = supHardNtVpCheckDlls(pThis, hProcess);
suplibHardenedFree(pThis);
}
else
rc = VERR_SUP_VP_NO_MEMORY_STATE;
}
return rc;
}