VirtualBox

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

Revision 67950, 99.7 KB checked in by vboxsync, 3 months ago (diff)

supHardNtVpNewImage: Always log the 8dot3->long conversion results. ticketref:16878

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* $Id$ */
2/** @file
3 * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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/*********************************************************************************************************************************
29*   Header Files                                                                                                                 *
30*********************************************************************************************************************************/
31#ifdef IN_RING0
32# define IPRT_NT_MAP_TO_ZW
33# include <iprt/nt/nt.h>
34# include <ntimage.h>
35#else
36# include <iprt/nt/nt-and-windows.h>
37#endif
38
39#include <VBox/sup.h>
40#include <VBox/err.h>
41#include <iprt/alloca.h>
42#include <iprt/ctype.h>
43#include <iprt/param.h>
44#include <iprt/string.h>
45#include <iprt/zero.h>
46
47#ifdef IN_RING0
48# include "SUPDrvInternal.h"
49#else
50# include "SUPLibInternal.h"
51#endif
52#include "win/SUPHardenedVerify-win.h"
53
54
55/*********************************************************************************************************************************
56*   Structures and Typedefs                                                                                                      *
57*********************************************************************************************************************************/
58/**
59 * Virtual address space region.
60 */
61typedef struct SUPHNTVPREGION
62{
63    /** The RVA of the region. */
64    uint32_t    uRva;
65    /** The size of the region. */
66    uint32_t    cb;
67    /** The protection of the region. */
68    uint32_t    fProt;
69} SUPHNTVPREGION;
70/** Pointer to a virtual address space region. */
71typedef SUPHNTVPREGION *PSUPHNTVPREGION;
72
73/**
74 * Virtual address space image information.
75 */
76typedef struct SUPHNTVPIMAGE
77{
78    /** The base address of the image. */
79    uintptr_t       uImageBase;
80    /** The size of the image mapping. */
81    uintptr_t       cbImage;
82
83    /** The name from the allowed lists. */
84    const char     *pszName;
85    /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
86    struct
87    {
88        /** The full unicode name. */
89        UNICODE_STRING  UniStr;
90        /** Buffer space. */
91        WCHAR           awcBuffer[260];
92    } Name;
93
94    /** The number of mapping regions. */
95    uint32_t        cRegions;
96    /** Mapping regions. */
97    SUPHNTVPREGION  aRegions[16];
98
99    /** The image characteristics from the FileHeader. */
100    uint16_t        fImageCharecteristics;
101    /** The DLL characteristics from the OptionalHeader. */
102    uint16_t        fDllCharecteristics;
103
104    /** Set if this is the DLL. */
105    bool            fDll;
106    /** Set if the image is NTDLL an the verficiation code needs to watch out for
107     *  the NtCreateSection patch. */
108    bool            fNtCreateSectionPatch;
109    /** Whether the API set schema hack needs to be applied when verifying memory
110     * content.  The hack means that we only check if the 1st section is mapped. */
111    bool            fApiSetSchemaOnlySection1;
112    /** This may be a 32-bit resource DLL. */
113    bool            f32bitResourceDll;
114
115    /** Pointer to the loader cache entry for the image. */
116    PSUPHNTLDRCACHEENTRY    pCacheEntry;
117#ifdef IN_RING0
118    /** In ring-0 we don't currently cache images, so put it here. */
119    SUPHNTLDRCACHEENTRY     CacheEntry;
120#endif
121} SUPHNTVPIMAGE;
122/** Pointer to image info from the virtual address space scan. */
123typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
124
125/**
126 * Virtual address space scanning state.
127 */
128typedef struct SUPHNTVPSTATE
129{
130    /** Type of verification to perform. */
131    SUPHARDNTVPKIND         enmKind;
132    /** Combination of SUPHARDNTVP_F_XXX. */
133    uint32_t                fFlags;
134    /** The result. */
135    int                     rcResult;
136    /** Number of fixes we've done.
137     * Only applicable in the purification modes.  */
138    uint32_t                cFixes;
139    /** Number of images in aImages. */
140    uint32_t                cImages;
141    /** The index of the last image we looked up. */
142    uint32_t                iImageHint;
143    /** The process handle. */
144    HANDLE                  hProcess;
145    /** Images found in the process.
146     * The array is large enough to hold the executable, all allowed DLLs, and one
147     * more so we can get the image name of the first unwanted DLL. */
148    SUPHNTVPIMAGE           aImages[1 + 6 + 1
149#ifdef VBOX_PERMIT_VERIFIER_DLL
150                                    + 1
151#endif
152#ifdef VBOX_PERMIT_MORE
153                                    + 5
154#endif
155#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
156                                    + 16
157#endif
158                                   ];
159    /** Memory compare scratch buffer.*/
160    uint8_t                 abMemory[_4K];
161    /** File compare scratch buffer.*/
162    uint8_t                 abFile[_4K];
163    /** Section headers for use when comparing file and loaded image. */
164    IMAGE_SECTION_HEADER    aSecHdrs[16];
165    /** Pointer to the error info. */
166    PRTERRINFO              pErrInfo;
167} SUPHNTVPSTATE;
168/** Pointer to stat information of a virtual address space scan. */
169typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
170
171
172/*********************************************************************************************************************************
173*   Global Variables                                                                                                             *
174*********************************************************************************************************************************/
175/**
176 * System DLLs allowed to be loaded into the process.
177 * @remarks supHardNtVpCheckDlls assumes these are lower case.
178 */
179static const char *g_apszSupNtVpAllowedDlls[] =
180{
181    "ntdll.dll",
182    "kernel32.dll",
183    "kernelbase.dll",
184    "apphelp.dll",
185    "apisetschema.dll",
186#ifdef VBOX_PERMIT_VERIFIER_DLL
187    "verifier.dll",
188#endif
189#ifdef VBOX_PERMIT_MORE
190# define VBOX_PERMIT_MORE_FIRST_IDX 5
191    "sfc.dll",
192    "sfc_os.dll",
193    "user32.dll",
194    "acres.dll",
195    "acgenral.dll",
196#endif
197#ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
198    "psapi.dll",
199    "msvcrt.dll",
200    "advapi32.dll",
201    "sechost.dll",
202    "rpcrt4.dll",
203    "SamplingRuntime.dll",
204#endif
205};
206
207/**
208 * VBox executables allowed to start VMs.
209 * @remarks Remember to keep in sync with g_aSupInstallFiles in
210 *          SUPR3HardenedVerify.cpp.
211 */
212static const char *g_apszSupNtVpAllowedVmExes[] =
213{
214    "VBoxHeadless.exe",
215    "VirtualBox.exe",
216    "VBoxSDL.exe",
217    "VBoxNetDHCP.exe",
218    "VBoxNetNAT.exe",
219    "VBoxVMMPreload.exe",
220
221    "tstMicro.exe",
222    "tstPDMAsyncCompletion.exe",
223    "tstPDMAsyncCompletionStress.exe",
224    "tstVMM.exe",
225    "tstVMREQ.exe",
226    "tstCFGM.exe",
227    "tstGIP-2.exe",
228    "tstIntNet-1.exe",
229    "tstMMHyperHeap.exe",
230    "tstRTR0ThreadPreemptionDriver.exe",
231    "tstRTR0MemUserKernelDriver.exe",
232    "tstRTR0SemMutexDriver.exe",
233    "tstRTR0TimerDriver.exe",
234    "tstSSM.exe",
235};
236
237/** Pointer to NtQueryVirtualMemory.  Initialized by SUPDrv-win.cpp in
238 *  ring-0, in ring-3 it's just a slightly confusing define. */
239#ifdef IN_RING0
240PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
241#else
242# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
243#endif
244
245#ifdef IN_RING3
246/** The number of valid entries in the loader cache. */
247static uint32_t                 g_cSupNtVpLdrCacheEntries = 0;
248/** The loader cache entries. */
249static SUPHNTLDRCACHEENTRY      g_aSupNtVpLdrCacheEntries[RT_ELEMENTS(g_apszSupNtVpAllowedDlls) + 1 + 3];
250#endif
251
252
253/**
254 * Fills in error information.
255 *
256 * @returns @a rc.
257 * @param   pErrInfo            Pointer to the extended error info structure.
258 *                              Can be NULL.
259 * @param   rc                  The status to return.
260 * @param   pszMsg              The format string for the message.
261 * @param   ...                 The arguments for the format string.
262 */
263static int supHardNtVpSetInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
264{
265    va_list va;
266#ifdef IN_RING3
267    va_start(va, pszMsg);
268    supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
269    va_end(va);
270#endif
271
272    va_start(va, pszMsg);
273    RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
274    va_end(va);
275
276    return rc;
277}
278
279
280/**
281 * Adds error information.
282 *
283 * @returns @a rc.
284 * @param   pErrInfo            Pointer to the extended error info structure
285 *                              which may contain some details already.  Can be
286 *                              NULL.
287 * @param   rc                  The status to return.
288 * @param   pszMsg              The format string for the message.
289 * @param   ...                 The arguments for the format string.
290 */
291static int supHardNtVpAddInfo1(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
292{
293    va_list va;
294#ifdef IN_RING3
295    va_start(va, pszMsg);
296    if (pErrInfo && pErrInfo->pszMsg)
297        supR3HardenedError(rc, false /*fFatal*/, "%N - %s\n", pszMsg, &va, pErrInfo->pszMsg);
298    else
299        supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
300    va_end(va);
301#endif
302
303    va_start(va, pszMsg);
304    RTErrInfoAddV(pErrInfo, rc, pszMsg, va);
305    va_end(va);
306
307    return rc;
308}
309
310
311/**
312 * Fills in error information.
313 *
314 * @returns @a rc.
315 * @param   pThis               The process validator instance.
316 * @param   rc                  The status to return.
317 * @param   pszMsg              The format string for the message.
318 * @param   ...                 The arguments for the format string.
319 */
320static int supHardNtVpSetInfo2(PSUPHNTVPSTATE pThis, int rc, const char *pszMsg, ...)
321{
322    va_list va;
323#ifdef IN_RING3
324    va_start(va, pszMsg);
325    supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
326    va_end(va);
327#endif
328
329    va_start(va, pszMsg);
330#ifdef IN_RING0
331    RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
332    pThis->rcResult = rc;
333#else
334    if (RT_SUCCESS(pThis->rcResult))
335    {
336        RTErrInfoSetV(pThis->pErrInfo, rc, pszMsg, va);
337        pThis->rcResult = rc;
338    }
339    else
340    {
341        RTErrInfoAddF(pThis->pErrInfo, rc, " \n[rc=%d] ", rc);
342        RTErrInfoAddV(pThis->pErrInfo, rc, pszMsg, va);
343    }
344#endif
345    va_end(va);
346
347    return pThis->rcResult;
348}
349
350
351static int supHardNtVpReadImage(PSUPHNTVPIMAGE pImage, uint64_t off, void *pvBuf, size_t cbRead)
352{
353    return pImage->pCacheEntry->pNtViRdr->Core.pfnRead(&pImage->pCacheEntry->pNtViRdr->Core, pvBuf, cbRead, off);
354}
355
356
357static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
358{
359#ifdef IN_RING0
360    /* ASSUMES hProcess is the current process. */
361    RT_NOREF1(hProcess);
362    /** @todo use MmCopyVirtualMemory where available! */
363    int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
364    if (RT_SUCCESS(rc))
365        return STATUS_SUCCESS;
366    return STATUS_ACCESS_DENIED;
367#else
368    SIZE_T cbIgn;
369    NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
370    if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
371        rcNt = STATUS_IO_DEVICE_ERROR;
372    return rcNt;
373#endif
374}
375
376
377#ifdef IN_RING3
378static NTSTATUS supHardNtVpFileMemRestore(PSUPHNTVPSTATE pThis, PVOID pvRestoreAddr, uint8_t const *pbFile, uint32_t cbToRestore,
379                                          uint32_t fCorrectProtection)
380{
381    PVOID  pvProt   = pvRestoreAddr;
382    SIZE_T cbProt   = cbToRestore;
383    ULONG  fOldProt = 0;
384    NTSTATUS rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_READWRITE, &fOldProt);
385    if (NT_SUCCESS(rcNt))
386    {
387        SIZE_T cbIgnored;
388        rcNt = NtWriteVirtualMemory(pThis->hProcess, pvRestoreAddr, pbFile, cbToRestore, &cbIgnored);
389
390        pvProt = pvRestoreAddr;
391        cbProt = cbToRestore;
392        NTSTATUS rcNt2 = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fCorrectProtection, &fOldProt);
393        if (NT_SUCCESS(rcNt))
394            rcNt = rcNt2;
395    }
396    pThis->cFixes++;
397    return rcNt;
398}
399#endif /* IN_RING3 */
400
401
402typedef struct SUPHNTVPSKIPAREA
403{
404    uint32_t uRva;
405    uint32_t cb;
406} SUPHNTVPSKIPAREA;
407typedef SUPHNTVPSKIPAREA *PSUPHNTVPSKIPAREA;
408
409static int supHardNtVpFileMemCompareSection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
410                                            uint32_t uRva, uint32_t cb, const uint8_t *pbFile,
411                                            int32_t iSh, PSUPHNTVPSKIPAREA paSkipAreas, uint32_t cSkipAreas,
412                                            uint32_t fCorrectProtection)
413{
414#ifndef IN_RING3
415    RT_NOREF1(fCorrectProtection);
416#endif
417    AssertCompileAdjacentMembers(SUPHNTVPSTATE, abMemory, abFile); /* Use both the memory and file buffers here. Parfait might hate me for this... */
418    uint32_t  const cbMemory = sizeof(pThis->abMemory) + sizeof(pThis->abFile);
419    uint8_t * const pbMemory = &pThis->abMemory[0];
420
421    while (cb > 0)
422    {
423        uint32_t cbThis = RT_MIN(cb, cbMemory);
424
425        /* Clipping. */
426        uint32_t uNextRva = uRva + cbThis;
427        if (cSkipAreas)
428        {
429            uint32_t uRvaEnd = uNextRva;
430            uint32_t i = cSkipAreas;
431            while (i-- > 0)
432            {
433                uint32_t uSkipEnd = paSkipAreas[i].uRva + paSkipAreas[i].cb;
434                if (   uRva    < uSkipEnd
435                    && uRvaEnd > paSkipAreas[i].uRva)
436                {
437                    if (uRva < paSkipAreas[i].uRva)
438                    {
439                        cbThis   = paSkipAreas[i].uRva - uRva;
440                        uRvaEnd  = paSkipAreas[i].uRva;
441                        uNextRva = uSkipEnd;
442                    }
443                    else if (uRvaEnd >= uSkipEnd)
444                    {
445                        cbThis  -= uSkipEnd - uRva;
446                        pbFile  += uSkipEnd - uRva;
447                        uRva     = uSkipEnd;
448                    }
449                    else
450                    {
451                        uNextRva = uSkipEnd;
452                        cbThis   = 0;
453                        break;
454                    }
455                }
456            }
457        }
458
459        /* Read the memory. */
460        NTSTATUS rcNt = supHardNtVpReadMem(pThis->hProcess, pImage->uImageBase + uRva, pbMemory, cbThis);
461        if (!NT_SUCCESS(rcNt))
462            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_READ_ERROR,
463                                       "%s: Error reading %#x bytes at %p (rva %#x, #%u, %.8s) from memory: %#x",
464                                       pImage->pszName, cbThis, pImage->uImageBase + uRva, uRva, iSh + 1,
465                                       iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers", rcNt);
466
467        /* Do the compare. */
468        if (memcmp(pbFile, pbMemory, cbThis) != 0)
469        {
470            const char *pachSectNm = iSh >= 0 ? (char *)pThis->aSecHdrs[iSh].Name : "headers";
471            SUP_DPRINTF(("%s: Differences in section #%u (%s) between file and memory:\n", pImage->pszName, iSh + 1, pachSectNm));
472
473            uint32_t off = 0;
474            while (off < cbThis && pbFile[off] == pbMemory[off])
475                off++;
476            SUP_DPRINTF(("  %p / %#09x: %02x != %02x\n",
477                         pImage->uImageBase + uRva + off, uRva + off, pbFile[off], pbMemory[off]));
478            uint32_t offLast = off;
479            uint32_t cDiffs  = 1;
480            for (uint32_t off2 = off + 1; off2 < cbThis; off2++)
481                if (pbFile[off2] != pbMemory[off2])
482                {
483                    SUP_DPRINTF(("  %p / %#09x: %02x != %02x\n",
484                                 pImage->uImageBase + uRva + off2, uRva + off2, pbFile[off2], pbMemory[off2]));
485                    cDiffs++;
486                    offLast = off2;
487                }
488
489#ifdef IN_RING3
490            if (   pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
491                || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
492            {
493                PVOID pvRestoreAddr = (uint8_t *)pImage->uImageBase + uRva;
494                rcNt = supHardNtVpFileMemRestore(pThis, pvRestoreAddr, pbFile, cbThis, fCorrectProtection);
495                if (NT_SUCCESS(rcNt))
496                    SUP_DPRINTF(("  Restored %#x bytes of original file content at %p\n", cbThis, pvRestoreAddr));
497                else
498                    return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
499                                               "%s: Failed to restore %#x bytes at %p (%#x, #%u, %s): %#x (cDiffs=%#x, first=%#x)",
500                                               pImage->pszName, cbThis, pvRestoreAddr, uRva, iSh + 1, pachSectNm, rcNt,
501                                               cDiffs, uRva + off);
502            }
503            else
504#endif /* IN_RING3 */
505                return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
506                                           "%s: %u differences between %#x and %#x in #%u (%.8s), first: %02x != %02x",
507                                           pImage->pszName, cDiffs, uRva + off, uRva + offLast, iSh + 1,
508                                           pachSectNm, pbFile[off], pbMemory[off]);
509        }
510
511        /* Advance. The clipping makes it a little bit complicated. */
512        cbThis  = uNextRva - uRva;
513        if (cbThis >= cb)
514            break;
515        cb     -= cbThis;
516        pbFile += cbThis;
517        uRva    = uNextRva;
518    }
519    return VINF_SUCCESS;
520}
521
522
523
524static int supHardNtVpCheckSectionProtection(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage,
525                                             uint32_t uRva, uint32_t cb, uint32_t fProt)
526{
527    uint32_t const cbOrg = cb;
528    if (!cb)
529        return VINF_SUCCESS;
530    if (   pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION
531        || pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
532        return VINF_SUCCESS;
533
534    for (uint32_t i = 0; i < pImage->cRegions; i++)
535    {
536        uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
537        if (offRegion < pImage->aRegions[i].cb)
538        {
539            uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
540            if (   pImage->aRegions[i].fProt != fProt
541                && (   fProt != PAGE_READWRITE
542                    || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
543                return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
544                                           "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
545                                           pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
546            if (cbLeft >= cb)
547                return VINF_SUCCESS;
548            cb   -= cbLeft;
549            uRva += cbLeft;
550
551#if 0 /* This shouldn't ever be necessary. */
552            if (   i + 1 < pImage->cRegions
553                && uRva < pImage->aRegions[i + 1].uRva)
554            {
555                cbLeft = pImage->aRegions[i + 1].uRva - uRva;
556                if (cbLeft >= cb)
557                    return VINF_SUCCESS;
558                cb   -= cbLeft;
559                uRva += cbLeft;
560            }
561#endif
562        }
563    }
564
565    return supHardNtVpSetInfo2(pThis, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
566                               "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
567}
568
569
570DECLINLINE(bool) supHardNtVpIsModuleNameMatch(PSUPHNTVPIMAGE pImage, const char *pszModule)
571{
572    if (pImage->fDll)
573    {
574        const char *pszImageNm = pImage->pszName;
575        for (;;)
576        {
577            char chLeft  = *pszImageNm++;
578            char chRight = *pszModule++;
579            if (chLeft != chRight)
580            {
581                Assert(chLeft == RT_C_TO_LOWER(chLeft));
582                if (chLeft != RT_C_TO_LOWER(chRight))
583                {
584                    if (   chRight == '\0'
585                        && chLeft  == '.'
586                        && pszImageNm[0] == 'd'
587                        && pszImageNm[1] == 'l'
588                        && pszImageNm[2] == 'l'
589                        && pszImageNm[3] == '\0')
590                        return true;
591                    break;
592                }
593            }
594
595            if (chLeft == '\0')
596                return true;
597        }
598    }
599
600    return false;
601}
602
603
604/**
605 * Worker for supHardNtVpGetImport that looks up a module in the module table.
606 *
607 * @returns Pointer to the module if found, NULL if not found.
608 * @param   pThis               The process validator instance.
609 * @param   pszModule           The name of the module we're looking for.
610 */
611static PSUPHNTVPIMAGE supHardNtVpFindModule(PSUPHNTVPSTATE pThis, const char *pszModule)
612{
613    /*
614     * Check out the hint first.
615     */
616    if (   pThis->iImageHint < pThis->cImages
617        && supHardNtVpIsModuleNameMatch(&pThis->aImages[pThis->iImageHint], pszModule))
618        return &pThis->aImages[pThis->iImageHint];
619
620    /*
621     * Linear array search next.
622     */
623    uint32_t i = pThis->cImages;
624    while (i-- > 0)
625        if (supHardNtVpIsModuleNameMatch(&pThis->aImages[i], pszModule))
626        {
627            pThis->iImageHint = i;
628            return &pThis->aImages[i];
629        }
630
631    /* No cigar. */
632    return NULL;
633}
634
635
636/**
637 * @callback_method_impl{FNRTLDRIMPORT}
638 */
639static DECLCALLBACK(int) supHardNtVpGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol,
640                                              PRTLDRADDR pValue, void *pvUser)
641{
642    RT_NOREF1(hLdrMod);
643    /*SUP_DPRINTF(("supHardNtVpGetImport: %s / %#x / %s.\n", pszModule, uSymbol, pszSymbol));*/
644    PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)pvUser;
645
646    int rc = VERR_MODULE_NOT_FOUND;
647    PSUPHNTVPIMAGE pImage = supHardNtVpFindModule(pThis, pszModule);
648    if (pImage)
649    {
650        rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
651                              pImage->uImageBase, uSymbol, pszSymbol, pValue);
652        if (RT_SUCCESS(rc))
653            return rc;
654    }
655    /*
656     * API set hacks.
657     */
658    else if (!RTStrNICmp(pszModule, RT_STR_TUPLE("api-ms-win-")))
659    {
660        static const char * const s_apszDlls[] = { "ntdll.dll", "kernelbase.dll", "kernel32.dll" };
661        for (uint32_t i = 0; i < RT_ELEMENTS(s_apszDlls); i++)
662        {
663            pImage = supHardNtVpFindModule(pThis, s_apszDlls[i]);
664            if (pImage)
665            {
666                rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
667                                      pImage->uImageBase, uSymbol, pszSymbol, pValue);
668                if (RT_SUCCESS(rc))
669                    return rc;
670                if (rc != VERR_SYMBOL_NOT_FOUND)
671                    break;
672            }
673        }
674    }
675
676    /*
677     * Deal with forwarders.
678     * ASSUMES no forwarders thru any api-ms-win-core-*.dll.
679     * ASSUMES forwarders are resolved after one redirection.
680     */
681    if (rc == VERR_LDR_FORWARDER)
682    {
683        size_t           cbInfo = RT_MIN((uint32_t)*pValue, sizeof(RTLDRIMPORTINFO) + 32);
684        PRTLDRIMPORTINFO pInfo  = (PRTLDRIMPORTINFO)alloca(cbInfo);
685        rc = RTLdrQueryForwarderInfo(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
686                                     uSymbol, pszSymbol, pInfo, cbInfo);
687        if (RT_SUCCESS(rc))
688        {
689            rc = VERR_MODULE_NOT_FOUND;
690            pImage = supHardNtVpFindModule(pThis, pInfo->szModule);
691            if (pImage)
692            {
693                rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pImage->pCacheEntry->pbBits,
694                                      pImage->uImageBase, pInfo->iOrdinal, pInfo->pszSymbol, pValue);
695                if (RT_SUCCESS(rc))
696                    return rc;
697
698                SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol '%s' in '%s' (forwarded from %s / %s): %Rrc\n",
699                             pInfo->pszSymbol, pInfo->szModule, pszModule, pszSymbol, rc));
700                if (rc == VERR_LDR_FORWARDER)
701                    rc = VERR_LDR_FORWARDER_CHAIN_TOO_LONG;
702            }
703            else
704                SUP_DPRINTF(("supHardNtVpGetImport: Failed to find forwarder module '%s' (%#x / %s; originally %s / %#x / %s): %Rrc\n",
705                             pInfo->szModule, pInfo->iOrdinal, pInfo->pszSymbol, pszModule, uSymbol, pszSymbol, rc));
706        }
707        else
708            SUP_DPRINTF(("supHardNtVpGetImport: RTLdrQueryForwarderInfo failed on symbol %#x/'%s' in '%s': %Rrc\n",
709                         uSymbol, pszSymbol, pszModule, rc));
710    }
711    else
712        SUP_DPRINTF(("supHardNtVpGetImport: Failed to find symbol %#x / '%s' in '%s': %Rrc\n",
713                     uSymbol, pszSymbol, pszModule, rc));
714    return rc;
715}
716
717
718/**
719 * Compares process memory with the disk content.
720 *
721 * @returns VBox status code.
722 * @param   pThis               The process scanning state structure (for the
723 *                              two scratch buffers).
724 * @param   pImage              The image data collected during the address
725 *                              space scan.
726 */
727static int supHardNtVpVerifyImageMemoryCompare(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
728{
729
730    /*
731     * Read and find the file headers.
732     */
733    int rc = supHardNtVpReadImage(pImage, 0 /*off*/, pThis->abFile, sizeof(pThis->abFile));
734    if (RT_FAILURE(rc))
735        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
736                                   "%s: Error reading image header: %Rrc", pImage->pszName, rc);
737
738    uint32_t offNtHdrs = 0;
739    PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
740    if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
741    {
742        offNtHdrs = pDosHdr->e_lfanew;
743        if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
744            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_MZ_OFFSET,
745                                       "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
746    }
747    PIMAGE_NT_HEADERS   pNtHdrs   = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
748    PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)pNtHdrs;
749    if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
750        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
751                                   "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
752
753    /*
754     * Do basic header validation.
755     */
756#ifdef RT_ARCH_AMD64
757    if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && !pImage->f32bitResourceDll)
758#else
759    if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
760#endif
761        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
762                                   "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
763    bool const fIs32Bit = pNtHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
764
765    if (pNtHdrs->FileHeader.SizeOfOptionalHeader != (fIs32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
766        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
767                                   "%s: Unexpected optional header size: %#x",
768                                   pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
769
770    if (pNtHdrs->OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
771        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
772                                   "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
773
774    uint32_t cDirs = (fIs32Bit ? pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes : pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
775    if (cDirs != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
776        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
777                                   "%s: Unexpected data dirs: %#x", pImage->pszName, cDirs);
778
779    /*
780     * Before we start comparing things, store what we need to know from the headers.
781     */
782    uint32_t  const cSections  = pNtHdrs->FileHeader.NumberOfSections;
783    if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
784        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_SECTIONS,
785                                   "%s: Too many section headers: %#x", pImage->pszName, cSections);
786    suplibHardenedMemCopy(pThis->aSecHdrs, (fIs32Bit ? (void *)(pNtHdrs32 + 1) : (void *)(pNtHdrs + 1)),
787                          cSections * sizeof(IMAGE_SECTION_HEADER));
788
789    uintptr_t const uImageBase = fIs32Bit ? pNtHdrs32->OptionalHeader.ImageBase : pNtHdrs->OptionalHeader.ImageBase;
790    if (uImageBase & PAGE_OFFSET_MASK)
791        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_BASE,
792                                   "%s: Invalid image base: %p", pImage->pszName, uImageBase);
793
794    uint32_t  const cbImage    = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfImage : pNtHdrs->OptionalHeader.SizeOfImage;
795    if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
796        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
797                                   "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
798                                   pImage->pszName, cbImage, pImage->cbImage);
799    if (cbImage != RTLdrSize(pImage->pCacheEntry->hLdrMod))
800        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_IMAGE_SIZE,
801                                   "%s: SizeOfImage (%#x) differs from what RTLdrSize returns (%#zx)",
802                                   pImage->pszName, cbImage, RTLdrSize(pImage->pCacheEntry->hLdrMod));
803
804    uint32_t const cbSectAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.SectionAlignment : pNtHdrs->OptionalHeader.SectionAlignment;
805    if (   !RT_IS_POWER_OF_TWO(cbSectAlign)
806        || cbSectAlign < PAGE_SIZE
807        || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
808        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
809                                   "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
810
811    uint32_t const cbFileAlign = fIs32Bit ? pNtHdrs32->OptionalHeader.FileAlignment : pNtHdrs->OptionalHeader.FileAlignment;
812    if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
813        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
814                                   "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
815                                   pImage->pszName, cbFileAlign, cbSectAlign);
816
817    uint32_t  const cbHeaders  = fIs32Bit ? pNtHdrs32->OptionalHeader.SizeOfHeaders : pNtHdrs->OptionalHeader.SizeOfHeaders;
818    uint32_t  const cbMinHdrs  = offNtHdrs + (fIs32Bit ? sizeof(*pNtHdrs32) : sizeof(*pNtHdrs) )
819                               + sizeof(IMAGE_SECTION_HEADER) * cSections;
820    if (cbHeaders < cbMinHdrs)
821        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
822                                   "%s: Headers are too small: %#x < %#x (cSections=%#x)",
823                                   pImage->pszName, cbHeaders, cbMinHdrs, cSections);
824    uint32_t  const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
825    if (cbHdrsFile > sizeof(pThis->abFile))
826        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
827                                   "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
828                                   pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
829
830    /*
831     * Save some header fields we might be using later on.
832     */
833    pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
834    pImage->fDllCharecteristics   = fIs32Bit ? pNtHdrs32->OptionalHeader.DllCharacteristics : pNtHdrs->OptionalHeader.DllCharacteristics;
835
836    /*
837     * Correct the apisetschema image base, size and region rva.
838     */
839    if (pImage->fApiSetSchemaOnlySection1)
840    {
841        pImage->uImageBase      -= pThis->aSecHdrs[0].VirtualAddress;
842        pImage->cbImage         += pThis->aSecHdrs[0].VirtualAddress;
843        pImage->aRegions[0].uRva = pThis->aSecHdrs[0].VirtualAddress;
844    }
845
846    /*
847     * Get relocated bits.
848     */
849    uint8_t *pbBits;
850    if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
851        rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
852                                           pThis->pErrInfo);
853    else
854        rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
855                                           pThis->pErrInfo);
856    if (RT_FAILURE(rc))
857        return rc;
858
859    /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
860    if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
861    {
862        if (fIs32Bit)
863            ((PIMAGE_NT_HEADERS32)&pbBits[offNtHdrs])->OptionalHeader.ImageBase = (uint32_t)pImage->uImageBase;
864        else
865            ((PIMAGE_NT_HEADERS)&pbBits[offNtHdrs])->OptionalHeader.ImageBase   = pImage->uImageBase;
866    }
867
868    /*
869     * Figure out areas we should skip during comparison.
870     */
871    uint32_t         cSkipAreas = 0;
872    SUPHNTVPSKIPAREA aSkipAreas[5];
873    if (pImage->fNtCreateSectionPatch)
874    {
875        RTLDRADDR uValue;
876        if (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY)
877        {
878            /* Ignore our NtCreateSection hack. */
879            rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "NtCreateSection", &uValue);
880            if (RT_FAILURE(rc))
881                return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'NtCreateSection': %Rrc", pImage->pszName, rc);
882            aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
883            aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
884
885            /* Ignore our LdrLoadDll hack. */
886            rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrLoadDll", &uValue);
887            if (RT_FAILURE(rc))
888                return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrLoadDll': %Rrc", pImage->pszName, rc);
889            aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
890            aSkipAreas[cSkipAreas++].cb = ARCH_BITS == 32 ? 5 : 12;
891        }
892
893        /* Ignore our patched LdrInitializeThunk hack. */
894        rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
895        if (RT_FAILURE(rc))
896            return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
897        aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
898        aSkipAreas[cSkipAreas++].cb = 14;
899
900        /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
901        rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "LdrSystemDllInitBlock", &uValue);
902        if (RT_SUCCESS(rc))
903        {
904            aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
905            aSkipAreas[cSkipAreas++].cb = RT_MAX(pbBits[(uint32_t)uValue], 0x50);
906        }
907
908        Assert(cSkipAreas <= RT_ELEMENTS(aSkipAreas));
909    }
910
911    /*
912     * Compare the file header with the loaded bits.  The loader will fiddle
913     * with image base, changing it to the actual load address.
914     */
915    if (!pImage->fApiSetSchemaOnlySection1)
916    {
917        rc = supHardNtVpFileMemCompareSection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, pbBits, -1, NULL, 0, PAGE_READONLY);
918        if (RT_FAILURE(rc))
919            return rc;
920
921        rc = supHardNtVpCheckSectionProtection(pThis, pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY);
922        if (RT_FAILURE(rc))
923            return rc;
924    }
925
926    /*
927     * Validate sections:
928     *      - Check them against the mapping regions.
929     *      - Check section bits according to enmKind.
930     */
931    uint32_t fPrevProt = PAGE_READONLY;
932    uint32_t uRva      = cbHdrsFile;
933    for (uint32_t i = 0; i < cSections; i++)
934    {
935        /* Validate the section. */
936        uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
937        if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
938            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_RVA,
939                                       "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
940                                       pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
941        uint32_t cbMap  = pThis->aSecHdrs[i].Misc.VirtualSize;
942        if (cbMap > cbImage || uRva + cbMap > cbImage)
943            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
944                                       "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
945                                       pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
946        uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
947        if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
948            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
949                                       "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
950                                       pImage->pszName, i, cbFile, cbMap, uSectRva);
951
952        /* Validate the protection and bits. */
953        if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
954        {
955            uint32_t fProt;
956            switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
957            {
958                case IMAGE_SCN_MEM_READ:
959                    fProt = PAGE_READONLY;
960                    break;
961                case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
962                    fProt = PAGE_READWRITE;
963                    if (   pThis->enmKind != SUPHARDNTVPKIND_VERIFY_ONLY
964                        && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION
965                        && !suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll. Changed by proc init. */
966                        fProt = PAGE_READONLY;
967                    break;
968                case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
969                    fProt = PAGE_EXECUTE_READ;
970                    break;
971                case IMAGE_SCN_MEM_EXECUTE:
972                    fProt = PAGE_EXECUTE;
973                    break;
974                case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
975                    /* Only the executable is allowed to have this section,
976                       and it's protected after we're done patching. */
977                    if (!pImage->fDll)
978                    {
979                        if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
980                            fProt = PAGE_EXECUTE_READWRITE;
981                        else
982                            fProt = PAGE_EXECUTE_READ;
983                        break;
984                    }
985                default:
986                    return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
987                                               "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
988                                               pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
989            }
990
991            /* The section bits. Child purification verifies all, normal
992               verification verifies all except where the executable is
993               concerned (due to opening vboxdrv during early process init). */
994            if (   (   (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
995                    && !(pThis->aSecHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
996                || (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == IMAGE_SCN_MEM_READ
997                || (pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY && pImage->fDll)
998                || pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
999            {
1000                rc = VINF_SUCCESS;
1001                if (uRva < uSectRva && !pImage->fApiSetSchemaOnlySection1) /* Any gap worth checking? */
1002                    rc = supHardNtVpFileMemCompareSection(pThis, pImage, uRva, uSectRva - uRva, pbBits + uRva,
1003                                                          i - 1, NULL, 0, fPrevProt);
1004                if (RT_SUCCESS(rc))
1005                    rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva, cbMap, pbBits + uSectRva,
1006                                                          i, aSkipAreas, cSkipAreas, fProt);
1007                if (RT_SUCCESS(rc))
1008                {
1009                    uint32_t cbMapAligned = i + 1 < cSections && !pImage->fApiSetSchemaOnlySection1
1010                                          ? RT_ALIGN_32(cbMap, cbSectAlign) : RT_ALIGN_32(cbMap, PAGE_SIZE);
1011                    if (cbMapAligned > cbMap)
1012                        rc = supHardNtVpFileMemCompareSection(pThis, pImage, uSectRva + cbMap, cbMapAligned - cbMap,
1013                                                              g_abRTZeroPage, i, NULL, 0, fProt);
1014                }
1015                if (RT_FAILURE(rc))
1016                    return rc;
1017            }
1018
1019            /* The protection (must be checked afterwards!). */
1020            rc = supHardNtVpCheckSectionProtection(pThis, pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt);
1021            if (RT_FAILURE(rc))
1022                return rc;
1023
1024            fPrevProt = fProt;
1025        }
1026
1027        /* Advance the RVA. */
1028        uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
1029    }
1030
1031    return VINF_SUCCESS;
1032}
1033
1034
1035/**
1036 * Verifies the signature of the given image on disk, then checks if the memory
1037 * mapping matches what we verified.
1038 *
1039 * @returns VBox status code.
1040 * @param   pThis               The process scanning state structure (for the
1041 *                              two scratch buffers).
1042 * @param   pImage              The image data collected during the address
1043 *                              space scan.
1044 */
1045static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage)
1046{
1047    /*
1048     * Validate the file signature first, then do the memory compare.
1049     */
1050    int rc;
1051    if (   pImage->pCacheEntry != NULL
1052        && pImage->pCacheEntry->hLdrMod != NIL_RTLDRMOD)
1053    {
1054        rc = supHardNtLdrCacheEntryVerify(pImage->pCacheEntry, pImage->Name.UniStr.Buffer, pThis->pErrInfo);
1055        if (RT_SUCCESS(rc))
1056            rc = supHardNtVpVerifyImageMemoryCompare(pThis, pImage);
1057    }
1058    else
1059        rc = supHardNtVpSetInfo2(pThis, VERR_OPEN_FAILED, "pCacheEntry/hLdrMod is NIL! Impossible!");
1060    return rc;
1061}
1062
1063
1064/**
1065 * Verifies that there is only one thread in the process.
1066 *
1067 * @returns VBox status code.
1068 * @param   hProcess            The process.
1069 * @param   hThread             The thread.
1070 * @param   pErrInfo            Pointer to error info structure. Optional.
1071 */
1072DECLHIDDEN(int) supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
1073{
1074    RT_NOREF1(hProcess);
1075
1076    /*
1077     * Use the ThreadAmILastThread request to check that there is only one
1078     * thread in the process.
1079     * Seems this isn't entirely reliable when hThread isn't the current thread?
1080     */
1081    ULONG cbIgn = 0;
1082    ULONG fAmI  = 0;
1083    NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
1084    if (!NT_SUCCESS(rcNt))
1085        return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
1086                                   "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
1087    if (!fAmI)
1088        return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
1089                                   "More than one thread in process");
1090
1091    /** @todo Would be nice to verify the relation ship between hProcess and hThread
1092     *        as well... */
1093    return VINF_SUCCESS;
1094}
1095
1096
1097/**
1098 * Verifies that there isn't a debugger attached to the process.
1099 *
1100 * @returns VBox status code.
1101 * @param   hProcess            The process.
1102 * @param   pErrInfo            Pointer to error info structure. Optional.
1103 */
1104DECLHIDDEN(int) supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
1105{
1106#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
1107    /*
1108     * Use the ProcessDebugPort request to check there is no debugger
1109     * currently attached to the process.
1110     */
1111    ULONG     cbIgn = 0;
1112    uintptr_t uPtr  = ~(uintptr_t)0;
1113    NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
1114                                              ProcessDebugPort,
1115                                              &uPtr, sizeof(uPtr), &cbIgn);
1116    if (!NT_SUCCESS(rcNt))
1117        return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
1118                                   "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
1119    if (uPtr != 0)
1120        return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_DEBUGGED,
1121                                   "Debugger attached (%#zx)", uPtr);
1122#else
1123    RT_NOREF2(hProcess, pErrInfo);
1124#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
1125    return VINF_SUCCESS;
1126}
1127
1128
1129/**
1130 * Matches two UNICODE_STRING structures in a case sensitive fashion.
1131 *
1132 * @returns true if equal, false if not.
1133 * @param   pUniStr1            The first unicode string.
1134 * @param   pUniStr2            The first unicode string.
1135 */
1136static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
1137{
1138    if (pUniStr1->Length != pUniStr2->Length)
1139        return false;
1140    return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
1141}
1142
1143
1144/**
1145 * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
1146 *
1147 * @returns true / false
1148 * @param   pszName1            The ASCII name.
1149 * @param   pwszName2           The UTF-16 name.
1150 */
1151static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
1152{
1153    for (;;)
1154    {
1155        char    ch1 = *pszName1++;
1156        RTUTF16 wc2 = *pwszName2++;
1157        if (ch1 != wc2)
1158        {
1159            ch1 = RT_C_TO_LOWER(ch1);
1160            wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
1161            if (ch1 != wc2)
1162                return false;
1163        }
1164        if (!ch1)
1165            return true;
1166    }
1167}
1168
1169
1170/**
1171 * Records an additional memory region for an image.
1172 *
1173 * May trash pThis->abMemory.
1174 *
1175 * @returns VBox status code.
1176 * @retval  VINF_OBJECT_DESTROYED if we've unmapped the image (child
1177 *          purification only).
1178 * @param   pThis               The process scanning state structure.
1179 * @param   pImage              The new image structure.  Only the unicode name
1180 *                              buffer is valid (it's zero-terminated).
1181 * @param   pMemInfo            The memory information for the image.
1182 */
1183static int supHardNtVpNewImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1184{
1185    /*
1186     * If the filename or path contains short names, we have to get the long
1187     * path so that we will recognize the DLLs and their location.
1188     */
1189    int rc83Exp = VERR_IGNORED;
1190    PUNICODE_STRING pLongName = &pImage->Name.UniStr;
1191    if (RTNtPathFindPossible8dot3Name(pLongName->Buffer))
1192    {
1193        AssertCompile(sizeof(pThis->abMemory) > sizeof(pImage->Name));
1194        PUNICODE_STRING pTmp = (PUNICODE_STRING)pThis->abMemory;
1195        pTmp->MaximumLength = (USHORT)RT_MIN(_64K - 1, sizeof(pThis->abMemory) - sizeof(*pTmp)) - sizeof(RTUTF16);
1196        pTmp->Length = pImage->Name.UniStr.Length;
1197        pTmp->Buffer = (PRTUTF16)(pTmp + 1);
1198        memcpy(pTmp->Buffer, pLongName->Buffer, pLongName->Length + sizeof(RTUTF16));
1199
1200        rc83Exp = RTNtPathExpand8dot3Path(pTmp, false /*fPathOnly*/);
1201        Assert(rc83Exp == VINF_SUCCESS);
1202        Assert(pTmp->Buffer[pTmp->Length / sizeof(RTUTF16)] == '\0');
1203        if (rc83Exp == VINF_SUCCESS)
1204            SUP_DPRINTF(("supHardNtVpNewImage: 8dot3 -> long: '%ls' -> '%ls'\n", pLongName->Buffer, pTmp->Buffer));
1205        else
1206            SUP_DPRINTF(("supHardNtVpNewImage: RTNtPathExpand8dot3Path returns %Rrc for '%ls' (-> '%ls')\n",
1207                         rc83Exp, pLongName->Buffer, pTmp->Buffer));
1208
1209        pLongName = pTmp;
1210    }
1211
1212    /*
1213     * Extract the final component.
1214     */
1215    RTUTF16   wc;
1216    unsigned  cwcDirName   = pLongName->Length / sizeof(WCHAR);
1217    PCRTUTF16 pwszFilename = &pLongName->Buffer[cwcDirName];
1218    while (   cwcDirName > 0
1219           && (wc = pwszFilename[-1]) != '\\'
1220           && wc != '/'
1221           && wc != ':')
1222    {
1223        pwszFilename--;
1224        cwcDirName--;
1225    }
1226    if (!*pwszFilename)
1227        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
1228                                   "Empty filename (len=%u) for image at %p.", pLongName->Length, pMemInfo->BaseAddress);
1229
1230    /*
1231     * Drop trailing slashes from the directory name.
1232     */
1233    while (   cwcDirName > 0
1234           && (   pLongName->Buffer[cwcDirName - 1] == '\\'
1235               || pLongName->Buffer[cwcDirName - 1] == '/'))
1236        cwcDirName--;
1237
1238    /*
1239     * Match it against known DLLs.
1240     */
1241    pImage->pszName = NULL;
1242    for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
1243        if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
1244        {
1245            pImage->pszName = g_apszSupNtVpAllowedDlls[i];
1246            pImage->fDll    = true;
1247
1248#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1249            /* The directory name must match the one we've got for System32. */
1250            if (   (   cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
1251                    || suplibHardenedMemComp(pLongName->Buffer, g_System32NtPath.UniStr.Buffer, cwcDirName * sizeof(WCHAR)) )
1252# ifdef VBOX_PERMIT_MORE
1253                && (   pImage->pszName[0] != 'a'
1254                    || pImage->pszName[1] != 'c'
1255                    || !supHardViIsAppPatchDir(pLongName->Buffer, pLongName->Length / sizeof(WCHAR)) )
1256# endif
1257                )
1258                return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NON_SYSTEM32_DLL,
1259                                           "Expected %ls to be loaded from %ls.",
1260                                           pLongName->Buffer, g_System32NtPath.UniStr.Buffer);
1261# ifdef VBOX_PERMIT_MORE
1262            if (g_uNtVerCombined < SUP_NT_VER_W70 && i >= VBOX_PERMIT_MORE_FIRST_IDX)
1263                pImage->pszName = NULL; /* hard limit: user32.dll is unwanted prior to w7. */
1264# endif
1265
1266#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1267            break;
1268        }
1269    if (!pImage->pszName)
1270    {
1271        /*
1272         * Not a known DLL, is it a known executable?
1273         */
1274        for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
1275            if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
1276            {
1277                pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
1278                pImage->fDll    = false;
1279                break;
1280            }
1281    }
1282    if (!pImage->pszName)
1283    {
1284        /*
1285         * Unknown image.
1286         *
1287         * If we're cleaning up a child process, we can unmap the offending
1288         * DLL...  Might have interesting side effects, or at least interesting
1289         * as in "may you live in interesting times".
1290         */
1291#ifdef IN_RING3
1292        if (   pMemInfo->AllocationBase == pMemInfo->BaseAddress
1293            && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1294        {
1295            SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping image mem at %p (%p LB %#zx) - '%ls'\n",
1296                         pMemInfo->AllocationBase, pMemInfo->BaseAddress, pMemInfo->RegionSize, pwszFilename));
1297            NTSTATUS rcNt = NtUnmapViewOfSection(pThis->hProcess, pMemInfo->AllocationBase);
1298            if (NT_SUCCESS(rcNt))
1299                return VINF_OBJECT_DESTROYED;
1300            pThis->cFixes++;
1301            SUP_DPRINTF(("supHardNtVpScanVirtualMemory: NtUnmapViewOfSection(,%p) failed: %#x\n", pMemInfo->AllocationBase, rcNt));
1302        }
1303#endif
1304        /*
1305         * Special error message if we can.
1306         */
1307        if (   pMemInfo->AllocationBase == pMemInfo->BaseAddress
1308            && (   supHardNtVpAreNamesEqual("sysfer.dll", pwszFilename)
1309                || supHardNtVpAreNamesEqual("sysfer32.dll", pwszFilename)
1310                || supHardNtVpAreNamesEqual("sysfer64.dll", pwszFilename)
1311                || supHardNtVpAreNamesEqual("sysfrethunk.dll", pwszFilename)) )
1312        {
1313            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_SYSFER_DLL,
1314                                "Found %ls at %p - This is probably part of Symantec Endpoint Protection. \n"
1315                                "You or your admin need to add and exception to the Application and Device Control (ADC) "
1316                                "component (or disable it) to prevent ADC from injecting itself into the VirtualBox VM processes. "
1317                                "See http://www.symantec.com/connect/articles/creating-application-control-exclusions-symantec-endpoint-protection-121"
1318                                , pLongName->Buffer, pMemInfo->BaseAddress);
1319            return pThis->rcResult = VERR_SUP_VP_SYSFER_DLL; /* Try make sure this is what the user sees first! */
1320        }
1321        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
1322                                   "Unknown image file %ls at %p. (rc83Exp=%Rrc)",
1323                                   pLongName->Buffer, pMemInfo->BaseAddress, rc83Exp);
1324    }
1325
1326    /*
1327     * Checks for multiple mappings of the same DLL but with different image file paths.
1328     */
1329    uint32_t i = pThis->cImages;
1330    while (i-- > 1)
1331        if (pImage->pszName == pThis->aImages[i].pszName)
1332            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
1333                                       "Duplicate image entries for %s: %ls and %ls",
1334                                       pImage->pszName, pImage->Name.UniStr.Buffer, pThis->aImages[i].Name.UniStr.Buffer);
1335
1336    /*
1337     * Since it's a new image, we expect to be at the start of the mapping now.
1338     */
1339    if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
1340        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
1341                                   "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
1342                                   pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
1343
1344    /*
1345     * Check for size/rva overflow.
1346     */
1347    if (pMemInfo->RegionSize >= _2G)
1348        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1349                                   "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
1350
1351    /*
1352     * Fill in details from the memory info.
1353     */
1354    pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
1355    pImage->cbImage    = pMemInfo->RegionSize;
1356    pImage->pCacheEntry= NULL;
1357    pImage->cRegions   = 1;
1358    pImage->aRegions[0].uRva    = 0;
1359    pImage->aRegions[0].cb      = (uint32_t)pMemInfo->RegionSize;
1360    pImage->aRegions[0].fProt   = pMemInfo->Protect;
1361
1362    if (suplibHardenedStrCmp(pImage->pszName, "ntdll.dll") == 0)
1363        pImage->fNtCreateSectionPatch = true;
1364    else if (suplibHardenedStrCmp(pImage->pszName, "apisetschema.dll") == 0)
1365        pImage->fApiSetSchemaOnlySection1 = true; /** @todo Check the ApiSetMap field in the PEB. */
1366#ifdef VBOX_PERMIT_MORE
1367    else if (suplibHardenedStrCmp(pImage->pszName, "acres.dll") == 0)
1368        pImage->f32bitResourceDll = true;
1369#endif
1370
1371    return VINF_SUCCESS;
1372}
1373
1374
1375/**
1376 * Records an additional memory region for an image.
1377 *
1378 * @returns VBox status code.
1379 * @param   pThis               The process scanning state structure.
1380 * @param   pImage              The image.
1381 * @param   pMemInfo            The memory information for the region.
1382 */
1383static int supHardNtVpAddRegion(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo)
1384{
1385    /*
1386     * Make sure the base address matches.
1387     */
1388    if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
1389        return supHardNtVpSetInfo2(pThis, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
1390                                   "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
1391                                   pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
1392                                   pMemInfo->BaseAddress, pMemInfo->RegionSize);
1393
1394    /*
1395     * Check for size and rva overflows.
1396     */
1397    uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
1398    if (pMemInfo->RegionSize >= _2G)
1399        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_LARGE_REGION,
1400                                   "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1401    if (uRva >= _2G)
1402        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
1403                                   "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
1404
1405
1406    /*
1407     * Record the region.
1408     */
1409    uint32_t iRegion = pImage->cRegions;
1410    if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
1411        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
1412                                   "Too many regions for %s.", pImage->pszName);
1413    pImage->aRegions[iRegion].uRva  = (uint32_t)uRva;
1414    pImage->aRegions[iRegion].cb    = (uint32_t)pMemInfo->RegionSize;
1415    pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
1416    pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
1417    pImage->cRegions++;
1418    pImage->fApiSetSchemaOnlySection1 = false;
1419
1420    return VINF_SUCCESS;
1421}
1422
1423
1424#ifdef IN_RING3
1425/**
1426 * Frees (or replaces) executable memory of allocation type private.
1427 *
1428 * @returns True if nothing really bad happen, false if to quit ASAP because we
1429 *          killed the process being scanned.
1430 * @param   pThis               The process scanning state structure. Details
1431 *                              about images are added to this.
1432 * @param   hProcess            The process to verify.
1433 * @param   pMemInfo            The information we've got on this private
1434 *                              executable memory.
1435 */
1436static bool supHardNtVpFreeOrReplacePrivateExecMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess,
1437                                                      MEMORY_BASIC_INFORMATION const *pMemInfo)
1438{
1439    NTSTATUS rcNt;
1440
1441    /*
1442     * Try figure the entire allocation size. Free/Alloc may fail otherwise.
1443     */
1444    PVOID   pvFree = pMemInfo->AllocationBase;
1445    SIZE_T  cbFree = pMemInfo->RegionSize + ((uintptr_t)pMemInfo->BaseAddress - (uintptr_t)pMemInfo->AllocationBase);
1446    for (;;)
1447    {
1448        SIZE_T                      cbActual = 0;
1449        MEMORY_BASIC_INFORMATION    MemInfo2 = { 0, 0, 0, 0, 0, 0, 0 };
1450        uintptr_t                   uPtrNext = (uintptr_t)pvFree + cbFree;
1451        rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1452                                         (void const *)uPtrNext,
1453                                         MemoryBasicInformation,
1454                                         &MemInfo2,
1455                                         sizeof(MemInfo2),
1456                                         &cbActual);
1457        if (!NT_SUCCESS(rcNt))
1458            break;
1459        if (pMemInfo->AllocationBase != MemInfo2.AllocationBase)
1460            break;
1461        if (MemInfo2.RegionSize == 0)
1462            break;
1463        cbFree += MemInfo2.RegionSize;
1464    }
1465    SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: %s exec mem at %p (LB %#zx, %p LB %#zx)\n",
1466                 pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW ? "Replacing" : "Freeing",
1467                 pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize));
1468
1469    /*
1470     * In the BSOD workaround mode, we need to make a copy of the memory before
1471     * freeing it.
1472     */
1473    uintptr_t   uCopySrc  = (uintptr_t)pvFree;
1474    size_t      cbCopy    = 0;
1475    void       *pvCopy    = NULL;
1476    if (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW)
1477    {
1478        cbCopy = cbFree;
1479        pvCopy = RTMemAllocZ(cbCopy);
1480        if (!pvCopy)
1481        {
1482            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED, "RTMemAllocZ(%#zx) failed", cbCopy);
1483            return true;
1484        }
1485
1486        rcNt = supHardNtVpReadMem(hProcess, uCopySrc, pvCopy, cbCopy);
1487        if (!NT_SUCCESS(rcNt))
1488            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1489                                "Error reading data from original alloc: %#x (%p LB %#zx)", rcNt, uCopySrc, cbCopy, rcNt);
1490        supR3HardenedLogFlush();
1491    }
1492
1493    /*
1494     * Free the memory.
1495     */
1496    for (uint32_t i = 0; i < 10; i++)
1497    {
1498        PVOID  pvFreeInOut = pvFree;
1499        SIZE_T cbFreeInOut = 0;
1500        rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1501        if (NT_SUCCESS(rcNt))
1502        {
1503            SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 succeeded: %#x [%p/%p LB 0/%#zx]\n",
1504                         rcNt, pvFree, pvFreeInOut, cbFreeInOut));
1505            supR3HardenedLogFlush();
1506        }
1507        else
1508        {
1509            SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #1 failed: %#x [%p LB 0]\n", rcNt, pvFree));
1510            supR3HardenedLogFlush();
1511            pvFreeInOut = pvFree;
1512            cbFreeInOut = cbFree;
1513            rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1514            if (NT_SUCCESS(rcNt))
1515            {
1516                SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 succeeded: %#x [%p/%p LB %#zx/%#zx]\n",
1517                             rcNt, pvFree, pvFreeInOut, cbFree, cbFreeInOut));
1518                supR3HardenedLogFlush();
1519            }
1520            else
1521            {
1522                SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #2 failed: %#x [%p LB %#zx]\n",
1523                             rcNt, pvFree, cbFree));
1524                supR3HardenedLogFlush();
1525                pvFreeInOut = pMemInfo->BaseAddress;
1526                cbFreeInOut = pMemInfo->RegionSize;
1527                rcNt = NtFreeVirtualMemory(hProcess, &pvFreeInOut, &cbFreeInOut, MEM_RELEASE);
1528                if (NT_SUCCESS(rcNt))
1529                {
1530                    pvFree = pMemInfo->BaseAddress;
1531                    cbFree = pMemInfo->RegionSize;
1532                    SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Free attempt #3 succeeded [%p LB %#zx]\n",
1533                                 pvFree, cbFree));
1534                    supR3HardenedLogFlush();
1535                }
1536                else
1537                    supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1538                                        "NtFreeVirtualMemory [%p LB %#zx and %p LB %#zx] failed: %#x",
1539                                        pvFree, cbFree, pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
1540            }
1541        }
1542
1543        /*
1544         * Query the region again, redo the free operation if there's still memory there.
1545         */
1546        if (!NT_SUCCESS(rcNt))
1547            break;
1548        SIZE_T                      cbActual = 0;
1549        MEMORY_BASIC_INFORMATION    MemInfo3 = { 0, 0, 0, 0, 0, 0, 0 };
1550        NTSTATUS rcNt2 = g_pfnNtQueryVirtualMemory(hProcess, pvFree, MemoryBasicInformation,
1551                                                   &MemInfo3, sizeof(MemInfo3), &cbActual);
1552        if (!NT_SUCCESS(rcNt2))
1553            break;
1554        SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: QVM after free %u: [%p]/%p LB %#zx s=%#x ap=%#x rp=%#p\n",
1555                     i, MemInfo3.AllocationBase, MemInfo3.BaseAddress, MemInfo3.RegionSize, MemInfo3.State,
1556                     MemInfo3.AllocationProtect, MemInfo3.Protect));
1557        supR3HardenedLogFlush();
1558        if (MemInfo3.State == MEM_FREE || !(pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
1559            break;
1560        NtYieldExecution();
1561        SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Retrying free...\n"));
1562        supR3HardenedLogFlush();
1563    }
1564
1565    /*
1566     * Restore memory as non-executable - Kludge for Trend Micro sakfile.sys
1567     * and Digital Guardian dgmaster.sys BSODs.
1568     */
1569    if (NT_SUCCESS(rcNt) && (pThis->fFlags & SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW))
1570    {
1571        PVOID  pvAlloc = pvFree;
1572        SIZE_T cbAlloc = cbFree;
1573        rcNt = NtAllocateVirtualMemory(hProcess, &pvAlloc, 0, &cbAlloc, MEM_COMMIT, PAGE_READWRITE);
1574        if (!NT_SUCCESS(rcNt))
1575        {
1576            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1577                                "NtAllocateVirtualMemory (%p LB %#zx) failed with rcNt=%#x allocating "
1578                                "replacement memory for working around buggy protection software. "
1579                                "See VBoxStartup.log for more details",
1580                                pvAlloc, cbFree, rcNt);
1581            supR3HardenedLogFlush();
1582            NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1583            return false;
1584        }
1585
1586        if (   (uintptr_t)pvFree < (uintptr_t)pvAlloc
1587            || (uintptr_t)pvFree + cbFree > (uintptr_t)pvAlloc + cbFree)
1588        {
1589            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED,
1590                                "We wanted NtAllocateVirtualMemory to get us %p LB %#zx, but it returned %p LB %#zx.",
1591                                pMemInfo->BaseAddress, pMemInfo->RegionSize, pvFree, cbFree, rcNt);
1592            supR3HardenedLogFlush();
1593            NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1594            return false;
1595        }
1596
1597        /*
1598         * Copy what we can, considering the 2nd free attempt.
1599         */
1600        uint8_t *pbDst = (uint8_t *)pvFree;
1601        size_t   cbDst = cbFree;
1602        uint8_t *pbSrc = (uint8_t *)pvCopy;
1603        size_t   cbSrc = cbCopy;
1604        if ((uintptr_t)pbDst != uCopySrc)
1605        {
1606            if ((uintptr_t)pbDst > uCopySrc)
1607            {
1608                uintptr_t cbAdj = (uintptr_t)pbDst - uCopySrc;
1609                pbSrc += cbAdj;
1610                cbSrc -= cbAdj;
1611            }
1612            else
1613            {
1614                uintptr_t cbAdj = uCopySrc - (uintptr_t)pbDst;
1615                pbDst += cbAdj;
1616                cbDst -= cbAdj;
1617            }
1618        }
1619        if (cbSrc > cbDst)
1620            cbSrc = cbDst;
1621
1622        SIZE_T cbWritten;
1623        rcNt = NtWriteVirtualMemory(hProcess, pbDst, pbSrc, cbSrc, &cbWritten);
1624        if (NT_SUCCESS(rcNt))
1625        {
1626            SUP_DPRINTF(("supHardNtVpFreeOrReplacePrivateExecMemory: Restored the exec memory as non-exec.\n"));
1627            supR3HardenedLogFlush();
1628        }
1629        else
1630        {
1631            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FREE_VIRTUAL_MEMORY_FAILED,
1632                                "NtWriteVirtualMemory (%p LB %#zx) failed: %#x",
1633                                pMemInfo->BaseAddress, pMemInfo->RegionSize, rcNt);
1634            supR3HardenedLogFlush();
1635            NtTerminateProcess(hProcess, VERR_SUP_VP_REPLACE_VIRTUAL_MEMORY_FAILED);
1636            return false;
1637        }
1638    }
1639    if (pvCopy)
1640        RTMemFree(pvCopy);
1641    return true;
1642}
1643#endif /* IN_RING3 */
1644
1645
1646/**
1647 * Scans the virtual memory of the process.
1648 *
1649 * This collects the locations of DLLs and the EXE, and verifies that executable
1650 * memory is only associated with these.  May trash pThis->abMemory.
1651 *
1652 * @returns VBox status code.
1653 * @param   pThis               The process scanning state structure. Details
1654 *                              about images are added to this.
1655 * @param   hProcess            The process to verify.
1656 */
1657static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess)
1658{
1659    SUP_DPRINTF(("supHardNtVpScanVirtualMemory: enmKind=%s\n",
1660                 pThis->enmKind == SUPHARDNTVPKIND_VERIFY_ONLY ? "VERIFY_ONLY" :
1661                 pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION ? "CHILD_PURIFICATION" : "SELF_PURIFICATION"));
1662
1663    uint32_t    cXpExceptions = 0;
1664    uintptr_t   cbAdvance = 0;
1665    uintptr_t   uPtrWhere = 0;
1666#ifdef VBOX_PERMIT_VERIFIER_DLL
1667    for (uint32_t i = 0; i < 10240; i++)
1668#else
1669    for (uint32_t i = 0; i < 1024; i++)
1670#endif
1671    {
1672        SIZE_T                      cbActual = 0;
1673        MEMORY_BASIC_INFORMATION    MemInfo  = { 0, 0, 0, 0, 0, 0, 0 };
1674        NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1675                                                  (void const *)uPtrWhere,
1676                                                  MemoryBasicInformation,
1677                                                  &MemInfo,
1678                                                  sizeof(MemInfo),
1679                                                  &cbActual);
1680        if (!NT_SUCCESS(rcNt))
1681        {
1682            if (rcNt == STATUS_INVALID_PARAMETER)
1683                return pThis->rcResult;
1684            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
1685                                       "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
1686        }
1687
1688        /*
1689         * Record images.
1690         */
1691        if (   MemInfo.Type == SEC_IMAGE
1692            || MemInfo.Type == SEC_PROTECTED_IMAGE
1693            || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
1694        {
1695            uint32_t iImg = pThis->cImages;
1696            rcNt = g_pfnNtQueryVirtualMemory(hProcess,
1697                                             (void const *)uPtrWhere,
1698                                             MemorySectionName,
1699                                             &pThis->aImages[iImg].Name,
1700                                             sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
1701                                             &cbActual);
1702            if (!NT_SUCCESS(rcNt))
1703                return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
1704                                           "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
1705            pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
1706            SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1707                         ? " *%p-%p %#06x/%#06x %#09x  %ls\n"
1708                         : "  %p-%p %#06x/%#06x %#09x  %ls\n",
1709                         MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
1710                         MemInfo.AllocationProtect, MemInfo.Type, pThis->aImages[iImg].Name.UniStr.Buffer));
1711
1712            /* New or existing image? */
1713            bool fNew = true;
1714            uint32_t iSearch = iImg;
1715            while (iSearch-- > 0)
1716                if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
1717                {
1718                    int rc = supHardNtVpAddRegion(pThis, &pThis->aImages[iSearch], &MemInfo);
1719                    if (RT_FAILURE(rc))
1720                        return rc;
1721                    fNew = false;
1722                    break;
1723                }
1724                else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
1725                    return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
1726                                               "Unexpected base address match");
1727
1728            if (fNew)
1729            {
1730                int rc = supHardNtVpNewImage(pThis, &pThis->aImages[iImg], &MemInfo);
1731                if (RT_SUCCESS(rc))
1732                {
1733                    if (rc != VINF_OBJECT_DESTROYED)
1734                    {
1735                        pThis->cImages++;
1736                        if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
1737                            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
1738                                                       "Internal error: aImages is full.\n");
1739                    }
1740                }
1741#ifdef IN_RING3 /* Continue and add more information if unknown DLLs are found. */
1742                else if (rc != VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE && rc != VERR_SUP_VP_NON_SYSTEM32_DLL)
1743                    return rc;
1744#else
1745                else
1746                    return rc;
1747#endif
1748            }
1749        }
1750        /*
1751         * XP, W2K3: Ignore the CSRSS read-only region as best we can.
1752         */
1753        else if (      (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1754                    == PAGE_EXECUTE_READ
1755                 && cXpExceptions == 0
1756                 && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
1757                 /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
1758                 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
1759        {
1760            cXpExceptions++;
1761            SUP_DPRINTF(("  %p-%p %#06x/%#06x %#09x  XP CSRSS read-only region\n", MemInfo.BaseAddress,
1762                         (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1, MemInfo.Protect,
1763                         MemInfo.AllocationProtect, MemInfo.Type));
1764        }
1765        /*
1766         * Executable memory?
1767         */
1768#ifndef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
1769        else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
1770        {
1771            SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1772                         ? " *%p-%p %#06x/%#06x %#09x !!\n"
1773                         : "  %p-%p %#06x/%#06x %#09x !!\n",
1774                         MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
1775                         MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1776# ifdef IN_RING3
1777            if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
1778            {
1779                /*
1780                 * Free any private executable memory (sysplant.sys allocates executable memory).
1781                 */
1782                if (MemInfo.Type == MEM_PRIVATE)
1783                {
1784                    if (!supHardNtVpFreeOrReplacePrivateExecMemory(pThis, hProcess, &MemInfo))
1785                        break;
1786                }
1787                /*
1788                 * Unmap mapped memory, failing that, drop exec privileges.
1789                 */
1790                else if (MemInfo.Type == MEM_MAPPED)
1791                {
1792                    SUP_DPRINTF(("supHardNtVpScanVirtualMemory: Unmapping exec mem at %p (%p/%p LB %#zx)\n",
1793                                 uPtrWhere, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize));
1794                    rcNt = NtUnmapViewOfSection(hProcess, MemInfo.AllocationBase);
1795                    if (!NT_SUCCESS(rcNt))
1796                    {
1797                        PVOID  pvCopy = MemInfo.BaseAddress;
1798                        SIZE_T cbCopy = MemInfo.RegionSize;
1799                        NTSTATUS rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_NOACCESS, NULL);
1800                        if (!NT_SUCCESS(rcNt2))
1801                            rcNt2 = NtProtectVirtualMemory(hProcess, &pvCopy, &cbCopy, PAGE_READONLY, NULL);
1802                        if (!NT_SUCCESS(rcNt2))
1803                            supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNMAP_AND_PROTECT_FAILED,
1804                                                "NtUnmapViewOfSection (%p/%p LB %#zx) failed: %#x (%#x)",
1805                                                MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize, rcNt, rcNt2);
1806                    }
1807                }
1808                else
1809                    supHardNtVpSetInfo2(pThis, VERR_SUP_VP_UNKOWN_MEM_TYPE,
1810                                        "Unknown executable memory type %#x at %p/%p LB %#zx",
1811                                        MemInfo.Type, MemInfo.AllocationBase, MemInfo.BaseAddress, MemInfo.RegionSize);
1812                pThis->cFixes++;
1813            }
1814            else
1815# endif /* IN_RING3 */
1816                supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_EXEC_MEMORY,
1817                                    "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
1818                                    uPtrWhere, MemInfo.BaseAddress, MemInfo.RegionSize, MemInfo.Type, MemInfo.Protect,
1819                                    MemInfo.State, MemInfo.AllocationBase, MemInfo.AllocationProtect);
1820
1821# ifndef IN_RING3
1822            if (RT_FAILURE(pThis->rcResult))
1823                return pThis->rcResult;
1824# endif
1825            /* Continue add more information about the problematic process. */
1826        }
1827#endif /* VBOX_PERMIT_VISUAL_STUDIO_PROFILING */
1828        else
1829            SUP_DPRINTF((MemInfo.AllocationBase == MemInfo.BaseAddress
1830                         ? " *%p-%p %#06x/%#06x %#09x\n"
1831                         : "  %p-%p %#06x/%#06x %#09x\n",
1832                         MemInfo.BaseAddress, (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize - 1,
1833                         MemInfo.Protect, MemInfo.AllocationProtect, MemInfo.Type));
1834
1835        /*
1836         * Advance.
1837         */
1838        cbAdvance = MemInfo.RegionSize;
1839        if (uPtrWhere + cbAdvance <= uPtrWhere)
1840            return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
1841                                       "Empty region at %p.", uPtrWhere);
1842        uPtrWhere += MemInfo.RegionSize;
1843    }
1844
1845    return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
1846                               "Too many virtual memory regions.\n");
1847}
1848
1849
1850/**
1851 * Verifies the loader image, i.e. check cryptographic signatures if present.
1852 *
1853 * @returns VBox status code.
1854 * @param   pEntry              The loader cache entry.
1855 * @param   pwszName            The filename to use in error messages.
1856 * @param   pErrInfo            Where to return extened error information.
1857 */
1858DECLHIDDEN(int) supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo)
1859{
1860    int rc = VINF_SUCCESS;
1861    if (!pEntry->fVerified)
1862    {
1863        rc = supHardenedWinVerifyImageByLdrMod(pEntry->hLdrMod, pwszName, pEntry->pNtViRdr,
1864                                               false /*fAvoidWinVerifyTrust*/, NULL /*pfWinVerifyTrust*/, pErrInfo);
1865        pEntry->fVerified = RT_SUCCESS(rc);
1866    }
1867    return rc;
1868}
1869
1870
1871/**
1872 * Allocates a image bits buffer and calls RTLdrGetBits on them.
1873 *
1874 * An assumption here is that there won't ever be concurrent use of the cache.
1875 * It's currently 104% single threaded, non-reentrant.  Thus, we can't reuse the
1876 * pbBits allocation.
1877 *
1878 * @returns VBox status code
1879 * @param   pEntry              The loader cache entry.
1880 * @param   ppbBits             Where to return the pointer to the allocation.
1881 * @param   uBaseAddress        The image base address, see RTLdrGetBits.
1882 * @param   pfnGetImport        Import getter, see RTLdrGetBits.
1883 * @param   pvUser              The user argument for @a pfnGetImport.
1884 * @param   pErrInfo            Where to return extened error information.
1885 */
1886DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
1887                                              RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
1888                                              PRTERRINFO pErrInfo)
1889{
1890    int rc;
1891
1892    /*
1893     * First time around we have to allocate memory before we can get the image bits.
1894     */
1895    if (!pEntry->pbBits)
1896    {
1897        size_t cbBits = RTLdrSize(pEntry->hLdrMod);
1898        if (cbBits >= _1M*32U)
1899            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_TOO_BIG, "Image %s is too large: %zu bytes (%#zx).",
1900                                       pEntry->pszName, cbBits, cbBits);
1901
1902        pEntry->pbBits = (uint8_t *)RTMemAllocZ(cbBits);
1903        if (!pEntry->pbBits)
1904            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
1905                                       cbBits, pEntry->pszName);
1906
1907        pEntry->fValidBits = false; /* paranoia */
1908
1909        rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
1910        if (RT_FAILURE(rc))
1911            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
1912                                       pEntry->pszName, rc);
1913        pEntry->uImageBase = uBaseAddress;
1914        pEntry->fValidBits = pfnGetImport == NULL;
1915
1916    }
1917    /*
1918     * Cache hit? No?
1919     *
1920     * Note! We cannot currently cache image bits for images with imports as we
1921     *       don't control the way they're resolved.  Fortunately, NTDLL and
1922     *       the VM process images all have no imports.
1923     */
1924    else if (   !pEntry->fValidBits
1925             || pEntry->uImageBase != uBaseAddress
1926             || pfnGetImport)
1927    {
1928        pEntry->fValidBits = false;
1929
1930        rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
1931        if (RT_FAILURE(rc))
1932            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
1933                                       pEntry->pszName, rc);
1934        pEntry->uImageBase = uBaseAddress;
1935        pEntry->fValidBits = pfnGetImport == NULL;
1936    }
1937
1938    *ppbBits = pEntry->pbBits;
1939    return VINF_SUCCESS;
1940}
1941
1942
1943/**
1944 * Frees all resources associated with a cache entry and wipes the members
1945 * clean.
1946 *
1947 * @param   pEntry              The entry to delete.
1948 */
1949static void supHardNTLdrCacheDeleteEntry(PSUPHNTLDRCACHEENTRY pEntry)
1950{
1951    if (pEntry->pbBits)
1952    {
1953        RTMemFree(pEntry->pbBits);
1954        pEntry->pbBits = NULL;
1955    }
1956
1957    if (pEntry->hLdrMod != NIL_RTLDRMOD)
1958    {
1959        RTLdrClose(pEntry->hLdrMod);
1960        pEntry->hLdrMod = NIL_RTLDRMOD;
1961        pEntry->pNtViRdr = NULL;
1962    }
1963    else if (pEntry->pNtViRdr)
1964    {
1965        pEntry->pNtViRdr->Core.pfnDestroy(&pEntry->pNtViRdr->Core);
1966        pEntry->pNtViRdr = NULL;
1967    }
1968
1969    if (pEntry->hFile)
1970    {
1971        NtClose(pEntry->hFile);
1972        pEntry->hFile = NULL;
1973    }
1974
1975    pEntry->pszName    = NULL;
1976    pEntry->fVerified  = false;
1977    pEntry->fValidBits = false;
1978    pEntry->uImageBase = 0;
1979}
1980
1981#ifdef IN_RING3
1982
1983/**
1984 * Flushes the cache.
1985 *
1986 * This is called from one of two points in the hardened main code, first is
1987 * after respawning and the second is when we open the vboxdrv device for
1988 * unrestricted access.
1989 */
1990DECLHIDDEN(void) supR3HardenedWinFlushLoaderCache(void)
1991{
1992    uint32_t i = g_cSupNtVpLdrCacheEntries;
1993    while (i-- > 0)
1994        supHardNTLdrCacheDeleteEntry(&g_aSupNtVpLdrCacheEntries[i]);
1995    g_cSupNtVpLdrCacheEntries = 0;
1996}
1997
1998
1999/**
2000 * Searches the cache for a loader image.
2001 *
2002 * @returns Pointer to the cache entry if found, NULL if not.
2003 * @param   pszName             The name (from g_apszSupNtVpAllowedVmExes or
2004 *                              g_apszSupNtVpAllowedDlls).
2005 */
2006static PSUPHNTLDRCACHEENTRY supHardNtLdrCacheLookupEntry(const char *pszName)
2007{
2008    /*
2009     * Since the caller is supplying us a pszName from one of the two tables,
2010     * we can dispense with string compare and simply compare string pointers.
2011     */
2012    uint32_t i = g_cSupNtVpLdrCacheEntries;
2013    while (i-- > 0)
2014        if (g_aSupNtVpLdrCacheEntries[i].pszName == pszName)
2015            return &g_aSupNtVpLdrCacheEntries[i];
2016    return NULL;
2017}
2018
2019#endif /* IN_RING3 */
2020
2021static int supHardNtLdrCacheNewEntry(PSUPHNTLDRCACHEENTRY pEntry, const char *pszName, PUNICODE_STRING pUniStrPath,
2022                                     bool fDll, bool f32bitResourceDll, PRTERRINFO pErrInfo)
2023{
2024    /*
2025     * Open the image file.
2026     */
2027    HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
2028    IO_STATUS_BLOCK     Ios   = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2029
2030    OBJECT_ATTRIBUTES   ObjAttr;
2031    InitializeObjectAttributes(&ObjAttr, pUniStrPath, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2032#ifdef IN_RING0
2033    ObjAttr.Attributes |= OBJ_KERNEL_HANDLE;
2034#endif
2035
2036    NTSTATUS rcNt = NtCreateFile(&hFile,
2037                                 GENERIC_READ | SYNCHRONIZE,
2038                                 &ObjAttr,
2039                                 &Ios,
2040                                 NULL /* Allocation Size*/,
2041                                 FILE_ATTRIBUTE_NORMAL,
2042                                 FILE_SHARE_READ,
2043                                 FILE_OPEN,
2044                                 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2045                                 NULL /*EaBuffer*/,
2046                                 0 /*EaLength*/);
2047    if (NT_SUCCESS(rcNt))
2048        rcNt = Ios.Status;
2049    if (!NT_SUCCESS(rcNt))
2050        return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
2051                                   "Error opening image for scanning: %#x (name %ls)", rcNt, pUniStrPath->Buffer);
2052
2053    /*
2054     * Figure out validation flags we'll be using and create the reader
2055     * for this image.
2056     */
2057    uint32_t fFlags = fDll
2058                    ? SUPHNTVI_F_TRUSTED_INSTALLER_OWNER | SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION
2059                    : SUPHNTVI_F_REQUIRE_BUILD_CERT;
2060    if (f32bitResourceDll)
2061        fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
2062
2063    PSUPHNTVIRDR pNtViRdr;
2064    int rc = supHardNtViRdrCreate(hFile, pUniStrPath->Buffer, fFlags, &pNtViRdr);
2065    if (RT_FAILURE(rc))
2066    {
2067        NtClose(hFile);
2068        return rc;
2069    }
2070
2071    /*
2072     * Finally, open the image with the loader
2073     */
2074    RTLDRMOD hLdrMod;
2075    RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
2076    if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
2077        enmArch = RTLDRARCH_WHATEVER;
2078    rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
2079    if (RT_FAILURE(rc))
2080        return supHardNtVpAddInfo1(pErrInfo, rc, "RTLdrOpenWithReader failed: %Rrc (Image='%ls').",
2081                                   rc, pUniStrPath->Buffer);
2082
2083    /*
2084     * Fill in the cache entry.
2085     */
2086    pEntry->pszName    = pszName;
2087    pEntry->hLdrMod    = hLdrMod;
2088    pEntry->pNtViRdr   = pNtViRdr;
2089    pEntry->hFile      = hFile;
2090    pEntry->pbBits     = NULL;
2091    pEntry->fVerified  = false;
2092    pEntry->fValidBits = false;
2093    pEntry->uImageBase = ~(uintptr_t)0;
2094
2095#ifdef IN_SUP_HARDENED_R3
2096    /*
2097     * Log the image timestamp when in the hardened exe.
2098     */
2099    uint64_t uTimestamp = 0;
2100    rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uint64_t));
2101    SUP_DPRINTF(("%s: timestamp %#llx (rc=%Rrc)\n", pszName, uTimestamp, rc));
2102#endif
2103
2104    return VINF_SUCCESS;
2105}
2106
2107#ifdef IN_RING3
2108/**
2109 * Opens a loader cache entry.
2110 *
2111 * Currently this is only used by the import code for getting NTDLL.
2112 *
2113 * @returns VBox status code.
2114 * @param   pszName             The DLL name.  Must be one from the
2115 *                              g_apszSupNtVpAllowedDlls array.
2116 * @param   ppEntry             Where to return the entry we've opened/found.
2117 * @param   pErrInfo            Optional buffer where to return additional error
2118 *                              information.
2119 */
2120DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry, PRTERRINFO pErrInfo)
2121{
2122    /*
2123     * Locate the dll.
2124     */
2125    uint32_t i = 0;
2126    while (   i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)
2127           && strcmp(pszName, g_apszSupNtVpAllowedDlls[i]))
2128        i++;
2129    if (i >= RT_ELEMENTS(g_apszSupNtVpAllowedDlls))
2130        return VERR_FILE_NOT_FOUND;
2131    pszName = g_apszSupNtVpAllowedDlls[i];
2132
2133    /*
2134     * Try the cache.
2135     */
2136    *ppEntry = supHardNtLdrCacheLookupEntry(pszName);
2137    if (*ppEntry)
2138        return VINF_SUCCESS;
2139
2140    /*
2141     * Not in the cache, so open it.
2142     * Note! We cannot assume that g_System32NtPath has been initialized at this point.
2143     */
2144    if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
2145        return VERR_INTERNAL_ERROR_3;
2146
2147    static WCHAR s_wszSystem32[] = L"\\SystemRoot\\System32\\";
2148    WCHAR wszPath[64];
2149    memcpy(wszPath, s_wszSystem32, sizeof(s_wszSystem32));
2150    RTUtf16CatAscii(wszPath, sizeof(wszPath), pszName);
2151
2152    UNICODE_STRING UniStr;
2153    UniStr.Buffer = wszPath;
2154    UniStr.Length = (USHORT)(RTUtf16Len(wszPath) * sizeof(WCHAR));
2155    UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
2156
2157    int rc = supHardNtLdrCacheNewEntry(&g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries], pszName, &UniStr,
2158                                       true /*fDll*/, false /*f32bitResourceDll*/, pErrInfo);
2159    if (RT_SUCCESS(rc))
2160    {
2161        *ppEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
2162        g_cSupNtVpLdrCacheEntries++;
2163        return VINF_SUCCESS;
2164    }
2165    return rc;
2166}
2167#endif /* IN_RING3 */
2168
2169
2170/**
2171 * Opens all the images with the IPRT loader, setting both, hFile, pNtViRdr and
2172 * hLdrMod for each image.
2173 *
2174 * @returns VBox status code.
2175 * @param   pThis               The process scanning state structure.
2176 */
2177static int supHardNtVpOpenImages(PSUPHNTVPSTATE pThis)
2178{
2179    unsigned i = pThis->cImages;
2180    while (i-- > 0)
2181    {
2182        PSUPHNTVPIMAGE pImage = &pThis->aImages[i];
2183
2184#ifdef IN_RING3
2185        /*
2186         * Try the cache first.
2187         */
2188        pImage->pCacheEntry = supHardNtLdrCacheLookupEntry(pImage->pszName);
2189        if (pImage->pCacheEntry)
2190            continue;
2191
2192        /*
2193         * Not in the cache, so load it into the cache.
2194         */
2195        if (g_cSupNtVpLdrCacheEntries >= RT_ELEMENTS(g_aSupNtVpLdrCacheEntries))
2196            return supHardNtVpSetInfo2(pThis, VERR_INTERNAL_ERROR_3, "Loader cache overflow.");
2197        pImage->pCacheEntry = &g_aSupNtVpLdrCacheEntries[g_cSupNtVpLdrCacheEntries];
2198#else
2199        /*
2200         * In ring-0 we don't have a cache at the moment (resource reasons), so
2201         * we have a static cache entry in each image structure that we use instead.
2202         */
2203        pImage->pCacheEntry = &pImage->CacheEntry;
2204#endif
2205
2206        int rc = supHardNtLdrCacheNewEntry(pImage->pCacheEntry, pImage->pszName, &pImage->Name.UniStr,
2207                                           pImage->fDll, pImage->f32bitResourceDll, pThis->pErrInfo);
2208        if (RT_FAILURE(rc))
2209            return rc;
2210#ifdef IN_RING3
2211        g_cSupNtVpLdrCacheEntries++;
2212#endif
2213    }
2214
2215    return VINF_SUCCESS;
2216}
2217
2218
2219/**
2220 * Check the integrity of the executable of the process.
2221 *
2222 * @returns VBox status code.
2223 * @param   pThis               The process scanning state structure. Details
2224 *                              about images are added to this.  The hProcess
2225 *                              member holds the handle to the process that is
2226 *                              to be verified.
2227 */
2228static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis)
2229{
2230    /*
2231     * Make sure there is exactly one executable image.
2232     */
2233    unsigned cExecs = 0;
2234    unsigned iExe   = ~0U;
2235    unsigned i = pThis->cImages;
2236    while (i-- > 0)
2237    {
2238        if (!pThis->aImages[i].fDll)
2239        {
2240            cExecs++;
2241            iExe = i;
2242        }
2243    }
2244    if (cExecs == 0)
2245        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
2246                                   "No executable mapping found in the virtual address space.");
2247    if (cExecs != 1)
2248        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
2249                                   "Found more than one executable mapping in the virtual address space.");
2250    PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
2251
2252    /*
2253     * Check that it matches the executable image of the process.
2254     */
2255    int             rc;
2256    ULONG           cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
2257    PUNICODE_STRING pUniStr  = (PUNICODE_STRING)RTMemAllocZ(cbUniStr);
2258    if (!pUniStr)
2259        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_MEMORY,
2260                                  "Error allocating %zu bytes for process name.", cbUniStr);
2261    ULONG    cbIgn = 0;
2262    NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
2263    if (NT_SUCCESS(rcNt))
2264    {
2265        if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
2266            rc = VINF_SUCCESS;
2267        else
2268        {
2269            pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
2270            rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
2271                                     "Process image name does not match the exectuable we found: %ls vs %ls.",
2272                                     pUniStr->Buffer, pImage->Name.UniStr.Buffer);
2273        }
2274    }
2275    else
2276        rc = supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
2277                                 "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
2278    RTMemFree(pUniStr);
2279    if (RT_FAILURE(rc))
2280        return rc;
2281
2282    /*
2283     * Validate the signing of the executable image.
2284     * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
2285     */
2286    rc = supHardNtVpVerifyImage(pThis, pImage);
2287    if (RT_FAILURE(rc))
2288        return rc;
2289
2290    /*
2291     * Check linking requirements.
2292     * This query is only available using the current process pseudo handle on
2293     * older windows versions.  The cut-off seems to be Vista.
2294     */
2295    SECTION_IMAGE_INFORMATION ImageInfo;
2296    rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
2297    if (!NT_SUCCESS(rcNt))
2298    {
2299        if (   rcNt == STATUS_INVALID_PARAMETER
2300            && g_uNtVerCombined < SUP_NT_VER_VISTA
2301            && pThis->hProcess != NtCurrentProcess() )
2302            return VINF_SUCCESS;
2303        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
2304                                   "NtQueryInformationProcess/ProcessImageInformation failed: %#x hProcess=%#x",
2305                                   rcNt, pThis->hProcess);
2306    }
2307    if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
2308        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
2309                                   "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
2310                                   ImageInfo.DllCharacteristics);
2311    if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
2312        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
2313                                   "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
2314                                   ImageInfo.DllCharacteristics);
2315    if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
2316        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
2317                                   "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
2318                                   ImageInfo.DllCharacteristics);
2319
2320    if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
2321        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2322                                   "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
2323                                   ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
2324
2325    if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
2326        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
2327                                   "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
2328                                   ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
2329
2330    return VINF_SUCCESS;
2331}
2332
2333
2334/**
2335 * Check the integrity of the DLLs found in the process.
2336 *
2337 * @returns VBox status code.
2338 * @param   pThis               The process scanning state structure. Details
2339 *                              about images are added to this.  The hProcess
2340 *                              member holds the handle to the process that is
2341 *                              to be verified.
2342 */
2343static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis)
2344{
2345    /*
2346     * Check for duplicate entries (paranoia).
2347     */
2348    uint32_t i = pThis->cImages;
2349    while (i-- > 1)
2350    {
2351        const char *pszName = pThis->aImages[i].pszName;
2352        uint32_t j = i;
2353        while (j-- > 0)
2354            if (pThis->aImages[j].pszName == pszName)
2355                return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
2356                                           "Duplicate image entries for %s: %ls and %ls",
2357                                           pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
2358    }
2359
2360    /*
2361     * Check that both ntdll and kernel32 are present.
2362     * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
2363     */
2364    uint32_t iNtDll    = UINT32_MAX;
2365    uint32_t iKernel32 = UINT32_MAX;
2366    i = pThis->cImages;
2367    while (i-- > 0)
2368        if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
2369            iNtDll = i;
2370        else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
2371            iKernel32 = i;
2372    if (iNtDll == UINT32_MAX)
2373        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
2374                                   "The process has no NTDLL.DLL.");
2375    if (iKernel32 == UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
2376        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
2377                                   "The process has no KERNEL32.DLL.");
2378    else if (iKernel32 != UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
2379        return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_KERNEL32_ALREADY_MAPPED,
2380                                   "The process already has KERNEL32.DLL loaded.");
2381
2382    /*
2383     * Verify that the DLLs are correctly signed (by MS).
2384     */
2385    i = pThis->cImages;
2386    while (i-- > 0)
2387    {
2388        int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i]);
2389        if (RT_FAILURE(rc))
2390            return rc;
2391    }
2392
2393    return VINF_SUCCESS;
2394}
2395
2396
2397/**
2398 * Verifies the given process.
2399 *
2400 * The following requirements are checked:
2401 *  - The process only has one thread, the calling thread.
2402 *  - The process has no debugger attached.
2403 *  - The executable image of the process is verified to be signed with
2404 *    certificate known to this code at build time.
2405 *  - The executable image is one of a predefined set.
2406 *  - The process has only a very limited set of system DLLs loaded.
2407 *  - The system DLLs signatures check out fine.
2408 *  - The only executable memory in the process belongs to the system DLLs and
2409 *    the executable image.
2410 *
2411 * @returns VBox status code.
2412 * @param   hProcess            The process to verify.
2413 * @param   hThread             A thread in the process (the caller).
2414 * @param   enmKind             The kind of process verification to perform.
2415 * @param   fFlags              Valid combination of SUPHARDNTVP_F_XXX flags.
2416 * @param   pErrInfo            Pointer to error info structure. Optional.
2417 * @param   pcFixes             Where to return the number of fixes made during
2418 *                              purification.  Optional.
2419 */
2420DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, SUPHARDNTVPKIND enmKind, uint32_t fFlags,
2421                                            uint32_t *pcFixes, PRTERRINFO pErrInfo)
2422{
2423    if (pcFixes)
2424        *pcFixes = 0;
2425
2426    /*
2427     * Some basic checks regarding threads and debuggers. We don't need
2428     * allocate any state memory for these.
2429     */
2430    int rc = VINF_SUCCESS;
2431    if (enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION)
2432       rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
2433    if (RT_SUCCESS(rc))
2434        rc = supHardNtVpDebugger(hProcess, pErrInfo);
2435    if (RT_SUCCESS(rc))
2436    {
2437        /*
2438         * Allocate and initialize memory for the state.
2439         */
2440        PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)RTMemAllocZ(sizeof(*pThis));
2441        if (pThis)
2442        {
2443            pThis->enmKind  = enmKind;
2444            pThis->fFlags   = fFlags;
2445            pThis->rcResult = VINF_SUCCESS;
2446            pThis->hProcess = hProcess;
2447            pThis->pErrInfo = pErrInfo;
2448
2449            /*
2450             * Perform the verification.
2451             */
2452            rc = supHardNtVpScanVirtualMemory(pThis, hProcess);
2453            if (RT_SUCCESS(rc))
2454                rc = supHardNtVpOpenImages(pThis);
2455            if (RT_SUCCESS(rc))
2456                rc = supHardNtVpCheckExe(pThis);
2457            if (RT_SUCCESS(rc))
2458                rc = supHardNtVpCheckDlls(pThis);
2459
2460            if (pcFixes)
2461                *pcFixes = pThis->cFixes;
2462
2463            /*
2464             * Clean up the state.
2465             */
2466#ifdef IN_RING0
2467            for (uint32_t i = 0; i < pThis->cImages; i++)
2468                supHardNTLdrCacheDeleteEntry(&pThis->aImages[i].CacheEntry);
2469#endif
2470            RTMemFree(pThis);
2471        }
2472        else
2473            rc = supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY_STATE,
2474                                     "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
2475    }
2476    return rc;
2477}
2478
Note: See TracBrowser for help on using the repository browser.

www.oracle.com
ContactPrivacy policyTerms of Use