VirtualBox

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

Last change on this file since 69498 was 69498, checked in by vboxsync, 7 years ago

backed out r118835 as it incorrectly updated the 'This file is based on' file headers.

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