VirtualBox

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

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

SUP,LDR: Changed RTLdrGetBits to allow not resolving imports. Combined the memory and image purification code with the process validation code, adding a validation kind/mode parameter. The process verfication code now checks that code sections are unmodified. Had to add a self purification run before hooking NtCreateSection to undo a weird kernel32 change that avast made (making GetBinaryTypeW specify write thru when opening a file). So, VM startup is now even slower thanks to avast.

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