VirtualBox

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

最後變更 在這個檔案從106893是 106893,由 vboxsync 提交於 4 月 前

SUPHardNt: Rough and untested port of the C code to win.arm64 so the extpack builds. VBP-1447

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette