SUPHardenedVerifyProcess-win.cpp revision 2be6efa3a888d76feb7ad8b050e0b6c7e2d66c36
2N/A * available from http://www.virtualbox.org. This file is free software;
2N/A# define IPRT_NT_MAP_TO_ZW
2N/A# include "SUPDrvInternal.h"
2N/A# include "SUPLibInternal.h"
2N/A#include "win/SUPHardenedVerify-win.h"
typedef struct SUPHNTVPREGION
typedef struct SUPHNTVPIMAGE
const char *pszName;
/** Name structure for NtQueryVirtualMemory/MemorySectionName. */
} Name;
bool fDll;
bool fNtCreateSectionPatch;
bool f32bitResourceDll;
#ifdef IN_RING0
typedef struct SUPHNTVPSTATE
int rcResult;
#ifdef VBOX_PERMIT_VERIFIER_DLL
#ifdef VBOX_PERMIT_MORE
static const char *g_apszSupNtVpAllowedDlls[] =
#ifdef VBOX_PERMIT_VERIFIER_DLL
#ifdef VBOX_PERMIT_MORE
* @remarks Remember to keep in sync with SUPR3HardenedVerify.cpp.
static const char *g_apszSupNtVpAllowedVmExes[] =
/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
#ifdef IN_RING0
#ifdef IN_RING3
static SUPHNTLDRCACHEENTRY g_aSupNtVpLdrCacheEntries[RT_ELEMENTS(g_apszSupNtVpAllowedDlls) + 1 + 3];
#ifdef IN_RING3
return rc;
#ifdef IN_RING3
#ifdef IN_RING0
return pImage->pCacheEntry->pNtViRdr->Core.pfnRead(&pImage->pCacheEntry->pNtViRdr->Core, pvBuf, cbRead, off);
#ifdef IN_RING0
return STATUS_SUCCESS;
return STATUS_ACCESS_DENIED;
return rcNt;
#ifdef IN_RING3
static NTSTATUS supHardNtVpFileMemRestore(PSUPHNTVPSTATE pThis, PVOID pvRestoreAddr, uint8_t const *pbFile, uint32_t cbToRestore,
NTSTATUS rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_READWRITE, &fOldProt);
NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fCorrectProtection, &fOldProt);
return rcNt;
typedef struct SUPHNTVPSKIPAREA
AssertCompileAdjacentMembers(SUPHNTVPSTATE, abMemory, abFile); /* Use both the memory and file buffers here. Parfait might hate me for this... */
while (cb > 0)
if (cSkipAreas)
cbThis = 0;
SUP_DPRINTF(("%s: Differences in section #%u (%s) between file and memory:\n", pImage->pszName, iSh + 1, pachSectNm));
off++;
cDiffs++;
#ifdef IN_RING3
return VINF_SUCCESS;
if (!cb)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
return NULL;
static DECLCALLBACK(int) supHardNtVpGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol,
if (pImage)
return rc;
if (pImage)
return rc;
if (pImage)
return rc;
SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol '%s' in '%s' (forwarded from %s / %s): %Rrc\n",
SUP_DPRINTF(("supHardNtVpGetImport: Failed to find forwarder module '%s' (%#x / %s; originally %s / %#x / %s): %Rrc\n",
SUP_DPRINTF(("supHardNtVpGetImport: RTLdrQueryForwarderInfo failed on symbol %#x/'%s' in '%s': %Rrc\n",
return rc;
static int supHardNtVpVerifyImageMemoryCompare(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, PRTERRINFO pErrInfo)
#ifdef RT_ARCH_AMD64
if (pNtHdrs->FileHeader.SizeOfOptionalHeader != (fIs32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
if (pNtHdrs->OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
uint32_t cDirs = (fIs32Bit ? pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes : pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
suplibHardenedMemCopy(pThis->aSecHdrs, (fIs32Bit ? (void *)(pNtHdrs32 + 1) : (void *)(pNtHdrs + 1)),
uintptr_t const uImageBase = fIs32Bit ? pNtHdrs32->OptionalHeader.ImageBase : pNtHdrs->OptionalHeader.ImageBase;
uint32_t const cbImage = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfImage : pNtHdrs->OptionalHeader.SizeOfImage;
if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
uint32_t const cbSectAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.SectionAlignment : pNtHdrs->OptionalHeader.SectionAlignment;
uint32_t const cbFileAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.FileAlignment : pNtHdrs->OptionalHeader.FileAlignment;
if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
uint32_t const cbHeaders = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfHeaders : pNtHdrs->OptionalHeader.SizeOfHeaders;
pImage->fDllCharecteristics = fIs32Bit ? pNtHdrs32->OptionalHeader.DllCharacteristics : pNtHdrs->OptionalHeader.DllCharacteristics;
rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
return rc;
if (fIs32Bit)
rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "NtCreateSection", &uValue);
return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrLoadDll': %Rrc", pImage->pszName, rc);
rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
/* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
return rc;
return rc;
switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
case IMAGE_SCN_MEM_READ:
&& !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
case IMAGE_SCN_MEM_EXECUTE:
|| (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
return rc;
rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
return rc;
return VINF_SUCCESS;
int rc;
rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
return rc;
NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
if (!fAmI)
return VINF_SUCCESS;
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
if (uPtr != 0)
return VINF_SUCCESS;
if (!ch1)
static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
while ( cwcDirName > 0
pwszFilename--;
cwcDirName--;
if (!*pwszFilename)
while ( cwcDirName > 0
cwcDirName--;
# ifdef VBOX_PERMIT_MORE
|| !supHardViIsAppPatchDir(pImage->Name.UniStr.Buffer, pImage->Name.UniStr.Length / sizeof(WCHAR)) )
# ifdef VBOX_PERMIT_MORE
#ifdef IN_RING3
return VINF_OBJECT_DESTROYED;
SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
"See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
"Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
#ifdef VBOX_PERMIT_MORE
return VINF_SUCCESS;
static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
return VINF_SUCCESS;
pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
#ifdef VBOX_PERMIT_VERIFIER_DLL
(void const *)uPtrWhere,
&MemInfo,
sizeof(MemInfo),
&cbActual);
(void const *)uPtrWhere,
&cbActual);
pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
bool fNew = true;
while (iSearch-- > 0)
if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
return rc;
fNew = false;
if (fNew)
return rc;
return rc;
else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
&& cXpExceptions == 0
else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
# ifdef IN_RING3
* Free any private executable memory (sysplant.sys allocates executable memory).
/* The Trend Micro sakfile.sys and Digital Guardian dgmaster.sys BSOD kludge. */
if (pvCopy)
&cbWritten);
# ifndef IN_RING3
DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
return rc;
int rc;
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
|| pfnGetImport)
return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
return VINF_SUCCESS;
#ifdef IN_RING3
return &g_aSupNtVpLdrCacheEntries[i];
return NULL;
static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
#ifdef IN_RING0
&ObjAttr,
&Ios,
if (f32bitResourceDll)
return rc;
#ifdef IN_SUP_HARDENED_R3
return VINF_SUCCESS;
#ifdef IN_RING3
uint32_t i = 0;
return VERR_FILE_NOT_FOUND;
if (*ppEntry)
return VINF_SUCCESS;
return VERR_INTERNAL_ERROR_3;
int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
return VINF_SUCCESS;
return rc;
#ifdef IN_RING3
return rc;
#ifdef IN_RING3
return VINF_SUCCESS;
unsigned cExecs = 0;
unsigned iExe = ~0U;
cExecs++;
iExe = i;
if (cExecs == 0)
int rc;
if (!pUniStr)
NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
return rc;
return rc;
rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
return VINF_SUCCESS;
return VINF_SUCCESS;
uint32_t j = i;
iNtDll = i;
iKernel32 = i;
return rc;
return VINF_SUCCESS;
DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, uint32_t fFlags,
if (pcFixes)
*pcFixes = 0;
if (pThis)
if (pcFixes)
#ifdef IN_RING0
return rc;