SUPR3HardenedMain-win.cpp revision ee6e158c08eec8c8cbec363eb13d9ecd81a5d105
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/* $Id$ */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @file
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * VirtualBox Support Library - Hardened main(), windows bits.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Copyright (C) 2006-2014 Oracle Corporation
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * available from http://www.virtualbox.org. This file is free software;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * General Public License (GPL) as published by the Free Software
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * The contents of this file may alternatively be used under the terms
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * of the Common Development and Distribution License Version 1.0
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * VirtualBox OSE distribution, in which case the provisions of the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * CDDL are applicable instead of those of the GPL.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * You may elect to license modified versions of this file under the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * terms and conditions of either the GPL or the CDDL or both.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/*******************************************************************************
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync* Header Files *
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync*******************************************************************************/
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/nt/nt-and-windows.h>
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#include <AccCtrl.h>
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#include <AclApi.h>
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#ifndef PROCESS_SET_LIMITED_INFORMATION
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync# define PROCESS_SET_LIMITED_INFORMATION 0x2000
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#endif
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync#endif
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync#include <VBox/sup.h>
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync#include <VBox/err.h>
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync#include <VBox/dis.h>
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#include <iprt/ctype.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/string.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/initterm.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/param.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/path.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/thread.h>
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#include <iprt/zero.h>
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync#include "SUPLibInternal.h"
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync#include "win/SUPHardenedVerify-win.h"
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync#include "../SUPDrvIOC.h"
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#ifndef IMAGE_SCN_TYPE_NOLOAD
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync# define IMAGE_SCN_TYPE_NOLOAD 0x00000002
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync#endif
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync/*******************************************************************************
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync* Defined Constants And Macros *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync*******************************************************************************/
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The first argument of a respawed stub when respawned for the first time.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * This just needs to be unique enough to avoid most confusion with real
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * executable names, there are other checks in place to make sure we've respanwed. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPR3_RESPAWN_1_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-2ndchild"
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The first argument of a respawed stub when respawned for the second time.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * This just needs to be unique enough to avoid most confusion with real
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * executable names, there are other checks in place to make sure we've respanwed. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPR3_RESPAWN_2_ARG0 "60eaff78-4bdd-042d-2e72-669728efd737-suplib-3rdchild"
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Unconditional assertion. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPR3HARDENED_ASSERT(a_Expr) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync do { \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if (!(a_Expr)) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync supR3HardenedFatal("%s: %s\n", __FUNCTION__, #a_Expr); \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync } while (0)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync/** Unconditional assertion of NT_SUCCESS. */
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync do { \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NTSTATUS rcNtAssert = (a_Expr); \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if (!NT_SUCCESS(rcNtAssert)) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, rcNtAssert); \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync } while (0)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync/** Unconditional assertion of a WIN32 API returning non-FALSE. */
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync do { \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync BOOL fRcAssert = (a_Expr); \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if (fRcAssert == FALSE) \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync supR3HardenedFatal("%s: %s -> %#x\n", __FUNCTION__, #a_Expr, RtlGetLastWin32Error()); \
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync } while (0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/*******************************************************************************
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync* Structures and Typedefs *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync*******************************************************************************/
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Security descriptor cleanup structure.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef struct MYSECURITYCLEANUP
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync union
ffb50166c9adb4ae583b914d405197035cf890advboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync SID Sid;
ffb50166c9adb4ae583b914d405197035cf890advboxsync uint8_t abPadding[SECURITY_MAX_SID_SIZE];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync } Everyone, Owner, User, Login;
ffb50166c9adb4ae583b914d405197035cf890advboxsync union
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync ACL AclHdr;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint8_t abPadding[1024];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync } Acl;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PSECURITY_DESCRIPTOR pSecDesc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync} MYSECURITYCLEANUP;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to security cleanup structure. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Image verifier cache entry.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef struct VERIFIERCACHEENTRY
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Pointer to the next entry with the same hash value. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync struct VERIFIERCACHEENTRY * volatile pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Next entry in the WinVerifyTrust todo list. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync struct VERIFIERCACHEENTRY * volatile pNextTodoWvt;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The file handle. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hFile;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** If fIndexNumber is set, this is an file system internal file identifier. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync LARGE_INTEGER IndexNumber;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The path hash value. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The verification result. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync int rc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Used for shutting up load and error messages after a while so they don't
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * flood the the log file and fill up the disk. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t volatile cHits;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The validation flags (for WinVerifyTrust retry). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t fFlags;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Whether IndexNumber is valid */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool fIndexNumberValid;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Whether verified by WinVerifyTrust. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool volatile fWinVerifyTrust;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** cwcPath * sizeof(RTUTF16). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint16_t cbPath;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The full path of this entry (variable size). */
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync RTUTF16 wszPath[1];
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync} VERIFIERCACHEENTRY;
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync/** Pointer to an image verifier path entry. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Name of an import DLL that we need to check out.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef struct VERIFIERCACHEIMPORT
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
11b175175a0ed424b8e8354acda681ad0adde0f8vboxsync /** Pointer to the next DLL in the list. */
11b175175a0ed424b8e8354acda681ad0adde0f8vboxsync struct VERIFIERCACHEIMPORT * volatile pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The length of pwszAltSearchDir if available. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t cwcAltSearchDir;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** This points the directory containing the DLL needing it, this will be
11b175175a0ed424b8e8354acda681ad0adde0f8vboxsync * NULL for a System32 DLL. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PWCHAR pwszAltSearchDir;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The name of the import DLL (variable length). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync char szName[1];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync} VERIFIERCACHEIMPORT;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to a import DLL that needs checking out. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef VERIFIERCACHEIMPORT *PVERIFIERCACHEIMPORT;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Child requests.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef enum SUPR3WINCHILDREQ
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Perform child purification and close full access handles (must be zero). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync kSupR3WinChildReq_PurifyChildAndCloseHandles = 0,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Close the events, we're good on our own from here on. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync kSupR3WinChildReq_CloseEvents,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Reporting error. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync kSupR3WinChildReq_Error,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** End of valid requests. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync kSupR3WinChildReq_End
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync} SUPR3WINCHILDREQ;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Child process parameters.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef struct SUPR3WINPROCPARAMS
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The event semaphore the child will be waiting on. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hEvtChild;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The event semaphore the parent will be waiting on. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hEvtParent;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The address of the NTDLL. This is only valid during the very early
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * initialization as we abuse for thread creation protection. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uintptr_t uNtDllAddr;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The requested operation (set by the child). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUPR3WINCHILDREQ enmRequest;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The last status. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync int32_t rc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The init operation the error relates to if message, kSupInitOp_Invalid if
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * not message. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUPINITOP enmWhat;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Where if message. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync char szWhere[80];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Error message / path name string space. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync char szErrorMsg[4096];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync} SUPR3WINPROCPARAMS;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Child process data structure for use during child process init setup and
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * purification.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef struct SUPR3HARDNTCHILD
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Process handle. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hProcess;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Primary thread handle. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hThread;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Handle to the parent process, if we're the middle (stub) process. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hParent;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The event semaphore the child will be waiting on. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hEvtChild;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The event semaphore the parent will be waiting on. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hEvtParent;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The address of NTDLL in the child. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uintptr_t uNtDllAddr;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The address of NTDLL in this process. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uintptr_t uNtDllParentAddr;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** Which respawn number this is (1 = stub, 2 = VM). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync int iWhich;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The basic process info. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PROCESS_BASIC_INFORMATION BasicInfo;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The probable size of the PEB. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync size_t cbPeb;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The pristine process environment block. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PEB Peb;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /** The child process parameters. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUPR3WINPROCPARAMS ProcParams;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync} SUPR3HARDNTCHILD;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to a child process data structure. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsynctypedef SUPR3HARDNTCHILD *PSUPR3HARDNTCHILD;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/*******************************************************************************
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync* Global Variables *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync*******************************************************************************/
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Process parameters. Specified by parent if VM process, see
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * supR3HardenedVmProcessInit. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic SUPR3WINPROCPARAMS g_ProcParams = { NULL, NULL, 0, (SUPR3WINCHILDREQ)0, 0 };
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Set if supR3HardenedEarlyProcessInit was invoked. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncbool g_fSupEarlyProcessInit = false;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Set if the stub device has been opened (stub process only). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncbool g_fSupStubOpened = false;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @name Global variables initialized by suplibHardenedWindowsMain.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @{ */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncuint32_t g_uNtVerCombined = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Count calls to the special main function for linking santity checks. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The UTF-16 windows path to the executable. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncRTUTF16 g_wszSupLibHardenedExePath[1024];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The NT path of the executable. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncSUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * not byte). This also gives the length of the exectuable directory path,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * including a trailing slash. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncuint32_t g_offSupLibHardenedExeNtName;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @} */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @name Hook related variables.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @{ */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to the bit of assembly code that will perform the original
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * NtCreateSection operation. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic NTSTATUS (NTAPI * g_pfnNtCreateSectionReal)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PLARGE_INTEGER, ULONG, ULONG, HANDLE);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to the NtCreateSection function in NtDll (for patching purposes). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint8_t *g_pbNtCreateSection;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The patched NtCreateSection bytes (for restoring). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint8_t g_abNtCreateSectionPatch[16];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to the bit of assembly code that will perform the original
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * LdrLoadDll operation. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic NTSTATUS (NTAPI * g_pfnLdrLoadDllReal)(PWSTR, PULONG, PUNICODE_STRING, PHANDLE);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Pointer to the LdrLoadDll function in NtDll (for patching purposes). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint8_t *g_pbLdrLoadDll;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The patched LdrLoadDll bytes (for restoring). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint8_t g_abLdrLoadDllPatch[16];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The hash table of verifier cache . */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVERIFIERCACHEENTRY volatile g_apVerifierCache[128];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Queue of cached images which needs WinVerifyTrust to check them. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVERIFIERCACHEENTRY volatile g_pVerifierCacheTodoWvt = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Queue of cached images which needs their imports checked. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVERIFIERCACHEIMPORT volatile g_pVerifierCacheTodoImports = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The windows path to dir \\SystemRoot\\System32 directory (technically
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * this whatever \KnownDlls\KnownDllPath points to). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncSUPSYSROOTDIRBUF g_System32WinPath;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @ */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Positive if the DLL notification callback has been registered, counts
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * registration attempts as negative. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic int g_cDllNotificationRegistered = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The registration cookie of the DLL notification callback. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVOID g_pvDllNotificationCookie = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Static error info structure used during init. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic RTERRINFOSTATIC g_ErrInfoStatic;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** In the assembly file. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncextern "C" uint8_t g_abSupHardReadWriteExecPage[PAGE_SIZE];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Whether we've patched our own LdrInitializeThunk or not. We do this to
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * disable thread creation. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic bool g_fSupInitThunkSelfPatched;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** The backup of our own LdrInitializeThunk code, for enabling and disabling
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * thread creation in this process. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint8_t g_abLdrInitThunkSelfBackup[16];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Mask of adversaries that we've detected (SUPHARDNT_ADVERSARY_XXX). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint32_t g_fSupAdversaries = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @name SUPHARDNT_ADVERSARY_XXX - Adversaries
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @{ */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Symantec endpoint protection or similar including SysPlant.sys. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_SYMANTEC_SYSPLANT RT_BIT_32(0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Symantec Norton 360. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_SYMANTEC_N360 RT_BIT_32(1)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Avast! */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_AVAST RT_BIT_32(2)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** TrendMicro OfficeScan and probably others. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_TRENDMICRO RT_BIT_32(3)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** TrendMicro potentially buggy sakfile.sys. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE RT_BIT_32(4)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** McAfee. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_MCAFEE RT_BIT_32(5)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Kaspersky or OEMs of it. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_KASPERSKY RT_BIT_32(6)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Malwarebytes Anti-Malware (MBAM). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_MBAM RT_BIT_32(7)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** AVG Internet Security. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_AVG RT_BIT_32(8)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Panda Security. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_PANDA RT_BIT_32(9)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Microsoft Security Essentials. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_MSE RT_BIT_32(10)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Comodo. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_COMODO RT_BIT_32(11)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Check Point's Zone Alarm (may include Kaspersky). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_ZONE_ALARM RT_BIT_32(12)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Digital guardian. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN RT_BIT_32(13)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** Unknown adversary detected while waiting on child. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#define SUPHARDNT_ADVERSARY_UNKNOWN RT_BIT_32(31)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/** @} */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/*******************************************************************************
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync* Internal Functions *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync*******************************************************************************/
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic NTSTATUS supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool *pfQuiet);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic void supR3HardenedWinRegisterDllNotificationCallback(void);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic void supR3HardenedWinReInstallHooks(bool fFirst);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncDECLASM(void) supR3HardenedEarlyProcessInitThunk(void);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Simple wide char search routine.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * NULL if not found.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pwszHaystack Pointer to the string that should be searched.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param wcNeedle The character to search for.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PRTUTF16 suplibHardenedWStrChr(PCRTUTF16 pwszHaystack, RTUTF16 wcNeedle)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync for (;;)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wcCur = *pwszHaystack;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wcCur == wcNeedle)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return (PRTUTF16)pwszHaystack;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wcCur == '\0')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pwszHaystack++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Simple wide char string length routine.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns The number of characters in the given string. (Excludes the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * terminator.)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pwsz The string.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic size_t suplibHardenedWStrLen(PCRTUTF16 pwsz)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PCRTUTF16 pwszCur = pwsz;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (*pwszCur != '\0')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pwszCur++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return pwszCur - pwsz;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Our version of GetTickCount.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns Millisecond timestamp.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint64_t supR3HardenedWinGetMilliTS(void)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)(uintptr_t)0x7ffe0000;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* use interrupt time */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync LARGE_INTEGER Time;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync do
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync Time.HighPart = pUserSharedData->InterruptTime.High1Time;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync Time.LowPart = pUserSharedData->InterruptTime.LowPart;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return (uint64_t)Time.QuadPart / 10000;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * and supplies the right flags.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns Module handle on success, NULL on failure.
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync * @param pszName The full path to the DLL.
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync * @param fSystem32Only Whether to only look for imports in the system32
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * directory. If set to false, the application
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * directory is also searched.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncDECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync WCHAR wszPath[RTPATH_MAX];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PRTUTF16 pwszPath = wszPath;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync int rc = RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszPath, RT_ELEMENTS(wszPath), NULL);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (RT_SUCCESS(rc))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (*pwszPath)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (*pwszPath == '/')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *pwszPath = '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pwszPath++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync DWORD fFlags = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync fFlags |= LOAD_LIBRARY_SEARCH_SYSTEM32;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!fSystem32Only)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync fFlags |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync void *pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, fFlags);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( !pvRet
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && fFlags
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, 0);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return pvRet;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync supR3HardenedFatal("RTStrToUtf16Ex failed on '%s': %Rrc", pszName, rc);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Gets the internal index number of the file.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns True if we got an index number, false if not.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param hFile The file in question.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pIndexNumber where to return the index number.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic bool supR3HardenedWinVerifyCacheGetIndexNumber(HANDLE hFile, PLARGE_INTEGER pIndexNumber)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (NT_SUCCESS(rcNt))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync rcNt = Ios.Status;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#ifdef DEBUG_bird
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!NT_SUCCESS(rcNt))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync __debugbreak();
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#endif
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return NT_SUCCESS(rcNt) && pIndexNumber->QuadPart != 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Calculates the hash value for the given UTF-16 path string.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns Hash value.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pUniStr String to hash.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint32_t supR3HardenedWinVerifyCacheHashPath(PCUNICODE_STRING pUniStr)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t uHash = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync unsigned cwcLeft = pUniStr->Length / sizeof(WCHAR);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PRTUTF16 pwc = pUniStr->Buffer;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (cwcLeft-- > 0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wc = *pwc++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wc < 0x80)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Calculates the hash value for a directory + filename combo as if they were
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * one single string.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns Hash value.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pawcDir The directory name.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param cwcDir The length of the directory name. RTSTR_MAX if
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * not available.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pszName The import name (UTF-8).
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic uint32_t supR3HardenedWinVerifyCacheHashDirAndFile(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t uHash = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (cwcDir-- > 0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wc = *pawcDir++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wc < 0x80)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync wc = wc != '/' ? RT_C_TO_LOWER(wc) : '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync unsigned char ch = '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while ((ch = *pszName++) != '\0')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync ch = RT_C_TO_LOWER(ch);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uHash = ch + (uHash << 6) + (uHash << 16) - uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return uHash;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Verify string cache compare function.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @returns true if the strings match, false if not.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pawcLeft The left hand string.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pawcRight The right hand string.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param cwcToCompare The number of chars to compare.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic bool supR3HardenedWinVerifyCacheIsMatch(PCRTUTF16 pawcLeft, PCRTUTF16 pawcRight, uint32_t cwcToCompare)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Try a quick memory compare first. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (memcmp(pawcLeft, pawcRight, cwcToCompare * sizeof(RTUTF16)) == 0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return true;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Slow char by char compare. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (cwcToCompare-- > 0)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wcLeft = *pawcLeft++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wcRight = *pawcRight++;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wcLeft != wcRight)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync wcLeft = wcLeft != '/' ? RT_C_TO_LOWER(wcLeft) : '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync wcRight = wcRight != '/' ? RT_C_TO_LOWER(wcRight) : '\\';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (wcLeft != wcRight)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return false;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return true;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Inserts the given verifier result into the cache.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pUniStr The full path of the image.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param hFile The file handle - must either be entered into
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * the cache or closed.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param rc The verifier result.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param fWinVerifyTrust Whether verified by WinVerifyTrust or not.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param fFlags The image verification flags.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool fWinVerifyTrust, uint32_t fFlags)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Allocate and initalize a new entry.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pEntry = (PVERIFIERCACHEENTRY)RTMemAllocZ(sizeof(VERIFIERCACHEENTRY) + pUniStr->Length);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (pEntry)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->pNext = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->pNextTodoWvt = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->hFile = hFile;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->rc = rc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->fFlags = fFlags;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->cHits = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->fWinVerifyTrust = fWinVerifyTrust;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->cbPath = pUniStr->Length;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync memcpy(pEntry->wszPath, pUniStr->Buffer, pUniStr->Length);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->wszPath[pUniStr->Length / sizeof(WCHAR)] = '\0';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &pEntry->IndexNumber);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Try insert it, careful with concurrent code as well as potential duplicates.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t iHashTab = pEntry->uHash % RT_ELEMENTS(g_apVerifierCache);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync VERIFIERCACHEENTRY * volatile *ppEntry = &g_apVerifierCache[iHashTab];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync for (;;)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (ASMAtomicCmpXchgPtr(ppEntry, pEntry, NULL))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!fWinVerifyTrust)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync do
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pEntry->pNextTodoWvt = g_pVerifierCacheTodoWvt;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pEntry, pEntry->pNextTodoWvt));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheInsert: %ls\n", pUniStr->Buffer));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pOther = *ppEntry;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!pOther)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync continue;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( pOther->uHash == pEntry->uHash
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && pOther->cbPath == pEntry->cbPath
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && supR3HardenedWinVerifyCacheIsMatch(pOther->wszPath, pEntry->wszPath, pEntry->cbPath / sizeof(RTUTF16)))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync break;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync ppEntry = &pOther->pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Duplicate entry (may happen due to races). */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTMemFree(pEntry);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NtClose(hFile);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Looks up an entry in the verifier hash table.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @return Pointer to the entry on if found, NULL if not.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pUniStr The full path of the image.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param hFile The file handle.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PRTUTF16 const pwszPath = pUniStr->Buffer;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint16_t const cbPath = pUniStr->Length;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (pCur)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( pCur->uHash == uHash
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && pCur->cbPath == cbPath
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pwszPath, cbPath / sizeof(RTUTF16)))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!pCur->fIndexNumberValid)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return pCur;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync LARGE_INTEGER IndexNumber;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &IndexNumber);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( fIndexNumberValid
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && IndexNumber.QuadPart == pCur->IndexNumber.QuadPart)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return pCur;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#ifdef DEBUG_bird
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync __debugbreak();
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync#endif
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pCur = pCur->pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Looks up an import DLL in the verifier hash table.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @return Pointer to the entry on if found, NULL if not.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pawcDir The directory name.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param cwcDir The length of the directory name.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pszName The import name (UTF-8).
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookupImport(PCRTUTF16 pawcDir, uint32_t cwcDir, const char *pszName)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t uHash = supR3HardenedWinVerifyCacheHashDirAndFile(pawcDir, cwcDir, pszName);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t const cbPath = (uint32_t)((cwcDir + 1 + strlen(pszName)) * sizeof(RTUTF16));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (pCur)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( pCur->uHash == uHash
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && pCur->cbPath == cbPath)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (supR3HardenedWinVerifyCacheIsMatch(pCur->wszPath, pawcDir, cwcDir))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (pCur->wszPath[cwcDir] == '\\' || pCur->wszPath[cwcDir] == '/')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (RTUtf16ICmpAscii(&pCur->wszPath[cwcDir + 1], pszName))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return pCur;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pCur = pCur->pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Schedules the import DLLs for verification and entry into the cache.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync *
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param hLdrMod The loader module which imports should be
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * scheduled for verification.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * @param pwszName The full NT path of the module.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncDECLHIDDEN(void) supR3HardenedWinVerifyCacheScheduleImports(RTLDRMOD hLdrMod, PCRTUTF16 pwszName)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Any imports?
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t cImports;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync int rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_COUNT, NULL /*pvBits*/, &cImports, sizeof(cImports), NULL);
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (RT_SUCCESS(rc))
ffb50166c9adb4ae583b914d405197035cf890advboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (cImports)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Figure out the DLL directory from pwszName.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PCRTUTF16 pawcDir = pwszName;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t cwcDir = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t i = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while ((wc = pawcDir[i++]) != '\0')
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ((wc == '\\' || wc == '/' || wc == ':') && cwcDir + 2 != i)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync cwcDir = i - 1;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( g_System32NtPath.UniStr.Length / sizeof(WCHAR) == cwcDir
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && supR3HardenedWinVerifyCacheIsMatch(pawcDir, g_System32NtPath.UniStr.Buffer, cwcDir))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pawcDir = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Enumerate the imports.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync for (i = 0; i < cImports; i++)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync union
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync char szName[256];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t iImport;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync } uBuf;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uBuf.iImport = i;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_IMPORT_MODULE, NULL /*pvBits*/, &uBuf, sizeof(uBuf), NULL);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (RT_SUCCESS(rc))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Skip kernel32, ntdll and API set stuff.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTStrToLower(uBuf.szName);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( RTStrCmp(uBuf.szName, "kernel32.dll") == 0
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync || RTStrCmp(uBuf.szName, "kernelbase.dll") == 0
ffb50166c9adb4ae583b914d405197035cf890advboxsync || RTStrCmp(uBuf.szName, "ntdll.dll") == 0
ffb50166c9adb4ae583b914d405197035cf890advboxsync || RTStrNCmp(uBuf.szName, RT_STR_TUPLE("api-ms-win-")) == 0 )
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync continue;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Skip to the next one if it's already in the cache.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
ffb50166c9adb4ae583b914d405197035cf890advboxsync g_System32NtPath.UniStr.Length / sizeof(WCHAR),
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uBuf.szName) != NULL)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for system32\n", uBuf.szName));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync continue;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync }
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedExeNtPath.UniStr.Buffer,
ffb50166c9adb4ae583b914d405197035cf890advboxsync g_offSupLibHardenedExeNtName,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync uBuf.szName) != NULL)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync {
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for appdir\n", uBuf.szName));
ffb50166c9adb4ae583b914d405197035cf890advboxsync continue;
ffb50166c9adb4ae583b914d405197035cf890advboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (pawcDir && supR3HardenedWinVerifyCacheLookupImport(pawcDir, cwcDir, uBuf.szName) != NULL)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: '%s' cached for dll dir\n", uBuf.szName));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync continue;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* We could skip already scheduled modules, but that'll require serialization and extra work... */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Add it to the todo list.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheScheduleImports: Import todo: #%u '%s'.\n", i, uBuf.szName));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync uint32_t cbName = (uint32_t)strlen(uBuf.szName) + 1;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t cbNameAligned = RT_ALIGN_32(cbName, sizeof(RTUTF16));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint32_t cbNeeded = RT_OFFSETOF(VERIFIERCACHEIMPORT, szName[cbNameAligned])
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync + (pawcDir ? (cwcDir + 1) * sizeof(RTUTF16) : 0);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEIMPORT pImport = (PVERIFIERCACHEIMPORT)RTMemAllocZ(cbNeeded);
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if (pImport)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Init it. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync memcpy(pImport->szName, uBuf.szName, cbName);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!pawcDir)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync pImport->cwcAltSearchDir = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pImport->pwszAltSearchDir = NULL;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync else
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync pImport->cwcAltSearchDir = cwcDir;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pImport->pwszAltSearchDir = (PRTUTF16)&pImport->szName[cbNameAligned];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync memcpy(pImport->pwszAltSearchDir, pawcDir, cwcDir * sizeof(RTUTF16));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pImport->pwszAltSearchDir[cwcDir] = '\0';
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Insert it. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync do
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pImport->pNext = g_pVerifierCacheTodoImports;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoImports, pImport, pImport->pNext));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync else
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("RTLDRPROP_IMPORT_MODULE failed with rc=%Rrc i=%#x on '%ls'\n", rc, i, pwszName));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync else
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("'%ls' has no imports\n", pwszName));
ffb50166c9adb4ae583b914d405197035cf890advboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync else
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("RTLDRPROP_IMPORT_COUNT failed with rc=%Rrc on '%ls'\n", rc, pwszName));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync}
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync/**
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync * Processes the list of import todos.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsyncstatic void supR3HardenedWinVerifyCacheProcessImportTodos(void)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync{
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Work until we've got nothing more todo.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync for (;;)
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync {
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync PVERIFIERCACHEIMPORT pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoImports, NULL, PVERIFIERCACHEIMPORT);
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync if (!pTodo)
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync break;
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync do
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync {
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync PVERIFIERCACHEIMPORT pCur = pTodo;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pTodo = pTodo->pNext;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Not in the cached already?
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if ( !supR3HardenedWinVerifyCacheLookupImport(g_System32NtPath.UniStr.Buffer,
c10a6f0c7041e4d1ee50ad38425aab9d43c55522vboxsync g_System32NtPath.UniStr.Length / sizeof(WCHAR),
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pCur->szName)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && !supR3HardenedWinVerifyCacheLookupImport(g_SupLibHardenedExeNtPath.UniStr.Buffer,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync g_offSupLibHardenedExeNtName,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pCur->szName)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync && ( pCur->cwcAltSearchDir == 0
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync || !supR3HardenedWinVerifyCacheLookupImport(pCur->pwszAltSearchDir, pCur->cwcAltSearchDir, pCur->szName)) )
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Try locate the imported DLL and open it.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Processing '%s'...\n", pCur->szName));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NTSTATUS rcNt;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NTSTATUS rcNtRedir = 0x22222222;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hFile = INVALID_HANDLE_VALUE;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RTUTF16 wszPath[260 + 260]; /* Assumes we've limited the import name length to 256. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync AssertCompile(sizeof(wszPath) > sizeof(g_System32NtPath));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Check for DLL isolation / redirection / mapping.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync size_t cwcName = 260;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PRTUTF16 pwszName = &wszPath[0];
0dd3967035b8a02985920baa57f948dc542b9388vboxsync int rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (RT_SUCCESS(rc))
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync UNICODE_STRING UniStrName;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UniStrName.Buffer = wszPath;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UniStrName.Length = (USHORT)cwcName * sizeof(WCHAR);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UNICODE_STRING UniStrStatic;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync UniStrStatic.Buffer = &wszPath[cwcName + 1];
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UniStrStatic.Length = 0;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UniStrStatic.MaximumLength = (USHORT)(sizeof(wszPath) - cwcName * sizeof(WCHAR) - sizeof(WCHAR));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PUNICODE_STRING pUniStrResult = NULL;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync rcNtRedir = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &UniStrName,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync (PUNICODE_STRING)&s_DefaultSuffix,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &UniStrStatic,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &UniStrDynamic,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &pUniStrResult,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync NULL /*pNewFlags*/,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync NULL /*pcbFilename*/,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync NULL /*pcbNeeded*/);
0dd3967035b8a02985920baa57f948dc542b9388vboxsync if (NT_SUCCESS(rcNtRedir))
0dd3967035b8a02985920baa57f948dc542b9388vboxsync {
0dd3967035b8a02985920baa57f948dc542b9388vboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync OBJECT_ATTRIBUTES ObjAttr;
0dd3967035b8a02985920baa57f948dc542b9388vboxsync InitializeObjectAttributes(&ObjAttr, pUniStrResult,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
0dd3967035b8a02985920baa57f948dc542b9388vboxsync rcNt = NtCreateFile(&hFile,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync &ObjAttr,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync &Ios,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync NULL /* Allocation Size*/,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync FILE_ATTRIBUTE_NORMAL,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync FILE_SHARE_READ,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync FILE_OPEN,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
0dd3967035b8a02985920baa57f948dc542b9388vboxsync NULL /*EaBuffer*/,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync 0 /*EaLength*/);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (NT_SUCCESS(rcNt))
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync rcNt = Ios.Status;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (NT_SUCCESS(rcNt))
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync /* For accurate logging. */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync size_t cwcCopy = RT_MIN(pUniStrResult->Length / sizeof(RTUTF16), RT_ELEMENTS(wszPath) - 1);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync memcpy(wszPath, pUniStrResult->Buffer, cwcCopy * sizeof(RTUTF16));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync wszPath[cwcCopy] = '\0';
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync hFile = INVALID_HANDLE_VALUE;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync RtlFreeUnicodeString(&UniStrDynamic);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #1 failed: %Rrc\n", rc));
0dd3967035b8a02985920baa57f948dc542b9388vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync /*
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync * If not something that gets remapped, do the half normal searching we need.
0dd3967035b8a02985920baa57f948dc542b9388vboxsync */
0dd3967035b8a02985920baa57f948dc542b9388vboxsync if (hFile == INVALID_HANDLE_VALUE)
0dd3967035b8a02985920baa57f948dc542b9388vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync struct
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PRTUTF16 pawcDir;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync uint32_t cwcDir;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync } Tmp, aDirs[] =
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync { g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR) },
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync { g_SupLibHardenedExeNtPath.UniStr.Buffer, g_offSupLibHardenedExeNtName - 1 },
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync { pCur->pwszAltSearchDir, pCur->cwcAltSearchDir },
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync };
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync /* Search System32 first, unless it's a 'V*' or 'm*' name, the latter for msvcrt. */
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync if ( pCur->szName[0] == 'v'
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync || pCur->szName[0] == 'V'
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync || pCur->szName[0] == 'm'
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync || pCur->szName[0] == 'M')
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync {
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync Tmp = aDirs[0];
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync aDirs[0] = aDirs[1];
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync aDirs[1] = Tmp;
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync }
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync for (uint32_t i = 0; i < RT_ELEMENTS(aDirs); i++)
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync {
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync if (aDirs[i].pawcDir && aDirs[i].cwcDir && aDirs[i].cwcDir < RT_ELEMENTS(wszPath) / 3 * 2)
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync {
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync memcpy(wszPath, aDirs[i].pawcDir, aDirs[i].cwcDir * sizeof(RTUTF16));
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync uint32_t cwc = aDirs[i].cwcDir;
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync wszPath[cwc++] = '\\';
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync cwcName = RT_ELEMENTS(wszPath) - cwc;
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync pwszName = &wszPath[cwc];
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync rc = RTStrToUtf16Ex(pCur->szName, RTSTR_MAX, &pwszName, cwcName, &cwcName);
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync if (RT_SUCCESS(rc))
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync UNICODE_STRING NtName;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync NtName.Buffer = wszPath;
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync NtName.Length = (USHORT)((cwc + cwcName) * sizeof(WCHAR));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync OBJECT_ATTRIBUTES ObjAttr;
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync rcNt = NtCreateFile(&hFile,
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &ObjAttr,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync &Ios,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync NULL /* Allocation Size*/,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FILE_ATTRIBUTE_NORMAL,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FILE_SHARE_READ,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FILE_OPEN,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync NULL /*EaBuffer*/,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync 0 /*EaLength*/);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (NT_SUCCESS(rcNt))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync rcNt = Ios.Status;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (NT_SUCCESS(rcNt))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync break;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync hFile = INVALID_HANDLE_VALUE;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync else
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: RTStrToUtf16Ex #2 failed: %Rrc\n", rc));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * If we successfully opened it, verify it and cache the result.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (hFile != INVALID_HANDLE_VALUE)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' -> '%ls' [rcNtRedir=%#x]\n",
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync pCur->szName, wszPath, rcNtRedir));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync ULONG fAccess = 0;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync ULONG fProtect = 0;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync bool fCallRealApi = false;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, false /*fIgnoreArch*/, &fAccess, &fProtect,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync &fCallRealApi, "Imports", false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NtClose(hFile);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: Failed to locate '%s'\n", pCur->szName));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessImportTodos: '%s' is in the cache.\n", pCur->szName));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync RTMemFree(pCur);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync } while (pTodo);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync}
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync/**
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * Processes the list of WinVerifyTrust todos.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsyncstatic void supR3HardenedWinVerifyCacheProcessWvtTodos(void)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync{
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pReschedule = NULL;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PVERIFIERCACHEENTRY volatile *ppReschedLastNext = NULL;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Work until we've got nothing more todo.
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync for (;;)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (!supHardenedWinIsWinVerifyTrustCallable())
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync break;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PVERIFIERCACHEENTRY pTodo = ASMAtomicXchgPtrT(&g_pVerifierCacheTodoWvt, NULL, PVERIFIERCACHEENTRY);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (!pTodo)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync break;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync do
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pCur = pTodo;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync pTodo = pTodo->pNextTodoWvt;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync pCur->pNextTodoWvt = NULL;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if ( !pCur->fWinVerifyTrust
ffb50166c9adb4ae583b914d405197035cf890advboxsync && RT_SUCCESS(pCur->rc))
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync bool fWinVerifyTrust = false;
ffb50166c9adb4ae583b914d405197035cf890advboxsync int rc = supHardenedWinVerifyImageTrust(pCur->hFile, pCur->wszPath, pCur->fFlags, pCur->rc,
ffb50166c9adb4ae583b914d405197035cf890advboxsync &fWinVerifyTrust, NULL /* pErrInfo*/);
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (RT_FAILURE(rc) || fWinVerifyTrust)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
ffb50166c9adb4ae583b914d405197035cf890advboxsync rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
ffb50166c9adb4ae583b914d405197035cf890advboxsync pCur->fWinVerifyTrust = true;
ffb50166c9adb4ae583b914d405197035cf890advboxsync pCur->rc = rc;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
ffb50166c9adb4ae583b914d405197035cf890advboxsync else
ffb50166c9adb4ae583b914d405197035cf890advboxsync {
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync /* Retry it at a later time. */
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync SUP_DPRINTF(("supR3HardenedWinVerifyCacheProcessWvtTodos: %d (was %d) fWinVerifyTrust=%d for '%ls' [rescheduled]\n",
ffb50166c9adb4ae583b914d405197035cf890advboxsync rc, pCur->rc, fWinVerifyTrust, pCur->wszPath));
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (!pReschedule)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync ppReschedLastNext = &pCur->pNextTodoWvt;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync pCur->pNextTodoWvt = pReschedule;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync }
f75c6db919d277952ca03b7acf643e5e3ac96cafvboxsync /* else: already processed. */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync } while (pTodo);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync /*
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * Anything to reschedule.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (pReschedule)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync do
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync *ppReschedLastNext = g_pVerifierCacheTodoWvt;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync while (!ASMAtomicCmpXchgPtr(&g_pVerifierCacheTodoWvt, pReschedule, *ppReschedLastNext));
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync}
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync/**
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * Checks whether the path could be containing alternative 8.3 names generated
0dd3967035b8a02985920baa57f948dc542b9388vboxsync * by NTFS, FAT, or other similar file systems.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync *
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * @returns Pointer to the first component that might be an 8.3 name, NULL if
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * not 8.3 path.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * @param pwszPath The path to check.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsyncstatic PRTUTF16 supR3HardenedWinIsPossible8dot3Path(PCRTUTF16 pwszPath)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync{
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync PCRTUTF16 pwszName = pwszPath;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync for (;;)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync RTUTF16 wc = *pwszPath++;
0dd3967035b8a02985920baa57f948dc542b9388vboxsync if (wc == '~')
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync /* Could check more here before jumping to conclusions... */
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (pwszPath - pwszName <= 8+1+3)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync return (PRTUTF16)pwszName;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else if (wc == '\\' || wc == '/' || wc == ':')
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync pwszName = pwszPath;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync else if (wc == 0)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync break;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync }
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync return NULL;
ffb50166c9adb4ae583b914d405197035cf890advboxsync}
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
ffb50166c9adb4ae583b914d405197035cf890advboxsync/**
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * Fixes up a path possibly containing one or more alternative 8-dot-3 style
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync * components.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync *
ffb50166c9adb4ae583b914d405197035cf890advboxsync * The path is fixed up in place. Errors are ignored.
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync *
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync * @param hFile The handle to the file which path we're fixing up.
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync * @param pUniStr The path to fix up. MaximumLength is the max buffer
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync * length.
ffb50166c9adb4ae583b914d405197035cf890advboxsync */
ffb50166c9adb4ae583b914d405197035cf890advboxsyncstatic void supR3HardenedWinFix8dot3Path(HANDLE hFile, PUNICODE_STRING pUniStr)
ffb50166c9adb4ae583b914d405197035cf890advboxsync{
ffb50166c9adb4ae583b914d405197035cf890advboxsync /*
ffb50166c9adb4ae583b914d405197035cf890advboxsync * We could use FileNormalizedNameInformation here and slap the volume device
ffb50166c9adb4ae583b914d405197035cf890advboxsync * path in front of the result, but it's only supported since windows 8.0
ffb50166c9adb4ae583b914d405197035cf890advboxsync * according to some docs... So we expand all supicious names.
ffb50166c9adb4ae583b914d405197035cf890advboxsync */
ffb50166c9adb4ae583b914d405197035cf890advboxsync PRTUTF16 pwszFix = pUniStr->Buffer;
ffb50166c9adb4ae583b914d405197035cf890advboxsync while (*pwszFix)
ffb50166c9adb4ae583b914d405197035cf890advboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync pwszFix = supR3HardenedWinIsPossible8dot3Path(pwszFix);
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (pwszFix == NULL)
ffb50166c9adb4ae583b914d405197035cf890advboxsync break;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync RTUTF16 wc;
ffb50166c9adb4ae583b914d405197035cf890advboxsync PRTUTF16 pwszFixEnd = pwszFix;
ffb50166c9adb4ae583b914d405197035cf890advboxsync while ((wc = *pwszFixEnd) != '\0' && wc != '\\' && wc != '/')
ffb50166c9adb4ae583b914d405197035cf890advboxsync pwszFixEnd++;
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (wc == '\0')
ffb50166c9adb4ae583b914d405197035cf890advboxsync break;
ffb50166c9adb4ae583b914d405197035cf890advboxsync
ffb50166c9adb4ae583b914d405197035cf890advboxsync RTUTF16 const wcSaved = *pwszFix;
ffb50166c9adb4ae583b914d405197035cf890advboxsync *pwszFix = '\0'; /* paranoia. */
ffb50166c9adb4ae583b914d405197035cf890advboxsync
3c6306a66deef467e3c13483dd6529e1e1c6b822vboxsync UNICODE_STRING NtDir;
ffb50166c9adb4ae583b914d405197035cf890advboxsync NtDir.Buffer = pUniStr->Buffer;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NtDir.Length = NtDir.MaximumLength = (USHORT)((pwszFix - pUniStr->Buffer) * sizeof(WCHAR));
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync HANDLE hDir = RTNT_INVALID_HANDLE_VALUE;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync
ffb50166c9adb4ae583b914d405197035cf890advboxsync OBJECT_ATTRIBUTES ObjAttr;
ffb50166c9adb4ae583b914d405197035cf890advboxsync InitializeObjectAttributes(&ObjAttr, &NtDir, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
ffb50166c9adb4ae583b914d405197035cf890advboxsync
ffb50166c9adb4ae583b914d405197035cf890advboxsync NTSTATUS rcNt = NtCreateFile(&hDir,
ffb50166c9adb4ae583b914d405197035cf890advboxsync FILE_READ_DATA | SYNCHRONIZE,
ffb50166c9adb4ae583b914d405197035cf890advboxsync &ObjAttr,
ffb50166c9adb4ae583b914d405197035cf890advboxsync &Ios,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NULL /* Allocation Size*/,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync FILE_ATTRIBUTE_NORMAL,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FILE_OPEN,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync NULL /*EaBuffer*/,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync 0 /*EaLength*/);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync *pwszFix = wcSaved;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync if (NT_SUCCESS(rcNt))
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync union
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FILE_BOTH_DIR_INFORMATION Info;
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync uint8_t abBuffer[sizeof(FILE_BOTH_DIR_INFORMATION) + 2048 * sizeof(WCHAR)];
b4d7b4dbcc45b8bde7502aa129440d92d7ffd038vboxsync } uBuf;
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync RT_ZERO(uBuf);
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
ffb50166c9adb4ae583b914d405197035cf890advboxsync UNICODE_STRING NtFilterStr;
ffb50166c9adb4ae583b914d405197035cf890advboxsync NtFilterStr.Buffer = pwszFix;
ffb50166c9adb4ae583b914d405197035cf890advboxsync NtFilterStr.Length = (USHORT)((uintptr_t)pwszFixEnd - (uintptr_t)pwszFix);
ffb50166c9adb4ae583b914d405197035cf890advboxsync NtFilterStr.MaximumLength = NtFilterStr.Length;
ffb50166c9adb4ae583b914d405197035cf890advboxsync rcNt = NtQueryDirectoryFile(hDir,
ffb50166c9adb4ae583b914d405197035cf890advboxsync NULL /* Event */,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NULL /* ApcRoutine */,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NULL /* ApcContext */,
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync &Ios,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync &uBuf,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync sizeof(uBuf) - sizeof(WCHAR),
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FileBothDirectoryInformation,
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync FALSE /*ReturnSingleEntry*/,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync &NtFilterStr,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync FALSE /*RestartScan */);
ffb50166c9adb4ae583b914d405197035cf890advboxsync if (NT_SUCCESS(rcNt) && uBuf.Info.NextEntryOffset == 0) /* There shall only be one entry matching... */
ffb50166c9adb4ae583b914d405197035cf890advboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync uint32_t offName = uBuf.Info.FileNameLength / sizeof(WCHAR);
ffb50166c9adb4ae583b914d405197035cf890advboxsync while (offName > 0 && uBuf.Info.FileName[offName - 1] != '\\' && uBuf.Info.FileName[offName - 1] != '/')
ffb50166c9adb4ae583b914d405197035cf890advboxsync offName--;
ffb50166c9adb4ae583b914d405197035cf890advboxsync uint32_t cwcNameNew = (uBuf.Info.FileNameLength / sizeof(WCHAR)) - offName;
ffb50166c9adb4ae583b914d405197035cf890advboxsync uint32_t cwcNameOld = pwszFixEnd - pwszFix;
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync if (cwcNameOld == cwcNameNew)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync memcpy(pwszFix, &uBuf.Info.FileName[offName], cwcNameNew * sizeof(WCHAR));
ffb50166c9adb4ae583b914d405197035cf890advboxsync else if ( pUniStr->Length + cwcNameNew * sizeof(WCHAR) - cwcNameOld * sizeof(WCHAR) + sizeof(WCHAR)
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync <= pUniStr->MaximumLength)
c89333d3e41e439ed9e74768000edc399d3e72e6vboxsync {
ffb50166c9adb4ae583b914d405197035cf890advboxsync size_t cwcLeft = pUniStr->Length - (pwszFixEnd - pUniStr->Buffer) * sizeof(WCHAR) + sizeof(WCHAR);
ffb50166c9adb4ae583b914d405197035cf890advboxsync memmove(&pwszFix[cwcNameNew], pwszFixEnd, cwcLeft * sizeof(WCHAR));
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync pUniStr->Length -= (USHORT)(cwcNameOld * sizeof(WCHAR));
ffb50166c9adb4ae583b914d405197035cf890advboxsync pUniStr->Length += (USHORT)(cwcNameNew * sizeof(WCHAR));
ffb50166c9adb4ae583b914d405197035cf890advboxsync pwszFixEnd -= cwcNameOld;
ffb50166c9adb4ae583b914d405197035cf890advboxsync pwszFixEnd -= cwcNameNew;
ffb50166c9adb4ae583b914d405197035cf890advboxsync memcpy(pwszFix, &uBuf.Info.FileName[offName], cwcNameNew * sizeof(WCHAR));
ffb50166c9adb4ae583b914d405197035cf890advboxsync }
ffb50166c9adb4ae583b914d405197035cf890advboxsync /* else: ignore overflow. */
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync }
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync /* else: ignore failure. */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync NtClose(hDir);
750df3fe104e01cadbc3d5bd20243055d283d4e5vboxsync }
ffb50166c9adb4ae583b914d405197035cf890advboxsync
ffb50166c9adb4ae583b914d405197035cf890advboxsync /* Advance */
ffb50166c9adb4ae583b914d405197035cf890advboxsync pwszFix = pwszFixEnd;
ffb50166c9adb4ae583b914d405197035cf890advboxsync }
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync}
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync/**
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * Screens an image file or file mapped with execute access.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync *
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @returns NT status code.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param hFile The file handle.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param fImage Set if image file mapping being made
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * (NtCreateSection thing).
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param fIgnoreArch Using the DONT_RESOLVE_DLL_REFERENCES flag,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * which also implies that DLL init / term code
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * isn't called, so the architecture should be
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * ignored.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param pfAccess Pointer to the NtCreateSection access flags,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * so we can modify them if necessary.
ffb50166c9adb4ae583b914d405197035cf890advboxsync * @param pfProtect Pointer to the NtCreateSection protection
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * flags, so we can modify them if necessary.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param pfCallRealApi Whether it's ok to go on to the real API.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param pszCaller Who is calling (for debugging / logging).
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * @param fAvoidWinVerifyTrust Whether we should avoid WinVerifyTrust.
ffb50166c9adb4ae583b914d405197035cf890advboxsync * @param pfQuiet Where to return whether to be quiet about
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * this image in the log (i.e. we've seen it
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * lots of times already). Optional.
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync */
6475559a7e0e52892efbab4fbdedc879f6866109vboxsyncstatic NTSTATUS supR3HardenedScreenImage(HANDLE hFile, bool fImage, bool fIgnoreArch, PULONG pfAccess, PULONG pfProtect,
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync bool *pfCallRealApi, const char *pszCaller, bool fAvoidWinVerifyTrust, bool *pfQuiet)
ffb50166c9adb4ae583b914d405197035cf890advboxsync{
ffb50166c9adb4ae583b914d405197035cf890advboxsync *pfCallRealApi = false;
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync if (pfQuiet)
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync *pfQuiet = false;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync /*
6475559a7e0e52892efbab4fbdedc879f6866109vboxsync * Query the name of the file, making sure to zero terminator the
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * string. (2nd half of buffer is used for error info, see below.)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync union
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync UNICODE_STRING UniStr;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uint8_t abBuffer[sizeof(UNICODE_STRING) + 2048 * sizeof(WCHAR)];
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync } uBuf;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync RT_ZERO(uBuf);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync ULONG cbNameBuf;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (!NT_SUCCESS(rcNt))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync supR3HardenedError(VINF_SUCCESS, false,
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync "supR3HardenedScreenImage/%s: NtQueryObject -> %#x (fImage=%d fProtect=%#x fAccess=%#x)\n",
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync pszCaller, fImage, *pfProtect, *pfAccess);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync return rcNt;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (supR3HardenedWinIsPossible8dot3Path(uBuf.UniStr.Buffer))
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync uBuf.UniStr.MaximumLength = sizeof(uBuf) - 128;
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync supR3HardenedWinFix8dot3Path(hFile, &uBuf.UniStr);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync }
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /*
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync * Check the cache.
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync */
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync PVERIFIERCACHEENTRY pCacheHit = supR3HardenedWinVerifyCacheLookup(&uBuf.UniStr, hFile);
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync if (pCacheHit)
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync {
553a2f0d8ef91a6dad8de4eef206ff093af53a5dvboxsync /* Do hit accounting and figure whether we need to be quiet or not. */
uint32_t cHits = ASMAtomicIncU32(&pCacheHit->cHits);
bool const fQuiet = cHits >= 8 && !RT_IS_POWER_OF_TWO(cHits);
if (pfQuiet)
*pfQuiet = fQuiet;
/* If we haven't done the WinVerifyTrust thing, do it if we can. */
if ( !pCacheHit->fWinVerifyTrust
&& RT_SUCCESS(pCacheHit->rc)
&& supHardenedWinIsWinVerifyTrustCallable() )
{
if (!fAvoidWinVerifyTrust)
{
SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [redoing WinVerifyTrust]\n",
pszCaller, pCacheHit->rc, pCacheHit->wszPath));
bool fWinVerifyTrust = false;
int rc = supHardenedWinVerifyImageTrust(pCacheHit->hFile, pCacheHit->wszPath, pCacheHit->fFlags, pCacheHit->rc,
&fWinVerifyTrust, NULL /* pErrInfo*/);
if (RT_FAILURE(rc) || fWinVerifyTrust)
{
SUP_DPRINTF(("supR3HardenedScreenImage/%s: %d (was %d) fWinVerifyTrust=%d for '%ls'\n",
pszCaller, rc, pCacheHit->rc, fWinVerifyTrust, pCacheHit->wszPath));
pCacheHit->fWinVerifyTrust = true;
pCacheHit->rc = rc;
}
else
SUP_DPRINTF(("supR3HardenedScreenImage/%s: WinVerifyTrust not available, rescheduling %ls\n",
pszCaller, pCacheHit->wszPath));
}
else
SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls [avoiding WinVerifyTrust]\n",
pszCaller, pCacheHit->rc, pCacheHit->wszPath));
}
else if (!fQuiet || !pCacheHit->fWinVerifyTrust)
SUP_DPRINTF(("supR3HardenedScreenImage/%s: cache hit (%Rrc) on %ls%s\n",
pszCaller, pCacheHit->rc, pCacheHit->wszPath, pCacheHit->fWinVerifyTrust ? "" : " [lacks WinVerifyTrust]"));
/* Return the cached value. */
if (RT_SUCCESS(pCacheHit->rc))
{
*pfCallRealApi = true;
return STATUS_SUCCESS;
}
if (!fQuiet)
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: cached rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x cHits=%u %ls\n",
pszCaller, pCacheHit->rc, fImage, *pfProtect, *pfAccess, cHits, uBuf.UniStr.Buffer);
return STATUS_TRUST_FAILURE;
}
/*
* On XP the loader might hand us handles with just FILE_EXECUTE and
* SYNCHRONIZE, the means reading will fail later on. Also, we need
* READ_CONTROL access to check the file ownership later on, and non
* of the OS versions seems be giving us that. So, in effect we
* more or less always reopen the file here.
*/
HANDLE hMyFile = NULL;
rcNt = NtDuplicateObject(NtCurrentProcess(), hFile, NtCurrentProcess(),
&hMyFile,
FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
0 /* Handle attributes*/, 0 /* Options */);
if (!NT_SUCCESS(rcNt))
{
if (rcNt == STATUS_ACCESS_DENIED)
{
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &uBuf.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
rcNt = NtCreateFile(&hMyFile,
FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (!NT_SUCCESS(rcNt))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
pszCaller, rcNt, hFile, uBuf.UniStr.Buffer);
return rcNt;
}
/* Check that we've got the same file. */
LARGE_INTEGER idMyFile, idInFile;
bool fMyValid = supR3HardenedWinVerifyCacheGetIndexNumber(hMyFile, &idMyFile);
bool fInValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &idInFile);
if ( fMyValid
&& ( fMyValid != fInValid
|| idMyFile.QuadPart != idInFile.QuadPart))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: Re-opened has different ID that input: %#llx vx %#llx (%ls)\n",
pszCaller, rcNt, idMyFile.QuadPart, idInFile.QuadPart, uBuf.UniStr.Buffer);
NtClose(hMyFile);
return STATUS_TRUST_FAILURE;
}
}
else
{
SUP_DPRINTF(("supR3HardenedScreenImage/%s: NtDuplicateObject -> %#x\n", pszCaller, rcNt));
#ifdef DEBUG
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: NtDuplicateObject(,%#x,) failed: %#x\n", pszCaller, hFile, rcNt);
#endif
hMyFile = hFile;
}
}
/*
* Special Kludge for Windows XP and W2K3 and their stupid attempts
* at mapping a hidden XML file called c:\Windows\WindowsShell.Manifest
* with executable access. The image bit isn't set, fortunately.
*/
if ( !fImage
&& uBuf.UniStr.Length > g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)
&& memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer,
g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) == 0)
{
PRTUTF16 pwszName = &uBuf.UniStr.Buffer[(g_System32NtPath.UniStr.Length - sizeof(L"System32") + sizeof(WCHAR)) / sizeof(WCHAR)];
if (RTUtf16ICmpAscii(pwszName, "WindowsShell.Manifest") == 0)
{
/*
* Drop all executable access to the mapping and let it continue.
*/
SUP_DPRINTF(("supR3HardenedScreenImage/%s: Applying the drop-exec-kludge for '%ls'\n", pszCaller, uBuf.UniStr.Buffer));
if (*pfAccess & SECTION_MAP_EXECUTE)
*pfAccess = (*pfAccess & ~SECTION_MAP_EXECUTE) | SECTION_MAP_READ;
if (*pfProtect & PAGE_EXECUTE)
*pfProtect = (*pfProtect & ~PAGE_EXECUTE) | PAGE_READONLY;
*pfProtect = (*pfProtect & ~UINT32_C(0xf0)) | ((*pfProtect & UINT32_C(0xe0)) >> 4);
if (hMyFile != hFile)
NtClose(hMyFile);
*pfCallRealApi = true;
return STATUS_SUCCESS;
}
}
#ifndef VBOX_PERMIT_EVEN_MORE
/*
* Check the path. We don't allow DLLs to be loaded from just anywhere:
* 1. System32 - normal code or cat signing, owner TrustedInstaller.
* 2. WinSxS - normal code or cat signing, owner TrustedInstaller.
* 3. VirtualBox - kernel code signing and integrity checks.
* 4. AppPatchDir - normal code or cat signing, owner TrustedInstaller.
* 5. Program Files - normal code or cat signing, owner TrustedInstaller.
* 6. Common Files - normal code or cat signing, owner TrustedInstaller.
* 7. x86 variations of 4 & 5 - ditto.
*/
Assert(g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] == '\\');
uint32_t fFlags = 0;
if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_System32NtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_WinSxSNtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
else if (supHardViUtf16PathStartsWithEx(uBuf.UniStr.Buffer, uBuf.UniStr.Length / sizeof(WCHAR),
g_SupLibHardenedExeNtPath.UniStr.Buffer,
g_offSupLibHardenedExeNtName, false /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
# ifdef VBOX_PERMIT_MORE
else if (supHardViIsAppPatchDir(uBuf.UniStr.Buffer, uBuf.UniStr.Length / sizeof(WCHAR)))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesNtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesNtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
# ifdef RT_ARCH_AMD64
else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_ProgramFilesX86NtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
else if (supHardViUniStrPathStartsWithUniStr(&uBuf.UniStr, &g_CommonFilesX86NtPath.UniStr, true /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
# endif
# endif
# ifdef VBOX_PERMIT_VISUAL_STUDIO_PROFILING
/* Hack to allow profiling our code with Visual Studio. */
else if ( uBuf.UniStr.Length > sizeof(L"\\SamplingRuntime.dll")
&& memcmp(uBuf.UniStr.Buffer + (uBuf.UniStr.Length - sizeof(L"\\SamplingRuntime.dll") + sizeof(WCHAR)) / sizeof(WCHAR),
L"\\SamplingRuntime.dll", sizeof(L"\\SamplingRuntime.dll") - sizeof(WCHAR)) == 0 )
{
if (hMyFile != hFile)
NtClose(hMyFile);
*pfCallRealApi = true;
return STATUS_SUCCESS;
}
# endif
else
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: Not a trusted location: '%ls' (fImage=%d fProtect=%#x fAccess=%#x)\n",
pszCaller, uBuf.UniStr.Buffer, fImage, *pfAccess, *pfProtect);
if (hMyFile != hFile)
NtClose(hMyFile);
return STATUS_TRUST_FAILURE;
}
#else /* VBOX_PERMIT_EVEN_MORE */
/*
* Require trusted installer + some kind of signature on everything, except
* for the VBox bits where we require kernel code signing and special
* integrity checks.
*/
Assert(g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] == '\\');
uint32_t fFlags = 0;
if (supHardViUtf16PathStartsWithEx(uBuf.UniStr.Buffer, uBuf.UniStr.Length / sizeof(WCHAR),
g_SupLibHardenedExeNtPath.UniStr.Buffer,
g_offSupLibHardenedExeNtName, false /*fCheckSlash*/))
fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
else
fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION | SUPHNTVI_F_TRUSTED_INSTALLER_OWNER;
#endif /* VBOX_PERMIT_EVEN_MORE */
/*
* Do the verification. For better error message we borrow what's
* left of the path buffer for an RTERRINFO buffer.
*/
if (fIgnoreArch)
fFlags |= SUPHNTVI_F_IGNORE_ARCHITECTURE;
RTERRINFO ErrInfo;
RTErrInfoInit(&ErrInfo, (char *)&uBuf.abBuffer[cbNameBuf], sizeof(uBuf) - cbNameBuf);
int rc;
bool fWinVerifyTrust = false;
rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, fAvoidWinVerifyTrust, &fWinVerifyTrust, &ErrInfo);
if (RT_FAILURE(rc))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedScreenImage/%s: rc=%Rrc fImage=%d fProtect=%#x fAccess=%#x %ls: %s\n",
pszCaller, rc, fImage, *pfAccess, *pfProtect, uBuf.UniStr.Buffer, ErrInfo.pszMsg);
if (hMyFile != hFile)
supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
return STATUS_TRUST_FAILURE;
}
/*
* Insert into the cache.
*/
if (hMyFile != hFile)
supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fWinVerifyTrust, fFlags);
*pfCallRealApi = true;
return STATUS_SUCCESS;
}
/**
* Preloads a file into the verify cache if possible.
*
* This is used to avoid known cyclic LoadLibrary issues with WinVerifyTrust.
*
* @param pwszName The name of the DLL to verify.
*/
DECLHIDDEN(void) supR3HardenedWinVerifyCachePreload(PCRTUTF16 pwszName)
{
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
UNICODE_STRING UniStr;
UniStr.Buffer = (PWCHAR)pwszName;
UniStr.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
NTSTATUS rcNt = NtCreateFile(&hFile,
FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (!NT_SUCCESS(rcNt))
{
SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: Error %#x opening '%ls'.\n", rcNt, pwszName));
return;
}
ULONG fAccess = 0;
ULONG fProtect = 0;
bool fCallRealApi;
//SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: scanning %ls\n", pwszName));
supR3HardenedScreenImage(hFile, false, false /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi, "preload",
false /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
//SUP_DPRINTF(("supR3HardenedWinVerifyCachePreload: done %ls\n", pwszName));
NtClose(hFile);
}
/**
* Hook that monitors NtCreateSection calls.
*
* @returns NT status code.
* @param phSection Where to return the section handle.
* @param fAccess The desired access.
* @param pObjAttribs The object attributes (optional).
* @param pcbSection The section size (optional).
* @param fProtect The max section protection.
* @param fAttribs The section attributes.
* @param hFile The file to create a section from (optional).
*/
static NTSTATUS NTAPI
supR3HardenedMonitor_NtCreateSection(PHANDLE phSection, ACCESS_MASK fAccess, POBJECT_ATTRIBUTES pObjAttribs,
PLARGE_INTEGER pcbSection, ULONG fProtect, ULONG fAttribs, HANDLE hFile)
{
if ( hFile != NULL
&& hFile != INVALID_HANDLE_VALUE)
{
bool const fImage = RT_BOOL(fAttribs & (SEC_IMAGE | SEC_PROTECTED_IMAGE));
bool const fExecMap = RT_BOOL(fAccess & SECTION_MAP_EXECUTE);
bool const fExecProt = RT_BOOL(fProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
| PAGE_EXECUTE_READWRITE));
if (fImage || fExecMap || fExecProt)
{
DWORD dwSavedLastError = RtlGetLastWin32Error();
bool fCallRealApi;
//SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 1\n"));
NTSTATUS rcNt = supR3HardenedScreenImage(hFile, fImage, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
"NtCreateSection", true /*fAvoidWinVerifyTrust*/, NULL /*pfQuiet*/);
//SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: 2 rcNt=%#x fCallRealApi=%#x\n", rcNt, fCallRealApi));
RtlRestoreLastWin32Error(dwSavedLastError);
if (!NT_SUCCESS(rcNt))
return rcNt;
Assert(fCallRealApi);
if (!fCallRealApi)
return STATUS_TRUST_FAILURE;
}
}
/*
* Call checked out OK, call the original.
*/
return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
}
/**
* Helper for supR3HardenedMonitor_LdrLoadDll.
*
* @returns NT status code.
* @param pwszPath The path destination buffer.
* @param cwcPath The size of the path buffer.
* @param pUniStrResult The result string.
* @param pOrgName The orignal name (for errors).
* @param pcwc Where to return the actual length.
*/
static NTSTATUS supR3HardenedCopyRedirectionResult(WCHAR *pwszPath, size_t cwcPath, PUNICODE_STRING pUniStrResult,
PUNICODE_STRING pOrgName, UINT *pcwc)
{
UINT cwc;
*pcwc = cwc = pUniStrResult->Length / sizeof(WCHAR);
if (pUniStrResult->Buffer == pwszPath)
pwszPath[cwc] = '\0';
else
{
if (cwc > cwcPath - 1)
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_LdrLoadDll: Name too long: %.*ls -> %.*ls (RtlDosApplyFileIoslationRedirection_Ustr)\n",
pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer,
pUniStrResult->Length / sizeof(WCHAR), pUniStrResult->Buffer);
return STATUS_NAME_TOO_LONG;
}
memcpy(&pwszPath[0], pUniStrResult->Buffer, pUniStrResult->Length);
pwszPath[cwc] = '\0';
}
return STATUS_SUCCESS;
}
/**
* Hooks that intercepts LdrLoadDll calls.
*
* Two purposes:
* -# Enforce our own search path restrictions.
* -# Prevalidate DLLs about to be loaded so we don't upset the loader data
* by doing it from within the NtCreateSection hook (WinVerifyTrust
* seems to be doing harm there on W7/32).
*
* @returns
* @param pwszSearchPath The search path to use.
* @param pfFlags Flags on input. DLL characteristics or something
* on return?
* @param pName The name of the module.
* @param phMod Where the handle of the loaded DLL is to be
* returned to the caller.
*/
static NTSTATUS NTAPI
supR3HardenedMonitor_LdrLoadDll(PWSTR pwszSearchPath, PULONG pfFlags, PUNICODE_STRING pName, PHANDLE phMod)
{
DWORD dwSavedLastError = RtlGetLastWin32Error();
PUNICODE_STRING const pOrgName = pName;
NTSTATUS rcNt;
/*
* Make sure the DLL notification callback is registered. If we could, we
* would've done this during early process init, but due to lack of heap
* and uninitialized loader lock, it's not possible that early on.
*
* The callback protects our NtDll hooks from getting unhooked by
* "friendly" fire from the AV crowd.
*/
supR3HardenedWinRegisterDllNotificationCallback();
/*
* Process WinVerifyTrust todo before and after.
*/
supR3HardenedWinVerifyCacheProcessWvtTodos();
/*
* Reject things we don't want to deal with.
*/
if (!pName || pName->Length == 0)
{
supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: name is NULL or have a zero length.\n");
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x (pName=%p)\n", STATUS_INVALID_PARAMETER, pName));
RtlRestoreLastWin32Error(dwSavedLastError);
return STATUS_INVALID_PARAMETER;
}
/*SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls *pfFlags=%#x pwszSearchPath=%p:%ls\n",
(unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
!((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));*/
/*
* Reject long paths that's close to the 260 limit without looking.
*/
if (pName->Length > 256 * sizeof(WCHAR))
{
supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: too long name: %#x bytes\n", pName->Length);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
RtlRestoreLastWin32Error(dwSavedLastError);
return STATUS_NAME_TOO_LONG;
}
/*
* Absolute path?
*/
NTSTATUS rcNtResolve = STATUS_SUCCESS;
bool fSkipValidation = false;
bool fCheckIfLoaded = false;
WCHAR wszPath[260];
static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
PUNICODE_STRING pUniStrResult = NULL;
UNICODE_STRING ResolvedName;
if ( ( pName->Length >= 4 * sizeof(WCHAR)
&& RT_C_IS_ALPHA(pName->Buffer[0])
&& pName->Buffer[1] == ':'
&& RTPATH_IS_SLASH(pName->Buffer[2]) )
|| ( pName->Length >= 1 * sizeof(WCHAR)
&& RTPATH_IS_SLASH(pName->Buffer[1]) )
)
{
rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
pName,
(PUNICODE_STRING)&s_DefaultSuffix,
&UniStrStatic,
&UniStrDynamic,
&pUniStrResult,
NULL /*pNewFlags*/,
NULL /*pcbFilename*/,
NULL /*pcbNeeded*/);
if (NT_SUCCESS(rcNtResolve))
{
UINT cwc;
rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
RtlFreeUnicodeString(&UniStrDynamic);
if (!NT_SUCCESS(rcNt))
{
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
RtlRestoreLastWin32Error(dwSavedLastError);
return rcNt;
}
ResolvedName.Buffer = wszPath;
ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: '%.*ls' -> '%.*ls' [redir]\n",
(unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
ResolvedName.Length / sizeof(WCHAR), ResolvedName.Buffer, rcNt));
pName = &ResolvedName;
}
else
{
memcpy(wszPath, pName->Buffer, pName->Length);
wszPath[pName->Length / sizeof(WCHAR)] = '\0';
}
}
/*
* Not an absolute path. Check if it's one of those special API set DLLs
* or something we're known to use but should be taken from WinSxS.
*/
else if (supHardViUtf16PathStartsWithEx(pName->Buffer, pName->Length / sizeof(WCHAR),
L"api-ms-win-", 11, false /*fCheckSlash*/))
{
memcpy(wszPath, pName->Buffer, pName->Length);
wszPath[pName->Length / sizeof(WCHAR)] = '\0';
fSkipValidation = true;
}
/*
* Not an absolute path or special API set. There are two alternatives
* now, either there is no path at all or there is a relative path. We
* will resolve it to an absolute path in either case, failing the call
* if we can't.
*/
else
{
PCWCHAR pawcName = pName->Buffer;
uint32_t cwcName = pName->Length / sizeof(WCHAR);
uint32_t offLastSlash = UINT32_MAX;
uint32_t offLastDot = UINT32_MAX;
for (uint32_t i = 0; i < cwcName; i++)
switch (pawcName[i])
{
case '\\':
case '/':
offLastSlash = i;
offLastDot = UINT32_MAX;
break;
case '.':
offLastDot = i;
break;
}
bool const fNeedDllSuffix = offLastDot == UINT32_MAX && offLastSlash == UINT32_MAX;
if (offLastDot != UINT32_MAX && offLastDot == cwcName - 1)
cwcName--;
/*
* Reject relative paths for now as they might be breakout attempts.
*/
if (offLastSlash != UINT32_MAX)
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_LdrLoadDll: relative name not permitted: %.*ls\n",
cwcName, pawcName);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
RtlRestoreLastWin32Error(dwSavedLastError);
return STATUS_OBJECT_NAME_INVALID;
}
/*
* Perform dll redirection to WinSxS such. We using an undocumented
* API here, which as always is a bit risky... ASSUMES that the API
* returns a full DOS path.
*/
UINT cwc;
rcNtResolve = RtlDosApplyFileIsolationRedirection_Ustr(1 /*fFlags*/,
pName,
(PUNICODE_STRING)&s_DefaultSuffix,
&UniStrStatic,
&UniStrDynamic,
&pUniStrResult,
NULL /*pNewFlags*/,
NULL /*pcbFilename*/,
NULL /*pcbNeeded*/);
if (NT_SUCCESS(rcNtResolve))
{
rcNt = supR3HardenedCopyRedirectionResult(wszPath, RT_ELEMENTS(wszPath), pUniStrResult, pName, &cwc);
RtlFreeUnicodeString(&UniStrDynamic);
if (!NT_SUCCESS(rcNt))
{
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", rcNt));
RtlRestoreLastWin32Error(dwSavedLastError);
return rcNt;
}
}
else
{
/*
* Search for the DLL. Only System32 is allowed as the target of
* a search on the API level, all VBox calls will have full paths.
* If the DLL is not in System32, we will resort to check if it's
* refering to an already loaded DLL (fCheckIfLoaded).
*/
AssertCompile(sizeof(g_System32WinPath.awcBuffer) <= sizeof(wszPath));
cwc = g_System32WinPath.UniStr.Length / sizeof(RTUTF16); Assert(cwc > 2);
if (cwc + 1 + cwcName + fNeedDllSuffix * 4 >= RT_ELEMENTS(wszPath))
{
supR3HardenedError(VINF_SUCCESS, false,
"supR3HardenedMonitor_LdrLoadDll: Name too long (system32): %.*ls\n", cwcName, pawcName);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_NAME_TOO_LONG));
RtlRestoreLastWin32Error(dwSavedLastError);
return STATUS_NAME_TOO_LONG;
}
memcpy(wszPath, g_System32WinPath.UniStr.Buffer, cwc * sizeof(RTUTF16));
wszPath[cwc++] = '\\';
memcpy(&wszPath[cwc], pawcName, cwcName * sizeof(WCHAR));
cwc += cwcName;
if (!fNeedDllSuffix)
wszPath[cwc] = '\0';
else
{
memcpy(&wszPath[cwc], L".dll", 5 * sizeof(WCHAR));
cwc += 4;
}
fCheckIfLoaded = true;
}
ResolvedName.Buffer = wszPath;
ResolvedName.Length = (USHORT)(cwc * sizeof(WCHAR));
ResolvedName.MaximumLength = ResolvedName.Length + sizeof(WCHAR);
pName = &ResolvedName;
}
bool fQuiet = false;
if (!fSkipValidation)
{
/*
* Try open the file. If this fails, never mind, just pass it on to
* the real API as we've replaced any searchable name with a full name
* and the real API can come up with a fitting status code for it.
*/
HANDLE hRootDir;
UNICODE_STRING NtPathUniStr;
int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, wszPath, RTSTR_MAX);
if (RT_FAILURE(rc))
{
supR3HardenedError(rc, false,
"supR3HardenedMonitor_LdrLoadDll: RTNtPathFromWinUtf16Ex failed on '%ls': %Rrc\n", wszPath, rc);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
RtlRestoreLastWin32Error(dwSavedLastError);
return STATUS_OBJECT_NAME_INVALID;
}
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
rcNt = NtCreateFile(&hFile,
FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (NT_SUCCESS(rcNt))
{
ULONG fAccess = 0;
ULONG fProtect = 0;
bool fCallRealApi = false;
rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, RT_VALID_PTR(pfFlags) && (*pfFlags & 0x2) /*fIgnoreArch*/,
&fAccess, &fProtect, &fCallRealApi,
"LdrLoadDll", false /*fAvoidWinVerifyTrust*/, &fQuiet);
NtClose(hFile);
if (!NT_SUCCESS(rcNt))
{
if (!fQuiet)
{
if (pOrgName != pName)
supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls': rcNt=%#x\n",
wszPath, rcNt);
else
supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_LdrLoadDll: rejecting '%ls' (%.*ls): rcNt=%#x\n",
wszPath, pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNt);
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
}
RtlRestoreLastWin32Error(dwSavedLastError);
return rcNt;
}
supR3HardenedWinVerifyCacheProcessImportTodos();
}
else
{
DWORD dwErr = RtlGetLastWin32Error();
/*
* Deal with special case where the caller (first case was MS LifeCam)
* is using LoadLibrary instead of GetModuleHandle to find a loaded DLL.
*/
NTSTATUS rcNtGetDll = STATUS_SUCCESS;
if ( fCheckIfLoaded
&& ( rcNt == STATUS_OBJECT_NAME_NOT_FOUND
|| rcNt == STATUS_OBJECT_PATH_NOT_FOUND))
{
rcNtGetDll = LdrGetDllHandle(NULL /*DllPath*/, NULL /*pfFlags*/, pOrgName, phMod);
if (NT_SUCCESS(rcNtGetDll))
{
RtlRestoreLastWin32Error(dwSavedLastError);
return rcNtGetDll;
}
}
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u (NtPath=%.*ls; Input=%.*ls; rcNtGetDll=%#x\n",
wszPath, dwErr, NtPathUniStr.Length / sizeof(RTUTF16), NtPathUniStr.Buffer,
pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtGetDll));
}
RTNtPathFree(&NtPathUniStr, &hRootDir);
}
/*
* Screened successfully enough. Call the real thing.
*/
if (!fQuiet)
{
if (pOrgName != pName)
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (Input=%.*ls, rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
(unsigned)pName->Length / sizeof(WCHAR), pName->Buffer,
(unsigned)pOrgName->Length / sizeof(WCHAR), pOrgName->Buffer, rcNtResolve,
pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
!((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
else
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: pName=%.*ls (rcNtResolve=%#x) *pfFlags=%#x pwszSearchPath=%p:%ls [calling]\n",
(unsigned)pName->Length / sizeof(WCHAR), pName->Buffer, rcNtResolve,
pfFlags ? *pfFlags : UINT32_MAX, pwszSearchPath,
!((uintptr_t)pwszSearchPath & 1) && (uintptr_t)pwszSearchPath >= 0x2000U ? pwszSearchPath : L"<flags>"));
}
RtlRestoreLastWin32Error(dwSavedLastError);
rcNt = g_pfnLdrLoadDllReal(pwszSearchPath, pfFlags, pName, phMod);
/*
* Log the result and process pending WinVerifyTrust work if we can.
*/
dwSavedLastError = RtlGetLastWin32Error();
if (NT_SUCCESS(rcNt) && phMod)
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x hMod=%p '%ls'\n", rcNt, *phMod, wszPath));
else if (!NT_SUCCESS(rcNt) || !fQuiet)
SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x '%ls'\n", rcNt, wszPath));
supR3HardenedWinVerifyCacheProcessWvtTodos();
RtlRestoreLastWin32Error(dwSavedLastError);
return rcNt;
}
/**
* DLL load and unload notification callback.
*
* This is a safety against our LdrLoadDll hook being replaced by protection
* software. Though, we prefer the LdrLoadDll hook to this one as it allows us
* to call WinVerifyTrust more freely.
*
* @param ulReason The reason we're called, see
* LDR_DLL_NOTIFICATION_REASON_XXX.
* @param pData Reason specific data. (Format is currently the same for
* both load and unload.)
* @param pvUser User parameter (ignored).
*
* @remarks Vista and later.
* @remarks The loader lock is held when we're called, at least on Windows 7.
*/
static VOID CALLBACK supR3HardenedDllNotificationCallback(ULONG ulReason, PCLDR_DLL_NOTIFICATION_DATA pData, PVOID pvUser)
{
NOREF(pvUser);
/*
* Screen the image on load. We will normally get a verification cache
* hit here because of the LdrLoadDll and NtCreateSection hooks, so it
* should be relatively cheap to recheck. In case our NtDll patches
* got re
*
* This ASSUMES that we get informed after the fact as indicated by the
* available documentation.
*/
if (ulReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
{
SUP_DPRINTF(("supR3HardenedDllNotificationCallback: load %p LB %#010x %.*ls [fFlags=%#x]\n",
pData->Loaded.DllBase, pData->Loaded.SizeOfImage,
pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
pData->Loaded.Flags));
/* Convert the windows path to an NT path and open it. */
HANDLE hRootDir;
UNICODE_STRING NtPathUniStr;
int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, pData->Loaded.FullDllName->Buffer,
pData->Loaded.FullDllName->Length / sizeof(WCHAR));
if (RT_FAILURE(rc))
{
supR3HardenedFatal("supR3HardenedDllNotificationCallback: RTNtPathFromWinUtf16Ex failed on '%.*ls': %Rrc\n",
pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer, rc);
return;
}
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
NTSTATUS rcNt = NtCreateFile(&hFile,
FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (!NT_SUCCESS(rcNt))
{
supR3HardenedFatal("supR3HardenedDllNotificationCallback: NtCreateFile failed on '%.*ls' / '%.*ls': %#x\n",
pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
RTNtPathFree(&NtPathUniStr, &hRootDir);
return;
}
/* Do the screening. */
ULONG fAccess = 0;
ULONG fProtect = 0;
bool fCallRealApi = false;
bool fQuietFailure = false;
rcNt = supR3HardenedScreenImage(hFile, true /*fImage*/, true /*fIgnoreArch*/, &fAccess, &fProtect, &fCallRealApi,
"LdrLoadDll", true /*fAvoidWinVerifyTrust*/, &fQuietFailure);
NtClose(hFile);
if (!NT_SUCCESS(rcNt))
{
supR3HardenedFatal("supR3HardenedDllNotificationCallback: supR3HardenedScreenImage failed on '%.*ls' / '%.*ls': %#x\n",
pData->Loaded.FullDllName->Length / sizeof(WCHAR), pData->Loaded.FullDllName->Buffer,
NtPathUniStr.Length / sizeof(WCHAR), NtPathUniStr.Buffer, rcNt);
RTNtPathFree(&NtPathUniStr, &hRootDir);
return;
}
RTNtPathFree(&NtPathUniStr, &hRootDir);
}
/*
* Log the unload call.
*/
else if (ulReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
{
SUP_DPRINTF(("supR3HardenedDllNotificationCallback: Unload %p LB %#010x %.*ls [flags=%#x]\n",
pData->Unloaded.DllBase, pData->Unloaded.SizeOfImage,
pData->Unloaded.FullDllName->Length / sizeof(WCHAR), pData->Unloaded.FullDllName->Buffer,
pData->Unloaded.Flags));
}
/*
* Just log things we don't know and then return without caching anything.
*/
else
{
static uint32_t s_cLogEntries = 0;
if (s_cLogEntries++ < 32)
SUP_DPRINTF(("supR3HardenedDllNotificationCallback: ulReason=%u pData=%p\n", ulReason, pData));
return;
}
/*
* Use this opportunity to make sure our NtDll patches are still in place,
* since they may be replaced by indecent protection software solutions.
*/
supR3HardenedWinReInstallHooks(false /*fFirstCall */);
}
/**
* Registers the DLL notification callback if it hasn't already been registered.
*/
static void supR3HardenedWinRegisterDllNotificationCallback(void)
{
/*
* The notification API was added in Vista, so it's an optional (weak) import.
*/
if ( LdrRegisterDllNotification != NULL
&& g_cDllNotificationRegistered <= 0
&& g_cDllNotificationRegistered > -32)
{
NTSTATUS rcNt = LdrRegisterDllNotification(0, supR3HardenedDllNotificationCallback, NULL, &g_pvDllNotificationCookie);
if (NT_SUCCESS(rcNt))
{
SUP_DPRINTF(("Registered Dll notification callback with NTDLL.\n"));
g_cDllNotificationRegistered = 1;
}
else
{
supR3HardenedError(rcNt, false /*fFatal*/, "LdrRegisterDllNotification failed: %#x\n", rcNt);
g_cDllNotificationRegistered--;
}
}
}
static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
{
supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
"Failed to install %s monitor: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n "
#ifdef RT_ARCH_X86
"(It is also possible you are running 32-bit VirtualBox under 64-bit windows.)\n"
#endif
,
pszWhich,
pbPrologue[0], pbPrologue[1], pbPrologue[2], pbPrologue[3],
pbPrologue[4], pbPrologue[5], pbPrologue[6], pbPrologue[7],
pbPrologue[8], pbPrologue[9], pbPrologue[10], pbPrologue[11],
pbPrologue[12], pbPrologue[13], pbPrologue[14], pbPrologue[15]);
}
/**
* IPRT thread that waits for the parent process to terminate and reacts by
* exiting the current process.
*
* @returns VINF_SUCCESS
* @param hSelf The current thread. Ignored.
* @param pvUser The handle of the parent process.
*/
static DECLCALLBACK(int) supR3HardenedWinParentWatcherThread(RTTHREAD hSelf, void *pvUser)
{
HANDLE hProcWait = (HANDLE)pvUser;
NOREF(hSelf);
/*
* Wait for the parent to terminate.
*/
NTSTATUS rcNt;
for (;;)
{
rcNt = NtWaitForSingleObject(hProcWait, TRUE /*Alertable*/, NULL /*pTimeout*/);
if ( rcNt == STATUS_WAIT_0
|| rcNt == STATUS_ABANDONED_WAIT_0)
break;
if ( rcNt != STATUS_TIMEOUT
&& rcNt != STATUS_USER_APC
&& rcNt != STATUS_ALERTED)
supR3HardenedFatal("NtWaitForSingleObject returned %#x\n", rcNt);
}
/*
* Proxy the termination code of the child, if it exited already.
*/
PROCESS_BASIC_INFORMATION BasicInfo;
NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
if ( !NT_SUCCESS(rcNt2)
|| BasicInfo.ExitStatus == STATUS_PENDING)
BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
NtClose(hProcWait);
SUP_DPRINTF(("supR3HardenedWinParentWatcherThread: Quitting: ExitCode=%#x rcNt=%#x\n", BasicInfo.ExitStatus, rcNt));
suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
return VINF_SUCCESS; /* won't be reached. */
}
/**
* Creates the parent watcher thread that will make sure this process exits when
* the parent does.
*
* This is a necessary evil to make VBoxNetDhcp and VBoxNetNat termination from
* Main work without too much new magic. It also makes Ctrl-C or similar work
* in on the hardened processes in the windows console.
*
* @param hVBoxRT The VBoxRT.dll handle. We use RTThreadCreate to
* spawn the thread to avoid duplicating thread
* creation and thread naming code from IPRT.
*/
DECLHIDDEN(void) supR3HardenedWinCreateParentWatcherThread(HMODULE hVBoxRT)
{
/*
* Resolve runtime methods that we need.
*/
PFNRTTHREADCREATE pfnRTThreadCreate = (PFNRTTHREADCREATE)GetProcAddress(hVBoxRT, "RTThreadCreate");
SUPR3HARDENED_ASSERT(pfnRTThreadCreate != NULL);
/*
* Find the parent process ID.
*/
PROCESS_BASIC_INFORMATION BasicInfo;
NTSTATUS rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: NtQueryInformationProcess failed: %#x\n", rcNt);
/*
* Open the parent process for waiting and exitcode query.
*/
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
CLIENT_ID ClientId;
ClientId.UniqueProcess = (HANDLE)BasicInfo.InheritedFromUniqueProcessId;
ClientId.UniqueThread = NULL;
HANDLE hParent;
rcNt = NtOpenProcess(&hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinCreateParentWatcherThread", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
"NtOpenProcess(%p.0) failed: %#x\n", ClientId.UniqueProcess, rcNt);
/*
* Create the thread that should do the waiting.
*/
int rc = pfnRTThreadCreate(NULL, supR3HardenedWinParentWatcherThread, hParent, _64K /* stack */,
RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "ParentWatcher");
if (RT_FAILURE(rc))
supR3HardenedFatal("supR3HardenedWinCreateParentWatcherThread: RTThreadCreate failed: %Rrc\n", rc);
}
/**
* Checks if the calling thread is the only one in the process.
*
* @returns true if we're positive we're alone, false if not.
*/
static bool supR3HardenedWinAmIAlone(void)
{
ULONG fAmIAlone = 0;
ULONG cbIgn = 0;
NTSTATUS rcNt = NtQueryInformationThread(NtCurrentThread(), ThreadAmILastThread, &fAmIAlone, sizeof(fAmIAlone), &cbIgn);
Assert(NT_SUCCESS(rcNt));
return NT_SUCCESS(rcNt) && fAmIAlone != 0;
}
/**
* Simplify NtProtectVirtualMemory interface.
*
* Modifies protection for the current process. Caller must know the current
* protection as it's not returned.
*
* @returns NT status code.
* @param pvMem The memory to change protection for.
* @param cbMem The amount of memory to change.
* @param fNewProt The new protection.
*/
static NTSTATUS supR3HardenedWinProtectMemory(PVOID pvMem, SIZE_T cbMem, ULONG fNewProt)
{
ULONG fOldProt = 0;
return NtProtectVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, fNewProt, &fOldProt);
}
/**
* Installs or reinstalls the NTDLL patches.
*/
static void supR3HardenedWinReInstallHooks(bool fFirstCall)
{
struct
{
size_t cbPatch;
uint8_t const *pabPatch;
uint8_t **ppbApi;
const char *pszName;
} const s_aPatches[] =
{
{ sizeof(g_abNtCreateSectionPatch), g_abNtCreateSectionPatch, &g_pbNtCreateSection, "NtCreateSection" },
{ sizeof(g_abLdrLoadDllPatch), g_abLdrLoadDllPatch, &g_pbLdrLoadDll, "LdrLoadDll" },
};
ULONG fAmIAlone = ~(ULONG)0;
for (uint32_t i = 0; i < RT_ELEMENTS(s_aPatches); i++)
{
uint8_t *pbApi = *s_aPatches[i].ppbApi;
if (memcmp(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch) != 0)
{
/*
* Log the incident if it's not the initial call.
*/
static uint32_t volatile s_cTimes = 0;
if (!fFirstCall && s_cTimes < 128)
{
s_cTimes++;
SUP_DPRINTF(("supR3HardenedWinReInstallHooks: Reinstalling %s (%p: %.*Rhxs).\n",
s_aPatches[i].pszName, pbApi, s_aPatches[i].cbPatch, pbApi));
}
Assert(s_aPatches[i].cbPatch >= 4);
SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READWRITE));
/*
* If we're alone, just memcpy the patch in.
*/
if (fAmIAlone == ~(ULONG)0)
fAmIAlone = supR3HardenedWinAmIAlone();
if (fAmIAlone)
memcpy(pbApi, s_aPatches[i].pabPatch, s_aPatches[i].cbPatch);
else
{
/*
* Not alone. Start by injecting a JMP $-2, then waste some
* CPU cycles to get the other threads a good chance of getting
* out of the code before we replace it.
*/
RTUINT32U uJmpDollarMinus;
uJmpDollarMinus.au8[0] = 0xeb;
uJmpDollarMinus.au8[1] = 0xfe;
uJmpDollarMinus.au8[2] = pbApi[2];
uJmpDollarMinus.au8[3] = pbApi[3];
ASMAtomicXchgU32((uint32_t volatile *)pbApi, uJmpDollarMinus.u);
NtYieldExecution();
NtYieldExecution();
/* Copy in the tail bytes of the patch, then xchg the jmp $-2. */
if (s_aPatches[i].cbPatch > 4)
memcpy(&pbApi[4], &s_aPatches[i].pabPatch[4], s_aPatches[i].cbPatch - 4);
ASMAtomicXchgU32((uint32_t volatile *)pbApi, *(uint32_t *)s_aPatches[i].pabPatch);
}
SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pbApi, s_aPatches[i].cbPatch, PAGE_EXECUTE_READ));
}
}
}
/**
* Install hooks for intercepting calls dealing with mapping shared libraries
* into the process.
*
* This allows us to prevent undesirable shared libraries from being loaded.
*
* @remarks We assume we're alone in this process, so no seralizing trickery is
* necessary when installing the patch.
*
* @remarks We would normally just copy the prologue sequence somewhere and add
* a jump back at the end of it. But because we wish to avoid
* allocating executable memory, we need to have preprepared assembly
* "copies". This makes the non-system call patching a little tedious
* and inflexible.
*/
static void supR3HardenedWinInstallHooks(void)
{
NTSTATUS rcNt;
/*
* Disable hard error popups so we can quietly refuse images to be loaded.
*/
ULONG fHardErr = 0;
rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr), NULL);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
"NtQueryInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
if (fHardErr & PROCESS_HARDERR_CRITICAL_ERROR)
{
fHardErr &= ~PROCESS_HARDERR_CRITICAL_ERROR;
rcNt = NtSetInformationProcess(NtCurrentProcess(), ProcessDefaultHardErrorMode, &fHardErr, sizeof(fHardErr));
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
"NtSetInformationProcess/ProcessDefaultHardErrorMode failed: %#x\n", rcNt);
}
/*
* Locate the routines first so we can allocate memory that's near enough.
*/
PFNRT pfnNtCreateSection = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtCreateSection");
SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
//SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
PFNRT pfnLdrLoadDll = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "LdrLoadDll");
SUPR3HARDENED_ASSERT(pfnLdrLoadDll != NULL);
//SUPR3HARDENED_ASSERT(pfnLdrLoadDll == (FARPROC)LdrLoadDll);
/*
* Exec page setup & management.
*/
uint32_t offExecPage = 0;
memset(g_abSupHardReadWriteExecPage, 0xcc, PAGE_SIZE);
/*
* Hook #1 - NtCreateSection.
* Purpose: Validate everything that can be mapped into the process before
* it's mapped and we still have a file handle to work with.
*/
uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
g_pbNtCreateSection = pbNtCreateSection;
memcpy(g_abNtCreateSectionPatch, pbNtCreateSection, sizeof(g_abNtCreateSectionPatch));
g_pfnNtCreateSectionReal = NtCreateSection; /* our direct syscall */
#ifdef RT_ARCH_AMD64
/*
* Patch 64-bit hosts.
*/
/* Pattern #1: XP64/W2K3-64 thru Windows 8.1
0:000> u ntdll!NtCreateSection
ntdll!NtCreateSection:
00000000`779f1750 4c8bd1 mov r10,rcx
00000000`779f1753 b847000000 mov eax,47h
00000000`779f1758 0f05 syscall
00000000`779f175a c3 ret
00000000`779f175b 0f1f440000 nop dword ptr [rax+rax]
The variant is the value loaded into eax: W2K3=??, Vista=47h?, W7=47h, W80=48h, W81=49h */
/* Assemble the patch. */
g_abNtCreateSectionPatch[0] = 0x48; /* mov rax, qword */
g_abNtCreateSectionPatch[1] = 0xb8;
*(uint64_t *)&g_abNtCreateSectionPatch[2] = (uint64_t)supR3HardenedMonitor_NtCreateSection;
g_abNtCreateSectionPatch[10] = 0xff; /* jmp rax */
g_abNtCreateSectionPatch[11] = 0xe0;
#else
/*
* Patch 32-bit hosts.
*/
/* Pattern #1: XP thru Windows 7
kd> u ntdll!NtCreateSection
ntdll!NtCreateSection:
7c90d160 b832000000 mov eax,32h
7c90d165 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c90d16a ff12 call dword ptr [edx]
7c90d16c c21c00 ret 1Ch
7c90d16f 90 nop
The variable bit is the value loaded into eax: XP=32h, W2K3=34h, Vista=4bh, W7=54h
Pattern #2: Windows 8.1
0:000:x86> u ntdll_6a0f0000!NtCreateSection
ntdll_6a0f0000!NtCreateSection:
6a15eabc b854010000 mov eax,154h
6a15eac1 e803000000 call ntdll_6a0f0000!NtCreateSection+0xd (6a15eac9)
6a15eac6 c21c00 ret 1Ch
6a15eac9 8bd4 mov edx,esp
6a15eacb 0f34 sysenter
6a15eacd c3 ret
The variable bit is the value loaded into eax: W81=154h */
/* Assemble the patch. */
g_abNtCreateSectionPatch[0] = 0xe9; /* jmp rel32 */
*(uint32_t *)&g_abNtCreateSectionPatch[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
- (uintptr_t)&pbNtCreateSection[1+4];
#endif
/*
* Hook #2 - LdrLoadDll
* Purpose: (a) Enforce LdrLoadDll search path constraints, and (b) pre-validate
* DLLs so we can avoid calling WinVerifyTrust from the first hook,
* and thus avoiding messing up the loader data on some installations.
*
* This differs from the above function in that is no a system call and
* we're at the mercy of the compiler.
*/
uint8_t * const pbLdrLoadDll = (uint8_t *)(uintptr_t)pfnLdrLoadDll;
g_pbLdrLoadDll = pbLdrLoadDll;
memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
DISSTATE Dis;
uint32_t cbInstr;
uint32_t offJmpBack = 0;
#ifdef RT_ARCH_AMD64
/*
* Patch 64-bit hosts.
*/
/* Just use the disassembler to skip 12 bytes or more. */
while (offJmpBack < 12)
{
cbInstr = 1;
int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_64BIT, &Dis, &cbInstr);
if ( RT_FAILURE(rc)
|| (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW))
|| (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */) )
supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
offJmpBack += cbInstr;
}
/* Assemble the code for resuming the call.*/
*(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
offExecPage += offJmpBack;
g_abSupHardReadWriteExecPage[offExecPage++] = 0xff; /* jmp qword [$+8 wrt RIP] */
g_abSupHardReadWriteExecPage[offExecPage++] = 0x25;
*(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = RT_ALIGN_32(offExecPage + 4, 8) - (offExecPage + 4);
offExecPage = RT_ALIGN_32(offExecPage + 4, 8);
*(uint64_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack];
offExecPage = RT_ALIGN_32(offJmpBack + 8, 16);
/* Assemble the LdrLoadDll patch. */
Assert(offJmpBack >= 12);
g_abLdrLoadDllPatch[0] = 0x48; /* mov rax, qword */
g_abLdrLoadDllPatch[1] = 0xb8;
*(uint64_t *)&g_abLdrLoadDllPatch[2] = (uint64_t)supR3HardenedMonitor_LdrLoadDll;
g_abLdrLoadDllPatch[10] = 0xff; /* jmp rax */
g_abLdrLoadDllPatch[11] = 0xe0;
#else
/*
* Patch 32-bit hosts.
*/
/* Just use the disassembler to skip 5 bytes or more. */
while (offJmpBack < 5)
{
cbInstr = 1;
int rc = DISInstr(pbLdrLoadDll + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
if ( RT_FAILURE(rc)
|| (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
supR3HardenedWinHookFailed("LdrLoadDll", pbLdrLoadDll);
offJmpBack += cbInstr;
}
/* Assemble the code for resuming the call.*/
*(PFNRT *)&g_pfnLdrLoadDllReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbLdrLoadDll, offJmpBack);
offExecPage += offJmpBack;
g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
*(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbLdrLoadDll[offJmpBack]
- (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
offExecPage = RT_ALIGN_32(offJmpBack + 4, 16);
/* Assemble the LdrLoadDll patch. */
memcpy(g_abLdrLoadDllPatch, pbLdrLoadDll, sizeof(g_abLdrLoadDllPatch));
Assert(offJmpBack >= 5);
g_abLdrLoadDllPatch[0] = 0xe9;
*(uint32_t *)&g_abLdrLoadDllPatch[1] = (uintptr_t)supR3HardenedMonitor_LdrLoadDll - (uintptr_t)&pbLdrLoadDll[1+4];
#endif
/*
* Seal the rwx page.
*/
SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(g_abSupHardReadWriteExecPage, PAGE_SIZE, PAGE_EXECUTE_READ));
/*
* Install the patches.
*/
supR3HardenedWinReInstallHooks(true /*fFirstCall*/);
}
/*
*
* T h r e a d c r e a t i o n c o n t r o l
* T h r e a d c r e a t i o n c o n t r o l
* T h r e a d c r e a t i o n c o n t r o l
*
*/
/**
* Common code used for child and parent to make new threads exit immediately.
*
* This patches the LdrInitializeThunk code to call NtTerminateThread with
* STATUS_SUCCESS instead of doing the NTDLL initialization.
*
* @returns VBox status code.
* @param hProcess The process to do this to.
* @param pvLdrInitThunk The address of the LdrInitializeThunk code to
* override.
* @param pvNtTerminateThread The address of the NtTerminateThread function in
* the NTDLL instance we're patching. (Must be +/-
* 2GB from the thunk code.)
* @param pabBackup Where to back up the original instruction bytes
* at pvLdrInitThunk.
* @param cbBackup The size of the backup area. Must be 16 bytes.
* @param pErrInfo Where to return extended error information.
* Optional.
*/
static int supR3HardNtDisableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, void *pvNtTerminateThread,
uint8_t *pabBackup, size_t cbBackup, PRTERRINFO pErrInfo)
{
SUP_DPRINTF(("supR3HardNtDisableThreadCreation: pvLdrInitThunk=%p pvNtTerminateThread=%p\n", pvLdrInitThunk, pvNtTerminateThread));
SUPR3HARDENED_ASSERT(cbBackup == 16);
SUPR3HARDENED_ASSERT(RT_ABS((intptr_t)pvLdrInitThunk - (intptr_t)pvNtTerminateThread) < 16*_1M);
/*
* Back up the thunk code.
*/
SIZE_T cbIgnored;
NTSTATUS rcNt = NtReadVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtDisableThreadCreation: NtReadVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
/*
* Cook up replacement code that calls NtTerminateThread.
*/
uint8_t abReplacement[16];
memcpy(abReplacement, pabBackup, sizeof(abReplacement));
#ifdef RT_ARCH_AMD64
abReplacement[0] = 0x31; /* xor ecx, ecx */
abReplacement[1] = 0xc9;
abReplacement[2] = 0x31; /* xor edx, edx */
abReplacement[3] = 0xd2;
abReplacement[4] = 0xe8; /* call near NtTerminateThread */
*(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
abReplacement[9] = 0xcc; /* int3 */
#elif defined(RT_ARCH_X86)
abReplacement[0] = 0x6a; /* push 0 */
abReplacement[1] = 0x00;
abReplacement[2] = 0x6a; /* push 0 */
abReplacement[3] = 0x00;
abReplacement[4] = 0xe8; /* call near NtTerminateThread */
*(int32_t *)&abReplacement[5] = (int32_t)((uintptr_t)pvNtTerminateThread - ((uintptr_t)pvLdrInitThunk + 9));
abReplacement[9] = 0xcc; /* int3 */
#else
# error "Unsupported arch."
#endif
/*
* Install the replacment code.
*/
PVOID pvProt = pvLdrInitThunk;
SIZE_T cbProt = cbBackup;
ULONG fOldProt = 0;
rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abReplacement, sizeof(abReplacement), &cbIgnored);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtDisableThreadCreationEx: NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
pvProt = pvLdrInitThunk;
cbProt = cbBackup;
rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk/2 failed: %#x", rcNt);
return VINF_SUCCESS;
}
/**
* Undo the effects of supR3HardNtDisableThreadCreationEx.
*
* @returns VBox status code.
* @param hProcess The process to do this to.
* @param pvLdrInitThunk The address of the LdrInitializeThunk code to
* override.
* @param pabBackup Where to back up the original instruction bytes
* at pvLdrInitThunk.
* @param cbBackup The size of the backup area. Must be 16 bytes.
* @param pErrInfo Where to return extended error information.
* Optional.
*/
static int supR3HardNtEnableThreadCreationEx(HANDLE hProcess, void *pvLdrInitThunk, uint8_t const *pabBackup, size_t cbBackup,
PRTERRINFO pErrInfo)
{
SUP_DPRINTF(("supR3HardNtEnableThreadCreation:\n"));
SUPR3HARDENED_ASSERT(cbBackup == 16);
PVOID pvProt = pvLdrInitThunk;
SIZE_T cbProt = cbBackup;
ULONG fOldProt = 0;
NTSTATUS rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtDisableThreadCreationEx: NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
SIZE_T cbIgnored;
rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, pabBackup, cbBackup, &cbIgnored);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtEnableThreadCreation: NtWriteVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
rcNt);
pvProt = pvLdrInitThunk;
cbProt = cbBackup;
rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
if (!NT_SUCCESS(rcNt))
return RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
"supR3HardNtEnableThreadCreation: NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x",
rcNt);
return VINF_SUCCESS;
}
/**
* Disable thread creation for the current process.
*
* @remarks Doesn't really disables it, just makes the threads exit immediately
* without executing any real code.
*/
static void supR3HardenedWinDisableThreadCreation(void)
{
/* Cannot use the imported NtTerminateThread as it's pointing to our own
syscall assembly code. */
static PFNRT s_pfnNtTerminateThread = NULL;
if (s_pfnNtTerminateThread == NULL)
s_pfnNtTerminateThread = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "NtTerminateThread");
SUPR3HARDENED_ASSERT(s_pfnNtTerminateThread);
int rc = supR3HardNtDisableThreadCreationEx(NtCurrentProcess(),
(void *)(uintptr_t)&LdrInitializeThunk,
(void *)(uintptr_t)s_pfnNtTerminateThread,
g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
NULL /* pErrInfo*/);
g_fSupInitThunkSelfPatched = RT_SUCCESS(rc);
}
/**
* Undoes the effects of supR3HardenedWinDisableThreadCreation.
*/
DECLHIDDEN(void) supR3HardenedWinEnableThreadCreation(void)
{
if (g_fSupInitThunkSelfPatched)
{
int rc = supR3HardNtEnableThreadCreationEx(NtCurrentProcess(),
(void *)(uintptr_t)&LdrInitializeThunk,
g_abLdrInitThunkSelfBackup, sizeof(g_abLdrInitThunkSelfBackup),
RTErrInfoInitStatic(&g_ErrInfoStatic));
if (RT_FAILURE(rc))
supR3HardenedError(rc, true /*fFatal*/, "%s", g_ErrInfoStatic.szMsg);
g_fSupInitThunkSelfPatched = false;
}
}
/*
*
* R e s p a w n
* R e s p a w n
* R e s p a w n
*
*/
/**
* Gets the SID of the user associated with the process.
*
* @returns @c true if we've got a login SID, @c false if not.
* @param pSidUser Where to return the user SID.
* @param cbSidUser The size of the user SID buffer.
* @param pSidLogin Where to return the login SID.
* @param cbSidLogin The size of the login SID buffer.
*/
static bool supR3HardNtChildGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
{
HANDLE hToken;
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));
union
{
TOKEN_USER UserInfo;
TOKEN_GROUPS Groups;
uint8_t abPadding[4096];
} uBuf;
ULONG cbRet = 0;
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryInformationToken(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbRet));
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidUser, pSidUser, uBuf.UserInfo.User.Sid));
bool fLoginSid = false;
NTSTATUS rcNt = NtQueryInformationToken(hToken, TokenLogonSid, &uBuf, sizeof(uBuf), &cbRet);
if (NT_SUCCESS(rcNt))
{
for (DWORD i = 0; i < uBuf.Groups.GroupCount; i++)
if ((uBuf.Groups.Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
{
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCopySid(cbSidLogin, pSidLogin, uBuf.Groups.Groups[i].Sid));
fLoginSid = true;
break;
}
}
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hToken));
return fLoginSid;
}
/**
* Build security attributes for the process or the primary thread (@a fProcess)
*
* Process DACLs can be bypassed using the SeDebugPrivilege (generally available
* to admins, i.e. normal windows users), or by taking ownership and/or
* modifying the DACL. However, it restricts
*
* @param pSecAttrs Where to return the security attributes.
* @param pCleanup Cleanup record.
* @param fProcess Set if it's for the process, clear if it's for
* the primary thread.
*/
static void supR3HardNtChildInitSecAttrs(PSECURITY_ATTRIBUTES pSecAttrs, PMYSECURITYCLEANUP pCleanup, bool fProcess)
{
/*
* Safe return values.
*/
suplibHardenedMemSet(pCleanup, 0, sizeof(*pCleanup));
pSecAttrs->nLength = sizeof(*pSecAttrs);
pSecAttrs->bInheritHandle = FALSE;
pSecAttrs->lpSecurityDescriptor = NULL;
/** @todo This isn't at all complete, just sketches... */
/*
* Create an ACL detailing the access of the above groups.
*/
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateAcl(&pCleanup->Acl.AclHdr, sizeof(pCleanup->Acl), ACL_REVISION));
ULONG fDeny = DELETE | WRITE_DAC | WRITE_OWNER;
ULONG fAllow = SYNCHRONIZE | READ_CONTROL;
ULONG fAllowLogin = SYNCHRONIZE | READ_CONTROL;
if (fProcess)
{
fDeny |= PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
| PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA
| PROCESS_SET_INFORMATION | PROCESS_SUSPEND_RESUME;
fAllow |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
fAllowLogin |= PROCESS_TERMINATE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
{
fAllow |= PROCESS_QUERY_LIMITED_INFORMATION;
fAllowLogin |= PROCESS_QUERY_LIMITED_INFORMATION;
}
if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)) /* Introduced in Windows 8.1. */
fAllow |= PROCESS_SET_LIMITED_INFORMATION;
}
else
{
fDeny |= THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN
| THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
fAllow |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
fAllowLogin |= THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION;
if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
{
fAllow |= THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION;
fAllowLogin |= THREAD_QUERY_LIMITED_INFORMATION;
}
}
fDeny |= ~fAllow & (SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL);
/* Deny everyone access to bad bits. */
#if 1
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Everyone.Sid, &SIDAuthWorld, 1));
*RtlSubAuthoritySid(&pCleanup->Everyone.Sid, 0) = SECURITY_WORLD_RID;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fDeny, &pCleanup->Everyone.Sid));
#endif
#if 0
/* Grant some access to the owner - doesn't work. */
SID_IDENTIFIER_AUTHORITY SIDAuthCreator = SECURITY_CREATOR_SID_AUTHORITY;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlInitializeSid(&pCleanup->Owner.Sid, &SIDAuthCreator, 1));
*RtlSubAuthoritySid(&pCleanup->Owner.Sid, 0) = SECURITY_CREATOR_OWNER_RID;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fDeny, &pCleanup->Owner.Sid));
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fAllow, &pCleanup->Owner.Sid));
#endif
#if 1
bool fHasLoginSid = supR3HardNtChildGetUserAndLogSids(&pCleanup->User.Sid, sizeof(pCleanup->User),
&pCleanup->Login.Sid, sizeof(pCleanup->Login));
# if 1
/* Grant minimal access to the user. */
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessDeniedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fDeny, &pCleanup->User.Sid));
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fAllow, &pCleanup->User.Sid));
# endif
# if 1
/* Grant very limited access to the login sid. */
if (fHasLoginSid)
{
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlAddAccessAllowedAce(&pCleanup->Acl.AclHdr, ACL_REVISION,
fAllowLogin, &pCleanup->Login.Sid));
}
# endif
#endif
/*
* Create a security descriptor with the above ACL.
*/
PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemAllocZ(SECURITY_DESCRIPTOR_MIN_LENGTH);
pCleanup->pSecDesc = pSecDesc;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlSetDaclSecurityDescriptor(pSecDesc, TRUE /*fDaclPresent*/, &pCleanup->Acl.AclHdr,
FALSE /*fDaclDefaulted*/));
pSecAttrs->lpSecurityDescriptor = pSecDesc;
}
/**
* Predicate function which tests whether @a ch is a argument separator
* character.
*
* @returns True/false.
* @param ch The character to examine.
*/
DECLINLINE(bool) suplibCommandLineIsArgSeparator(int ch)
{
return ch == ' '
|| ch == '\t'
|| ch == '\n'
|| ch == '\r';
}
/**
* Construct the new command line.
*
* Since argc/argv are both derived from GetCommandLineW (see
* suplibHardenedWindowsMain), we skip the argument by argument UTF-8 -> UTF-16
* conversion and quoting by going to the original source.
*
* The executable name, though, is replaced in case it's not a fullly
* qualified path.
*
* The re-spawn indicator is added immediately after the executable name
* so that we don't get tripped up missing close quote chars in the last
* argument.
*
* @returns Pointer to a command line string (heap).
* @param pUniStr Unicode string structure to initialize to the
* command line. Optional.
* @param iWhich Which respawn we're to check for, 1 being the first
* one, and 2 the second and final.
*/
static PRTUTF16 supR3HardNtChildConstructCmdLine(PUNICODE_STRING pString, int iWhich)
{
SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
/*
* Get the command line and skip the executable name.
*/
PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
PCRTUTF16 pawcArgs = pCmdLineStr->Buffer;
uint32_t cwcArgs = pCmdLineStr->Length / sizeof(WCHAR);
/* Skip leading space (shouldn't be any, but whatever). */
while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs) )
cwcArgs--, pawcArgs++;
SUPR3HARDENED_ASSERT(cwcArgs > 0 && *pawcArgs != '\0');
/* Walk to the end of it. */
int fQuoted = false;
do
{
if (*pawcArgs == '"')
{
fQuoted = !fQuoted;
cwcArgs--; pawcArgs++;
}
else if (*pawcArgs != '\\' || (pawcArgs[1] != '\\' && pawcArgs[1] != '"'))
cwcArgs--, pawcArgs++;
else
{
unsigned cSlashes = 0;
do
{
cSlashes++;
cwcArgs--;
pawcArgs++;
}
while (cwcArgs > 0 && *pawcArgs == '\\');
if (cwcArgs > 0 && *pawcArgs == '"' && (cSlashes & 1))
cwcArgs--, pawcArgs++; /* odd number of slashes == escaped quote */
}
} while (cwcArgs > 0 && (fQuoted || !suplibCommandLineIsArgSeparator(*pawcArgs)));
/* Skip trailing spaces. */
while (cwcArgs > 0 && suplibCommandLineIsArgSeparator(*pawcArgs))
cwcArgs--, pawcArgs++;
/*
* Allocate a new buffer.
*/
AssertCompile(sizeof(SUPR3_RESPAWN_1_ARG0) == sizeof(SUPR3_RESPAWN_2_ARG0));
size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_1_ARG0) - 1) / sizeof(SUPR3_RESPAWN_1_ARG0[0]) /* Respawn exe name. */
+ !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
if (cwcCmdLine * sizeof(WCHAR) >= 0xfff0)
supR3HardenedFatalMsg("supR3HardNtChildConstructCmdLine", kSupInitOp_Misc, VERR_OUT_OF_RANGE,
"Command line is too long (%u chars)!", cwcCmdLine);
PRTUTF16 pwszCmdLine = (PRTUTF16)RTMemAlloc((cwcCmdLine + 1) * sizeof(RTUTF16));
SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
/*
* Construct the new command line.
*/
PRTUTF16 pwszDst = pwszCmdLine;
for (const char *pszSrc = iWhich == 1 ? SUPR3_RESPAWN_1_ARG0 : SUPR3_RESPAWN_2_ARG0; *pszSrc; pszSrc++)
*pwszDst++ = *pszSrc;
if (cwcArgs)
{
*pwszDst++ = ' ';
suplibHardenedMemCopy(pwszDst, pawcArgs, cwcArgs * sizeof(RTUTF16));
pwszDst += cwcArgs;
}
*pwszDst = '\0';
SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
if (pString)
{
pString->Buffer = pwszCmdLine;
pString->Length = (USHORT)(cwcCmdLine * sizeof(WCHAR));
pString->MaximumLength = pString->Length + sizeof(WCHAR);
}
return pwszCmdLine;
}
/**
* Terminates the child process.
*
* @param hProcess The process handle.
* @param pszWhere Who's having child rasing troubles.
* @param rc The status code to report.
* @param pszFormat The message format string.
* @param ... Message format arguments.
*/
static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
{
/*
* Terminate the process ASAP and display error.
*/
NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
va_list va;
va_start(va, pszFormat);
supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
va_end(va);
/*
* Wait for the process to really go away.
*/
PROCESS_BASIC_INFORMATION BasicInfo;
NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
if (!fExitOk)
{
NTSTATUS rcNtWait;
uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
do
{
NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
LARGE_INTEGER Timeout;
Timeout.QuadPart = -20000000; /* 2 second */
rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
} while ( !fExitOk
&& ( rcNtWait == STATUS_TIMEOUT
|| rcNtWait == STATUS_USER_APC
|| rcNtWait == STATUS_ALERTED)
&& supR3HardenedWinGetMilliTS() - uMsTsStart < 60 * 1000);
if (fExitOk)
supR3HardenedError(rc, false /*fFatal*/,
"NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
rc, rc, rcNtWait, hProcess);
}
/*
* Final error message.
*/
va_start(va, pszFormat);
supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
va_end(va);
}
/**
* Checks the child process when hEvtParent is signalled.
*
* This will read the request data from the child and check it against expected
* request. If an error is signalled, we'll raise it and make sure the child
* terminates before terminating the calling process.
*
* @param pThis The child process data structure.
* @param enmExpectedRequest The expected child request.
* @param pszWhat What we're waiting for.
*/
static void supR3HardNtChildProcessRequest(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, const char *pszWhat)
{
/*
* Read the process parameters from the child.
*/
uintptr_t uChildAddr = (uintptr_t)pThis->Peb.ImageBaseAddress
+ ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
SIZE_T cbIgnored = 0;
RT_ZERO(pThis->ProcParams);
NTSTATUS rcNt = NtReadVirtualMemory(pThis->hProcess, (PVOID)uChildAddr,
&pThis->ProcParams, sizeof(pThis->ProcParams), &cbIgnored);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt,
"NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
/*
* Is it the expected request?
*/
if (pThis->ProcParams.enmRequest == enmExpectedRequest)
return;
/*
* No, not the expected request. If it's an error request, tell the child
* to terminate itself, otherwise we'll have to terminate it.
*/
pThis->ProcParams.szErrorMsg[sizeof(pThis->ProcParams.szErrorMsg) - 1] = '\0';
pThis->ProcParams.szWhere[sizeof(pThis->ProcParams.szWhere) - 1] = '\0';
SUP_DPRINTF(("supR3HardenedWinCheckChild: enmRequest=%d rc=%d enmWhat=%d %s: %s\n",
pThis->ProcParams.enmRequest, pThis->ProcParams.rc, pThis->ProcParams.enmWhat,
pThis->ProcParams.szWhere, pThis->ProcParams.szErrorMsg));
if (pThis->ProcParams.enmRequest != kSupR3WinChildReq_Error)
supR3HardenedWinKillChild(pThis, "supR3HardenedWinCheckChild", VERR_INVALID_PARAMETER,
"Unexpected child request #%d. Was expecting #%d (%s).\n",
pThis->ProcParams.enmRequest, enmExpectedRequest, pszWhat);
rcNt = NtSetEvent(pThis->hEvtChild, NULL);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildProcessRequest", rcNt, "NtSetEvent failed: %#x\n", rcNt);
/* Wait for it to terminate. */
LARGE_INTEGER Timeout;
Timeout.QuadPart = -50000000; /* 5 seconds */
rcNt = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, &Timeout);
if (rcNt != STATUS_WAIT_0)
{
SUP_DPRINTF(("supR3HardNtChildProcessRequest: Child is taking too long to quit (rcWait=%#x), killing it...\n", rcNt));
NtTerminateProcess(pThis->hProcess, DBG_TERMINATE_PROCESS);
}
/*
* Report the error in the same way as it occured in the guest.
*/
if (pThis->ProcParams.enmWhat == kSupInitOp_Invalid)
supR3HardenedFatalMsg("supR3HardenedWinCheckChild", kSupInitOp_Misc, pThis->ProcParams.rc,
"%s", pThis->ProcParams.szErrorMsg);
else
supR3HardenedFatalMsg(pThis->ProcParams.szWhere, pThis->ProcParams.enmWhat, pThis->ProcParams.rc,
"%s", pThis->ProcParams.szErrorMsg);
}
/**
* Waits for the child to make a certain request or terminate.
*
* The stub process will also wait on it's parent to terminate.
* This call will only return if the child made the expected request.
*
* @param pThis The child process data structure.
* @param enmExpectedRequest The child request to wait for.
* @param cMsTimeout The number of milliseconds to wait (at least).
* @param pszWhat What we're waiting for.
*/
static void supR3HardNtChildWaitFor(PSUPR3HARDNTCHILD pThis, SUPR3WINCHILDREQ enmExpectedRequest, RTMSINTERVAL cMsTimeout,
const char *pszWhat)
{
/*
* The wait loop.
* Will return when the expected request arrives.
* Will break out when one of the processes terminates.
*/
NTSTATUS rcNtWait;
LARGE_INTEGER Timeout;
uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
uint64_t cMsElapsed = 0;
for (;;)
{
/*
* Assemble handles to wait for.
*/
ULONG cHandles = 1;
HANDLE ahHandles[3];
ahHandles[0] = pThis->hProcess;
if (pThis->hEvtParent)
ahHandles[cHandles++] = pThis->hEvtParent;
if (pThis->hParent)
ahHandles[cHandles++] = pThis->hParent;
/*
* Do the waiting according to the callers wishes.
*/
if ( enmExpectedRequest == kSupR3WinChildReq_End
|| cMsTimeout == RT_INDEFINITE_WAIT)
rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*Timeout*/);
else
{
Timeout.QuadPart = -(int64_t)(cMsTimeout - cMsElapsed) * 10000;
rcNtWait = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, &Timeout);
}
/*
* Process child request.
*/
if (rcNtWait == STATUS_WAIT_0 + 1 && pThis->hEvtParent != NULL)
{
supR3HardNtChildProcessRequest(pThis, enmExpectedRequest, pszWhat);
SUP_DPRINTF(("supR3HardNtChildWaitFor: Found expected request %d (%s) after %llu ms.\n",
enmExpectedRequest, pszWhat, supR3HardenedWinGetMilliTS() - uMsTsStart));
return; /* Expected request received. */
}
/*
* Process termination?
*/
if ( (ULONG)rcNtWait - (ULONG)STATUS_WAIT_0 < cHandles
|| (ULONG)rcNtWait - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
break;
/*
* Check sanity.
*/
if ( rcNtWait != STATUS_TIMEOUT
&& rcNtWait != STATUS_USER_APC
&& rcNtWait != STATUS_ALERTED)
supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
"NtWaitForMultipleObjects returned %#x waiting for #%d (%s)\n",
rcNtWait, enmExpectedRequest, pszWhat);
/*
* Calc elapsed time for the next timeout calculation, checking to see
* if we've timed out already.
*/
cMsElapsed = supR3HardenedWinGetMilliTS() - uMsTsStart;
if ( cMsElapsed > cMsTimeout
&& cMsTimeout != RT_INDEFINITE_WAIT
&& enmExpectedRequest != kSupR3WinChildReq_End)
{
if (rcNtWait == STATUS_USER_APC || rcNtWait == STATUS_ALERTED)
cMsElapsed = cMsTimeout - 1; /* try again */
else
{
/* We timed out. */
supR3HardenedWinKillChild(pThis, "supR3HardNtChildWaitFor", rcNtWait,
"Timed out after %llu ms waiting for child request #%d (%s).\n",
cMsElapsed, enmExpectedRequest, pszWhat);
}
}
}
/*
* Proxy the termination code of the child, if it exited already.
*/
PROCESS_BASIC_INFORMATION BasicInfo;
NTSTATUS rcNt1 = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
NTSTATUS rcNt2 = STATUS_PENDING;
NTSTATUS rcNt3 = STATUS_PENDING;
if ( !NT_SUCCESS(rcNt1)
|| BasicInfo.ExitStatus == STATUS_PENDING)
{
rcNt2 = NtTerminateProcess(pThis->hProcess, RTEXITCODE_FAILURE);
Timeout.QuadPart = NT_SUCCESS(rcNt2) ? -20000000 /* 2 sec */ : -1280000 /* 128 ms */;
rcNt3 = NtWaitForSingleObject(pThis->hProcess, FALSE /*Alertable*/, NULL /*Timeout*/);
BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
}
SUP_DPRINTF(("supR3HardNtChildWaitFor[%d]: Quitting: ExitCode=%#x (rcNtWait=%#x, rcNt1=%#x, rcNt2=%#x, rcNt3=%#x, %llu ms, %s);\n",
pThis->iWhich, BasicInfo.ExitStatus, rcNtWait, rcNt1, rcNt2, rcNt3,
supR3HardenedWinGetMilliTS() - uMsTsStart, pszWhat));
suplibHardenedExit((RTEXITCODE)BasicInfo.ExitStatus);
}
/**
* Closes full access child thread and process handles, making a harmless
* duplicate of the process handle first.
*
* The hProcess member of the child process data structure will be change to the
* harmless handle, while the hThread will be set to NULL.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildCloseFullAccessHandles(PSUPR3HARDNTCHILD pThis)
{
/*
* The thread handle.
*/
NTSTATUS rcNt = NtClose(pThis->hThread);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt, "NtClose(hThread) failed: %#x", rcNt);
pThis->hThread = NULL;
/*
* Duplicate the process handle into a harmless one.
*/
HANDLE hProcWait;
ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_VM_READ;
if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
else
fRights |= PROCESS_QUERY_INFORMATION;
rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
NtCurrentProcess(), &hProcWait,
fRights, 0 /*HandleAttributes*/, 0);
if (rcNt == STATUS_ACCESS_DENIED)
{
supR3HardenedError(rcNt, false /*fFatal*/,
"supR3HardenedWinDoReSpawn: NtDuplicateObject(,,,,%#x,,) -> %#x, retrying with only %#x...\n",
fRights, rcNt, SYNCHRONIZE);
rcNt = NtDuplicateObject(NtCurrentProcess(), pThis->hProcess,
NtCurrentProcess(), &hProcWait,
SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
}
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", rcNt,
"NtDuplicateObject failed on child process handle: %#x\n", rcNt);
/*
* Close the process handle and replace it with the harmless one.
*/
rcNt = NtClose(pThis->hProcess);
pThis->hProcess = hProcWait;
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
"NtClose failed on child process handle: %#x\n", rcNt);
}
/**
* This restores the child PEB and tweaks a couple of fields before we do the
* child purification and let the process run normally.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildSanitizePeb(PSUPR3HARDNTCHILD pThis)
{
/*
* Make a copy of the pre-execution PEB.
*/
PEB Peb = pThis->Peb;
#if 0
/*
* There should not be any activation context, so if there is, we scratch the memory associated with it.
*/
int rc = 0;
if (RT_SUCCESS(rc) && Peb.pShimData && !((uintptr_t)Peb.pShimData & PAGE_OFFSET_MASK))
rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.pShimData, PAGE_SIZE, "pShimData", pErrInfo);
if (RT_SUCCESS(rc) && Peb.ActivationContextData && !((uintptr_t)Peb.ActivationContextData & PAGE_OFFSET_MASK))
rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ActivationContextData, PAGE_SIZE, "ActivationContextData", pErrInfo);
if (RT_SUCCESS(rc) && Peb.ProcessAssemblyStorageMap && !((uintptr_t)Peb.ProcessAssemblyStorageMap & PAGE_OFFSET_MASK))
rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "ProcessAssemblyStorageMap", pErrInfo);
if (RT_SUCCESS(rc) && Peb.SystemDefaultActivationContextData && !((uintptr_t)Peb.SystemDefaultActivationContextData & PAGE_OFFSET_MASK))
rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.ProcessAssemblyStorageMap, PAGE_SIZE, "SystemDefaultActivationContextData", pErrInfo);
if (RT_SUCCESS(rc) && Peb.SystemAssemblyStorageMap && !((uintptr_t)Peb.SystemAssemblyStorageMap & PAGE_OFFSET_MASK))
rc = supR3HardenedWinScratchChildMemory(hProcess, Peb.SystemAssemblyStorageMap, PAGE_SIZE, "SystemAssemblyStorageMap", pErrInfo);
if (RT_FAILURE(rc))
return rc;
#endif
/*
* Clear compatibility and activation related fields.
*/
Peb.AppCompatFlags.QuadPart = 0;
Peb.AppCompatFlagsUser.QuadPart = 0;
Peb.pShimData = NULL;
Peb.AppCompatInfo = NULL;
#if 0
Peb.ActivationContextData = NULL;
Peb.ProcessAssemblyStorageMap = NULL;
Peb.SystemDefaultActivationContextData = NULL;
Peb.SystemAssemblyStorageMap = NULL;
/*Peb.Diff0.W6.IsProtectedProcess = 1;*/
#endif
/*
* Write back the PEB.
*/
SIZE_T cbActualMem = pThis->cbPeb;
NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildSanitizePeb", rcNt,
"NtWriteVirtualMemory/Peb failed: %#x", rcNt);
}
/**
* Purifies the child process after very early init has been performed.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildPurify(PSUPR3HARDNTCHILD pThis)
{
/*
* We loop until we no longer make any fixes. This is similar to what
* we do (or used to do, really) in the fAvastKludge case of
* supR3HardenedWinInit. We might be up against asynchronous changes,
* which we fudge by waiting a short while before earch purification. This
* is arguably a fragile technique, but it's currently the best we've got.
* Fortunately, most AVs seems to either favor immediate action on initial
* load events or (much better for us) later events like kernel32.
*/
uint64_t uMsTsOuterStart = supR3HardenedWinGetMilliTS();
uint32_t cMsFudge = g_fSupAdversaries ? 512 : 256;
uint32_t cTotalFixes = 0;
uint32_t cFixes;
for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
{
/*
* Delay.
*/
uint32_t cSleeps = 0;
uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
do
{
NtYieldExecution();
LARGE_INTEGER Time;
Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
NtDelayExecution(FALSE, &Time);
cSleeps++;
} while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
|| cSleeps < 8);
SUP_DPRINTF(("supR3HardNtChildPurify: Startup delay kludge #1/%u: %u ms, %u sleeps\n",
iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
/*
* Purify.
*/
cFixes = 0;
int rc = supHardenedWinVerifyProcess(pThis->hProcess, pThis->hThread, SUPHARDNTVPKIND_CHILD_PURIFICATION,
g_fSupAdversaries & ( SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE
| SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN)
? SUPHARDNTVP_F_EXEC_ALLOC_REPLACE_WITH_RW : 0,
&cFixes, RTErrInfoInitStatic(&g_ErrInfoStatic));
if (RT_FAILURE(rc))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", rc,
"supHardenedWinVerifyProcess failed with %Rrc: %s", rc, g_ErrInfoStatic.szMsg);
if (cFixes == 0)
{
SUP_DPRINTF(("supR3HardNtChildPurify: Done after %llu ms and %u fixes (loop #%u).\n",
supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cTotalFixes, iLoop));
return; /* We're probably good. */
}
cTotalFixes += cFixes;
if (!g_fSupAdversaries)
g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
cMsFudge = 512;
/*
* Log the KiOpPrefetchPatchCount value if available, hoping it might
* sched some light on spider38's case.
*/
ULONG cPatchCount = 0;
NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
&cPatchCount, sizeof(cPatchCount), NULL);
if (NT_SUCCESS(rcNt))
SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
cFixes, g_fSupAdversaries, cPatchCount));
else
SUP_DPRINTF(("supR3HardNtChildPurify: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
}
/*
* We've given up fixing the child process. Probably fighting someone
* that monitors their patches or/and our activities.
*/
supR3HardenedWinKillChild(pThis, "supR3HardNtChildPurify", VERR_TRY_AGAIN,
"Unable to purify child process! After 16 tries over %llu ms, we still %u fix(es) in the last pass.",
supR3HardenedWinGetMilliTS() - uMsTsOuterStart, cFixes);
}
/**
* Sets up the early process init.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildSetUpChildInit(PSUPR3HARDNTCHILD pThis)
{
uintptr_t const uChildExeAddr = (uintptr_t)pThis->Peb.ImageBaseAddress;
/*
* Plant the process parameters. This ASSUMES the handle inheritance is
* performed when creating the child process.
*/
RT_ZERO(pThis->ProcParams);
pThis->ProcParams.hEvtChild = pThis->hEvtChild;
pThis->ProcParams.hEvtParent = pThis->hEvtParent;
pThis->ProcParams.uNtDllAddr = pThis->uNtDllAddr;
pThis->ProcParams.enmRequest = kSupR3WinChildReq_Error;
pThis->ProcParams.rc = VINF_SUCCESS;
uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
SIZE_T cbIgnored;
NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, (PVOID)uChildAddr, &pThis->ProcParams,
sizeof(pThis->ProcParams), &cbIgnored);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
"NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
/*
* Locate the LdrInitializeThunk address in the child as well as pristine
* code bits for it.
*/
PSUPHNTLDRCACHEENTRY pLdrEntry;
int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
if (RT_FAILURE(rc))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
"supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
uint8_t *pbChildNtDllBits;
rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, pThis->uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
if (RT_FAILURE(rc))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
"supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
RTLDRADDR uLdrInitThunk;
rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, pThis->uNtDllAddr, UINT32_MAX,
"LdrInitializeThunk", &uLdrInitThunk);
if (RT_FAILURE(rc))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rc,
"Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
SUP_DPRINTF(("supR3HardenedWinSetupChildInit: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
/*
* Calculate the address of our code in the child process.
*/
uintptr_t uEarlyProcInitEP = uChildExeAddr + ( (uintptr_t)&supR3HardenedEarlyProcessInitThunk
- (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
/*
* Compose the LdrInitializeThunk replacement bytes.
* Note! The amount of code we replace here must be less or equal to what
* the process verification code ignores.
*/
uint8_t abNew[16];
memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - pThis->uNtDllAddr), sizeof(abNew));
#ifdef RT_ARCH_AMD64
abNew[0] = 0xff;
abNew[1] = 0x25;
*(uint32_t *)&abNew[2] = 0;
*(uint64_t *)&abNew[6] = uEarlyProcInitEP;
#elif defined(RT_ARCH_X86)
abNew[0] = 0xe9;
*(uint32_t *)&abNew[1] = uEarlyProcInitEP - ((uint32_t)uLdrInitThunk + 5);
#else
# error "Unsupported arch."
#endif
/*
* Install the LdrInitializeThunk replacement code in the child process.
*/
PVOID pvProt = pvLdrInitThunk;
SIZE_T cbProt = sizeof(abNew);
ULONG fOldProt;
rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
"NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
rcNt = NtWriteVirtualMemory(pThis->hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
"NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
pvProt = pvLdrInitThunk;
cbProt = sizeof(abNew);
rcNt = NtProtectVirtualMemory(pThis->hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardenedWinSetupChildInit", rcNt,
"NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
/* Caller starts child execution. */
SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Start child.\n"));
}
/**
* This messes with the child PEB before we trigger the initial image events.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildScrewUpPebForInitialImageEvents(PSUPR3HARDNTCHILD pThis)
{
/*
* Not sure if any of the cracker software uses the PEB at this point, but
* just in case they do make some of the PEB fields a little less useful.
*/
PEB Peb = pThis->Peb;
/* Make ImageBaseAddress useless. */
Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress ^ UINT32_C(0x5f139000));
#ifdef RT_ARCH_AMD64
Peb.ImageBaseAddress = (PVOID)((uintptr_t)Peb.ImageBaseAddress | UINT64_C(0x0313000000000000));
#endif
/*
* Write the PEB.
*/
SIZE_T cbActualMem = pThis->cbPeb;
NTSTATUS rcNt = NtWriteVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &Peb, pThis->cbPeb, &cbActualMem);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildScrewUpPebForInitialImageEvents", rcNt,
"NtWriteVirtualMemory/Peb failed: %#x", rcNt);
}
/**
* Check if the zero terminated NT unicode string is the path to the given
* system32 DLL.
*
* @returns true if it is, false if not.
* @param pUniStr The zero terminated NT unicode string path.
* @param pszName The name of the system32 DLL.
*/
static bool supR3HardNtIsNamedSystem32Dll(PUNICODE_STRING pUniStr, const char *pszName)
{
if (pUniStr->Length > g_System32NtPath.UniStr.Length)
{
if (memcmp(pUniStr->Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
{
if (pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
{
if (RTUtf16ICmpAscii(&pUniStr->Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR) + 1], pszName) == 0)
return true;
}
}
}
return false;
}
/**
* Worker for supR3HardNtChildGatherData that locates NTDLL in the child
* process.
*
* @param pThis The child process data structure.
*/
static void supR3HardNtChildFindNtdll(PSUPR3HARDNTCHILD pThis)
{
/*
* Find NTDLL in this process first and take that as a starting point.
*/
pThis->uNtDllParentAddr = (uintptr_t)GetModuleHandleW(L"ntdll.dll");
SUPR3HARDENED_ASSERT(pThis->uNtDllParentAddr != 0 && !(pThis->uNtDllParentAddr & PAGE_OFFSET_MASK));
pThis->uNtDllAddr = pThis->uNtDllParentAddr;
/*
* Scan the virtual memory of the child.
*/
uintptr_t cbAdvance = 0;
uintptr_t uPtrWhere = 0;
for (uint32_t i = 0; i < 1024; i++)
{
/* Query information. */
SIZE_T cbActual = 0;
MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
(void const *)uPtrWhere,
MemoryBasicInformation,
&MemInfo,
sizeof(MemInfo),
&cbActual);
if (!NT_SUCCESS(rcNt))
break;
if ( MemInfo.Type == SEC_IMAGE
|| MemInfo.Type == SEC_PROTECTED_IMAGE
|| MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
{
if (MemInfo.BaseAddress == MemInfo.AllocationBase)
{
/* Get the image name. */
union
{
UNICODE_STRING UniStr;
uint8_t abPadding[4096];
} uBuf;
NTSTATUS rcNt = NtQueryVirtualMemory(pThis->hProcess,
MemInfo.BaseAddress,
MemorySectionName,
&uBuf,
sizeof(uBuf) - sizeof(WCHAR),
&cbActual);
if (NT_SUCCESS(rcNt))
{
uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
if (supR3HardNtIsNamedSystem32Dll(&uBuf.UniStr, "ntdll.dll"))
{
pThis->uNtDllAddr = (uintptr_t)MemInfo.AllocationBase;
SUP_DPRINTF(("supR3HardNtPuChFindNtdll: uNtDllParentAddr=%p uNtDllChildAddr=%p\n",
pThis->uNtDllParentAddr, pThis->uNtDllAddr));
return;
}
}
}
}
/*
* Advance.
*/
cbAdvance = MemInfo.RegionSize;
if (uPtrWhere + cbAdvance <= uPtrWhere)
break;
uPtrWhere += MemInfo.RegionSize;
}
supR3HardenedWinKillChild(pThis, "supR3HardNtChildFindNtdll", VERR_MODULE_NOT_FOUND, "ntdll.dll not found in child process.");
}
/**
* Gather child data.
*
* @param This The child process data structure.
*/
static void supR3HardNtChildGatherData(PSUPR3HARDNTCHILD pThis)
{
/*
* Basic info.
*/
ULONG cbActual = 0;
NTSTATUS rcNt = NtQueryInformationProcess(pThis->hProcess, ProcessBasicInformation,
&pThis->BasicInfo, sizeof(pThis->BasicInfo), &cbActual);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
"NtQueryInformationProcess/ProcessBasicInformation failed: %#x", rcNt);
/*
* If this is the middle (stub) process, we wish to wait for both child
* and parent. So open the parent process. Not fatal if we cannnot.
*/
if (pThis->iWhich > 1)
{
PROCESS_BASIC_INFORMATION SelfInfo;
rcNt = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &SelfInfo, sizeof(SelfInfo), &cbActual);
if (NT_SUCCESS(rcNt))
{
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, NULL, 0, NULL /*hRootDir*/, NULL /*pSecDesc*/);
CLIENT_ID ClientId;
ClientId.UniqueProcess = (HANDLE)SelfInfo.InheritedFromUniqueProcessId;
ClientId.UniqueThread = NULL;
rcNt = NtOpenProcess(&pThis->hParent, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, &ObjAttr, &ClientId);
#ifdef DEBUG
SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
#endif
if (!NT_SUCCESS(rcNt))
{
pThis->hParent = NULL;
SUP_DPRINTF(("supR3HardNtChildGatherData: Failed to open parent process (%#p): %#x\n", ClientId.UniqueProcess, rcNt));
}
}
}
/*
* Process environment block.
*/
if (g_uNtVerCombined < SUP_NT_VER_W2K3)
pThis->cbPeb = PEB_SIZE_W51;
else if (g_uNtVerCombined < SUP_NT_VER_VISTA)
pThis->cbPeb = PEB_SIZE_W52;
else if (g_uNtVerCombined < SUP_NT_VER_W70)
pThis->cbPeb = PEB_SIZE_W6;
else if (g_uNtVerCombined < SUP_NT_VER_W80)
pThis->cbPeb = PEB_SIZE_W7;
else if (g_uNtVerCombined < SUP_NT_VER_W81)
pThis->cbPeb = PEB_SIZE_W80;
else
pThis->cbPeb = PEB_SIZE_W81;
SUP_DPRINTF(("supR3HardNtChildGatherData: PebBaseAddress=%p cbPeb=%#x\n",
pThis->BasicInfo.PebBaseAddress, pThis->cbPeb));
SIZE_T cbActualMem;
RT_ZERO(pThis->Peb);
rcNt = NtReadVirtualMemory(pThis->hProcess, pThis->BasicInfo.PebBaseAddress, &pThis->Peb, sizeof(pThis->Peb), &cbActualMem);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(pThis, "supR3HardNtChildGatherData", rcNt,
"NtReadVirtualMemory/Peb failed: %#x", rcNt);
/*
* Locate NtDll.
*/
supR3HardNtChildFindNtdll(pThis);
}
/**
* Does the actually respawning.
*
* @returns Never, will call exit or raise fatal error.
* @param iWhich Which respawn we're to check for, 1 being the
* first one, and 2 the second and final.
*/
static void supR3HardenedWinDoReSpawn(int iWhich)
{
NTSTATUS rcNt;
PPEB pPeb = NtCurrentPeb();
PRTL_USER_PROCESS_PARAMETERS pParentProcParams = pPeb->ProcessParameters;
SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
/*
* Init the child process data structure, creating the child communication
* event sempahores.
*/
SUPR3HARDNTCHILD This;
RT_ZERO(This);
This.iWhich = iWhich;
OBJECT_ATTRIBUTES ObjAttrs;
This.hEvtChild = NULL;
InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
This.hEvtParent = NULL;
InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&This.hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, SynchronizationEvent, FALSE));
/*
* Set up security descriptors.
*/
SECURITY_ATTRIBUTES ProcessSecAttrs;
MYSECURITYCLEANUP ProcessSecAttrsCleanup;
supR3HardNtChildInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
SECURITY_ATTRIBUTES ThreadSecAttrs;
MYSECURITYCLEANUP ThreadSecAttrsCleanup;
supR3HardNtChildInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
#if 1
/*
* Configure the startup info and creation flags.
*/
DWORD dwCreationFlags = CREATE_SUSPENDED;
STARTUPINFOEXW SiEx;
suplibHardenedMemSet(&SiEx, 0, sizeof(SiEx));
if (1)
SiEx.StartupInfo.cb = sizeof(SiEx.StartupInfo);
else
{
SiEx.StartupInfo.cb = sizeof(SiEx);
dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
/** @todo experiment with protected process stuff later on. */
}
SiEx.StartupInfo.dwFlags |= pParentProcParams->WindowFlags & STARTF_USESHOWWINDOW;
SiEx.StartupInfo.wShowWindow = (WORD)pParentProcParams->ShowWindowFlags;
SiEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
SiEx.StartupInfo.hStdInput = pParentProcParams->StandardInput;
SiEx.StartupInfo.hStdOutput = pParentProcParams->StandardOutput;
SiEx.StartupInfo.hStdError = pParentProcParams->StandardError;
/*
* Construct the command line and launch the process.
*/
PRTUTF16 pwszCmdLine = supR3HardNtChildConstructCmdLine(NULL, iWhich);
supR3HardenedWinEnableThreadCreation();
PROCESS_INFORMATION ProcessInfoW32;
if (!CreateProcessW(g_wszSupLibHardenedExePath,
pwszCmdLine,
&ProcessSecAttrs,
&ThreadSecAttrs,
TRUE /*fInheritHandles*/,
dwCreationFlags,
NULL /*pwszzEnvironment*/,
NULL /*pwszCurDir*/,
&SiEx.StartupInfo,
&ProcessInfoW32))
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
"Error relaunching VirtualBox VM process: %u\n"
"Command line: '%ls'",
RtlGetLastWin32Error(), pwszCmdLine);
supR3HardenedWinDisableThreadCreation();
SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [kernel32].\n",
iWhich, ProcessInfoW32.dwProcessId, ProcessInfoW32.dwThreadId));
This.hProcess = ProcessInfoW32.hProcess;
This.hThread = ProcessInfoW32.hThread;
#else
/*
* Construct the process parameters.
*/
UNICODE_STRING W32ImageName;
W32ImageName.Buffer = g_wszSupLibHardenedExePath; /* Yes the windows name for the process parameters. */
W32ImageName.Length = (USHORT)RTUtf16Len(g_wszSupLibHardenedExePath) * sizeof(WCHAR);
W32ImageName.MaximumLength = W32ImageName.Length + sizeof(WCHAR);
UNICODE_STRING CmdLine;
supR3HardNtChildConstructCmdLine(&CmdLine, iWhich);
PRTL_USER_PROCESS_PARAMETERS pProcParams = NULL;
SUPR3HARDENED_ASSERT_NT_SUCCESS(RtlCreateProcessParameters(&pProcParams,
&W32ImageName,
NULL /* DllPath - inherit from this process */,
NULL /* CurrentDirectory - inherit from this process */,
&CmdLine,
NULL /* Environment - inherit from this process */,
NULL /* WindowsTitle - none */,
NULL /* DesktopTitle - none. */,
NULL /* ShellInfo - none. */,
NULL /* RuntimeInfo - none (byte array for MSVCRT file info) */)
);
/** @todo this doesn't work. :-( */
pProcParams->ConsoleHandle = pParentProcParams->ConsoleHandle;
pProcParams->ConsoleFlags = pParentProcParams->ConsoleFlags;
pProcParams->StandardInput = pParentProcParams->StandardInput;
pProcParams->StandardOutput = pParentProcParams->StandardOutput;
pProcParams->StandardError = pParentProcParams->StandardError;
RTL_USER_PROCESS_INFORMATION ProcessInfoNt = { sizeof(ProcessInfoNt) };
rcNt = RtlCreateUserProcess(&g_SupLibHardenedExeNtPath.UniStr,
OBJ_INHERIT | OBJ_CASE_INSENSITIVE /*Attributes*/,
pProcParams,
NULL, //&ProcessSecAttrs,
NULL, //&ThreadSecAttrs,
NtCurrentProcess() /* ParentProcess */,
FALSE /*fInheritHandles*/,
NULL /* DebugPort */,
NULL /* ExceptionPort */,
&ProcessInfoNt);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
"Error relaunching VirtualBox VM process: %#x\n"
"Command line: '%ls'",
rcNt, CmdLine.Buffer);
SUP_DPRINTF(("supR3HardenedWinDoReSpawn(%d): New child %x.%x [ntdll].\n",
iWhich, ProcessInfo.ClientId.UniqueProcess, ProcessInfo.ClientId.UniqueThread));
RtlDestroyProcessParameters(pProcParams);
This.hProcess = ProcessInfoNt.ProcessHandle;
This.hThread = ProcessInfoNt.ThreadHandle;
#endif
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
/*
* Apply anti debugger notification trick to the thread. (Also done in
* supR3HardenedWinInit.) This may fail with STATUS_ACCESS_DENIED and
* maybe other errors.
*/
rcNt = NtSetInformationThread(This.hThread, ThreadHideFromDebugger, NULL, 0);
if (!NT_SUCCESS(rcNt))
SUP_DPRINTF(("supR3HardenedWinReSpawn: NtSetInformationThread/ThreadHideFromDebugger failed: %#x (harmless)\n", rcNt));
#endif
/*
* Perform very early child initialization.
*/
supR3HardNtChildGatherData(&This);
supR3HardNtChildScrewUpPebForInitialImageEvents(&This);
supR3HardNtChildSetUpChildInit(&This);
ULONG cSuspendCount = 0;
rcNt = NtResumeThread(This.hThread, &cSuspendCount);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(&This, "supR3HardenedWinDoReSpawn", rcNt, "NtResumeThread failed: %#x", rcNt);
/*
* Santizie the pre-NTDLL child when it's ready.
*
* AV software and other things injecting themselves into the embryonic
* and budding process to intercept API calls and what not. Unfortunately
* this is also the behavior of viruses, malware and other unfriendly
* software, so we won't stand for it. AV software can scan our image
* as they are loaded via kernel hooks, that's sufficient. No need for
* patching half of NTDLL or messing with the import table of the
* process executable.
*/
supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_PurifyChildAndCloseHandles, 2000 /*ms*/, "PurifyChildAndCloseHandles");
supR3HardNtChildPurify(&This);
supR3HardNtChildSanitizePeb(&This);
/*
* Close the unrestricted access handles. Since we need to wait on the
* child process, we'll reopen the process with limited access before doing
* away with the process handle returned by CreateProcess.
*/
supR3HardNtChildCloseFullAccessHandles(&This);
/*
* Signal the child that we've closed the unrestricted handles and it can
* safely try open the driver.
*/
rcNt = NtSetEvent(This.hEvtChild, NULL);
if (!NT_SUCCESS(rcNt))
supR3HardenedWinKillChild(&This, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
"NtSetEvent failed on child process handle: %#x\n", rcNt);
/*
* Ditch the loader cache so we don't sit on too much memory while waiting.
*/
supR3HardenedWinFlushLoaderCache();
supR3HardenedWinCompactHeaps();
/*
* Enable thread creation at this point so Ctrl-C and Ctrl-Break can be processed.
*/
supR3HardenedWinEnableThreadCreation();
/*
* Wait for the child to get to suplibHardenedWindowsMain so we can close the handles.
*/
supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_CloseEvents, 60000 /*ms*/, "CloseEvents");
NtClose(This.hEvtChild);
NtClose(This.hEvtParent);
This.hEvtChild = NULL;
This.hEvtParent = NULL;
/*
* Wait for the process to terminate.
*/
supR3HardNtChildWaitFor(&This, kSupR3WinChildReq_End, RT_INDEFINITE_WAIT, "the end");
SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
}
/**
* Logs the content of the given object directory.
*
* @returns true if it exists, false if not.
* @param pszDir The path of the directory to log (ASCII).
*/
static void supR3HardenedWinLogObjDir(const char *pszDir)
{
/*
* Open the driver object directory.
*/
RTUTF16 wszDir[128];
int rc = RTUtf16CopyAscii(wszDir, RT_ELEMENTS(wszDir), pszDir);
if (RT_FAILURE(rc))
{
SUP_DPRINTF(("supR3HardenedWinLogObjDir: RTUtf16CopyAscii -> %Rrc on '%s'\n", rc, pszDir));
return;
}
UNICODE_STRING NtDirName;
NtDirName.Buffer = (WCHAR *)wszDir;
NtDirName.Length = (USHORT)(RTUtf16Len(wszDir) * sizeof(WCHAR));
NtDirName.MaximumLength = NtDirName.Length + sizeof(WCHAR);
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
HANDLE hDir;
NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
SUP_DPRINTF(("supR3HardenedWinLogObjDir: %ls => %#x\n", wszDir, rcNt));
if (!NT_SUCCESS(rcNt))
return;
/*
* Enumerate it, looking for the driver.
*/
ULONG uObjDirCtx = 0;
for (;;)
{
uint32_t abBuffer[_64K + _1K];
ULONG cbActual;
rcNt = NtQueryDirectoryObject(hDir,
abBuffer,
sizeof(abBuffer) - 4, /* minus four for string terminator space. */
FALSE /*ReturnSingleEntry */,
FALSE /*RestartScan*/,
&uObjDirCtx,
&cbActual);
if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
{
SUP_DPRINTF(("supR3HardenedWinLogObjDir: NtQueryDirectoryObject => rcNt=%#x cbActual=%#x\n", rcNt, cbActual));
break;
}
POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
while (pObjDir->Name.Length != 0)
{
WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
SUP_DPRINTF((" %.*ls %.*ls\n",
pObjDir->TypeName.Length / sizeof(WCHAR), pObjDir->TypeName.Buffer,
pObjDir->Name.Length / sizeof(WCHAR), pObjDir->Name.Buffer));
/* Next directory entry. */
pObjDir++;
}
}
/*
* Clean up and return.
*/
NtClose(hDir);
}
/**
* Tries to open VBoxDrvErrorInfo and read extra error info from it.
*
* @returns pszErrorInfo.
* @param pszErrorInfo The destination buffer. Will always be
* terminated.
* @param cbErrorInfo The size of the destination buffer.
* @param pszPrefix What to prefix the error info with, if we got
* anything.
*/
DECLHIDDEN(char *) supR3HardenedWinReadErrorInfoDevice(char *pszErrorInfo, size_t cbErrorInfo, const char *pszPrefix)
{
RT_BZERO(pszErrorInfo, cbErrorInfo);
/*
* Try open the device.
*/
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(SUPDRV_NT_DEVICE_NAME_ERROR_INFO);
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
NTSTATUS rcNt = NtCreateFile(&hFile,
GENERIC_READ, /* No SYNCHRONIZE. */
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (NT_SUCCESS(rcNt))
{
/*
* Try read error info.
*/
size_t cchPrefix = strlen(pszPrefix);
if (cchPrefix + 3 < cbErrorInfo)
{
LARGE_INTEGER offRead;
offRead.QuadPart = 0;
rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
&pszErrorInfo[cchPrefix], (ULONG)(cbErrorInfo - cchPrefix - 1), &offRead, NULL);
if (NT_SUCCESS(rcNt))
{
memcpy(pszErrorInfo, pszPrefix, cchPrefix);
pszErrorInfo[cbErrorInfo - 1] = '\0';
SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: '%s'", &pszErrorInfo[cchPrefix]));
}
else
{
*pszErrorInfo = '\0';
if (rcNt != STATUS_END_OF_FILE)
SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtReadFile -> %#x\n", rcNt));
}
}
else
RTStrCopy(pszErrorInfo, cbErrorInfo, "error info buffer too small");
NtClose(hFile);
}
else
SUP_DPRINTF(("supR3HardenedWinReadErrorInfoDevice: NtCreateFile -> %#x\n", rcNt));
return pszErrorInfo;
}
/**
* Checks if the driver exists.
*
* This checks whether the driver is present in the /Driver object directory.
* Drivers being initialized or terminated will have an object there
* before/after their devices nodes are created/deleted.
*
* @returns true if it exists, false if not.
* @param pszDriver The driver name.
*/
static bool supR3HardenedWinDriverExists(const char *pszDriver)
{
/*
* Open the driver object directory.
*/
UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
HANDLE hDir;
NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
#ifdef VBOX_STRICT
SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
#endif
if (!NT_SUCCESS(rcNt))
return true;
/*
* Enumerate it, looking for the driver.
*/
bool fFound = true;
ULONG uObjDirCtx = 0;
do
{
uint32_t abBuffer[_64K + _1K];
ULONG cbActual;
rcNt = NtQueryDirectoryObject(hDir,
abBuffer,
sizeof(abBuffer) - 4, /* minus four for string terminator space. */
FALSE /*ReturnSingleEntry */,
FALSE /*RestartScan*/,
&uObjDirCtx,
&cbActual);
if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
break;
POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
while (pObjDir->Name.Length != 0)
{
WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
if ( pObjDir->Name.Length > 1
&& RTUtf16ICmpAscii(pObjDir->Name.Buffer, pszDriver) == 0)
{
fFound = true;
break;
}
pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
/* Next directory entry. */
pObjDir++;
}
} while (!fFound);
/*
* Clean up and return.
*/
NtClose(hDir);
return fFound;
}
/**
* Open the stub device before the 2nd respawn.
*/
static void supR3HardenedWinOpenStubDevice(void)
{
if (g_fSupStubOpened)
return;
/*
* Retry if we think driver might still be initializing (STATUS_NO_SUCH_DEVICE + \Drivers\VBoxDrv).
*/
static const WCHAR s_wszName[] = SUPDRV_NT_DEVICE_NAME_STUB;
uint64_t const uMsTsStart = supR3HardenedWinGetMilliTS();
NTSTATUS rcNt;
uint32_t iTry;
for (iTry = 0;; iTry++)
{
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
UNICODE_STRING NtName;
NtName.Buffer = (PWSTR)s_wszName;
NtName.Length = sizeof(s_wszName) - sizeof(WCHAR);
NtName.MaximumLength = sizeof(s_wszName);
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
rcNt = NtCreateFile(&hFile,
GENERIC_READ | GENERIC_WRITE, /* No SYNCHRONIZE. */
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE, /* No FILE_SYNCHRONOUS_IO_NONALERT. */
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
/* The STATUS_NO_SUCH_DEVICE might be returned if the device is not
completely initialized. Delay a little bit and try again. */
if (rcNt != STATUS_NO_SUCH_DEVICE)
break;
if (iTry > 0 && supR3HardenedWinGetMilliTS() - uMsTsStart > 5000) /* 5 sec, at least two tries */
break;
if (!supR3HardenedWinDriverExists("VBoxDrv"))
{
/** @todo Consider starting the VBoxdrv.sys service. Requires 2nd process
* though, rather complicated actually as CreateProcess causes all
* kind of things to happen to this process which would make it hard to
* pass the process verification tests... :-/ */
break;
}
LARGE_INTEGER Time;
if (iTry < 8)
Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
else
Time.QuadPart = -32000000 / 100; /* 32ms in 100ns units, relative time. */
NtDelayExecution(TRUE, &Time);
}
if (NT_SUCCESS(rcNt))
g_fSupStubOpened = true;
else
{
/*
* Report trouble (fatal). For some errors codes we try gather some
* extra information that goes into VBoxStartup.log so that we stand a
* better chance resolving the issue.
*/
char szErrorInfo[_4K];
int rc = VERR_OPEN_FAILED;
if (SUP_NT_STATUS_IS_VBOX(rcNt)) /* See VBoxDrvNtErr2NtStatus. */
{
rc = SUP_NT_STATUS_TO_VBOX(rcNt);
/*
* \Windows\ApiPort open trouble. So far only
* STATUS_OBJECT_TYPE_MISMATCH has been observed.
*/
if (rc == VERR_SUPDRV_APIPORT_OPEN_ERROR)
{
SUP_DPRINTF(("Error opening VBoxDrvStub: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"));
uint32_t uSessionId = NtCurrentPeb()->SessionId;
SUP_DPRINTF((" SessionID=%#x\n", uSessionId));
char szDir[64];
if (uSessionId == 0)
RTStrCopy(szDir, sizeof(szDir), "\\Windows");
else
{
RTStrPrintf(szDir, sizeof(szDir), "\\Sessions\\%u\\Windows", uSessionId);
supR3HardenedWinLogObjDir(szDir);
}
supR3HardenedWinLogObjDir("\\Windows");
supR3HardenedWinLogObjDir("\\Sessions");
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, rc,
"NtCreateFile(%ls) failed: VERR_SUPDRV_APIPORT_OPEN_ERROR\n"
"\n"
"Error getting %s\\ApiPort in the driver from vboxdrv.\n"
"\n"
"Could be due to security software is redirecting access to it, so please include full "
"details of such software in a bug report. VBoxStartup.log may contain details important "
"to resolving the issue.%s"
, s_wszName, szDir,
supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
"\n\nVBoxDrvStub error: "));
}
/*
* Generic VBox failure message.
*/
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, rc,
"NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)%s", s_wszName, rc, rcNt,
supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
"\nVBoxDrvStub error: "));
}
else
{
const char *pszDefine;
switch (rcNt)
{
case STATUS_NO_SUCH_DEVICE: pszDefine = " STATUS_NO_SUCH_DEVICE"; break;
case STATUS_OBJECT_NAME_NOT_FOUND: pszDefine = " STATUS_OBJECT_NAME_NOT_FOUND"; break;
case STATUS_ACCESS_DENIED: pszDefine = " STATUS_ACCESS_DENIED"; break;
case STATUS_TRUST_FAILURE: pszDefine = " STATUS_TRUST_FAILURE"; break;
default: pszDefine = ""; break;
}
/*
* Problems opening the device is generally due to driver load/
* unload issues. Check whether the driver is loaded and make
* suggestions accordingly.
*/
/** @todo don't fail during early init, wait till later and try load the driver if missing or at least query the service manager for additional information. */
if ( rcNt == STATUS_NO_SUCH_DEVICE
|| rcNt == STATUS_OBJECT_NAME_NOT_FOUND)
{
SUP_DPRINTF(("Error opening VBoxDrvStub: %s\n", pszDefine));
if (supR3HardenedWinDriverExists("VBoxDrv"))
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
"NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
"\n"
"Driver is probably stuck stopping/starting. Try 'sc.exe query vboxdrv' to get more "
"information about its state. Rebooting may actually help.%s"
, s_wszName, rcNt, pszDefine, iTry,
supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
"\nVBoxDrvStub error: "));
else
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
"NtCreateFile(%ls) failed: %#x%s (%u retries)\n"
"\n"
"Driver is does not appear to be loaded. Try 'sc.exe start vboxdrv', reinstall "
"VirtualBox or reboot.%s"
, s_wszName, rcNt, pszDefine, iTry,
supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
"\nVBoxDrvStub error: "));
}
/* Generic NT failure message. */
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
"NtCreateFile(%ls) failed: %#x%s (%u retries)%s",
s_wszName, rcNt, pszDefine, iTry,
supR3HardenedWinReadErrorInfoDevice(szErrorInfo, sizeof(szErrorInfo),
"\nVBoxDrvStub error: "));
}
}
}
/**
* Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
*
* @returns Program exit code.
*/
DECLHIDDEN(int) supR3HardenedWinReSpawn(int iWhich)
{
/*
* Before the 2nd respawn we set up a child protection deal with the
* support driver via /Devices/VBoxDrvStub. (We tried to do this
* during the early init, but in case we had trouble accessing vboxdrv we
* retry it here where we have kernel32.dll and others to pull in for
* better diagnostics.)
*/
if (iWhich == 2)
supR3HardenedWinOpenStubDevice();
/*
* Make sure we're alone in the stub process before creating the VM process
* and that there isn't any debuggers attached.
*/
if (iWhich == 2)
{
int rc = supHardNtVpDebugger(NtCurrentProcess(), RTErrInfoInitStatic(&g_ErrInfoStatic));
if (RT_SUCCESS(rc))
rc = supHardNtVpThread(NtCurrentProcess(), NtCurrentThread(), RTErrInfoInitStatic(&g_ErrInfoStatic));
if (RT_FAILURE(rc))
supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Integrity, rc, "%s", g_ErrInfoStatic.szMsg);
}
/*
* Respawn the process with kernel protection for the new process.
*/
supR3HardenedWinDoReSpawn(iWhich);
SUPR3HARDENED_ASSERT(false); /* We're not supposed to get here! */
return RTEXITCODE_FAILURE;
}
/**
* Checks if re-spawning is required, replacing the respawn argument if not.
*
* @returns true if required, false if not. In the latter case, the first
* argument in the vector is replaced.
* @param iWhich Which respawn we're to check for, 1 being the
* first one, and 2 the second and final.
* @param cArgs The number of arguments.
* @param papszArgs Pointer to the argument vector.
*/
DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int iWhich, int cArgs, char **papszArgs)
{
SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
SUPR3HARDENED_ASSERT(iWhich == 1 || iWhich == 2);
if (cArgs < 1)
return true;
if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
{
if (iWhich > 1)
return true;
}
else if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
{
if (iWhich < 2)
return false;
}
else
return true;
/* Replace the argument. */
papszArgs[0] = g_szSupLibHardenedExePath;
return false;
}
/**
* Initializes the windows verficiation bits and other things we're better off
* doing after main() has passed on it's data.
*
* @param fFlags The main flags.
* @param fAvastKludge Whether to apply the avast kludge.
*/
DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge)
{
NTSTATUS rcNt;
#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
/*
* Install a anti debugging hack before we continue. This prevents most
* notifications from ending up in the debugger. (Also applied to the
* child process when respawning.)
*/
rcNt = NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, NULL, 0);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_GENERAL_FAILURE,
"NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
#endif
/*
* Init the verifier.
*/
RTErrInfoInitStatic(&g_ErrInfoStatic);
int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
if (RT_FAILURE(rc))
supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
"supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
/*
* Get the windows system directory from the KnownDlls dir.
*/
HANDLE hSymlink = INVALID_HANDLE_VALUE;
UNICODE_STRING UniStr = RTNT_CONSTANT_UNISTR(L"\\KnownDlls\\KnownDllPath");
OBJECT_ATTRIBUTES ObjAttrs;
InitializeObjectAttributes(&ObjAttrs, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
rcNt = NtOpenSymbolicLinkObject(&hSymlink, SYMBOLIC_LINK_QUERY, &ObjAttrs);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error opening '%ls': %#x", UniStr.Buffer, rcNt);
g_System32WinPath.UniStr.Buffer = g_System32WinPath.awcBuffer;
g_System32WinPath.UniStr.Length = 0;
g_System32WinPath.UniStr.MaximumLength = sizeof(g_System32WinPath.awcBuffer) - sizeof(RTUTF16);
rcNt = NtQuerySymbolicLinkObject(hSymlink, &g_System32WinPath.UniStr, NULL);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error querying '%ls': %#x", UniStr.Buffer, rcNt);
g_System32WinPath.UniStr.Buffer[g_System32WinPath.UniStr.Length / sizeof(RTUTF16)] = '\0';
SUP_DPRINTF(("KnownDllPath: %ls\n", g_System32WinPath.UniStr.Buffer));
NtClose(hSymlink);
if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
{
if (fAvastKludge)
{
/*
* Do a self purification to cure avast's weird NtOpenFile write-thru
* change in GetBinaryTypeW change in kernel32. Unfortunately, avast
* uses a system thread to perform the process modifications, which
* means it's hard to make sure it had the chance to make them...
*
* We have to resort to kludge doing yield and sleep fudging for a
* number of milliseconds and schedulings before we can hope that avast
* and similar products have done what they need to do. If we do any
* fixes, we wait for a while again and redo it until we're clean.
*
* This is unfortunately kind of fragile.
*/
uint32_t cMsFudge = g_fSupAdversaries ? 512 : 128;
uint32_t cFixes;
for (uint32_t iLoop = 0; iLoop < 16; iLoop++)
{
uint32_t cSleeps = 0;
uint64_t uMsTsStart = supR3HardenedWinGetMilliTS();
do
{
NtYieldExecution();
LARGE_INTEGER Time;
Time.QuadPart = -8000000 / 100; /* 8ms in 100ns units, relative time. */
NtDelayExecution(FALSE, &Time);
cSleeps++;
} while ( supR3HardenedWinGetMilliTS() - uMsTsStart <= cMsFudge
|| cSleeps < 8);
SUP_DPRINTF(("supR3HardenedWinInit: Startup delay kludge #2/%u: %u ms, %u sleeps\n",
iLoop, supR3HardenedWinGetMilliTS() - uMsTsStart, cSleeps));
cFixes = 0;
rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), SUPHARDNTVPKIND_SELF_PURIFICATION,
0 /*fFlags*/, &cFixes, NULL /*pErrInfo*/);
if (RT_FAILURE(rc) || cFixes == 0)
break;
if (!g_fSupAdversaries)
g_fSupAdversaries |= SUPHARDNT_ADVERSARY_UNKNOWN;
cMsFudge = 512;
/* Log the KiOpPrefetchPatchCount value if available, hoping it might sched some light on spider38's case. */
ULONG cPatchCount = 0;
rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
&cPatchCount, sizeof(cPatchCount), NULL);
if (NT_SUCCESS(rcNt))
SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
cFixes, g_fSupAdversaries, cPatchCount));
else
SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x\n", cFixes, g_fSupAdversaries));
}
}
/*
* Install the hooks.
*/
supR3HardenedWinInstallHooks();
}
#ifndef VBOX_WITH_VISTA_NO_SP
/*
* Complain about Vista w/o service pack if we're launching a VM.
*/
if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
&& g_uNtVerCombined >= SUP_NT_VER_VISTA
&& g_uNtVerCombined < SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, VERR_NOT_SUPPORTED,
"Window Vista without any service pack installed is not supported. Please install the latest service pack.");
#endif
}
/**
* Converts the Windows command line string (UTF-16) to an array of UTF-8
* arguments suitable for passing to main().
*
* @returns Pointer to the argument array.
* @param pawcCmdLine The UTF-16 windows command line to parse.
* @param cwcCmdLine The length of the command line.
* @param pcArgs Where to return the number of arguments.
*/
static char **suplibCommandLineToArgvWStub(PCRTUTF16 pawcCmdLine, size_t cwcCmdLine, int *pcArgs)
{
/*
* Convert the command line string to UTF-8.
*/
char *pszCmdLine = NULL;
SUPR3HARDENED_ASSERT(RT_SUCCESS(RTUtf16ToUtf8Ex(pawcCmdLine, cwcCmdLine, &pszCmdLine, 0, NULL)));
/*
* Parse the command line, carving argument strings out of it.
*/
int cArgs = 0;
int cArgsAllocated = 4;
char **papszArgs = (char **)RTMemAllocZ(sizeof(char *) * cArgsAllocated);
char *pszSrc = pszCmdLine;
for (;;)
{
/* skip leading blanks. */
char ch = *pszSrc;
while (suplibCommandLineIsArgSeparator(ch))
ch = *++pszSrc;
if (!ch)
break;
/* Add argument to the vector. */
if (cArgs + 2 >= cArgsAllocated)
{
cArgsAllocated *= 2;
papszArgs = (char **)RTMemRealloc(papszArgs, sizeof(char *) * cArgsAllocated);
}
papszArgs[cArgs++] = pszSrc;
papszArgs[cArgs] = NULL;
/* Unquote and unescape the string. */
char *pszDst = pszSrc++;
bool fQuoted = false;
do
{
if (ch == '"')
fQuoted = !fQuoted;
else if (ch != '\\' || (*pszSrc != '\\' && *pszSrc != '"'))
*pszDst++ = ch;
else
{
unsigned cSlashes = 0;
while ((ch = *pszSrc++) == '\\')
cSlashes++;
if (ch == '"')
{
while (cSlashes >= 2)
{
cSlashes -= 2;
*pszDst++ = '\\';
}
if (cSlashes)
*pszDst++ = '"';
else
fQuoted = !fQuoted;
}
else
{
pszSrc--;
while (cSlashes-- > 0)
*pszDst++ = '\\';
}
}
ch = *pszSrc++;
} while (ch != '\0' && (fQuoted || !suplibCommandLineIsArgSeparator(ch)));
/* Terminate the argument. */
*pszDst = '\0';
if (!ch)
break;
}
*pcArgs = cArgs;
return papszArgs;
}
/**
* Logs information about a file from a protection product or from Windows.
*
* The purpose here is to better see which version of the product is installed
* and not needing to depend on the user supplying the correct information.
*
* @param pwszFile The NT path to the file.
* @param fAdversarial Set if from a protection product, false if
* system file.
*/
static void supR3HardenedLogFileInfo(PCRTUTF16 pwszFile, bool fAdversarial)
{
/*
* Open the file.
*/
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
UNICODE_STRING UniStrName;
UniStrName.Buffer = (WCHAR *)pwszFile;
UniStrName.Length = (USHORT)(RTUtf16Len(pwszFile) * sizeof(WCHAR));
UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
NTSTATUS rcNt = NtCreateFile(&hFile,
GENERIC_READ | SYNCHRONIZE,
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
rcNt = Ios.Status;
if (NT_SUCCESS(rcNt))
{
SUP_DPRINTF(("%ls:\n", pwszFile));
union
{
uint64_t u64AlignmentInsurance;
FILE_BASIC_INFORMATION BasicInfo;
FILE_STANDARD_INFORMATION StdInfo;
uint8_t abBuf[32768];
RTUTF16 awcBuf[16384];
IMAGE_DOS_HEADER MzHdr;
} u;
RTTIMESPEC TimeSpec;
char szTmp[64];
/*
* Print basic file information available via NtQueryInformationFile.
*/
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
rcNt = NtQueryInformationFile(hFile, &Ios, &u.BasicInfo, sizeof(u.BasicInfo), FileBasicInformation);
if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
{
SUP_DPRINTF((" CreationTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.CreationTime.QuadPart), szTmp, sizeof(szTmp))));
/*SUP_DPRINTF((" LastAccessTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastAccessTime.QuadPart), szTmp, sizeof(szTmp))));*/
SUP_DPRINTF((" LastWriteTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.LastWriteTime.QuadPart), szTmp, sizeof(szTmp))));
SUP_DPRINTF((" ChangeTime: %s\n", RTTimeSpecToString(RTTimeSpecSetNtTime(&TimeSpec, u.BasicInfo.ChangeTime.QuadPart), szTmp, sizeof(szTmp))));
SUP_DPRINTF((" FileAttributes: %#x\n", u.BasicInfo.FileAttributes));
}
else
SUP_DPRINTF((" FileBasicInformation -> %#x %#x\n", rcNt, Ios.Status));
rcNt = NtQueryInformationFile(hFile, &Ios, &u.StdInfo, sizeof(u.StdInfo), FileStandardInformation);
if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
SUP_DPRINTF((" Size: %#llx\n", u.StdInfo.EndOfFile.QuadPart));
else
SUP_DPRINTF((" FileStandardInformation -> %#x %#x\n", rcNt, Ios.Status));
/*
* Read the image header and extract the timestamp and other useful info.
*/
RT_ZERO(u);
LARGE_INTEGER offRead;
offRead.QuadPart = 0;
rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
&u, (ULONG)sizeof(u), &offRead, NULL);
if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
{
uint32_t offNtHdrs = 0;
if (u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE)
offNtHdrs = u.MzHdr.e_lfanew;
if (offNtHdrs < sizeof(u) - sizeof(IMAGE_NT_HEADERS))
{
PIMAGE_NT_HEADERS64 pNtHdrs64 = (PIMAGE_NT_HEADERS64)&u.abBuf[offNtHdrs];
PIMAGE_NT_HEADERS32 pNtHdrs32 = (PIMAGE_NT_HEADERS32)&u.abBuf[offNtHdrs];
if (pNtHdrs64->Signature == IMAGE_NT_SIGNATURE)
{
SUP_DPRINTF((" NT Headers: %#x\n", offNtHdrs));
SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
SUP_DPRINTF((" Machine: %#x%s\n", pNtHdrs64->FileHeader.Machine,
pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ? " - i386"
: pNtHdrs64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ? " - amd64" : ""));
SUP_DPRINTF((" Timestamp: %#x\n", pNtHdrs64->FileHeader.TimeDateStamp));
SUP_DPRINTF((" Image Version: %u.%u\n",
pNtHdrs64->OptionalHeader.MajorImageVersion, pNtHdrs64->OptionalHeader.MinorImageVersion));
SUP_DPRINTF((" SizeOfImage: %#x (%u)\n", pNtHdrs64->OptionalHeader.SizeOfImage, pNtHdrs64->OptionalHeader.SizeOfImage));
/*
* Very crude way to extract info from the file version resource.
*/
PIMAGE_SECTION_HEADER paSectHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs64->OptionalHeader
+ pNtHdrs64->FileHeader.SizeOfOptionalHeader);
IMAGE_DATA_DIRECTORY RsrcDir = { 0, 0 };
if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)
&& pNtHdrs64->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
RsrcDir = pNtHdrs64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
else if ( pNtHdrs64->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
&& pNtHdrs32->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE)
RsrcDir = pNtHdrs32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
SUP_DPRINTF((" Resource Dir: %#x LB %#x\n", RsrcDir.VirtualAddress, RsrcDir.Size));
if ( RsrcDir.VirtualAddress > offNtHdrs
&& RsrcDir.Size > 0
&& (uintptr_t)&u + sizeof(u) - (uintptr_t)paSectHdrs
>= pNtHdrs64->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) )
{
offRead.QuadPart = 0;
for (uint32_t i = 0; i < pNtHdrs64->FileHeader.NumberOfSections; i++)
if ( paSectHdrs[i].VirtualAddress - RsrcDir.VirtualAddress < paSectHdrs[i].SizeOfRawData
&& paSectHdrs[i].PointerToRawData > offNtHdrs)
{
offRead.QuadPart = paSectHdrs[i].PointerToRawData
+ (paSectHdrs[i].VirtualAddress - RsrcDir.VirtualAddress);
break;
}
if (offRead.QuadPart > 0)
{
RT_ZERO(u);
rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios,
&u, (ULONG)sizeof(u), &offRead, NULL);
if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
{
static const struct { PCRTUTF16 pwsz; size_t cb; } s_abFields[] =
{
#define MY_WIDE_STR_TUPLE(a_sz) { L ## a_sz, sizeof(L ## a_sz) - sizeof(RTUTF16) }
MY_WIDE_STR_TUPLE("ProductName"),
MY_WIDE_STR_TUPLE("ProductVersion"),
MY_WIDE_STR_TUPLE("FileVersion"),
MY_WIDE_STR_TUPLE("SpecialBuild"),
MY_WIDE_STR_TUPLE("PrivateBuild"),
MY_WIDE_STR_TUPLE("FileDescription"),
#undef MY_WIDE_STR_TUPLE
};
for (uint32_t i = 0; i < RT_ELEMENTS(s_abFields); i++)
{
size_t cwcLeft = (sizeof(u) - s_abFields[i].cb - 10) / sizeof(RTUTF16);
PCRTUTF16 pwc = u.awcBuf;
RTUTF16 const wcFirst = *s_abFields[i].pwsz;
while (cwcLeft-- > 0)
{
if ( pwc[0] == 1 /* wType == text */
&& pwc[1] == wcFirst)
{
if (memcmp(pwc + 1, s_abFields[i].pwsz, s_abFields[i].cb + sizeof(RTUTF16)) == 0)
{
size_t cwcField = s_abFields[i].cb / sizeof(RTUTF16);
pwc += cwcField + 2;
cwcLeft -= cwcField + 2;
for (uint32_t iPadding = 0; iPadding < 3; iPadding++, pwc++, cwcLeft--)
if (*pwc)
break;
int rc = RTUtf16ValidateEncodingEx(pwc, cwcLeft,
RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
if (RT_SUCCESS(rc))
SUP_DPRINTF((" %ls:%*s %ls",
s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", pwc));
else
SUP_DPRINTF((" %ls:%*s rc=%Rrc",
s_abFields[i].pwsz, cwcField < 15 ? 15 - cwcField : 0, "", rc));
break;
}
}
pwc++;
}
}
}
else
SUP_DPRINTF((" NtReadFile @%#llx -> %#x %#x\n", offRead.QuadPart, rcNt, Ios.Status));
}
else
SUP_DPRINTF((" Resource section not found.\n"));
}
}
else
SUP_DPRINTF((" Nt Headers @%#x: Invalid signature\n", offNtHdrs));
}
else
SUP_DPRINTF((" Nt Headers @%#x: out side buffer\n", offNtHdrs));
}
else
SUP_DPRINTF((" NtReadFile @0 -> %#x %#x\n", rcNt, Ios.Status));
NtClose(hFile);
}
}
/**
* Scans the Driver directory for drivers which may invade our processes.
*
* @returns Mask of SUPHARDNT_ADVERSARY_XXX flags.
*
* @remarks The enumeration of \Driver normally requires administrator
* privileges. So, the detection we're doing here isn't always gonna
* work just based on that.
*
* @todo Find drivers in \FileSystems as well, then we could detect VrNsdDrv
* from ViRobot APT Shield 2.0.
*/
static uint32_t supR3HardenedWinFindAdversaries(void)
{
static const struct
{
uint32_t fAdversary;
const char *pszDriver;
} s_aDrivers[] =
{
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SRTSPX" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymDS" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymEvent" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymIRON" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, "SymNetS" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswHwid" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswMonFlt" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswRdr2" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswRvrt" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswSnx" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswsp" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswStm" },
{ SUPHARDNT_ADVERSARY_AVAST, "aswVmm" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmcomm" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmactmon" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmevtmgr" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmtdi" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmebc64" }, /* Titanium internet security, not officescan. */
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmeevw" }, /* Titanium internet security, not officescan. */
{ SUPHARDNT_ADVERSARY_TRENDMICRO, "tmciesc" }, /* Titanium internet security, not officescan. */
{ SUPHARDNT_ADVERSARY_MCAFEE, "cfwids" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "McPvDrv" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfeapfk" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfeavfk" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfefirek" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfehidk" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfencbdc" },
{ SUPHARDNT_ADVERSARY_MCAFEE, "mfewfpk" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "kl1" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "klflt" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "klif" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "KLIM6" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "klkbdflt" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "klmouflt" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "kltdi" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, "kneps" },
{ SUPHARDNT_ADVERSARY_MBAM, "MBAMWebAccessControl" },
{ SUPHARDNT_ADVERSARY_MBAM, "mbam" },
{ SUPHARDNT_ADVERSARY_MBAM, "mbamchameleon" },
{ SUPHARDNT_ADVERSARY_MBAM, "mwav" },
{ SUPHARDNT_ADVERSARY_MBAM, "mbamswissarmy" },
{ SUPHARDNT_ADVERSARY_AVG, "avgfwfd" },
{ SUPHARDNT_ADVERSARY_AVG, "avgtdia" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINAflt" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINFile" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINKNC" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINProc" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINProt" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSINReg" },
{ SUPHARDNT_ADVERSARY_PANDA, "PSKMAD" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSAlpc" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSHttp" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNShttps" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSIds" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSNAHSL" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSpicc" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSPihsw" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSPop3" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSProt" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSPrv" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSSmtp" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNSStrm" },
{ SUPHARDNT_ADVERSARY_PANDA, "NNStlsc" },
{ SUPHARDNT_ADVERSARY_MSE, "NisDrv" },
/*{ SUPHARDNT_ADVERSARY_COMODO, "cmdguard" }, file system */
{ SUPHARDNT_ADVERSARY_COMODO, "inspect" },
{ SUPHARDNT_ADVERSARY_COMODO, "cmdHlp" },
{ SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN, "dgmaster" }, /* Not verified. */
};
static const struct
{
uint32_t fAdversary;
PCRTUTF16 pwszFile;
} s_aFiles[] =
{
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\SysPlant.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\sysfer.dll" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\sysferThunk.dll" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ccsetx64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\ironx64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtsp64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\srtspx64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symds64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symefa64.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symelam.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\N360x64\\1505000.013\\symnets.sys" },
{ SUPHARDNT_ADVERSARY_SYMANTEC_N360, L"\\SystemRoot\\System32\\drivers\\symevent64x86.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswHwid.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswMonFlt.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRdr2.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswRvrt.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswSnx.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswsp.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswStm.sys" },
{ SUPHARDNT_ADVERSARY_AVAST, L"\\SystemRoot\\System32\\drivers\\aswVmm.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmcomm.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmactmon.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmevtmgr.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmtdi.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmebc64.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmeevw.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\tmciesc.sys" },
{ SUPHARDNT_ADVERSARY_TRENDMICRO_SAKFILE, L"\\SystemRoot\\System32\\drivers\\sakfile.sys" }, /* Data Loss Prevention, not officescan. */
{ SUPHARDNT_ADVERSARY_TRENDMICRO, L"\\SystemRoot\\System32\\drivers\\sakcd.sys" }, /* Data Loss Prevention, not officescan. */
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\cfwids.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\McPvDrv.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeapfk.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfeavfk.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfefirek.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfehidk.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfencbdc.sys" },
{ SUPHARDNT_ADVERSARY_MCAFEE, L"\\SystemRoot\\System32\\drivers\\mfewfpk.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kl1.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klflt.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klif.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klim6.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klkbdflt.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\klmouflt.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kltdi.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\drivers\\kneps.sys" },
{ SUPHARDNT_ADVERSARY_KASPERSKY, L"\\SystemRoot\\System32\\klfphc.dll" },
{ SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\MBAMSwissArmy.sys" },
{ SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mwac.sys" },
{ SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbamchameleon.sys" },
{ SUPHARDNT_ADVERSARY_MBAM, L"\\SystemRoot\\System32\\drivers\\mbam.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgrkx64.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgmfx64.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsdrivera.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgidsha.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgtdia.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgloga.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgldx64.sys" },
{ SUPHARDNT_ADVERSARY_AVG, L"\\SystemRoot\\System32\\drivers\\avgdiska.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINAflt.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINFile.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINKNC.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProc.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINProt.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSINReg.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\PSKMAD.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSAlpc.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSHttp.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNShttps.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSIds.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSNAHSL.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSpicc.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPihsw.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPop3.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSProt.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSPrv.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSSmtp.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNSStrm.sys" },
{ SUPHARDNT_ADVERSARY_PANDA, L"\\SystemRoot\\System32\\drivers\\NNStlsc.sys" },
{ SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\MpFilter.sys" },
{ SUPHARDNT_ADVERSARY_MSE, L"\\SystemRoot\\System32\\drivers\\NisDrvWFP.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdguard.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmderd.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\inspect.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cmdhlp.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\cfrmd.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\drivers\\hmd.sys" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\guard64.dll" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdvrt64.dll" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdkbd64.dll" },
{ SUPHARDNT_ADVERSARY_COMODO, L"\\SystemRoot\\System32\\cmdcsr.dll" },
{ SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\drivers\\vsdatant.sys" },
{ SUPHARDNT_ADVERSARY_ZONE_ALARM, L"\\SystemRoot\\System32\\AntiTheftCredentialProvider.dll" },
{ SUPHARDNT_ADVERSARY_DIGITAL_GUARDIAN, L"\\SystemRoot\\System32\\drivers\\dgmaster.sys" },
};
uint32_t fFound = 0;
/*
* Open the driver object directory.
*/
UNICODE_STRING NtDirName = RTNT_CONSTANT_UNISTR(L"\\Driver");
OBJECT_ATTRIBUTES ObjAttr;
InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
HANDLE hDir;
NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | FILE_LIST_DIRECTORY, &ObjAttr);
#ifdef VBOX_STRICT
SUPR3HARDENED_ASSERT_NT_SUCCESS(rcNt);
#endif
if (NT_SUCCESS(rcNt))
{
/*
* Enumerate it, looking for the driver.
*/
ULONG uObjDirCtx = 0;
for (;;)
{
uint32_t abBuffer[_64K + _1K];
ULONG cbActual;
rcNt = NtQueryDirectoryObject(hDir,
abBuffer,
sizeof(abBuffer) - 4, /* minus four for string terminator space. */
FALSE /*ReturnSingleEntry */,
FALSE /*RestartScan*/,
&uObjDirCtx,
&cbActual);
if (!NT_SUCCESS(rcNt) || cbActual < sizeof(OBJECT_DIRECTORY_INFORMATION))
break;
POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)abBuffer;
while (pObjDir->Name.Length != 0)
{
WCHAR wcSaved = pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)];
pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = '\0';
for (uint32_t i = 0; i < RT_ELEMENTS(s_aDrivers); i++)
if (RTUtf16ICmpAscii(pObjDir->Name.Buffer, s_aDrivers[i].pszDriver) == 0)
{
fFound |= s_aDrivers[i].fAdversary;
SUP_DPRINTF(("Found driver %s (%#x)\n", s_aDrivers[i].pszDriver, s_aDrivers[i].fAdversary));
break;
}
pObjDir->Name.Buffer[pObjDir->Name.Length / sizeof(WCHAR)] = wcSaved;
/* Next directory entry. */
pObjDir++;
}
}
NtClose(hDir);
}
else
SUP_DPRINTF(("NtOpenDirectoryObject failed on \\Driver: %#x\n", rcNt));
/*
* Look for files.
*/
for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
{
HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
UNICODE_STRING UniStrName;
UniStrName.Buffer = (WCHAR *)s_aFiles[i].pwszFile;
UniStrName.Length = (USHORT)(RTUtf16Len(s_aFiles[i].pwszFile) * sizeof(WCHAR));
UniStrName.MaximumLength = UniStrName.Length + sizeof(WCHAR);
InitializeObjectAttributes(&ObjAttr, &UniStrName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
rcNt = NtCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &ObjAttr, &Ios, NULL /* Allocation Size*/,
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*EaBuffer*/, 0 /*EaLength*/);
if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
{
fFound |= s_aFiles[i].fAdversary;
NtClose(hFile);
}
}
/*
* Log details.
*/
SUP_DPRINTF(("supR3HardenedWinFindAdversaries: %#x\n", fFound));
for (uint32_t i = 0; i < RT_ELEMENTS(s_aFiles); i++)
if (fFound & s_aFiles[i].fAdversary)
supR3HardenedLogFileInfo(s_aFiles[i].pwszFile, true /* fAdversarial */);
return fFound;
}
extern "C" int main(int argc, char **argv, char **envp);
/**
* The executable entry point.
*
* This is normally taken care of by the C runtime library, but we don't want to
* get involved with anything as complicated like the CRT in this setup. So, we
* it everything ourselves, including parameter parsing.
*/
extern "C" void __stdcall suplibHardenedWindowsMain(void)
{
RTEXITCODE rcExit = RTEXITCODE_FAILURE;
g_cSuplibHardenedWindowsMainCalls++;
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED;
/*
* Initialize the NTDLL API wrappers. This aims at bypassing patched NTDLL
* in all the processes leading up the VM process.
*/
supR3HardenedWinInitImports();
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED;
/*
* Notify the parent process that we're probably capable of reporting our
* own errors.
*/
if (g_ProcParams.hEvtParent || g_ProcParams.hEvtChild)
{
SUPR3HARDENED_ASSERT(g_fSupEarlyProcessInit);
g_ProcParams.enmRequest = kSupR3WinChildReq_CloseEvents;
NtSetEvent(g_ProcParams.hEvtParent, NULL);
NtClose(g_ProcParams.hEvtParent);
NtClose(g_ProcParams.hEvtChild);
g_ProcParams.hEvtParent = NULL;
g_ProcParams.hEvtChild = NULL;
}
else
SUPR3HARDENED_ASSERT(!g_fSupEarlyProcessInit);
/*
* After having resolved imports we patch the LdrInitializeThunk code so
* that it's more difficult to invade our privacy by CreateRemoteThread.
* We'll re-enable this after opening the driver or temporarily while respawning.
*/
supR3HardenedWinDisableThreadCreation();
/*
* Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
* SUPHardenedVerfiyImage-win.cpp.)
*/
supR3HardenedWinInitVersion();
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERSION_INITIALIZED;
/*
* Convert the arguments to UTF-8 and open the log file if specified.
* This must be done as early as possible since the code below may fail.
*/
PUNICODE_STRING pCmdLineStr = &NtCurrentPeb()->ProcessParameters->CommandLine;
int cArgs;
char **papszArgs = suplibCommandLineToArgvWStub(pCmdLineStr->Buffer, pCmdLineStr->Length / sizeof(WCHAR), &cArgs);
supR3HardenedOpenLog(&cArgs, papszArgs);
/*
* Log information about important system files.
*/
supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\ntdll.dll", false /* fAdversarial */);
supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\kernel32.dll", false /* fAdversarial */);
supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\KernelBase.dll", false /* fAdversarial */);
supR3HardenedLogFileInfo(L"\\SystemRoot\\System32\\apisetschema.dll", false /* fAdversarial */);
/*
* Scan the system for adversaries, logging information about them.
*/
g_fSupAdversaries = supR3HardenedWinFindAdversaries();
/*
* Get the executable name.
*/
DWORD cwcExecName = GetModuleFileNameW(GetModuleHandleW(NULL), g_wszSupLibHardenedExePath,
RT_ELEMENTS(g_wszSupLibHardenedExePath));
if (cwcExecName >= RT_ELEMENTS(g_wszSupLibHardenedExePath))
supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, VERR_BUFFER_OVERFLOW,
"The executable path is too long.");
/* The NT version. */
HANDLE hFile = CreateFileW(g_wszSupLibHardenedExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromWin32(RtlGetLastWin32Error()),
"Error opening the executable: %u (%ls).", RtlGetLastWin32Error());
RT_ZERO(g_SupLibHardenedExeNtPath);
ULONG cbIgn;
NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &g_SupLibHardenedExeNtPath,
sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbIgn);
if (!NT_SUCCESS(rcNt))
supR3HardenedFatalMsg("suplibHardenedWindowsMain", kSupInitOp_Integrity, RTErrConvertFromNtStatus(rcNt),
"NtQueryObject -> %#x (on %ls)\n", rcNt, g_wszSupLibHardenedExePath);
NtClose(hFile);
/* The NT executable name offset / dir path length. */
g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
while ( g_offSupLibHardenedExeNtName > 1
&& g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
g_offSupLibHardenedExeNtName--;
/*
* If we've done early init already, register the DLL load notification
* callback and reinstall the NtDll patches.
*/
if (g_fSupEarlyProcessInit)
{
supR3HardenedWinRegisterDllNotificationCallback();
supR3HardenedWinReInstallHooks(false /*fFirstCall */);
}
/*
* Call the C/C++ main function.
*/
SUP_DPRINTF(("Calling main()\n"));
rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
/*
* Exit the process (never return).
*/
SUP_DPRINTF(("Terminating the normal way: rcExit=%d\n", rcExit));
suplibHardenedExit(rcExit);
}
/**
* Reports an error to the parent process via the process parameter structure.
*
* @param pszWhere Where this error occured, if fatal message. NULL
* if not message.
* @param enmWhat Which init operation went wrong if fatal
* message. kSupInitOp_Invalid if not message.
* @param rc The status code to report.
* @param pszFormat The format string.
* @param va The format arguments.
*/
DECLHIDDEN(void) supR3HardenedWinReportErrorToParent(const char *pszWhere, SUPINITOP enmWhat, int rc,
const char *pszFormat, va_list va)
{
if (pszWhere)
RTStrCopy(g_ProcParams.szWhere, sizeof(g_ProcParams.szWhere), pszWhere);
else
g_ProcParams.szWhere[0] = '\0';
RTStrPrintfV(g_ProcParams.szErrorMsg, sizeof(g_ProcParams.szErrorMsg), pszFormat, va);
g_ProcParams.enmWhat = enmWhat;
g_ProcParams.rc = RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR_2 : rc;
g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
NtClearEvent(g_ProcParams.hEvtChild);
NTSTATUS rcNt = NtSetEvent(g_ProcParams.hEvtParent, NULL);
if (NT_SUCCESS(rcNt))
{
LARGE_INTEGER Timeout;
Timeout.QuadPart = -300000000; /* 30 second */
NTSTATUS rcNt = NtWaitForSingleObject(g_ProcParams.hEvtChild, FALSE /*Alertable*/, &Timeout);
}
}
/**
* Routine called by the supR3HardenedEarlyProcessInitThunk assembly routine
* when LdrInitializeThunk is executed in during process initialization.
*
* This initializes the Stub and VM processes, hooking NTDLL APIs and opening
* the device driver before any other DLLs gets loaded into the process. This
* greately reduces and controls the trusted code base of the process compared
* to opening the driver from SUPR3HardenedMain. It also avoids issues with so
* call protection software that is in the habit of patching half of the ntdll
* and kernel32 APIs in the process, making it almost indistinguishable from
* software that is up to no good. Once we've opened vboxdrv, the process
* should be locked down so thighly that only kernel software and csrss can mess
* with the process.
*/
DECLASM(uintptr_t) supR3HardenedEarlyProcessInit(void)
{
/*
* When the first thread gets here we wait for the parent to continue with
* the process purifications. The primary thread must execute for image
* load notifications to trigger, at least in more recent windows versions.
* The old trick of starting a different thread that terminates immediately
* thus doesn't work.
*
* We are not allowed to modify any data at this point because it will be
* reset by the child process purification the parent does when we stop. To
* sabotage thread creation during purification, and to avoid unnecessary
* work for the parent, we reset g_ProcParams before signalling the parent
* here.
*/
if (g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
{
NtTerminateThread(0, 0);
return 0x22; /* crash */
}
/* Retrieve the data we need. */
uintptr_t uNtDllAddr = ASMAtomicXchgPtrT(&g_ProcParams.uNtDllAddr, 0, uintptr_t);
if (!RT_VALID_PTR(uNtDllAddr))
{
NtTerminateThread(0, 0);
return 0x23; /* crash */
}
HANDLE hEvtChild = g_ProcParams.hEvtChild;
HANDLE hEvtParent = g_ProcParams.hEvtParent;
if ( hEvtChild == NULL
|| hEvtChild == RTNT_INVALID_HANDLE_VALUE
|| hEvtParent == NULL
|| hEvtParent == RTNT_INVALID_HANDLE_VALUE)
{
NtTerminateThread(0, 0);
return 0x24; /* crash */
}
/* Resolve the APIs we need. */
PFNNTWAITFORSINGLEOBJECT pfnNtWaitForSingleObject;
PFNNTSETEVENT pfnNtSetEvent;
supR3HardenedWinGetVeryEarlyImports(uNtDllAddr, &pfnNtWaitForSingleObject, &pfnNtSetEvent);
/* Signal the parent that we're ready for purification. */
RT_ZERO(g_ProcParams);
g_ProcParams.enmRequest = kSupR3WinChildReq_PurifyChildAndCloseHandles;
NTSTATUS rcNt = pfnNtSetEvent(hEvtParent, NULL);
if (rcNt != STATUS_SUCCESS)
return 0x33; /* crash */
/* Wait up to 2 mins for the parent to exorcise evil. */
LARGE_INTEGER Timeout;
Timeout.QuadPart = -1200000000; /* 120 second */
rcNt = pfnNtWaitForSingleObject(hEvtChild, FALSE /*Alertable*/, &Timeout);
if (rcNt != STATUS_SUCCESS)
return 0x34; /* crash */
/*
* We're good to go, work global state and restore process parameters.
* Note that we will not restore uNtDllAddr since that is our first defence
* against unwanted threads (see above).
*/
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_INIT_CALLED;
g_fSupEarlyProcessInit = true;
g_ProcParams.hEvtChild = hEvtChild;
g_ProcParams.hEvtParent = hEvtParent;
g_ProcParams.enmRequest = kSupR3WinChildReq_Error;
g_ProcParams.rc = VINF_SUCCESS;
/*
* Initialize the NTDLL imports that we consider usable before the
* process has been initialized.
*/
supR3HardenedWinInitImportsEarly(uNtDllAddr);
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_IMPORTS_RESOLVED;
/*
* Init g_uNtVerCombined as well as we can at this point.
*/
supR3HardenedWinInitVersion();
/*
* Convert the arguments to UTF-8 so we can open the log file if specified.
* We may have to normalize the pointer on older windows version (not w7/64 +).
* Note! This leaks memory at present.
*/
PRTL_USER_PROCESS_PARAMETERS pUserProcParams = NtCurrentPeb()->ProcessParameters;
UNICODE_STRING CmdLineStr = pUserProcParams->CommandLine;
if ( CmdLineStr.Buffer != NULL
&& !(pUserProcParams->Flags & RTL_USER_PROCESS_PARAMS_FLAG_NORMALIZED) )
CmdLineStr.Buffer = (WCHAR *)((uintptr_t)CmdLineStr.Buffer + (uintptr_t)pUserProcParams);
int cArgs;
char **papszArgs = suplibCommandLineToArgvWStub(CmdLineStr.Buffer, CmdLineStr.Length / sizeof(WCHAR), &cArgs);
supR3HardenedOpenLog(&cArgs, papszArgs);
SUP_DPRINTF(("supR3HardenedVmProcessInit: uNtDllAddr=%p\n", uNtDllAddr));
/*
* Set up the direct system calls so we can more easily hook NtCreateSection.
*/
supR3HardenedWinInitSyscalls(true /*fReportErrors*/);
/*
* Determine the executable path and name. Will NOT determine the windows style
* executable path here as we don't need it.
*/
SIZE_T cbActual = 0;
rcNt = NtQueryVirtualMemory(NtCurrentProcess(), &g_ProcParams, MemorySectionName, &g_SupLibHardenedExeNtPath,
sizeof(g_SupLibHardenedExeNtPath) - sizeof(WCHAR), &cbActual);
if ( !NT_SUCCESS(rcNt)
|| g_SupLibHardenedExeNtPath.UniStr.Length == 0
|| g_SupLibHardenedExeNtPath.UniStr.Length & 1)
supR3HardenedFatal("NtQueryVirtualMemory/MemorySectionName failed in supR3HardenedVmProcessInit: %#x\n", rcNt);
/* The NT executable name offset / dir path length. */
g_offSupLibHardenedExeNtName = g_SupLibHardenedExeNtPath.UniStr.Length / sizeof(WCHAR);
while ( g_offSupLibHardenedExeNtName > 1
&& g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] != '\\' )
g_offSupLibHardenedExeNtName--;
/*
* Initialize the image verification stuff (hooks LdrLoadDll and NtCreateSection).
*/
supR3HardenedWinInit(0, false /*fAvastKludge*/);
/*
* Open the driver.
*/
if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_1_ARG0) == 0)
{
SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv stub...\n"));
supR3HardenedWinOpenStubDevice();
}
else if (cArgs >= 1 && suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_2_ARG0) == 0)
{
SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv...\n"));
supR3HardenedMainOpenDevice();
}
else
supR3HardenedFatal("Unexpected first argument '%s'!\n", papszArgs[0]);
g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_DEVICE_OPENED;
/*
* Reinstall the NtDll patches since there is a slight possibility that
* someone undid them while we where busy opening the device.
*/
supR3HardenedWinReInstallHooks(false /*fFirstCall */);
/*
* Restore the LdrInitializeThunk code so we can initialize the process
* normally when we return.
*/
SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrInitializeThunk...\n"));
PSUPHNTLDRCACHEENTRY pLdrEntry;
int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
if (RT_FAILURE(rc))
supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
uint8_t *pbBits;
rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
if (RT_FAILURE(rc))
supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
RTLDRADDR uValue;
rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
if (RT_FAILURE(rc))
supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - uNtDllAddr), 16);
SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrInitializeThunk...\n"));
return (uintptr_t)pvLdrInitThunk;
}