VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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