VirtualBox

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

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

Fixed problem spotted by the verifier.

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