VirtualBox

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

Last change on this file since 78374 was 77816, checked in by vboxsync, 6 years ago

SupHardNt: Made RTNtPathExpand8dot3Path() work correctly in kernel context (needs IPRT_NT_MAP_TO_ZW) and expand 8.3 names when comparing the executable image we found in the memory map with what NT returns for the process.

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