VirtualBox

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

Last change on this file since 106791 was 106267, checked in by vboxsync, 4 months ago

SUPHardNt: Try deal with extra pages attached to (system?) DLL mappings in Windows 11 24H2 and insider builds. ticketref:22162

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