[51770] | 1 | /* $Id: SUPHardenedVerifyProcess-win.cpp 104435 2024-04-25 16:51:16Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[51770] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
[51770] | 11 | *
|
---|
[96407] | 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
[51770] | 25 | * The contents of this file may alternatively be used under the terms
|
---|
| 26 | * of the Common Development and Distribution License Version 1.0
|
---|
[96407] | 27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
| 28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
[51770] | 29 | * CDDL are applicable instead of those of the GPL.
|
---|
| 30 | *
|
---|
| 31 | * You may elect to license modified versions of this file under the
|
---|
| 32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
[96407] | 33 | *
|
---|
| 34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
[51770] | 35 | */
|
---|
| 36 |
|
---|
[57358] | 37 |
|
---|
| 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[51770] | 41 | #ifdef IN_RING0
|
---|
[77816] | 42 | # ifndef IPRT_NT_MAP_TO_ZW
|
---|
| 43 | # define IPRT_NT_MAP_TO_ZW
|
---|
| 44 | # endif
|
---|
[51770] | 45 | # include <iprt/nt/nt.h>
|
---|
| 46 | # include <ntimage.h>
|
---|
| 47 | #else
|
---|
| 48 | # include <iprt/nt/nt-and-windows.h>
|
---|
| 49 | #endif
|
---|
| 50 |
|
---|
| 51 | #include <VBox/sup.h>
|
---|
| 52 | #include <VBox/err.h>
|
---|
[52213] | 53 | #include <iprt/alloca.h>
|
---|
[51770] | 54 | #include <iprt/ctype.h>
|
---|
[52213] | 55 | #include <iprt/param.h>
|
---|
| 56 | #include <iprt/string.h>
|
---|
[76412] | 57 | #include <iprt/utf16.h>
|
---|
[52204] | 58 | #include <iprt/zero.h>
|
---|
[51770] | 59 |
|
---|
| 60 | #ifdef IN_RING0
|
---|
| 61 | # include "SUPDrvInternal.h"
|
---|
| 62 | #else
|
---|
| 63 | # include "SUPLibInternal.h"
|
---|
| 64 | #endif
|
---|
| 65 | #include "win/SUPHardenedVerify-win.h"
|
---|
| 66 |
|
---|
| 67 |
|
---|
[57358] | 68 | /*********************************************************************************************************************************
|
---|
| 69 | * Structures and Typedefs *
|
---|
| 70 | *********************************************************************************************************************************/
|
---|
[51770] | 71 | /**
|
---|
| 72 | * Virtual address space region.
|
---|
| 73 | */
|
---|
| 74 | typedef struct SUPHNTVPREGION
|
---|
| 75 | {
|
---|
| 76 | /** The RVA of the region. */
|
---|
| 77 | uint32_t uRva;
|
---|
| 78 | /** The size of the region. */
|
---|
| 79 | uint32_t cb;
|
---|
| 80 | /** The protection of the region. */
|
---|
| 81 | uint32_t fProt;
|
---|
| 82 | } SUPHNTVPREGION;
|
---|
| 83 | /** Pointer to a virtual address space region. */
|
---|
| 84 | typedef SUPHNTVPREGION *PSUPHNTVPREGION;
|
---|
| 85 |
|
---|
| 86 | /**
|
---|
| 87 | * Virtual address space image information.
|
---|
| 88 | */
|
---|
| 89 | typedef struct SUPHNTVPIMAGE
|
---|
| 90 | {
|
---|
| 91 | /** The base address of the image. */
|
---|
| 92 | uintptr_t uImageBase;
|
---|
| 93 | /** The size of the image mapping. */
|
---|
| 94 | uintptr_t cbImage;
|
---|
| 95 |
|
---|
| 96 | /** The name from the allowed lists. */
|
---|
| 97 | const char *pszName;
|
---|
| 98 | /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
|
---|
| 99 | struct
|
---|
| 100 | {
|
---|
| 101 | /** The full unicode name. */
|
---|
| 102 | UNICODE_STRING UniStr;
|
---|
| 103 | /** Buffer space. */
|
---|
| 104 | WCHAR awcBuffer[260];
|
---|
| 105 | } Name;
|
---|
| 106 |
|
---|
| 107 | /** The number of mapping regions. */
|
---|
| 108 | uint32_t cRegions;
|
---|
| 109 | /** Mapping regions. */
|
---|
| 110 | SUPHNTVPREGION aRegions[16];
|
---|
| 111 |
|
---|
| 112 | /** The image characteristics from the FileHeader. */
|
---|
| 113 | uint16_t fImageCharecteristics;
|
---|
| 114 | /** The DLL characteristics from the OptionalHeader. */
|
---|
| 115 | uint16_t fDllCharecteristics;
|
---|
| 116 |
|
---|
| 117 | /** Set if this is the DLL. */
|
---|
| 118 | bool fDll;
|
---|
| 119 | /** Set if the image is NTDLL an the verficiation code needs to watch out for
|
---|
| 120 | * the NtCreateSection patch. */
|
---|
| 121 | bool fNtCreateSectionPatch;
|
---|
| 122 | /** Whether the API set schema hack needs to be applied when verifying memory
|
---|
| 123 | * content. The hack means that we only check if the 1st section is mapped. */
|
---|
| 124 | bool fApiSetSchemaOnlySection1;
|
---|
[52030] | 125 | /** This may be a 32-bit resource DLL. */
|
---|
| 126 | bool f32bitResourceDll;
|
---|
[52204] | 127 |
|
---|
[52366] | 128 | /** Pointer to the loader cache entry for the image. */
|
---|
| 129 | PSUPHNTLDRCACHEENTRY pCacheEntry;
|
---|
| 130 | #ifdef IN_RING0
|
---|
| 131 | /** In ring-0 we don't currently cache images, so put it here. */
|
---|
| 132 | SUPHNTLDRCACHEENTRY CacheEntry;
|
---|
| 133 | #endif
|
---|
[51770] | 134 | } SUPHNTVPIMAGE;
|
---|
| 135 | /** Pointer to image info from the virtual address space scan. */
|
---|
| 136 | typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
|
---|
| 137 |
|
---|
| 138 | /**
|
---|
| 139 | * Virtual address space scanning state.
|
---|
| 140 | */
|
---|
| 141 | typedef struct SUPHNTVPSTATE
|
---|
| 142 | {
|
---|
[52204] | 143 | /** Type of verification to perform. */
|
---|
| 144 | SUPHARDNTVPKIND enmKind;
|
---|
[53017] | 145 | /** Combination of SUPHARDNTVP_F_XXX. */
|
---|
| 146 | uint32_t fFlags;
|
---|
[51977] | 147 | /** The result. */
|
---|
| 148 | int rcResult;
|
---|
[52529] | 149 | /** Number of fixes we've done.
|
---|
| 150 | * Only applicable in the purification modes. */
|
---|
| 151 | uint32_t cFixes;
|
---|
[51770] | 152 | /** Number of images in aImages. */
|
---|
[51977] | 153 | uint32_t cImages;
|
---|
[52213] | 154 | /** The index of the last image we looked up. */
|
---|
| 155 | uint32_t iImageHint;
|
---|
[52204] | 156 | /** The process handle. */
|
---|
| 157 | HANDLE hProcess;
|
---|
[51770] | 158 | /** Images found in the process.
|
---|
| 159 | * The array is large enough to hold the executable, all allowed DLLs, and one
|
---|
| 160 | * more so we can get the image name of the first unwanted DLL. */
|
---|
[51999] | 161 | SUPHNTVPIMAGE aImages[1 + 6 + 1
|
---|
[52529] | 162 | #ifdef VBOX_PERMIT_VERIFIER_DLL
|
---|
| 163 | + 1
|
---|
| 164 | #endif
|
---|
[52030] | 165 | #ifdef VBOX_PERMIT_MORE
|
---|
| 166 | + 5
|
---|
| 167 | #endif
|
---|
[51999] | 168 | #ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
|
---|
| 169 | + 16
|
---|
| 170 | #endif
|
---|
| 171 | ];
|
---|
[51770] | 172 | /** Memory compare scratch buffer.*/
|
---|
| 173 | uint8_t abMemory[_4K];
|
---|
| 174 | /** File compare scratch buffer.*/
|
---|
| 175 | uint8_t abFile[_4K];
|
---|
| 176 | /** Section headers for use when comparing file and loaded image. */
|
---|
| 177 | IMAGE_SECTION_HEADER aSecHdrs[16];
|
---|
[51977] | 178 | /** Pointer to the error info. */
|
---|
| 179 | PRTERRINFO pErrInfo;
|
---|
[51770] | 180 | } SUPHNTVPSTATE;
|
---|
| 181 | /** Pointer to stat information of a virtual address space scan. */
|
---|
| 182 | typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
|
---|
| 183 |
|
---|
| 184 |
|
---|
[57358] | 185 | /*********************************************************************************************************************************
|
---|
| 186 | * Global Variables *
|
---|
| 187 | *********************************************************************************************************************************/
|
---|
[51770] | 188 | /**
|
---|
| 189 | * System DLLs allowed to be loaded into the process.
|
---|
| 190 | * @remarks supHardNtVpCheckDlls assumes these are lower case.
|
---|
| 191 | */
|
---|
| 192 | static const char *g_apszSupNtVpAllowedDlls[] =
|
---|
| 193 | {
|
---|
| 194 | "ntdll.dll",
|
---|
| 195 | "kernel32.dll",
|
---|
| 196 | "kernelbase.dll",
|
---|
| 197 | "apphelp.dll",
|
---|
[51977] | 198 | "apisetschema.dll",
|
---|
[52529] | 199 | #ifdef VBOX_PERMIT_VERIFIER_DLL
|
---|
| 200 | "verifier.dll",
|
---|
| 201 | #endif
|
---|
[52030] | 202 | #ifdef VBOX_PERMIT_MORE
|
---|
| 203 | # define VBOX_PERMIT_MORE_FIRST_IDX 5
|
---|
[51999] | 204 | "sfc.dll",
|
---|
[52030] | 205 | "sfc_os.dll",
|
---|
| 206 | "user32.dll",
|
---|
| 207 | "acres.dll",
|
---|
| 208 | "acgenral.dll",
|
---|
| 209 | #endif
|
---|
[51999] | 210 | #ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
|
---|
| 211 | "psapi.dll",
|
---|
| 212 | "msvcrt.dll",
|
---|
| 213 | "advapi32.dll",
|
---|
| 214 | "sechost.dll",
|
---|
| 215 | "rpcrt4.dll",
|
---|
| 216 | "SamplingRuntime.dll",
|
---|
| 217 | #endif
|
---|
[51770] | 218 | };
|
---|
| 219 |
|
---|
| 220 | /**
|
---|
| 221 | * VBox executables allowed to start VMs.
|
---|
[57351] | 222 | * @remarks Remember to keep in sync with g_aSupInstallFiles in
|
---|
| 223 | * SUPR3HardenedVerify.cpp.
|
---|
[51770] | 224 | */
|
---|
| 225 | static const char *g_apszSupNtVpAllowedVmExes[] =
|
---|
| 226 | {
|
---|
| 227 | "VBoxHeadless.exe",
|
---|
[72247] | 228 | "VirtualBoxVM.exe",
|
---|
[51770] | 229 | "VBoxSDL.exe",
|
---|
| 230 | "VBoxNetDHCP.exe",
|
---|
| 231 | "VBoxNetNAT.exe",
|
---|
[58374] | 232 | "VBoxVMMPreload.exe",
|
---|
[51910] | 233 |
|
---|
| 234 | "tstMicro.exe",
|
---|
| 235 | "tstPDMAsyncCompletion.exe",
|
---|
| 236 | "tstPDMAsyncCompletionStress.exe",
|
---|
| 237 | "tstVMM.exe",
|
---|
| 238 | "tstVMREQ.exe",
|
---|
| 239 | "tstCFGM.exe",
|
---|
[57351] | 240 | "tstGIP-2.exe",
|
---|
[51910] | 241 | "tstIntNet-1.exe",
|
---|
| 242 | "tstMMHyperHeap.exe",
|
---|
[54453] | 243 | "tstRTR0ThreadPreemptionDriver.exe",
|
---|
[51910] | 244 | "tstRTR0MemUserKernelDriver.exe",
|
---|
| 245 | "tstRTR0SemMutexDriver.exe",
|
---|
| 246 | "tstRTR0TimerDriver.exe",
|
---|
| 247 | "tstSSM.exe",
|
---|
[51770] | 248 | };
|
---|
| 249 |
|
---|
| 250 | /** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
|
---|
| 251 | * ring-0, in ring-3 it's just a slightly confusing define. */
|
---|
| 252 | #ifdef IN_RING0
|
---|
| 253 | PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
|
---|
| 254 | #else
|
---|
| 255 | # define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
|
---|
| 256 | #endif
|
---|
| 257 |
|
---|
[52366] | 258 | #ifdef IN_RING3
|
---|
[58339] | 259 | /** The number of valid entries in the loader cache. */
|
---|
[52366] | 260 | static uint32_t g_cSupNtVpLdrCacheEntries = 0;
|
---|
| 261 | /** The loader cache entries. */
|
---|
| 262 | static SUPHNTLDRCACHEENTRY g_aSupNtVpLdrCacheEntries[RT_ELEMENTS(g_apszSupNtVpAllowedDlls) + 1 + 3];
|
---|
| 263 | #endif
|
---|
[51770] | 264 |
|
---|
[52366] | 265 |
|
---|
[51770] | 266 | /**
|
---|
| 267 | * Fills in error information.
|
---|
| 268 | *
|
---|
| 269 | * @returns @a rc.
|
---|
[51977] | 270 | * @param pErrInfo Pointer to the extended error info structure.
|
---|
[51770] | 271 | * Can be NULL.
|
---|
| 272 | * @param rc The status to return.
|
---|
| 273 | * @param pszMsg The format string for the message.
|
---|
| 274 | * @param ... The arguments for the format string.
|
---|
| 275 | */
|
---|
[51977] | 276 | static int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
|
---|
[51770] | 277 | {
|
---|
| 278 | va_list va;
|
---|
| 279 | #ifdef IN_RING3
|
---|
| 280 | va_start(va, pszMsg);
|
---|
| 281 | supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
|
---|
| 282 | va_end(va);
|
---|
| 283 | #endif
|
---|
| 284 |
|
---|
| 285 | va_start(va, pszMsg);
|
---|
| 286 | RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
|
---|
| 287 | va_end(va);
|
---|
| 288 |
|
---|
| 289 | return rc;
|
---|
| 290 | }
|
---|
| 291 |
|
---|
| 292 |
|
---|
[51977] | 293 | /**
|
---|
[60700] | 294 | * Adds error information.
|
---|
| 295 | *
|
---|
| 296 | * @returns @a rc.
|
---|
| 297 | * @param pErrInfo Pointer to the extended error info structure
|
---|
| 298 | * which may contain some details already. Can be
|
---|
| 299 | * NULL.
|
---|
| 300 | * @param rc The status to return.
|
---|
| 301 | * @param pszMsg The format string for the message.
|
---|
| 302 | * @param ... The arguments for the format string.
|
---|
| 303 | */
|
---|
| 304 | static int supHardNtVpAddInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
|
---|
| 305 | {
|
---|
| 306 | va_list va;
|
---|
| 307 | #ifdef IN_RING3
|
---|
| 308 | va_start(va, pszMsg);
|
---|
| 309 | if (pErrInfo && pErrInfo->pszMsg)
|
---|
| 310 | supR3HardenedError(rc, false /*fFatal*/, "%N - %s\n", pszMsg, &va, pErrInfo->pszMsg);
|
---|
| 311 | else
|
---|
| 312 | supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
|
---|
| 313 | va_end(va);
|
---|
| 314 | #endif
|
---|
| 315 |
|
---|
| 316 | va_start(va, pszMsg);
|
---|
| 317 | RTErrInfoAddV(pErrInfo, rc, pszMsg, va);
|
---|
| 318 | va_end(va);
|
---|
| 319 |
|
---|
| 320 | return rc;
|
---|
| 321 | }
|
---|
| 322 |
|
---|
| 323 |
|
---|
| 324 | /**
|
---|
[51977] | 325 | * Fills in error information.
|
---|
| 326 | *
|
---|
| 327 | * @returns @a rc.
|
---|
| 328 | * @param pThis The process validator instance.
|
---|
| 329 | * @param rc The status to return.
|
---|
| 330 | * @param pszMsg The format string for the message.
|
---|
| 331 | * @param ... The arguments for the format string.
|
---|
| 332 | */
|
---|
| 333 | static int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
|
---|
| 334 | {
|
---|
| 335 | va_list va;
|
---|
| 336 | #ifdef IN_RING3
|
---|
| 337 | va_start(va, pszMsg);
|
---|
| 338 | supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
|
---|
| 339 | va_end(va);
|
---|
| 340 | #endif
|
---|
| 341 |
|
---|
| 342 | va_start(va, pszMsg);
|
---|
| 343 | #ifdef IN_RING0
|
---|
| 344 | RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
|
---|
| 345 | pThis->rcResult = rc;
|
---|
| 346 | #else
|
---|
| 347 | if (RT_SUCCESS(pThis->rcResult))
|
---|
| 348 | {
|
---|
| 349 | RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
|
---|
| 350 | pThis->rcResult = rc;
|
---|
| 351 | }
|
---|
| 352 | else
|
---|
| 353 | {
|
---|
| 354 | RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
|
---|
| 355 | RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
|
---|
| 356 | }
|
---|
| 357 | #endif
|
---|
| 358 | va_end(va);
|
---|
| 359 |
|
---|
| 360 | return pThis->rcResult;
|
---|
| 361 | }
|
---|
| 362 |
|
---|
| 363 |
|
---|
[52204] | 364 | static int supHardNtVpReadImage(PSUPHNTVPIMAGE pImage, uint64_t off, void *pvBuf, size_t cbRead)
|
---|
[51770] | 365 | {
|
---|
[52366] | 366 | return pImage->pCacheEntry->pNtViRdr->Core.pfnRead(&pImage->pCacheEntry->pNtViRdr->Core, pvBuf, cbRead, off);
|
---|
[51770] | 367 | }
|
---|
| 368 |
|
---|
| 369 |
|
---|
| 370 | static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
|
---|
| 371 | {
|
---|
| 372 | #ifdef IN_RING0
|
---|
| 373 | /* ASSUMES hProcess is the current process. */
|
---|
[62677] | 374 | RT_NOREF1(hProcess);
|
---|
[51770] | 375 | /** @todo use MmCopyVirtualMemory where available! */
|
---|
| 376 | int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
|
---|
| 377 | if (RT_SUCCESS(rc))
|
---|
| 378 | return STATUS_SUCCESS;
|
---|
| 379 | return STATUS_ACCESS_DENIED;
|
---|
| 380 | #else
|
---|
| 381 | SIZE_T cbIgn;
|
---|
| 382 | NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
|
---|
| 383 | if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
|
---|
| 384 | rcNt = STATUS_IO_DEVICE_ERROR;
|
---|
| 385 | return rcNt;
|
---|
| 386 | #endif
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 |
|
---|
[52204] | 390 | #ifdef IN_RING3
|
---|
| 391 | static NTSTATUS supHardNtVpFileMemRestore(PSUPHNTVPSTATE pThis, PVOID pvRestoreAddr, uint8_t const *pbFile, uint32_t cbToRestore,
|
---|
| 392 | uint32_t fCorrectProtection)
|
---|
[51770] | 393 | {
|
---|
[52204] | 394 | PVOID pvProt = pvRestoreAddr;
|
---|
| 395 | SIZE_T cbProt = cbToRestore;
|
---|
| 396 | ULONG fOldProt = 0;
|
---|
| 397 | NTSTATUS rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_READWRITE, &fOldProt);
|
---|
| 398 | if (NT_SUCCESS(rcNt))
|
---|
| 399 | {
|
---|
| 400 | SIZE_T cbIgnored;
|
---|
| 401 | rcNt = NtWriteVirtualMemory(pThis->hProcess, pvRestoreAddr, pbFile, cbToRestore, &cbIgnored);
|
---|
[51770] | 402 |
|
---|
[52204] | 403 | pvProt = pvRestoreAddr;
|
---|
| 404 | cbProt = cbToRestore;
|
---|
| 405 | NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fCorrectProtection, &fOldProt);
|
---|
| 406 | if (NT_SUCCESS(rcNt))
|
---|
| 407 | rcNt = rcNt2;
|
---|
| 408 | }
|
---|
[52529] | 409 | pThis->cFixes++;
|
---|
[52204] | 410 | return rcNt;
|
---|
| 411 | }
|
---|
| 412 | #endif /* IN_RING3 */
|
---|
| 413 |
|
---|
| 414 |
|
---|
| 415 | typedef struct SUPHNTVPSKIPAREA
|
---|
| 416 | {
|
---|
| 417 | uint32_t uRva;
|
---|
| 418 | uint32_t cb;
|
---|
| 419 | } SUPHNTVPSKIPAREA;
|
---|
| 420 | typedef SUPHNTVPSKIPAREA *PSUPHNTVPSKIPAREA;
|
---|
| 421 |
|
---|
| 422 | static int supHardNtVpFileMemCompareSection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
|
---|
| 423 | uint32_t uRva, uint32_t cb, const uint8_t *pbFile,
|
---|
| 424 | int32_t iSh, PSUPHNTVPSKIPAREA paSkipAreas, uint32_t cSkipAreas,
|
---|
| 425 | uint32_t fCorrectProtection)
|
---|
| 426 | {
|
---|
[62677] | 427 | #ifndef IN_RING3
|
---|
| 428 | RT_NOREF1(fCorrectProtection);
|
---|
| 429 | #endif
|
---|
[52204] | 430 | AssertCompileAdjacentMembers(SUPHNTVPSTATE, abMemory, abFile); /* Use both the memory and file buffers here. Parfait might hate me for this... */
|
---|
| 431 | uint32_t const cbMemory = sizeof(pThis->abMemory) + sizeof(pThis->abFile);
|
---|
| 432 | uint8_t * const pbMemory = &pThis->abMemory[0];
|
---|
| 433 |
|
---|
| 434 | while (cb > 0)
|
---|
[51770] | 435 | {
|
---|
[52204] | 436 | uint32_t cbThis = RT_MIN(cb, cbMemory);
|
---|
| 437 |
|
---|
| 438 | /* Clipping. */
|
---|
| 439 | uint32_t uNextRva = uRva + cbThis;
|
---|
| 440 | if (cSkipAreas)
|
---|
| 441 | {
|
---|
| 442 | uint32_t uRvaEnd = uNextRva;
|
---|
| 443 | uint32_t i = cSkipAreas;
|
---|
| 444 | while (i-- > 0)
|
---|
| 445 | {
|
---|
| 446 | uint32_t uSkipEnd = paSkipAreas[i].uRva + paSkipAreas[i].cb;
|
---|
| 447 | if ( uRva < uSkipEnd
|
---|
| 448 | && uRvaEnd > paSkipAreas[i].uRva)
|
---|
| 449 | {
|
---|
| 450 | if (uRva < paSkipAreas[i].uRva)
|
---|
| 451 | {
|
---|
| 452 | cbThis = paSkipAreas[i].uRva - uRva;
|
---|
| 453 | uRvaEnd = paSkipAreas[i].uRva;
|
---|
| 454 | uNextRva = uSkipEnd;
|
---|
| 455 | }
|
---|
| 456 | else if (uRvaEnd >= uSkipEnd)
|
---|
| 457 | {
|
---|
| 458 | cbThis -= uSkipEnd - uRva;
|
---|
[55689] | 459 | pbFile += uSkipEnd - uRva;
|
---|
[52204] | 460 | uRva = uSkipEnd;
|
---|
| 461 | }
|
---|
| 462 | else
|
---|
| 463 | {
|
---|
| 464 | uNextRva = uSkipEnd;
|
---|
| 465 | cbThis = 0;
|
---|
| 466 | break;
|
---|
| 467 | }
|
---|
| 468 | }
|
---|
| 469 | }
|
---|
| 470 | }
|
---|
| 471 |
|
---|
| 472 | /* Read the memory. */
|
---|
| 473 | NTSTATUS rcNt = supHardNtVpReadMem(pThis->hProcess, pImage->uImageBase + uRva, pbMemory, cbThis);
|
---|
| 474 | if (!NT_SUCCESS(rcNt))
|
---|
| 475 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
|
---|
| 476 | "%s: Error reading %#x bytes at %p (rva %#x, #%u, %.8s) from memory: %#x",
|
---|
| 477 | pImage->pszName, cbThis, pImage->uImageBase + uRva, uRva, iSh + 1,
|
---|
| 478 | iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers", rcNt);
|
---|
| 479 |
|
---|
| 480 | /* Do the compare. */
|
---|
| 481 | if (memcmp(pbFile, pbMemory, cbThis) != 0)
|
---|
| 482 | {
|
---|
| 483 | const char *pachSectNm = iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers";
|
---|
| 484 | SUP_DPRINTF(("%s: Differences in section #%u (%s) between file and memory:\n", pImage->pszName, iSh + 1, pachSectNm));
|
---|
| 485 |
|
---|
| 486 | uint32_t off = 0;
|
---|
| 487 | while (off < cbThis && pbFile[off] == pbMemory[off])
|
---|
| 488 | off++;
|
---|
| 489 | SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
|
---|
| 490 | pImage->uImageBase + uRva + off, uRva + off, pbFile[off], pbMemory[off]));
|
---|
| 491 | uint32_t offLast = off;
|
---|
| 492 | uint32_t cDiffs = 1;
|
---|
| 493 | for (uint32_t off2 = off + 1; off2 < cbThis; off2++)
|
---|
| 494 | if (pbFile[off2] != pbMemory[off2])
|
---|
| 495 | {
|
---|
| 496 | SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
|
---|
| 497 | pImage->uImageBase + uRva + off2, uRva + off2, pbFile[off2], pbMemory[off2]));
|
---|
| 498 | cDiffs++;
|
---|
| 499 | offLast = off2;
|
---|
| 500 | }
|
---|
| 501 |
|
---|
| 502 | #ifdef IN_RING3
|
---|
| 503 | if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
|
---|
[80216] | 504 | || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
|
---|
| 505 | || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
[52204] | 506 | {
|
---|
| 507 | PVOID pvRestoreAddr = (uint8_t *)pImage->uImageBase + uRva;
|
---|
| 508 | rcNt = supHardNtVpFileMemRestore(pThis, pvRestoreAddr, pbFile, cbThis, fCorrectProtection);
|
---|
| 509 | if (NT_SUCCESS(rcNt))
|
---|
| 510 | SUP_DPRINTF((" Restored %#x bytes of original file content at %p\n", cbThis, pvRestoreAddr));
|
---|
| 511 | else
|
---|
| 512 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
|
---|
| 513 | "%s: Failed to restore %#x bytes at %p (%#x, #%u, %s): %#x (cDiffs=%#x, first=%#x)",
|
---|
| 514 | pImage->pszName, cbThis, pvRestoreAddr, uRva, iSh + 1, pachSectNm, rcNt,
|
---|
| 515 | cDiffs, uRva + off);
|
---|
| 516 | }
|
---|
| 517 | else
|
---|
| 518 | #endif /* IN_RING3 */
|
---|
| 519 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
|
---|
| 520 | "%s: %u differences between %#x and %#x in #%u (%.8s), first: %02x != %02x",
|
---|
| 521 | pImage->pszName, cDiffs, uRva + off, uRva + offLast, iSh + 1,
|
---|
| 522 | pachSectNm, pbFile[off], pbMemory[off]);
|
---|
| 523 | }
|
---|
| 524 |
|
---|
| 525 | /* Advance. The clipping makes it a little bit complicated. */
|
---|
| 526 | cbThis = uNextRva - uRva;
|
---|
| 527 | if (cbThis >= cb)
|
---|
| 528 | break;
|
---|
| 529 | cb -= cbThis;
|
---|
| 530 | pbFile += cbThis;
|
---|
| 531 | uRva = uNextRva;
|
---|
[51770] | 532 | }
|
---|
[52204] | 533 | return VINF_SUCCESS;
|
---|
[51770] | 534 | }
|
---|
| 535 |
|
---|
| 536 |
|
---|
[52204] | 537 |
|
---|
[51977] | 538 | static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
|
---|
| 539 | uint32_t uRva, uint32_t cb, uint32_t fProt)
|
---|
[51770] | 540 | {
|
---|
| 541 | uint32_t const cbOrg = cb;
|
---|
| 542 | if (!cb)
|
---|
| 543 | return VINF_SUCCESS;
|
---|
[52204] | 544 | if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
|
---|
[80216] | 545 | || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
|
---|
| 546 | || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
[52204] | 547 | return VINF_SUCCESS;
|
---|
[51770] | 548 |
|
---|
| 549 | for (uint32_t i = 0; i < pImage->cRegions; i++)
|
---|
| 550 | {
|
---|
| 551 | uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
|
---|
| 552 | if (offRegion < pImage->aRegions[i].cb)
|
---|
| 553 | {
|
---|
| 554 | uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
|
---|
| 555 | if ( pImage->aRegions[i].fProt != fProt
|
---|
| 556 | && ( fProt != PAGE_READWRITE
|
---|
| 557 | || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
|
---|
[51977] | 558 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
|
---|
| 559 | "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
|
---|
| 560 | pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
|
---|
[51770] | 561 | if (cbLeft >= cb)
|
---|
| 562 | return VINF_SUCCESS;
|
---|
| 563 | cb -= cbLeft;
|
---|
| 564 | uRva += cbLeft;
|
---|
| 565 |
|
---|
| 566 | #if 0 /* This shouldn't ever be necessary. */
|
---|
| 567 | if ( i + 1 < pImage->cRegions
|
---|
| 568 | && uRva < pImage->aRegions[i + 1].uRva)
|
---|
| 569 | {
|
---|
| 570 | cbLeft = pImage->aRegions[i + 1].uRva - uRva;
|
---|
| 571 | if (cbLeft >= cb)
|
---|
| 572 | return VINF_SUCCESS;
|
---|
| 573 | cb -= cbLeft;
|
---|
| 574 | uRva += cbLeft;
|
---|
| 575 | }
|
---|
| 576 | #endif
|
---|
| 577 | }
|
---|
| 578 | }
|
---|
| 579 |
|
---|
[51977] | 580 | return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
|
---|
| 581 | "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
|
---|
[51770] | 582 | }
|
---|
| 583 |
|
---|
| 584 |
|
---|
[60193] | 585 | DECLINLINE(bool) supHardNtVpIsModuleNameMatch(PSUPHNTVPIMAGE pImage, const char *pszModule)
|
---|
[52213] | 586 | {
|
---|
| 587 | if (pImage->fDll)
|
---|
| 588 | {
|
---|
| 589 | const char *pszImageNm = pImage->pszName;
|
---|
| 590 | for (;;)
|
---|
| 591 | {
|
---|
| 592 | char chLeft = *pszImageNm++;
|
---|
| 593 | char chRight = *pszModule++;
|
---|
| 594 | if (chLeft != chRight)
|
---|
| 595 | {
|
---|
| 596 | Assert(chLeft == RT_C_TO_LOWER(chLeft));
|
---|
| 597 | if (chLeft != RT_C_TO_LOWER(chRight))
|
---|
| 598 | {
|
---|
| 599 | if ( chRight == '\0'
|
---|
| 600 | && chLeft == '.'
|
---|
| 601 | && pszImageNm[0] == 'd'
|
---|
| 602 | && pszImageNm[1] == 'l'
|
---|
| 603 | && pszImageNm[2] == 'l'
|
---|
| 604 | && pszImageNm[3] == '\0')
|
---|
| 605 | return true;
|
---|
| 606 | break;
|
---|
| 607 | }
|
---|
| 608 | }
|
---|
| 609 |
|
---|
| 610 | if (chLeft == '\0')
|
---|
| 611 | return true;
|
---|
| 612 | }
|
---|
| 613 | }
|
---|
| 614 |
|
---|
| 615 | return false;
|
---|
| 616 | }
|
---|
| 617 |
|
---|
| 618 |
|
---|
[51770] | 619 | /**
|
---|
[52213] | 620 | * Worker for supHardNtVpGetImport that looks up a module in the module table.
|
---|
| 621 | *
|
---|
| 622 | * @returns Pointer to the module if found, NULL if not found.
|
---|
| 623 | * @param pThis The process validator instance.
|
---|
| 624 | * @param pszModule The name of the module we're looking for.
|
---|
| 625 | */
|
---|
| 626 | static PSUPHNTVPIMAGE supHardNtVpFindModule(PSUPHNTVPSTATE pThis, const char *pszModule)
|
---|
| 627 | {
|
---|
| 628 | /*
|
---|
| 629 | * Check out the hint first.
|
---|
| 630 | */
|
---|
| 631 | if ( pThis->iImageHint < pThis->cImages
|
---|
| 632 | && supHardNtVpIsModuleNameMatch(&pThis->aImages[pThis->iImageHint], pszModule))
|
---|
| 633 | return &pThis->aImages[pThis->iImageHint];
|
---|
| 634 |
|
---|
| 635 | /*
|
---|
| 636 | * Linear array search next.
|
---|
| 637 | */
|
---|
| 638 | uint32_t i = pThis->cImages;
|
---|
| 639 | while (i-- > 0)
|
---|
| 640 | if (supHardNtVpIsModuleNameMatch(&pThis->aImages[i], pszModule))
|
---|
| 641 | {
|
---|
| 642 | pThis->iImageHint = i;
|
---|
| 643 | return &pThis->aImages[i];
|
---|
| 644 | }
|
---|
| 645 |
|
---|
| 646 | /* No cigar. */
|
---|
| 647 | return NULL;
|
---|
| 648 | }
|
---|
| 649 |
|
---|
| 650 |
|
---|
| 651 | /**
|
---|
| 652 | * @callback_method_impl{FNRTLDRIMPORT}
|
---|
| 653 | */
|
---|
| 654 | static DECLCALLBACK(int) supHardNtVpGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol,
|
---|
| 655 | PRTLDRADDR pValue, void *pvUser)
|
---|
| 656 | {
|
---|
[62677] | 657 | RT_NOREF1(hLdrMod);
|
---|
[52213] | 658 | /*SUP_DPRINTF(("supHardNtVpGetImport: %s / %#x / %s.\n", pszModule, uSymbol, pszSymbol));*/
|
---|
| 659 | PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)pvUser;
|
---|
| 660 |
|
---|
| 661 | int rc = VERR_MODULE_NOT_FOUND;
|
---|
| 662 | PSUPHNTVPIMAGE pImage = supHardNtVpFindModule(pThis, pszModule);
|
---|
| 663 | if (pImage)
|
---|
| 664 | {
|
---|
[52366] | 665 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
|
---|
| 666 | pImage->uImageBase, uSymbol, pszSymbol, pValue);
|
---|
[52213] | 667 | if (RT_SUCCESS(rc))
|
---|
| 668 | return rc;
|
---|
| 669 | }
|
---|
| 670 | /*
|
---|
| 671 | * API set hacks.
|
---|
| 672 | */
|
---|
| 673 | else if (!RTStrNICmp(pszModule, RT_STR_TUPLE("api-ms-win-")))
|
---|
| 674 | {
|
---|
| 675 | static const char * const s_apszDlls[] = { "ntdll.dll", "kernelbase.dll", "kernel32.dll" };
|
---|
| 676 | for (uint32_t i = 0; i < RT_ELEMENTS(s_apszDlls); i++)
|
---|
| 677 | {
|
---|
| 678 | pImage = supHardNtVpFindModule(pThis, s_apszDlls[i]);
|
---|
| 679 | if (pImage)
|
---|
| 680 | {
|
---|
[52366] | 681 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
|
---|
| 682 | pImage->uImageBase, uSymbol, pszSymbol, pValue);
|
---|
[52213] | 683 | if (RT_SUCCESS(rc))
|
---|
| 684 | return rc;
|
---|
| 685 | if (rc != VERR_SYMBOL_NOT_FOUND)
|
---|
| 686 | break;
|
---|
| 687 | }
|
---|
| 688 | }
|
---|
| 689 | }
|
---|
| 690 |
|
---|
| 691 | /*
|
---|
| 692 | * Deal with forwarders.
|
---|
| 693 | * ASSUMES no forwarders thru any api-ms-win-core-*.dll.
|
---|
| 694 | * ASSUMES forwarders are resolved after one redirection.
|
---|
| 695 | */
|
---|
| 696 | if (rc == VERR_LDR_FORWARDER)
|
---|
| 697 | {
|
---|
| 698 | size_t cbInfo = RT_MIN((uint32_t)*pValue, sizeof(RTLDRIMPORTINFO) + 32);
|
---|
| 699 | PRTLDRIMPORTINFO pInfo = (PRTLDRIMPORTINFO)alloca(cbInfo);
|
---|
[52366] | 700 | rc = RTLdrQueryForwarderInfo(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
|
---|
| 701 | uSymbol, pszSymbol, pInfo, cbInfo);
|
---|
[52213] | 702 | if (RT_SUCCESS(rc))
|
---|
| 703 | {
|
---|
| 704 | rc = VERR_MODULE_NOT_FOUND;
|
---|
| 705 | pImage = supHardNtVpFindModule(pThis, pInfo->szModule);
|
---|
| 706 | if (pImage)
|
---|
| 707 | {
|
---|
[52366] | 708 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
|
---|
| 709 | pImage->uImageBase, pInfo->iOrdinal, pInfo->pszSymbol, pValue);
|
---|
[52213] | 710 | if (RT_SUCCESS(rc))
|
---|
| 711 | return rc;
|
---|
| 712 |
|
---|
| 713 | SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol '%s' in '%s' (forwarded from %s / %s): %Rrc\n",
|
---|
| 714 | pInfo->pszSymbol, pInfo->szModule, pszModule, pszSymbol, rc));
|
---|
| 715 | if (rc == VERR_LDR_FORWARDER)
|
---|
| 716 | rc = VERR_LDR_FORWARDER_CHAIN_TOO_LONG;
|
---|
| 717 | }
|
---|
| 718 | else
|
---|
| 719 | SUP_DPRINTF(("supHardNtVpGetImport: Failed to find forwarder module '%s' (%#x / %s; originally %s / %#x / %s): %Rrc\n",
|
---|
| 720 | pInfo->szModule, pInfo->iOrdinal, pInfo->pszSymbol, pszModule, uSymbol, pszSymbol, rc));
|
---|
| 721 | }
|
---|
| 722 | else
|
---|
| 723 | SUP_DPRINTF(("supHardNtVpGetImport: RTLdrQueryForwarderInfo failed on symbol %#x/'%s' in '%s': %Rrc\n",
|
---|
| 724 | uSymbol, pszSymbol, pszModule, rc));
|
---|
| 725 | }
|
---|
| 726 | else
|
---|
| 727 | SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol %#x / '%s' in '%s': %Rrc\n",
|
---|
| 728 | uSymbol, pszSymbol, pszModule, rc));
|
---|
| 729 | return rc;
|
---|
| 730 | }
|
---|
| 731 |
|
---|
| 732 |
|
---|
| 733 | /**
|
---|
[51770] | 734 | * Compares process memory with the disk content.
|
---|
| 735 | *
|
---|
| 736 | * @returns VBox status code.
|
---|
| 737 | * @param pThis The process scanning state structure (for the
|
---|
| 738 | * two scratch buffers).
|
---|
| 739 | * @param pImage The image data collected during the address
|
---|
| 740 | * space scan.
|
---|
| 741 | */
|
---|
[62677] | 742 | static int supHardNtVpVerifyImageMemoryCompare(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
|
---|
[51770] | 743 | {
|
---|
[62677] | 744 |
|
---|
[51770] | 745 | /*
|
---|
| 746 | * Read and find the file headers.
|
---|
| 747 | */
|
---|
[52204] | 748 | int rc = supHardNtVpReadImage(pImage, 0 /*off*/, pThis->abFile, sizeof(pThis->abFile));
|
---|
| 749 | if (RT_FAILURE(rc))
|
---|
[51977] | 750 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
|
---|
[52204] | 751 | "%s: Error reading image header: %Rrc", pImage->pszName, rc);
|
---|
[51770] | 752 |
|
---|
| 753 | uint32_t offNtHdrs = 0;
|
---|
| 754 | PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
|
---|
| 755 | if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
|
---|
| 756 | {
|
---|
| 757 | offNtHdrs = pDosHdr->e_lfanew;
|
---|
| 758 | if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
|
---|
[51977] | 759 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
|
---|
| 760 | "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
|
---|
[51770] | 761 | }
|
---|
[52030] | 762 | PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
|
---|
| 763 | PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)pNtHdrs;
|
---|
[51770] | 764 | if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
|
---|
[51977] | 765 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
|
---|
| 766 | "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
|
---|
[51770] | 767 |
|
---|
| 768 | /*
|
---|
| 769 | * Do basic header validation.
|
---|
| 770 | */
|
---|
| 771 | #ifdef RT_ARCH_AMD64
|
---|
[52030] | 772 | if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && !pImage->f32bitResourceDll)
|
---|
[51770] | 773 | #else
|
---|
| 774 | if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
|
---|
| 775 | #endif
|
---|
[51977] | 776 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
|
---|
| 777 | "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
|
---|
[52030] | 778 | bool const fIs32Bit = pNtHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
|
---|
[51770] | 779 |
|
---|
[52030] | 780 | if (pNtHdrs->FileHeader.SizeOfOptionalHeader != (fIs32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
|
---|
[51977] | 781 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
|
---|
| 782 | "%s: Unexpected optional header size: %#x",
|
---|
| 783 | pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
|
---|
[51770] | 784 |
|
---|
[52030] | 785 | if (pNtHdrs->OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
|
---|
[51977] | 786 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
|
---|
| 787 | "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
|
---|
[52030] | 788 |
|
---|
| 789 | uint32_t cDirs = (fIs32Bit ? pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes : pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
|
---|
| 790 | if (cDirs != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
|
---|
[51977] | 791 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
|
---|
[52030] | 792 | "%s: Unexpected data dirs: %#x", pImage->pszName, cDirs);
|
---|
[51770] | 793 |
|
---|
| 794 | /*
|
---|
| 795 | * Before we start comparing things, store what we need to know from the headers.
|
---|
| 796 | */
|
---|
| 797 | uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
|
---|
| 798 | if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
|
---|
[51977] | 799 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
|
---|
| 800 | "%s: Too many section headers: %#x", pImage->pszName, cSections);
|
---|
[52030] | 801 | suplibHardenedMemCopy(pThis->aSecHdrs, (fIs32Bit ? (void *)(pNtHdrs32 + 1) : (void *)(pNtHdrs + 1)),
|
---|
| 802 | cSections * sizeof(IMAGE_SECTION_HEADER));
|
---|
[51770] | 803 |
|
---|
[52030] | 804 | uintptr_t const uImageBase = fIs32Bit ? pNtHdrs32->OptionalHeader.ImageBase : pNtHdrs->OptionalHeader.ImageBase;
|
---|
[51770] | 805 | if (uImageBase & PAGE_OFFSET_MASK)
|
---|
[51977] | 806 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
|
---|
| 807 | "%s: Invalid image base: %p", pImage->pszName, uImageBase);
|
---|
[51770] | 808 |
|
---|
[52030] | 809 | uint32_t const cbImage = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfImage : pNtHdrs->OptionalHeader.SizeOfImage;
|
---|
[51770] | 810 | if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
|
---|
[51977] | 811 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
|
---|
| 812 | "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
|
---|
| 813 | pImage->pszName, cbImage, pImage->cbImage);
|
---|
[52366] | 814 | if (cbImage != RTLdrSize(pImage->pCacheEntry->hLdrMod))
|
---|
[52204] | 815 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
|
---|
| 816 | "%s: SizeOfImage (%#x) differs from what RTLdrSize returns (%#zx)",
|
---|
[52366] | 817 | pImage->pszName, cbImage, RTLdrSize(pImage->pCacheEntry->hLdrMod));
|
---|
[51770] | 818 |
|
---|
[52030] | 819 | uint32_t const cbSectAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.SectionAlignment : pNtHdrs->OptionalHeader.SectionAlignment;
|
---|
[51770] | 820 | if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
|
---|
| 821 | || cbSectAlign < PAGE_SIZE
|
---|
| 822 | || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
|
---|
[51977] | 823 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
|
---|
| 824 | "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
|
---|
[51770] | 825 |
|
---|
[52030] | 826 | uint32_t const cbFileAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.FileAlignment : pNtHdrs->OptionalHeader.FileAlignment;
|
---|
[51770] | 827 | if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
|
---|
[51977] | 828 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
|
---|
| 829 | "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
|
---|
| 830 | pImage->pszName, cbFileAlign, cbSectAlign);
|
---|
[51770] | 831 |
|
---|
[52030] | 832 | uint32_t const cbHeaders = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfHeaders : pNtHdrs->OptionalHeader.SizeOfHeaders;
|
---|
| 833 | uint32_t const cbMinHdrs = offNtHdrs + (fIs32Bit ? sizeof(*pNtHdrs32) : sizeof(*pNtHdrs) )
|
---|
| 834 | + sizeof(IMAGE_SECTION_HEADER) * cSections;
|
---|
[51770] | 835 | if (cbHeaders < cbMinHdrs)
|
---|
[51977] | 836 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
|
---|
| 837 | "%s: Headers are too small: %#x < %#x (cSections=%#x)",
|
---|
| 838 | pImage->pszName, cbHeaders, cbMinHdrs, cSections);
|
---|
[51770] | 839 | uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
|
---|
| 840 | if (cbHdrsFile > sizeof(pThis->abFile))
|
---|
[51977] | 841 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
|
---|
| 842 | "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
|
---|
| 843 | pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
|
---|
[51770] | 844 |
|
---|
| 845 | /*
|
---|
[52204] | 846 | * Save some header fields we might be using later on.
|
---|
| 847 | */
|
---|
| 848 | pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
|
---|
| 849 | pImage->fDllCharecteristics = fIs32Bit ? pNtHdrs32->OptionalHeader.DllCharacteristics : pNtHdrs->OptionalHeader.DllCharacteristics;
|
---|
| 850 |
|
---|
| 851 | /*
|
---|
| 852 | * Correct the apisetschema image base, size and region rva.
|
---|
| 853 | */
|
---|
| 854 | if (pImage->fApiSetSchemaOnlySection1)
|
---|
| 855 | {
|
---|
| 856 | pImage->uImageBase -= pThis->aSecHdrs[0].VirtualAddress;
|
---|
| 857 | pImage->cbImage += pThis->aSecHdrs[0].VirtualAddress;
|
---|
| 858 | pImage->aRegions[0].uRva = pThis->aSecHdrs[0].VirtualAddress;
|
---|
| 859 | }
|
---|
| 860 |
|
---|
| 861 | /*
|
---|
| 862 | * Get relocated bits.
|
---|
| 863 | */
|
---|
[52366] | 864 | uint8_t *pbBits;
|
---|
[52213] | 865 | if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
[52947] | 866 | rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
|
---|
| 867 | pThis->pErrInfo);
|
---|
[52213] | 868 | else
|
---|
[52947] | 869 | rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
|
---|
| 870 | pThis->pErrInfo);
|
---|
[52204] | 871 | if (RT_FAILURE(rc))
|
---|
[52947] | 872 | return rc;
|
---|
[52204] | 873 |
|
---|
[52207] | 874 | /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
|
---|
| 875 | if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
|
---|
| 876 | {
|
---|
| 877 | if (fIs32Bit)
|
---|
[52366] | 878 | ((PIMAGE_NT_HEADERS32)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = (uint32_t)pImage->uImageBase;
|
---|
[52207] | 879 | else
|
---|
[52366] | 880 | ((PIMAGE_NT_HEADERS)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = pImage->uImageBase;
|
---|
[52207] | 881 | }
|
---|
[52204] | 882 |
|
---|
| 883 | /*
|
---|
| 884 | * Figure out areas we should skip during comparison.
|
---|
| 885 | */
|
---|
| 886 | uint32_t cSkipAreas = 0;
|
---|
[81118] | 887 | SUPHNTVPSKIPAREA aSkipAreas[7];
|
---|
[52204] | 888 | if (pImage->fNtCreateSectionPatch)
|
---|
| 889 | {
|
---|
| 890 | RTLDRADDR uValue;
|
---|
| 891 | if (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
|
---|
| 892 | {
|
---|
| 893 | /* Ignore our NtCreateSection hack. */
|
---|
[52366] | 894 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "NtCreateSection", &uValue);
|
---|
[52204] | 895 | if (RT_FAILURE(rc))
|
---|
| 896 | return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
|
---|
| 897 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
|
---|
[52967] | 898 | aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
|
---|
[52403] | 899 |
|
---|
| 900 | /* Ignore our LdrLoadDll hack. */
|
---|
| 901 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrLoadDll", &uValue);
|
---|
| 902 | if (RT_FAILURE(rc))
|
---|
| 903 | return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrLoadDll': %Rrc", pImage->pszName, rc);
|
---|
| 904 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
|
---|
[52967] | 905 | aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
|
---|
[52204] | 906 | }
|
---|
| 907 |
|
---|
[52973] | 908 | /* Ignore our patched LdrInitializeThunk hack. */
|
---|
| 909 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
|
---|
| 910 | if (RT_FAILURE(rc))
|
---|
| 911 | return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
|
---|
| 912 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
|
---|
| 913 | aSkipAreas[cSkipAreas++].cb = 14;
|
---|
[52523] | 914 |
|
---|
[80212] | 915 | /* Ignore our patched KiUserApcDispatcher hack. */
|
---|
| 916 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserApcDispatcher", &uValue);
|
---|
| 917 | if (RT_FAILURE(rc))
|
---|
| 918 | return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserApcDispatcher': %Rrc", pImage->pszName, rc);
|
---|
| 919 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
|
---|
| 920 | aSkipAreas[cSkipAreas++].cb = 14;
|
---|
| 921 |
|
---|
[81118] | 922 | #ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
|
---|
| 923 | /* Ignore our patched KiUserExceptionDispatcher hack. */
|
---|
| 924 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserExceptionDispatcher", &uValue);
|
---|
| 925 | if (RT_FAILURE(rc))
|
---|
| 926 | return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserExceptionDispatcher': %Rrc", pImage->pszName, rc);
|
---|
| 927 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue + (HC_ARCH_BITS == 64);
|
---|
| 928 | aSkipAreas[cSkipAreas++].cb = HC_ARCH_BITS == 64 ? 13 : 12;
|
---|
| 929 | #endif
|
---|
| 930 |
|
---|
[52204] | 931 | /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
|
---|
[52366] | 932 | rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
|
---|
[52204] | 933 | if (RT_SUCCESS(rc))
|
---|
| 934 | {
|
---|
| 935 | aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
|
---|
[52366] | 936 | aSkipAreas[cSkipAreas++].cb = RT_MAX(pbBits[(uint32_t)uValue], 0x50);
|
---|
[52204] | 937 | }
|
---|
[52403] | 938 |
|
---|
| 939 | Assert(cSkipAreas <= RT_ELEMENTS(aSkipAreas));
|
---|
[52204] | 940 | }
|
---|
| 941 |
|
---|
| 942 | /*
|
---|
[51770] | 943 | * Compare the file header with the loaded bits. The loader will fiddle
|
---|
| 944 | * with image base, changing it to the actual load address.
|
---|
| 945 | */
|
---|
| 946 | if (!pImage->fApiSetSchemaOnlySection1)
|
---|
| 947 | {
|
---|
[52366] | 948 | rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
|
---|
[51770] | 949 | if (RT_FAILURE(rc))
|
---|
| 950 | return rc;
|
---|
[52204] | 951 |
|
---|
[51977] | 952 | rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
|
---|
[51770] | 953 | if (RT_FAILURE(rc))
|
---|
| 954 | return rc;
|
---|
| 955 | }
|
---|
| 956 |
|
---|
| 957 | /*
|
---|
[52204] | 958 | * Validate sections:
|
---|
| 959 | * - Check them against the mapping regions.
|
---|
| 960 | * - Check section bits according to enmKind.
|
---|
[51770] | 961 | */
|
---|
[52204] | 962 | uint32_t fPrevProt = PAGE_READONLY;
|
---|
| 963 | uint32_t uRva = cbHdrsFile;
|
---|
[51770] | 964 | for (uint32_t i = 0; i < cSections; i++)
|
---|
| 965 | {
|
---|
| 966 | /* Validate the section. */
|
---|
| 967 | uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
|
---|
| 968 | if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
|
---|
[51977] | 969 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
|
---|
| 970 | "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
|
---|
| 971 | pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
|
---|
[51770] | 972 | uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
|
---|
| 973 | if (cbMap > cbImage || uRva + cbMap > cbImage)
|
---|
[51977] | 974 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
|
---|
| 975 | "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
|
---|
| 976 | pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
|
---|
[51770] | 977 | uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
|
---|
| 978 | if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
|
---|
[51977] | 979 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
|
---|
| 980 | "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
|
---|
| 981 | pImage->pszName, i, cbFile, cbMap, uSectRva);
|
---|
[51770] | 982 |
|
---|
[52204] | 983 | /* Validate the protection and bits. */
|
---|
[51770] | 984 | if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
|
---|
| 985 | {
|
---|
| 986 | uint32_t fProt;
|
---|
| 987 | switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
|
---|
| 988 | {
|
---|
| 989 | case IMAGE_SCN_MEM_READ:
|
---|
| 990 | fProt = PAGE_READONLY;
|
---|
| 991 | break;
|
---|
| 992 | case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
|
---|
| 993 | fProt = PAGE_READWRITE;
|
---|
[52966] | 994 | if ( pThis->enmKind != SUPHARDNTVPKIND_VERIFY_ONLY
|
---|
| 995 | && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
|
---|
| 996 | && !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
|
---|
[51770] | 997 | fProt = PAGE_READONLY;
|
---|
| 998 | break;
|
---|
| 999 | case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
|
---|
| 1000 | fProt = PAGE_EXECUTE_READ;
|
---|
| 1001 | break;
|
---|
| 1002 | case IMAGE_SCN_MEM_EXECUTE:
|
---|
| 1003 | fProt = PAGE_EXECUTE;
|
---|
| 1004 | break;
|
---|
[52403] | 1005 | case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
|
---|
| 1006 | /* Only the executable is allowed to have this section,
|
---|
| 1007 | and it's protected after we're done patching. */
|
---|
| 1008 | if (!pImage->fDll)
|
---|
| 1009 | {
|
---|
| 1010 | if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
| 1011 | fProt = PAGE_EXECUTE_READWRITE;
|
---|
| 1012 | else
|
---|
| 1013 | fProt = PAGE_EXECUTE_READ;
|
---|
| 1014 | break;
|
---|
| 1015 | }
|
---|
[51770] | 1016 | default:
|
---|
[51977] | 1017 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
|
---|
| 1018 | "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
|
---|
| 1019 | pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
|
---|
[52204] | 1020 | }
|
---|
[51770] | 1021 |
|
---|
[52962] | 1022 | /* The section bits. Child purification verifies all, normal
|
---|
| 1023 | verification verifies all except where the executable is
|
---|
| 1024 | concerned (due to opening vboxdrv during early process init). */
|
---|
| 1025 | if ( ( (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
|
---|
[52403] | 1026 | && !(pThis->aSecHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
|
---|
[52962] | 1027 | || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
|
---|
| 1028 | || (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY && pImage->fDll)
|
---|
| 1029 | || pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
[52204] | 1030 | {
|
---|
| 1031 | rc = VINF_SUCCESS;
|
---|
| 1032 | if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
|
---|
[52366] | 1033 | rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pbBits + uRva,
|
---|
[52204] | 1034 | i - 1, NULL, 0, fPrevProt);
|
---|
| 1035 | if (RT_SUCCESS(rc))
|
---|
[52366] | 1036 | rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pbBits + uSectRva,
|
---|
[52204] | 1037 | i, aSkipAreas, cSkipAreas, fProt);
|
---|
| 1038 | if (RT_SUCCESS(rc))
|
---|
| 1039 | {
|
---|
| 1040 | uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
|
---|
| 1041 | ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
|
---|
| 1042 | if (cbMapAligned > cbMap)
|
---|
| 1043 | rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
|
---|
| 1044 | g_abRTZeroPage, i, NULL, 0, fProt);
|
---|
| 1045 | }
|
---|
| 1046 | if (RT_FAILURE(rc))
|
---|
| 1047 | return rc;
|
---|
[51770] | 1048 | }
|
---|
[52204] | 1049 |
|
---|
| 1050 | /* The protection (must be checked afterwards!). */
|
---|
[51977] | 1051 | rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
|
---|
[51770] | 1052 | if (RT_FAILURE(rc))
|
---|
| 1053 | return rc;
|
---|
[52204] | 1054 |
|
---|
| 1055 | fPrevProt = fProt;
|
---|
[51770] | 1056 | }
|
---|
| 1057 |
|
---|
| 1058 | /* Advance the RVA. */
|
---|
| 1059 | uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
|
---|
| 1060 | }
|
---|
| 1061 |
|
---|
| 1062 | return VINF_SUCCESS;
|
---|
| 1063 | }
|
---|
| 1064 |
|
---|
| 1065 |
|
---|
| 1066 | /**
|
---|
| 1067 | * Verifies the signature of the given image on disk, then checks if the memory
|
---|
| 1068 | * mapping matches what we verified.
|
---|
| 1069 | *
|
---|
| 1070 | * @returns VBox status code.
|
---|
| 1071 | * @param pThis The process scanning state structure (for the
|
---|
| 1072 | * two scratch buffers).
|
---|
| 1073 | * @param pImage The image data collected during the address
|
---|
| 1074 | * space scan.
|
---|
| 1075 | */
|
---|
[62677] | 1076 | static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
|
---|
[51770] | 1077 | {
|
---|
| 1078 | /*
|
---|
[52204] | 1079 | * Validate the file signature first, then do the memory compare.
|
---|
[51770] | 1080 | */
|
---|
[52204] | 1081 | int rc;
|
---|
[52366] | 1082 | if ( pImage->pCacheEntry != NULL
|
---|
| 1083 | && pImage->pCacheEntry->hLdrMod != NIL_RTLDRMOD)
|
---|
[52204] | 1084 | {
|
---|
[52366] | 1085 | rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
|
---|
[52204] | 1086 | if (RT_SUCCESS(rc))
|
---|
[62677] | 1087 | rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage);
|
---|
[52204] | 1088 | }
|
---|
| 1089 | else
|
---|
[52366] | 1090 | rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "pCacheEntry/hLdrMod is NIL! Impossible!");
|
---|
[51770] | 1091 | return rc;
|
---|
| 1092 | }
|
---|
| 1093 |
|
---|
| 1094 |
|
---|
| 1095 | /**
|
---|
| 1096 | * Verifies that there is only one thread in the process.
|
---|
| 1097 | *
|
---|
| 1098 | * @returns VBox status code.
|
---|
| 1099 | * @param hProcess The process.
|
---|
| 1100 | * @param hThread The thread.
|
---|
| 1101 | * @param pErrInfo Pointer to error info structure. Optional.
|
---|
| 1102 | */
|
---|
[52954] | 1103 | DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
|
---|
[51770] | 1104 | {
|
---|
[62677] | 1105 | RT_NOREF1(hProcess);
|
---|
| 1106 |
|
---|
[51770] | 1107 | /*
|
---|
| 1108 | * Use the ThreadAmILastThread request to check that there is only one
|
---|
| 1109 | * thread in the process.
|
---|
[52488] | 1110 | * Seems this isn't entirely reliable when hThread isn't the current thread?
|
---|
[51770] | 1111 | */
|
---|
| 1112 | ULONG cbIgn = 0;
|
---|
| 1113 | ULONG fAmI = 0;
|
---|
| 1114 | NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
|
---|
| 1115 | if (!NT_SUCCESS(rcNt))
|
---|
[51977] | 1116 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
|
---|
| 1117 | "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
|
---|
[51770] | 1118 | if (!fAmI)
|
---|
[51977] | 1119 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
|
---|
| 1120 | "More than one thread in process");
|
---|
[51770] | 1121 |
|
---|
[69774] | 1122 | /** @todo Would be nice to verify the relationship between hProcess and hThread
|
---|
[51770] | 1123 | * as well... */
|
---|
| 1124 | return VINF_SUCCESS;
|
---|
| 1125 | }
|
---|
| 1126 |
|
---|
| 1127 |
|
---|
| 1128 | /**
|
---|
| 1129 | * Verifies that there isn't a debugger attached to the process.
|
---|
| 1130 | *
|
---|
| 1131 | * @returns VBox status code.
|
---|
| 1132 | * @param hProcess The process.
|
---|
| 1133 | * @param pErrInfo Pointer to error info structure. Optional.
|
---|
| 1134 | */
|
---|
[52954] | 1135 | DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
|
---|
[51770] | 1136 | {
|
---|
[52954] | 1137 | #ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
|
---|
[51770] | 1138 | /*
|
---|
| 1139 | * Use the ProcessDebugPort request to check there is no debugger
|
---|
| 1140 | * currently attached to the process.
|
---|
| 1141 | */
|
---|
| 1142 | ULONG cbIgn = 0;
|
---|
| 1143 | uintptr_t uPtr = ~(uintptr_t)0;
|
---|
| 1144 | NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
|
---|
| 1145 | ProcessDebugPort,
|
---|
| 1146 | &uPtr, sizeof(uPtr), &cbIgn);
|
---|
| 1147 | if (!NT_SUCCESS(rcNt))
|
---|
[51977] | 1148 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
|
---|
| 1149 | "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
|
---|
[51770] | 1150 | if (uPtr != 0)
|
---|
[51977] | 1151 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
|
---|
| 1152 | "Debugger attached (%#zx)", uPtr);
|
---|
[62677] | 1153 | #else
|
---|
| 1154 | RT_NOREF2(hProcess, pErrInfo);
|
---|
[52954] | 1155 | #endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
|
---|
[51770] | 1156 | return VINF_SUCCESS;
|
---|
| 1157 | }
|
---|
| 1158 |
|
---|
| 1159 |
|
---|
| 1160 | /**
|
---|
| 1161 | * Matches two UNICODE_STRING structures in a case sensitive fashion.
|
---|
| 1162 | *
|
---|
| 1163 | * @returns true if equal, false if not.
|
---|
| 1164 | * @param pUniStr1 The first unicode string.
|
---|
| 1165 | * @param pUniStr2 The first unicode string.
|
---|
| 1166 | */
|
---|
| 1167 | static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
|
---|
| 1168 | {
|
---|
| 1169 | if (pUniStr1->Length != pUniStr2->Length)
|
---|
| 1170 | return false;
|
---|
| 1171 | return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
|
---|
| 1172 | }
|
---|
| 1173 |
|
---|
| 1174 |
|
---|
| 1175 | /**
|
---|
| 1176 | * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
|
---|
| 1177 | *
|
---|
| 1178 | * @returns true / false
|
---|
| 1179 | * @param pszName1 The ASCII name.
|
---|
| 1180 | * @param pwszName2 The UTF-16 name.
|
---|
| 1181 | */
|
---|
| 1182 | static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
|
---|
| 1183 | {
|
---|
| 1184 | for (;;)
|
---|
| 1185 | {
|
---|
| 1186 | char ch1 = *pszName1++;
|
---|
| 1187 | RTUTF16 wc2 = *pwszName2++;
|
---|
| 1188 | if (ch1 != wc2)
|
---|
| 1189 | {
|
---|
| 1190 | ch1 = RT_C_TO_LOWER(ch1);
|
---|
| 1191 | wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
|
---|
| 1192 | if (ch1 != wc2)
|
---|
| 1193 | return false;
|
---|
| 1194 | }
|
---|
| 1195 | if (!ch1)
|
---|
| 1196 | return true;
|
---|
| 1197 | }
|
---|
| 1198 | }
|
---|
| 1199 |
|
---|
| 1200 |
|
---|
| 1201 | /**
|
---|
[77816] | 1202 | * Compares two paths, expanding 8.3 short names as needed.
|
---|
| 1203 | *
|
---|
| 1204 | * @returns true / false.
|
---|
| 1205 | * @param pUniStr1 The first path. Must be zero terminated!
|
---|
| 1206 | * @param pUniStr2 The second path. Must be zero terminated!
|
---|
| 1207 | */
|
---|
| 1208 | static bool supHardNtVpArePathsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
|
---|
| 1209 | {
|
---|
| 1210 | /* Both strings must be null terminated. */
|
---|
| 1211 | Assert(pUniStr1->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
|
---|
| 1212 | Assert(pUniStr2->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
|
---|
| 1213 |
|
---|
| 1214 | /* Simple compare first.*/
|
---|
| 1215 | if (supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2))
|
---|
| 1216 | return true;
|
---|
| 1217 |
|
---|
| 1218 | /* Make long names if needed. */
|
---|
| 1219 | UNICODE_STRING UniStrLong1 = { 0, 0, NULL };
|
---|
| 1220 | if (RTNtPathFindPossible8dot3Name(pUniStr1->Buffer))
|
---|
| 1221 | {
|
---|
| 1222 | int rc = RTNtPathExpand8dot3PathA(pUniStr1, false /*fPathOnly*/, &UniStrLong1);
|
---|
| 1223 | if (RT_SUCCESS(rc))
|
---|
| 1224 | pUniStr1 = &UniStrLong1;
|
---|
| 1225 | }
|
---|
| 1226 |
|
---|
| 1227 | UNICODE_STRING UniStrLong2 = { 0, 0, NULL };
|
---|
| 1228 | if (RTNtPathFindPossible8dot3Name(pUniStr2->Buffer))
|
---|
| 1229 | {
|
---|
| 1230 | int rc = RTNtPathExpand8dot3PathA(pUniStr2, false /*fPathOnly*/, &UniStrLong2);
|
---|
| 1231 | if (RT_SUCCESS(rc))
|
---|
| 1232 | pUniStr2 = &UniStrLong2;
|
---|
| 1233 | }
|
---|
| 1234 |
|
---|
| 1235 | /* Compare again. */
|
---|
| 1236 | bool fCompare = supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2);
|
---|
| 1237 |
|
---|
| 1238 | /* Clean up. */
|
---|
| 1239 | if (UniStrLong1.Buffer)
|
---|
| 1240 | RTUtf16Free(UniStrLong1.Buffer);
|
---|
| 1241 | if (UniStrLong2.Buffer)
|
---|
| 1242 | RTUtf16Free(UniStrLong2.Buffer);
|
---|
| 1243 |
|
---|
| 1244 | return fCompare;
|
---|
| 1245 | }
|
---|
| 1246 |
|
---|
| 1247 |
|
---|
| 1248 | /**
|
---|
[51770] | 1249 | * Records an additional memory region for an image.
|
---|
| 1250 | *
|
---|
[54560] | 1251 | * May trash pThis->abMemory.
|
---|
| 1252 | *
|
---|
[51770] | 1253 | * @returns VBox status code.
|
---|
[52446] | 1254 | * @retval VINF_OBJECT_DESTROYED if we've unmapped the image (child
|
---|
| 1255 | * purification only).
|
---|
[51977] | 1256 | * @param pThis The process scanning state structure.
|
---|
[51770] | 1257 | * @param pImage The new image structure. Only the unicode name
|
---|
[54560] | 1258 | * buffer is valid (it's zero-terminated).
|
---|
[51770] | 1259 | * @param pMemInfo The memory information for the image.
|
---|
| 1260 | */
|
---|
[51977] | 1261 | static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
|
---|
[51770] | 1262 | {
|
---|
| 1263 | /*
|
---|
[54560] | 1264 | * If the filename or path contains short names, we have to get the long
|
---|
| 1265 | * path so that we will recognize the DLLs and their location.
|
---|
| 1266 | */
|
---|
[64737] | 1267 | int rc83Exp = VERR_IGNORED;
|
---|
[54560] | 1268 | PUNICODE_STRING pLongName = &pImage->Name.UniStr;
|
---|
[60480] | 1269 | if (RTNtPathFindPossible8dot3Name(pLongName->Buffer))
|
---|
[54560] | 1270 | {
|
---|
| 1271 | AssertCompile(sizeof(pThis->abMemory) > sizeof(pImage->Name));
|
---|
| 1272 | PUNICODE_STRING pTmp = (PUNICODE_STRING)pThis->abMemory;
|
---|
| 1273 | pTmp->MaximumLength = (USHORT)RT_MIN(_64K - 1, sizeof(pThis->abMemory) - sizeof(*pTmp)) - sizeof(RTUTF16);
|
---|
| 1274 | pTmp->Length = pImage->Name.UniStr.Length;
|
---|
| 1275 | pTmp->Buffer = (PRTUTF16)(pTmp + 1);
|
---|
| 1276 | memcpy(pTmp->Buffer, pLongName->Buffer, pLongName->Length + sizeof(RTUTF16));
|
---|
| 1277 |
|
---|
[64737] | 1278 | rc83Exp = RTNtPathExpand8dot3Path(pTmp, false /*fPathOnly*/);
|
---|
| 1279 | Assert(rc83Exp == VINF_SUCCESS);
|
---|
[54560] | 1280 | Assert(pTmp->Buffer[pTmp->Length / sizeof(RTUTF16)] == '\0');
|
---|
[67950] | 1281 | if (rc83Exp == VINF_SUCCESS)
|
---|
| 1282 | SUP_DPRINTF(("supHardNtVpNewImage: 8dot3 -> long: '%ls' -> '%ls'\n", pLongName->Buffer, pTmp->Buffer));
|
---|
| 1283 | else
|
---|
[64737] | 1284 | SUP_DPRINTF(("supHardNtVpNewImage: RTNtPathExpand8dot3Path returns %Rrc for '%ls' (-> '%ls')\n",
|
---|
| 1285 | rc83Exp, pLongName->Buffer, pTmp->Buffer));
|
---|
[54560] | 1286 |
|
---|
| 1287 | pLongName = pTmp;
|
---|
| 1288 | }
|
---|
| 1289 |
|
---|
| 1290 | /*
|
---|
[51770] | 1291 | * Extract the final component.
|
---|
| 1292 | */
|
---|
[54560] | 1293 | RTUTF16 wc;
|
---|
| 1294 | unsigned cwcDirName = pLongName->Length / sizeof(WCHAR);
|
---|
| 1295 | PCRTUTF16 pwszFilename = &pLongName->Buffer[cwcDirName];
|
---|
[51770] | 1296 | while ( cwcDirName > 0
|
---|
[54560] | 1297 | && (wc = pwszFilename[-1]) != '\\'
|
---|
| 1298 | && wc != '/'
|
---|
| 1299 | && wc != ':')
|
---|
[51770] | 1300 | {
|
---|
| 1301 | pwszFilename--;
|
---|
| 1302 | cwcDirName--;
|
---|
| 1303 | }
|
---|
| 1304 | if (!*pwszFilename)
|
---|
[51977] | 1305 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
|
---|
[54560] | 1306 | "Empty filename (len=%u) for image at %p.", pLongName->Length, pMemInfo->BaseAddress);
|
---|
[51770] | 1307 |
|
---|
| 1308 | /*
|
---|
| 1309 | * Drop trailing slashes from the directory name.
|
---|
| 1310 | */
|
---|
| 1311 | while ( cwcDirName > 0
|
---|
[54560] | 1312 | && ( pLongName->Buffer[cwcDirName - 1] == '\\'
|
---|
| 1313 | || pLongName->Buffer[cwcDirName - 1] == '/'))
|
---|
[51770] | 1314 | cwcDirName--;
|
---|
| 1315 |
|
---|
| 1316 | /*
|
---|
| 1317 | * Match it against known DLLs.
|
---|
| 1318 | */
|
---|
| 1319 | pImage->pszName = NULL;
|
---|
| 1320 | for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
|
---|
| 1321 | if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
|
---|
| 1322 | {
|
---|
| 1323 | pImage->pszName = g_apszSupNtVpAllowedDlls[i];
|
---|
| 1324 | pImage->fDll = true;
|
---|
| 1325 |
|
---|
[51999] | 1326 | #ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
|
---|
[51770] | 1327 | /* The directory name must match the one we've got for System32. */
|
---|
[52030] | 1328 | if ( ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
|
---|
[54560] | 1329 | || suplibHardenedMemComp(pLongName->Buffer, g_System32NtPath.UniStr.Buffer, cwcDirName * sizeof(WCHAR)) )
|
---|
[52030] | 1330 | # ifdef VBOX_PERMIT_MORE
|
---|
| 1331 | && ( pImage->pszName[0] != 'a'
|
---|
| 1332 | || pImage->pszName[1] != 'c'
|
---|
[54560] | 1333 | || !supHardViIsAppPatchDir(pLongName->Buffer, pLongName->Length / sizeof(WCHAR)) )
|
---|
[52030] | 1334 | # endif
|
---|
| 1335 | )
|
---|
[51977] | 1336 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
|
---|
| 1337 | "Expected %ls to be loaded from %ls.",
|
---|
[54560] | 1338 | pLongName->Buffer, g_System32NtPath.UniStr.Buffer);
|
---|
[52030] | 1339 | # ifdef VBOX_PERMIT_MORE
|
---|
| 1340 | if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
|
---|
| 1341 | pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
|
---|
| 1342 | # endif
|
---|
| 1343 |
|
---|
| 1344 | #endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
|
---|
[51770] | 1345 | break;
|
---|
| 1346 | }
|
---|
| 1347 | if (!pImage->pszName)
|
---|
| 1348 | {
|
---|
| 1349 | /*
|
---|
[52213] | 1350 | * Not a known DLL, is it a known executable?
|
---|
[51770] | 1351 | */
|
---|
| 1352 | for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
|
---|
| 1353 | if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
|
---|
| 1354 | {
|
---|
| 1355 | pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
|
---|
| 1356 | pImage->fDll = false;
|
---|
| 1357 | break;
|
---|
| 1358 | }
|
---|
| 1359 | }
|
---|
| 1360 | if (!pImage->pszName)
|
---|
[51977] | 1361 | {
|
---|
[52446] | 1362 | /*
|
---|
| 1363 | * Unknown image.
|
---|
| 1364 | *
|
---|
| 1365 | * If we're cleaning up a child process, we can unmap the offending
|
---|
| 1366 | * DLL... Might have interesting side effects, or at least interesting
|
---|
| 1367 | * as in "may you live in interesting times".
|
---|
| 1368 | */
|
---|
| 1369 | #ifdef IN_RING3
|
---|
[51977] | 1370 | if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
|
---|
[52446] | 1371 | && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
| 1372 | {
|
---|
| 1373 | SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping image mem at %p (%p LB %#zx) - '%ls'\n",
|
---|
[54560] | 1374 | pMemInfo->AllocationBase, pMemInfo->BaseAddress, pMemInfo->RegionSize, pwszFilename));
|
---|
[52446] | 1375 | NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pMemInfo->AllocationBase);
|
---|
| 1376 | if (NT_SUCCESS(rcNt))
|
---|
| 1377 | return VINF_OBJECT_DESTROYED;
|
---|
[52529] | 1378 | pThis->cFixes++;
|
---|
[52446] | 1379 | SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
|
---|
| 1380 | }
|
---|
[80216] | 1381 | else if (pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
| 1382 | {
|
---|
| 1383 | SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Ignoring unknown mem at %p LB %#zx (base %p) - '%ls'\n",
|
---|
| 1384 | pMemInfo->BaseAddress, pMemInfo->RegionSize, pMemInfo->AllocationBase, pwszFilename));
|
---|
| 1385 | return VINF_OBJECT_DESTROYED;
|
---|
| 1386 | }
|
---|
[52446] | 1387 | #endif
|
---|
| 1388 | /*
|
---|
| 1389 | * Special error message if we can.
|
---|
| 1390 | */
|
---|
| 1391 | if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
|
---|
[51977] | 1392 | && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
|
---|
| 1393 | || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
|
---|
[52446] | 1394 | || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)
|
---|
| 1395 | || supHardNtVpAreNamesEqual("sysfrethunk.dll", pwszFilename)) )
|
---|
[51977] | 1396 | {
|
---|
| 1397 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
|
---|
| 1398 | "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
|
---|
| 1399 | "You or your admin need to add and exception to the Application and Device Control (ADC) "
|
---|
| 1400 | "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
|
---|
| 1401 | "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
|
---|
[54560] | 1402 | , pLongName->Buffer, pMemInfo->BaseAddress);
|
---|
[51977] | 1403 | return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
|
---|
| 1404 | }
|
---|
| 1405 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
|
---|
[64737] | 1406 | "Unknown image file %ls at %p. (rc83Exp=%Rrc)",
|
---|
| 1407 | pLongName->Buffer, pMemInfo->BaseAddress, rc83Exp);
|
---|
[51977] | 1408 | }
|
---|
[51770] | 1409 |
|
---|
| 1410 | /*
|
---|
[52204] | 1411 | * Checks for multiple mappings of the same DLL but with different image file paths.
|
---|
| 1412 | */
|
---|
| 1413 | uint32_t i = pThis->cImages;
|
---|
| 1414 | while (i-- > 1)
|
---|
| 1415 | if (pImage->pszName == pThis->aImages[i].pszName)
|
---|
| 1416 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
|
---|
| 1417 | "Duplicate image entries for %s: %ls and %ls",
|
---|
| 1418 | pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
|
---|
| 1419 |
|
---|
| 1420 | /*
|
---|
[51770] | 1421 | * Since it's a new image, we expect to be at the start of the mapping now.
|
---|
| 1422 | */
|
---|
| 1423 | if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
|
---|
[51977] | 1424 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
|
---|
| 1425 | "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
|
---|
| 1426 | pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
|
---|
[51770] | 1427 |
|
---|
| 1428 | /*
|
---|
| 1429 | * Check for size/rva overflow.
|
---|
| 1430 | */
|
---|
| 1431 | if (pMemInfo->RegionSize >= _2G)
|
---|
[51977] | 1432 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
|
---|
| 1433 | "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
|
---|
[51770] | 1434 |
|
---|
| 1435 | /*
|
---|
| 1436 | * Fill in details from the memory info.
|
---|
| 1437 | */
|
---|
| 1438 | pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
|
---|
| 1439 | pImage->cbImage = pMemInfo->RegionSize;
|
---|
[52366] | 1440 | pImage->pCacheEntry= NULL;
|
---|
[51770] | 1441 | pImage->cRegions = 1;
|
---|
| 1442 | pImage->aRegions[0].uRva = 0;
|
---|
| 1443 | pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
|
---|
| 1444 | pImage->aRegions[0].fProt = pMemInfo->Protect;
|
---|
| 1445 |
|
---|
[52204] | 1446 | if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
|
---|
| 1447 | pImage->fNtCreateSectionPatch = true;
|
---|
| 1448 | else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
|
---|
| 1449 | pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
|
---|
| 1450 | #ifdef VBOX_PERMIT_MORE
|
---|
| 1451 | else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
|
---|
| 1452 | pImage->f32bitResourceDll = true;
|
---|
| 1453 | #endif
|
---|
| 1454 |
|
---|
[51770] | 1455 | return VINF_SUCCESS;
|
---|
| 1456 | }
|
---|
| 1457 |
|
---|
| 1458 |
|
---|
| 1459 | /**
|
---|
| 1460 | * Records an additional memory region for an image.
|
---|
| 1461 | *
|
---|
| 1462 | * @returns VBox status code.
|
---|
[51977] | 1463 | * @param pThis The process scanning state structure.
|
---|
[51770] | 1464 | * @param pImage The image.
|
---|
| 1465 | * @param pMemInfo The memory information for the region.
|
---|
| 1466 | */
|
---|
[51977] | 1467 | static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
|
---|
[51770] | 1468 | {
|
---|
| 1469 | /*
|
---|
| 1470 | * Make sure the base address matches.
|
---|
| 1471 | */
|
---|
| 1472 | if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
|
---|
[51977] | 1473 | return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
|
---|
| 1474 | "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
|
---|
| 1475 | pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
|
---|
| 1476 | pMemInfo->BaseAddress, pMemInfo->RegionSize);
|
---|
[51770] | 1477 |
|
---|
| 1478 | /*
|
---|
| 1479 | * Check for size and rva overflows.
|
---|
| 1480 | */
|
---|
| 1481 | uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
|
---|
| 1482 | if (pMemInfo->RegionSize >= _2G)
|
---|
[51977] | 1483 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
|
---|
| 1484 | "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
|
---|
[51770] | 1485 | if (uRva >= _2G)
|
---|
[51977] | 1486 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
|
---|
| 1487 | "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
|
---|
[51770] | 1488 |
|
---|
| 1489 |
|
---|
| 1490 | /*
|
---|
| 1491 | * Record the region.
|
---|
| 1492 | */
|
---|
| 1493 | uint32_t iRegion = pImage->cRegions;
|
---|
| 1494 | if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
|
---|
[51977] | 1495 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
|
---|
| 1496 | "Too many regions for %s.", pImage->pszName);
|
---|
[51770] | 1497 | pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
|
---|
| 1498 | pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
|
---|
| 1499 | pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
|
---|
| 1500 | pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
|
---|
| 1501 | pImage->cRegions++;
|
---|
[52204] | 1502 | pImage->fApiSetSchemaOnlySection1 = false;
|
---|
[51770] | 1503 |
|
---|
| 1504 | return VINF_SUCCESS;
|
---|
| 1505 | }
|
---|
| 1506 |
|
---|
| 1507 |
|
---|
[54993] | 1508 | #ifdef IN_RING3
|
---|
[51770] | 1509 | /**
|
---|
[54993] | 1510 | * Frees (or replaces) executable memory of allocation type private.
|
---|
| 1511 | *
|
---|
[54998] | 1512 | * @returns True if nothing really bad happen, false if to quit ASAP because we
|
---|
| 1513 | * killed the process being scanned.
|
---|
[54993] | 1514 | * @param pThis The process scanning state structure. Details
|
---|
| 1515 | * about images are added to this.
|
---|
| 1516 | * @param hProcess The process to verify.
|
---|
| 1517 | * @param pMemInfo The information we've got on this private
|
---|
| 1518 | * executable memory.
|
---|
| 1519 | */
|
---|
[54998] | 1520 | static bool supHardNtVpFreeOrReplacePrivateExecMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess,
|
---|
[54993] | 1521 | MEMORY_BASIC_INFORMATION const *pMemInfo)
|
---|
| 1522 | {
|
---|
| 1523 | NTSTATUS rcNt;
|
---|
| 1524 |
|
---|
| 1525 | /*
|
---|
[62456] | 1526 | * Try figure the entire allocation size. Free/Alloc may fail otherwise.
|
---|
[54993] | 1527 | */
|
---|
| 1528 | PVOID pvFree = pMemInfo->AllocationBase;
|
---|
| 1529 | SIZE_T cbFree = pMemInfo->RegionSize + ((uintptr_t)pMemInfo->BaseAddress - (uintptr_t)pMemInfo->AllocationBase);
|
---|
| 1530 | for (;;)
|
---|
| 1531 | {
|
---|
| 1532 | SIZE_T cbActual = 0;
|
---|
| 1533 | MEMORY_BASIC_INFORMATION MemInfo2 = { 0, 0, 0, 0, 0, 0, 0 };
|
---|
| 1534 | uintptr_t uPtrNext = (uintptr_t)pvFree + cbFree;
|
---|
| 1535 | rcNt = g_pfnNtQueryVirtualMemory(hProcess,
|
---|
| 1536 | (void const *)uPtrNext,
|
---|
| 1537 | MemoryBasicInformation,
|
---|
| 1538 | &MemInfo2,
|
---|
| 1539 | sizeof(MemInfo2),
|
---|
| 1540 | &cbActual);
|
---|
| 1541 | if (!NT_SUCCESS(rcNt))
|
---|
| 1542 | break;
|
---|
| 1543 | if (pMemInfo->AllocationBase != MemInfo2.AllocationBase)
|
---|
| 1544 | break;
|
---|
[62457] | 1545 | if (MemInfo2.RegionSize == 0)
|
---|
[62456] | 1546 | break;
|
---|
| 1547 | cbFree += MemInfo2.RegionSize;
|
---|
[54993] | 1548 | }
|
---|
| 1549 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: %s exec mem at %p (LB %#zx, %p LB %#zx)\n",
|
---|
| 1550 | pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW ? "Replacing" : "Freeing",
|
---|
| 1551 | pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize));
|
---|
| 1552 |
|
---|
| 1553 | /*
|
---|
| 1554 | * In the BSOD workaround mode, we need to make a copy of the memory before
|
---|
[80212] | 1555 | * freeing it. Bird abuses this code for logging purposes too.
|
---|
[54993] | 1556 | */
|
---|
| 1557 | uintptr_t uCopySrc = (uintptr_t)pvFree;
|
---|
| 1558 | size_t cbCopy = 0;
|
---|
| 1559 | void *pvCopy = NULL;
|
---|
[80212] | 1560 | //if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
|
---|
[54993] | 1561 | {
|
---|
| 1562 | cbCopy = cbFree;
|
---|
| 1563 | pvCopy = RTMemAllocZ(cbCopy);
|
---|
| 1564 | if (!pvCopy)
|
---|
| 1565 | {
|
---|
| 1566 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED, "RTMemAllocZ(%#zx) failed", cbCopy);
|
---|
[54998] | 1567 | return true;
|
---|
[54993] | 1568 | }
|
---|
| 1569 |
|
---|
| 1570 | rcNt = supHardNtVpReadMem(hProcess, uCopySrc, pvCopy, cbCopy);
|
---|
| 1571 | if (!NT_SUCCESS(rcNt))
|
---|
| 1572 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
|
---|
| 1573 | "Error reading data from original alloc: %#x (%p LB %#zx)", rcNt, uCopySrc, cbCopy, rcNt);
|
---|
[80212] | 1574 | for (size_t off = 0; off < cbCopy; off += 256)
|
---|
| 1575 | {
|
---|
| 1576 | size_t const cbChunk = RT_MIN(256, cbCopy - off);
|
---|
| 1577 | void const *pvChunk = (uint8_t const *)pvCopy + off;
|
---|
| 1578 | if (!ASMMemIsZero(pvChunk, cbChunk))
|
---|
| 1579 | SUP_DPRINTF(("%.*RhxD\n", cbChunk, pvChunk));
|
---|
| 1580 | }
|
---|
| 1581 | if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
|
---|
| 1582 | supR3HardenedLogFlush();
|
---|
[54993] | 1583 | }
|
---|
| 1584 |
|
---|
| 1585 | /*
|
---|
| 1586 | * Free the memory.
|
---|
| 1587 | */
|
---|
| 1588 | for (uint32_t i = 0; i < 10; i++)
|
---|
| 1589 | {
|
---|
| 1590 | PVOID pvFreeInOut = pvFree;
|
---|
| 1591 | SIZE_T cbFreeInOut = 0;
|
---|
| 1592 | rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
|
---|
[54998] | 1593 | if (NT_SUCCESS(rcNt))
|
---|
[54993] | 1594 | {
|
---|
[54998] | 1595 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 succeeded: %#x [%p/%p LB 0/%#zx]\n",
|
---|
| 1596 | rcNt, pvFree, pvFreeInOut, cbFreeInOut));
|
---|
| 1597 | supR3HardenedLogFlush();
|
---|
| 1598 | }
|
---|
| 1599 | else
|
---|
| 1600 | {
|
---|
[54993] | 1601 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 failed: %#x [%p LB 0]\n", rcNt, pvFree));
|
---|
[54998] | 1602 | supR3HardenedLogFlush();
|
---|
[54993] | 1603 | pvFreeInOut = pvFree;
|
---|
| 1604 | cbFreeInOut = cbFree;
|
---|
| 1605 | rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
|
---|
[54998] | 1606 | if (NT_SUCCESS(rcNt))
|
---|
[54993] | 1607 | {
|
---|
[54998] | 1608 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 succeeded: %#x [%p/%p LB %#zx/%#zx]\n",
|
---|
| 1609 | rcNt, pvFree, pvFreeInOut, cbFree, cbFreeInOut));
|
---|
| 1610 | supR3HardenedLogFlush();
|
---|
| 1611 | }
|
---|
| 1612 | else
|
---|
| 1613 | {
|
---|
[54993] | 1614 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 failed: %#x [%p LB %#zx]\n",
|
---|
| 1615 | rcNt, pvFree, cbFree));
|
---|
[54998] | 1616 | supR3HardenedLogFlush();
|
---|
[54993] | 1617 | pvFreeInOut = pMemInfo->BaseAddress;
|
---|
| 1618 | cbFreeInOut = pMemInfo->RegionSize;
|
---|
| 1619 | rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
|
---|
| 1620 | if (NT_SUCCESS(rcNt))
|
---|
| 1621 | {
|
---|
| 1622 | pvFree = pMemInfo->BaseAddress;
|
---|
| 1623 | cbFree = pMemInfo->RegionSize;
|
---|
| 1624 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #3 succeeded [%p LB %#zx]\n",
|
---|
| 1625 | pvFree, cbFree));
|
---|
[54998] | 1626 | supR3HardenedLogFlush();
|
---|
[54993] | 1627 | }
|
---|
| 1628 | else
|
---|
| 1629 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
|
---|
| 1630 | "NtFreeVirtualMemory [%p LB %#zx and %p LB %#zx] failed: %#x",
|
---|
| 1631 | pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
|
---|
| 1632 | }
|
---|
| 1633 | }
|
---|
| 1634 |
|
---|
| 1635 | /*
|
---|
| 1636 | * Query the region again, redo the free operation if there's still memory there.
|
---|
| 1637 | */
|
---|
[55026] | 1638 | if (!NT_SUCCESS(rcNt))
|
---|
[54993] | 1639 | break;
|
---|
| 1640 | SIZE_T cbActual = 0;
|
---|
| 1641 | MEMORY_BASIC_INFORMATION MemInfo3 = { 0, 0, 0, 0, 0, 0, 0 };
|
---|
| 1642 | NTSTATUS rcNt2 = g_pfnNtQueryVirtualMemory(hProcess, pvFree, MemoryBasicInformation,
|
---|
| 1643 | &MemInfo3, sizeof(MemInfo3), &cbActual);
|
---|
| 1644 | if (!NT_SUCCESS(rcNt2))
|
---|
| 1645 | break;
|
---|
| 1646 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: QVM after free %u: [%p]/%p LB %#zx s=%#x ap=%#x rp=%#p\n",
|
---|
| 1647 | i, MemInfo3.AllocationBase, MemInfo3.BaseAddress, MemInfo3.RegionSize, MemInfo3.State,
|
---|
| 1648 | MemInfo3.AllocationProtect, MemInfo3.Protect));
|
---|
[54998] | 1649 | supR3HardenedLogFlush();
|
---|
[55026] | 1650 | if (MemInfo3.State == MEM_FREE || !(pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
|
---|
[54993] | 1651 | break;
|
---|
[54998] | 1652 | NtYieldExecution();
|
---|
[54993] | 1653 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Retrying free...\n"));
|
---|
[54998] | 1654 | supR3HardenedLogFlush();
|
---|
[54993] | 1655 | }
|
---|
| 1656 |
|
---|
| 1657 | /*
|
---|
| 1658 | * Restore memory as non-executable - Kludge for Trend Micro sakfile.sys
|
---|
| 1659 | * and Digital Guardian dgmaster.sys BSODs.
|
---|
| 1660 | */
|
---|
| 1661 | if (NT_SUCCESS(rcNt) && (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
|
---|
| 1662 | {
|
---|
| 1663 | PVOID pvAlloc = pvFree;
|
---|
| 1664 | SIZE_T cbAlloc = cbFree;
|
---|
| 1665 | rcNt = NtAllocateVirtualMemory(hProcess, &pvAlloc, 0, &cbAlloc, MEM_COMMIT, PAGE_READWRITE);
|
---|
| 1666 | if (!NT_SUCCESS(rcNt))
|
---|
[54998] | 1667 | {
|
---|
[54993] | 1668 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
|
---|
| 1669 | "NtAllocateVirtualMemory (%p LB %#zx) failed with rcNt=%#x allocating "
|
---|
| 1670 | "replacement memory for working around buggy protection software. "
|
---|
| 1671 | "See VBoxStartup.log for more details",
|
---|
| 1672 | pvAlloc, cbFree, rcNt);
|
---|
[54998] | 1673 | supR3HardenedLogFlush();
|
---|
| 1674 | NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
|
---|
| 1675 | return false;
|
---|
| 1676 | }
|
---|
| 1677 |
|
---|
| 1678 | if ( (uintptr_t)pvFree < (uintptr_t)pvAlloc
|
---|
| 1679 | || (uintptr_t)pvFree + cbFree > (uintptr_t)pvAlloc + cbFree)
|
---|
| 1680 | {
|
---|
[54993] | 1681 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
|
---|
| 1682 | "We wanted NtAllocateVirtualMemory to get us %p LB %#zx, but it returned %p LB %#zx.",
|
---|
| 1683 | pMemInfo->BaseAddress, pMemInfo->RegionSize, pvFree, cbFree, rcNt);
|
---|
[54998] | 1684 | supR3HardenedLogFlush();
|
---|
| 1685 | NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
|
---|
| 1686 | return false;
|
---|
| 1687 | }
|
---|
| 1688 |
|
---|
| 1689 | /*
|
---|
| 1690 | * Copy what we can, considering the 2nd free attempt.
|
---|
| 1691 | */
|
---|
| 1692 | uint8_t *pbDst = (uint8_t *)pvFree;
|
---|
| 1693 | size_t cbDst = cbFree;
|
---|
| 1694 | uint8_t *pbSrc = (uint8_t *)pvCopy;
|
---|
| 1695 | size_t cbSrc = cbCopy;
|
---|
| 1696 | if ((uintptr_t)pbDst != uCopySrc)
|
---|
[54993] | 1697 | {
|
---|
[54998] | 1698 | if ((uintptr_t)pbDst > uCopySrc)
|
---|
[54993] | 1699 | {
|
---|
[54998] | 1700 | uintptr_t cbAdj = (uintptr_t)pbDst - uCopySrc;
|
---|
| 1701 | pbSrc += cbAdj;
|
---|
[55778] | 1702 | cbSrc -= cbAdj;
|
---|
[54993] | 1703 | }
|
---|
[54998] | 1704 | else
|
---|
| 1705 | {
|
---|
| 1706 | uintptr_t cbAdj = uCopySrc - (uintptr_t)pbDst;
|
---|
| 1707 | pbDst += cbAdj;
|
---|
| 1708 | cbDst -= cbAdj;
|
---|
| 1709 | }
|
---|
| 1710 | }
|
---|
| 1711 | if (cbSrc > cbDst)
|
---|
| 1712 | cbSrc = cbDst;
|
---|
[54993] | 1713 |
|
---|
[54998] | 1714 | SIZE_T cbWritten;
|
---|
| 1715 | rcNt = NtWriteVirtualMemory(hProcess, pbDst, pbSrc, cbSrc, &cbWritten);
|
---|
| 1716 | if (NT_SUCCESS(rcNt))
|
---|
| 1717 | {
|
---|
| 1718 | SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Restored the exec memory as non-exec.\n"));
|
---|
| 1719 | supR3HardenedLogFlush();
|
---|
[54993] | 1720 | }
|
---|
[54998] | 1721 | else
|
---|
| 1722 | {
|
---|
| 1723 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
|
---|
| 1724 | "NtWriteVirtualMemory (%p LB %#zx) failed: %#x",
|
---|
| 1725 | pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
|
---|
| 1726 | supR3HardenedLogFlush();
|
---|
| 1727 | NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
|
---|
| 1728 | return false;
|
---|
| 1729 | }
|
---|
[54993] | 1730 | }
|
---|
| 1731 | if (pvCopy)
|
---|
| 1732 | RTMemFree(pvCopy);
|
---|
[54998] | 1733 | return true;
|
---|
[54993] | 1734 | }
|
---|
| 1735 | #endif /* IN_RING3 */
|
---|
| 1736 |
|
---|
| 1737 |
|
---|
| 1738 | /**
|
---|
[51770] | 1739 | * Scans the virtual memory of the process.
|
---|
| 1740 | *
|
---|
| 1741 | * This collects the locations of DLLs and the EXE, and verifies that executable
|
---|
[54560] | 1742 | * memory is only associated with these. May trash pThis->abMemory.
|
---|
[51770] | 1743 | *
|
---|
| 1744 | * @returns VBox status code.
|
---|
| 1745 | * @param pThis The process scanning state structure. Details
|
---|
| 1746 | * about images are added to this.
|
---|
| 1747 | * @param hProcess The process to verify.
|
---|
| 1748 | */
|
---|
[51977] | 1749 | static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
|
---|
[51770] | 1750 | {
|
---|
[52204] | 1751 | SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
|
---|
| 1752 | pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
|
---|
| 1753 | pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
|
---|
[52169] | 1754 |
|
---|
[51770] | 1755 | uint32_t cXpExceptions = 0;
|
---|
| 1756 | uintptr_t cbAdvance = 0;
|
---|
| 1757 | uintptr_t uPtrWhere = 0;
|
---|
[52529] | 1758 | #ifdef VBOX_PERMIT_VERIFIER_DLL
|
---|
| 1759 | for (uint32_t i = 0; i < 10240; i++)
|
---|
| 1760 | #else
|
---|
[51770] | 1761 | for (uint32_t i = 0; i < 1024; i++)
|
---|
[52529] | 1762 | #endif
|
---|
[51770] | 1763 | {
|
---|
| 1764 | SIZE_T cbActual = 0;
|
---|
| 1765 | MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
|
---|
| 1766 | NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
|
---|
| 1767 | (void const *)uPtrWhere,
|
---|
| 1768 | MemoryBasicInformation,
|
---|
| 1769 | &MemInfo,
|
---|
| 1770 | sizeof(MemInfo),
|
---|
| 1771 | &cbActual);
|
---|
| 1772 | if (!NT_SUCCESS(rcNt))
|
---|
| 1773 | {
|
---|
| 1774 | if (rcNt == STATUS_INVALID_PARAMETER)
|
---|
[51977] | 1775 | return pThis->rcResult;
|
---|
| 1776 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
|
---|
| 1777 | "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
|
---|
[51770] | 1778 | }
|
---|
| 1779 |
|
---|
| 1780 | /*
|
---|
| 1781 | * Record images.
|
---|
| 1782 | */
|
---|
| 1783 | if ( MemInfo.Type == SEC_IMAGE
|
---|
| 1784 | || MemInfo.Type == SEC_PROTECTED_IMAGE
|
---|
| 1785 | || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
|
---|
| 1786 | {
|
---|
| 1787 | uint32_t iImg = pThis->cImages;
|
---|
| 1788 | rcNt = g_pfnNtQueryVirtualMemory(hProcess,
|
---|
| 1789 | (void const *)uPtrWhere,
|
---|
| 1790 | MemorySectionName,
|
---|
| 1791 | &pThis->aImages[iImg].Name,
|
---|
| 1792 | sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
|
---|
| 1793 | &cbActual);
|
---|
| 1794 | if (!NT_SUCCESS(rcNt))
|
---|
[51977] | 1795 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
|
---|
| 1796 | "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
|
---|
[51770] | 1797 | pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
|
---|
[52169] | 1798 | SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
|
---|
| 1799 | ? " *%p-%p %#06x/%#06x %#09x %ls\n"
|
---|
| 1800 | : " %p-%p %#06x/%#06x %#09x %ls\n",
|
---|
[54993] | 1801 | MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
|
---|
[52169] | 1802 | MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
|
---|
[51770] | 1803 |
|
---|
| 1804 | /* New or existing image? */
|
---|
| 1805 | bool fNew = true;
|
---|
| 1806 | uint32_t iSearch = iImg;
|
---|
| 1807 | while (iSearch-- > 0)
|
---|
| 1808 | if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
|
---|
| 1809 | {
|
---|
[51977] | 1810 | int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
|
---|
[51770] | 1811 | if (RT_FAILURE(rc))
|
---|
| 1812 | return rc;
|
---|
| 1813 | fNew = false;
|
---|
| 1814 | break;
|
---|
| 1815 | }
|
---|
| 1816 | else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
|
---|
[51977] | 1817 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
|
---|
| 1818 | "Unexpected base address match");
|
---|
[51770] | 1819 |
|
---|
| 1820 | if (fNew)
|
---|
| 1821 | {
|
---|
[51977] | 1822 | int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
|
---|
| 1823 | if (RT_SUCCESS(rc))
|
---|
| 1824 | {
|
---|
[52446] | 1825 | if (rc != VINF_OBJECT_DESTROYED)
|
---|
| 1826 | {
|
---|
| 1827 | pThis->cImages++;
|
---|
| 1828 | if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
|
---|
| 1829 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
|
---|
| 1830 | "Internal error: aImages is full.\n");
|
---|
| 1831 | }
|
---|
[51977] | 1832 | }
|
---|
| 1833 | #ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
|
---|
| 1834 | else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
|
---|
[51770] | 1835 | return rc;
|
---|
[51977] | 1836 | #else
|
---|
| 1837 | else
|
---|
| 1838 | return rc;
|
---|
| 1839 | #endif
|
---|
[51770] | 1840 | }
|
---|
| 1841 | }
|
---|
| 1842 | /*
|
---|
| 1843 | * XP, W2K3: Ignore the CSRSS read-only region as best we can.
|
---|
| 1844 | */
|
---|
| 1845 | else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
|
---|
| 1846 | == PAGE_EXECUTE_READ
|
---|
| 1847 | && cXpExceptions == 0
|
---|
| 1848 | && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
|
---|
| 1849 | /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
|
---|
| 1850 | && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
|
---|
[52169] | 1851 | {
|
---|
[51770] | 1852 | cXpExceptions++;
|
---|
[52169] | 1853 | SUP_DPRINTF((" %p-%p %#06x/%#06x %#09x XP CSRSS read-only region\n", MemInfo.BaseAddress,
|
---|
[66351] | 1854 | (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
|
---|
[52169] | 1855 | MemInfo.AllocationProtect, MemInfo.Type));
|
---|
| 1856 | }
|
---|
[51770] | 1857 | /*
|
---|
| 1858 | * Executable memory?
|
---|
| 1859 | */
|
---|
[51999] | 1860 | #ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
|
---|
[51770] | 1861 | else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
|
---|
[51977] | 1862 | {
|
---|
[52204] | 1863 | SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
|
---|
| 1864 | ? " *%p-%p %#06x/%#06x %#09x !!\n"
|
---|
| 1865 | : " %p-%p %#06x/%#06x %#09x !!\n",
|
---|
[66351] | 1866 | MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
|
---|
[52204] | 1867 | MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
|
---|
[51999] | 1868 | # ifdef IN_RING3
|
---|
[52204] | 1869 | if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
| 1870 | {
|
---|
| 1871 | /*
|
---|
| 1872 | * Free any private executable memory (sysplant.sys allocates executable memory).
|
---|
| 1873 | */
|
---|
| 1874 | if (MemInfo.Type == MEM_PRIVATE)
|
---|
[54998] | 1875 | {
|
---|
| 1876 | if (!supHardNtVpFreeOrReplacePrivateExecMemory(pThis, hProcess, &MemInfo))
|
---|
| 1877 | break;
|
---|
| 1878 | }
|
---|
[52204] | 1879 | /*
|
---|
| 1880 | * Unmap mapped memory, failing that, drop exec privileges.
|
---|
| 1881 | */
|
---|
| 1882 | else if (MemInfo.Type == MEM_MAPPED)
|
---|
| 1883 | {
|
---|
| 1884 | SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
|
---|
| 1885 | uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
|
---|
[54993] | 1886 | rcNt = NtUnmapViewOfSection(hProcess, MemInfo.AllocationBase);
|
---|
[52204] | 1887 | if (!NT_SUCCESS(rcNt))
|
---|
| 1888 | {
|
---|
| 1889 | PVOID pvCopy = MemInfo.BaseAddress;
|
---|
| 1890 | SIZE_T cbCopy = MemInfo.RegionSize;
|
---|
[54993] | 1891 | NTSTATUS rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
|
---|
[52204] | 1892 | if (!NT_SUCCESS(rcNt2))
|
---|
[54993] | 1893 | rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
|
---|
[52204] | 1894 | if (!NT_SUCCESS(rcNt2))
|
---|
[52205] | 1895 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
|
---|
[52204] | 1896 | "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
|
---|
| 1897 | MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
|
---|
| 1898 | }
|
---|
| 1899 | }
|
---|
| 1900 | else
|
---|
[52205] | 1901 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
|
---|
[52204] | 1902 | "Unknown executable memory type %#x at %p/%p LB %#zx",
|
---|
| 1903 | MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
|
---|
[52529] | 1904 | pThis->cFixes++;
|
---|
[52204] | 1905 | }
|
---|
[80216] | 1906 | else if (pThis->enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
[52204] | 1907 | # endif /* IN_RING3 */
|
---|
| 1908 | supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
|
---|
| 1909 | "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
|
---|
| 1910 | uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
|
---|
| 1911 | MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
|
---|
| 1912 |
|
---|
| 1913 | # ifndef IN_RING3
|
---|
| 1914 | if (RT_FAILURE(pThis->rcResult))
|
---|
| 1915 | return pThis->rcResult;
|
---|
| 1916 | # endif
|
---|
[51977] | 1917 | /* Continue add more information about the problematic process. */
|
---|
[51999] | 1918 | }
|
---|
[52204] | 1919 | #endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
|
---|
[52169] | 1920 | else
|
---|
| 1921 | SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
|
---|
| 1922 | ? " *%p-%p %#06x/%#06x %#09x\n"
|
---|
| 1923 | : " %p-%p %#06x/%#06x %#09x\n",
|
---|
[66351] | 1924 | MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
|
---|
[52169] | 1925 | MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
|
---|
[51770] | 1926 |
|
---|
| 1927 | /*
|
---|
| 1928 | * Advance.
|
---|
| 1929 | */
|
---|
| 1930 | cbAdvance = MemInfo.RegionSize;
|
---|
| 1931 | if (uPtrWhere + cbAdvance <= uPtrWhere)
|
---|
[51977] | 1932 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
|
---|
| 1933 | "Empty region at %p.", uPtrWhere);
|
---|
[51770] | 1934 | uPtrWhere += MemInfo.RegionSize;
|
---|
| 1935 | }
|
---|
| 1936 |
|
---|
[51977] | 1937 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
|
---|
| 1938 | "Too many virtual memory regions.\n");
|
---|
[51770] | 1939 | }
|
---|
| 1940 |
|
---|
[52366] | 1941 |
|
---|
[52204] | 1942 | /**
|
---|
[52366] | 1943 | * Verifies the loader image, i.e. check cryptographic signatures if present.
|
---|
| 1944 | *
|
---|
| 1945 | * @returns VBox status code.
|
---|
| 1946 | * @param pEntry The loader cache entry.
|
---|
| 1947 | * @param pwszName The filename to use in error messages.
|
---|
[58339] | 1948 | * @param pErrInfo Where to return extened error information.
|
---|
[52366] | 1949 | */
|
---|
| 1950 | DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
|
---|
| 1951 | {
|
---|
| 1952 | int rc = VINF_SUCCESS;
|
---|
| 1953 | if (!pEntry->fVerified)
|
---|
| 1954 | {
|
---|
[52634] | 1955 | rc = supHardenedWinVerifyImageByLdrMod(pEntry->hLdrMod, pwszName, pEntry->pNtViRdr,
|
---|
| 1956 | false /*fAvoidWinVerifyTrust*/, NULL /*pfWinVerifyTrust*/, pErrInfo);
|
---|
[52366] | 1957 | pEntry->fVerified = RT_SUCCESS(rc);
|
---|
| 1958 | }
|
---|
| 1959 | return rc;
|
---|
| 1960 | }
|
---|
| 1961 |
|
---|
| 1962 |
|
---|
| 1963 | /**
|
---|
[52947] | 1964 | * Allocates a image bits buffer and calls RTLdrGetBits on them.
|
---|
[52366] | 1965 | *
|
---|
| 1966 | * An assumption here is that there won't ever be concurrent use of the cache.
|
---|
| 1967 | * It's currently 104% single threaded, non-reentrant. Thus, we can't reuse the
|
---|
| 1968 | * pbBits allocation.
|
---|
| 1969 | *
|
---|
| 1970 | * @returns VBox status code
|
---|
| 1971 | * @param pEntry The loader cache entry.
|
---|
| 1972 | * @param ppbBits Where to return the pointer to the allocation.
|
---|
[52947] | 1973 | * @param uBaseAddress The image base address, see RTLdrGetBits.
|
---|
| 1974 | * @param pfnGetImport Import getter, see RTLdrGetBits.
|
---|
| 1975 | * @param pvUser The user argument for @a pfnGetImport.
|
---|
| 1976 | * @param pErrInfo Where to return extened error information.
|
---|
[52366] | 1977 | */
|
---|
[52947] | 1978 | DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
|
---|
| 1979 | RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
|
---|
| 1980 | PRTERRINFO pErrInfo)
|
---|
[52366] | 1981 | {
|
---|
[52947] | 1982 | int rc;
|
---|
| 1983 |
|
---|
| 1984 | /*
|
---|
| 1985 | * First time around we have to allocate memory before we can get the image bits.
|
---|
| 1986 | */
|
---|
[52366] | 1987 | if (!pEntry->pbBits)
|
---|
| 1988 | {
|
---|
| 1989 | size_t cbBits = RTLdrSize(pEntry->hLdrMod);
|
---|
| 1990 | if (cbBits >= _1M*32U)
|
---|
| 1991 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
|
---|
| 1992 | pEntry->pszName, cbBits, cbBits);
|
---|
| 1993 |
|
---|
[52940] | 1994 | pEntry->pbBits = (uint8_t *)RTMemAllocZ(cbBits);
|
---|
[52366] | 1995 | if (!pEntry->pbBits)
|
---|
| 1996 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
|
---|
| 1997 | cbBits, pEntry->pszName);
|
---|
[52947] | 1998 |
|
---|
| 1999 | pEntry->fValidBits = false; /* paranoia */
|
---|
| 2000 |
|
---|
| 2001 | rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
|
---|
| 2002 | if (RT_FAILURE(rc))
|
---|
| 2003 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
|
---|
| 2004 | pEntry->pszName, rc);
|
---|
| 2005 | pEntry->uImageBase = uBaseAddress;
|
---|
| 2006 | pEntry->fValidBits = pfnGetImport == NULL;
|
---|
| 2007 |
|
---|
[52366] | 2008 | }
|
---|
[52947] | 2009 | /*
|
---|
| 2010 | * Cache hit? No?
|
---|
| 2011 | *
|
---|
| 2012 | * Note! We cannot currently cache image bits for images with imports as we
|
---|
| 2013 | * don't control the way they're resolved. Fortunately, NTDLL and
|
---|
| 2014 | * the VM process images all have no imports.
|
---|
| 2015 | */
|
---|
| 2016 | else if ( !pEntry->fValidBits
|
---|
| 2017 | || pEntry->uImageBase != uBaseAddress
|
---|
| 2018 | || pfnGetImport)
|
---|
| 2019 | {
|
---|
| 2020 | pEntry->fValidBits = false;
|
---|
[52366] | 2021 |
|
---|
[52947] | 2022 | rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
|
---|
| 2023 | if (RT_FAILURE(rc))
|
---|
| 2024 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
|
---|
| 2025 | pEntry->pszName, rc);
|
---|
| 2026 | pEntry->uImageBase = uBaseAddress;
|
---|
| 2027 | pEntry->fValidBits = pfnGetImport == NULL;
|
---|
| 2028 | }
|
---|
[52366] | 2029 |
|
---|
| 2030 | *ppbBits = pEntry->pbBits;
|
---|
| 2031 | return VINF_SUCCESS;
|
---|
| 2032 | }
|
---|
| 2033 |
|
---|
| 2034 |
|
---|
| 2035 | /**
|
---|
| 2036 | * Frees all resources associated with a cache entry and wipes the members
|
---|
| 2037 | * clean.
|
---|
| 2038 | *
|
---|
| 2039 | * @param pEntry The entry to delete.
|
---|
| 2040 | */
|
---|
| 2041 | static void supHardNTLdrCacheDeleteEntry(PSUPHNTLDRCACHEENTRY pEntry)
|
---|
| 2042 | {
|
---|
| 2043 | if (pEntry->pbBits)
|
---|
| 2044 | {
|
---|
[52940] | 2045 | RTMemFree(pEntry->pbBits);
|
---|
[52366] | 2046 | pEntry->pbBits = NULL;
|
---|
| 2047 | }
|
---|
| 2048 |
|
---|
| 2049 | if (pEntry->hLdrMod != NIL_RTLDRMOD)
|
---|
| 2050 | {
|
---|
| 2051 | RTLdrClose(pEntry->hLdrMod);
|
---|
| 2052 | pEntry->hLdrMod = NIL_RTLDRMOD;
|
---|
| 2053 | pEntry->pNtViRdr = NULL;
|
---|
| 2054 | }
|
---|
| 2055 | else if (pEntry->pNtViRdr)
|
---|
| 2056 | {
|
---|
| 2057 | pEntry->pNtViRdr->Core.pfnDestroy(&pEntry->pNtViRdr->Core);
|
---|
| 2058 | pEntry->pNtViRdr = NULL;
|
---|
| 2059 | }
|
---|
| 2060 |
|
---|
| 2061 | if (pEntry->hFile)
|
---|
| 2062 | {
|
---|
| 2063 | NtClose(pEntry->hFile);
|
---|
| 2064 | pEntry->hFile = NULL;
|
---|
| 2065 | }
|
---|
| 2066 |
|
---|
[52947] | 2067 | pEntry->pszName = NULL;
|
---|
| 2068 | pEntry->fVerified = false;
|
---|
| 2069 | pEntry->fValidBits = false;
|
---|
| 2070 | pEntry->uImageBase = 0;
|
---|
[52366] | 2071 | }
|
---|
| 2072 |
|
---|
| 2073 | #ifdef IN_RING3
|
---|
| 2074 |
|
---|
| 2075 | /**
|
---|
| 2076 | * Flushes the cache.
|
---|
| 2077 | *
|
---|
| 2078 | * This is called from one of two points in the hardened main code, first is
|
---|
| 2079 | * after respawning and the second is when we open the vboxdrv device for
|
---|
| 2080 | * unrestricted access.
|
---|
| 2081 | */
|
---|
| 2082 | DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache(void)
|
---|
| 2083 | {
|
---|
| 2084 | uint32_t i = g_cSupNtVpLdrCacheEntries;
|
---|
| 2085 | while (i-- > 0)
|
---|
| 2086 | supHardNTLdrCacheDeleteEntry(&g_aSupNtVpLdrCacheEntries[i]);
|
---|
| 2087 | g_cSupNtVpLdrCacheEntries = 0;
|
---|
| 2088 | }
|
---|
| 2089 |
|
---|
| 2090 |
|
---|
| 2091 | /**
|
---|
| 2092 | * Searches the cache for a loader image.
|
---|
| 2093 | *
|
---|
| 2094 | * @returns Pointer to the cache entry if found, NULL if not.
|
---|
| 2095 | * @param pszName The name (from g_apszSupNtVpAllowedVmExes or
|
---|
| 2096 | * g_apszSupNtVpAllowedDlls).
|
---|
| 2097 | */
|
---|
| 2098 | static PSUPHNTLDRCACHEENTRY supHardNtLdrCacheLookupEntry(const char *pszName)
|
---|
| 2099 | {
|
---|
| 2100 | /*
|
---|
| 2101 | * Since the caller is supplying us a pszName from one of the two tables,
|
---|
| 2102 | * we can dispense with string compare and simply compare string pointers.
|
---|
| 2103 | */
|
---|
| 2104 | uint32_t i = g_cSupNtVpLdrCacheEntries;
|
---|
| 2105 | while (i-- > 0)
|
---|
| 2106 | if (g_aSupNtVpLdrCacheEntries[i].pszName == pszName)
|
---|
| 2107 | return &g_aSupNtVpLdrCacheEntries[i];
|
---|
| 2108 | return NULL;
|
---|
| 2109 | }
|
---|
| 2110 |
|
---|
| 2111 | #endif /* IN_RING3 */
|
---|
| 2112 |
|
---|
| 2113 | static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
|
---|
| 2114 | bool fDll, bool f32bitResourceDll, PRTERRINFO pErrInfo)
|
---|
| 2115 | {
|
---|
| 2116 | /*
|
---|
| 2117 | * Open the image file.
|
---|
| 2118 | */
|
---|
| 2119 | HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
|
---|
| 2120 | IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
|
---|
| 2121 |
|
---|
| 2122 | OBJECT_ATTRIBUTES ObjAttr;
|
---|
| 2123 | InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
|
---|
| 2124 | #ifdef IN_RING0
|
---|
| 2125 | ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
|
---|
| 2126 | #endif
|
---|
| 2127 |
|
---|
| 2128 | NTSTATUS rcNt = NtCreateFile(&hFile,
|
---|
[53036] | 2129 | GENERIC_READ | SYNCHRONIZE,
|
---|
[52366] | 2130 | &ObjAttr,
|
---|
| 2131 | &Ios,
|
---|
| 2132 | NULL /* Allocation Size*/,
|
---|
| 2133 | FILE_ATTRIBUTE_NORMAL,
|
---|
| 2134 | FILE_SHARE_READ,
|
---|
| 2135 | FILE_OPEN,
|
---|
[53034] | 2136 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
---|
[52366] | 2137 | NULL /*EaBuffer*/,
|
---|
| 2138 | 0 /*EaLength*/);
|
---|
| 2139 | if (NT_SUCCESS(rcNt))
|
---|
| 2140 | rcNt = Ios.Status;
|
---|
| 2141 | if (!NT_SUCCESS(rcNt))
|
---|
| 2142 | return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
|
---|
| 2143 | "Error opening image for scanning: %#x (name %ls)", rcNt, pUniStrPath->Buffer);
|
---|
| 2144 |
|
---|
| 2145 | /*
|
---|
| 2146 | * Figure out validation flags we'll be using and create the reader
|
---|
| 2147 | * for this image.
|
---|
| 2148 | */
|
---|
| 2149 | uint32_t fFlags = fDll
|
---|
[104384] | 2150 | ? SUPHNTVI_F_TRUSTED_INSTALLER_OR_SIMILAR_OWNER | SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION
|
---|
[52366] | 2151 | : SUPHNTVI_F_REQUIRE_BUILD_CERT;
|
---|
| 2152 | if (f32bitResourceDll)
|
---|
[53220] | 2153 | fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
|
---|
[52366] | 2154 |
|
---|
| 2155 | PSUPHNTVIRDR pNtViRdr;
|
---|
| 2156 | int rc = supHardNtViRdrCreate(hFile, pUniStrPath->Buffer, fFlags, &pNtViRdr);
|
---|
| 2157 | if (RT_FAILURE(rc))
|
---|
| 2158 | {
|
---|
| 2159 | NtClose(hFile);
|
---|
| 2160 | return rc;
|
---|
| 2161 | }
|
---|
| 2162 |
|
---|
| 2163 | /*
|
---|
| 2164 | * Finally, open the image with the loader
|
---|
| 2165 | */
|
---|
| 2166 | RTLDRMOD hLdrMod;
|
---|
| 2167 | RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
|
---|
[53220] | 2168 | if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
|
---|
[52366] | 2169 | enmArch = RTLDRARCH_WHATEVER;
|
---|
| 2170 | rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
|
---|
| 2171 | if (RT_FAILURE(rc))
|
---|
[60700] | 2172 | return supHardNtVpAddInfo1(pErrInfo, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
|
---|
[52366] | 2173 | rc, pUniStrPath->Buffer);
|
---|
| 2174 |
|
---|
| 2175 | /*
|
---|
| 2176 | * Fill in the cache entry.
|
---|
| 2177 | */
|
---|
[52947] | 2178 | pEntry->pszName = pszName;
|
---|
| 2179 | pEntry->hLdrMod = hLdrMod;
|
---|
| 2180 | pEntry->pNtViRdr = pNtViRdr;
|
---|
| 2181 | pEntry->hFile = hFile;
|
---|
| 2182 | pEntry->pbBits = NULL;
|
---|
| 2183 | pEntry->fVerified = false;
|
---|
| 2184 | pEntry->fValidBits = false;
|
---|
| 2185 | pEntry->uImageBase = ~(uintptr_t)0;
|
---|
[52366] | 2186 |
|
---|
[52875] | 2187 | #ifdef IN_SUP_HARDENED_R3
|
---|
| 2188 | /*
|
---|
| 2189 | * Log the image timestamp when in the hardened exe.
|
---|
| 2190 | */
|
---|
| 2191 | uint64_t uTimestamp = 0;
|
---|
| 2192 | rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uint64_t));
|
---|
| 2193 | SUP_DPRINTF(("%s: timestamp %#llx (rc=%Rrc)\n", pszName, uTimestamp, rc));
|
---|
| 2194 | #endif
|
---|
| 2195 |
|
---|
[52366] | 2196 | return VINF_SUCCESS;
|
---|
| 2197 | }
|
---|
| 2198 |
|
---|
[52373] | 2199 | #ifdef IN_RING3
|
---|
| 2200 | /**
|
---|
| 2201 | * Opens a loader cache entry.
|
---|
| 2202 | *
|
---|
| 2203 | * Currently this is only used by the import code for getting NTDLL.
|
---|
| 2204 | *
|
---|
| 2205 | * @returns VBox status code.
|
---|
| 2206 | * @param pszName The DLL name. Must be one from the
|
---|
| 2207 | * g_apszSupNtVpAllowedDlls array.
|
---|
| 2208 | * @param ppEntry Where to return the entry we've opened/found.
|
---|
[60700] | 2209 | * @param pErrInfo Optional buffer where to return additional error
|
---|
| 2210 | * information.
|
---|
[52373] | 2211 | */
|
---|
[60700] | 2212 | DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry, PRTERRINFO pErrInfo)
|
---|
[52366] | 2213 | {
|
---|
[52373] | 2214 | /*
|
---|
| 2215 | * Locate the dll.
|
---|
| 2216 | */
|
---|
| 2217 | uint32_t i = 0;
|
---|
[52795] | 2218 | while ( i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)
|
---|
| 2219 | && strcmp(pszName, g_apszSupNtVpAllowedDlls[i]))
|
---|
| 2220 | i++;
|
---|
[52373] | 2221 | if (i >= RT_ELEMENTS(g_apszSupNtVpAllowedDlls))
|
---|
| 2222 | return VERR_FILE_NOT_FOUND;
|
---|
| 2223 | pszName = g_apszSupNtVpAllowedDlls[i];
|
---|
| 2224 |
|
---|
| 2225 | /*
|
---|
| 2226 | * Try the cache.
|
---|
| 2227 | */
|
---|
| 2228 | *ppEntry = supHardNtLdrCacheLookupEntry(pszName);
|
---|
| 2229 | if (*ppEntry)
|
---|
| 2230 | return VINF_SUCCESS;
|
---|
| 2231 |
|
---|
| 2232 | /*
|
---|
| 2233 | * Not in the cache, so open it.
|
---|
| 2234 | * Note! We cannot assume that g_System32NtPath has been initialized at this point.
|
---|
| 2235 | */
|
---|
| 2236 | if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
|
---|
| 2237 | return VERR_INTERNAL_ERROR_3;
|
---|
| 2238 |
|
---|
| 2239 | static WCHAR s_wszSystem32[] = L"\\SystemRoot\\System32\\";
|
---|
| 2240 | WCHAR wszPath[64];
|
---|
| 2241 | memcpy(wszPath, s_wszSystem32, sizeof(s_wszSystem32));
|
---|
| 2242 | RTUtf16CatAscii(wszPath, sizeof(wszPath), pszName);
|
---|
| 2243 |
|
---|
| 2244 | UNICODE_STRING UniStr;
|
---|
| 2245 | UniStr.Buffer = wszPath;
|
---|
| 2246 | UniStr.Length = (USHORT)(RTUtf16Len(wszPath) * sizeof(WCHAR));
|
---|
| 2247 | UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
|
---|
| 2248 |
|
---|
| 2249 | int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
|
---|
[60700] | 2250 | true /*fDll*/, false /*f32bitResourceDll*/, pErrInfo);
|
---|
[52373] | 2251 | if (RT_SUCCESS(rc))
|
---|
| 2252 | {
|
---|
| 2253 | *ppEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
|
---|
| 2254 | g_cSupNtVpLdrCacheEntries++;
|
---|
| 2255 | return VINF_SUCCESS;
|
---|
| 2256 | }
|
---|
| 2257 | return rc;
|
---|
[52366] | 2258 | }
|
---|
[52373] | 2259 | #endif /* IN_RING3 */
|
---|
[52366] | 2260 |
|
---|
| 2261 |
|
---|
| 2262 | /**
|
---|
[52207] | 2263 | * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
|
---|
| 2264 | * hLdrMod for each image.
|
---|
[52204] | 2265 | *
|
---|
| 2266 | * @returns VBox status code.
|
---|
| 2267 | * @param pThis The process scanning state structure.
|
---|
| 2268 | */
|
---|
| 2269 | static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
|
---|
| 2270 | {
|
---|
| 2271 | unsigned i = pThis->cImages;
|
---|
| 2272 | while (i-- > 0)
|
---|
| 2273 | {
|
---|
| 2274 | PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
|
---|
[51770] | 2275 |
|
---|
[52366] | 2276 | #ifdef IN_RING3
|
---|
[52204] | 2277 | /*
|
---|
[52366] | 2278 | * Try the cache first.
|
---|
[52204] | 2279 | */
|
---|
[52366] | 2280 | pImage->pCacheEntry = supHardNtLdrCacheLookupEntry(pImage->pszName);
|
---|
| 2281 | if (pImage->pCacheEntry)
|
---|
| 2282 | continue;
|
---|
[52204] | 2283 |
|
---|
| 2284 | /*
|
---|
[52366] | 2285 | * Not in the cache, so load it into the cache.
|
---|
[52204] | 2286 | */
|
---|
[52366] | 2287 | if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
|
---|
| 2288 | return supHardNtVpSetInfo2(pThis, VERR_INTERNAL_ERROR_3, "Loader cache overflow.");
|
---|
| 2289 | pImage->pCacheEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
|
---|
| 2290 | #else
|
---|
| 2291 | /*
|
---|
| 2292 | * In ring-0 we don't have a cache at the moment (resource reasons), so
|
---|
| 2293 | * we have a static cache entry in each image structure that we use instead.
|
---|
| 2294 | */
|
---|
| 2295 | pImage->pCacheEntry = &pImage->CacheEntry;
|
---|
| 2296 | #endif
|
---|
[52204] | 2297 |
|
---|
[52366] | 2298 | int rc = supHardNtLdrCacheNewEntry(pImage->pCacheEntry, pImage->pszName, &pImage->Name.UniStr,
|
---|
| 2299 | pImage->fDll, pImage->f32bitResourceDll, pThis->pErrInfo);
|
---|
[52204] | 2300 | if (RT_FAILURE(rc))
|
---|
| 2301 | return rc;
|
---|
[52366] | 2302 | #ifdef IN_RING3
|
---|
| 2303 | g_cSupNtVpLdrCacheEntries++;
|
---|
| 2304 | #endif
|
---|
[52204] | 2305 | }
|
---|
| 2306 |
|
---|
| 2307 | return VINF_SUCCESS;
|
---|
| 2308 | }
|
---|
| 2309 |
|
---|
| 2310 |
|
---|
[51770] | 2311 | /**
|
---|
| 2312 | * Check the integrity of the executable of the process.
|
---|
| 2313 | *
|
---|
| 2314 | * @returns VBox status code.
|
---|
| 2315 | * @param pThis The process scanning state structure. Details
|
---|
[62677] | 2316 | * about images are added to this. The hProcess
|
---|
| 2317 | * member holds the handle to the process that is
|
---|
| 2318 | * to be verified.
|
---|
[51770] | 2319 | */
|
---|
[62677] | 2320 | static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis)
|
---|
[51770] | 2321 | {
|
---|
| 2322 | /*
|
---|
| 2323 | * Make sure there is exactly one executable image.
|
---|
| 2324 | */
|
---|
| 2325 | unsigned cExecs = 0;
|
---|
| 2326 | unsigned iExe = ~0U;
|
---|
| 2327 | unsigned i = pThis->cImages;
|
---|
| 2328 | while (i-- > 0)
|
---|
| 2329 | {
|
---|
| 2330 | if (!pThis->aImages[i].fDll)
|
---|
| 2331 | {
|
---|
| 2332 | cExecs++;
|
---|
| 2333 | iExe = i;
|
---|
| 2334 | }
|
---|
| 2335 | }
|
---|
| 2336 | if (cExecs == 0)
|
---|
[51977] | 2337 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
|
---|
| 2338 | "No executable mapping found in the virtual address space.");
|
---|
[51770] | 2339 | if (cExecs != 1)
|
---|
[51977] | 2340 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
|
---|
| 2341 | "Found more than one executable mapping in the virtual address space.");
|
---|
[51770] | 2342 | PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
|
---|
| 2343 |
|
---|
| 2344 | /*
|
---|
| 2345 | * Check that it matches the executable image of the process.
|
---|
| 2346 | */
|
---|
| 2347 | int rc;
|
---|
| 2348 | ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
|
---|
[52940] | 2349 | PUNICODE_STRING pUniStr = (PUNICODE_STRING)RTMemAllocZ(cbUniStr);
|
---|
[51770] | 2350 | if (!pUniStr)
|
---|
[51977] | 2351 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
|
---|
[51770] | 2352 | "Error allocating %zu bytes for process name.", cbUniStr);
|
---|
| 2353 | ULONG cbIgn = 0;
|
---|
[62677] | 2354 | NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
|
---|
[51770] | 2355 | if (NT_SUCCESS(rcNt))
|
---|
| 2356 | {
|
---|
[77816] | 2357 | pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
|
---|
| 2358 | if (supHardNtVpArePathsEqual(pUniStr, &pImage->Name.UniStr))
|
---|
[51770] | 2359 | rc = VINF_SUCCESS;
|
---|
| 2360 | else
|
---|
[51977] | 2361 | rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
|
---|
| 2362 | "Process image name does not match the exectuable we found: %ls vs %ls.",
|
---|
| 2363 | pUniStr->Buffer, pImage->Name.UniStr.Buffer);
|
---|
[51770] | 2364 | }
|
---|
| 2365 | else
|
---|
[51977] | 2366 | rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
|
---|
| 2367 | "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
|
---|
[52940] | 2368 | RTMemFree(pUniStr);
|
---|
[51770] | 2369 | if (RT_FAILURE(rc))
|
---|
| 2370 | return rc;
|
---|
| 2371 |
|
---|
| 2372 | /*
|
---|
| 2373 | * Validate the signing of the executable image.
|
---|
| 2374 | * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
|
---|
| 2375 | */
|
---|
[62677] | 2376 | rc = supHardNtVpVerifyImage(pThis, pImage);
|
---|
[51770] | 2377 | if (RT_FAILURE(rc))
|
---|
| 2378 | return rc;
|
---|
| 2379 |
|
---|
| 2380 | /*
|
---|
| 2381 | * Check linking requirements.
|
---|
[52207] | 2382 | * This query is only available using the current process pseudo handle on
|
---|
| 2383 | * older windows versions. The cut-off seems to be Vista.
|
---|
[51770] | 2384 | */
|
---|
| 2385 | SECTION_IMAGE_INFORMATION ImageInfo;
|
---|
[62677] | 2386 | rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
|
---|
[51770] | 2387 | if (!NT_SUCCESS(rcNt))
|
---|
[52207] | 2388 | {
|
---|
| 2389 | if ( rcNt == STATUS_INVALID_PARAMETER
|
---|
| 2390 | && g_uNtVerCombined < SUP_NT_VER_VISTA
|
---|
[62677] | 2391 | && pThis->hProcess != NtCurrentProcess() )
|
---|
[52207] | 2392 | return VINF_SUCCESS;
|
---|
[51977] | 2393 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
|
---|
[62677] | 2394 | "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x",
|
---|
| 2395 | rcNt, pThis->hProcess);
|
---|
[52207] | 2396 | }
|
---|
[104435] | 2397 | #ifndef VBOX_WITHOUT_WINDOWS_KERNEL_CODE_SIGNING_CERT /* A kernel code signing cert is only via way to use /IntegrityCheck. */
|
---|
[51770] | 2398 | if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
|
---|
[51977] | 2399 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
|
---|
| 2400 | "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
|
---|
| 2401 | ImageInfo.DllCharacteristics);
|
---|
[104381] | 2402 | #endif
|
---|
[51770] | 2403 | if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
|
---|
[51977] | 2404 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
|
---|
| 2405 | "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
|
---|
| 2406 | ImageInfo.DllCharacteristics);
|
---|
[51770] | 2407 | if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
|
---|
[51977] | 2408 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
|
---|
| 2409 | "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
|
---|
| 2410 | ImageInfo.DllCharacteristics);
|
---|
[51770] | 2411 |
|
---|
| 2412 | if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
|
---|
[51977] | 2413 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
|
---|
| 2414 | "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
|
---|
| 2415 | ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
|
---|
[51770] | 2416 |
|
---|
| 2417 | if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
|
---|
[51977] | 2418 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
|
---|
| 2419 | "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
|
---|
| 2420 | ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
|
---|
[51770] | 2421 |
|
---|
| 2422 | return VINF_SUCCESS;
|
---|
| 2423 | }
|
---|
| 2424 |
|
---|
| 2425 |
|
---|
| 2426 | /**
|
---|
| 2427 | * Check the integrity of the DLLs found in the process.
|
---|
| 2428 | *
|
---|
| 2429 | * @returns VBox status code.
|
---|
| 2430 | * @param pThis The process scanning state structure. Details
|
---|
[62677] | 2431 | * about images are added to this. The hProcess
|
---|
| 2432 | * member holds the handle to the process that is
|
---|
| 2433 | * to be verified.
|
---|
[51770] | 2434 | */
|
---|
[62677] | 2435 | static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis)
|
---|
[51770] | 2436 | {
|
---|
| 2437 | /*
|
---|
[52204] | 2438 | * Check for duplicate entries (paranoia).
|
---|
[51770] | 2439 | */
|
---|
| 2440 | uint32_t i = pThis->cImages;
|
---|
| 2441 | while (i-- > 1)
|
---|
| 2442 | {
|
---|
| 2443 | const char *pszName = pThis->aImages[i].pszName;
|
---|
| 2444 | uint32_t j = i;
|
---|
| 2445 | while (j-- > 0)
|
---|
| 2446 | if (pThis->aImages[j].pszName == pszName)
|
---|
[51977] | 2447 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
|
---|
| 2448 | "Duplicate image entries for %s: %ls and %ls",
|
---|
| 2449 | pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
|
---|
[51770] | 2450 | }
|
---|
| 2451 |
|
---|
| 2452 | /*
|
---|
| 2453 | * Check that both ntdll and kernel32 are present.
|
---|
| 2454 | * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
|
---|
| 2455 | */
|
---|
| 2456 | uint32_t iNtDll = UINT32_MAX;
|
---|
| 2457 | uint32_t iKernel32 = UINT32_MAX;
|
---|
| 2458 | i = pThis->cImages;
|
---|
| 2459 | while (i-- > 0)
|
---|
| 2460 | if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
|
---|
| 2461 | iNtDll = i;
|
---|
| 2462 | else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
|
---|
| 2463 | iKernel32 = i;
|
---|
| 2464 | if (iNtDll == UINT32_MAX)
|
---|
[51977] | 2465 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
|
---|
| 2466 | "The process has no NTDLL.DLL.");
|
---|
[80216] | 2467 | if (iKernel32 == UINT32_MAX && ( pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
|
---|
| 2468 | || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED))
|
---|
[51977] | 2469 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
|
---|
| 2470 | "The process has no KERNEL32.DLL.");
|
---|
[52204] | 2471 | else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
|
---|
[52205] | 2472 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
|
---|
[52204] | 2473 | "The process already has KERNEL32.DLL loaded.");
|
---|
[51770] | 2474 |
|
---|
| 2475 | /*
|
---|
| 2476 | * Verify that the DLLs are correctly signed (by MS).
|
---|
| 2477 | */
|
---|
| 2478 | i = pThis->cImages;
|
---|
| 2479 | while (i-- > 0)
|
---|
| 2480 | {
|
---|
[62677] | 2481 | int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i]);
|
---|
[51770] | 2482 | if (RT_FAILURE(rc))
|
---|
| 2483 | return rc;
|
---|
| 2484 | }
|
---|
| 2485 |
|
---|
| 2486 | return VINF_SUCCESS;
|
---|
| 2487 | }
|
---|
| 2488 |
|
---|
| 2489 |
|
---|
[97023] | 2490 | #ifdef IN_RING3
|
---|
[51770] | 2491 | /**
|
---|
[97023] | 2492 | * Verifies that we don't have any inheritable handles around, other than a few
|
---|
| 2493 | * ones for file and event objects.
|
---|
| 2494 | *
|
---|
| 2495 | * When finding an inheritable handle of a different type, it will change it to
|
---|
| 2496 | * non-inhertiable. This must NOT be called in the final process prior to
|
---|
| 2497 | * opening the device!
|
---|
| 2498 | *
|
---|
| 2499 | * @returns VBox status code
|
---|
| 2500 | * @param pThis The process scanning state structure.
|
---|
| 2501 | */
|
---|
| 2502 | static int supHardNtVpCheckHandles(PSUPHNTVPSTATE pThis)
|
---|
| 2503 | {
|
---|
| 2504 | SUP_DPRINTF(("supHardNtVpCheckHandles:\n"));
|
---|
| 2505 |
|
---|
| 2506 | /*
|
---|
| 2507 | * Take a snapshot of all the handles in the system.
|
---|
| 2508 | * (Because the current process handle snapshot was added in Windows 8,
|
---|
| 2509 | * so we cannot use that yet.)
|
---|
| 2510 | */
|
---|
| 2511 | uint32_t cbBuf = _256K;
|
---|
| 2512 | uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
|
---|
| 2513 | ULONG cbNeeded = cbBuf;
|
---|
| 2514 | NTSTATUS rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
|
---|
| 2515 | if (!NT_SUCCESS(rcNt))
|
---|
| 2516 | {
|
---|
| 2517 | while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
|
---|
| 2518 | && cbNeeded > cbBuf
|
---|
| 2519 | && cbBuf <= _32M)
|
---|
| 2520 | {
|
---|
| 2521 | cbBuf = RT_ALIGN_32(cbNeeded + _4K, _64K);
|
---|
| 2522 | RTMemFree(pbBuf);
|
---|
| 2523 | pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
|
---|
| 2524 | if (!pbBuf)
|
---|
| 2525 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes querying handles.", cbBuf);
|
---|
| 2526 | rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
|
---|
| 2527 | }
|
---|
| 2528 | if (!NT_SUCCESS(rcNt))
|
---|
| 2529 | {
|
---|
| 2530 | RTMemFree(pbBuf);
|
---|
| 2531 | return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes querying handles.", cbBuf);
|
---|
| 2532 | }
|
---|
| 2533 | }
|
---|
| 2534 |
|
---|
| 2535 | /*
|
---|
| 2536 | * Examine the snapshot for handles for this process.
|
---|
| 2537 | */
|
---|
| 2538 | int rcRet = VINF_SUCCESS;
|
---|
| 2539 | HANDLE const idProcess = RTNtCurrentTeb()->ClientId.UniqueProcess;
|
---|
| 2540 | SYSTEM_HANDLE_INFORMATION_EX const *pInfo = (SYSTEM_HANDLE_INFORMATION_EX const *)pbBuf;
|
---|
| 2541 | ULONG_PTR i = pInfo->NumberOfHandles;
|
---|
| 2542 | AssertRelease(RT_UOFFSETOF_DYN(SYSTEM_HANDLE_INFORMATION_EX, Handles[i]) == cbNeeded);
|
---|
| 2543 | while (i-- > 0)
|
---|
| 2544 | {
|
---|
| 2545 | SYSTEM_HANDLE_ENTRY_INFO_EX const *pHandleInfo = &pInfo->Handles[i];
|
---|
| 2546 | if ( (pHandleInfo->HandleAttributes & OBJ_INHERIT)
|
---|
| 2547 | && pHandleInfo->UniqueProcessId == idProcess)
|
---|
| 2548 | {
|
---|
| 2549 | ULONG cbNeeded2 = 0;
|
---|
| 2550 | rcNt = NtQueryObject(pHandleInfo->HandleValue, ObjectTypeInformation,
|
---|
| 2551 | pThis->abMemory, sizeof(pThis->abMemory), &cbNeeded2);
|
---|
| 2552 | if (NT_SUCCESS(rcNt))
|
---|
| 2553 | {
|
---|
| 2554 | POBJECT_TYPE_INFORMATION pTypeInfo = (POBJECT_TYPE_INFORMATION)pThis->abMemory;
|
---|
| 2555 | if ( pTypeInfo->TypeName.Length == sizeof(L"File") - sizeof(wchar_t)
|
---|
| 2556 | && memcmp(pTypeInfo->TypeName.Buffer, L"File", sizeof(L"File") - sizeof(wchar_t)) == 0)
|
---|
| 2557 | SUP_DPRINTF(("supHardNtVpCheckHandles: Inheritable file handle: %p\n", pHandleInfo->HandleValue));
|
---|
| 2558 | else if ( pTypeInfo->TypeName.Length == sizeof(L"Event") - sizeof(wchar_t)
|
---|
| 2559 | && memcmp(pTypeInfo->TypeName.Buffer, L"Event", sizeof(L"Event") - sizeof(wchar_t)) == 0)
|
---|
| 2560 | SUP_DPRINTF(("supHardNtVpCheckHandles: Inheritable event handle: %p\n", pHandleInfo->HandleValue));
|
---|
| 2561 | else
|
---|
| 2562 | {
|
---|
| 2563 | OBJECT_HANDLE_FLAG_INFORMATION SetInfo;
|
---|
| 2564 | SetInfo.Inherit = FALSE;
|
---|
| 2565 | SetInfo.ProtectFromClose = FALSE;
|
---|
| 2566 | rcNt = NtSetInformationObject(pHandleInfo->HandleValue, ObjectHandleFlagInformation,
|
---|
| 2567 | &SetInfo, sizeof(SetInfo));
|
---|
| 2568 | if (NT_SUCCESS(rcNt))
|
---|
| 2569 | {
|
---|
| 2570 | SUP_DPRINTF(("supHardNtVpCheckHandles: Marked %ls handle non-inheritable: %p\n",
|
---|
| 2571 | pTypeInfo->TypeName.Buffer, pHandleInfo->HandleValue));
|
---|
| 2572 | pThis->cFixes++;
|
---|
| 2573 | }
|
---|
| 2574 | else
|
---|
| 2575 | {
|
---|
| 2576 | rcRet = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SET_HANDLE_NOINHERIT,
|
---|
| 2577 | "NtSetInformationObject(%p,,,) -> %#x", pHandleInfo->HandleValue, rcNt);
|
---|
| 2578 | break;
|
---|
| 2579 | }
|
---|
| 2580 | }
|
---|
| 2581 | }
|
---|
| 2582 | else
|
---|
| 2583 | {
|
---|
| 2584 | rcRet = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_QUERY_HANDLE_TYPE,
|
---|
| 2585 | "NtQueryObject(%p,,,,) -> %#x", pHandleInfo->HandleValue, rcNt);
|
---|
| 2586 | break;
|
---|
| 2587 | }
|
---|
| 2588 |
|
---|
| 2589 | }
|
---|
| 2590 | }
|
---|
| 2591 | RTMemFree(pbBuf);
|
---|
| 2592 | return rcRet;
|
---|
| 2593 | }
|
---|
| 2594 | #endif /* IN_RING3 */
|
---|
| 2595 |
|
---|
| 2596 |
|
---|
| 2597 | /**
|
---|
[51770] | 2598 | * Verifies the given process.
|
---|
| 2599 | *
|
---|
| 2600 | * The following requirements are checked:
|
---|
| 2601 | * - The process only has one thread, the calling thread.
|
---|
| 2602 | * - The process has no debugger attached.
|
---|
| 2603 | * - The executable image of the process is verified to be signed with
|
---|
| 2604 | * certificate known to this code at build time.
|
---|
| 2605 | * - The executable image is one of a predefined set.
|
---|
| 2606 | * - The process has only a very limited set of system DLLs loaded.
|
---|
| 2607 | * - The system DLLs signatures check out fine.
|
---|
| 2608 | * - The only executable memory in the process belongs to the system DLLs and
|
---|
| 2609 | * the executable image.
|
---|
| 2610 | *
|
---|
| 2611 | * @returns VBox status code.
|
---|
| 2612 | * @param hProcess The process to verify.
|
---|
| 2613 | * @param hThread A thread in the process (the caller).
|
---|
[52204] | 2614 | * @param enmKind The kind of process verification to perform.
|
---|
[53017] | 2615 | * @param fFlags Valid combination of SUPHARDNTVP_F_XXX flags.
|
---|
[51770] | 2616 | * @param pErrInfo Pointer to error info structure. Optional.
|
---|
[52529] | 2617 | * @param pcFixes Where to return the number of fixes made during
|
---|
| 2618 | * purification. Optional.
|
---|
[51770] | 2619 | */
|
---|
[53017] | 2620 | DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, uint32_t fFlags,
|
---|
[52529] | 2621 | uint32_t *pcFixes, PRTERRINFO pErrInfo)
|
---|
[51770] | 2622 | {
|
---|
[52529] | 2623 | if (pcFixes)
|
---|
| 2624 | *pcFixes = 0;
|
---|
| 2625 |
|
---|
[52204] | 2626 | /*
|
---|
| 2627 | * Some basic checks regarding threads and debuggers. We don't need
|
---|
| 2628 | * allocate any state memory for these.
|
---|
| 2629 | */
|
---|
[52488] | 2630 | int rc = VINF_SUCCESS;
|
---|
[80216] | 2631 | if ( enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
|
---|
| 2632 | && enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
[52488] | 2633 | rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
|
---|
[51770] | 2634 | if (RT_SUCCESS(rc))
|
---|
| 2635 | rc = supHardNtVpDebugger(hProcess, pErrInfo);
|
---|
| 2636 | if (RT_SUCCESS(rc))
|
---|
| 2637 | {
|
---|
[52204] | 2638 | /*
|
---|
| 2639 | * Allocate and initialize memory for the state.
|
---|
| 2640 | */
|
---|
[52940] | 2641 | PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)RTMemAllocZ(sizeof(*pThis));
|
---|
[51770] | 2642 | if (pThis)
|
---|
| 2643 | {
|
---|
[52204] | 2644 | pThis->enmKind = enmKind;
|
---|
[53017] | 2645 | pThis->fFlags = fFlags;
|
---|
[52204] | 2646 | pThis->rcResult = VINF_SUCCESS;
|
---|
| 2647 | pThis->hProcess = hProcess;
|
---|
| 2648 | pThis->pErrInfo = pErrInfo;
|
---|
| 2649 |
|
---|
| 2650 | /*
|
---|
| 2651 | * Perform the verification.
|
---|
| 2652 | */
|
---|
[51977] | 2653 | rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
|
---|
[51770] | 2654 | if (RT_SUCCESS(rc))
|
---|
[52204] | 2655 | rc = supHardNtVpOpenImages(pThis);
|
---|
| 2656 | if (RT_SUCCESS(rc))
|
---|
[62677] | 2657 | rc = supHardNtVpCheckExe(pThis);
|
---|
[51770] | 2658 | if (RT_SUCCESS(rc))
|
---|
[62677] | 2659 | rc = supHardNtVpCheckDlls(pThis);
|
---|
[97023] | 2660 | #ifdef IN_RING3
|
---|
| 2661 | if (enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
|
---|
| 2662 | rc = supHardNtVpCheckHandles(pThis);
|
---|
| 2663 | #endif
|
---|
[51770] | 2664 |
|
---|
[52529] | 2665 | if (pcFixes)
|
---|
| 2666 | *pcFixes = pThis->cFixes;
|
---|
| 2667 |
|
---|
[52204] | 2668 | /*
|
---|
| 2669 | * Clean up the state.
|
---|
| 2670 | */
|
---|
[52366] | 2671 | #ifdef IN_RING0
|
---|
[52204] | 2672 | for (uint32_t i = 0; i < pThis->cImages; i++)
|
---|
[52366] | 2673 | supHardNTLdrCacheDeleteEntry(&pThis->aImages[i].CacheEntry);
|
---|
| 2674 | #endif
|
---|
[52940] | 2675 | RTMemFree(pThis);
|
---|
[51770] | 2676 | }
|
---|
| 2677 | else
|
---|
[52204] | 2678 | rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
|
---|
| 2679 | "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
|
---|
[51770] | 2680 | }
|
---|
| 2681 | return rc;
|
---|
| 2682 | }
|
---|
| 2683 |
|
---|