VirtualBox

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

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

FE/Qt and HostDriver/Support: bugref:9049: Hardening support for split GUI functionality (at least Win host).

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