VirtualBox

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

Last change on this file since 52795 was 52795, checked in by vboxsync, 10 years ago

SUP: Deal with comodo's ntdll export and getprocaddress modifications. Fixed bug in supHardNtLdrCacheOpen.

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