VirtualBox

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

Last change on this file since 86719 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 103.3 KB
Line 
1/* $Id: SUPHardenedVerifyProcess-win.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
496 {
497 PVOID pvRestoreAddr = (uint8_t *)pImage->uImageBase + uRva;
498 rcNt = supHardNtVpFileMemRestore(pThis, pvRestoreAddr, pbFile, cbThis, fCorrectProtection);
499 if (NT_SUCCESS(rcNt))
500 SUP_DPRINTF((" Restored %#x bytes of original file content at %p\n", cbThis, pvRestoreAddr));
501 else
502 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
503 "%s: Failed to restore %#x bytes at %p (%#x, #%u, %s): %#x (cDiffs=%#x, first=%#x)",
504 pImage->pszName, cbThis, pvRestoreAddr, uRva, iSh + 1, pachSectNm, rcNt,
505 cDiffs, uRva + off);
506 }
507 else
508#endif /* IN_RING3 */
509 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
510 "%s: %u differences between %#x and %#x in #%u (%.8s), first: %02x != %02x",
511 pImage->pszName, cDiffs, uRva + off, uRva + offLast, iSh + 1,
512 pachSectNm, pbFile[off], pbMemory[off]);
513 }
514
515 /* Advance. The clipping makes it a little bit complicated. */
516 cbThis = uNextRva - uRva;
517 if (cbThis >= cb)
518 break;
519 cb -= cbThis;
520 pbFile += cbThis;
521 uRva = uNextRva;
522 }
523 return VINF_SUCCESS;
524}
525
526
527
528static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
529 uint32_t uRva, uint32_t cb, uint32_t fProt)
530{
531 uint32_t const cbOrg = cb;
532 if (!cb)
533 return VINF_SUCCESS;
534 if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
535 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
536 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
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[7];
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 /* Ignore our patched KiUserApcDispatcher hack. */
906 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserApcDispatcher", &uValue);
907 if (RT_FAILURE(rc))
908 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserApcDispatcher': %Rrc", pImage->pszName, rc);
909 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
910 aSkipAreas[cSkipAreas++].cb = 14;
911
912#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
913 /* Ignore our patched KiUserExceptionDispatcher hack. */
914 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserExceptionDispatcher", &uValue);
915 if (RT_FAILURE(rc))
916 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserExceptionDispatcher': %Rrc", pImage->pszName, rc);
917 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue + (HC_ARCH_BITS == 64);
918 aSkipAreas[cSkipAreas++].cb = HC_ARCH_BITS == 64 ? 13 : 12;
919#endif
920
921 /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
922 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
923 if (RT_SUCCESS(rc))
924 {
925 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
926 aSkipAreas[cSkipAreas++].cb = RT_MAX(pbBits[(uint32_t)uValue], 0x50);
927 }
928
929 Assert(cSkipAreas <= RT_ELEMENTS(aSkipAreas));
930 }
931
932 /*
933 * Compare the file header with the loaded bits. The loader will fiddle
934 * with image base, changing it to the actual load address.
935 */
936 if (!pImage->fApiSetSchemaOnlySection1)
937 {
938 rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
939 if (RT_FAILURE(rc))
940 return rc;
941
942 rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
943 if (RT_FAILURE(rc))
944 return rc;
945 }
946
947 /*
948 * Validate sections:
949 * - Check them against the mapping regions.
950 * - Check section bits according to enmKind.
951 */
952 uint32_t fPrevProt = PAGE_READONLY;
953 uint32_t uRva = cbHdrsFile;
954 for (uint32_t i = 0; i < cSections; i++)
955 {
956 /* Validate the section. */
957 uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
958 if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
959 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
960 "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
961 pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
962 uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
963 if (cbMap > cbImage || uRva + cbMap > cbImage)
964 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
965 "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
966 pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
967 uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
968 if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
969 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
970 "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
971 pImage->pszName, i, cbFile, cbMap, uSectRva);
972
973 /* Validate the protection and bits. */
974 if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
975 {
976 uint32_t fProt;
977 switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
978 {
979 case IMAGE_SCN_MEM_READ:
980 fProt = PAGE_READONLY;
981 break;
982 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
983 fProt = PAGE_READWRITE;
984 if ( pThis->enmKind != SUPHARDNTVPKIND_VERIFY_ONLY
985 && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
986 && !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
987 fProt = PAGE_READONLY;
988 break;
989 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
990 fProt = PAGE_EXECUTE_READ;
991 break;
992 case IMAGE_SCN_MEM_EXECUTE:
993 fProt = PAGE_EXECUTE;
994 break;
995 case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
996 /* Only the executable is allowed to have this section,
997 and it's protected after we're done patching. */
998 if (!pImage->fDll)
999 {
1000 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1001 fProt = PAGE_EXECUTE_READWRITE;
1002 else
1003 fProt = PAGE_EXECUTE_READ;
1004 break;
1005 }
1006 default:
1007 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
1008 "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
1009 pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
1010 }
1011
1012 /* The section bits. Child purification verifies all, normal
1013 verification verifies all except where the executable is
1014 concerned (due to opening vboxdrv during early process init). */
1015 if ( ( (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
1016 && !(pThis->aSecHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
1017 || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
1018 || (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY && pImage->fDll)
1019 || pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1020 {
1021 rc = VINF_SUCCESS;
1022 if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
1023 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pbBits + uRva,
1024 i - 1, NULL, 0, fPrevProt);
1025 if (RT_SUCCESS(rc))
1026 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pbBits + uSectRva,
1027 i, aSkipAreas, cSkipAreas, fProt);
1028 if (RT_SUCCESS(rc))
1029 {
1030 uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
1031 ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
1032 if (cbMapAligned > cbMap)
1033 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
1034 g_abRTZeroPage, i, NULL, 0, fProt);
1035 }
1036 if (RT_FAILURE(rc))
1037 return rc;
1038 }
1039
1040 /* The protection (must be checked afterwards!). */
1041 rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
1042 if (RT_FAILURE(rc))
1043 return rc;
1044
1045 fPrevProt = fProt;
1046 }
1047
1048 /* Advance the RVA. */
1049 uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
1050 }
1051
1052 return VINF_SUCCESS;
1053}
1054
1055
1056/**
1057 * Verifies the signature of the given image on disk, then checks if the memory
1058 * mapping matches what we verified.
1059 *
1060 * @returns VBox status code.
1061 * @param pThis The process scanning state structure (for the
1062 * two scratch buffers).
1063 * @param pImage The image data collected during the address
1064 * space scan.
1065 */
1066static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
1067{
1068 /*
1069 * Validate the file signature first, then do the memory compare.
1070 */
1071 int rc;
1072 if ( pImage->pCacheEntry != NULL
1073 && pImage->pCacheEntry->hLdrMod != NIL_RTLDRMOD)
1074 {
1075 rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
1076 if (RT_SUCCESS(rc))
1077 rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage);
1078 }
1079 else
1080 rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "pCacheEntry/hLdrMod is NIL! Impossible!");
1081 return rc;
1082}
1083
1084
1085/**
1086 * Verifies that there is only one thread in the process.
1087 *
1088 * @returns VBox status code.
1089 * @param hProcess The process.
1090 * @param hThread The thread.
1091 * @param pErrInfo Pointer to error info structure. Optional.
1092 */
1093DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
1094{
1095 RT_NOREF1(hProcess);
1096
1097 /*
1098 * Use the ThreadAmILastThread request to check that there is only one
1099 * thread in the process.
1100 * Seems this isn't entirely reliable when hThread isn't the current thread?
1101 */
1102 ULONG cbIgn = 0;
1103 ULONG fAmI = 0;
1104 NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
1105 if (!NT_SUCCESS(rcNt))
1106 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
1107 "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
1108 if (!fAmI)
1109 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
1110 "More than one thread in process");
1111
1112 /** @todo Would be nice to verify the relationship between hProcess and hThread
1113 * as well... */
1114 return VINF_SUCCESS;
1115}
1116
1117
1118/**
1119 * Verifies that there isn't a debugger attached to the process.
1120 *
1121 * @returns VBox status code.
1122 * @param hProcess The process.
1123 * @param pErrInfo Pointer to error info structure. Optional.
1124 */
1125DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
1126{
1127#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
1128 /*
1129 * Use the ProcessDebugPort request to check there is no debugger
1130 * currently attached to the process.
1131 */
1132 ULONG cbIgn = 0;
1133 uintptr_t uPtr = ~(uintptr_t)0;
1134 NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
1135 ProcessDebugPort,
1136 &uPtr, sizeof(uPtr), &cbIgn);
1137 if (!NT_SUCCESS(rcNt))
1138 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
1139 "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
1140 if (uPtr != 0)
1141 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
1142 "Debugger attached (%#zx)", uPtr);
1143#else
1144 RT_NOREF2(hProcess, pErrInfo);
1145#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
1146 return VINF_SUCCESS;
1147}
1148
1149
1150/**
1151 * Matches two UNICODE_STRING structures in a case sensitive fashion.
1152 *
1153 * @returns true if equal, false if not.
1154 * @param pUniStr1 The first unicode string.
1155 * @param pUniStr2 The first unicode string.
1156 */
1157static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
1158{
1159 if (pUniStr1->Length != pUniStr2->Length)
1160 return false;
1161 return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
1162}
1163
1164
1165/**
1166 * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
1167 *
1168 * @returns true / false
1169 * @param pszName1 The ASCII name.
1170 * @param pwszName2 The UTF-16 name.
1171 */
1172static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
1173{
1174 for (;;)
1175 {
1176 char ch1 = *pszName1++;
1177 RTUTF16 wc2 = *pwszName2++;
1178 if (ch1 != wc2)
1179 {
1180 ch1 = RT_C_TO_LOWER(ch1);
1181 wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
1182 if (ch1 != wc2)
1183 return false;
1184 }
1185 if (!ch1)
1186 return true;
1187 }
1188}
1189
1190
1191/**
1192 * Compares two paths, expanding 8.3 short names as needed.
1193 *
1194 * @returns true / false.
1195 * @param pUniStr1 The first path. Must be zero terminated!
1196 * @param pUniStr2 The second path. Must be zero terminated!
1197 */
1198static bool supHardNtVpArePathsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
1199{
1200 /* Both strings must be null terminated. */
1201 Assert(pUniStr1->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
1202 Assert(pUniStr2->Buffer[pUniStr1->Length / sizeof(WCHAR)] == '\0');
1203
1204 /* Simple compare first.*/
1205 if (supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2))
1206 return true;
1207
1208 /* Make long names if needed. */
1209 UNICODE_STRING UniStrLong1 = { 0, 0, NULL };
1210 if (RTNtPathFindPossible8dot3Name(pUniStr1->Buffer))
1211 {
1212 int rc = RTNtPathExpand8dot3PathA(pUniStr1, false /*fPathOnly*/, &UniStrLong1);
1213 if (RT_SUCCESS(rc))
1214 pUniStr1 = &UniStrLong1;
1215 }
1216
1217 UNICODE_STRING UniStrLong2 = { 0, 0, NULL };
1218 if (RTNtPathFindPossible8dot3Name(pUniStr2->Buffer))
1219 {
1220 int rc = RTNtPathExpand8dot3PathA(pUniStr2, false /*fPathOnly*/, &UniStrLong2);
1221 if (RT_SUCCESS(rc))
1222 pUniStr2 = &UniStrLong2;
1223 }
1224
1225 /* Compare again. */
1226 bool fCompare = supHardNtVpAreUniStringsEqual(pUniStr1, pUniStr2);
1227
1228 /* Clean up. */
1229 if (UniStrLong1.Buffer)
1230 RTUtf16Free(UniStrLong1.Buffer);
1231 if (UniStrLong2.Buffer)
1232 RTUtf16Free(UniStrLong2.Buffer);
1233
1234 return fCompare;
1235}
1236
1237
1238/**
1239 * Records an additional memory region for an image.
1240 *
1241 * May trash pThis->abMemory.
1242 *
1243 * @returns VBox status code.
1244 * @retval VINF_OBJECT_DESTROYED if we've unmapped the image (child
1245 * purification only).
1246 * @param pThis The process scanning state structure.
1247 * @param pImage The new image structure. Only the unicode name
1248 * buffer is valid (it's zero-terminated).
1249 * @param pMemInfo The memory information for the image.
1250 */
1251static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1252{
1253 /*
1254 * If the filename or path contains short names, we have to get the long
1255 * path so that we will recognize the DLLs and their location.
1256 */
1257 int rc83Exp = VERR_IGNORED;
1258 PUNICODE_STRING pLongName = &pImage->Name.UniStr;
1259 if (RTNtPathFindPossible8dot3Name(pLongName->Buffer))
1260 {
1261 AssertCompile(sizeof(pThis->abMemory) > sizeof(pImage->Name));
1262 PUNICODE_STRING pTmp = (PUNICODE_STRING)pThis->abMemory;
1263 pTmp->MaximumLength = (USHORT)RT_MIN(_64K - 1, sizeof(pThis->abMemory) - sizeof(*pTmp)) - sizeof(RTUTF16);
1264 pTmp->Length = pImage->Name.UniStr.Length;
1265 pTmp->Buffer = (PRTUTF16)(pTmp + 1);
1266 memcpy(pTmp->Buffer, pLongName->Buffer, pLongName->Length + sizeof(RTUTF16));
1267
1268 rc83Exp = RTNtPathExpand8dot3Path(pTmp, false /*fPathOnly*/);
1269 Assert(rc83Exp == VINF_SUCCESS);
1270 Assert(pTmp->Buffer[pTmp->Length / sizeof(RTUTF16)] == '\0');
1271 if (rc83Exp == VINF_SUCCESS)
1272 SUP_DPRINTF(("supHardNtVpNewImage: 8dot3 -> long: '%ls' -> '%ls'\n", pLongName->Buffer, pTmp->Buffer));
1273 else
1274 SUP_DPRINTF(("supHardNtVpNewImage: RTNtPathExpand8dot3Path returns %Rrc for '%ls' (-> '%ls')\n",
1275 rc83Exp, pLongName->Buffer, pTmp->Buffer));
1276
1277 pLongName = pTmp;
1278 }
1279
1280 /*
1281 * Extract the final component.
1282 */
1283 RTUTF16 wc;
1284 unsigned cwcDirName = pLongName->Length / sizeof(WCHAR);
1285 PCRTUTF16 pwszFilename = &pLongName->Buffer[cwcDirName];
1286 while ( cwcDirName > 0
1287 && (wc = pwszFilename[-1]) != '\\'
1288 && wc != '/'
1289 && wc != ':')
1290 {
1291 pwszFilename--;
1292 cwcDirName--;
1293 }
1294 if (!*pwszFilename)
1295 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
1296 "Empty filename (len=%u) for image at %p.", pLongName->Length, pMemInfo->BaseAddress);
1297
1298 /*
1299 * Drop trailing slashes from the directory name.
1300 */
1301 while ( cwcDirName > 0
1302 && ( pLongName->Buffer[cwcDirName - 1] == '\\'
1303 || pLongName->Buffer[cwcDirName - 1] == '/'))
1304 cwcDirName--;
1305
1306 /*
1307 * Match it against known DLLs.
1308 */
1309 pImage->pszName = NULL;
1310 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
1311 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
1312 {
1313 pImage->pszName = g_apszSupNtVpAllowedDlls[i];
1314 pImage->fDll = true;
1315
1316#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1317 /* The directory name must match the one we've got for System32. */
1318 if ( ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
1319 || suplibHardenedMemComp(pLongName->Buffer, g_System32NtPath.UniStr.Buffer, cwcDirName * sizeof(WCHAR)) )
1320# ifdef VBOX_PERMIT_MORE
1321 && ( pImage->pszName[0] != 'a'
1322 || pImage->pszName[1] != 'c'
1323 || !supHardViIsAppPatchDir(pLongName->Buffer, pLongName->Length / sizeof(WCHAR)) )
1324# endif
1325 )
1326 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
1327 "Expected %ls to be loaded from %ls.",
1328 pLongName->Buffer, g_System32NtPath.UniStr.Buffer);
1329# ifdef VBOX_PERMIT_MORE
1330 if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
1331 pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
1332# endif
1333
1334#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1335 break;
1336 }
1337 if (!pImage->pszName)
1338 {
1339 /*
1340 * Not a known DLL, is it a known executable?
1341 */
1342 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
1343 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
1344 {
1345 pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
1346 pImage->fDll = false;
1347 break;
1348 }
1349 }
1350 if (!pImage->pszName)
1351 {
1352 /*
1353 * Unknown image.
1354 *
1355 * If we're cleaning up a child process, we can unmap the offending
1356 * DLL... Might have interesting side effects, or at least interesting
1357 * as in "may you live in interesting times".
1358 */
1359#ifdef IN_RING3
1360 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
1361 && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1362 {
1363 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping image mem at %p (%p LB %#zx) - '%ls'\n",
1364 pMemInfo->AllocationBase, pMemInfo->BaseAddress, pMemInfo->RegionSize, pwszFilename));
1365 NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pMemInfo->AllocationBase);
1366 if (NT_SUCCESS(rcNt))
1367 return VINF_OBJECT_DESTROYED;
1368 pThis->cFixes++;
1369 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
1370 }
1371 else if (pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
1372 {
1373 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Ignoring unknown mem at %p LB %#zx (base %p) - '%ls'\n",
1374 pMemInfo->BaseAddress, pMemInfo->RegionSize, pMemInfo->AllocationBase, pwszFilename));
1375 return VINF_OBJECT_DESTROYED;
1376 }
1377#endif
1378 /*
1379 * Special error message if we can.
1380 */
1381 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
1382 && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
1383 || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
1384 || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)
1385 || supHardNtVpAreNamesEqual("sysfrethunk.dll", pwszFilename)) )
1386 {
1387 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
1388 "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
1389 "You or your admin need to add and exception to the Application and Device Control (ADC) "
1390 "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
1391 "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
1392 , pLongName->Buffer, pMemInfo->BaseAddress);
1393 return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
1394 }
1395 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
1396 "Unknown image file %ls at %p. (rc83Exp=%Rrc)",
1397 pLongName->Buffer, pMemInfo->BaseAddress, rc83Exp);
1398 }
1399
1400 /*
1401 * Checks for multiple mappings of the same DLL but with different image file paths.
1402 */
1403 uint32_t i = pThis->cImages;
1404 while (i-- > 1)
1405 if (pImage->pszName == pThis->aImages[i].pszName)
1406 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1407 "Duplicate image entries for %s: %ls and %ls",
1408 pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
1409
1410 /*
1411 * Since it's a new image, we expect to be at the start of the mapping now.
1412 */
1413 if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
1414 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
1415 "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
1416 pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
1417
1418 /*
1419 * Check for size/rva overflow.
1420 */
1421 if (pMemInfo->RegionSize >= _2G)
1422 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1423 "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
1424
1425 /*
1426 * Fill in details from the memory info.
1427 */
1428 pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
1429 pImage->cbImage = pMemInfo->RegionSize;
1430 pImage->pCacheEntry= NULL;
1431 pImage->cRegions = 1;
1432 pImage->aRegions[0].uRva = 0;
1433 pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
1434 pImage->aRegions[0].fProt = pMemInfo->Protect;
1435
1436 if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
1437 pImage->fNtCreateSectionPatch = true;
1438 else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
1439 pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
1440#ifdef VBOX_PERMIT_MORE
1441 else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
1442 pImage->f32bitResourceDll = true;
1443#endif
1444
1445 return VINF_SUCCESS;
1446}
1447
1448
1449/**
1450 * Records an additional memory region for an image.
1451 *
1452 * @returns VBox status code.
1453 * @param pThis The process scanning state structure.
1454 * @param pImage The image.
1455 * @param pMemInfo The memory information for the region.
1456 */
1457static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1458{
1459 /*
1460 * Make sure the base address matches.
1461 */
1462 if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
1463 return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
1464 "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
1465 pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
1466 pMemInfo->BaseAddress, pMemInfo->RegionSize);
1467
1468 /*
1469 * Check for size and rva overflows.
1470 */
1471 uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
1472 if (pMemInfo->RegionSize >= _2G)
1473 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1474 "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1475 if (uRva >= _2G)
1476 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
1477 "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1478
1479
1480 /*
1481 * Record the region.
1482 */
1483 uint32_t iRegion = pImage->cRegions;
1484 if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
1485 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
1486 "Too many regions for %s.", pImage->pszName);
1487 pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
1488 pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
1489 pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
1490 pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
1491 pImage->cRegions++;
1492 pImage->fApiSetSchemaOnlySection1 = false;
1493
1494 return VINF_SUCCESS;
1495}
1496
1497
1498#ifdef IN_RING3
1499/**
1500 * Frees (or replaces) executable memory of allocation type private.
1501 *
1502 * @returns True if nothing really bad happen, false if to quit ASAP because we
1503 * killed the process being scanned.
1504 * @param pThis The process scanning state structure. Details
1505 * about images are added to this.
1506 * @param hProcess The process to verify.
1507 * @param pMemInfo The information we've got on this private
1508 * executable memory.
1509 */
1510static bool supHardNtVpFreeOrReplacePrivateExecMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess,
1511 MEMORY_BASIC_INFORMATION const *pMemInfo)
1512{
1513 NTSTATUS rcNt;
1514
1515 /*
1516 * Try figure the entire allocation size. Free/Alloc may fail otherwise.
1517 */
1518 PVOID pvFree = pMemInfo->AllocationBase;
1519 SIZE_T cbFree = pMemInfo->RegionSize + ((uintptr_t)pMemInfo->BaseAddress - (uintptr_t)pMemInfo->AllocationBase);
1520 for (;;)
1521 {
1522 SIZE_T cbActual = 0;
1523 MEMORY_BASIC_INFORMATION MemInfo2 = { 0, 0, 0, 0, 0, 0, 0 };
1524 uintptr_t uPtrNext = (uintptr_t)pvFree + cbFree;
1525 rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1526 (void const *)uPtrNext,
1527 MemoryBasicInformation,
1528 &MemInfo2,
1529 sizeof(MemInfo2),
1530 &cbActual);
1531 if (!NT_SUCCESS(rcNt))
1532 break;
1533 if (pMemInfo->AllocationBase != MemInfo2.AllocationBase)
1534 break;
1535 if (MemInfo2.RegionSize == 0)
1536 break;
1537 cbFree += MemInfo2.RegionSize;
1538 }
1539 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: %s exec mem at %p (LB %#zx, %p LB %#zx)\n",
1540 pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW ? "Replacing" : "Freeing",
1541 pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize));
1542
1543 /*
1544 * In the BSOD workaround mode, we need to make a copy of the memory before
1545 * freeing it. Bird abuses this code for logging purposes too.
1546 */
1547 uintptr_t uCopySrc = (uintptr_t)pvFree;
1548 size_t cbCopy = 0;
1549 void *pvCopy = NULL;
1550 //if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
1551 {
1552 cbCopy = cbFree;
1553 pvCopy = RTMemAllocZ(cbCopy);
1554 if (!pvCopy)
1555 {
1556 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED, "RTMemAllocZ(%#zx) failed", cbCopy);
1557 return true;
1558 }
1559
1560 rcNt = supHardNtVpReadMem(hProcess, uCopySrc, pvCopy, cbCopy);
1561 if (!NT_SUCCESS(rcNt))
1562 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1563 "Error reading data from original alloc: %#x (%p LB %#zx)", rcNt, uCopySrc, cbCopy, rcNt);
1564 for (size_t off = 0; off < cbCopy; off += 256)
1565 {
1566 size_t const cbChunk = RT_MIN(256, cbCopy - off);
1567 void const *pvChunk = (uint8_t const *)pvCopy + off;
1568 if (!ASMMemIsZero(pvChunk, cbChunk))
1569 SUP_DPRINTF(("%.*RhxD\n", cbChunk, pvChunk));
1570 }
1571 if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
1572 supR3HardenedLogFlush();
1573 }
1574
1575 /*
1576 * Free the memory.
1577 */
1578 for (uint32_t i = 0; i < 10; i++)
1579 {
1580 PVOID pvFreeInOut = pvFree;
1581 SIZE_T cbFreeInOut = 0;
1582 rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1583 if (NT_SUCCESS(rcNt))
1584 {
1585 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 succeeded: %#x [%p/%p LB 0/%#zx]\n",
1586 rcNt, pvFree, pvFreeInOut, cbFreeInOut));
1587 supR3HardenedLogFlush();
1588 }
1589 else
1590 {
1591 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 failed: %#x [%p LB 0]\n", rcNt, pvFree));
1592 supR3HardenedLogFlush();
1593 pvFreeInOut = pvFree;
1594 cbFreeInOut = cbFree;
1595 rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1596 if (NT_SUCCESS(rcNt))
1597 {
1598 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 succeeded: %#x [%p/%p LB %#zx/%#zx]\n",
1599 rcNt, pvFree, pvFreeInOut, cbFree, cbFreeInOut));
1600 supR3HardenedLogFlush();
1601 }
1602 else
1603 {
1604 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 failed: %#x [%p LB %#zx]\n",
1605 rcNt, pvFree, cbFree));
1606 supR3HardenedLogFlush();
1607 pvFreeInOut = pMemInfo->BaseAddress;
1608 cbFreeInOut = pMemInfo->RegionSize;
1609 rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1610 if (NT_SUCCESS(rcNt))
1611 {
1612 pvFree = pMemInfo->BaseAddress;
1613 cbFree = pMemInfo->RegionSize;
1614 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #3 succeeded [%p LB %#zx]\n",
1615 pvFree, cbFree));
1616 supR3HardenedLogFlush();
1617 }
1618 else
1619 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1620 "NtFreeVirtualMemory [%p LB %#zx and %p LB %#zx] failed: %#x",
1621 pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
1622 }
1623 }
1624
1625 /*
1626 * Query the region again, redo the free operation if there's still memory there.
1627 */
1628 if (!NT_SUCCESS(rcNt))
1629 break;
1630 SIZE_T cbActual = 0;
1631 MEMORY_BASIC_INFORMATION MemInfo3 = { 0, 0, 0, 0, 0, 0, 0 };
1632 NTSTATUS rcNt2 = g_pfnNtQueryVirtualMemory(hProcess, pvFree, MemoryBasicInformation,
1633 &MemInfo3, sizeof(MemInfo3), &cbActual);
1634 if (!NT_SUCCESS(rcNt2))
1635 break;
1636 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: QVM after free %u: [%p]/%p LB %#zx s=%#x ap=%#x rp=%#p\n",
1637 i, MemInfo3.AllocationBase, MemInfo3.BaseAddress, MemInfo3.RegionSize, MemInfo3.State,
1638 MemInfo3.AllocationProtect, MemInfo3.Protect));
1639 supR3HardenedLogFlush();
1640 if (MemInfo3.State == MEM_FREE || !(pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
1641 break;
1642 NtYieldExecution();
1643 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Retrying free...\n"));
1644 supR3HardenedLogFlush();
1645 }
1646
1647 /*
1648 * Restore memory as non-executable - Kludge for Trend Micro sakfile.sys
1649 * and Digital Guardian dgmaster.sys BSODs.
1650 */
1651 if (NT_SUCCESS(rcNt) && (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
1652 {
1653 PVOID pvAlloc = pvFree;
1654 SIZE_T cbAlloc = cbFree;
1655 rcNt = NtAllocateVirtualMemory(hProcess, &pvAlloc, 0, &cbAlloc, MEM_COMMIT, PAGE_READWRITE);
1656 if (!NT_SUCCESS(rcNt))
1657 {
1658 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1659 "NtAllocateVirtualMemory (%p LB %#zx) failed with rcNt=%#x allocating "
1660 "replacement memory for working around buggy protection software. "
1661 "See VBoxStartup.log for more details",
1662 pvAlloc, cbFree, rcNt);
1663 supR3HardenedLogFlush();
1664 NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1665 return false;
1666 }
1667
1668 if ( (uintptr_t)pvFree < (uintptr_t)pvAlloc
1669 || (uintptr_t)pvFree + cbFree > (uintptr_t)pvAlloc + cbFree)
1670 {
1671 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1672 "We wanted NtAllocateVirtualMemory to get us %p LB %#zx, but it returned %p LB %#zx.",
1673 pMemInfo->BaseAddress, pMemInfo->RegionSize, pvFree, cbFree, rcNt);
1674 supR3HardenedLogFlush();
1675 NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1676 return false;
1677 }
1678
1679 /*
1680 * Copy what we can, considering the 2nd free attempt.
1681 */
1682 uint8_t *pbDst = (uint8_t *)pvFree;
1683 size_t cbDst = cbFree;
1684 uint8_t *pbSrc = (uint8_t *)pvCopy;
1685 size_t cbSrc = cbCopy;
1686 if ((uintptr_t)pbDst != uCopySrc)
1687 {
1688 if ((uintptr_t)pbDst > uCopySrc)
1689 {
1690 uintptr_t cbAdj = (uintptr_t)pbDst - uCopySrc;
1691 pbSrc += cbAdj;
1692 cbSrc -= cbAdj;
1693 }
1694 else
1695 {
1696 uintptr_t cbAdj = uCopySrc - (uintptr_t)pbDst;
1697 pbDst += cbAdj;
1698 cbDst -= cbAdj;
1699 }
1700 }
1701 if (cbSrc > cbDst)
1702 cbSrc = cbDst;
1703
1704 SIZE_T cbWritten;
1705 rcNt = NtWriteVirtualMemory(hProcess, pbDst, pbSrc, cbSrc, &cbWritten);
1706 if (NT_SUCCESS(rcNt))
1707 {
1708 SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Restored the exec memory as non-exec.\n"));
1709 supR3HardenedLogFlush();
1710 }
1711 else
1712 {
1713 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1714 "NtWriteVirtualMemory (%p LB %#zx) failed: %#x",
1715 pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
1716 supR3HardenedLogFlush();
1717 NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1718 return false;
1719 }
1720 }
1721 if (pvCopy)
1722 RTMemFree(pvCopy);
1723 return true;
1724}
1725#endif /* IN_RING3 */
1726
1727
1728/**
1729 * Scans the virtual memory of the process.
1730 *
1731 * This collects the locations of DLLs and the EXE, and verifies that executable
1732 * memory is only associated with these. May trash pThis->abMemory.
1733 *
1734 * @returns VBox status code.
1735 * @param pThis The process scanning state structure. Details
1736 * about images are added to this.
1737 * @param hProcess The process to verify.
1738 */
1739static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1740{
1741 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
1742 pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
1743 pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
1744
1745 uint32_t cXpExceptions = 0;
1746 uintptr_t cbAdvance = 0;
1747 uintptr_t uPtrWhere = 0;
1748#ifdef VBOX_PERMIT_VERIFIER_DLL
1749 for (uint32_t i = 0; i < 10240; i++)
1750#else
1751 for (uint32_t i = 0; i < 1024; i++)
1752#endif
1753 {
1754 SIZE_T cbActual = 0;
1755 MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
1756 NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1757 (void const *)uPtrWhere,
1758 MemoryBasicInformation,
1759 &MemInfo,
1760 sizeof(MemInfo),
1761 &cbActual);
1762 if (!NT_SUCCESS(rcNt))
1763 {
1764 if (rcNt == STATUS_INVALID_PARAMETER)
1765 return pThis->rcResult;
1766 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
1767 "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
1768 }
1769
1770 /*
1771 * Record images.
1772 */
1773 if ( MemInfo.Type == SEC_IMAGE
1774 || MemInfo.Type == SEC_PROTECTED_IMAGE
1775 || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
1776 {
1777 uint32_t iImg = pThis->cImages;
1778 rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1779 (void const *)uPtrWhere,
1780 MemorySectionName,
1781 &pThis->aImages[iImg].Name,
1782 sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
1783 &cbActual);
1784 if (!NT_SUCCESS(rcNt))
1785 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
1786 "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
1787 pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
1788 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1789 ? " *%p-%p %#06x/%#06x %#09x %ls\n"
1790 : " %p-%p %#06x/%#06x %#09x %ls\n",
1791 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
1792 MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
1793
1794 /* New or existing image? */
1795 bool fNew = true;
1796 uint32_t iSearch = iImg;
1797 while (iSearch-- > 0)
1798 if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
1799 {
1800 int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
1801 if (RT_FAILURE(rc))
1802 return rc;
1803 fNew = false;
1804 break;
1805 }
1806 else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
1807 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
1808 "Unexpected base address match");
1809
1810 if (fNew)
1811 {
1812 int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
1813 if (RT_SUCCESS(rc))
1814 {
1815 if (rc != VINF_OBJECT_DESTROYED)
1816 {
1817 pThis->cImages++;
1818 if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
1819 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
1820 "Internal error: aImages is full.\n");
1821 }
1822 }
1823#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
1824 else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
1825 return rc;
1826#else
1827 else
1828 return rc;
1829#endif
1830 }
1831 }
1832 /*
1833 * XP, W2K3: Ignore the CSRSS read-only region as best we can.
1834 */
1835 else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1836 == PAGE_EXECUTE_READ
1837 && cXpExceptions == 0
1838 && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
1839 /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
1840 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
1841 {
1842 cXpExceptions++;
1843 SUP_DPRINTF((" %p-%p %#06x/%#06x %#09x XP CSRSS read-only region\n", MemInfo.BaseAddress,
1844 (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
1845 MemInfo.AllocationProtect, MemInfo.Type));
1846 }
1847 /*
1848 * Executable memory?
1849 */
1850#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1851 else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1852 {
1853 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1854 ? " *%p-%p %#06x/%#06x %#09x !!\n"
1855 : " %p-%p %#06x/%#06x %#09x !!\n",
1856 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
1857 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1858# ifdef IN_RING3
1859 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1860 {
1861 /*
1862 * Free any private executable memory (sysplant.sys allocates executable memory).
1863 */
1864 if (MemInfo.Type == MEM_PRIVATE)
1865 {
1866 if (!supHardNtVpFreeOrReplacePrivateExecMemory(pThis, hProcess, &MemInfo))
1867 break;
1868 }
1869 /*
1870 * Unmap mapped memory, failing that, drop exec privileges.
1871 */
1872 else if (MemInfo.Type == MEM_MAPPED)
1873 {
1874 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
1875 uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
1876 rcNt = NtUnmapViewOfSection(hProcess, MemInfo.AllocationBase);
1877 if (!NT_SUCCESS(rcNt))
1878 {
1879 PVOID pvCopy = MemInfo.BaseAddress;
1880 SIZE_T cbCopy = MemInfo.RegionSize;
1881 NTSTATUS rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
1882 if (!NT_SUCCESS(rcNt2))
1883 rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
1884 if (!NT_SUCCESS(rcNt2))
1885 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
1886 "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
1887 MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
1888 }
1889 }
1890 else
1891 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
1892 "Unknown executable memory type %#x at %p/%p LB %#zx",
1893 MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
1894 pThis->cFixes++;
1895 }
1896 else if (pThis->enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
1897# endif /* IN_RING3 */
1898 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
1899 "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
1900 uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
1901 MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
1902
1903# ifndef IN_RING3
1904 if (RT_FAILURE(pThis->rcResult))
1905 return pThis->rcResult;
1906# endif
1907 /* Continue add more information about the problematic process. */
1908 }
1909#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1910 else
1911 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1912 ? " *%p-%p %#06x/%#06x %#09x\n"
1913 : " %p-%p %#06x/%#06x %#09x\n",
1914 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
1915 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1916
1917 /*
1918 * Advance.
1919 */
1920 cbAdvance = MemInfo.RegionSize;
1921 if (uPtrWhere + cbAdvance <= uPtrWhere)
1922 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
1923 "Empty region at %p.", uPtrWhere);
1924 uPtrWhere += MemInfo.RegionSize;
1925 }
1926
1927 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
1928 "Too many virtual memory regions.\n");
1929}
1930
1931
1932/**
1933 * Verifies the loader image, i.e. check cryptographic signatures if present.
1934 *
1935 * @returns VBox status code.
1936 * @param pEntry The loader cache entry.
1937 * @param pwszName The filename to use in error messages.
1938 * @param pErrInfo Where to return extened error information.
1939 */
1940DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
1941{
1942 int rc = VINF_SUCCESS;
1943 if (!pEntry->fVerified)
1944 {
1945 rc = supHardenedWinVerifyImageByLdrMod(pEntry->hLdrMod, pwszName, pEntry->pNtViRdr,
1946 false /*fAvoidWinVerifyTrust*/, NULL /*pfWinVerifyTrust*/, pErrInfo);
1947 pEntry->fVerified = RT_SUCCESS(rc);
1948 }
1949 return rc;
1950}
1951
1952
1953/**
1954 * Allocates a image bits buffer and calls RTLdrGetBits on them.
1955 *
1956 * An assumption here is that there won't ever be concurrent use of the cache.
1957 * It's currently 104% single threaded, non-reentrant. Thus, we can't reuse the
1958 * pbBits allocation.
1959 *
1960 * @returns VBox status code
1961 * @param pEntry The loader cache entry.
1962 * @param ppbBits Where to return the pointer to the allocation.
1963 * @param uBaseAddress The image base address, see RTLdrGetBits.
1964 * @param pfnGetImport Import getter, see RTLdrGetBits.
1965 * @param pvUser The user argument for @a pfnGetImport.
1966 * @param pErrInfo Where to return extened error information.
1967 */
1968DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
1969 RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
1970 PRTERRINFO pErrInfo)
1971{
1972 int rc;
1973
1974 /*
1975 * First time around we have to allocate memory before we can get the image bits.
1976 */
1977 if (!pEntry->pbBits)
1978 {
1979 size_t cbBits = RTLdrSize(pEntry->hLdrMod);
1980 if (cbBits >= _1M*32U)
1981 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
1982 pEntry->pszName, cbBits, cbBits);
1983
1984 pEntry->pbBits = (uint8_t *)RTMemAllocZ(cbBits);
1985 if (!pEntry->pbBits)
1986 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
1987 cbBits, pEntry->pszName);
1988
1989 pEntry->fValidBits = false; /* paranoia */
1990
1991 rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
1992 if (RT_FAILURE(rc))
1993 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
1994 pEntry->pszName, rc);
1995 pEntry->uImageBase = uBaseAddress;
1996 pEntry->fValidBits = pfnGetImport == NULL;
1997
1998 }
1999 /*
2000 * Cache hit? No?
2001 *
2002 * Note! We cannot currently cache image bits for images with imports as we
2003 * don't control the way they're resolved. Fortunately, NTDLL and
2004 * the VM process images all have no imports.
2005 */
2006 else if ( !pEntry->fValidBits
2007 || pEntry->uImageBase != uBaseAddress
2008 || pfnGetImport)
2009 {
2010 pEntry->fValidBits = false;
2011
2012 rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
2013 if (RT_FAILURE(rc))
2014 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
2015 pEntry->pszName, rc);
2016 pEntry->uImageBase = uBaseAddress;
2017 pEntry->fValidBits = pfnGetImport == NULL;
2018 }
2019
2020 *ppbBits = pEntry->pbBits;
2021 return VINF_SUCCESS;
2022}
2023
2024
2025/**
2026 * Frees all resources associated with a cache entry and wipes the members
2027 * clean.
2028 *
2029 * @param pEntry The entry to delete.
2030 */
2031static void supHardNTLdrCacheDeleteEntry(PSUPHNTLDRCACHEENTRY pEntry)
2032{
2033 if (pEntry->pbBits)
2034 {
2035 RTMemFree(pEntry->pbBits);
2036 pEntry->pbBits = NULL;
2037 }
2038
2039 if (pEntry->hLdrMod != NIL_RTLDRMOD)
2040 {
2041 RTLdrClose(pEntry->hLdrMod);
2042 pEntry->hLdrMod = NIL_RTLDRMOD;
2043 pEntry->pNtViRdr = NULL;
2044 }
2045 else if (pEntry->pNtViRdr)
2046 {
2047 pEntry->pNtViRdr->Core.pfnDestroy(&pEntry->pNtViRdr->Core);
2048 pEntry->pNtViRdr = NULL;
2049 }
2050
2051 if (pEntry->hFile)
2052 {
2053 NtClose(pEntry->hFile);
2054 pEntry->hFile = NULL;
2055 }
2056
2057 pEntry->pszName = NULL;
2058 pEntry->fVerified = false;
2059 pEntry->fValidBits = false;
2060 pEntry->uImageBase = 0;
2061}
2062
2063#ifdef IN_RING3
2064
2065/**
2066 * Flushes the cache.
2067 *
2068 * This is called from one of two points in the hardened main code, first is
2069 * after respawning and the second is when we open the vboxdrv device for
2070 * unrestricted access.
2071 */
2072DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache(void)
2073{
2074 uint32_t i = g_cSupNtVpLdrCacheEntries;
2075 while (i-- > 0)
2076 supHardNTLdrCacheDeleteEntry(&g_aSupNtVpLdrCacheEntries[i]);
2077 g_cSupNtVpLdrCacheEntries = 0;
2078}
2079
2080
2081/**
2082 * Searches the cache for a loader image.
2083 *
2084 * @returns Pointer to the cache entry if found, NULL if not.
2085 * @param pszName The name (from g_apszSupNtVpAllowedVmExes or
2086 * g_apszSupNtVpAllowedDlls).
2087 */
2088static PSUPHNTLDRCACHEENTRY supHardNtLdrCacheLookupEntry(const char *pszName)
2089{
2090 /*
2091 * Since the caller is supplying us a pszName from one of the two tables,
2092 * we can dispense with string compare and simply compare string pointers.
2093 */
2094 uint32_t i = g_cSupNtVpLdrCacheEntries;
2095 while (i-- > 0)
2096 if (g_aSupNtVpLdrCacheEntries[i].pszName == pszName)
2097 return &g_aSupNtVpLdrCacheEntries[i];
2098 return NULL;
2099}
2100
2101#endif /* IN_RING3 */
2102
2103static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
2104 bool fDll, bool f32bitResourceDll, PRTERRINFO pErrInfo)
2105{
2106 /*
2107 * Open the image file.
2108 */
2109 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
2110 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2111
2112 OBJECT_ATTRIBUTES ObjAttr;
2113 InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2114#ifdef IN_RING0
2115 ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
2116#endif
2117
2118 NTSTATUS rcNt = NtCreateFile(&hFile,
2119 GENERIC_READ | SYNCHRONIZE,
2120 &ObjAttr,
2121 &Ios,
2122 NULL /* Allocation Size*/,
2123 FILE_ATTRIBUTE_NORMAL,
2124 FILE_SHARE_READ,
2125 FILE_OPEN,
2126 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2127 NULL /*EaBuffer*/,
2128 0 /*EaLength*/);
2129 if (NT_SUCCESS(rcNt))
2130 rcNt = Ios.Status;
2131 if (!NT_SUCCESS(rcNt))
2132 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
2133 "Error opening image for scanning: %#x (name %ls)", rcNt, pUniStrPath->Buffer);
2134
2135 /*
2136 * Figure out validation flags we'll be using and create the reader
2137 * for this image.
2138 */
2139 uint32_t fFlags = fDll
2140 ? SUPHNTVI_F_TRUSTED_INSTALLER_OWNER | SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION
2141 : SUPHNTVI_F_REQUIRE_BUILD_CERT;
2142 if (f32bitResourceDll)
2143 fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
2144
2145 PSUPHNTVIRDR pNtViRdr;
2146 int rc = supHardNtViRdrCreate(hFile, pUniStrPath->Buffer, fFlags, &pNtViRdr);
2147 if (RT_FAILURE(rc))
2148 {
2149 NtClose(hFile);
2150 return rc;
2151 }
2152
2153 /*
2154 * Finally, open the image with the loader
2155 */
2156 RTLDRMOD hLdrMod;
2157 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
2158 if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
2159 enmArch = RTLDRARCH_WHATEVER;
2160 rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
2161 if (RT_FAILURE(rc))
2162 return supHardNtVpAddInfo1(pErrInfo, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
2163 rc, pUniStrPath->Buffer);
2164
2165 /*
2166 * Fill in the cache entry.
2167 */
2168 pEntry->pszName = pszName;
2169 pEntry->hLdrMod = hLdrMod;
2170 pEntry->pNtViRdr = pNtViRdr;
2171 pEntry->hFile = hFile;
2172 pEntry->pbBits = NULL;
2173 pEntry->fVerified = false;
2174 pEntry->fValidBits = false;
2175 pEntry->uImageBase = ~(uintptr_t)0;
2176
2177#ifdef IN_SUP_HARDENED_R3
2178 /*
2179 * Log the image timestamp when in the hardened exe.
2180 */
2181 uint64_t uTimestamp = 0;
2182 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uint64_t));
2183 SUP_DPRINTF(("%s: timestamp %#llx (rc=%Rrc)\n", pszName, uTimestamp, rc));
2184#endif
2185
2186 return VINF_SUCCESS;
2187}
2188
2189#ifdef IN_RING3
2190/**
2191 * Opens a loader cache entry.
2192 *
2193 * Currently this is only used by the import code for getting NTDLL.
2194 *
2195 * @returns VBox status code.
2196 * @param pszName The DLL name. Must be one from the
2197 * g_apszSupNtVpAllowedDlls array.
2198 * @param ppEntry Where to return the entry we've opened/found.
2199 * @param pErrInfo Optional buffer where to return additional error
2200 * information.
2201 */
2202DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry, PRTERRINFO pErrInfo)
2203{
2204 /*
2205 * Locate the dll.
2206 */
2207 uint32_t i = 0;
2208 while ( i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)
2209 && strcmp(pszName, g_apszSupNtVpAllowedDlls[i]))
2210 i++;
2211 if (i >= RT_ELEMENTS(g_apszSupNtVpAllowedDlls))
2212 return VERR_FILE_NOT_FOUND;
2213 pszName = g_apszSupNtVpAllowedDlls[i];
2214
2215 /*
2216 * Try the cache.
2217 */
2218 *ppEntry = supHardNtLdrCacheLookupEntry(pszName);
2219 if (*ppEntry)
2220 return VINF_SUCCESS;
2221
2222 /*
2223 * Not in the cache, so open it.
2224 * Note! We cannot assume that g_System32NtPath has been initialized at this point.
2225 */
2226 if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
2227 return VERR_INTERNAL_ERROR_3;
2228
2229 static WCHAR s_wszSystem32[] = L"\\SystemRoot\\System32\\";
2230 WCHAR wszPath[64];
2231 memcpy(wszPath, s_wszSystem32, sizeof(s_wszSystem32));
2232 RTUtf16CatAscii(wszPath, sizeof(wszPath), pszName);
2233
2234 UNICODE_STRING UniStr;
2235 UniStr.Buffer = wszPath;
2236 UniStr.Length = (USHORT)(RTUtf16Len(wszPath) * sizeof(WCHAR));
2237 UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
2238
2239 int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
2240 true /*fDll*/, false /*f32bitResourceDll*/, pErrInfo);
2241 if (RT_SUCCESS(rc))
2242 {
2243 *ppEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
2244 g_cSupNtVpLdrCacheEntries++;
2245 return VINF_SUCCESS;
2246 }
2247 return rc;
2248}
2249#endif /* IN_RING3 */
2250
2251
2252/**
2253 * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
2254 * hLdrMod for each image.
2255 *
2256 * @returns VBox status code.
2257 * @param pThis The process scanning state structure.
2258 */
2259static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
2260{
2261 unsigned i = pThis->cImages;
2262 while (i-- > 0)
2263 {
2264 PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
2265
2266#ifdef IN_RING3
2267 /*
2268 * Try the cache first.
2269 */
2270 pImage->pCacheEntry = supHardNtLdrCacheLookupEntry(pImage->pszName);
2271 if (pImage->pCacheEntry)
2272 continue;
2273
2274 /*
2275 * Not in the cache, so load it into the cache.
2276 */
2277 if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
2278 return supHardNtVpSetInfo2(pThis, VERR_INTERNAL_ERROR_3, "Loader cache overflow.");
2279 pImage->pCacheEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
2280#else
2281 /*
2282 * In ring-0 we don't have a cache at the moment (resource reasons), so
2283 * we have a static cache entry in each image structure that we use instead.
2284 */
2285 pImage->pCacheEntry = &pImage->CacheEntry;
2286#endif
2287
2288 int rc = supHardNtLdrCacheNewEntry(pImage->pCacheEntry, pImage->pszName, &pImage->Name.UniStr,
2289 pImage->fDll, pImage->f32bitResourceDll, pThis->pErrInfo);
2290 if (RT_FAILURE(rc))
2291 return rc;
2292#ifdef IN_RING3
2293 g_cSupNtVpLdrCacheEntries++;
2294#endif
2295 }
2296
2297 return VINF_SUCCESS;
2298}
2299
2300
2301/**
2302 * Check the integrity of the executable of the process.
2303 *
2304 * @returns VBox status code.
2305 * @param pThis The process scanning state structure. Details
2306 * about images are added to this. The hProcess
2307 * member holds the handle to the process that is
2308 * to be verified.
2309 */
2310static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis)
2311{
2312 /*
2313 * Make sure there is exactly one executable image.
2314 */
2315 unsigned cExecs = 0;
2316 unsigned iExe = ~0U;
2317 unsigned i = pThis->cImages;
2318 while (i-- > 0)
2319 {
2320 if (!pThis->aImages[i].fDll)
2321 {
2322 cExecs++;
2323 iExe = i;
2324 }
2325 }
2326 if (cExecs == 0)
2327 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
2328 "No executable mapping found in the virtual address space.");
2329 if (cExecs != 1)
2330 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
2331 "Found more than one executable mapping in the virtual address space.");
2332 PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
2333
2334 /*
2335 * Check that it matches the executable image of the process.
2336 */
2337 int rc;
2338 ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
2339 PUNICODE_STRING pUniStr = (PUNICODE_STRING)RTMemAllocZ(cbUniStr);
2340 if (!pUniStr)
2341 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
2342 "Error allocating %zu bytes for process name.", cbUniStr);
2343 ULONG cbIgn = 0;
2344 NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
2345 if (NT_SUCCESS(rcNt))
2346 {
2347 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
2348 if (supHardNtVpArePathsEqual(pUniStr, &pImage->Name.UniStr))
2349 rc = VINF_SUCCESS;
2350 else
2351 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
2352 "Process image name does not match the exectuable we found: %ls vs %ls.",
2353 pUniStr->Buffer, pImage->Name.UniStr.Buffer);
2354 }
2355 else
2356 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
2357 "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
2358 RTMemFree(pUniStr);
2359 if (RT_FAILURE(rc))
2360 return rc;
2361
2362 /*
2363 * Validate the signing of the executable image.
2364 * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
2365 */
2366 rc = supHardNtVpVerifyImage(pThis, pImage);
2367 if (RT_FAILURE(rc))
2368 return rc;
2369
2370 /*
2371 * Check linking requirements.
2372 * This query is only available using the current process pseudo handle on
2373 * older windows versions. The cut-off seems to be Vista.
2374 */
2375 SECTION_IMAGE_INFORMATION ImageInfo;
2376 rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
2377 if (!NT_SUCCESS(rcNt))
2378 {
2379 if ( rcNt == STATUS_INVALID_PARAMETER
2380 && g_uNtVerCombined < SUP_NT_VER_VISTA
2381 && pThis->hProcess != NtCurrentProcess() )
2382 return VINF_SUCCESS;
2383 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
2384 "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x",
2385 rcNt, pThis->hProcess);
2386 }
2387 if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
2388 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
2389 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
2390 ImageInfo.DllCharacteristics);
2391 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
2392 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
2393 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
2394 ImageInfo.DllCharacteristics);
2395 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
2396 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
2397 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
2398 ImageInfo.DllCharacteristics);
2399
2400 if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
2401 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2402 "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
2403 ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
2404
2405 if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
2406 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2407 "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
2408 ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
2409
2410 return VINF_SUCCESS;
2411}
2412
2413
2414/**
2415 * Check the integrity of the DLLs found in the process.
2416 *
2417 * @returns VBox status code.
2418 * @param pThis The process scanning state structure. Details
2419 * about images are added to this. The hProcess
2420 * member holds the handle to the process that is
2421 * to be verified.
2422 */
2423static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis)
2424{
2425 /*
2426 * Check for duplicate entries (paranoia).
2427 */
2428 uint32_t i = pThis->cImages;
2429 while (i-- > 1)
2430 {
2431 const char *pszName = pThis->aImages[i].pszName;
2432 uint32_t j = i;
2433 while (j-- > 0)
2434 if (pThis->aImages[j].pszName == pszName)
2435 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
2436 "Duplicate image entries for %s: %ls and %ls",
2437 pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
2438 }
2439
2440 /*
2441 * Check that both ntdll and kernel32 are present.
2442 * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
2443 */
2444 uint32_t iNtDll = UINT32_MAX;
2445 uint32_t iKernel32 = UINT32_MAX;
2446 i = pThis->cImages;
2447 while (i-- > 0)
2448 if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
2449 iNtDll = i;
2450 else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
2451 iKernel32 = i;
2452 if (iNtDll == UINT32_MAX)
2453 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
2454 "The process has no NTDLL.DLL.");
2455 if (iKernel32 == UINT32_MAX && ( pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
2456 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED))
2457 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
2458 "The process has no KERNEL32.DLL.");
2459 else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
2460 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
2461 "The process already has KERNEL32.DLL loaded.");
2462
2463 /*
2464 * Verify that the DLLs are correctly signed (by MS).
2465 */
2466 i = pThis->cImages;
2467 while (i-- > 0)
2468 {
2469 int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i]);
2470 if (RT_FAILURE(rc))
2471 return rc;
2472 }
2473
2474 return VINF_SUCCESS;
2475}
2476
2477
2478/**
2479 * Verifies the given process.
2480 *
2481 * The following requirements are checked:
2482 * - The process only has one thread, the calling thread.
2483 * - The process has no debugger attached.
2484 * - The executable image of the process is verified to be signed with
2485 * certificate known to this code at build time.
2486 * - The executable image is one of a predefined set.
2487 * - The process has only a very limited set of system DLLs loaded.
2488 * - The system DLLs signatures check out fine.
2489 * - The only executable memory in the process belongs to the system DLLs and
2490 * the executable image.
2491 *
2492 * @returns VBox status code.
2493 * @param hProcess The process to verify.
2494 * @param hThread A thread in the process (the caller).
2495 * @param enmKind The kind of process verification to perform.
2496 * @param fFlags Valid combination of SUPHARDNTVP_F_XXX flags.
2497 * @param pErrInfo Pointer to error info structure. Optional.
2498 * @param pcFixes Where to return the number of fixes made during
2499 * purification. Optional.
2500 */
2501DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, uint32_t fFlags,
2502 uint32_t *pcFixes, PRTERRINFO pErrInfo)
2503{
2504 if (pcFixes)
2505 *pcFixes = 0;
2506
2507 /*
2508 * Some basic checks regarding threads and debuggers. We don't need
2509 * allocate any state memory for these.
2510 */
2511 int rc = VINF_SUCCESS;
2512 if ( enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
2513 && enmKind != SUPHARDNTVPKIND_SELF_PURIFICATION_LIMITED)
2514 rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
2515 if (RT_SUCCESS(rc))
2516 rc = supHardNtVpDebugger(hProcess, pErrInfo);
2517 if (RT_SUCCESS(rc))
2518 {
2519 /*
2520 * Allocate and initialize memory for the state.
2521 */
2522 PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)RTMemAllocZ(sizeof(*pThis));
2523 if (pThis)
2524 {
2525 pThis->enmKind = enmKind;
2526 pThis->fFlags = fFlags;
2527 pThis->rcResult = VINF_SUCCESS;
2528 pThis->hProcess = hProcess;
2529 pThis->pErrInfo = pErrInfo;
2530
2531 /*
2532 * Perform the verification.
2533 */
2534 rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
2535 if (RT_SUCCESS(rc))
2536 rc = supHardNtVpOpenImages(pThis);
2537 if (RT_SUCCESS(rc))
2538 rc = supHardNtVpCheckExe(pThis);
2539 if (RT_SUCCESS(rc))
2540 rc = supHardNtVpCheckDlls(pThis);
2541
2542 if (pcFixes)
2543 *pcFixes = pThis->cFixes;
2544
2545 /*
2546 * Clean up the state.
2547 */
2548#ifdef IN_RING0
2549 for (uint32_t i = 0; i < pThis->cImages; i++)
2550 supHardNTLdrCacheDeleteEntry(&pThis->aImages[i].CacheEntry);
2551#endif
2552 RTMemFree(pThis);
2553 }
2554 else
2555 rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
2556 "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
2557 }
2558 return rc;
2559}
2560
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