VirtualBox

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

Last change on this file since 76527 was 76412, checked in by vboxsync, 6 years ago

iprt/string.h: Dropped including utf16.h and let those who need it include it themselves. bugref:9344

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