VirtualBox

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

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

SUP: Fixed handle leak in the driver. Adjusted NtQueryInformationProcess/ProcessImageInformation for XP. Shut up an DEBUG assertion caused by certificate(s) with malformed ASN.1 UTC TIME objects (not zulu time).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.6 KB
Line 
1/* $Id: SUPHardenedVerifyProcess-win.cpp 52207 2014-07-27 19:33:11Z 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/ctype.h>
41#include <iprt/zero.h>
42#include <iprt/param.h>
43
44#ifdef IN_RING0
45# include "SUPDrvInternal.h"
46#else
47# include "SUPLibInternal.h"
48#endif
49#include "win/SUPHardenedVerify-win.h"
50
51
52/*******************************************************************************
53* Structures and Typedefs *
54*******************************************************************************/
55/**
56 * Virtual address space region.
57 */
58typedef struct SUPHNTVPREGION
59{
60 /** The RVA of the region. */
61 uint32_t uRva;
62 /** The size of the region. */
63 uint32_t cb;
64 /** The protection of the region. */
65 uint32_t fProt;
66} SUPHNTVPREGION;
67/** Pointer to a virtual address space region. */
68typedef SUPHNTVPREGION *PSUPHNTVPREGION;
69
70/**
71 * Virtual address space image information.
72 */
73typedef struct SUPHNTVPIMAGE
74{
75 /** The base address of the image. */
76 uintptr_t uImageBase;
77 /** The size of the image mapping. */
78 uintptr_t cbImage;
79
80 /** The name from the allowed lists. */
81 const char *pszName;
82 /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
83 struct
84 {
85 /** The full unicode name. */
86 UNICODE_STRING UniStr;
87 /** Buffer space. */
88 WCHAR awcBuffer[260];
89 } Name;
90
91 /** The number of mapping regions. */
92 uint32_t cRegions;
93 /** Mapping regions. */
94 SUPHNTVPREGION aRegions[16];
95
96 /** The image characteristics from the FileHeader. */
97 uint16_t fImageCharecteristics;
98 /** The DLL characteristics from the OptionalHeader. */
99 uint16_t fDllCharecteristics;
100
101 /** Set if this is the DLL. */
102 bool fDll;
103 /** Set if the image is NTDLL an the verficiation code needs to watch out for
104 * the NtCreateSection patch. */
105 bool fNtCreateSectionPatch;
106 /** Whether the API set schema hack needs to be applied when verifying memory
107 * content. The hack means that we only check if the 1st section is mapped. */
108 bool fApiSetSchemaOnlySection1;
109 /** This may be a 32-bit resource DLL. */
110 bool f32bitResourceDll;
111
112 /** Load module associated with the image during content verfication. */
113 RTLDRMOD hLdrMod;
114 /** The file reader. */
115 PSUPHNTVIRDR pNtViRdr;
116 /** The module file handle, if we've opened it.
117 * (pNtviRdr does not close the file handle on destruction.) */
118 HANDLE hFile;
119 /** Image bits for lazy cleanup. */
120 uint8_t *pbBits;
121} SUPHNTVPIMAGE;
122/** Pointer to image info from the virtual address space scan. */
123typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
124
125/**
126 * Virtual address space scanning state.
127 */
128typedef struct SUPHNTVPSTATE
129{
130 /** Type of verification to perform. */
131 SUPHARDNTVPKIND enmKind;
132 /** The result. */
133 int rcResult;
134 /** Number of images in aImages. */
135 uint32_t cImages;
136 /** The process handle. */
137 HANDLE hProcess;
138 /** Images found in the process.
139 * The array is large enough to hold the executable, all allowed DLLs, and one
140 * more so we can get the image name of the first unwanted DLL. */
141 SUPHNTVPIMAGE aImages[1 + 6 + 1
142#ifdef VBOX_PERMIT_MORE
143 + 5
144#endif
145#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
146 + 16
147#endif
148 ];
149 /** Memory compare scratch buffer.*/
150 uint8_t abMemory[_4K];
151 /** File compare scratch buffer.*/
152 uint8_t abFile[_4K];
153 /** Section headers for use when comparing file and loaded image. */
154 IMAGE_SECTION_HEADER aSecHdrs[16];
155 /** Pointer to the error info. */
156 PRTERRINFO pErrInfo;
157} SUPHNTVPSTATE;
158/** Pointer to stat information of a virtual address space scan. */
159typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
160
161
162/*******************************************************************************
163* Global Variables *
164*******************************************************************************/
165/**
166 * System DLLs allowed to be loaded into the process.
167 * @remarks supHardNtVpCheckDlls assumes these are lower case.
168 */
169static const char *g_apszSupNtVpAllowedDlls[] =
170{
171 "ntdll.dll",
172 "kernel32.dll",
173 "kernelbase.dll",
174 "apphelp.dll",
175 "apisetschema.dll",
176#ifdef VBOX_PERMIT_MORE
177# define VBOX_PERMIT_MORE_FIRST_IDX 5
178 "sfc.dll",
179 "sfc_os.dll",
180 "user32.dll",
181 "acres.dll",
182 "acgenral.dll",
183#endif
184#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
185 "psapi.dll",
186 "msvcrt.dll",
187 "advapi32.dll",
188 "sechost.dll",
189 "rpcrt4.dll",
190 "SamplingRuntime.dll",
191#endif
192};
193
194/**
195 * VBox executables allowed to start VMs.
196 * @remarks Remember to keep in sync with SUPR3HardenedVerify.cpp.
197 */
198static const char *g_apszSupNtVpAllowedVmExes[] =
199{
200 "VBoxHeadless.exe",
201 "VirtualBox.exe",
202 "VBoxSDL.exe",
203 "VBoxNetDHCP.exe",
204 "VBoxNetNAT.exe",
205
206 "tstMicro.exe",
207 "tstPDMAsyncCompletion.exe",
208 "tstPDMAsyncCompletionStress.exe",
209 "tstVMM.exe",
210 "tstVMREQ.exe",
211 "tstCFGM.exe",
212 "tstIntNet-1.exe",
213 "tstMMHyperHeap.exe",
214 "tstR0ThreadPreemptionDriver.exe",
215 "tstRTR0MemUserKernelDriver.exe",
216 "tstRTR0SemMutexDriver.exe",
217 "tstRTR0TimerDriver.exe",
218 "tstSSM.exe",
219};
220
221/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
222 * ring-0, in ring-3 it's just a slightly confusing define. */
223#ifdef IN_RING0
224PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
225#else
226# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
227#endif
228
229
230/**
231 * Fills in error information.
232 *
233 * @returns @a rc.
234 * @param pErrInfo Pointer to the extended error info structure.
235 * Can be NULL.
236 * @param pszErr Where to return error details.
237 * @param cbErr Size of the buffer @a pszErr points to.
238 * @param rc The status to return.
239 * @param pszMsg The format string for the message.
240 * @param ... The arguments for the format string.
241 */
242static int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
243{
244 va_list va;
245#ifdef IN_RING3
246 va_start(va, pszMsg);
247 supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
248 va_end(va);
249#endif
250
251 va_start(va, pszMsg);
252 RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
253 va_end(va);
254
255 return rc;
256}
257
258
259/**
260 * Fills in error information.
261 *
262 * @returns @a rc.
263 * @param pThis The process validator instance.
264 * @param pszErr Where to return error details.
265 * @param cbErr Size of the buffer @a pszErr points to.
266 * @param rc The status to return.
267 * @param pszMsg The format string for the message.
268 * @param ... The arguments for the format string.
269 */
270static int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
271{
272 va_list va;
273#ifdef IN_RING3
274 va_start(va, pszMsg);
275 supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
276 va_end(va);
277#endif
278
279 va_start(va, pszMsg);
280#ifdef IN_RING0
281 RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
282 pThis->rcResult = rc;
283#else
284 if (RT_SUCCESS(pThis->rcResult))
285 {
286 RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
287 pThis->rcResult = rc;
288 }
289 else
290 {
291 RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
292 RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
293 }
294#endif
295 va_end(va);
296
297 return pThis->rcResult;
298}
299
300
301static int supHardNtVpReadImage(PSUPHNTVPIMAGE pImage, uint64_t off, void *pvBuf, size_t cbRead)
302{
303 return pImage->pNtViRdr->Core.pfnRead(&pImage->pNtViRdr->Core, pvBuf, cbRead, off);
304}
305
306
307static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
308{
309#ifdef IN_RING0
310 /* ASSUMES hProcess is the current process. */
311 /** @todo use MmCopyVirtualMemory where available! */
312 int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
313 if (RT_SUCCESS(rc))
314 return STATUS_SUCCESS;
315 return STATUS_ACCESS_DENIED;
316#else
317 SIZE_T cbIgn;
318 NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
319 if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
320 rcNt = STATUS_IO_DEVICE_ERROR;
321 return rcNt;
322#endif
323}
324
325
326#ifdef IN_RING3
327static NTSTATUS supHardNtVpFileMemRestore(PSUPHNTVPSTATE pThis, PVOID pvRestoreAddr, uint8_t const *pbFile, uint32_t cbToRestore,
328 uint32_t fCorrectProtection)
329{
330 PVOID pvProt = pvRestoreAddr;
331 SIZE_T cbProt = cbToRestore;
332 ULONG fOldProt = 0;
333 NTSTATUS rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_READWRITE, &fOldProt);
334 if (NT_SUCCESS(rcNt))
335 {
336 SIZE_T cbIgnored;
337 rcNt = NtWriteVirtualMemory(pThis->hProcess, pvRestoreAddr, pbFile, cbToRestore, &cbIgnored);
338
339 pvProt = pvRestoreAddr;
340 cbProt = cbToRestore;
341 NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fCorrectProtection, &fOldProt);
342 if (NT_SUCCESS(rcNt))
343 rcNt = rcNt2;
344 }
345 return rcNt;
346}
347#endif /* IN_RING3 */
348
349
350typedef struct SUPHNTVPSKIPAREA
351{
352 uint32_t uRva;
353 uint32_t cb;
354} SUPHNTVPSKIPAREA;
355typedef SUPHNTVPSKIPAREA *PSUPHNTVPSKIPAREA;
356
357static int supHardNtVpFileMemCompareSection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
358 uint32_t uRva, uint32_t cb, const uint8_t *pbFile,
359 int32_t iSh, PSUPHNTVPSKIPAREA paSkipAreas, uint32_t cSkipAreas,
360 uint32_t fCorrectProtection)
361{
362 AssertCompileAdjacentMembers(SUPHNTVPSTATE, abMemory, abFile); /* Use both the memory and file buffers here. Parfait might hate me for this... */
363 uint32_t const cbMemory = sizeof(pThis->abMemory) + sizeof(pThis->abFile);
364 uint8_t * const pbMemory = &pThis->abMemory[0];
365
366 while (cb > 0)
367 {
368 uint32_t cbThis = RT_MIN(cb, cbMemory);
369
370 /* Clipping. */
371 uint32_t uNextRva = uRva + cbThis;
372 if (cSkipAreas)
373 {
374 uint32_t uRvaEnd = uNextRva;
375 uint32_t i = cSkipAreas;
376 while (i-- > 0)
377 {
378 uint32_t uSkipEnd = paSkipAreas[i].uRva + paSkipAreas[i].cb;
379 if ( uRva < uSkipEnd
380 && uRvaEnd > paSkipAreas[i].uRva)
381 {
382 if (uRva < paSkipAreas[i].uRva)
383 {
384 cbThis = paSkipAreas[i].uRva - uRva;
385 uRvaEnd = paSkipAreas[i].uRva;
386 uNextRva = uSkipEnd;
387 }
388 else if (uRvaEnd >= uSkipEnd)
389 {
390 cbThis -= uSkipEnd - uRva;
391 uRva = uSkipEnd;
392 }
393 else
394 {
395 uNextRva = uSkipEnd;
396 cbThis = 0;
397 break;
398 }
399 }
400 }
401 }
402
403 /* Read the memory. */
404 NTSTATUS rcNt = supHardNtVpReadMem(pThis->hProcess, pImage->uImageBase + uRva, pbMemory, cbThis);
405 if (!NT_SUCCESS(rcNt))
406 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
407 "%s: Error reading %#x bytes at %p (rva %#x, #%u, %.8s) from memory: %#x",
408 pImage->pszName, cbThis, pImage->uImageBase + uRva, uRva, iSh + 1,
409 iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers", rcNt);
410
411 /* Do the compare. */
412 if (memcmp(pbFile, pbMemory, cbThis) != 0)
413 {
414 const char *pachSectNm = iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers";
415 SUP_DPRINTF(("%s: Differences in section #%u (%s) between file and memory:\n", pImage->pszName, iSh + 1, pachSectNm));
416
417 uint32_t off = 0;
418 while (off < cbThis && pbFile[off] == pbMemory[off])
419 off++;
420 SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
421 pImage->uImageBase + uRva + off, uRva + off, pbFile[off], pbMemory[off]));
422 uint32_t offLast = off;
423 uint32_t cDiffs = 1;
424 for (uint32_t off2 = off + 1; off2 < cbThis; off2++)
425 if (pbFile[off2] != pbMemory[off2])
426 {
427 SUP_DPRINTF((" %p / %#09x: %02x != %02x\n",
428 pImage->uImageBase + uRva + off2, uRva + off2, pbFile[off2], pbMemory[off2]));
429 cDiffs++;
430 offLast = off2;
431 }
432
433#ifdef IN_RING3
434 if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
435 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
436 {
437 PVOID pvRestoreAddr = (uint8_t *)pImage->uImageBase + uRva;
438 rcNt = supHardNtVpFileMemRestore(pThis, pvRestoreAddr, pbFile, cbThis, fCorrectProtection);
439 if (NT_SUCCESS(rcNt))
440 SUP_DPRINTF((" Restored %#x bytes of original file content at %p\n", cbThis, pvRestoreAddr));
441 else
442 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
443 "%s: Failed to restore %#x bytes at %p (%#x, #%u, %s): %#x (cDiffs=%#x, first=%#x)",
444 pImage->pszName, cbThis, pvRestoreAddr, uRva, iSh + 1, pachSectNm, rcNt,
445 cDiffs, uRva + off);
446 }
447 else
448#endif /* IN_RING3 */
449 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
450 "%s: %u differences between %#x and %#x in #%u (%.8s), first: %02x != %02x",
451 pImage->pszName, cDiffs, uRva + off, uRva + offLast, iSh + 1,
452 pachSectNm, pbFile[off], pbMemory[off]);
453 }
454
455 /* Advance. The clipping makes it a little bit complicated. */
456 cbThis = uNextRva - uRva;
457 if (cbThis >= cb)
458 break;
459 cb -= cbThis;
460 pbFile += cbThis;
461 uRva = uNextRva;
462 }
463 return VINF_SUCCESS;
464}
465
466
467
468static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
469 uint32_t uRva, uint32_t cb, uint32_t fProt)
470{
471 uint32_t const cbOrg = cb;
472 if (!cb)
473 return VINF_SUCCESS;
474 if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
475 || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
476 return VINF_SUCCESS;
477
478 for (uint32_t i = 0; i < pImage->cRegions; i++)
479 {
480 uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
481 if (offRegion < pImage->aRegions[i].cb)
482 {
483 uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
484 if ( pImage->aRegions[i].fProt != fProt
485 && ( fProt != PAGE_READWRITE
486 || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
487 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
488 "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
489 pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
490 if (cbLeft >= cb)
491 return VINF_SUCCESS;
492 cb -= cbLeft;
493 uRva += cbLeft;
494
495#if 0 /* This shouldn't ever be necessary. */
496 if ( i + 1 < pImage->cRegions
497 && uRva < pImage->aRegions[i + 1].uRva)
498 {
499 cbLeft = pImage->aRegions[i + 1].uRva - uRva;
500 if (cbLeft >= cb)
501 return VINF_SUCCESS;
502 cb -= cbLeft;
503 uRva += cbLeft;
504 }
505#endif
506 }
507 }
508
509 return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
510 "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
511}
512
513
514/**
515 * Compares process memory with the disk content.
516 *
517 * @returns VBox status code.
518 * @param pThis The process scanning state structure (for the
519 * two scratch buffers).
520 * @param pImage The image data collected during the address
521 * space scan.
522 * @param hProcess Handle to the process.
523 * @param pErrInfo Pointer to error info structure. Optional.
524 */
525static int supHardNtVpVerifyImageMemoryCompare(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, PRTERRINFO pErrInfo)
526{
527 /*
528 * Read and find the file headers.
529 */
530 int rc = supHardNtVpReadImage(pImage, 0 /*off*/, pThis->abFile, sizeof(pThis->abFile));
531 if (RT_FAILURE(rc))
532 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
533 "%s: Error reading image header: %Rrc", pImage->pszName, rc);
534
535 uint32_t offNtHdrs = 0;
536 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
537 if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
538 {
539 offNtHdrs = pDosHdr->e_lfanew;
540 if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
541 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
542 "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
543 }
544 PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
545 PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)pNtHdrs;
546 if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
547 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
548 "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
549
550 /*
551 * Do basic header validation.
552 */
553#ifdef RT_ARCH_AMD64
554 if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && !pImage->f32bitResourceDll)
555#else
556 if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
557#endif
558 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
559 "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
560 bool const fIs32Bit = pNtHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
561
562 if (pNtHdrs->FileHeader.SizeOfOptionalHeader != (fIs32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
563 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
564 "%s: Unexpected optional header size: %#x",
565 pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
566
567 if (pNtHdrs->OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
568 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
569 "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
570
571 uint32_t cDirs = (fIs32Bit ? pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes : pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
572 if (cDirs != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
573 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
574 "%s: Unexpected data dirs: %#x", pImage->pszName, cDirs);
575
576 /*
577 * Before we start comparing things, store what we need to know from the headers.
578 */
579 uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
580 if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
581 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
582 "%s: Too many section headers: %#x", pImage->pszName, cSections);
583 suplibHardenedMemCopy(pThis->aSecHdrs, (fIs32Bit ? (void *)(pNtHdrs32 + 1) : (void *)(pNtHdrs + 1)),
584 cSections * sizeof(IMAGE_SECTION_HEADER));
585
586 uintptr_t const uImageBase = fIs32Bit ? pNtHdrs32->OptionalHeader.ImageBase : pNtHdrs->OptionalHeader.ImageBase;
587 if (uImageBase & PAGE_OFFSET_MASK)
588 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
589 "%s: Invalid image base: %p", pImage->pszName, uImageBase);
590
591 uint32_t const cbImage = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfImage : pNtHdrs->OptionalHeader.SizeOfImage;
592 if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
593 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
594 "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
595 pImage->pszName, cbImage, pImage->cbImage);
596 if (cbImage != RTLdrSize(pImage->hLdrMod))
597 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
598 "%s: SizeOfImage (%#x) differs from what RTLdrSize returns (%#zx)",
599 pImage->pszName, cbImage, RTLdrSize(pImage->hLdrMod));
600
601 uint32_t const cbSectAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.SectionAlignment : pNtHdrs->OptionalHeader.SectionAlignment;
602 if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
603 || cbSectAlign < PAGE_SIZE
604 || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
605 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
606 "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
607
608 uint32_t const cbFileAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.FileAlignment : pNtHdrs->OptionalHeader.FileAlignment;
609 if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
610 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
611 "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
612 pImage->pszName, cbFileAlign, cbSectAlign);
613
614 uint32_t const cbHeaders = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfHeaders : pNtHdrs->OptionalHeader.SizeOfHeaders;
615 uint32_t const cbMinHdrs = offNtHdrs + (fIs32Bit ? sizeof(*pNtHdrs32) : sizeof(*pNtHdrs) )
616 + sizeof(IMAGE_SECTION_HEADER) * cSections;
617 if (cbHeaders < cbMinHdrs)
618 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
619 "%s: Headers are too small: %#x < %#x (cSections=%#x)",
620 pImage->pszName, cbHeaders, cbMinHdrs, cSections);
621 uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
622 if (cbHdrsFile > sizeof(pThis->abFile))
623 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
624 "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
625 pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
626
627 /*
628 * Save some header fields we might be using later on.
629 */
630 pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
631 pImage->fDllCharecteristics = fIs32Bit ? pNtHdrs32->OptionalHeader.DllCharacteristics : pNtHdrs->OptionalHeader.DllCharacteristics;
632
633 /*
634 * Correct the apisetschema image base, size and region rva.
635 */
636 if (pImage->fApiSetSchemaOnlySection1)
637 {
638 pImage->uImageBase -= pThis->aSecHdrs[0].VirtualAddress;
639 pImage->cbImage += pThis->aSecHdrs[0].VirtualAddress;
640 pImage->aRegions[0].uRva = pThis->aSecHdrs[0].VirtualAddress;
641 }
642
643 /*
644 * Get relocated bits.
645 */
646 pImage->pbBits = (uint8_t *)suplibHardenedAllocZ(cbImage);
647 if (RT_UNLIKELY(!pImage->pbBits))
648 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
649 "%s: Error allocating %#x bytes for fixed up image bits.", pImage->pszName, cbImage);
650 rc = RTLdrGetBits(pImage->hLdrMod, pImage->pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis);
651 /**@todo resolve import when not in SUPHARDNTVPKIND_CHILD_PURIFICATION mode. */
652 if (RT_FAILURE(rc))
653 return supHardNtVpSetInfo2(pThis, rc, "%s: RTLdrGetBits failed: %Rrc", pImage->pszName, rc);
654
655 /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
656 if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
657 {
658 if (fIs32Bit)
659 ((PIMAGE_NT_HEADERS32)&pImage->pbBits[offNtHdrs])->OptionalHeader.ImageBase = (uint32_t)pImage->uImageBase;
660 else
661 ((PIMAGE_NT_HEADERS)&pImage->pbBits[offNtHdrs])->OptionalHeader.ImageBase = pImage->uImageBase;
662 }
663
664 /*
665 * Figure out areas we should skip during comparison.
666 */
667 uint32_t cSkipAreas = 0;
668 SUPHNTVPSKIPAREA aSkipAreas[2];
669 if (pImage->fNtCreateSectionPatch)
670 {
671 RTLDRADDR uValue;
672 if (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
673 {
674 /* Ignore our NtCreateSection hack. */
675 rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pbBits, 0, "NtCreateSection", &uValue);
676 if (RT_FAILURE(rc))
677 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
678 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
679 aSkipAreas[cSkipAreas++].cb = 16;
680 }
681
682 /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
683 rc = RTLdrGetSymbolEx(pImage->hLdrMod, pImage->pbBits, 0, "LdrSystemDllInitBlock", &uValue);
684 if (RT_SUCCESS(rc))
685 {
686 aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
687 aSkipAreas[cSkipAreas++].cb = RT_MAX(pImage->pbBits[(uint32_t)uValue], 0x50);
688 }
689 }
690
691 /*
692 * Compare the file header with the loaded bits. The loader will fiddle
693 * with image base, changing it to the actual load address.
694 */
695 if (!pImage->fApiSetSchemaOnlySection1)
696 {
697 rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pImage->pbBits, -1, NULL, 0, PAGE_READONLY);
698 if (RT_FAILURE(rc))
699 return rc;
700
701 rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
702 if (RT_FAILURE(rc))
703 return rc;
704 }
705
706 /*
707 * Validate sections:
708 * - Check them against the mapping regions.
709 * - Check section bits according to enmKind.
710 */
711 uint32_t fPrevProt = PAGE_READONLY;
712 uint32_t uRva = cbHdrsFile;
713 for (uint32_t i = 0; i < cSections; i++)
714 {
715 /* Validate the section. */
716 uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
717 if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
718 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
719 "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
720 pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
721 uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
722 if (cbMap > cbImage || uRva + cbMap > cbImage)
723 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
724 "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
725 pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
726 uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
727 if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
728 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
729 "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
730 pImage->pszName, i, cbFile, cbMap, uSectRva);
731
732 /* Validate the protection and bits. */
733 if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
734 {
735 uint32_t fProt;
736 switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
737 {
738 case IMAGE_SCN_MEM_READ:
739 fProt = PAGE_READONLY;
740 break;
741 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
742 fProt = PAGE_READWRITE;
743 if (!suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll */
744 fProt = PAGE_READONLY;
745 break;
746 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
747 fProt = PAGE_EXECUTE_READ;
748 break;
749 case IMAGE_SCN_MEM_EXECUTE:
750 fProt = PAGE_EXECUTE;
751 break;
752 default:
753 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
754 "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
755 pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
756 }
757
758 /* The section bits, only child purification verifies all bits . */
759 if ( pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
760 || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE)) )
761 {
762 rc = VINF_SUCCESS;
763 if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
764 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pImage->pbBits + uRva,
765 i - 1, NULL, 0, fPrevProt);
766 if (RT_SUCCESS(rc))
767 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pImage->pbBits + uSectRva,
768 i, aSkipAreas, cSkipAreas, fProt);
769 if (RT_SUCCESS(rc))
770 {
771 uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
772 ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
773 if (cbMapAligned > cbMap)
774 rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
775 g_abRTZeroPage, i, NULL, 0, fProt);
776 }
777 if (RT_FAILURE(rc))
778 return rc;
779 }
780
781 /* The protection (must be checked afterwards!). */
782 rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
783 if (RT_FAILURE(rc))
784 return rc;
785
786 fPrevProt = fProt;
787 }
788
789 /* Advance the RVA. */
790 uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
791 }
792
793 return VINF_SUCCESS;
794}
795
796
797/**
798 * Verifies the signature of the given image on disk, then checks if the memory
799 * mapping matches what we verified.
800 *
801 * @returns VBox status code.
802 * @param pThis The process scanning state structure (for the
803 * two scratch buffers).
804 * @param pImage The image data collected during the address
805 * space scan.
806 * @param hProcess Handle to the process.
807 * @param hFile Handle to the image file.
808 */
809static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess)
810{
811 /*
812 * Validate the file signature first, then do the memory compare.
813 */
814 int rc;
815 if (pImage->hLdrMod != NIL_RTLDRMOD)
816 {
817 rc = supHardenedWinVerifyImageByLdrMod(pImage->hLdrMod, pImage->Name.UniStr.Buffer, pImage->pNtViRdr,
818 NULL /*pfCacheable*/, pThis->pErrInfo);
819 if (RT_SUCCESS(rc))
820 {
821 rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage, hProcess, pThis->pErrInfo);
822
823 if (pImage->pbBits)
824 {
825 suplibHardenedFree(pImage->pbBits);
826 pImage->pbBits = NULL;
827 }
828 }
829 }
830 else
831 rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "hLdrMod is NIL! Impossible!");
832 return rc;
833}
834
835
836/**
837 * Verifies that there is only one thread in the process.
838 *
839 * @returns VBox status code.
840 * @param hProcess The process.
841 * @param hThread The thread.
842 * @param pErrInfo Pointer to error info structure. Optional.
843 */
844static int supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
845{
846 /*
847 * Use the ThreadAmILastThread request to check that there is only one
848 * thread in the process.
849 */
850 ULONG cbIgn = 0;
851 ULONG fAmI = 0;
852 NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
853 if (!NT_SUCCESS(rcNt))
854 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
855 "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
856 if (!fAmI)
857 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
858 "More than one thread in process");
859
860 /** @todo Would be nice to verify the relation ship between hProcess and hThread
861 * as well... */
862 return VINF_SUCCESS;
863}
864
865
866#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
867/**
868 * Verifies that there isn't a debugger attached to the process.
869 *
870 * @returns VBox status code.
871 * @param hProcess The process.
872 * @param pErrInfo Pointer to error info structure. Optional.
873 */
874static int supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
875{
876 /*
877 * Use the ProcessDebugPort request to check there is no debugger
878 * currently attached to the process.
879 */
880 ULONG cbIgn = 0;
881 uintptr_t uPtr = ~(uintptr_t)0;
882 NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
883 ProcessDebugPort,
884 &uPtr, sizeof(uPtr), &cbIgn);
885 if (!NT_SUCCESS(rcNt))
886 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
887 "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
888 if (uPtr != 0)
889 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
890 "Debugger attached (%#zx)", uPtr);
891 return VINF_SUCCESS;
892}
893#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
894
895
896/**
897 * Matches two UNICODE_STRING structures in a case sensitive fashion.
898 *
899 * @returns true if equal, false if not.
900 * @param pUniStr1 The first unicode string.
901 * @param pUniStr2 The first unicode string.
902 */
903static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
904{
905 if (pUniStr1->Length != pUniStr2->Length)
906 return false;
907 return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
908}
909
910
911/**
912 * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
913 *
914 * @returns true / false
915 * @param pszName1 The ASCII name.
916 * @param pwszName2 The UTF-16 name.
917 */
918static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
919{
920 for (;;)
921 {
922 char ch1 = *pszName1++;
923 RTUTF16 wc2 = *pwszName2++;
924 if (ch1 != wc2)
925 {
926 ch1 = RT_C_TO_LOWER(ch1);
927 wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
928 if (ch1 != wc2)
929 return false;
930 }
931 if (!ch1)
932 return true;
933 }
934}
935
936
937/**
938 * Records an additional memory region for an image.
939 *
940 * @returns VBox status code.
941 * @param pThis The process scanning state structure.
942 * @param pImage The new image structure. Only the unicode name
943 * buffer is valid.
944 * @param pMemInfo The memory information for the image.
945 */
946static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
947{
948 /*
949 * Extract the final component.
950 */
951 unsigned cwcDirName = pImage->Name.UniStr.Length / sizeof(WCHAR);
952 PCRTUTF16 pwszFilename = &pImage->Name.UniStr.Buffer[cwcDirName];
953 while ( cwcDirName > 0
954 && pwszFilename[-1] != '\\'
955 && pwszFilename[-1] != '/'
956 && pwszFilename[-1] != ':')
957 {
958 pwszFilename--;
959 cwcDirName--;
960 }
961 if (!*pwszFilename)
962 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
963 "Empty filename (len=%u) for image at %p.", pImage->Name.UniStr.Length, pMemInfo->BaseAddress);
964
965 /*
966 * Drop trailing slashes from the directory name.
967 */
968 while ( cwcDirName > 0
969 && ( pImage->Name.UniStr.Buffer[cwcDirName - 1] == '\\'
970 || pImage->Name.UniStr.Buffer[cwcDirName - 1] == '/'))
971 cwcDirName--;
972
973 /*
974 * Match it against known DLLs.
975 */
976 pImage->pszName = NULL;
977 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
978 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
979 {
980 pImage->pszName = g_apszSupNtVpAllowedDlls[i];
981 pImage->fDll = true;
982
983#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
984 /* The directory name must match the one we've got for System32. */
985 if ( ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
986 || suplibHardenedMemComp(pImage->Name.UniStr.Buffer,
987 g_System32NtPath.UniStr.Buffer,
988 cwcDirName * sizeof(WCHAR)) )
989# ifdef VBOX_PERMIT_MORE
990 && ( pImage->pszName[0] != 'a'
991 || pImage->pszName[1] != 'c'
992 || !supHardViIsAppPatchDir(pImage->Name.UniStr.Buffer, pImage->Name.UniStr.Length / sizeof(WCHAR)) )
993# endif
994 )
995 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
996 "Expected %ls to be loaded from %ls.",
997 pImage->Name.UniStr.Buffer, g_System32NtPath.UniStr.Buffer);
998# ifdef VBOX_PERMIT_MORE
999 if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
1000 pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
1001# endif
1002
1003#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1004 break;
1005 }
1006 if (!pImage->pszName)
1007 {
1008 /*
1009 * Not a known DLL, executable?
1010 */
1011 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
1012 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
1013 {
1014 pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
1015 pImage->fDll = false;
1016 break;
1017 }
1018 }
1019 if (!pImage->pszName)
1020 {
1021 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
1022 && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
1023 || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
1024 || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)) )
1025 {
1026 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
1027 "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
1028 "You or your admin need to add and exception to the Application and Device Control (ADC) "
1029 "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
1030 "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
1031 , pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
1032 return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
1033 }
1034 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
1035 "Unknown image file %ls at %p.", pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
1036 }
1037
1038 /*
1039 * Checks for multiple mappings of the same DLL but with different image file paths.
1040 */
1041 uint32_t i = pThis->cImages;
1042 while (i-- > 1)
1043 if (pImage->pszName == pThis->aImages[i].pszName)
1044 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1045 "Duplicate image entries for %s: %ls and %ls",
1046 pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
1047
1048 /*
1049 * Since it's a new image, we expect to be at the start of the mapping now.
1050 */
1051 if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
1052 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
1053 "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
1054 pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
1055
1056 /*
1057 * Check for size/rva overflow.
1058 */
1059 if (pMemInfo->RegionSize >= _2G)
1060 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1061 "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
1062
1063 /*
1064 * Fill in details from the memory info.
1065 */
1066 pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
1067 pImage->cbImage = pMemInfo->RegionSize;
1068 pImage->hFile = NULL;
1069 pImage->hLdrMod = NIL_RTLDRMOD;
1070 pImage->pNtViRdr = NULL;
1071 pImage->cRegions = 1;
1072 pImage->aRegions[0].uRva = 0;
1073 pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
1074 pImage->aRegions[0].fProt = pMemInfo->Protect;
1075
1076 if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
1077 pImage->fNtCreateSectionPatch = true;
1078 else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
1079 pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
1080#ifdef VBOX_PERMIT_MORE
1081 else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
1082 pImage->f32bitResourceDll = true;
1083#endif
1084
1085 return VINF_SUCCESS;
1086}
1087
1088
1089/**
1090 * Records an additional memory region for an image.
1091 *
1092 * @returns VBox status code.
1093 * @param pThis The process scanning state structure.
1094 * @param pImage The image.
1095 * @param pMemInfo The memory information for the region.
1096 */
1097static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1098{
1099 /*
1100 * Make sure the base address matches.
1101 */
1102 if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
1103 return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
1104 "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
1105 pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
1106 pMemInfo->BaseAddress, pMemInfo->RegionSize);
1107
1108 /*
1109 * Check for size and rva overflows.
1110 */
1111 uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
1112 if (pMemInfo->RegionSize >= _2G)
1113 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1114 "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1115 if (uRva >= _2G)
1116 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
1117 "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1118
1119
1120 /*
1121 * Record the region.
1122 */
1123 uint32_t iRegion = pImage->cRegions;
1124 if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
1125 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
1126 "Too many regions for %s.", pImage->pszName);
1127 pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
1128 pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
1129 pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
1130 pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
1131 pImage->cRegions++;
1132 pImage->fApiSetSchemaOnlySection1 = false;
1133
1134 return VINF_SUCCESS;
1135}
1136
1137
1138/**
1139 * Scans the virtual memory of the process.
1140 *
1141 * This collects the locations of DLLs and the EXE, and verifies that executable
1142 * memory is only associated with these.
1143 *
1144 * @returns VBox status code.
1145 * @param pThis The process scanning state structure. Details
1146 * about images are added to this.
1147 * @param hProcess The process to verify.
1148 */
1149static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1150{
1151 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
1152 pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
1153 pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
1154
1155 uint32_t cXpExceptions = 0;
1156 uintptr_t cbAdvance = 0;
1157 uintptr_t uPtrWhere = 0;
1158 for (uint32_t i = 0; i < 1024; i++)
1159 {
1160 SIZE_T cbActual = 0;
1161 MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
1162 NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1163 (void const *)uPtrWhere,
1164 MemoryBasicInformation,
1165 &MemInfo,
1166 sizeof(MemInfo),
1167 &cbActual);
1168 if (!NT_SUCCESS(rcNt))
1169 {
1170 if (rcNt == STATUS_INVALID_PARAMETER)
1171 return pThis->rcResult;
1172 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
1173 "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
1174 }
1175
1176 /*
1177 * Record images.
1178 */
1179 if ( MemInfo.Type == SEC_IMAGE
1180 || MemInfo.Type == SEC_PROTECTED_IMAGE
1181 || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
1182 {
1183 uint32_t iImg = pThis->cImages;
1184 rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1185 (void const *)uPtrWhere,
1186 MemorySectionName,
1187 &pThis->aImages[iImg].Name,
1188 sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
1189 &cbActual);
1190 if (!NT_SUCCESS(rcNt))
1191 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
1192 "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
1193 pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
1194 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1195 ? " *%p-%p %#06x/%#06x %#09x %ls\n"
1196 : " %p-%p %#06x/%#06x %#09x %ls\n",
1197 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1, MemInfo.Protect,
1198 MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
1199
1200 /* New or existing image? */
1201 bool fNew = true;
1202 uint32_t iSearch = iImg;
1203 while (iSearch-- > 0)
1204 if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
1205 {
1206 int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
1207 if (RT_FAILURE(rc))
1208 return rc;
1209 fNew = false;
1210 break;
1211 }
1212 else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
1213 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
1214 "Unexpected base address match");
1215
1216 if (fNew)
1217 {
1218 int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
1219 if (RT_SUCCESS(rc))
1220 {
1221 pThis->cImages++;
1222 if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
1223 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
1224 "Internal error: aImages is full.\n");
1225 }
1226#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
1227 else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
1228 return rc;
1229#else
1230 else
1231 return rc;
1232#endif
1233 }
1234 }
1235 /*
1236 * XP, W2K3: Ignore the CSRSS read-only region as best we can.
1237 */
1238 else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1239 == PAGE_EXECUTE_READ
1240 && cXpExceptions == 0
1241 && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
1242 /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
1243 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
1244 {
1245 cXpExceptions++;
1246 SUP_DPRINTF((" %p-%p %#06x/%#06x %#09x XP CSRSS read-only region\n", MemInfo.BaseAddress,
1247 (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1, MemInfo.Protect,
1248 MemInfo.AllocationProtect, MemInfo.Type));
1249 }
1250 /*
1251 * Executable memory?
1252 */
1253#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1254 else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1255 {
1256 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1257 ? " *%p-%p %#06x/%#06x %#09x !!\n"
1258 : " %p-%p %#06x/%#06x %#09x !!\n",
1259 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1,
1260 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1261# ifdef IN_RING3
1262 if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1263 {
1264 /*
1265 * Free any private executable memory (sysplant.sys allocates executable memory).
1266 */
1267 if (MemInfo.Type == MEM_PRIVATE)
1268 {
1269 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Freeing exec mem at %p (%p LB %#zx)\n",
1270 uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize));
1271 PVOID pvFree = MemInfo.BaseAddress;
1272 SIZE_T cbFree = MemInfo.RegionSize;
1273 rcNt = NtFreeVirtualMemory(pThis->hProcess, &pvFree, &cbFree, MEM_RELEASE);
1274 if (!NT_SUCCESS(rcNt))
1275 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1276 "NtFreeVirtualMemory (%p LB %#zx) failed: %#x",
1277 MemInfo.BaseAddress, MemInfo.RegionSize, rcNt);
1278 }
1279 /*
1280 * Unmap mapped memory, failing that, drop exec privileges.
1281 */
1282 else if (MemInfo.Type == MEM_MAPPED)
1283 {
1284 SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
1285 uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
1286 rcNt = NtUnmapViewOfSection(pThis->hProcess, MemInfo.AllocationBase);
1287 if (!NT_SUCCESS(rcNt))
1288 {
1289 PVOID pvCopy = MemInfo.BaseAddress;
1290 SIZE_T cbCopy = MemInfo.RegionSize;
1291 NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
1292 if (!NT_SUCCESS(rcNt2))
1293 rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
1294 if (!NT_SUCCESS(rcNt2))
1295 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
1296 "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
1297 MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
1298 }
1299 }
1300 else
1301 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
1302 "Unknown executable memory type %#x at %p/%p LB %#zx",
1303 MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
1304 }
1305 else
1306# endif /* IN_RING3 */
1307 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
1308 "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
1309 uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
1310 MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
1311
1312# ifndef IN_RING3
1313 if (RT_FAILURE(pThis->rcResult))
1314 return pThis->rcResult;
1315# endif
1316 /* Continue add more information about the problematic process. */
1317 }
1318#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1319 else
1320 SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1321 ? " *%p-%p %#06x/%#06x %#09x\n"
1322 : " %p-%p %#06x/%#06x %#09x\n",
1323 MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress - MemInfo.RegionSize - 1,
1324 MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1325
1326 /*
1327 * Advance.
1328 */
1329 cbAdvance = MemInfo.RegionSize;
1330 if (uPtrWhere + cbAdvance <= uPtrWhere)
1331 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
1332 "Empty region at %p.", uPtrWhere);
1333 uPtrWhere += MemInfo.RegionSize;
1334 }
1335
1336 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
1337 "Too many virtual memory regions.\n");
1338}
1339
1340/**
1341 * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
1342 * hLdrMod for each image.
1343 *
1344 * @returns VBox status code.
1345 * @param pThis The process scanning state structure.
1346 */
1347static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
1348{
1349 unsigned i = pThis->cImages;
1350 while (i-- > 0)
1351 {
1352 PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
1353
1354 /*
1355 * Open the image file.
1356 */
1357 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1358 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1359
1360 OBJECT_ATTRIBUTES ObjAttr;
1361 InitializeObjectAttributes(&ObjAttr, &pImage->Name.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1362#ifdef IN_RING0
1363 ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
1364#endif
1365
1366 NTSTATUS rcNt = NtCreateFile(&hFile,
1367 GENERIC_READ,
1368 &ObjAttr,
1369 &Ios,
1370 NULL /* Allocation Size*/,
1371 FILE_ATTRIBUTE_NORMAL,
1372 FILE_SHARE_READ,
1373 FILE_OPEN,
1374 FILE_NON_DIRECTORY_FILE,
1375 NULL /*EaBuffer*/,
1376 0 /*EaLength*/);
1377 if (NT_SUCCESS(rcNt))
1378 rcNt = Ios.Status;
1379 if (!NT_SUCCESS(rcNt))
1380 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
1381 "Error opening image for scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
1382
1383 /*
1384 * Figure out validation flags we'll be using and create the reader
1385 * for this image.
1386 */
1387 uint32_t fFlags = pImage->fDll ? 0 : SUPHNTVI_F_REQUIRE_BUILD_CERT;
1388 if (pImage->f32bitResourceDll)
1389 fFlags |= SUPHNTVI_F_RESOURCE_IMAGE;
1390
1391 PSUPHNTVIRDR pNtViRdr;
1392 int rc = supHardNtViRdrCreate(hFile, pImage->Name.UniStr.Buffer, fFlags, &pNtViRdr);
1393 if (RT_FAILURE(rc))
1394 {
1395 NtClose(hFile);
1396 return rc;
1397 }
1398 pImage->hFile = hFile;
1399 pImage->pNtViRdr = pNtViRdr;
1400
1401 /*
1402 * Finally, open the image with the laoder.
1403 */
1404 RTLDRMOD hLdrMod;
1405 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
1406 if (fFlags & SUPHNTVI_F_RESOURCE_IMAGE)
1407 enmArch = RTLDRARCH_WHATEVER;
1408 rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pThis->pErrInfo);
1409 if (RT_FAILURE(rc))
1410 return supHardNtVpSetInfo2(pThis, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
1411 rc, pImage->Name.UniStr.Buffer);
1412
1413 pImage->hLdrMod = hLdrMod;
1414 }
1415
1416 return VINF_SUCCESS;
1417}
1418
1419
1420/**
1421 * Check the integrity of the executable of the process.
1422 *
1423 * @returns VBox status code.
1424 * @param pThis The process scanning state structure. Details
1425 * about images are added to this.
1426 * @param hProcess The process to verify.
1427 */
1428static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1429{
1430 /*
1431 * Make sure there is exactly one executable image.
1432 */
1433 unsigned cExecs = 0;
1434 unsigned iExe = ~0U;
1435 unsigned i = pThis->cImages;
1436 while (i-- > 0)
1437 {
1438 if (!pThis->aImages[i].fDll)
1439 {
1440 cExecs++;
1441 iExe = i;
1442 }
1443 }
1444 if (cExecs == 0)
1445 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
1446 "No executable mapping found in the virtual address space.");
1447 if (cExecs != 1)
1448 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
1449 "Found more than one executable mapping in the virtual address space.");
1450 PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
1451
1452 /*
1453 * Check that it matches the executable image of the process.
1454 */
1455 int rc;
1456 ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
1457 PUNICODE_STRING pUniStr = (PUNICODE_STRING)suplibHardenedAllocZ(cbUniStr);
1458 if (!pUniStr)
1459 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
1460 "Error allocating %zu bytes for process name.", cbUniStr);
1461 ULONG cbIgn = 0;
1462 NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
1463 if (NT_SUCCESS(rcNt))
1464 {
1465 if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
1466 rc = VINF_SUCCESS;
1467 else
1468 {
1469 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1470 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
1471 "Process image name does not match the exectuable we found: %ls vs %ls.",
1472 pUniStr->Buffer, pImage->Name.UniStr.Buffer);
1473 }
1474 }
1475 else
1476 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
1477 "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
1478 suplibHardenedFree(pUniStr);
1479 if (RT_FAILURE(rc))
1480 return rc;
1481
1482 /*
1483 * Validate the signing of the executable image.
1484 * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
1485 */
1486 rc = supHardNtVpVerifyImage(pThis, pImage, hProcess);
1487 if (RT_FAILURE(rc))
1488 return rc;
1489
1490 /*
1491 * Check linking requirements.
1492 * This query is only available using the current process pseudo handle on
1493 * older windows versions. The cut-off seems to be Vista.
1494 */
1495 SECTION_IMAGE_INFORMATION ImageInfo;
1496 rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
1497 if (!NT_SUCCESS(rcNt))
1498 {
1499 if ( rcNt == STATUS_INVALID_PARAMETER
1500 && g_uNtVerCombined < SUP_NT_VER_VISTA
1501 && hProcess != NtCurrentProcess() )
1502 return VINF_SUCCESS;
1503 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
1504 "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x", rcNt, hProcess);
1505 }
1506 if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
1507 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
1508 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
1509 ImageInfo.DllCharacteristics);
1510 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
1511 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
1512 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
1513 ImageInfo.DllCharacteristics);
1514 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
1515 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
1516 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
1517 ImageInfo.DllCharacteristics);
1518
1519 if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
1520 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
1521 "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
1522 ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
1523
1524 if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
1525 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
1526 "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
1527 ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
1528
1529 return VINF_SUCCESS;
1530}
1531
1532
1533/**
1534 * Check the integrity of the DLLs found in the process.
1535 *
1536 * @returns VBox status code.
1537 * @param pThis The process scanning state structure. Details
1538 * about images are added to this.
1539 * @param hProcess The process to verify.
1540 */
1541static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1542{
1543 /*
1544 * Check for duplicate entries (paranoia).
1545 */
1546 uint32_t i = pThis->cImages;
1547 while (i-- > 1)
1548 {
1549 const char *pszName = pThis->aImages[i].pszName;
1550 uint32_t j = i;
1551 while (j-- > 0)
1552 if (pThis->aImages[j].pszName == pszName)
1553 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1554 "Duplicate image entries for %s: %ls and %ls",
1555 pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
1556 }
1557
1558 /*
1559 * Check that both ntdll and kernel32 are present.
1560 * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
1561 */
1562 uint32_t iNtDll = UINT32_MAX;
1563 uint32_t iKernel32 = UINT32_MAX;
1564 i = pThis->cImages;
1565 while (i-- > 0)
1566 if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
1567 iNtDll = i;
1568 else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
1569 iKernel32 = i;
1570 if (iNtDll == UINT32_MAX)
1571 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
1572 "The process has no NTDLL.DLL.");
1573 if (iKernel32 == UINT32_MAX && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION)
1574 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
1575 "The process has no KERNEL32.DLL.");
1576 else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1577 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
1578 "The process already has KERNEL32.DLL loaded.");
1579
1580 /*
1581 * Verify that the DLLs are correctly signed (by MS).
1582 */
1583 i = pThis->cImages;
1584 while (i-- > 0)
1585 {
1586 int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i], hProcess);
1587 if (RT_FAILURE(rc))
1588 return rc;
1589 }
1590
1591 return VINF_SUCCESS;
1592}
1593
1594
1595/**
1596 * Verifies the given process.
1597 *
1598 * The following requirements are checked:
1599 * - The process only has one thread, the calling thread.
1600 * - The process has no debugger attached.
1601 * - The executable image of the process is verified to be signed with
1602 * certificate known to this code at build time.
1603 * - The executable image is one of a predefined set.
1604 * - The process has only a very limited set of system DLLs loaded.
1605 * - The system DLLs signatures check out fine.
1606 * - The only executable memory in the process belongs to the system DLLs and
1607 * the executable image.
1608 *
1609 * @returns VBox status code.
1610 * @param hProcess The process to verify.
1611 * @param hThread A thread in the process (the caller).
1612 * @param enmKind The kind of process verification to perform.
1613 * @param pErrInfo Pointer to error info structure. Optional.
1614 */
1615DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, PRTERRINFO pErrInfo)
1616{
1617 /*
1618 * Some basic checks regarding threads and debuggers. We don't need
1619 * allocate any state memory for these.
1620 */
1621 int rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
1622#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
1623 if (RT_SUCCESS(rc))
1624 rc = supHardNtVpDebugger(hProcess, pErrInfo);
1625#endif
1626 if (RT_SUCCESS(rc))
1627 {
1628 /*
1629 * Allocate and initialize memory for the state.
1630 */
1631 PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)suplibHardenedAllocZ(sizeof(*pThis));
1632 if (pThis)
1633 {
1634 pThis->enmKind = enmKind;
1635 pThis->rcResult = VINF_SUCCESS;
1636 pThis->hProcess = hProcess;
1637 pThis->pErrInfo = pErrInfo;
1638
1639 /*
1640 * Perform the verification.
1641 */
1642 rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
1643 if (RT_SUCCESS(rc))
1644 rc = supHardNtVpOpenImages(pThis);
1645 if (RT_SUCCESS(rc))
1646 rc = supHardNtVpCheckExe(pThis, hProcess);
1647 if (RT_SUCCESS(rc))
1648 rc = supHardNtVpCheckDlls(pThis, hProcess);
1649
1650 /*
1651 * Clean up the state.
1652 */
1653 for (uint32_t i = 0; i < pThis->cImages; i++)
1654 {
1655 if (pThis->aImages[i].hLdrMod != NIL_RTLDRMOD)
1656 RTLdrClose(pThis->aImages[i].hLdrMod);
1657 else if (pThis->aImages[i].pNtViRdr)
1658 pThis->aImages[i].pNtViRdr->Core.pfnDestroy(&pThis->aImages[i].pNtViRdr->Core);
1659 if (pThis->aImages[i].hFile)
1660 NtClose(pThis->aImages[i].hFile);
1661 }
1662 suplibHardenedFree(pThis);
1663 }
1664 else
1665 rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
1666 "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
1667 }
1668 return rc;
1669}
1670
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