VirtualBox

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

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

Refuse symantec sysfer.dll; accept microsoft sfc.dll.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.0 KB
Line 
1/* $Id: SUPHardenedVerifyProcess-win.cpp 51977 2014-07-11 02:20:36Z 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/param.h>
42
43#ifdef IN_RING0
44# include "SUPDrvInternal.h"
45#else
46# include "SUPLibInternal.h"
47#endif
48#include "win/SUPHardenedVerify-win.h"
49
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54/**
55 * Virtual address space region.
56 */
57typedef struct SUPHNTVPREGION
58{
59 /** The RVA of the region. */
60 uint32_t uRva;
61 /** The size of the region. */
62 uint32_t cb;
63 /** The protection of the region. */
64 uint32_t fProt;
65} SUPHNTVPREGION;
66/** Pointer to a virtual address space region. */
67typedef SUPHNTVPREGION *PSUPHNTVPREGION;
68
69/**
70 * Virtual address space image information.
71 */
72typedef struct SUPHNTVPIMAGE
73{
74 /** The base address of the image. */
75 uintptr_t uImageBase;
76 /** The size of the image mapping. */
77 uintptr_t cbImage;
78
79 /** The name from the allowed lists. */
80 const char *pszName;
81 /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
82 struct
83 {
84 /** The full unicode name. */
85 UNICODE_STRING UniStr;
86 /** Buffer space. */
87 WCHAR awcBuffer[260];
88 } Name;
89
90 /** The number of mapping regions. */
91 uint32_t cRegions;
92 /** Mapping regions. */
93 SUPHNTVPREGION aRegions[16];
94
95 /** The image characteristics from the FileHeader. */
96 uint16_t fImageCharecteristics;
97 /** The DLL characteristics from the OptionalHeader. */
98 uint16_t fDllCharecteristics;
99
100 /** Set if this is the DLL. */
101 bool fDll;
102 /** Set if the image is NTDLL an the verficiation code needs to watch out for
103 * the NtCreateSection patch. */
104 bool fNtCreateSectionPatch;
105 /** Whether the API set schema hack needs to be applied when verifying memory
106 * content. The hack means that we only check if the 1st section is mapped. */
107 bool fApiSetSchemaOnlySection1;
108} SUPHNTVPIMAGE;
109/** Pointer to image info from the virtual address space scan. */
110typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
111
112/**
113 * Virtual address space scanning state.
114 */
115typedef struct SUPHNTVPSTATE
116{
117 /** The result. */
118 int rcResult;
119 /** Number of images in aImages. */
120 uint32_t cImages;
121 /** Images found in the process.
122 * The array is large enough to hold the executable, all allowed DLLs, and one
123 * more so we can get the image name of the first unwanted DLL. */
124 SUPHNTVPIMAGE aImages[1+6+1];
125 /** Memory compare scratch buffer.*/
126 uint8_t abMemory[_4K];
127 /** File compare scratch buffer.*/
128 uint8_t abFile[_4K];
129 /** Section headers for use when comparing file and loaded image. */
130 IMAGE_SECTION_HEADER aSecHdrs[16];
131 /** Pointer to the error info. */
132 PRTERRINFO pErrInfo;
133} SUPHNTVPSTATE;
134/** Pointer to stat information of a virtual address space scan. */
135typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
136
137
138/*******************************************************************************
139* Global Variables *
140*******************************************************************************/
141/**
142 * System DLLs allowed to be loaded into the process.
143 * @remarks supHardNtVpCheckDlls assumes these are lower case.
144 */
145static const char *g_apszSupNtVpAllowedDlls[] =
146{
147 "ntdll.dll",
148 "kernel32.dll",
149 "kernelbase.dll",
150 "apphelp.dll",
151 "apisetschema.dll",
152 "sfc.dll"
153};
154
155/**
156 * VBox executables allowed to start VMs.
157 * @remarks Remember to keep in sync with SUPR3HardenedVerify.cpp.
158 */
159static const char *g_apszSupNtVpAllowedVmExes[] =
160{
161 "VBoxHeadless.exe",
162 "VirtualBox.exe",
163 "VBoxSDL.exe",
164 "VBoxNetDHCP.exe",
165 "VBoxNetNAT.exe",
166
167 "tstMicro.exe",
168 "tstPDMAsyncCompletion.exe",
169 "tstPDMAsyncCompletionStress.exe",
170 "tstVMM.exe",
171 "tstVMREQ.exe",
172 "tstCFGM.exe",
173 "tstIntNet-1.exe",
174 "tstMMHyperHeap.exe",
175 "tstR0ThreadPreemptionDriver.exe",
176 "tstRTR0MemUserKernelDriver.exe",
177 "tstRTR0SemMutexDriver.exe",
178 "tstRTR0TimerDriver.exe",
179 "tstSSM.exe",
180};
181
182/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
183 * ring-0, in ring-3 it's just a slightly confusing define. */
184#ifdef IN_RING0
185PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
186#else
187# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
188#endif
189
190
191/**
192 * Fills in error information.
193 *
194 * @returns @a rc.
195 * @param pErrInfo Pointer to the extended error info structure.
196 * Can be NULL.
197 * @param pszErr Where to return error details.
198 * @param cbErr Size of the buffer @a pszErr points to.
199 * @param rc The status to return.
200 * @param pszMsg The format string for the message.
201 * @param ... The arguments for the format string.
202 */
203static int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
204{
205 va_list va;
206#ifdef IN_RING3
207 va_start(va, pszMsg);
208 supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
209 va_end(va);
210#endif
211
212 va_start(va, pszMsg);
213 RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
214 va_end(va);
215
216 return rc;
217}
218
219
220/**
221 * Fills in error information.
222 *
223 * @returns @a rc.
224 * @param pThis The process validator instance.
225 * @param pszErr Where to return error details.
226 * @param cbErr Size of the buffer @a pszErr points to.
227 * @param rc The status to return.
228 * @param pszMsg The format string for the message.
229 * @param ... The arguments for the format string.
230 */
231static int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
232{
233 va_list va;
234#ifdef IN_RING3
235 va_start(va, pszMsg);
236 supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
237 va_end(va);
238#endif
239
240 va_start(va, pszMsg);
241#ifdef IN_RING0
242 RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
243 pThis->rcResult = rc;
244#else
245 if (RT_SUCCESS(pThis->rcResult))
246 {
247 RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
248 pThis->rcResult = rc;
249 }
250 else
251 {
252 RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
253 RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
254 }
255#endif
256 va_end(va);
257
258 return pThis->rcResult;
259}
260
261
262static NTSTATUS supHardNtVpReadFile(HANDLE hFile, uint64_t off, void *pvBuf, size_t cbRead)
263{
264 if ((ULONG)cbRead != cbRead)
265 return STATUS_INTEGER_OVERFLOW;
266
267 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
268 NTSTATUS rcNt = NtReadFile(hFile,
269 NULL /*hEvent*/,
270 NULL /*ApcRoutine*/,
271 NULL /*ApcContext*/,
272 &Ios,
273 pvBuf,
274 (ULONG)cbRead,
275 (PLARGE_INTEGER)&off,
276 NULL);
277 if (NT_SUCCESS(rcNt))
278 rcNt = Ios.Status;
279 return rcNt;
280}
281
282
283static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
284{
285#ifdef IN_RING0
286 /* ASSUMES hProcess is the current process. */
287 /** @todo use MmCopyVirtualMemory where available! */
288 int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
289 if (RT_SUCCESS(rc))
290 return STATUS_SUCCESS;
291 return STATUS_ACCESS_DENIED;
292#else
293 SIZE_T cbIgn;
294 NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
295 if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
296 rcNt = STATUS_IO_DEVICE_ERROR;
297 return rcNt;
298#endif
299}
300
301
302static int supHardNtVpFileMemCompare(PSUPHNTVPSTATE pThis, const void *pvFile, const void *pvMemory, size_t cbToCompare,
303 PSUPHNTVPIMAGE pImage, uint32_t uRva)
304{
305 if (suplibHardenedMemComp(pvFile, pvMemory, cbToCompare) == 0)
306 return VINF_SUCCESS;
307
308 /* Find the exact location. */
309 const uint8_t *pbFile = (const uint8_t *)pvFile;
310 const uint8_t *pbMemory = (const uint8_t *)pvMemory;
311 while (cbToCompare > 0 && *pbFile == *pbMemory)
312 {
313 cbToCompare--;
314 pbFile++;
315 pbMemory++;
316 uRva++;
317 }
318
319 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
320 "%s: memory compare at %#x failed: %#x != %#x\n", pImage->pszName, uRva, *pbFile, *pbMemory);
321}
322
323
324static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
325 uint32_t uRva, uint32_t cb, uint32_t fProt)
326{
327 uint32_t const cbOrg = cb;
328 if (!cb)
329 return VINF_SUCCESS;
330
331 for (uint32_t i = 0; i < pImage->cRegions; i++)
332 {
333 uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
334 if (offRegion < pImage->aRegions[i].cb)
335 {
336 uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
337 if ( pImage->aRegions[i].fProt != fProt
338 && ( fProt != PAGE_READWRITE
339 || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
340 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
341 "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
342 pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
343 if (cbLeft >= cb)
344 return VINF_SUCCESS;
345 cb -= cbLeft;
346 uRva += cbLeft;
347
348#if 0 /* This shouldn't ever be necessary. */
349 if ( i + 1 < pImage->cRegions
350 && uRva < pImage->aRegions[i + 1].uRva)
351 {
352 cbLeft = pImage->aRegions[i + 1].uRva - uRva;
353 if (cbLeft >= cb)
354 return VINF_SUCCESS;
355 cb -= cbLeft;
356 uRva += cbLeft;
357 }
358#endif
359 }
360 }
361
362 return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
363 "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
364}
365
366
367/**
368 * Compares process memory with the disk content.
369 *
370 * @returns VBox status code.
371 * @param pThis The process scanning state structure (for the
372 * two scratch buffers).
373 * @param pImage The image data collected during the address
374 * space scan.
375 * @param hProcess Handle to the process.
376 * @param hFile Handle to the image file.
377 * @param pErrInfo Pointer to error info structure. Optional.
378 */
379static int supHardNtVpVerifyImageCompareMemory(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, HANDLE hFile,
380 PRTERRINFO pErrInfo)
381{
382 /*
383 * Read and find the file headers.
384 */
385 NTSTATUS rcNt = supHardNtVpReadFile(hFile, 0, pThis->abFile, sizeof(pThis->abFile));
386 if (!NT_SUCCESS(rcNt))
387 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
388 "%s: Error reading image header: %#x", pImage->pszName, rcNt);
389
390 uint32_t offNtHdrs = 0;
391 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
392 if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
393 {
394 offNtHdrs = pDosHdr->e_lfanew;
395 if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
396 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
397 "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
398 }
399 PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
400 if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
401 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
402 "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
403
404 /*
405 * Do basic header validation.
406 */
407#ifdef RT_ARCH_AMD64
408 if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
409#else
410 if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
411#endif
412 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
413 "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
414
415 if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
416 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
417 "%s: Unexpected optional header size: %#x",
418 pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
419
420 if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
421 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
422 "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
423 if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
424 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
425 "%s: Unexpected data dirs: %#x", pImage->pszName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
426
427 /*
428 * Before we start comparing things, store what we need to know from the headers.
429 */
430 uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
431 if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
432 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
433 "%s: Too many section headers: %#x", pImage->pszName, cSections);
434 suplibHardenedMemCopy(pThis->aSecHdrs, pNtHdrs + 1, cSections * sizeof(IMAGE_SECTION_HEADER));
435
436 uintptr_t const uImageBase = pNtHdrs->OptionalHeader.ImageBase;
437 if (uImageBase & PAGE_OFFSET_MASK)
438 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
439 "%s: Invalid image base: %p", pImage->pszName, uImageBase);
440
441 uint32_t const cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
442 if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
443 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
444 "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
445 pImage->pszName, cbImage, pImage->cbImage);
446
447 uint32_t const cbSectAlign = pNtHdrs->OptionalHeader.SectionAlignment;
448 if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
449 || cbSectAlign < PAGE_SIZE
450 || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
451 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
452 "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
453
454 uint32_t const cbFileAlign = pNtHdrs->OptionalHeader.FileAlignment;
455 if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
456 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
457 "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
458 pImage->pszName, cbFileAlign, cbSectAlign);
459
460 uint32_t const cbHeaders = pNtHdrs->OptionalHeader.SizeOfHeaders;
461 uint32_t const cbMinHdrs = offNtHdrs + sizeof(*pNtHdrs) + sizeof(IMAGE_SECTION_HEADER) * cSections;
462 if (cbHeaders < cbMinHdrs)
463 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
464 "%s: Headers are too small: %#x < %#x (cSections=%#x)",
465 pImage->pszName, cbHeaders, cbMinHdrs, cSections);
466 uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
467 if (cbHdrsFile > sizeof(pThis->abFile))
468 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
469 "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
470 pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
471
472 /*
473 * Compare the file header with the loaded bits. The loader will fiddle
474 * with image base, changing it to the actual load address.
475 */
476 int rc;
477 if (!pImage->fApiSetSchemaOnlySection1)
478 {
479 rcNt = supHardNtVpReadMem(hProcess, pImage->uImageBase, pThis->abMemory, cbHdrsFile);
480 if (!NT_SUCCESS(rcNt))
481 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
482 "%s: Error reading image header from memory: %#x", pImage->pszName, rcNt);
483 if (uImageBase != pImage->uImageBase)
484 pNtHdrs->OptionalHeader.ImageBase = pImage->uImageBase;
485
486 rc = supHardNtVpFileMemCompare(pThis, pThis->abFile, pThis->abMemory, cbHeaders, pImage, 0 /*uRva*/);
487 if (RT_FAILURE(rc))
488 return rc;
489 rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
490 if (RT_FAILURE(rc))
491 return rc;
492 }
493
494 /*
495 * Save some header fields we might be using later on.
496 */
497 pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
498 pImage->fDllCharecteristics = pNtHdrs->OptionalHeader.DllCharacteristics;
499
500 /*
501 * Validate sections and check them against the mapping regions.
502 */
503 uint32_t uRva = cbHdrsFile;
504 for (uint32_t i = 0; i < cSections; i++)
505 {
506 /* Validate the section. */
507 uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
508 if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
509 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
510 "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
511 pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
512 uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
513 if (cbMap > cbImage || uRva + cbMap > cbImage)
514 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
515 "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
516 pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
517 uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
518 if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
519 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
520 "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
521 pImage->pszName, i, cbFile, cbMap, uSectRva);
522
523 /* Validate the protection. */
524 if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
525 {
526 if (pImage->fApiSetSchemaOnlySection1)
527 {
528 pImage->uImageBase -= uSectRva;
529 pImage->cbImage += uSectRva;
530 pImage->aRegions[i].uRva = uSectRva;
531 }
532
533 uint32_t fProt;
534 switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
535 {
536 case IMAGE_SCN_MEM_READ:
537 fProt = PAGE_READONLY;
538 break;
539 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
540 fProt = PAGE_READWRITE;
541 if (!suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll */
542 fProt = PAGE_READONLY;
543 break;
544 case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
545 fProt = PAGE_EXECUTE_READ;
546 break;
547 case IMAGE_SCN_MEM_EXECUTE:
548 fProt = PAGE_EXECUTE;
549 break;
550 default:
551 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
552 "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
553 pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
554
555 }
556 rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
557 if (RT_FAILURE(rc))
558 return rc;
559 }
560
561 /* Advance the RVA. */
562 uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
563 }
564
565 /*
566 * Check the mapping regions with the image to make sure someone didn't
567 * fill executable code into some gap in the image.
568 */
569 /** @todo not vital. */
570
571
572 /*
573 * Compare executable code. If we're not loaded at the link address, we
574 * need to load base relocations and apply them while making the compare.
575 * A special case
576 */
577 /** @todo not vital. */
578
579
580 return VINF_SUCCESS;
581}
582
583
584/**
585 * Verifies the signature of the given image on disk, then checks if the memory
586 * mapping matches what we verified.
587 *
588 * @returns VBox status code.
589 * @param pThis The process scanning state structure (for the
590 * two scratch buffers).
591 * @param pImage The image data collected during the address
592 * space scan.
593 * @param hProcess Handle to the process.
594 * @param hFile Handle to the image file.
595 */
596static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess)
597{
598 /*
599 * Open the image.
600 */
601 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
602 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
603
604 OBJECT_ATTRIBUTES ObjAttr;
605 InitializeObjectAttributes(&ObjAttr, &pImage->Name.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
606#ifdef IN_RING0
607 ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
608#endif
609
610 NTSTATUS rcNt = NtCreateFile(&hFile,
611 GENERIC_READ,
612 &ObjAttr,
613 &Ios,
614 NULL /* Allocation Size*/,
615 FILE_ATTRIBUTE_NORMAL,
616 FILE_SHARE_READ,
617 FILE_OPEN,
618 FILE_NON_DIRECTORY_FILE,
619 NULL /*EaBuffer*/,
620 0 /*EaLength*/);
621 if (NT_SUCCESS(rcNt))
622 rcNt = Ios.Status;
623 if (!NT_SUCCESS(rcNt))
624 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
625 "Error opening image for scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
626
627 /*
628 * Validate the signature, then make an attempt at comparing memory and
629 * disk content.
630 */
631 int rc = supHardenedWinVerifyImageByHandle(hFile, pImage->Name.UniStr.Buffer,
632 pImage->fDll ? 0 : SUPHNTVI_F_REQUIRE_BUILD_CERT,
633 NULL /*pfCacheable*/, pThis->pErrInfo);
634 if (RT_SUCCESS(rc))
635 rc = supHardNtVpVerifyImageCompareMemory(pThis, pImage, hProcess, hFile, pThis->pErrInfo);
636
637 /*
638 * Clean up and return.
639 */
640 rcNt = NtClose(hFile);
641 if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
642 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_FILE_CLOSE_ERROR,
643 "Error closing image after scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
644 return rc;
645}
646
647
648/**
649 * Verifies that there is only one thread in the process.
650 *
651 * @returns VBox status code.
652 * @param hProcess The process.
653 * @param hThread The thread.
654 * @param pErrInfo Pointer to error info structure. Optional.
655 */
656static int supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
657{
658 /*
659 * Use the ThreadAmILastThread request to check that there is only one
660 * thread in the process.
661 */
662 ULONG cbIgn = 0;
663 ULONG fAmI = 0;
664 NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
665 if (!NT_SUCCESS(rcNt))
666 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
667 "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
668 if (!fAmI)
669 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
670 "More than one thread in process");
671
672 /** @todo Would be nice to verify the relation ship between hProcess and hThread
673 * as well... */
674 return VINF_SUCCESS;
675}
676
677
678#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
679/**
680 * Verifies that there isn't a debugger attached to the process.
681 *
682 * @returns VBox status code.
683 * @param hProcess The process.
684 * @param pErrInfo Pointer to error info structure. Optional.
685 */
686static int supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
687{
688 /*
689 * Use the ProcessDebugPort request to check there is no debugger
690 * currently attached to the process.
691 */
692 ULONG cbIgn = 0;
693 uintptr_t uPtr = ~(uintptr_t)0;
694 NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
695 ProcessDebugPort,
696 &uPtr, sizeof(uPtr), &cbIgn);
697 if (!NT_SUCCESS(rcNt))
698 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
699 "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
700 if (uPtr != 0)
701 return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
702 "Debugger attached (%#zx)", uPtr);
703 return VINF_SUCCESS;
704}
705#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
706
707
708/**
709 * Allocates and initalizes a process stat structure for process virtual memory
710 * scanning.
711 *
712 * @returns Pointer to the state structure on success, NULL on failure.
713 * @param pErrInfo Pointer to error info structure. Optional.
714 */
715static PSUPHNTVPSTATE supHardNtVpCreateState(PRTERRINFO pErrInfo)
716{
717 /*
718 * Allocate the memory.
719 */
720 PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)suplibHardenedAllocZ(sizeof(*pThis));
721 if (pThis)
722 {
723 pThis->rcResult = VINF_SUCCESS;
724 pThis->pErrInfo = pErrInfo;
725 return pThis;
726 }
727 supHardNtVpSetInfo1(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
728 return NULL;
729}
730
731
732/**
733 * Matches two UNICODE_STRING structures in a case sensitive fashion.
734 *
735 * @returns true if equal, false if not.
736 * @param pUniStr1 The first unicode string.
737 * @param pUniStr2 The first unicode string.
738 */
739static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
740{
741 if (pUniStr1->Length != pUniStr2->Length)
742 return false;
743 return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
744}
745
746
747/**
748 * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
749 *
750 * @returns true / false
751 * @param pszName1 The ASCII name.
752 * @param pwszName2 The UTF-16 name.
753 */
754static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
755{
756 for (;;)
757 {
758 char ch1 = *pszName1++;
759 RTUTF16 wc2 = *pwszName2++;
760 if (ch1 != wc2)
761 {
762 ch1 = RT_C_TO_LOWER(ch1);
763 wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
764 if (ch1 != wc2)
765 return false;
766 }
767 if (!ch1)
768 return true;
769 }
770}
771
772
773/**
774 * Records an additional memory region for an image.
775 *
776 * @returns VBox status code.
777 * @param pThis The process scanning state structure.
778 * @param pImage The new image structure. Only the unicode name
779 * buffer is valid.
780 * @param pMemInfo The memory information for the image.
781 */
782static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
783{
784 /*
785 * Extract the final component.
786 */
787 unsigned cwcDirName = pImage->Name.UniStr.Length / sizeof(WCHAR);
788 PCRTUTF16 pwszFilename = &pImage->Name.UniStr.Buffer[cwcDirName];
789 while ( cwcDirName > 0
790 && pwszFilename[-1] != '\\'
791 && pwszFilename[-1] != '/'
792 && pwszFilename[-1] != ':')
793 {
794 pwszFilename--;
795 cwcDirName--;
796 }
797 if (!*pwszFilename)
798 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
799 "Empty filename (len=%u) for image at %p.", pImage->Name.UniStr.Length, pMemInfo->BaseAddress);
800
801 /*
802 * Drop trailing slashes from the directory name.
803 */
804 while ( cwcDirName > 0
805 && ( pImage->Name.UniStr.Buffer[cwcDirName - 1] == '\\'
806 || pImage->Name.UniStr.Buffer[cwcDirName - 1] == '/'))
807 cwcDirName--;
808
809 /*
810 * Match it against known DLLs.
811 */
812 pImage->pszName = NULL;
813 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
814 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
815 {
816 pImage->pszName = g_apszSupNtVpAllowedDlls[i];
817 pImage->fDll = true;
818
819 /* The directory name must match the one we've got for System32. */
820 if ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
821 || suplibHardenedMemComp(pImage->Name.UniStr.Buffer,
822 g_System32NtPath.UniStr.Buffer,
823 cwcDirName * sizeof(WCHAR)))
824 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
825 "Expected %ls to be loaded from %ls.",
826 pImage->Name.UniStr.Buffer, g_System32NtPath.UniStr.Buffer);
827
828 break;
829 }
830 if (!pImage->pszName)
831 {
832 /*
833 * Not a known DLL, executable?
834 */
835 for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
836 if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
837 {
838 pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
839 pImage->fDll = false;
840 break;
841 }
842 }
843 if (!pImage->pszName)
844 {
845 if ( pMemInfo->AllocationBase == pMemInfo->BaseAddress
846 && ( supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
847 || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
848 || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)) )
849 {
850 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
851 "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
852 "You or your admin need to add and exception to the Application and Device Control (ADC) "
853 "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
854 "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
855 , pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
856 return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
857 }
858 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
859 "Unknown image file %ls at %p.", pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
860 }
861
862 /*
863 * Since it's a new image, we expect to be at the start of the mapping now.
864 */
865 if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
866 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
867 "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
868 pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
869
870 /*
871 * Check for size/rva overflow.
872 */
873 if (pMemInfo->RegionSize >= _2G)
874 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
875 "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
876
877 /*
878 * Fill in details from the memory info.
879 */
880 pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
881 pImage->cbImage = pMemInfo->RegionSize;
882 pImage->cRegions = 1;
883 pImage->aRegions[0].uRva = 0;
884 pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
885 pImage->aRegions[0].fProt = pMemInfo->Protect;
886
887 return VINF_SUCCESS;
888}
889
890
891/**
892 * Records an additional memory region for an image.
893 *
894 * @returns VBox status code.
895 * @param pThis The process scanning state structure.
896 * @param pImage The image.
897 * @param pMemInfo The memory information for the region.
898 */
899static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
900{
901 /*
902 * Make sure the base address matches.
903 */
904 if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
905 return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
906 "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
907 pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
908 pMemInfo->BaseAddress, pMemInfo->RegionSize);
909
910 /*
911 * Check for size and rva overflows.
912 */
913 uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
914 if (pMemInfo->RegionSize >= _2G)
915 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
916 "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
917 if (uRva >= _2G)
918 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
919 "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
920
921
922 /*
923 * Record the region.
924 */
925 uint32_t iRegion = pImage->cRegions;
926 if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
927 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
928 "Too many regions for %s.", pImage->pszName);
929 pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
930 pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
931 pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
932 pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
933 pImage->cRegions++;
934
935 return VINF_SUCCESS;
936}
937
938
939/**
940 * Scans the virtual memory of the process.
941 *
942 * This collects the locations of DLLs and the EXE, and verifies that executable
943 * memory is only associated with these.
944 *
945 * @returns VBox status code.
946 * @param pThis The process scanning state structure. Details
947 * about images are added to this.
948 * @param hProcess The process to verify.
949 */
950static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
951{
952 uint32_t cXpExceptions = 0;
953 uintptr_t cbAdvance = 0;
954 uintptr_t uPtrWhere = 0;
955 for (uint32_t i = 0; i < 1024; i++)
956 {
957 SIZE_T cbActual = 0;
958 MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
959 NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
960 (void const *)uPtrWhere,
961 MemoryBasicInformation,
962 &MemInfo,
963 sizeof(MemInfo),
964 &cbActual);
965 if (!NT_SUCCESS(rcNt))
966 {
967 if (rcNt == STATUS_INVALID_PARAMETER)
968 return pThis->rcResult;
969 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
970 "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
971 }
972
973 /*
974 * Record images.
975 */
976 if ( MemInfo.Type == SEC_IMAGE
977 || MemInfo.Type == SEC_PROTECTED_IMAGE
978 || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
979 {
980 uint32_t iImg = pThis->cImages;
981 rcNt = g_pfnNtQueryVirtualMemory(hProcess,
982 (void const *)uPtrWhere,
983 MemorySectionName,
984 &pThis->aImages[iImg].Name,
985 sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
986 &cbActual);
987 if (!NT_SUCCESS(rcNt))
988 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
989 "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
990 pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
991
992 /* New or existing image? */
993 bool fNew = true;
994 uint32_t iSearch = iImg;
995 while (iSearch-- > 0)
996 if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
997 {
998 int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
999 if (RT_FAILURE(rc))
1000 return rc;
1001 fNew = false;
1002 break;
1003 }
1004 else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
1005 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
1006 "Unexpected base address match");
1007
1008 if (fNew)
1009 {
1010 int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
1011 if (RT_SUCCESS(rc))
1012 {
1013 pThis->cImages++;
1014 if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
1015 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
1016 "Internal error: aImages is full.\n");
1017 }
1018#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
1019 else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
1020 return rc;
1021#else
1022 else
1023 return rc;
1024#endif
1025 }
1026 }
1027 /*
1028 * XP, W2K3: Ignore the CSRSS read-only region as best we can.
1029 */
1030 else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1031 == PAGE_EXECUTE_READ
1032 && cXpExceptions == 0
1033 && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
1034 /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
1035 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
1036 cXpExceptions++;
1037 /*
1038 * Executable memory?
1039 */
1040 else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1041 {
1042 supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
1043 "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
1044 uPtrWhere,
1045 MemInfo.BaseAddress,
1046 MemInfo.RegionSize,
1047 MemInfo.Type,
1048 MemInfo.Protect,
1049 MemInfo.State,
1050 MemInfo.AllocationBase,
1051 MemInfo.AllocationProtect);
1052#ifdef IN_RING3
1053 /* Continue add more information about the problematic process. */
1054#else
1055 return pThis->rcResult;
1056#endif
1057 }
1058
1059 /*
1060 * Advance.
1061 */
1062 cbAdvance = MemInfo.RegionSize;
1063 if (uPtrWhere + cbAdvance <= uPtrWhere)
1064 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
1065 "Empty region at %p.", uPtrWhere);
1066 uPtrWhere += MemInfo.RegionSize;
1067 }
1068
1069 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
1070 "Too many virtual memory regions.\n");
1071}
1072
1073
1074/**
1075 * Check the integrity of the executable of the process.
1076 *
1077 * @returns VBox status code.
1078 * @param pThis The process scanning state structure. Details
1079 * about images are added to this.
1080 * @param hProcess The process to verify.
1081 */
1082static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1083{
1084 /*
1085 * Make sure there is exactly one executable image.
1086 */
1087 unsigned cExecs = 0;
1088 unsigned iExe = ~0U;
1089 unsigned i = pThis->cImages;
1090 while (i-- > 0)
1091 {
1092 if (!pThis->aImages[i].fDll)
1093 {
1094 cExecs++;
1095 iExe = i;
1096 }
1097 }
1098 if (cExecs == 0)
1099 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
1100 "No executable mapping found in the virtual address space.");
1101 if (cExecs != 1)
1102 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
1103 "Found more than one executable mapping in the virtual address space.");
1104 PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
1105
1106 /*
1107 * Check that it matches the executable image of the process.
1108 */
1109 int rc;
1110 ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
1111 PUNICODE_STRING pUniStr = (PUNICODE_STRING)suplibHardenedAllocZ(cbUniStr);
1112 if (!pUniStr)
1113 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
1114 "Error allocating %zu bytes for process name.", cbUniStr);
1115 ULONG cbIgn = 0;
1116 NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
1117 if (NT_SUCCESS(rcNt))
1118 {
1119 if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
1120 rc = VINF_SUCCESS;
1121 else
1122 {
1123 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1124 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
1125 "Process image name does not match the exectuable we found: %ls vs %ls.",
1126 pUniStr->Buffer, pImage->Name.UniStr.Buffer);
1127 }
1128 }
1129 else
1130 rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
1131 "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
1132 suplibHardenedFree(pUniStr);
1133 if (RT_FAILURE(rc))
1134 return rc;
1135
1136 /*
1137 * Validate the signing of the executable image.
1138 * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
1139 */
1140 rc = supHardNtVpVerifyImage(pThis, pImage, hProcess);
1141 if (RT_FAILURE(rc))
1142 return rc;
1143
1144 /*
1145 * Check linking requirements.
1146 */
1147 SECTION_IMAGE_INFORMATION ImageInfo;
1148 rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
1149 if (!NT_SUCCESS(rcNt))
1150 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
1151 "NtQueryInformationProcess/ProcessImageInformation failed: %#x", rcNt);
1152 if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
1153 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
1154 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
1155 ImageInfo.DllCharacteristics);
1156 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
1157 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
1158 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
1159 ImageInfo.DllCharacteristics);
1160 if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
1161 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
1162 "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
1163 ImageInfo.DllCharacteristics);
1164
1165 if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
1166 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
1167 "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
1168 ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
1169
1170 if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
1171 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
1172 "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
1173 ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
1174
1175 return VINF_SUCCESS;
1176}
1177
1178
1179/**
1180 * Check the integrity of the DLLs found in the process.
1181 *
1182 * @returns VBox status code.
1183 * @param pThis The process scanning state structure. Details
1184 * about images are added to this.
1185 * @param hProcess The process to verify.
1186 */
1187static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1188{
1189 /*
1190 * Check for duplicate entries.
1191 */
1192 uint32_t i = pThis->cImages;
1193 while (i-- > 1)
1194 {
1195 const char *pszName = pThis->aImages[i].pszName;
1196 uint32_t j = i;
1197 while (j-- > 0)
1198 if (pThis->aImages[j].pszName == pszName)
1199 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1200 "Duplicate image entries for %s: %ls and %ls",
1201 pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
1202 }
1203
1204 /*
1205 * Check that both ntdll and kernel32 are present.
1206 * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
1207 */
1208 uint32_t iNtDll = UINT32_MAX;
1209 uint32_t iKernel32 = UINT32_MAX;
1210 uint32_t iApiSetSchema = UINT32_MAX;
1211 i = pThis->cImages;
1212 while (i-- > 0)
1213 if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
1214 iNtDll = i;
1215 else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
1216 iKernel32 = i;
1217 else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "apisetschema.dll") == 0)
1218 iApiSetSchema = i;
1219 if (iNtDll == UINT32_MAX)
1220 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
1221 "The process has no NTDLL.DLL.");
1222 if (iKernel32 == UINT32_MAX)
1223 return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
1224 "The process has no KERNEL32.DLL.");
1225
1226
1227 /*
1228 * Verify that the DLLs are correctly signed (by MS).
1229 */
1230 i = pThis->cImages;
1231 while (i-- > 0)
1232 {
1233 pThis->aImages[i].fNtCreateSectionPatch = i == iNtDll;
1234 pThis->aImages[i].fApiSetSchemaOnlySection1 = i == iApiSetSchema && pThis->aImages[i].cRegions == 1;
1235
1236 int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i], hProcess);
1237 if (RT_FAILURE(rc))
1238 return rc;
1239 }
1240
1241 return VINF_SUCCESS;
1242}
1243
1244
1245/**
1246 * Verifies the given process.
1247 *
1248 * The following requirements are checked:
1249 * - The process only has one thread, the calling thread.
1250 * - The process has no debugger attached.
1251 * - The executable image of the process is verified to be signed with
1252 * certificate known to this code at build time.
1253 * - The executable image is one of a predefined set.
1254 * - The process has only a very limited set of system DLLs loaded.
1255 * - The system DLLs signatures check out fine.
1256 * - The only executable memory in the process belongs to the system DLLs and
1257 * the executable image.
1258 *
1259 * @returns VBox status code.
1260 * @param hProcess The process to verify.
1261 * @param hThread A thread in the process (the caller).
1262 * @param pErrInfo Pointer to error info structure. Optional.
1263 */
1264DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
1265{
1266 int rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
1267#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
1268 if (RT_SUCCESS(rc))
1269 rc = supHardNtVpDebugger(hProcess, pErrInfo);
1270#endif
1271 if (RT_SUCCESS(rc))
1272 {
1273 PSUPHNTVPSTATE pThis = supHardNtVpCreateState(pErrInfo);
1274 if (pThis)
1275 {
1276 rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
1277 if (RT_SUCCESS(rc))
1278 rc = supHardNtVpCheckExe(pThis, hProcess);
1279 if (RT_SUCCESS(rc))
1280 rc = supHardNtVpCheckDlls(pThis, hProcess);
1281
1282 suplibHardenedFree(pThis);
1283 }
1284 else
1285 rc = VERR_SUP_VP_NO_MEMORY_STATE;
1286 }
1287 return rc;
1288}
1289
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