VirtualBox

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

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

SUPHardNt: The minimal hardening requires more than 16 regions per executable image when scanning the process opening the driver, because it turns out Qt6Gui.dll's .data section is split up quite a bit (fixups?). jiraref:VBP-1442

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

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