VirtualBox

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

Last change on this file since 57358 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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

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