VirtualBox

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

Last change on this file since 109027 was 109027, checked in by vboxsync, 2 weeks ago

SUPHardNt: VBoxCpuReport.exe on arm and hack for .fptable section being mapped RO (arm). jiraref:VBP-1598

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

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