VirtualBox

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

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

SUP: simplified the ntdll hooking / patching so we can avoid the jump table memory as it may end up where system dlls like kernel32 are supposed to be loaded (STATUS_CONFLICTING_ADDRESSES (0xc0000018) process init failure).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.2 KB
Line 
1/* $Id: SUPHardenedVerifyProcess-win.cpp 52967 2014-10-06 22:18:51Z 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 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
813 rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
814 pThis->pErrInfo);
815 else
816 rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
817 pThis->pErrInfo);
818 if (RT_FAILURE(rc))
819 return rc;
820
821 /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
822 if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
823 {
824 if (fIs32Bit)
825 ((PIMAGE_NT_HEADERS32)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = (uint32_t)pImage->uImageBase;
826 else
827 ((PIMAGE_NT_HEADERS)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = pImage->uImageBase;
828 }
829
830 /*
831 * Figure out areas we should skip during comparison.
832 */
833 uint32_t cSkipAreas = 0;
834 SUPHNTVPSKIPAREA aSkipAreas[5];
835 if (pImage->fNtCreateSectionPatch)
836 {
837 RTLDRADDR uValue;
838 if (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
839 {
840 /* Ignore our NtCreateSection hack. */
841 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "NtCreateSection", &uValue);
842 if (RT_FAILURE(rc))
843 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
844 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
845 aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
846
847 /* Ignore our LdrLoadDll hack. */
848 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrLoadDll", &uValue);
849 if (RT_FAILURE(rc))
850 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrLoadDll': %Rrc", pImage->pszName, rc);
851 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
852 aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
853 }
854
855 if ( pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION
856 || pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
857 {
858 /* Ignore our patched LdrInitializeThunk hack. */
859 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
860 if (RT_FAILURE(rc))
861 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
862 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
863 aSkipAreas[cSkipAreas++].cb = 14;
864 }
865
866 /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
867 rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
868 if (RT_SUCCESS(rc))
869 {
870 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
871 aSkipAreas[cSkipAreas++].cb = RT_MAX(pbBits[(uint32_t)uValue], 0x50);
872 }
873
874 Assert(cSkipAreas <= RT_ELEMENTS(aSkipAreas));
875 }
876
877 /*
878 * Compare the file header with the loaded bits. The loader will fiddle
879 * with image base, changing it to the actual load address.
880 */
881 if (!pImage->fApiSetSchemaOnlySection1)
882 {
883 rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
884 if (RT_FAILURE(rc))
885 return rc;
886
887 rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
888 if (RT_FAILURE(rc))
889 return rc;
890 }
891
892 /*
893 * Validate sections:
894 * - Check them against the mapping regions.
895 * - Check section bits according to enmKind.
896 */
897 uint32_t fPrevProt = PAGE_READONLY;
898 uint32_t uRva = cbHdrsFile;
899 for (uint32_t i = 0; i < cSections; i++)
900 {
901 /* Validate the section. */
902 uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
903 if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
904 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
905 "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
906 pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
907 uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
908 if (cbMap > cbImage || uRva + cbMap > cbImage)
909 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
910 "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
911 pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
912 uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
913 if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
914 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
915 "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
916 pImage->pszName, i, cbFile, cbMap, uSectRva);
917
918 /* Validate the protection and bits. */
919 if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
920 {
921 uint32_t fProt;
922 switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
923 {
924 case IMAGE_SCN_MEM_READ:
925 fProt = PAGE_READONLY;
926 break;
927 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
928 fProt = PAGE_READWRITE;
929 if ( pThis->enmKind != SUPHARDNTVPKIND_VERIFY_ONLY
930 && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
931 && !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
932 fProt = PAGE_READONLY;
933 break;
934 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
935 fProt = PAGE_EXECUTE_READ;
936 break;
937 case IMAGE_SCN_MEM_EXECUTE:
938 fProt = PAGE_EXECUTE;
939 break;
940 case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
941 /* Only the executable is allowed to have this section,
942 and it's protected after we're done patching. */
943 if (!pImage->fDll)
944 {
945 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
946 fProt = PAGE_EXECUTE_READWRITE;
947 else
948 fProt = PAGE_EXECUTE_READ;
949 break;
950 }
951 default:
952 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
953 "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
954 pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
955 }
956
957 /* The section bits. Child purification verifies all, normal
958 verification verifies all except where the executable is
959 concerned (due to opening vboxdrv during early process init). */
960 if ( ( (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
961 && !(pThis->aSecHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
962 || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
963 || (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY && pImage->fDll)
964 || pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
965 {
966 rc = VINF_SUCCESS;
967 if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
968 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pbBits + uRva,
969 i - 1, NULL, 0, fPrevProt);
970 if (RT_SUCCESS(rc))
971 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pbBits + uSectRva,
972 i, aSkipAreas, cSkipAreas, fProt);
973 if (RT_SUCCESS(rc))
974 {
975 uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
976 ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
977 if (cbMapAligned > cbMap)
978 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
979 g_abRTZeroPage, i, NULL, 0, fProt);
980 }
981 if (RT_FAILURE(rc))
982 return rc;
983 }
984
985 /* The protection (must be checked afterwards!). */
986 rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
987 if (RT_FAILURE(rc))
988 return rc;
989
990 fPrevProt = fProt;
991 }
992
993 /* Advance the RVA. */
994 uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
995 }
996
997 return VINF_SUCCESS;
998}
999
1000
1001/**
1002 * Verifies the signature of the given image on disk, then checks if the memory
1003 * mapping matches what we verified.
1004 *
1005 * @returns VBox status code.
1006 * @param pThis The process scanning state structure (for the
1007 * two scratch buffers).
1008 * @param pImage The image data collected during the address
1009 * space scan.
1010 * @param hProcess Handle to the process.
1011 * @param hFile Handle to the image file.
1012 */
1013static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess)
1014{
1015 /*
1016 * Validate the file signature first, then do the memory compare.
1017 */
1018 int rc;
1019 if ( pImage->pCacheEntry != NULL
1020 && pImage->pCacheEntry->hLdrMod != NIL_RTLDRMOD)
1021 {
1022 rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
1023 if (RT_SUCCESS(rc))
1024 rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage, hProcess, pThis->pErrInfo);
1025 }
1026 else
1027 rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "pCacheEntry/hLdrMod is NIL! Impossible!");
1028 return rc;
1029}
1030
1031
1032/**
1033 * Verifies that there is only one thread in the process.
1034 *
1035 * @returns VBox status code.
1036 * @param hProcess The process.
1037 * @param hThread The thread.
1038 * @param pErrInfo Pointer to error info structure. Optional.
1039 */
1040DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
1041{
1042 /*
1043 * Use the ThreadAmILastThread request to check that there is only one
1044 * thread in the process.
1045 * Seems this isn't entirely reliable when hThread isn't the current thread?
1046 */
1047 ULONG cbIgn = 0;
1048 ULONG fAmI = 0;
1049 NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
1050 if (!NT_SUCCESS(rcNt))
1051 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
1052 "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
1053 if (!fAmI)
1054 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
1055 "More than one thread in process");
1056
1057 /** @todo Would be nice to verify the relation ship between hProcess and hThread
1058 * as well... */
1059 return VINF_SUCCESS;
1060}
1061
1062
1063/**
1064 * Verifies that there isn't a debugger attached to the process.
1065 *
1066 * @returns VBox status code.
1067 * @param hProcess The process.
1068 * @param pErrInfo Pointer to error info structure. Optional.
1069 */
1070DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
1071{
1072#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
1073 /*
1074 * Use the ProcessDebugPort request to check there is no debugger
1075 * currently attached to the process.
1076 */
1077 ULONG cbIgn = 0;
1078 uintptr_t uPtr = ~(uintptr_t)0;
1079 NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
1080 ProcessDebugPort,
1081 &uPtr, sizeof(uPtr), &cbIgn);
1082 if (!NT_SUCCESS(rcNt))
1083 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
1084 "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
1085 if (uPtr != 0)
1086 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
1087 "Debugger attached (%#zx)", uPtr);
1088#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
1089 return VINF_SUCCESS;
1090}
1091
1092
1093/**
1094 * Matches two UNICODE_STRING structures in a case sensitive fashion.
1095 *
1096 * @returns true if equal, false if not.
1097 * @param pUniStr1 The first unicode string.
1098 * @param pUniStr2 The first unicode string.
1099 */
1100static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
1101{
1102 if (pUniStr1->Length != pUniStr2->Length)
1103 return false;
1104 return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
1105}
1106
1107
1108/**
1109 * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
1110 *
1111 * @returns true / false
1112 * @param pszName1 The ASCII name.
1113 * @param pwszName2 The UTF-16 name.
1114 */
1115static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
1116{
1117 for (;;)
1118 {
1119 char ch1 = *pszName1++;
1120 RTUTF16 wc2 = *pwszName2++;
1121 if (ch1 != wc2)
1122 {
1123 ch1 = RT_C_TO_LOWER(ch1);
1124 wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
1125 if (ch1 != wc2)
1126 return false;
1127 }
1128 if (!ch1)
1129 return true;
1130 }
1131}
1132
1133
1134/**
1135 * Records an additional memory region for an image.
1136 *
1137 * @returns VBox status code.
1138 * @retval VINF_OBJECT_DESTROYED if we've unmapped the image (child
1139 * purification only).
1140 * @param pThis The process scanning state structure.
1141 * @param pImage The new image structure. Only the unicode name
1142 * buffer is valid.
1143 * @param pMemInfo The memory information for the image.
1144 */
1145static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1146{
1147 /*
1148 * Extract the final component.
1149 */
1150 unsigned cwcDirName = pImage->Name.UniStr.Length / sizeof(WCHAR);
1151 PCRTUTF16 pwszFilename = &pImage->Name.UniStr.Buffer[cwcDirName];
1152 while ( cwcDirName > 0
1153 && pwszFilename[-1] != '\\'
1154 && pwszFilename[-1] != '/'
1155 && pwszFilename[-1] != ':')
1156 {
1157 pwszFilename--;
1158 cwcDirName--;
1159 }
1160 if (!*pwszFilename)
1161 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
1162 "Empty filename (len=%u) for image at %p.", pImage->Name.UniStr.Length, pMemInfo->BaseAddress);
1163
1164 /*
1165 * Drop trailing slashes from the directory name.
1166 */
1167 while ( cwcDirName > 0
1168 && ( pImage->Name.UniStr.Buffer[cwcDirName - 1] == '\\'
1169 || pImage->Name.UniStr.Buffer[cwcDirName - 1] == '/'))
1170 cwcDirName--;
1171
1172 /*
1173 * Match it against known DLLs.
1174 */
1175 pImage->pszName = NULL;
1176 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
1177 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
1178 {
1179 pImage->pszName = g_apszSupNtVpAllowedDlls[i];
1180 pImage->fDll = true;
1181
1182#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1183 /* The directory name must match the one we've got for System32. */
1184 if ( ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
1185 || suplibHardenedMemComp(pImage->Name.UniStr.Buffer,
1186 g_System32NtPath.UniStr.Buffer,
1187 cwcDirName * sizeof(WCHAR)) )
1188# ifdef VBOX_PERMIT_MORE
1189 && ( pImage->pszName[0] != 'a'
1190 || pImage->pszName[1] != 'c'
1191 || !supHardViIsAppPatchDir(pImage->Name.UniStr.Buffer, pImage->Name.UniStr.Length / sizeof(WCHAR)) )
1192# endif
1193 )
1194 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
1195 "Expected %ls to be loaded from %ls.",
1196 pImage->Name.UniStr.Buffer, g_System32NtPath.UniStr.Buffer);
1197# ifdef VBOX_PERMIT_MORE
1198 if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
1199 pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
1200# endif
1201
1202#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1203 break;
1204 }
1205 if (!pImage->pszName)
1206 {
1207 /*
1208 * Not a known DLL, is it a known executable?
1209 */
1210 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
1211 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
1212 {
1213 pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
1214 pImage->fDll = false;
1215 break;
1216 }
1217 }
1218 if (!pImage->pszName)
1219 {
1220 /*
1221 * Unknown image.
1222 *
1223 * If we're cleaning up a child process, we can unmap the offending
1224 * DLL... Might have interesting side effects, or at least interesting
1225 * as in "may you live in interesting times".
1226 */
1227#ifdef IN_RING3
1228 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
1229 && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1230 {
1231 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping image mem at %p (%p LB %#zx) - '%ls'\n",
1232 pMemInfo->AllocationBase, pMemInfo->BaseAddress, pMemInfo->RegionSize));
1233 NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pMemInfo->AllocationBase);
1234 if (NT_SUCCESS(rcNt))
1235 return VINF_OBJECT_DESTROYED;
1236 pThis->cFixes++;
1237 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
1238 }
1239#endif
1240 /*
1241 * Special error message if we can.
1242 */
1243 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
1244 && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
1245 || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
1246 || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)
1247 || supHardNtVpAreNamesEqual("sysfrethunk.dll", pwszFilename)) )
1248 {
1249 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
1250 "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
1251 "You or your admin need to add and exception to the Application and Device Control (ADC) "
1252 "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
1253 "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
1254 , pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
1255 return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
1256 }
1257 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
1258 "Unknown image file %ls at %p.", pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
1259 }
1260
1261 /*
1262 * Checks for multiple mappings of the same DLL but with different image file paths.
1263 */
1264 uint32_t i = pThis->cImages;
1265 while (i-- > 1)
1266 if (pImage->pszName == pThis->aImages[i].pszName)
1267 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1268 "Duplicate image entries for %s: %ls and %ls",
1269 pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
1270
1271 /*
1272 * Since it's a new image, we expect to be at the start of the mapping now.
1273 */
1274 if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
1275 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
1276 "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
1277 pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
1278
1279 /*
1280 * Check for size/rva overflow.
1281 */
1282 if (pMemInfo->RegionSize >= _2G)
1283 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1284 "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
1285
1286 /*
1287 * Fill in details from the memory info.
1288 */
1289 pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
1290 pImage->cbImage = pMemInfo->RegionSize;
1291 pImage->pCacheEntry= NULL;
1292 pImage->cRegions = 1;
1293 pImage->aRegions[0].uRva = 0;
1294 pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
1295 pImage->aRegions[0].fProt = pMemInfo->Protect;
1296
1297 if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
1298 pImage->fNtCreateSectionPatch = true;
1299 else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
1300 pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
1301#ifdef VBOX_PERMIT_MORE
1302 else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
1303 pImage->f32bitResourceDll = true;
1304#endif
1305
1306 return VINF_SUCCESS;
1307}
1308
1309
1310/**
1311 * Records an additional memory region for an image.
1312 *
1313 * @returns VBox status code.
1314 * @param pThis The process scanning state structure.
1315 * @param pImage The image.
1316 * @param pMemInfo The memory information for the region.
1317 */
1318static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1319{
1320 /*
1321 * Make sure the base address matches.
1322 */
1323 if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
1324 return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
1325 "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
1326 pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
1327 pMemInfo->BaseAddress, pMemInfo->RegionSize);
1328
1329 /*
1330 * Check for size and rva overflows.
1331 */
1332 uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
1333 if (pMemInfo->RegionSize >= _2G)
1334 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1335 "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1336 if (uRva >= _2G)
1337 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
1338 "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1339
1340
1341 /*
1342 * Record the region.
1343 */
1344 uint32_t iRegion = pImage->cRegions;
1345 if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
1346 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
1347 "Too many regions for %s.", pImage->pszName);
1348 pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
1349 pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
1350 pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
1351 pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
1352 pImage->cRegions++;
1353 pImage->fApiSetSchemaOnlySection1 = false;
1354
1355 return VINF_SUCCESS;
1356}
1357
1358
1359/**
1360 * Scans the virtual memory of the process.
1361 *
1362 * This collects the locations of DLLs and the EXE, and verifies that executable
1363 * memory is only associated with these.
1364 *
1365 * @returns VBox status code.
1366 * @param pThis The process scanning state structure. Details
1367 * about images are added to this.
1368 * @param hProcess The process to verify.
1369 */
1370static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1371{
1372 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
1373 pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
1374 pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
1375
1376 uint32_t cXpExceptions = 0;
1377 uintptr_t cbAdvance = 0;
1378 uintptr_t uPtrWhere = 0;
1379#ifdef VBOX_PERMIT_VERIFIER_DLL
1380 for (uint32_t i = 0; i < 10240; i++)
1381#else
1382 for (uint32_t i = 0; i < 1024; i++)
1383#endif
1384 {
1385 SIZE_T cbActual = 0;
1386 MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
1387 NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1388 (void const *)uPtrWhere,
1389 MemoryBasicInformation,
1390 &MemInfo,
1391 sizeof(MemInfo),
1392 &cbActual);
1393 if (!NT_SUCCESS(rcNt))
1394 {
1395 if (rcNt == STATUS_INVALID_PARAMETER)
1396 return pThis->rcResult;
1397 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
1398 "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
1399 }
1400
1401 /*
1402 * Record images.
1403 */
1404 if ( MemInfo.Type == SEC_IMAGE
1405 || MemInfo.Type == SEC_PROTECTED_IMAGE
1406 || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
1407 {
1408 uint32_t iImg = pThis->cImages;
1409 rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1410 (void const *)uPtrWhere,
1411 MemorySectionName,
1412 &pThis->aImages[iImg].Name,
1413 sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
1414 &cbActual);
1415 if (!NT_SUCCESS(rcNt))
1416 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
1417 "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
1418 pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
1419 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1420 ? " *%p-%p %#06x/%#06x %#09x %ls\n"
1421 : " %p-%p %#06x/%#06x %#09x %ls\n",
1422 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1, MemInfo.Protect,
1423 MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
1424
1425 /* New or existing image? */
1426 bool fNew = true;
1427 uint32_t iSearch = iImg;
1428 while (iSearch-- > 0)
1429 if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
1430 {
1431 int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
1432 if (RT_FAILURE(rc))
1433 return rc;
1434 fNew = false;
1435 break;
1436 }
1437 else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
1438 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
1439 "Unexpected base address match");
1440
1441 if (fNew)
1442 {
1443 int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
1444 if (RT_SUCCESS(rc))
1445 {
1446 if (rc != VINF_OBJECT_DESTROYED)
1447 {
1448 pThis->cImages++;
1449 if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
1450 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
1451 "Internal error: aImages is full.\n");
1452 }
1453 }
1454#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
1455 else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
1456 return rc;
1457#else
1458 else
1459 return rc;
1460#endif
1461 }
1462 }
1463 /*
1464 * XP, W2K3: Ignore the CSRSS read-only region as best we can.
1465 */
1466 else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1467 == PAGE_EXECUTE_READ
1468 && cXpExceptions == 0
1469 && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
1470 /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
1471 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
1472 {
1473 cXpExceptions++;
1474 SUP_DPRINTF((" %p-%p %#06x/%#06x %#09x XP CSRSS read-only region\n", MemInfo.BaseAddress,
1475 (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1, MemInfo.Protect,
1476 MemInfo.AllocationProtect, MemInfo.Type));
1477 }
1478 /*
1479 * Executable memory?
1480 */
1481#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1482 else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1483 {
1484 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1485 ? " *%p-%p %#06x/%#06x %#09x !!\n"
1486 : " %p-%p %#06x/%#06x %#09x !!\n",
1487 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1,
1488 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1489# ifdef IN_RING3
1490 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1491 {
1492 /*
1493 * Free any private executable memory (sysplant.sys allocates executable memory).
1494 */
1495 if (MemInfo.Type == MEM_PRIVATE)
1496 {
1497 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Freeing exec mem at %p (%p LB %#zx)\n",
1498 uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize));
1499 PVOID pvFree = MemInfo.BaseAddress;
1500 SIZE_T cbFree = MemInfo.RegionSize;
1501 rcNt = NtFreeVirtualMemory(pThis->hProcess, &pvFree, &cbFree, MEM_RELEASE);
1502 if (!NT_SUCCESS(rcNt))
1503 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1504 "NtFreeVirtualMemory (%p LB %#zx) failed: %#x",
1505 MemInfo.BaseAddress, MemInfo.RegionSize, rcNt);
1506 }
1507 /*
1508 * Unmap mapped memory, failing that, drop exec privileges.
1509 */
1510 else if (MemInfo.Type == MEM_MAPPED)
1511 {
1512 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
1513 uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
1514 rcNt = NtUnmapViewOfSection(pThis->hProcess, MemInfo.AllocationBase);
1515 if (!NT_SUCCESS(rcNt))
1516 {
1517 PVOID pvCopy = MemInfo.BaseAddress;
1518 SIZE_T cbCopy = MemInfo.RegionSize;
1519 NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
1520 if (!NT_SUCCESS(rcNt2))
1521 rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
1522 if (!NT_SUCCESS(rcNt2))
1523 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
1524 "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
1525 MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
1526 }
1527 }
1528 else
1529 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
1530 "Unknown executable memory type %#x at %p/%p LB %#zx",
1531 MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
1532 pThis->cFixes++;
1533 }
1534 else
1535# endif /* IN_RING3 */
1536 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
1537 "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
1538 uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
1539 MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
1540
1541# ifndef IN_RING3
1542 if (RT_FAILURE(pThis->rcResult))
1543 return pThis->rcResult;
1544# endif
1545 /* Continue add more information about the problematic process. */
1546 }
1547#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1548 else
1549 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1550 ? " *%p-%p %#06x/%#06x %#09x\n"
1551 : " %p-%p %#06x/%#06x %#09x\n",
1552 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1,
1553 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1554
1555 /*
1556 * Advance.
1557 */
1558 cbAdvance = MemInfo.RegionSize;
1559 if (uPtrWhere + cbAdvance <= uPtrWhere)
1560 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
1561 "Empty region at %p.", uPtrWhere);
1562 uPtrWhere += MemInfo.RegionSize;
1563 }
1564
1565 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
1566 "Too many virtual memory regions.\n");
1567}
1568
1569
1570/**
1571 * Verifies the loader image, i.e. check cryptographic signatures if present.
1572 *
1573 * @returns VBox status code.
1574 * @param pEntry The loader cache entry.
1575 * @param pwszName The filename to use in error messages.
1576 * @param pErRInfo Where to return extened error information.
1577 */
1578DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
1579{
1580 int rc = VINF_SUCCESS;
1581 if (!pEntry->fVerified)
1582 {
1583 rc = supHardenedWinVerifyImageByLdrMod(pEntry->hLdrMod, pwszName, pEntry->pNtViRdr,
1584 false /*fAvoidWinVerifyTrust*/, NULL /*pfWinVerifyTrust*/, pErrInfo);
1585 pEntry->fVerified = RT_SUCCESS(rc);
1586 }
1587 return rc;
1588}
1589
1590
1591/**
1592 * Allocates a image bits buffer and calls RTLdrGetBits on them.
1593 *
1594 * An assumption here is that there won't ever be concurrent use of the cache.
1595 * It's currently 104% single threaded, non-reentrant. Thus, we can't reuse the
1596 * pbBits allocation.
1597 *
1598 * @returns VBox status code
1599 * @param pEntry The loader cache entry.
1600 * @param ppbBits Where to return the pointer to the allocation.
1601 * @param uBaseAddress The image base address, see RTLdrGetBits.
1602 * @param pfnGetImport Import getter, see RTLdrGetBits.
1603 * @param pvUser The user argument for @a pfnGetImport.
1604 * @param pErrInfo Where to return extened error information.
1605 */
1606DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
1607 RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
1608 PRTERRINFO pErrInfo)
1609{
1610 int rc;
1611
1612 /*
1613 * First time around we have to allocate memory before we can get the image bits.
1614 */
1615 if (!pEntry->pbBits)
1616 {
1617 size_t cbBits = RTLdrSize(pEntry->hLdrMod);
1618 if (cbBits >= _1M*32U)
1619 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
1620 pEntry->pszName, cbBits, cbBits);
1621
1622 pEntry->pbBits = (uint8_t *)RTMemAllocZ(cbBits);
1623 if (!pEntry->pbBits)
1624 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
1625 cbBits, pEntry->pszName);
1626
1627 pEntry->fValidBits = false; /* paranoia */
1628
1629 rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
1630 if (RT_FAILURE(rc))
1631 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
1632 pEntry->pszName, rc);
1633 pEntry->uImageBase = uBaseAddress;
1634 pEntry->fValidBits = pfnGetImport == NULL;
1635
1636 }
1637 /*
1638 * Cache hit? No?
1639 *
1640 * Note! We cannot currently cache image bits for images with imports as we
1641 * don't control the way they're resolved. Fortunately, NTDLL and
1642 * the VM process images all have no imports.
1643 */
1644 else if ( !pEntry->fValidBits
1645 || pEntry->uImageBase != uBaseAddress
1646 || pfnGetImport)
1647 {
1648 pEntry->fValidBits = false;
1649
1650 rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
1651 if (RT_FAILURE(rc))
1652 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
1653 pEntry->pszName, rc);
1654 pEntry->uImageBase = uBaseAddress;
1655 pEntry->fValidBits = pfnGetImport == NULL;
1656 }
1657
1658 *ppbBits = pEntry->pbBits;
1659 return VINF_SUCCESS;
1660}
1661
1662
1663/**
1664 * Frees all resources associated with a cache entry and wipes the members
1665 * clean.
1666 *
1667 * @param pEntry The entry to delete.
1668 */
1669static void supHardNTLdrCacheDeleteEntry(PSUPHNTLDRCACHEENTRY pEntry)
1670{
1671 if (pEntry->pbBits)
1672 {
1673 RTMemFree(pEntry->pbBits);
1674 pEntry->pbBits = NULL;
1675 }
1676
1677 if (pEntry->hLdrMod != NIL_RTLDRMOD)
1678 {
1679 RTLdrClose(pEntry->hLdrMod);
1680 pEntry->hLdrMod = NIL_RTLDRMOD;
1681 pEntry->pNtViRdr = NULL;
1682 }
1683 else if (pEntry->pNtViRdr)
1684 {
1685 pEntry->pNtViRdr->Core.pfnDestroy(&pEntry->pNtViRdr->Core);
1686 pEntry->pNtViRdr = NULL;
1687 }
1688
1689 if (pEntry->hFile)
1690 {
1691 NtClose(pEntry->hFile);
1692 pEntry->hFile = NULL;
1693 }
1694
1695 pEntry->pszName = NULL;
1696 pEntry->fVerified = false;
1697 pEntry->fValidBits = false;
1698 pEntry->uImageBase = 0;
1699}
1700
1701#ifdef IN_RING3
1702
1703/**
1704 * Flushes the cache.
1705 *
1706 * This is called from one of two points in the hardened main code, first is
1707 * after respawning and the second is when we open the vboxdrv device for
1708 * unrestricted access.
1709 */
1710DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache(void)
1711{
1712 uint32_t i = g_cSupNtVpLdrCacheEntries;
1713 while (i-- > 0)
1714 supHardNTLdrCacheDeleteEntry(&g_aSupNtVpLdrCacheEntries[i]);
1715 g_cSupNtVpLdrCacheEntries = 0;
1716}
1717
1718
1719/**
1720 * Searches the cache for a loader image.
1721 *
1722 * @returns Pointer to the cache entry if found, NULL if not.
1723 * @param pszName The name (from g_apszSupNtVpAllowedVmExes or
1724 * g_apszSupNtVpAllowedDlls).
1725 */
1726static PSUPHNTLDRCACHEENTRY supHardNtLdrCacheLookupEntry(const char *pszName)
1727{
1728 /*
1729 * Since the caller is supplying us a pszName from one of the two tables,
1730 * we can dispense with string compare and simply compare string pointers.
1731 */
1732 uint32_t i = g_cSupNtVpLdrCacheEntries;
1733 while (i-- > 0)
1734 if (g_aSupNtVpLdrCacheEntries[i].pszName == pszName)
1735 return &g_aSupNtVpLdrCacheEntries[i];
1736 return NULL;
1737}
1738
1739#endif /* IN_RING3 */
1740
1741static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
1742 bool fDll, bool f32bitResourceDll, PRTERRINFO pErrInfo)
1743{
1744 /*
1745 * Open the image file.
1746 */
1747 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1748 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1749
1750 OBJECT_ATTRIBUTES ObjAttr;
1751 InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1752#ifdef IN_RING0
1753 ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
1754#endif
1755
1756 NTSTATUS rcNt = NtCreateFile(&hFile,
1757 GENERIC_READ,
1758 &ObjAttr,
1759 &Ios,
1760 NULL /* Allocation Size*/,
1761 FILE_ATTRIBUTE_NORMAL,
1762 FILE_SHARE_READ,
1763 FILE_OPEN,
1764 FILE_NON_DIRECTORY_FILE, /** @todo nonalert? */
1765 NULL /*EaBuffer*/,
1766 0 /*EaLength*/);
1767 if (NT_SUCCESS(rcNt))
1768 rcNt = Ios.Status;
1769 if (!NT_SUCCESS(rcNt))
1770 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
1771 "Error opening image for scanning: %#x (name %ls)", rcNt, pUniStrPath->Buffer);
1772
1773 /*
1774 * Figure out validation flags we'll be using and create the reader
1775 * for this image.
1776 */
1777 uint32_t fFlags = fDll
1778 ? SUPHNTVI_F_TRUSTED_INSTALLER_OWNER | SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION
1779 : SUPHNTVI_F_REQUIRE_BUILD_CERT;
1780 if (f32bitResourceDll)
1781 fFlags |= SUPHNTVI_F_RESOURCE_IMAGE;
1782
1783 PSUPHNTVIRDR pNtViRdr;
1784 int rc = supHardNtViRdrCreate(hFile, pUniStrPath->Buffer, fFlags, &pNtViRdr);
1785 if (RT_FAILURE(rc))
1786 {
1787 NtClose(hFile);
1788 return rc;
1789 }
1790
1791 /*
1792 * Finally, open the image with the loader
1793 */
1794 RTLDRMOD hLdrMod;
1795 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
1796 if (fFlags & SUPHNTVI_F_RESOURCE_IMAGE)
1797 enmArch = RTLDRARCH_WHATEVER;
1798 rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
1799 if (RT_FAILURE(rc))
1800 return supHardNtVpSetInfo1(pErrInfo, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
1801 rc, pUniStrPath->Buffer);
1802
1803 /*
1804 * Fill in the cache entry.
1805 */
1806 pEntry->pszName = pszName;
1807 pEntry->hLdrMod = hLdrMod;
1808 pEntry->pNtViRdr = pNtViRdr;
1809 pEntry->hFile = hFile;
1810 pEntry->pbBits = NULL;
1811 pEntry->fVerified = false;
1812 pEntry->fValidBits = false;
1813 pEntry->uImageBase = ~(uintptr_t)0;
1814
1815#ifdef IN_SUP_HARDENED_R3
1816 /*
1817 * Log the image timestamp when in the hardened exe.
1818 */
1819 uint64_t uTimestamp = 0;
1820 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uint64_t));
1821 SUP_DPRINTF(("%s: timestamp %#llx (rc=%Rrc)\n", pszName, uTimestamp, rc));
1822#endif
1823
1824 return VINF_SUCCESS;
1825}
1826
1827#ifdef IN_RING3
1828/**
1829 * Opens a loader cache entry.
1830 *
1831 * Currently this is only used by the import code for getting NTDLL.
1832 *
1833 * @returns VBox status code.
1834 * @param pszName The DLL name. Must be one from the
1835 * g_apszSupNtVpAllowedDlls array.
1836 * @param ppEntry Where to return the entry we've opened/found.
1837 */
1838DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry)
1839{
1840 /*
1841 * Locate the dll.
1842 */
1843 uint32_t i = 0;
1844 while ( i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)
1845 && strcmp(pszName, g_apszSupNtVpAllowedDlls[i]))
1846 i++;
1847 if (i >= RT_ELEMENTS(g_apszSupNtVpAllowedDlls))
1848 return VERR_FILE_NOT_FOUND;
1849 pszName = g_apszSupNtVpAllowedDlls[i];
1850
1851 /*
1852 * Try the cache.
1853 */
1854 *ppEntry = supHardNtLdrCacheLookupEntry(pszName);
1855 if (*ppEntry)
1856 return VINF_SUCCESS;
1857
1858 /*
1859 * Not in the cache, so open it.
1860 * Note! We cannot assume that g_System32NtPath has been initialized at this point.
1861 */
1862 if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
1863 return VERR_INTERNAL_ERROR_3;
1864
1865 static WCHAR s_wszSystem32[] = L"\\SystemRoot\\System32\\";
1866 WCHAR wszPath[64];
1867 memcpy(wszPath, s_wszSystem32, sizeof(s_wszSystem32));
1868 RTUtf16CatAscii(wszPath, sizeof(wszPath), pszName);
1869
1870 UNICODE_STRING UniStr;
1871 UniStr.Buffer = wszPath;
1872 UniStr.Length = (USHORT)(RTUtf16Len(wszPath) * sizeof(WCHAR));
1873 UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
1874
1875 int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
1876 true /*fDll*/, false /*f32bitResourceDll*/, NULL /*pErrInfo*/);
1877 if (RT_SUCCESS(rc))
1878 {
1879 *ppEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
1880 g_cSupNtVpLdrCacheEntries++;
1881 return VINF_SUCCESS;
1882 }
1883 return rc;
1884}
1885#endif /* IN_RING3 */
1886
1887
1888/**
1889 * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
1890 * hLdrMod for each image.
1891 *
1892 * @returns VBox status code.
1893 * @param pThis The process scanning state structure.
1894 */
1895static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
1896{
1897 unsigned i = pThis->cImages;
1898 while (i-- > 0)
1899 {
1900 PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
1901
1902#ifdef IN_RING3
1903 /*
1904 * Try the cache first.
1905 */
1906 pImage->pCacheEntry = supHardNtLdrCacheLookupEntry(pImage->pszName);
1907 if (pImage->pCacheEntry)
1908 continue;
1909
1910 /*
1911 * Not in the cache, so load it into the cache.
1912 */
1913 if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
1914 return supHardNtVpSetInfo2(pThis, VERR_INTERNAL_ERROR_3, "Loader cache overflow.");
1915 pImage->pCacheEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
1916#else
1917 /*
1918 * In ring-0 we don't have a cache at the moment (resource reasons), so
1919 * we have a static cache entry in each image structure that we use instead.
1920 */
1921 pImage->pCacheEntry = &pImage->CacheEntry;
1922#endif
1923
1924 int rc = supHardNtLdrCacheNewEntry(pImage->pCacheEntry, pImage->pszName, &pImage->Name.UniStr,
1925 pImage->fDll, pImage->f32bitResourceDll, pThis->pErrInfo);
1926 if (RT_FAILURE(rc))
1927 return rc;
1928#ifdef IN_RING3
1929 g_cSupNtVpLdrCacheEntries++;
1930#endif
1931 }
1932
1933 return VINF_SUCCESS;
1934}
1935
1936
1937/**
1938 * Check the integrity of the executable of the process.
1939 *
1940 * @returns VBox status code.
1941 * @param pThis The process scanning state structure. Details
1942 * about images are added to this.
1943 * @param hProcess The process to verify.
1944 */
1945static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1946{
1947 /*
1948 * Make sure there is exactly one executable image.
1949 */
1950 unsigned cExecs = 0;
1951 unsigned iExe = ~0U;
1952 unsigned i = pThis->cImages;
1953 while (i-- > 0)
1954 {
1955 if (!pThis->aImages[i].fDll)
1956 {
1957 cExecs++;
1958 iExe = i;
1959 }
1960 }
1961 if (cExecs == 0)
1962 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
1963 "No executable mapping found in the virtual address space.");
1964 if (cExecs != 1)
1965 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
1966 "Found more than one executable mapping in the virtual address space.");
1967 PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
1968
1969 /*
1970 * Check that it matches the executable image of the process.
1971 */
1972 int rc;
1973 ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
1974 PUNICODE_STRING pUniStr = (PUNICODE_STRING)RTMemAllocZ(cbUniStr);
1975 if (!pUniStr)
1976 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
1977 "Error allocating %zu bytes for process name.", cbUniStr);
1978 ULONG cbIgn = 0;
1979 NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
1980 if (NT_SUCCESS(rcNt))
1981 {
1982 if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
1983 rc = VINF_SUCCESS;
1984 else
1985 {
1986 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1987 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
1988 "Process image name does not match the exectuable we found: %ls vs %ls.",
1989 pUniStr->Buffer, pImage->Name.UniStr.Buffer);
1990 }
1991 }
1992 else
1993 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
1994 "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
1995 RTMemFree(pUniStr);
1996 if (RT_FAILURE(rc))
1997 return rc;
1998
1999 /*
2000 * Validate the signing of the executable image.
2001 * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
2002 */
2003 rc = supHardNtVpVerifyImage(pThis, pImage, hProcess);
2004 if (RT_FAILURE(rc))
2005 return rc;
2006
2007 /*
2008 * Check linking requirements.
2009 * This query is only available using the current process pseudo handle on
2010 * older windows versions. The cut-off seems to be Vista.
2011 */
2012 SECTION_IMAGE_INFORMATION ImageInfo;
2013 rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
2014 if (!NT_SUCCESS(rcNt))
2015 {
2016 if ( rcNt == STATUS_INVALID_PARAMETER
2017 && g_uNtVerCombined < SUP_NT_VER_VISTA
2018 && hProcess != NtCurrentProcess() )
2019 return VINF_SUCCESS;
2020 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
2021 "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x", rcNt, hProcess);
2022 }
2023 if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
2024 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
2025 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
2026 ImageInfo.DllCharacteristics);
2027 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
2028 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
2029 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
2030 ImageInfo.DllCharacteristics);
2031 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
2032 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
2033 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
2034 ImageInfo.DllCharacteristics);
2035
2036 if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
2037 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2038 "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
2039 ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
2040
2041 if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
2042 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2043 "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
2044 ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
2045
2046 return VINF_SUCCESS;
2047}
2048
2049
2050/**
2051 * Check the integrity of the DLLs found in the process.
2052 *
2053 * @returns VBox status code.
2054 * @param pThis The process scanning state structure. Details
2055 * about images are added to this.
2056 * @param hProcess The process to verify.
2057 */
2058static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis, HANDLE hProcess)
2059{
2060 /*
2061 * Check for duplicate entries (paranoia).
2062 */
2063 uint32_t i = pThis->cImages;
2064 while (i-- > 1)
2065 {
2066 const char *pszName = pThis->aImages[i].pszName;
2067 uint32_t j = i;
2068 while (j-- > 0)
2069 if (pThis->aImages[j].pszName == pszName)
2070 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
2071 "Duplicate image entries for %s: %ls and %ls",
2072 pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
2073 }
2074
2075 /*
2076 * Check that both ntdll and kernel32 are present.
2077 * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
2078 */
2079 uint32_t iNtDll = UINT32_MAX;
2080 uint32_t iKernel32 = UINT32_MAX;
2081 i = pThis->cImages;
2082 while (i-- > 0)
2083 if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
2084 iNtDll = i;
2085 else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
2086 iKernel32 = i;
2087 if (iNtDll == UINT32_MAX)
2088 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
2089 "The process has no NTDLL.DLL.");
2090 if (iKernel32 == UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
2091 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
2092 "The process has no KERNEL32.DLL.");
2093 else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
2094 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
2095 "The process already has KERNEL32.DLL loaded.");
2096
2097 /*
2098 * Verify that the DLLs are correctly signed (by MS).
2099 */
2100 i = pThis->cImages;
2101 while (i-- > 0)
2102 {
2103 int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i], hProcess);
2104 if (RT_FAILURE(rc))
2105 return rc;
2106 }
2107
2108 return VINF_SUCCESS;
2109}
2110
2111
2112/**
2113 * Verifies the given process.
2114 *
2115 * The following requirements are checked:
2116 * - The process only has one thread, the calling thread.
2117 * - The process has no debugger attached.
2118 * - The executable image of the process is verified to be signed with
2119 * certificate known to this code at build time.
2120 * - The executable image is one of a predefined set.
2121 * - The process has only a very limited set of system DLLs loaded.
2122 * - The system DLLs signatures check out fine.
2123 * - The only executable memory in the process belongs to the system DLLs and
2124 * the executable image.
2125 *
2126 * @returns VBox status code.
2127 * @param hProcess The process to verify.
2128 * @param hThread A thread in the process (the caller).
2129 * @param enmKind The kind of process verification to perform.
2130 * @param pErrInfo Pointer to error info structure. Optional.
2131 * @param pcFixes Where to return the number of fixes made during
2132 * purification. Optional.
2133 */
2134DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind,
2135 uint32_t *pcFixes, PRTERRINFO pErrInfo)
2136{
2137 if (pcFixes)
2138 *pcFixes = 0;
2139
2140 /*
2141 * Some basic checks regarding threads and debuggers. We don't need
2142 * allocate any state memory for these.
2143 */
2144 int rc = VINF_SUCCESS;
2145 if (enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION)
2146 rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
2147 if (RT_SUCCESS(rc))
2148 rc = supHardNtVpDebugger(hProcess, pErrInfo);
2149 if (RT_SUCCESS(rc))
2150 {
2151 /*
2152 * Allocate and initialize memory for the state.
2153 */
2154 PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)RTMemAllocZ(sizeof(*pThis));
2155 if (pThis)
2156 {
2157 pThis->enmKind = enmKind;
2158 pThis->rcResult = VINF_SUCCESS;
2159 pThis->hProcess = hProcess;
2160 pThis->pErrInfo = pErrInfo;
2161
2162 /*
2163 * Perform the verification.
2164 */
2165 rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
2166 if (RT_SUCCESS(rc))
2167 rc = supHardNtVpOpenImages(pThis);
2168 if (RT_SUCCESS(rc))
2169 rc = supHardNtVpCheckExe(pThis, hProcess);
2170 if (RT_SUCCESS(rc))
2171 rc = supHardNtVpCheckDlls(pThis, hProcess);
2172
2173 if (pcFixes)
2174 *pcFixes = pThis->cFixes;
2175
2176 /*
2177 * Clean up the state.
2178 */
2179#ifdef IN_RING0
2180 for (uint32_t i = 0; i < pThis->cImages; i++)
2181 supHardNTLdrCacheDeleteEntry(&pThis->aImages[i].CacheEntry);
2182#endif
2183 RTMemFree(pThis);
2184 }
2185 else
2186 rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
2187 "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
2188 }
2189 return rc;
2190}
2191
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