VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp@ 76882

Last change on this file since 76882 was 76818, checked in by vboxsync, 6 years ago

Config.kmk, HostDrivers/Support, Main, FE/Qt, Installer: bugref:9049: Wipe out VBOX_GUI_WITH_SHARED_LIBRARY, leaving split GUI build as the only possible.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette