summaryrefslogtreecommitdiff
path: root/src/VBox/HostDrivers/Support
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/Support')
-rw-r--r--src/VBox/HostDrivers/Support/Makefile.kmk276
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrv.c477
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrvInternal.h92
-rw-r--r--src/VBox/HostDrivers/Support/SUPLib.cpp4
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibInternal.h52
-rw-r--r--src/VBox/HostDrivers/Support/SUPLibLdr.cpp2
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp421
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp205
-rw-r--r--src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp112
-rw-r--r--src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp19
-rw-r--r--src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c19
-rw-r--r--src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c19
-rw-r--r--src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp19
-rw-r--r--src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c19
-rw-r--r--src/VBox/HostDrivers/Support/testcase/Makefile.kmk2
-rw-r--r--src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp80
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.cerbin0 -> 1801 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.tafbin0 -> 708 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.tafbin0 -> 400 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.tafbin0 -> 629 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.tafbin0 -> 432 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.tafbin0 -> 675 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.tafbin0 -> 718 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.tafbin0 -> 718 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.tafbin0 -> 332 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.cerbin0 -> 1419 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.tafbin0 -> 508 bytes
-rw-r--r--src/VBox/HostDrivers/Support/win/NtCreateSection-template-amd64-syscall-type-1.h78
-rw-r--r--src/VBox/HostDrivers/Support/win/NtCreateSection-template-x86-syscall-type-1.h286
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp2711
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm75
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h149
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp1873
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp1196
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPLib-win.cpp133
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp1650
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm76
-rw-r--r--src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp191
-rw-r--r--src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp92
40 files changed, 9731 insertions, 597 deletions
diff --git a/src/VBox/HostDrivers/Support/Makefile.kmk b/src/VBox/HostDrivers/Support/Makefile.kmk
index c336e51bfcb..0486cbbce19 100644
--- a/src/VBox/HostDrivers/Support/Makefile.kmk
+++ b/src/VBox/HostDrivers/Support/Makefile.kmk
@@ -30,7 +30,11 @@ include $(KBUILD_PATH)/subheader.kmk
#
# Targets
#
-LIBRARIES += SUPR3 SUPR3Static SUPR3HardenedStatic
+LIBRARIES += SUPR3 SUPR3Static
+ifdef VBOX_WITH_HARDENING
+ LIBRARIES += SUPR3HardenedStatic
+endif
+DLLS.win += VBoxSupLib
ifdef VBOX_WITH_32_ON_64_MAIN_API
LIBRARIES += SUPR3-x86
endif
@@ -69,11 +73,70 @@ if !defined(VBOX_ONLY_DOCS) \
endif
endif # !VBOX_ONLY_DOCS && !VBOX_ONLY_EXTPACKS && !VBOX_ONLY_VALIDATIONKIT
+#
+# Authenticode related trust anchors and certificates -> .cpp
+#
+VBOX_SUP_WIN_CERTS_FILE = $(SUPR3_0_OUTDIR)/TrustAnchorsAndCerts.cpp
+VBOX_SUP_WIN_CERTS := \
+ SpcRootMicrosoft0=SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf \
+ SpcRootMicrosoft1=SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf \
+ SpcRootMicrosoft2=SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf \
+ SpcRootMicrosoft3=SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf \
+ SpcRootMicrosoft4=SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf \
+ SpcRootMicrosoft5=SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf \
+ NtRootMicrosoft6=NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf \
+ TimeRootMicrosoft0=Timestamp-CopyrightC1997MicrosoftCorp-01.taf \
+ TrustedCertVBox0=Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.taf
+VBOX_SUP_WIN_CERT_NAMES := $(foreach cert,$(VBOX_SUP_WIN_CERTS),$(firstword $(subst =,$(SPACE) ,$(cert))))
+VBOX_PATH_SUPR3_CERTIFICATES := $(PATH_SUB_CURRENT)/win/Certificates
+
+# 1=name, 2=filter, 3=buildcert?.
+if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_HARDENING)
+ VBOX_SUP_GEN_CERT_MACRO = 'SUPTAENTRY const g_aSUP$(1)TAs[] =' '{' \
+ $(if-expr "$(3)" == "",,' SUPTAENTRY_GEN(g_abSUPBuildCert),') \
+ $(foreach certnm,$(filter $(2),$(VBOX_SUP_WIN_CERT_NAMES)), ' SUPTAENTRY_GEN(g_abSUP$(certnm)),') \
+ '};' 'unsigned const g_cSUP$(1)TAs = RT_ELEMENTS(g_aSUP$(1)TAs);' '' ''
+else
+ VBOX_SUP_GEN_CERT_MACRO = 'SUPTAENTRY const g_aSUP$(1)TAs[] =' '{' \
+ $(foreach certnm,$(filter $(2),$(VBOX_SUP_WIN_CERT_NAMES)), ' SUPTAENTRY_GEN(g_abSUP$(certnm)),') \
+ '};' 'unsigned const g_cSUP$(1)TAs = RT_ELEMENTS(g_aSUP$(1)TAs);' '' ''
+endif
+
+$$(VBOX_SUP_WIN_CERTS_FILE): $(MAKEFILE_CURRENT) \
+ $(foreach cert,$(VBOX_SUP_WIN_CERTS),$(VBOX_PATH_SUPR3_CERTIFICATES)/$(lastword $(subst =,$(SPACE) ,$(cert)))) \
+ $(VBOX_BIN2C) \
+ $(if-expr "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_HARDENING),$(VBOX_RTSIGNTOOL)) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@ $@.cer
+ $(QUIET)$(APPEND) -n "$@" \
+ '' \
+ '#include <VBox/sup.h>' \
+ ''
+ $(foreach cert,$(VBOX_SUP_WIN_CERTS), $(NLTAB)$(VBOX_BIN2C) -ascii --append \
+ "SUP$(firstword $(subst =,$(SP) ,$(cert)))" \
+ "$(VBOX_PATH_SUPR3_CERTIFICATES)/$(lastword $(subst =,$(SP) ,$(cert)))" \
+ "$@")
+# The build certificate.
+if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_HARDENING)
+ $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --exe $(VBOX_RTSIGNTOOL) --output "$@.cer" --der
+ $(VBOX_BIN2C) -ascii --append SUPBuildCert "$@.cer" $@
+ $(QUIET)$(RM) -f -- $@.cer
+endif
+# Generate certificate lists.
+ $(QUIET)$(APPEND) -n "$@" '' \
+ $(call VBOX_SUP_GEN_CERT_MACRO,All,%,build) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,SpcRoot,SpcRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,NtKernelRoot,NtRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,Timestamp,TimeRoot%) \
+ $(call VBOX_SUP_GEN_CERT_MACRO,Trusted,TrustedCert%,build)
+
+tst: $(VBOX_SUP_WIN_CERTS_FILE)
+
#
# The Ring-3 Support Library (this is linked into the IPRT dll, VBoxRT).
#
-SUPR3_TEMPLATE = VBOXR3NP
+SUPR3_TEMPLATE = VBOXR3NP
SUPR3_DEFS = \
IN_SUP_R3 IN_RT_R3 \
$(if $(VBOX_WITH_SUPSVC),VBOX_WITH_SUPSVC) \
@@ -87,32 +150,216 @@ SUPR3_SOURCES = \
SUPLibTracerA.asm \
SUPR3HardenedIPRT.cpp \
SUPR3HardenedVerify.cpp \
- $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp
+ $(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+ifdef VBOX_WITH_HARDENING
+ SUPR3_SOURCES.win = \
+ win/SUPHardenedVerifyImage-win.cpp
+endif
SUPR3-x86_TEMPLATE = VBoxR3Dll-x86
SUPR3-x86_EXTENDS = SUPR3
#
-# Static version of SUPR3.
+# Static version of SUPR3. This is more of a stub than anything else in a
+# hardened build, at least on windows.
#
SUPR3Static_TEMPLATE = VBOXR3STATIC
SUPR3Static_EXTENDS = SUPR3
+SUPR3Static_DEFS = $(SUPR3_DEFS) IN_SUP_R3_STATIC
+SUPR3Static_SOURCES.win = $(filter-out win/SUPHardenedVerifyImage-win.cpp, $(SUPR3_SOURCES.win))
+
#
# The static part of the hardened support library (ring-3).
#
+VBOX_PATH_RUNTIME_SRC ?= $(PATH_ROOT)/src/VBox/Runtime
SUPR3HardenedStatic_TEMPLATE = VBOXR3HARDENEDLIB
SUPR3HardenedStatic_DEFS = IN_SUP_HARDENED_R3
SUPR3HardenedStatic_DEFS += \
$(if $(VBOX_WITH_SUPSVC),VBOX_WITH_SUPSVC,) \
$(if $(VBOX_WITH_MAIN),VBOX_WITH_MAIN,) \
- $(if $(VBOX_WITH_RAW_MODE),VBOX_WITH_RAW_MODE,)
+ $(if $(VBOX_WITH_RAW_MODE),VBOX_WITH_RAW_MODE,) \
+ $(if $(VBOX_WITHOUT_DEBUGGER_CHECKS),VBOX_WITHOUT_DEBUGGER_CHECKS,)
+ifdef VBOX_WITH_VISTA_NO_SP
+ SUPR3HardenedStatic_DEFS.win += VBOX_WITH_VISTA_NO_SP
+endif
SUPR3HardenedStatic_INCS = .
SUPR3HardenedStatic_SOURCES = \
SUPR3HardenedMain.cpp \
SUPR3HardenedVerify.cpp \
+ SUPR3HardenedNoCrt.cpp \
$(KBUILD_TARGET)/SUPLib-$(KBUILD_TARGET).cpp
+SUPR3HardenedStatic_SOURCES.win = \
+ win/SUPR3HardenedMain-win.cpp \
+ win/SUPR3HardenedMainA-win.asm \
+ win/SUPHardenedVerifyProcess-win.cpp \
+ win/SUPHardenedVerifyImage-win.cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+
+if1of ($(KBUILD_TARGET),win) ## @todo some of this move up.
+ SUPR3HardenedStatic_SDKS += VBoxOpenSslHardened
+ SUPR3HardenedStatic_DEFS += IN_RT IN_RT_R3 IN_RT_STATIC IPRT_NO_CRT RT_WITH_NOCRT_ALIASES LOG_DISABLED IPRT_NO_ERROR_DATA
+ SUPR3HardenedStatic_DEFS.win += LDR_ONLY_PE __STRALIGN_H_
+
+ SUPR3HardenedStatic_INCS += $(PATH_ROOT)/include/iprt/nocrt $(VBOX_PATH_RUNTIME_SRC)/include
+
+ SUPR3HardenedStatic_SOURCES += \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldr.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrEx.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrPE.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-basics.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-cursor.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-default-allocator.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-dump.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-encode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-bitstring.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-bitstring-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-boolean.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-boolean-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-core-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-dyntype.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-dyntype-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-integer.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-integer-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-null.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-null-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-objid.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-objid-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-octetstring.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-octetstring-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-string.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-string-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-time.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-ut-time-decode.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/digest-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/digest-builtin.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkcs7-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-builtin.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-signature-rsa.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/pkix-util.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/rsa-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/spc-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-certpaths.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/x509-verify.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/store.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/store-inmem.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-asn1-decoder.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-core.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-init.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/crypto/taf-sanity.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md2.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md5.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha1.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha256.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha512.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md2str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/md5str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha1str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha256str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/checksum/sha512str.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/err/errinfo.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathChangeToUnixSlashes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/path/RTPathExt.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrPrintHexBytes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16PrintHexBytes.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16ICmpAscii.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strstrip.cpp \
+ \
+ $(VBOX_PATH_RUNTIME_SRC)/common/err/errmsg.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/math/bignum.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg1Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2Weak.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/misc/RTAssertMsg2WeakV.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memchr.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memcmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memmove.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/mempcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/memset.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCat.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCmp.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrCopy.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNCmp.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNLen.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strchr.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strcmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strcpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformat.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformatrt.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strformattype.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/stringalloc.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strlen.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strncmp.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strncpy.asm \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strprintf.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/strtonum.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-16.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-8.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/utf-8-case.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/unidata-upper.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/string/unidata-lower.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/common/time/time.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/RTAssertShouldPanic-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/memsafer-generic.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/generic/uuid-generic.cpp \
+
+
+ SUPR3HardenedStatic_SOURCES.win += \
+ win/SUPR3HardenedNoCrt-win.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/nt/RTErrConvertFromNtStatus.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/win/RTErrConvertFromWin32.cpp \
+ $(VBOX_PATH_RUNTIME_SRC)/win/errmsgwin.cpp
+
+ # Add necessary compiler specific files from libcmt.lib and the lib dir.
+ ifeq ($(KBUILD_TARGET),win)
+ SUPR3HardenedStatic_VBOX_LIBC_OBJS = chkstk.obj
+ ifeq ($(KBUILD_TARGET_ARCH),x86)
+ SUPR3HardenedStatic_VBOX_LIBC_OBJS += alloca16.obj
+ endif
+ SUPR3HardenedStatic_SOURCES.win += $(addprefix $(SUPR3HardenedStatic_0_OUTDIR)/,$(SUPR3HardenedStatic_VBOX_LIBC_OBJS))
+
+ $(addprefix $$(SUPR3HardenedStatic_0_OUTDIR)/,$(SUPR3HardenedStatic_VBOX_LIBC_OBJS)): \
+ $$(PATH_TOOL_$(TEMPLATE_VBOXR3EXE_TOOL.win.$(KBUILD_TARGET_ARCH))_LIB)/libcmt.lib | $$(dir $$@)
+ $(TOOL_$(TEMPLATE_VBOXR3HARDENEDEXE_TOOL.win.$(KBUILD_TARGET_ARCH))_AR) "$<" \
+ /EXTRACT:`$(TOOL_$(TEMPLATE_VBOXR3HARDENEDEXE_TOOL.win.$(KBUILD_TARGET_ARCH))_AR) "$<" /LIST \
+ | $(SED_EXT) -n -e '/$(notdir $@)/p'` \
+ "/OUT:$@"
+ endif
+endif
+
+
+#
+# VBoxSupLib - Windows DLL for catching thread creation and termination.
+#
+VBoxSupLib_TEMPLATE = $(if "$(KBUILD_TARGET)" == "win",VBOXR3HARDENEDLIB,VBOXR3)
+VBoxSupLib_LDFLAGS.win.amd64 = -Entry:DllMainEntrypoint
+VBoxSupLib_LDFLAGS.win.x86 = -Entry:DllMainEntrypoint
+VBoxSupLib_DEFS = \
+ $(if $(VBOX_WITHOUT_DEBUGGER_CHECKS),VBOX_WITHOUT_DEBUGGER_CHECKS,)
+VBoxSupLib_SOURCES = \
+ $(KBUILD_TARGET)/VBoxSupLib-$(KBUILD_TARGET).cpp
+VBoxSupLib_LIBS.win = \
+ $(SUPR3HardenedStatic_1_INS_TARGET)
+
#
# VBoxSupSvc - The system wide service/daemon.
@@ -262,11 +509,17 @@ if1of ($(KBUILD_TARGET), darwin linux solaris win)
VBoxDrv_INST.darwin = $(INST_VBOXDRV)Contents/MacOS/
VBoxDrv_DEBUG_INST.darwin= $(patsubst %/,%,$(INST_VBOXDRV))
VBoxDrv_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK)INCS
+ ifdef VBOX_WITH_HARDENING
+ VBoxDrv_SDKS.win += VBoxOpenSslHardened
+ endif
VBoxDrv_DEFS := IN_RT_R0 IN_SUP_R0 SUPDRV_WITH_RELEASE_LOGGER VBOX_SVN_REV=$(VBOX_SVN_REV)
ifdef VBOX_WITH_DTRACE_R0DRV
VBoxDrv_DEFS += VBOX_WITH_DTRACE VBOX_WITH_DTRACE_R0DRV
endif
+ ifdef VBOX_WITHOUT_DEBUGGER_CHECKS
+ VBoxDrv_DEFS += VBOX_WITHOUT_DEBUGGER_CHECKS
+ endif
#VBoxDrv_DEFS.debug += DEBUG_DARWIN_GIP
VBoxDrv_DEFS.darwin := VBOX_WITH_HOST_VMX
VBoxDrv_DEFS.linux := \
@@ -283,6 +536,9 @@ if1of ($(KBUILD_TARGET), darwin linux solaris win)
ifdef VBOX_WITHOUT_NATIVE_R0_LOADER
VBoxDrv_DEFS.win += VBOX_WITHOUT_NATIVE_R0_LOADER
endif
+ ifdef VBOX_WITH_VISTA_NO_SP
+ VBoxDrv_DEFS.win += VBOX_WITH_VISTA_NO_SP
+ endif
VBoxDrv_INCS = . $(VBoxDrv_0_OUTDIR)
VBoxDrv_INCS.darwin = ./darwin
@@ -291,9 +547,9 @@ if1of ($(KBUILD_TARGET), darwin linux solaris win)
VBoxDrv_LIBS = $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
VBoxDrv_LIBS.linux.debug = $(VBoxDrv_LIBS) $(VBOX_GCC_LIBGCC)
VBoxDrv_LIBS.win = \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
$(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
- $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
- $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib
#VBoxDrv_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded
VBoxDrv_LDFLAGS.solaris += -N misc/ctf
@@ -313,6 +569,12 @@ if1of ($(KBUILD_TARGET), darwin linux solaris win)
win/SUPDrv-win.cpp \
win/SUPDrvA-win.asm \
win/VBoxDrv.rc
+ ifdef VBOX_WITH_HARDENING
+ VBoxDrv_SOURCES.win += \
+ win/SUPHardenedVerifyImage-win.cpp \
+ win/SUPHardenedVerifyProcess-win.cpp \
+ $(VBOX_SUP_WIN_CERTS_FILE)
+ endif
VBoxDrv_SOURCES = \
SUPDrv.d \
SUPDrv.c \
diff --git a/src/VBox/HostDrivers/Support/SUPDrv.c b/src/VBox/HostDrivers/Support/SUPDrv.c
index 0be4002ceef..b2f81bab1fe 100644
--- a/src/VBox/HostDrivers/Support/SUPDrv.c
+++ b/src/VBox/HostDrivers/Support/SUPDrv.c
@@ -463,124 +463,130 @@ int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt, size_t cbSession)
/*
* Initialize it.
*/
- memset(pDevExt, 0, sizeof(*pDevExt));
+ memset(pDevExt, 0, sizeof(*pDevExt)); /* Does not wipe OS specific tail section of the structure. */
+ pDevExt->Spinlock = NIL_RTSPINLOCK;
+ pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
rc = RTSpinlockCreate(&pDevExt->Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvDevExt");
if (RT_SUCCESS(rc))
- {
rc = RTSpinlockCreate(&pDevExt->hGipSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvGip");
+ if (RT_SUCCESS(rc))
+ rc = RTSpinlockCreate(&pDevExt->hSessionHashTabSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "SUPDrvSession");
+
+ if (RT_SUCCESS(rc))
+#ifdef SUPDRV_USE_MUTEX_FOR_LDR
+ rc = RTSemMutexCreate(&pDevExt->mtxLdr);
+#else
+ rc = RTSemFastMutexCreate(&pDevExt->mtxLdr);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemFastMutexCreate(&pDevExt->mtxComponentFactory);
if (RT_SUCCESS(rc))
{
#ifdef SUPDRV_USE_MUTEX_FOR_LDR
- rc = RTSemMutexCreate(&pDevExt->mtxLdr);
+ rc = RTSemMutexCreate(&pDevExt->mtxGip);
#else
- rc = RTSemFastMutexCreate(&pDevExt->mtxLdr);
+ rc = RTSemFastMutexCreate(&pDevExt->mtxGip);
#endif
if (RT_SUCCESS(rc))
{
- rc = RTSemFastMutexCreate(&pDevExt->mtxComponentFactory);
+ rc = supdrvGipCreate(pDevExt);
if (RT_SUCCESS(rc))
{
-#ifdef SUPDRV_USE_MUTEX_FOR_LDR
- rc = RTSemMutexCreate(&pDevExt->mtxGip);
-#else
- rc = RTSemFastMutexCreate(&pDevExt->mtxGip);
-#endif
+ rc = supdrvTracerInit(pDevExt);
if (RT_SUCCESS(rc))
{
- rc = supdrvGipCreate(pDevExt);
- if (RT_SUCCESS(rc))
- {
- rc = supdrvTracerInit(pDevExt);
- if (RT_SUCCESS(rc))
- {
- pDevExt->pLdrInitImage = NULL;
- pDevExt->hLdrInitThread = NIL_RTNATIVETHREAD;
- pDevExt->u32Cookie = BIRD; /** @todo make this random? */
- pDevExt->cbSession = (uint32_t)cbSession;
-
- /*
- * Fixup the absolute symbols.
- *
- * Because of the table indexing assumptions we'll have a little #ifdef orgy
- * here rather than distributing this to OS specific files. At least for now.
- */
+ pDevExt->pLdrInitImage = NULL;
+ pDevExt->hLdrInitThread = NIL_RTNATIVETHREAD;
+ pDevExt->u32Cookie = BIRD; /** @todo make this random? */
+ pDevExt->cbSession = (uint32_t)cbSession;
+
+ /*
+ * Fixup the absolute symbols.
+ *
+ * Because of the table indexing assumptions we'll have a little #ifdef orgy
+ * here rather than distributing this to OS specific files. At least for now.
+ */
#ifdef RT_OS_DARWIN
# if ARCH_BITS == 32
- if (SUPR0GetPagingMode() >= SUPPAGINGMODE_AMD64)
- {
- g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
- g_aFunctions[1].pfn = (void *)0x80; /* SUPR0Abs64bitKernelCS - KERNEL64_CS, seg.h */
- g_aFunctions[2].pfn = (void *)0x88; /* SUPR0Abs64bitKernelSS - KERNEL64_SS, seg.h */
- g_aFunctions[3].pfn = (void *)0x88; /* SUPR0Abs64bitKernelDS - KERNEL64_SS, seg.h */
- }
- else
- g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0;
- g_aFunctions[4].pfn = (void *)0x08; /* SUPR0AbsKernelCS - KERNEL_CS, seg.h */
- g_aFunctions[5].pfn = (void *)0x10; /* SUPR0AbsKernelSS - KERNEL_DS, seg.h */
- g_aFunctions[6].pfn = (void *)0x10; /* SUPR0AbsKernelDS - KERNEL_DS, seg.h */
- g_aFunctions[7].pfn = (void *)0x10; /* SUPR0AbsKernelES - KERNEL_DS, seg.h */
- g_aFunctions[8].pfn = (void *)0x10; /* SUPR0AbsKernelFS - KERNEL_DS, seg.h */
- g_aFunctions[9].pfn = (void *)0x48; /* SUPR0AbsKernelGS - CPU_DATA_GS, seg.h */
+ if (SUPR0GetPagingMode() >= SUPPAGINGMODE_AMD64)
+ {
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)0x80; /* SUPR0Abs64bitKernelCS - KERNEL64_CS, seg.h */
+ g_aFunctions[2].pfn = (void *)0x88; /* SUPR0Abs64bitKernelSS - KERNEL64_SS, seg.h */
+ g_aFunctions[3].pfn = (void *)0x88; /* SUPR0Abs64bitKernelDS - KERNEL64_SS, seg.h */
+ }
+ else
+ g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0;
+ g_aFunctions[4].pfn = (void *)0x08; /* SUPR0AbsKernelCS - KERNEL_CS, seg.h */
+ g_aFunctions[5].pfn = (void *)0x10; /* SUPR0AbsKernelSS - KERNEL_DS, seg.h */
+ g_aFunctions[6].pfn = (void *)0x10; /* SUPR0AbsKernelDS - KERNEL_DS, seg.h */
+ g_aFunctions[7].pfn = (void *)0x10; /* SUPR0AbsKernelES - KERNEL_DS, seg.h */
+ g_aFunctions[8].pfn = (void *)0x10; /* SUPR0AbsKernelFS - KERNEL_DS, seg.h */
+ g_aFunctions[9].pfn = (void *)0x48; /* SUPR0AbsKernelGS - CPU_DATA_GS, seg.h */
# else /* 64-bit darwin: */
- g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
- g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
- g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
- g_aFunctions[3].pfn = (void *)0; /* SUPR0Abs64bitKernelDS */
- g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
- g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
- g_aFunctions[6].pfn = (void *)0; /* SUPR0AbsKernelDS */
- g_aFunctions[7].pfn = (void *)0; /* SUPR0AbsKernelES */
- g_aFunctions[8].pfn = (void *)0; /* SUPR0AbsKernelFS */
- g_aFunctions[9].pfn = (void *)0; /* SUPR0AbsKernelGS */
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
+ g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
+ g_aFunctions[3].pfn = (void *)0; /* SUPR0Abs64bitKernelDS */
+ g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
+ g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
+ g_aFunctions[6].pfn = (void *)0; /* SUPR0AbsKernelDS */
+ g_aFunctions[7].pfn = (void *)0; /* SUPR0AbsKernelES */
+ g_aFunctions[8].pfn = (void *)0; /* SUPR0AbsKernelFS */
+ g_aFunctions[9].pfn = (void *)0; /* SUPR0AbsKernelGS */
# endif
#else /* !RT_OS_DARWIN */
# if ARCH_BITS == 64
- g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
- g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
- g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
- g_aFunctions[3].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0Abs64bitKernelDS */
+ g_aFunctions[0].pfn = (void *)1; /* SUPR0AbsIs64bit */
+ g_aFunctions[1].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0Abs64bitKernelCS */
+ g_aFunctions[2].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0Abs64bitKernelSS */
+ g_aFunctions[3].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0Abs64bitKernelDS */
# else
- g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0;
+ g_aFunctions[0].pfn = g_aFunctions[1].pfn = g_aFunctions[2].pfn = g_aFunctions[4].pfn = (void *)0;
# endif
- g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
- g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
- g_aFunctions[6].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0AbsKernelDS */
- g_aFunctions[7].pfn = (void *)(uintptr_t)ASMGetES(); /* SUPR0AbsKernelES */
- g_aFunctions[8].pfn = (void *)(uintptr_t)ASMGetFS(); /* SUPR0AbsKernelFS */
- g_aFunctions[9].pfn = (void *)(uintptr_t)ASMGetGS(); /* SUPR0AbsKernelGS */
+ g_aFunctions[4].pfn = (void *)(uintptr_t)ASMGetCS(); /* SUPR0AbsKernelCS */
+ g_aFunctions[5].pfn = (void *)(uintptr_t)ASMGetSS(); /* SUPR0AbsKernelSS */
+ g_aFunctions[6].pfn = (void *)(uintptr_t)ASMGetDS(); /* SUPR0AbsKernelDS */
+ g_aFunctions[7].pfn = (void *)(uintptr_t)ASMGetES(); /* SUPR0AbsKernelES */
+ g_aFunctions[8].pfn = (void *)(uintptr_t)ASMGetFS(); /* SUPR0AbsKernelFS */
+ g_aFunctions[9].pfn = (void *)(uintptr_t)ASMGetGS(); /* SUPR0AbsKernelGS */
#endif /* !RT_OS_DARWIN */
- return VINF_SUCCESS;
- }
+ return VINF_SUCCESS;
+ }
- supdrvGipDestroy(pDevExt);
- }
+ supdrvGipDestroy(pDevExt);
+ }
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
- RTSemMutexDestroy(pDevExt->mtxGip);
- pDevExt->mtxGip = NIL_RTSEMMUTEX;
+ RTSemMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMMUTEX;
#else
- RTSemFastMutexDestroy(pDevExt->mtxGip);
- pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
+ RTSemFastMutexDestroy(pDevExt->mtxGip);
+ pDevExt->mtxGip = NIL_RTSEMFASTMUTEX;
#endif
- }
- RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
- pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
- }
+ }
+ RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
+ pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
+ }
#ifdef SUPDRV_USE_MUTEX_FOR_LDR
- RTSemMutexDestroy(pDevExt->mtxLdr);
- pDevExt->mtxLdr = NIL_RTSEMMUTEX;
+ RTSemMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMMUTEX;
#else
- RTSemFastMutexDestroy(pDevExt->mtxLdr);
- pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
+ RTSemFastMutexDestroy(pDevExt->mtxLdr);
+ pDevExt->mtxLdr = NIL_RTSEMFASTMUTEX;
#endif
- }
- RTSpinlockDestroy(pDevExt->hGipSpinlock);
- pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
- }
- RTSpinlockDestroy(pDevExt->Spinlock);
- pDevExt->Spinlock = NIL_RTSPINLOCK;
}
+
+ RTSpinlockDestroy(pDevExt->Spinlock);
+ pDevExt->Spinlock = NIL_RTSPINLOCK;
+ RTSpinlockDestroy(pDevExt->hGipSpinlock);
+ pDevExt->hGipSpinlock = NIL_RTSPINLOCK;
+ RTSpinlockDestroy(pDevExt->hSessionHashTabSpinlock);
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
+
#ifdef SUPDRV_WITH_RELEASE_LOGGER
RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
RTLogDestroy(RTLogSetDefaultInstance(NULL));
@@ -621,6 +627,8 @@ void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt)
pDevExt->Spinlock = NIL_RTSPINLOCK;
RTSemFastMutexDestroy(pDevExt->mtxComponentFactory);
pDevExt->mtxComponentFactory = NIL_RTSEMFASTMUTEX;
+ RTSpinlockDestroy(pDevExt->hSessionHashTabSpinlock);
+ pDevExt->hSessionHashTabSpinlock = NIL_RTSPINLOCK;
/*
* Free lists.
@@ -699,15 +707,10 @@ int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrest
pSession->pDevExt = pDevExt;
pSession->u32Cookie = BIRD_INV;
pSession->fUnrestricted = fUnrestricted;
+ /*pSession->fInHashTable = false; */
pSession->cRefs = 1;
- /*pSession->pLdrUsage = NULL;
- pSession->pVM = NULL;
- pSession->pUsage = NULL;
- pSession->pGip = NULL;
- pSession->fGipReferenced = false;
- pSession->Bundle.cUsed = 0; */
- pSession->Uid = NIL_RTUID;
- pSession->Gid = NIL_RTGID;
+ /*pSession->pCommonNextHash = NULL;
+ pSession->ppOsSessionPtr = NULL; */
if (fUser)
{
pSession->Process = RTProcSelf();
@@ -718,6 +721,14 @@ int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrest
pSession->Process = NIL_RTPROCESS;
pSession->R0Process = NIL_RTR0PROCESS;
}
+ /*pSession->pLdrUsage = NULL;
+ pSession->pVM = NULL;
+ pSession->pUsage = NULL;
+ pSession->pGip = NULL;
+ pSession->fGipReferenced = false;
+ pSession->Bundle.cUsed = 0; */
+ pSession->Uid = NIL_RTUID;
+ pSession->Gid = NIL_RTGID;
/*pSession->uTracerData = 0;*/
pSession->hTracerCaller = NIL_RTNATIVETHREAD;
RTListInit(&pSession->TpProviders);
@@ -745,14 +756,16 @@ int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrest
/**
- * Shared code for cleaning up a session (but not quite freeing it).
+ * Cleans up the session in the context of the process to which it belongs, the
+ * caller will free the session and the session spinlock.
*
- * This is primarily intended for MAC OS X where we have to clean up the memory
- * stuff before the file handle is closed.
+ * This should normally occur when the session is closed or as the process
+ * exits. Careful reference counting in the OS specfic code makes sure that
+ * there cannot be any races between process/handle cleanup callbacks and
+ * threads doing I/O control calls.
*
- * @param pDevExt Device extension.
+ * @param pDevExt The device extension.
* @param pSession Session data.
- * This data will be freed by this routine.
*/
static void supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
{
@@ -760,6 +773,11 @@ static void supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
PSUPDRVBUNDLE pBundle;
LogFlow(("supdrvCleanupSession: pSession=%p\n", pSession));
+ Assert(!pSession->fInHashTable);
+ Assert(!pSession->ppOsSessionPtr);
+ AssertReleaseMsg(pSession->R0Process == RTR0ProcHandleSelf() || pSession->R0Process == NIL_RTR0PROCESS,
+ ("R0Process=%p cur=%p; Process=%u curpid=%u\n", RTR0ProcHandleSelf(), RTProcSelf()));
+
/*
* Remove logger instances related to this session.
*/
@@ -963,13 +981,13 @@ static void supdrvCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
/**
- * Shared code for cleaning up a session.
+ * Common code for freeing a session when the reference count reaches zero.
*
* @param pDevExt Device extension.
* @param pSession Session data.
* This data will be freed by this routine.
*/
-static void supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+static void supdrvDestroySession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
{
VBOXDRV_SESSION_CLOSE(pSession);
@@ -977,6 +995,7 @@ static void supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
* Cleanup the session first.
*/
supdrvCleanupSession(pDevExt, pSession);
+ supdrvOSCleanupSession(pDevExt, pSession);
/*
* Free the rest of the session stuff.
@@ -985,7 +1004,261 @@ static void supdrvCloseSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
pSession->Spinlock = NIL_RTSPINLOCK;
pSession->pDevExt = NULL;
RTMemFree(pSession);
- LogFlow(("supdrvCloseSession: returns\n"));
+ LogFlow(("supdrvDestroySession: returns\n"));
+}
+
+
+/**
+ * Inserts the session into the global hash table.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_WRONG_ORDER if the session was already inserted (asserted).
+ * @retval VERR_INVALID_PARAMETER if the session handle is invalid or a ring-0
+ * session (asserted).
+ * @retval VERR_DUPLICATE if there is already a session for that pid.
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The session.
+ * @param ppOsSessionPtr Pointer to the OS session pointer, if any is
+ * available and used. This will set to point to the
+ * session while under the protection of the session
+ * hash table spinlock. It will also be kept in
+ * PSUPDRVSESSION::ppOsSessionPtr for lookup and
+ * cleanup use.
+ * @param pvUser Argument for supdrvOSSessionHashTabInserted.
+ */
+int VBOXCALL supdrvSessionHashTabInsert(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVSESSION *ppOsSessionPtr,
+ void *pvUser)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process != NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(pSession->Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * If there are a collisions, we need to carefully check if we got a
+ * duplicate. There can only be one open session per process.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ if (pCur)
+ {
+ while (pCur && pCur->Process != pSession->Process)
+ pCur = pCur->pCommonNextHash;
+
+ if (pCur)
+ {
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ if (pCur == pSession)
+ {
+ Assert(pSession->fInHashTable);
+ AssertFailed();
+ return VERR_WRONG_ORDER;
+ }
+ Assert(!pSession->fInHashTable);
+ if (pCur->R0Process == pSession->R0Process)
+ return VERR_RESOURCE_IN_USE;
+ return VERR_DUPLICATE;
+ }
+ }
+ Assert(!pSession->fInHashTable);
+ Assert(!pSession->ppOsSessionPtr);
+
+ /*
+ * Insert it, doing a callout to the OS specific code in case it has
+ * anything it wishes to do while we're holding the spinlock.
+ */
+ pSession->pCommonNextHash = pDevExt->apSessionHashTab[iHash];
+ pDevExt->apSessionHashTab[iHash] = pSession;
+ pSession->fInHashTable = true;
+ ASMAtomicIncS32(&pDevExt->cSessions);
+
+ pSession->ppOsSessionPtr = ppOsSessionPtr;
+ if (ppOsSessionPtr)
+ ASMAtomicWritePtr(ppOsSessionPtr, pSession);
+
+ supdrvOSSessionHashTabInserted(pDevExt, pSession, pvUser);
+
+ /*
+ * Retain a reference for the pointer in the session table.
+ */
+ ASMAtomicIncU32(&pSession->cRefs);
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Removes the session from the global hash table.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_FOUND if the session was already removed (asserted).
+ * @retval VERR_INVALID_PARAMETER if the session handle is invalid or a ring-0
+ * session (asserted).
+ *
+ * @param pDevExt The device extension.
+ * @param pSession The session. The caller is expected to have a reference
+ * to this so it won't croak on us when we release the hash
+ * table reference.
+ * @param pvUser OS specific context value for the
+ * supdrvOSSessionHashTabInserted callback.
+ */
+int VBOXCALL supdrvSessionHashTabRemove(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+ int32_t cRefs;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
+ AssertReturn(pSession->R0Process != NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(pSession->Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * Unlink it.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ if (pCur == pSession)
+ pDevExt->apSessionHashTab[iHash] = pSession->pCommonNextHash;
+ else
+ {
+ PSUPDRVSESSION pPrev = pCur;
+ while (pCur && pCur != pSession)
+ {
+ pPrev = pCur;
+ pCur = pCur->pCommonNextHash;
+ }
+ if (pCur)
+ pPrev->pCommonNextHash = pCur->pCommonNextHash;
+ else
+ {
+ Assert(!pSession->fInHashTable);
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+ return VERR_NOT_FOUND;
+ }
+ }
+
+ pSession->pCommonNextHash = NULL;
+ pSession->fInHashTable = false;
+
+ ASMAtomicDecU32(&pDevExt->cSessions);
+
+ /*
+ * Clear OS specific session pointer if available and do the OS callback.
+ */
+ if (pSession->ppOsSessionPtr)
+ {
+ ASMAtomicCmpXchgPtr(pSession->ppOsSessionPtr, NULL, pSession);
+ pSession->ppOsSessionPtr = NULL;
+ }
+
+ supdrvOSSessionHashTabRemoved(pDevExt, pSession, pvUser);
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * Drop the reference the hash table had to the session. This shouldn't
+ * be the last reference!
+ */
+ cRefs = ASMAtomicDecU32(&pSession->cRefs);
+ Assert(cRefs > 0 && cRefs < _1M);
+ if (cRefs == 0)
+ supdrvDestroySession(pDevExt, pSession);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Looks up the session for the current process in the global hash table or in
+ * OS specific pointer.
+ *
+ * @returns Pointer to the session with a reference that the caller must
+ * release. If no valid session was found, NULL is returned.
+ *
+ * @param pDevExt The device extension.
+ * @param Process The process ID.
+ * @param R0Process The ring-0 process handle.
+ * @param ppOsSessionPtr The OS session pointer if available. If not NULL,
+ * this is used instead of the hash table. For
+ * additional safety it must then be equal to the
+ * SUPDRVSESSION::ppOsSessionPtr member.
+ * This can be NULL even if the OS has a session
+ * pointer.
+ */
+PSUPDRVSESSION VBOXCALL supdrvSessionHashTabLookup(PSUPDRVDEVEXT pDevExt, RTPROCESS Process, RTR0PROCESS R0Process,
+ PSUPDRVSESSION *ppOsSessionPtr)
+{
+ PSUPDRVSESSION pCur;
+ unsigned iHash;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(R0Process != NIL_RTR0PROCESS, NULL);
+
+ /*
+ * Calculate the hash table index and acquire the spinlock.
+ */
+ iHash = SUPDRV_SESSION_HASH(Process);
+
+ RTSpinlockAcquire(pDevExt->hSessionHashTabSpinlock);
+
+ /*
+ * If an OS session pointer is provided, always use it.
+ */
+ if (ppOsSessionPtr)
+ {
+ pCur = *ppOsSessionPtr;
+ if ( pCur
+ && ( pCur->ppOsSessionPtr != ppOsSessionPtr
+ || pCur->Process != Process
+ || pCur->R0Process != R0Process) )
+ pCur = NULL;
+ }
+ else
+ {
+ /*
+ * Otherwise, do the hash table lookup.
+ */
+ pCur = pDevExt->apSessionHashTab[iHash];
+ while ( pCur
+ && ( pCur->Process != Process
+ || pCur->R0Process != R0Process) )
+ pCur = pCur->pCommonNextHash;
+ }
+
+ /*
+ * Retain the session.
+ */
+ if (pCur)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ Assert(cRefs > 1 && cRefs < _1M);
+ }
+
+ RTSpinlockRelease(pDevExt->hSessionHashTabSpinlock);
+
+ return pCur;
}
@@ -1022,7 +1295,7 @@ uint32_t VBOXCALL supdrvSessionRelease(PSUPDRVSESSION pSession)
cRefs = ASMAtomicDecU32(&pSession->cRefs);
AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pSession));
if (cRefs == 0)
- supdrvCloseSession(pSession->pDevExt, pSession);
+ supdrvDestroySession(pSession->pDevExt, pSession);
return cRefs;
}
diff --git a/src/VBox/HostDrivers/Support/SUPDrvInternal.h b/src/VBox/HostDrivers/Support/SUPDrvInternal.h
index 20cd0512c38..8a22c62a552 100644
--- a/src/VBox/HostDrivers/Support/SUPDrvInternal.h
+++ b/src/VBox/HostDrivers/Support/SUPDrvInternal.h
@@ -58,7 +58,7 @@
# define _interlockedbittestandset64 _interlockedbittestandset64_StupidDDKVsCompilerCrap
# define _interlockedbittestandreset64 _interlockedbittestandreset64_StupidDDKVsCompilerCrap
# pragma warning(disable : 4163)
-# include <ntddk.h>
+# include <iprt/nt/nt.h>
# pragma warning(default : 4163)
# undef _InterlockedExchange
# undef _InterlockedExchangeAdd
@@ -69,7 +69,7 @@
# undef _interlockedbittestandset64
# undef _interlockedbittestandreset64
# else
-# include <ntddk.h>
+# include <iprt/nt/nt.h>
# endif
# include <memory.h>
# define memcmp(a,b,c) mymemcmp(a,b,c)
@@ -177,6 +177,13 @@
*/
#define OSDBGPRINT(a) SUPR0Printf a
+/** Debug printf macro shared with the ring-3 part. */
+#ifdef DEBUG_bird
+# define SUP_DPRINTF(a) SUPR0Printf a
+#else
+# define SUP_DPRINTF(a) do { } while (0)
+#endif
+
/** @name Context values for the per-session handle tables.
* The context value is used to distinguish between the different kinds of
@@ -434,8 +441,24 @@ typedef struct SUPDRVSESSION
uint32_t u32Cookie;
/** Set if is an unrestricted session, clear if restricted. */
bool fUnrestricted;
- /* Reference counter. */
+
+ /** Set if we're in the hash table, clear if not. Protected by the hash
+ * table spinlock. */
+ bool fInHashTable;
+ /** Reference counter. */
uint32_t volatile cRefs;
+ /** Pointer to the next session with the same hash (common hash table).
+ * Protected by the hash table spinlock. */
+ PSUPDRVSESSION pCommonNextHash;
+ /** Pointer to the OS specific session pointer, if available and in use.
+ * This is atomically set and cleared as the session is inserted and removed
+ * from the hash table (protected by the session hash table spinlock). */
+ PSUPDRVSESSION *ppOsSessionPtr;
+ /** The process (id) of the session. */
+ RTPROCESS Process;
+ /** Which process this session is associated with.
+ * This is NIL_RTR0PROCESS for kernel sessions and valid for user ones. */
+ RTR0PROCESS R0Process;
/** The VM associated with the session. */
PVM pVM;
@@ -445,7 +468,9 @@ typedef struct SUPDRVSESSION
/** Load usage records. (protected by SUPDRVDEVEXT::mtxLdr) */
PSUPDRVLDRUSAGE volatile pLdrUsage;
- /** Spinlock protecting the bundles and the GIP members. */
+ /** Spinlock protecting the bundles, the GIP members and the
+ * fProcessCleanupDone flag. It continues to be valid until the last
+ * reference to the session is released. */
RTSPINLOCK Spinlock;
/** The ring-3 mapping of the GIP (readonly). */
RTR0MEMOBJ GipMapObjR3;
@@ -460,11 +485,6 @@ typedef struct SUPDRVSESSION
RTUID Uid;
/** The group id of the session. (Set by the OS part.) */
RTGID Gid;
- /** The process (id) of the session. */
- RTPROCESS Process;
- /** Which process this session is associated with.
- * This is NIL_RTR0PROCESS for kernel sessions and valid for user ones. */
- RTR0PROCESS R0Process;
/** Per session tracer specfic data. */
uintptr_t uTracerData;
/** The thread currently actively talking to the tracer. (One at the time!) */
@@ -497,6 +517,10 @@ typedef struct SUPDRVSESSION
/** Pointer to the next session with the same hash. */
PSUPDRVSESSION pNextHash;
# endif
+# if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
+ /** Pointer to the process protection structure for this session. */
+ struct SUPDRVNTPROTECT *pNtProtect;
+# endif
#endif /* !SUPDRV_AGNOSTIC */
} SUPDRVSESSION;
@@ -612,8 +636,21 @@ typedef struct SUPDRVDEVEXT
/** Hash table for user tracer modules (SUPDRVVTGCOPY). */
RTLISTANCHOR aTrackerUmodHash[128];
+ /** @name Session Handle Table.
+ * @{ */
+ /** Spinlock protecting apSessionHashTab, cSessions,
+ * SUPDRVSESSION::ppOsSessionPtr, SUPDRVSESSION::pCommonNextHash, and possibly
+ * others depending on the OS. */
+ RTSPINLOCK hSessionHashTabSpinlock;
+ /** Session hash table hash table. The size of this table must make sense in
+ * comparison to GVMM_MAX_HANDLES. */
+ PSUPDRVSESSION apSessionHashTab[HC_ARCH_BITS == 64 ? 8191 : 127];
+ /** The number of open sessions. */
+ int32_t cSessions;
+ /** @} */
+
/*
- * Note! The non-agnostic bits must be a the very end of the structure!
+ * Note! The non-agnostic bits must be at the very end of the structure!
*/
#ifndef SUPDRV_AGNOSTIC
# ifdef RT_OS_WINDOWS
@@ -625,12 +662,43 @@ typedef struct SUPDRVDEVEXT
#endif
} SUPDRVDEVEXT;
+/** Calculates the index into g_apSessionHashTab.*/
+#define SUPDRV_SESSION_HASH(a_pid) ( (a_pid) % RT_ELEMENTS(((SUPDRVDEVEXT *)NULL)->apSessionHashTab) )
+
RT_C_DECLS_BEGIN
/*******************************************************************************
* OS Specific Functions *
*******************************************************************************/
+/**
+ * Called to clean up the session structure before it's freed.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that's being cleaned up.
+ */
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession);
+
+/**
+ * Called to let the OS specfic code perform additional insertion work while
+ * still under the protection of the hash table spinlock.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that was inserted.
+ * @param pvUser User context specified to the insert call.
+ */
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+
+/**
+ * Called to let the OS specfic code perform additional removal work while still
+ * under the protection of the hash table spinlock.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that was removed.
+ * @param pvUser User context specified to the remove call.
+ */
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+
void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession);
bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc);
bool VBOXCALL supdrvOSGetForcedAsyncTscMode(PSUPDRVDEVEXT pDevExt);
@@ -762,6 +830,10 @@ int VBOXCALL supdrvIDC(uintptr_t uIOCtl, PSUPDRVDEVEXT pDevExt, PSUPDRVSESSIO
int VBOXCALL supdrvInitDevExt(PSUPDRVDEVEXT pDevExt, size_t cbSession);
void VBOXCALL supdrvDeleteDevExt(PSUPDRVDEVEXT pDevExt);
int VBOXCALL supdrvCreateSession(PSUPDRVDEVEXT pDevExt, bool fUser, bool fUnrestricted, PSUPDRVSESSION *ppSession);
+int VBOXCALL supdrvSessionHashTabInsert(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVSESSION *ppOsSessionPtr, void *pvUser);
+int VBOXCALL supdrvSessionHashTabRemove(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
+PSUPDRVSESSION VBOXCALL supdrvSessionHashTabLookup(PSUPDRVDEVEXT pDevExt, RTPROCESS Process, RTR0PROCESS R0Process,
+ PSUPDRVSESSION *ppOsSessionPtr);
uint32_t VBOXCALL supdrvSessionRetain(PSUPDRVSESSION pSession);
uint32_t VBOXCALL supdrvSessionRelease(PSUPDRVSESSION pSession);
diff --git a/src/VBox/HostDrivers/Support/SUPLib.cpp b/src/VBox/HostDrivers/Support/SUPLib.cpp
index a9c2b6692a5..2721d072308 100644
--- a/src/VBox/HostDrivers/Support/SUPLib.cpp
+++ b/src/VBox/HostDrivers/Support/SUPLib.cpp
@@ -1541,7 +1541,7 @@ SUPR3DECL(int) SUPR3HardenedVerifySelf(const char *pszArgv0, bool fInternal, PRT
/*
* Verify that the image file and parent directories are sane.
*/
- rc = supR3HardenedVerifyFile(szExecPath, RTHCUINTPTR_MAX, pErrInfo);
+ rc = supR3HardenedVerifyFile(szExecPath, RTHCUINTPTR_MAX, false /*fMaybe3rdParty*/, pErrInfo);
if (RT_FAILURE(rc))
return rc;
#endif
@@ -1585,7 +1585,7 @@ SUPR3DECL(int) SUPR3HardenedVerifyPlugIn(const char *pszFilename, PRTERRINFO pEr
* Only do the actual check in hardened builds.
*/
#ifdef VBOX_WITH_HARDENING
- int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, pErrInfo);
+ int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /*fMaybe3rdParty*/, pErrInfo);
if (RT_FAILURE(rc) && !RTErrInfoIsSet(pErrInfo))
LogRel(("supR3HardenedVerifyFile: Verification of \"%s\" failed, rc=%Rrc\n", pszFilename, rc));
return rc;
diff --git a/src/VBox/HostDrivers/Support/SUPLibInternal.h b/src/VBox/HostDrivers/Support/SUPLibInternal.h
index e1e05512746..bda35003c0b 100644
--- a/src/VBox/HostDrivers/Support/SUPLibInternal.h
+++ b/src/VBox/HostDrivers/Support/SUPLibInternal.h
@@ -103,7 +103,10 @@
* @{
*/
#if defined(IN_SUP_HARDENED_R3) && defined(RT_OS_WINDOWS)
+# define SUP_HARDENED_NEED_CRT_FUNCTIONS
+DECLHIDDEN(int) suplibHardenedMemComp(void const *pvDst, const void *pvSrc, size_t cbToComp);
DECLHIDDEN(void *) suplibHardenedMemCopy(void *pvDst, const void *pvSrc, size_t cbToCopy);
+DECLHIDDEN(void *) suplibHardenedMemSet(void *pvDst, int ch, size_t cbToSet);
DECLHIDDEN(char *) suplibHardenedStrCopy(char *pszDst, const char *pszSrc);
DECLHIDDEN(size_t) suplibHardenedStrLen(const char *psz);
DECLHIDDEN(char *) suplibHardenedStrCat(char *pszDst, const char *pszSrc);
@@ -111,7 +114,10 @@ DECLHIDDEN(int) suplibHardenedStrCmp(const char *psz1, const char *psz2);
DECLHIDDEN(int) suplibHardenedStrNCmp(const char *psz1, const char *psz2, size_t cchMax);
DECLHIDDEN(int) suplibHardenedStrICmp(const char *psz1, const char *psz2);
#else
+# undef SUP_HARDENED_NEED_CRT_FUNCTIONS
+# define suplibHardenedMemComp memcmp
# define suplibHardenedMemCopy memcpy
+# define suplibHardenedMemSet memset
# define suplibHardenedStrCopy strcpy
# define suplibHardenedStrLen strlen
# define suplibHardenedStrCat strcat
@@ -119,9 +125,23 @@ DECLHIDDEN(int) suplibHardenedStrICmp(const char *psz1, const char *psz2);
# define suplibHardenedStrNCmp strncmp
# define suplibHardenedStrICmp stricmp
#endif
-DECLNORETURN(void) suplibHardenedExit(RTEXITCODE rcExit);
+DECLNORETURN(void) suplibHardenedExit(RTEXITCODE rcExit);
+DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...);
+DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va);
+
/** @} */
+/** Debug output macro. */
+#ifdef DEBUG_bird
+# ifdef IN_SUP_HARDENED_R3
+# define SUP_DPRINTF(a) suplibHardenedPrintF a
+# else
+# define SUP_DPRINTF(a) RTLogPrintf a
+# endif
+#else
+# define SUP_DPRINTF(a) do { } while (0)
+#endif
+
/*******************************************************************************
* Structures and Typedefs *
@@ -134,6 +154,7 @@ typedef enum SUPINSTFILETYPE
kSupIFT_Invalid = 0,
kSupIFT_Exe,
kSupIFT_Dll,
+ kSupIFT_Rc,
kSupIFT_Sys,
kSupIFT_Script,
kSupIFT_Data,
@@ -275,6 +296,17 @@ typedef DECLCALLBACK(int) FNSUPR3PREINIT(PSUPPREINITDATA pPreInitData, uint32_t
/** Pointer to supR3PreInit. */
typedef FNSUPR3PREINIT *PFNSUPR3PREINIT;
+/** The current SUPR3HardenedMain state / location. */
+typedef enum SUPR3HARDENEDMAINSTATE
+{
+ SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED = 0,
+ SUPR3HARDENEDMAINSTATE_VERIFY_TRUST_READY,
+ SUPR3HARDENEDMAINSTATE_INIT_RUNTIME,
+ SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN,
+ SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN,
+ SUPR3HARDENEDMAINSTATE_END
+} SUPR3HARDENEDMAINSTATE;
+
/*******************************************************************************
* Global Variables *
@@ -287,6 +319,7 @@ extern DECLHIDDEN(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPageR0;
#ifdef ___SUPDrvIOC_h___
extern DECLHIDDEN(PSUPQUERYFUNCS) g_pSupFunctions;
#endif
+extern DECLHIDDEN(SUPR3HARDENEDMAINSTATE) g_enmSupR3HardenedMainState;
/*******************************************************************************
@@ -359,14 +392,29 @@ DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszForma
* Display an error which may or may not be fatal.
*/
DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...);
+
DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, bool fLeaveFilesOpen, const char *pszProgName);
DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal);
DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFatal);
DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo);
-DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile, bool fMaybe3rdParty,
+ PRTERRINFO pErrInfo);
DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData);
DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData);
+#ifdef RT_OS_WINDOWS
+DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags);
+DECLHIDDEN(void) supR3HardenedWinInitVersion(void);
+DECLHIDDEN(void) supR3HardenedWinVerifyProcess(void);
+DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(void);
+DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int cArgs, char **papszArgs);
+DECLHIDDEN(int) supR3HardenedWinReSpawn(void);
+DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only);
+extern RTUTF16 g_wszSupLibHardenedExePath[1024];
+# ifdef RTPATH_MAX
+extern char g_szSupLibHardenedExePath[RTPATH_MAX];
+# endif
+#endif
SUPR3DECL(int) supR3PageLock(void *pvStart, size_t cPages, PSUPPAGE paPages);
SUPR3DECL(int) supR3PageUnlock(void *pvStart);
diff --git a/src/VBox/HostDrivers/Support/SUPLibLdr.cpp b/src/VBox/HostDrivers/Support/SUPLibLdr.cpp
index 9c9afa685f6..69bfbb0559e 100644
--- a/src/VBox/HostDrivers/Support/SUPLibLdr.cpp
+++ b/src/VBox/HostDrivers/Support/SUPLibLdr.cpp
@@ -780,7 +780,7 @@ SUPR3DECL(int) SUPR3HardenedLdrLoadPlugIn(const char *pszFilename, PRTLDRMOD phL
/*
* Verify the image file.
*/
- int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, pErrInfo);
+ int rc = supR3HardenedVerifyFile(pszFilename, RTHCUINTPTR_MAX, true /*fMaybe3rdParty*/, pErrInfo);
if (RT_FAILURE(rc))
{
if (!RTErrInfoIsSet(pErrInfo))
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
index 151eed4e719..06a499e9229 100644
--- a/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2013 Oracle Corporation
+ * Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -120,25 +120,31 @@ typedef FNRTR3INITEX *PFNRTR3INITEX;
* Global Variables *
*******************************************************************************/
/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
-static SUPPREINITDATA g_SupPreInitData;
+static SUPPREINITDATA g_SupPreInitData;
/** The program executable path. */
-static char g_szSupLibHardenedExePath[RTPATH_MAX];
+#ifndef RT_OS_WINDOWS
+static
+#endif
+char g_szSupLibHardenedExePath[RTPATH_MAX];
/** The program directory path. */
-static char g_szSupLibHardenedDirPath[RTPATH_MAX];
+static char g_szSupLibHardenedDirPath[RTPATH_MAX];
/** The program name. */
-static const char *g_pszSupLibHardenedProgName;
+static const char *g_pszSupLibHardenedProgName;
#ifdef SUP_HARDENED_SUID
/** The real UID at startup. */
-static uid_t g_uid;
+static uid_t g_uid;
/** The real GID at startup. */
-static gid_t g_gid;
+static gid_t g_gid;
# ifdef RT_OS_LINUX
-static uint32_t g_uCaps;
+static uint32_t g_uCaps;
# endif
#endif
+/** The current SUPR3HardenedMain state / location. */
+SUPR3HARDENEDMAINSTATE g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED;
+
/*******************************************************************************
* Internal Functions *
@@ -149,129 +155,6 @@ static void supR3HardenedMainDropPrivileges(void);
static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
-#ifdef RT_OS_WINDOWS
-/*
- * No CRT here, thank you.
- */
-
-/** memcpy */
-DECLHIDDEN(void *) suplibHardenedMemCopy(void *pvDst, const void *pvSrc, size_t cbToCopy)
-{
- size_t *puDst = (size_t *)pvDst;
- size_t const *puSrc = (size_t const *)pvSrc;
- while (cbToCopy >= sizeof(size_t))
- {
- *puDst++ = *puSrc++;
- cbToCopy -= sizeof(size_t);
- }
-
- uint8_t *pbDst = (uint8_t *)puDst;
- uint8_t const *pbSrc = (uint8_t const *)puSrc;
- while (cbToCopy > 0)
- {
- *pbDst++ = *pbSrc++;
- cbToCopy--;
- }
-
- return pvDst;
-}
-
-
-/** strcpy */
-DECLHIDDEN(char *) suplibHardenedStrCopy(char *pszDst, const char *pszSrc)
-{
- char *pszRet = pszDst;
- char ch;
- do
- {
- ch = *pszSrc++;
- *pszDst++ = ch;
- } while (ch);
- return pszRet;
-}
-
-
-/** strlen */
-DECLHIDDEN(size_t) suplibHardenedStrLen(const char *psz)
-{
- const char *pszStart = psz;
- while (*psz)
- psz++;
- return psz - pszStart;
-}
-
-
-/** strcat */
-DECLHIDDEN(char *) suplibHardenedStrCat(char *pszDst, const char *pszSrc)
-{
- char *pszRet = pszDst;
- while (*pszDst)
- pszDst++;
- suplibHardenedStrCopy(pszDst, pszSrc);
- return pszRet;
-}
-
-
-# ifdef RT_OS_WINDOWS
-/** stricmp */
-DECLHIDDEN(int) suplibHardenedStrICmp(const char *psz1, const char *psz2)
-{
- const char *pszOrg1 = psz1;
- const char *pszOrg2 = psz2;
-
- for (;;)
- {
- char ch1 = *psz1++;
- char ch2 = *psz1++;
- if (ch1 != ch2)
- {
- int rc = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pszOrg1, -1, pszOrg2, -1);
-# ifdef VBOX_STRICT
- if (rc == 0)
- __debugbreak();
-# endif
- return rc - 2;
- }
- if (ch1 == 0)
- return 0;
- }
-}
-# endif
-
-
-/** strcmp */
-DECLHIDDEN(int) suplibHardenedStrCmp(const char *psz1, const char *psz2)
-{
- for (;;)
- {
- char ch1 = *psz1++;
- char ch2 = *psz1++;
- if (ch1 != ch2)
- return ch1 < ch2 ? -1 : 1;
- if (ch1 == 0)
- return 0;
- }
-}
-
-
-/** strncmp */
-DECLHIDDEN(int) suplibHardenedStrNCmp(const char *psz1, const char *psz2, size_t cchMax)
-{
- while (cchMax-- > 0)
- {
- char ch1 = *psz1++;
- char ch2 = *psz1++;
- if (ch1 != ch2)
- return ch1 < ch2 ? -1 : 1;
- if (ch1 == 0)
- break;
- }
- return 0;
-}
-
-#endif /* RT_OS_WINDOWS */
-
-
/**
* Safely copy one or more strings into the given buffer.
*
@@ -422,16 +305,13 @@ static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_
do
{
- unsigned iDigit = uValue & fDigitMask;
+ *psz-- = pchDigits[uValue & fDigitMask];
uValue >>= cShift;
-
- *psz-- = uValue % 10;
- uValue /= 10;
} while (uValue > 0);
if ((fFlags & RTSTR_F_SPECIAL) && uBase == 16)
{
- *psz-- = 'x';
+ *psz-- = !(fFlags & RTSTR_F_CAPITAL) ? 'x' : 'X';
*psz-- = '0';
}
@@ -441,6 +321,83 @@ static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_
/**
+ * Writes a wide character string to standard error.
+ *
+ * @param pwsz The string.
+ */
+static void suplibHardenedPrintWideStr(PCRTUTF16 pwsz)
+{
+ for (;;)
+ {
+ RTUTF16 wc = *pwsz++;
+ if (!wc)
+ return;
+ if ( (wc < 0x7f && wc >= 0x20)
+ || wc == '\n'
+ || wc == '\r')
+ suplibHardenedPrintChr((char)wc);
+ else
+ {
+ suplibHardenedPrintStrN(RT_STR_TUPLE("\\x"));
+ suplibHardenedPrintHexOctal(wc, 16, 0);
+ }
+ }
+}
+
+#ifdef IPRT_NO_CRT
+
+/** Buffer structure used by suplibHardenedOutput. */
+struct SUPLIBHARDENEDOUTPUTBUF
+{
+ size_t off;
+ char szBuf[2048];
+};
+
+/** Callback for RTStrFormatV, see FNRTSTROUTPUT. */
+static DECLCALLBACK(size_t) suplibHardenedOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ SUPLIBHARDENEDOUTPUTBUF *pBuf = (SUPLIBHARDENEDOUTPUTBUF *)pvArg;
+ size_t cbTodo = cbChars;
+ for (;;)
+ {
+ size_t cbSpace = sizeof(pBuf->szBuf) - pBuf->off - 1;
+
+ /* Flush the buffer? */
+ if ( cbSpace == 0
+ || (cbTodo == 0 && pBuf->off))
+ {
+ suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
+# ifdef RT_OS_WINDOWS
+ OutputDebugString(pBuf->szBuf);
+# endif
+ pBuf->off = 0;
+ cbSpace = sizeof(pBuf->szBuf) - 1;
+ }
+
+ /* Copy the string into the buffer. */
+ if (cbTodo == 1)
+ {
+ pBuf->szBuf[pBuf->off++] = *pachChars;
+ break;
+ }
+ if (cbSpace >= cbTodo)
+ {
+ memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbTodo);
+ pBuf->off += cbTodo;
+ break;
+ }
+ memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbSpace);
+ pBuf->off += cbSpace;
+ cbTodo -= cbSpace;
+ }
+ pBuf->szBuf[pBuf->off] = '\0';
+
+ return cbChars;
+}
+
+#endif /* IPRT_NO_CRT */
+
+/**
* Simple printf to standard error.
*
* @param pszFormat The format string.
@@ -448,6 +405,17 @@ static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_
*/
DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
{
+#ifdef IPRT_NO_CRT
+ /*
+ * Use buffered output here to avoid character mixing on the windows
+ * console and to enable us to use OutputDebugString.
+ */
+ SUPLIBHARDENEDOUTPUTBUF Buf;
+ Buf.off = 0;
+ Buf.szBuf[0] = '\0';
+ RTStrFormatV(suplibHardenedOutput, &Buf, NULL, NULL, pszFormat, va);
+
+#else /* !IPRT_NO_CRT */
/*
* Format loop.
*/
@@ -543,13 +511,22 @@ DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
break;
case 's':
- {
- const char *pszStr = va_arg(va, const char *);
- if (!RT_VALID_PTR(pszStr))
- pszStr = "<NULL>";
- suplibHardenedPrintStr(pszStr);
+ if (chArgSize == 'l')
+ {
+ PCRTUTF16 pwszStr = va_arg(va, PCRTUTF16 );
+ if (RT_VALID_PTR(pwszStr))
+ suplibHardenedPrintWideStr(pwszStr);
+ else
+ suplibHardenedPrintStr("<NULL>");
+ }
+ else
+ {
+ const char *pszStr = va_arg(va, const char *);
+ if (!RT_VALID_PTR(pszStr))
+ pszStr = "<NULL>";
+ suplibHardenedPrintStr(pszStr);
+ }
break;
- }
case 'd':
case 'i':
@@ -616,6 +593,20 @@ DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
break;
}
+ case 'R':
+ if (pszFormat[0] == 'r' && pszFormat[1] == 'c')
+ {
+ int iValue = va_arg(va, int);
+ if (iValue < 0)
+ {
+ suplibHardenedPrintChr('-');
+ iValue = -iValue;
+ }
+ suplibHardenedPrintDecimal(iValue);
+ pszFormat += 2;
+ break;
+ }
+ /* fall thru */
/*
* Custom format.
@@ -635,6 +626,7 @@ DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
/* Flush the last bits of the string. */
if (pszLast != pszFormat)
suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
+#endif /* !IPRT_NO_CRT */
}
@@ -653,7 +645,6 @@ DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...)
}
-
/**
* @copydoc RTPathStripFilename.
*/
@@ -862,9 +853,12 @@ static void supR3HardenedGetFullExePath(void)
suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
#elif defined(RT_OS_WINDOWS)
- HMODULE hExe = GetModuleHandle(NULL);
- if (!GetModuleFileName(hExe, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath)))
- supR3HardenedFatal("supR3HardenedExecDir: GetModuleFileName failed, rc=%d\n", GetLastError());
+ int cbRet = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/,
+ g_wszSupLibHardenedExePath, -1,
+ g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath),
+ NULL /*pchDefChar*/, NULL /* pfUsedDefChar */);
+ if (!cbRet)
+ supR3HardenedFatal("supR3HardenedExecDir: WideCharToMultiByte failed, rc=%d\n", GetLastError());
#else
# error needs porting.
#endif
@@ -955,6 +949,7 @@ DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWha
suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
break;
+ case kSupInitOp_Misc:
case kSupInitOp_IPRT:
case kSupInitOp_Integrity:
case kSupInitOp_RootCheck:
@@ -987,9 +982,17 @@ DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWha
if (pid <= 0)
#endif
{
- PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
- if (pfnTrustedError)
- pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
+ static volatile bool s_fRecursive = false; /* Loader hooks may cause recursion. */
+ if (!s_fRecursive)
+ {
+ s_fRecursive = true;
+
+ PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
+ if (pfnTrustedError)
+ pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
+
+ s_fRecursive = false;
+ }
}
/*
@@ -1081,6 +1084,14 @@ static void supR3HardenedMainOpenDevice(void)
case VERR_NO_MEMORY:
supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
"Kernel memory allocation/mapping failed");
+ case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
+ case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
+ supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
default:
supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
"Unknown rc=%d", rc);
@@ -1324,11 +1335,10 @@ static void supR3HardenedMainInitRuntime(uint32_t fFlags)
* Open it and resolve the symbols.
*/
#if defined(RT_OS_WINDOWS)
- /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
- HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
if (!hMod)
supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
- "LoadLibraryEx(\"%s\",,) failed (rc=%d)",
+ "LoadLibrary \"%s\" failed (rc=%d)",
szPath, GetLastError());
PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
if (!pfnRTInitEx)
@@ -1407,8 +1417,7 @@ static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgNa
* Open it and resolve the symbol.
*/
#if defined(RT_OS_WINDOWS)
- /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
- HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
if (!hMod)
return NULL;
FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
@@ -1451,10 +1460,9 @@ static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName
* Open it and resolve the symbol.
*/
#if defined(RT_OS_WINDOWS)
- /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
- HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
+ HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
if (!hMod)
- supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n",
+ supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
szPath, GetLastError());
FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
if (!pfn)
@@ -1535,15 +1543,37 @@ DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int
#endif
/*
- * Validate the installation.
+ * Validate the installation. On Windows we leave the files open so they
+ * cannot be tampered with after they've been verified. We also check
+ * install loader hooks and check the process integrity.
*/
+#ifndef RT_OS_WINDOWS
supR3HardenedVerifyAll(true /* fFatal */, false /* fLeaveFilesOpen */, pszProgName);
+#else
+ supR3HardenedWinInit(fFlags);
+ supR3HardenedVerifyAll(true /* fFatal */, true /* fLeaveFilesOpen */, pszProgName);
+ supR3HardenedWinVerifyProcess();
+#endif
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * On Windows we'll respawn the process with a special vboxdrv arrangement
+ * in place to monitor access to the process for its inception.
+ */
+ if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
+ && supR3HardenedWinIsReSpawnNeeded(argc, argv))
+ return supR3HardenedWinReSpawn();
+#endif
/*
* Open the vboxdrv device.
*/
if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
supR3HardenedMainOpenDevice();
+#ifdef RT_OS_WINDOWS
+ supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation();
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_VERIFY_TRUST_READY;
+#endif
/*
* Open the root service connection.
@@ -1567,85 +1597,16 @@ DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int
* Load the IPRT, hand the SUPLib part the open driver and
* call RTR3InitEx.
*/
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
supR3HardenedMainInitRuntime(fFlags);
/*
* Load the DLL/SO/DYLIB containing the actual program
* and pass control to it.
*/
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName);
+ g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
return pfnTrustedMain(argc, argv, envp);
}
-
-#ifdef RT_OS_WINDOWS
-
-extern "C" int main(int argc, char **argv, char **envp);
-
-/**
- * The executable entry point.
- */
-extern "C" void __stdcall suplibHardenedWindowsMain(void)
-{
- RTEXITCODE rcExit = RTEXITCODE_FAILURE;
-
- /*
- * Convert the arguments to UTF-8.
- */
- int cArgs;
- PWSTR *papwszArgs = CommandLineToArgvW(GetCommandLineW(), &cArgs); /** @todo fix me! */
- if (papwszArgs)
- {
- char **papszArgs = (char **)HeapAlloc(GetProcessHeap(), 0 /* dwFlags*/, (cArgs + 1) * sizeof(const char **));
- if (papszArgs)
- {
- int iArg;
- for (iArg = 0; iArg < cArgs; iArg++)
- {
- int cbNeeded = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, papwszArgs[iArg], -1, NULL /*pszDst*/, 0 /*cbDst*/,
- NULL /*pchDefChar*/, NULL /* pfUsedDefChar */);
- if (!cbNeeded)
- {
- suplibHardenedPrintF("CommandLineToArgvW failed on argument %d: %u\n", iArg, GetLastError());
- break;
- }
-
- papszArgs[iArg] = (char *)HeapAlloc(GetProcessHeap(), 0 /*dwFlags*/, cbNeeded);
- if (!papszArgs[iArg])
- {
- suplibHardenedPrintF("HeapAlloc failed");
- break;
- }
-
- int cbRet = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, papwszArgs[iArg], -1, papszArgs[iArg], cbNeeded,
- NULL /*pchDefChar*/, NULL /* pfUsedDefChar */);
- if (!cbRet)
- {
- suplibHardenedPrintF("CommandLineToArgvW failed on argument %d: %u\n", iArg, GetLastError());
- break;
- }
- }
- if (iArg == cArgs)
- {
- papszArgs[iArg] = NULL;
-
- /*
- * Call the main function.
- */
- rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
- }
- }
- else
- suplibHardenedPrintF("HeapAlloc failed\n");
- }
- else
- suplibHardenedPrintF("CommandLineToArgvW failed\n");
-
- /*
- * Exit the process (never return).
- */
- for (;;)
- ExitProcess(rcExit);
-}
-
-#endif
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp
new file mode 100644
index 00000000000..e3ff7c0f605
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedNoCrt.cpp
@@ -0,0 +1,205 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library - Hardened main() no-crt routines.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#if RT_OS_WINDOWS
+# include <Windows.h>
+#endif
+
+#include <VBox/sup.h>
+
+#include "SUPLibInternal.h"
+
+
+#ifdef SUP_HARDENED_NEED_CRT_FUNCTIONS
+
+/** memcmp */
+DECLHIDDEN(int) suplibHardenedMemComp(void const *pvDst, const void *pvSrc, size_t cbToComp)
+{
+ size_t const *puDst = (size_t const *)pvDst;
+ size_t const *puSrc = (size_t const *)pvSrc;
+ while (cbToComp >= sizeof(size_t))
+ {
+ if (*puDst != *puSrc)
+ break;
+ puDst++;
+ puSrc++;
+ cbToComp -= sizeof(size_t);
+ }
+
+ uint8_t const *pbDst = (uint8_t const *)puDst;
+ uint8_t const *pbSrc = (uint8_t const *)puSrc;
+ while (cbToComp > 0)
+ {
+ if (*pbDst != *pbSrc)
+ {
+ if (*pbDst < *pbSrc)
+ return -1;
+ return 1;
+ }
+
+ pbDst++;
+ pbSrc++;
+ cbToComp--;
+ }
+
+ return 0;
+}
+
+
+/** memcpy */
+DECLHIDDEN(void *) suplibHardenedMemCopy(void *pvDst, const void *pvSrc, size_t cbToCopy)
+{
+ size_t *puDst = (size_t *)pvDst;
+ size_t const *puSrc = (size_t const *)pvSrc;
+ while (cbToCopy >= sizeof(size_t))
+ {
+ *puDst++ = *puSrc++;
+ cbToCopy -= sizeof(size_t);
+ }
+
+ uint8_t *pbDst = (uint8_t *)puDst;
+ uint8_t const *pbSrc = (uint8_t const *)puSrc;
+ while (cbToCopy > 0)
+ {
+ *pbDst++ = *pbSrc++;
+ cbToCopy--;
+ }
+
+ return pvDst;
+}
+
+
+/** memset */
+DECLHIDDEN(void *) suplibHardenedMemSet(void *pvDst, int ch, size_t cbToSet)
+{
+ uint8_t *pbDst = (uint8_t *)pvDst;
+ while (cbToSet > 0)
+ {
+ *pbDst++ = (uint8_t)ch;
+ cbToSet--;
+ }
+
+ return pvDst;
+}
+
+
+/** strcpy */
+DECLHIDDEN(char *) suplibHardenedStrCopy(char *pszDst, const char *pszSrc)
+{
+ char *pszRet = pszDst;
+ char ch;
+ do
+ {
+ ch = *pszSrc++;
+ *pszDst++ = ch;
+ } while (ch);
+ return pszRet;
+}
+
+
+/** strlen */
+DECLHIDDEN(size_t) suplibHardenedStrLen(const char *psz)
+{
+ const char *pszStart = psz;
+ while (*psz)
+ psz++;
+ return psz - pszStart;
+}
+
+
+/** strcat */
+DECLHIDDEN(char *) suplibHardenedStrCat(char *pszDst, const char *pszSrc)
+{
+ char *pszRet = pszDst;
+ while (*pszDst)
+ pszDst++;
+ suplibHardenedStrCopy(pszDst, pszSrc);
+ return pszRet;
+}
+
+
+# ifdef RT_OS_WINDOWS
+/** stricmp */
+DECLHIDDEN(int) suplibHardenedStrICmp(const char *psz1, const char *psz2)
+{
+ const char *pszOrg1 = psz1;
+ const char *pszOrg2 = psz2;
+
+ for (;;)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ {
+ int rc = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pszOrg1, -1, pszOrg2, -1);
+# ifdef VBOX_STRICT
+ if (rc == 0)
+ __debugbreak();
+# endif
+ return rc - 2;
+ }
+ if (ch1 == 0)
+ return 0;
+ }
+}
+# endif
+
+
+/** strcmp */
+DECLHIDDEN(int) suplibHardenedStrCmp(const char *psz1, const char *psz2)
+{
+ for (;;)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ return ch1 < ch2 ? -1 : 1;
+ if (ch1 == 0)
+ return 0;
+ }
+}
+
+
+/** strncmp */
+DECLHIDDEN(int) suplibHardenedStrNCmp(const char *psz1, const char *psz2, size_t cchMax)
+{
+ while (cchMax-- > 0)
+ {
+ char ch1 = *psz1++;
+ char ch2 = *psz2++;
+ if (ch1 != ch2)
+ return ch1 < ch2 ? -1 : 1;
+ if (ch1 == 0)
+ break;
+ }
+ return 0;
+}
+
+#endif /* SUP_HARDENED_NEED_CRT_FUNCTIONS */
+
diff --git a/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
index 74b00c975b8..352c81f4522 100644
--- a/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
+++ b/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
@@ -73,6 +73,10 @@
#include <iprt/string.h>
#include "SUPLibInternal.h"
+#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
+# define SUPHNTVI_NO_NT_STUFF
+# include "win/SUPHardenedVerify-win.h"
+#endif
/*******************************************************************************
@@ -85,6 +89,13 @@
# define dirfd(d) ((d)->d_fd)
#endif
+/** Compare table file names with externally supplied names. */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+# define SUP_COMP_FILENAME suplibHardenedStrICmp
+#else
+# define SUP_COMP_FILENAME suplibHardenedStrCmp
+#endif
+
/*******************************************************************************
* Global Variables *
@@ -106,9 +117,9 @@ static SUPINSTFILE const g_aSupInstallFiles[] =
{ kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDD2R0.r0" },
#ifdef VBOX_WITH_RAW_MODE
- { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMGC.gc" },
- { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDGC.gc" },
- { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDD2GC.gc" },
+ { kSupIFT_Rc, kSupID_AppPrivArch, false, "VMMGC.gc" },
+ { kSupIFT_Rc, kSupID_AppPrivArch, false, "VBoxDDGC.gc" },
+ { kSupIFT_Rc, kSupID_AppPrivArch, false, "VBoxDD2GC.gc" },
#endif
{ kSupIFT_Dll, kSupID_SharedLib, false, "VBoxRT" SUPLIB_DLL_SUFF },
@@ -153,7 +164,7 @@ static SUPINSTFILE const g_aSupInstallFiles[] =
#ifdef VBOX_WITH_MAIN
{ kSupIFT_Exe, kSupID_AppBin, false, "VBoxSVC" SUPLIB_EXE_SUFF },
#ifdef RT_OS_WINDOWS
- { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxC" SUPLIB_DLL_SUFF },
+ { kSupIFT_Dll, kSupID_SharedLib, false, "VBoxC" SUPLIB_DLL_SUFF },
#else
{ kSupIFT_Exe, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_EXE_SUFF },
{ kSupIFT_Dll, kSupID_SharedLib, false, "VBoxXPCOM" SUPLIB_DLL_SUFF },
@@ -346,7 +357,7 @@ DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal)
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
NULL,
- OPEN_ALWAYS,
+ OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hDir != INVALID_HANDLE_VALUE)
@@ -465,17 +476,35 @@ static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFi
rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true /*fWithFilename*/, fFatal);
if (RT_SUCCESS(rc))
{
-#if defined(RT_OS_WINDOWS)
+#if defined(RT_OS_WINDOWS) /** @todo Need to use WCHAR on windows! */
HANDLE hFile = CreateFile(szPath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
- OPEN_ALWAYS,
+ OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
- /** @todo Check the type, and verify the signature (separate function so we can skip it). */
+# if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_R3_STATIC) /* Latter: Not in VBoxCpuReport and friends. */
+
+ char szErr[1024];
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, szErr, sizeof(szErr));
+
+ uint32_t fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
+ if (pFile->enmType == kSupIFT_Rc)
+ fFlags |= SUPHNTVI_F_RC_IMAGE;
+
+ rc = supHardenedWinVerifyImageByHandleNoName(hFile, fFlags, &ErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ rc = supR3HardenedError(rc, fFatal, "supR3HardenedVerifyFileInternal: '%s': Image verify error rc=%Rrc: %s\n",
+ szPath, rc, szErr);
+ CloseHandle(hFile);
+ }
+ else
+#endif
{
/* it's valid. */
if (fLeaveFileOpen)
@@ -490,8 +519,7 @@ static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFi
int err = GetLastError();
if (!pFile->fOptional || err != ERROR_FILE_NOT_FOUND)
rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
- "supR3HardenedVerifyFileInternal: Failed to open \"%s\": err=%d\n",
- szPath, err);
+ "supR3HardenedVerifyFileInternal: Failed to open '%s': err=%d\n", szPath, err);
}
#else /* UNIXY */
int fd = open(szPath, O_RDONLY, 0);
@@ -580,11 +608,7 @@ static int supR3HardenedVerifySameFile(int iFile, const char *pszFilename, bool
int rc = supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal);
if (RT_FAILURE(rc))
return rc;
-#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
- if (suplibHardenedStrICmp(szName, pszFilename))
-#else
- if (suplibHardenedStrCmp(szName, pszFilename))
-#endif
+ if (SUP_COMP_FILENAME(szName, pszFilename))
{
/*
* Normalize the two paths and compare again.
@@ -595,14 +619,14 @@ static int supR3HardenedVerifySameFile(int iFile, const char *pszFilename, bool
char szName2[RTPATH_MAX];
if ( GetFullPathName(szName, RT_ELEMENTS(szName2), &szName2[0], &pszIgnored)
&& GetFullPathName(pszFilename, RT_ELEMENTS(szName), &szName[0], &pszIgnored))
- if (!suplibHardenedStrICmp(szName2, szName))
+ if (!SUP_COMP_FILENAME(szName2, szName))
rc = VINF_SUCCESS;
#else
AssertCompile(RTPATH_MAX >= PATH_MAX);
char szName2[RTPATH_MAX];
if ( realpath(szName, szName2) != NULL
&& realpath(pszFilename, szName) != NULL)
- if (!suplibHardenedStrCmp(szName2, szName))
+ if (!SUP_COMP_FILENAME(szName2, szName))
rc = VINF_SUCCESS;
#endif
@@ -644,7 +668,7 @@ DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFata
*/
const char *pszName = supR3HardenedPathFilename(pszFilename);
for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
- if (!suplibHardenedStrCmp(pszName, g_aSupInstallFiles[iFile].pszFile))
+ if (!SUP_COMP_FILENAME(pszName, g_aSupInstallFiles[iFile].pszFile))
{
int rc = supR3HardenedVerifySameFile(iFile, pszFilename, fFatal);
if (RT_SUCCESS(rc))
@@ -903,7 +927,7 @@ static int supR3HardenedVerifyPathSanity(const char *pszPath, PRTERRINFO pErrInf
* Check that it's an absolute path and copy the volume/root specifier.
*/
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
- if ( RT_C_IS_ALPHA(pszSrc[0])
+ if ( !RT_C_IS_ALPHA(pszSrc[0])
|| pszSrc[1] != ':'
|| !RTPATH_IS_SLASH(pszSrc[2]))
return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
@@ -1441,10 +1465,14 @@ DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive,
* @param hNativeFile Handle to the file, verify that it's the same
* as we ended up with when verifying the path.
* RTHCUINTPTR_MAX means NIL here.
+ * @param fMaybe3rdParty Set if the file is could be a supplied by a
+ * third party. Different validation rules may
+ * apply to 3rd party code on some platforms.
* @param pErrInfo Where to return extended error information.
* Optional.
*/
-DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile, PRTERRINFO pErrInfo)
+DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile,
+ bool fMaybe3rdParty, PRTERRINFO pErrInfo)
{
/*
* Validate the input path and parse it.
@@ -1476,10 +1504,50 @@ DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNa
}
/*
- * Verify the file.
+ * Verify the file handle against the last component, if specified.
*/
if (hNativeFile != RTHCUINTPTR_MAX)
- return supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
+ {
+ rc = supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * The files shall be signed on windows, verify that.
+ */
+ HANDLE hVerify;
+ if (hNativeFile == RTHCUINTPTR_MAX)
+ hVerify = CreateFile(pszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ else if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)hNativeFile, GetCurrentProcess(), &hVerify,
+ GENERIC_READ, false /*bInheritHandle*/, 0 /*dwOptions*/))
+ hVerify = INVALID_HANDLE_VALUE;
+ if (hVerify != INVALID_HANDLE_VALUE)
+ {
+ uint32_t fFlags = SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING;
+ if (!fMaybe3rdParty)
+ fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
+ const char *pszSuffix = RTPathSuffix(pszFilename);
+ if ( pszSuffix
+ && pszSuffix[0] == '.'
+ && ( RT_C_TO_LOWER(pszSuffix[1]) == 'r'
+ || RT_C_TO_LOWER(pszSuffix[1]) == 'g')
+ && RT_C_TO_LOWER(pszSuffix[2]) == 'c'
+ && pszSuffix[3] == '\0' )
+ fFlags |= SUPHNTVI_F_RC_IMAGE;
+# ifndef IN_SUP_R3_STATIC /* Not in VBoxCpuReport and friends. */
+ rc = supHardenedWinVerifyImageByHandleNoName(hVerify, fFlags, pErrInfo);
+# endif
+ CloseHandle(hVerify);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
+ "Error %u trying to open (or duplicate handle for) '%s'", GetLastError(), pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
return VINF_SUCCESS;
}
diff --git a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp
index a8f41c830e8..318d8445b6e 100644
--- a/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp
+++ b/src/VBox/HostDrivers/Support/darwin/SUPDrv-darwin.cpp
@@ -781,6 +781,25 @@ DECLEXPORT(int) VBOXCALL SUPDrvDarwinIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
}
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
/**
* Initializes any OS specific object creator fields.
*/
diff --git a/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c
index f356a4998a5..6288a1eaa6c 100644
--- a/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c
+++ b/src/VBox/HostDrivers/Support/freebsd/SUPDrv-freebsd.c
@@ -495,6 +495,25 @@ int VBOXCALL SUPDrvFreeBSDIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
}
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
{
NOREF(pObj);
diff --git a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c
index 87c361987d2..96b54370330 100644
--- a/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c
+++ b/src/VBox/HostDrivers/Support/linux/SUPDrv-linux.c
@@ -808,6 +808,25 @@ int VBOXCALL SUPDrvLinuxIDC(uint32_t uReq, PSUPDRVIDCREQHDR pReq)
EXPORT_SYMBOL(SUPDrvLinuxIDC);
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
/**
* Initializes any OS specific object creator fields.
*/
diff --git a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
index c8fa3d13d3a..7e07ad634d6 100644
--- a/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
+++ b/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
@@ -363,6 +363,25 @@ DECLASM(int) VBoxDrvIOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *p
}
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
{
NOREF(pObj);
diff --git a/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c
index 7b32f225826..bd0a29fd3ee 100644
--- a/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c
+++ b/src/VBox/HostDrivers/Support/solaris/SUPDrv-solaris.c
@@ -902,6 +902,25 @@ static int VBoxSupDrvErr2SolarisErr(int rc)
}
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+ NOREF(pDevExt);
+ NOREF(pSession);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
/**
* Initializes any OS specific object creator fields.
*/
diff --git a/src/VBox/HostDrivers/Support/testcase/Makefile.kmk b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
index 90ab02dffce..6a8f959a2b7 100644
--- a/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
+++ b/src/VBox/HostDrivers/Support/testcase/Makefile.kmk
@@ -4,7 +4,7 @@
#
#
-# Copyright (C) 2006-2012 Oracle Corporation
+# Copyright (C) 2006-2014 Oracle Corporation
#
# This file is part of VirtualBox Open Source Edition (OSE), as
# available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
index 814c4191832..46d6712d769 100644
--- a/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
+++ b/src/VBox/HostDrivers/Support/testcase/tstNtQueryStuff.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -28,43 +28,7 @@
/*******************************************************************************
* Header Files *
*******************************************************************************/
-#include <ntstatus.h>
-#define WIN32_NO_STATUS
-#include <Windows.h>
-#include <winternl.h>
-
-typedef enum
-{
- MemoryBasicInformation = 0,
- MemoryWorkingSetList,
- MemorySectionName,
- MemoryBasicVlmInformation
-} MEMORY_INFORMATION_CLASS;
-
-typedef struct
-{
- UNICODE_STRING SectionFileName;
- WCHAR NameBuffer[ANYSIZE_ARRAY];
-} MEMORY_SECTION_NAME;
-
-extern "C"
-NTSYSAPI NTSTATUS NTAPI NtQueryVirtualMemory(IN HANDLE hProcess,
- IN LPCVOID pvWhere,
- IN MEMORY_INFORMATION_CLASS MemoryInfo,
- OUT PVOID pvBuf,
- IN SIZE_T cbBuf,
- OUT PSIZE_T pcbReturned OPTIONAL);
-
-#define ProcessDebugPort ((PROCESSINFOCLASS)7 )
-#define ProcessHandleCount ((PROCESSINFOCLASS)20)
-#define ProcessWow64Information ((PROCESSINFOCLASS)26)
-#define ProcessImageFileName ((PROCESSINFOCLASS)27)
-#define ProcessDebugObjectHandle ((PROCESSINFOCLASS)30)
-#define ProcessExecuteFlags ((PROCESSINFOCLASS)34)
-#define ProcessImageFileNameWin32 ((PROCESSINFOCLASS)43)
-#define ProcessImageFileMapping ((PROCESSINFOCLASS)44)
-
-
+#include <iprt/nt/nt-and-windows.h>
#include <iprt/test.h>
#include <iprt/string.h>
@@ -303,22 +267,21 @@ static void tstQueryInformationProcess(void)
RTTESTI_CHECK_MSG(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
if (NT_SUCCESS(rcNt))
RTTestIPrintf(RTTESTLVL_ALWAYS, "BasicInfo:\n"
- " UniqueProcessId = %#x (%6d)\n"
+ " UniqueProcessId = %#x (%6d)\n"
+ " InheritedFromUniqueProcessId = %#x (%6d)\n"
+ " ExitStatus = %#x\n"
" PebBaseAddress = %p\n"
- " Reserved1 = %p ExitStatus?\n"
- " Reserved2a = %p AffinityMask?\n"
- " Reserved2b = %p (%6d) BasePriority?\n"
- " Reserved3 = %p (%6d) InheritedFromUniqueProcessId?\n"
+ " AffinityMask = %#zx\n"
+ " BasePriority = %#zx\n"
,
BasicInfo.UniqueProcessId, BasicInfo.UniqueProcessId,
+ BasicInfo.InheritedFromUniqueProcessId, BasicInfo.InheritedFromUniqueProcessId,
+ BasicInfo.ExitStatus,
BasicInfo.PebBaseAddress,
- BasicInfo.Reserved1,
- BasicInfo.Reserved2[0],
- BasicInfo.Reserved2[1], BasicInfo.Reserved2[1],
- BasicInfo.Reserved3, BasicInfo.Reserved3
+ BasicInfo.AffinityMask,
+ BasicInfo.BasePriority
);
-
/* Debugger present? */
DWORD_PTR uPtr = ~(DWORD_PTR)0;
cbActual = 0;
@@ -434,7 +397,7 @@ static void tstQueryInformationProcess(void)
}
-int main()
+int main(int argc, char **argv)
{
RTEXITCODE rcExit = RTTestInitAndCreate("tstNtQueryStuff", &g_hTest);
if (rcExit != RTEXITCODE_SUCCESS)
@@ -442,10 +405,25 @@ int main()
RTTestBanner(g_hTest);
g_hProcess = GetCurrentProcess();
+ if (argc >= 2 && argv[1][0] != '-')
+ {
+ const char *pszPid = argv[1];
+ uint32_t idPid = RTStrToInt32(pszPid);
- //tstQueryVirtualMemory();
- tstQueryInformationProcess();
+ uint32_t fAccess = PROCESS_QUERY_INFORMATION;
+ if (argc >= 3)
+ fAccess = RTStrToInt32(argv[2]);
+ g_hProcess = OpenProcess(fAccess, FALSE, idPid);
+ if (g_hProcess == NULL)
+ {
+ RTTestIFailed("Error %u opening process %u (%s)\n", GetLastError(), idPid, pszPid);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ }
+
+ tstQueryVirtualMemory();
+ tstQueryInformationProcess();
return RTTestSummaryAndDestroy(g_hTest);
}
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/Makefile.kup b/src/VBox/HostDrivers/Support/win/Certificates/Makefile.kup
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/Makefile.kup
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.cer b/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.cer
new file mode 100644
index 00000000000..32f92899936
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.cer
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf b/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf
new file mode 100644
index 00000000000..486b6fd7ae5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/NtRoot-MicrosoftCodeVerificationRoot-729404101f3e0ca347837fca175a8438.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf
new file mode 100644
index 00000000000..7b46d69b9f5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftAuthenticodeTmRootAuthority-01.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf
new file mode 100644
index 00000000000..e55039abcd5
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftDigitalMediaAuthority2005-6eff330eb6e7569740680870104baaba.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf
new file mode 100644
index 00000000000..46c6250a849
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootAuthority-00c1008b3c3c8811d13ef663ecdf40.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf
new file mode 100644
index 00000000000..3131fc27236
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority-79ad16a14aa0a5ad4c7358f407132e65.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf
new file mode 100644
index 00000000000..945de02c859
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2010-28cc3a25bfba44ac449a9b586b4339aa.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf
new file mode 100644
index 00000000000..610b14f1ff1
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/SpcRoot-MicrosoftRootCertificateAuthority2011-3f8bc8b5fc9fb29643b569d66c42e144.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf b/src/VBox/HostDrivers/Support/win/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf
new file mode 100644
index 00000000000..b9d8f7b8a26
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/Timestamp-CopyrightC1997MicrosoftCorp-01.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.cer b/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.cer
new file mode 100644
index 00000000000..abb6ee6358d
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.cer
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.taf b/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.taf
new file mode 100644
index 00000000000..1bc36b69a7e
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/Certificates/Trusted-OracleCorporationVirtualBox-51ca009816fdbd80f120e015ee75823e.taf
Binary files differ
diff --git a/src/VBox/HostDrivers/Support/win/NtCreateSection-template-amd64-syscall-type-1.h b/src/VBox/HostDrivers/Support/win/NtCreateSection-template-amd64-syscall-type-1.h
new file mode 100644
index 00000000000..2b8c8c89147
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/NtCreateSection-template-amd64-syscall-type-1.h
@@ -0,0 +1,78 @@
+SYSCALL(0x30);
+SYSCALL(0x31);
+SYSCALL(0x32);
+SYSCALL(0x33);
+SYSCALL(0x34);
+SYSCALL(0x35);
+SYSCALL(0x36);
+SYSCALL(0x37);
+SYSCALL(0x38);
+SYSCALL(0x39);
+SYSCALL(0x3A);
+SYSCALL(0x3B);
+SYSCALL(0x3C);
+SYSCALL(0x3D);
+SYSCALL(0x3E);
+SYSCALL(0x3F);
+SYSCALL(0x40);
+SYSCALL(0x41);
+SYSCALL(0x42);
+SYSCALL(0x43);
+SYSCALL(0x44);
+SYSCALL(0x45);
+SYSCALL(0x46);
+SYSCALL(0x47); /* XP64/W2K3-64, Vista, Windows 7 */
+SYSCALL(0x48); /* Windows 8.0 */
+SYSCALL(0x49); /* windows 8.1 */
+SYSCALL(0x4A);
+SYSCALL(0x4B);
+SYSCALL(0x4C);
+SYSCALL(0x4D);
+SYSCALL(0x4E);
+SYSCALL(0x4F);
+SYSCALL(0x51);
+SYSCALL(0x52);
+SYSCALL(0x53);
+SYSCALL(0x54);
+SYSCALL(0x55);
+SYSCALL(0x56);
+SYSCALL(0x57);
+SYSCALL(0x59);
+SYSCALL(0x5A);
+SYSCALL(0x5B);
+SYSCALL(0x5C);
+SYSCALL(0x5D);
+SYSCALL(0x5E);
+SYSCALL(0x5F);
+SYSCALL(0x60);
+SYSCALL(0x61);
+SYSCALL(0x62);
+SYSCALL(0x63);
+SYSCALL(0x64);
+SYSCALL(0x65);
+SYSCALL(0x66);
+SYSCALL(0x67);
+SYSCALL(0x68);
+SYSCALL(0x69);
+SYSCALL(0x6A);
+SYSCALL(0x6B);
+SYSCALL(0x6C);
+SYSCALL(0x6D);
+SYSCALL(0x6E);
+SYSCALL(0x6F);
+SYSCALL(0x70);
+SYSCALL(0x71);
+SYSCALL(0x72);
+SYSCALL(0x73);
+SYSCALL(0x74);
+SYSCALL(0x75);
+SYSCALL(0x76);
+SYSCALL(0x77);
+SYSCALL(0x78);
+SYSCALL(0x79);
+SYSCALL(0x7A);
+SYSCALL(0x7B);
+SYSCALL(0x7C);
+SYSCALL(0x7D);
+SYSCALL(0x7E);
+SYSCALL(0x7F);
diff --git a/src/VBox/HostDrivers/Support/win/NtCreateSection-template-x86-syscall-type-1.h b/src/VBox/HostDrivers/Support/win/NtCreateSection-template-x86-syscall-type-1.h
new file mode 100644
index 00000000000..f80a4d3fe90
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/NtCreateSection-template-x86-syscall-type-1.h
@@ -0,0 +1,286 @@
+SYSCALL(0x28);
+SYSCALL(0x29);
+SYSCALL(0x2A);
+SYSCALL(0x2B);
+SYSCALL(0x2C);
+SYSCALL(0x2D);
+SYSCALL(0x2E);
+SYSCALL(0x2F);
+SYSCALL(0x30);
+SYSCALL(0x31);
+
+SYSCALL(0x32); /* WinXP */
+SYSCALL(0x33);
+
+SYSCALL(0x34); /* W2K3 */
+SYSCALL(0x35);
+SYSCALL(0x36);
+SYSCALL(0x37);
+SYSCALL(0x38);
+SYSCALL(0x39);
+SYSCALL(0x3A);
+SYSCALL(0x3B);
+SYSCALL(0x3C);
+SYSCALL(0x3D);
+SYSCALL(0x3E);
+SYSCALL(0x3F);
+SYSCALL(0x40);
+SYSCALL(0x41);
+SYSCALL(0x42);
+SYSCALL(0x43);
+SYSCALL(0x44);
+SYSCALL(0x45);
+SYSCALL(0x46);
+SYSCALL(0x47);
+SYSCALL(0x48);
+SYSCALL(0x49);
+SYSCALL(0x4A);
+
+SYSCALL(0x4B); /* Vista */
+SYSCALL(0x4C);
+SYSCALL(0x4D);
+SYSCALL(0x4E);
+SYSCALL(0x4F);
+SYSCALL(0x50);
+SYSCALL(0x51);
+SYSCALL(0x52);
+SYSCALL(0x53);
+
+SYSCALL(0x54); /* Windows 7 */
+SYSCALL(0x55);
+SYSCALL(0x56);
+SYSCALL(0x57);
+SYSCALL(0x59);
+SYSCALL(0x5A);
+SYSCALL(0x5B);
+SYSCALL(0x5C);
+SYSCALL(0x5D);
+SYSCALL(0x5E);
+SYSCALL(0x5F);
+SYSCALL(0x60);
+SYSCALL(0x61);
+SYSCALL(0x62);
+SYSCALL(0x63);
+SYSCALL(0x64);
+SYSCALL(0x65);
+SYSCALL(0x66);
+SYSCALL(0x67);
+SYSCALL(0x68);
+SYSCALL(0x69);
+SYSCALL(0x6A);
+SYSCALL(0x6B);
+SYSCALL(0x6C);
+SYSCALL(0x6D);
+SYSCALL(0x6E);
+SYSCALL(0x6F);
+SYSCALL(0x70);
+SYSCALL(0x71);
+SYSCALL(0x72);
+SYSCALL(0x73);
+SYSCALL(0x74);
+SYSCALL(0x75);
+SYSCALL(0x76);
+SYSCALL(0x77);
+SYSCALL(0x78);
+SYSCALL(0x79);
+SYSCALL(0x7A);
+SYSCALL(0x7B);
+SYSCALL(0x7C);
+SYSCALL(0x7D);
+SYSCALL(0x7E);
+SYSCALL(0x7F);
+SYSCALL(0x80);
+SYSCALL(0x81);
+SYSCALL(0x82);
+SYSCALL(0x83);
+SYSCALL(0x84);
+SYSCALL(0x85);
+SYSCALL(0x86);
+SYSCALL(0x87);
+SYSCALL(0x88);
+SYSCALL(0x89);
+SYSCALL(0x8A);
+SYSCALL(0x8B);
+SYSCALL(0x8C);
+SYSCALL(0x8D);
+SYSCALL(0x8E);
+SYSCALL(0x8F);
+SYSCALL(0x90);
+SYSCALL(0x91);
+SYSCALL(0x92);
+SYSCALL(0x93);
+SYSCALL(0x94);
+SYSCALL(0x95);
+SYSCALL(0x96);
+SYSCALL(0x97);
+SYSCALL(0x98);
+SYSCALL(0x99);
+SYSCALL(0x9A);
+SYSCALL(0x9B);
+SYSCALL(0x9C);
+SYSCALL(0x9D);
+SYSCALL(0x9E);
+SYSCALL(0x9F);
+
+SYSCALL(0x100);
+SYSCALL(0x101);
+SYSCALL(0x102);
+SYSCALL(0x103);
+SYSCALL(0x104);
+SYSCALL(0x105);
+SYSCALL(0x106);
+SYSCALL(0x107);
+SYSCALL(0x108);
+SYSCALL(0x109);
+SYSCALL(0x10A);
+SYSCALL(0x10B);
+SYSCALL(0x10C);
+SYSCALL(0x10D);
+SYSCALL(0x10E);
+SYSCALL(0x10F);
+SYSCALL(0x110);
+SYSCALL(0x111);
+SYSCALL(0x112);
+SYSCALL(0x113);
+SYSCALL(0x114);
+SYSCALL(0x115);
+SYSCALL(0x116);
+SYSCALL(0x117);
+SYSCALL(0x118);
+SYSCALL(0x119);
+SYSCALL(0x11A);
+SYSCALL(0x11B);
+SYSCALL(0x11C);
+SYSCALL(0x11D);
+SYSCALL(0x11E);
+SYSCALL(0x11F);
+SYSCALL(0x120);
+SYSCALL(0x121);
+SYSCALL(0x122);
+SYSCALL(0x123);
+SYSCALL(0x124);
+SYSCALL(0x125);
+SYSCALL(0x126);
+SYSCALL(0x127);
+SYSCALL(0x128);
+SYSCALL(0x129);
+SYSCALL(0x12A);
+SYSCALL(0x12B);
+SYSCALL(0x12C);
+SYSCALL(0x12D);
+SYSCALL(0x12E);
+SYSCALL(0x12F);
+SYSCALL(0x130);
+SYSCALL(0x131);
+SYSCALL(0x132);
+SYSCALL(0x133);
+SYSCALL(0x134);
+SYSCALL(0x135);
+SYSCALL(0x136);
+SYSCALL(0x137);
+SYSCALL(0x138);
+SYSCALL(0x139);
+SYSCALL(0x13A);
+SYSCALL(0x13B);
+SYSCALL(0x13C);
+SYSCALL(0x13D);
+SYSCALL(0x13E);
+SYSCALL(0x13F);
+SYSCALL(0x140);
+SYSCALL(0x141);
+SYSCALL(0x142);
+SYSCALL(0x143);
+SYSCALL(0x144);
+SYSCALL(0x145);
+SYSCALL(0x146);
+SYSCALL(0x147);
+SYSCALL(0x148);
+SYSCALL(0x149);
+SYSCALL(0x14A);
+SYSCALL(0x14B);
+SYSCALL(0x14C);
+SYSCALL(0x14D);
+SYSCALL(0x14E);
+SYSCALL(0x14F);
+SYSCALL(0x150);
+SYSCALL(0x151);
+SYSCALL(0x152);
+SYSCALL(0x153);
+
+SYSCALL(0x154); /* Windows 8.1 */
+SYSCALL(0x155);
+SYSCALL(0x156);
+SYSCALL(0x157);
+SYSCALL(0x158);
+SYSCALL(0x159);
+SYSCALL(0x15A);
+SYSCALL(0x15B);
+SYSCALL(0x15C);
+SYSCALL(0x15D);
+SYSCALL(0x15E);
+SYSCALL(0x15F);
+SYSCALL(0x160);
+SYSCALL(0x161);
+SYSCALL(0x162);
+SYSCALL(0x163);
+SYSCALL(0x164);
+SYSCALL(0x165);
+SYSCALL(0x166);
+SYSCALL(0x167);
+SYSCALL(0x168);
+SYSCALL(0x169);
+SYSCALL(0x16A);
+SYSCALL(0x16B);
+SYSCALL(0x16C);
+SYSCALL(0x16D);
+SYSCALL(0x16E);
+SYSCALL(0x16F);
+SYSCALL(0x170);
+SYSCALL(0x171);
+SYSCALL(0x172);
+SYSCALL(0x173);
+SYSCALL(0x174);
+SYSCALL(0x175);
+SYSCALL(0x176);
+SYSCALL(0x177);
+SYSCALL(0x178);
+SYSCALL(0x179);
+SYSCALL(0x17A);
+SYSCALL(0x17B);
+SYSCALL(0x17C);
+SYSCALL(0x17D);
+SYSCALL(0x17E);
+SYSCALL(0x17F);
+SYSCALL(0x180);
+SYSCALL(0x181);
+SYSCALL(0x182);
+SYSCALL(0x183);
+SYSCALL(0x184);
+SYSCALL(0x185);
+SYSCALL(0x186);
+SYSCALL(0x187);
+SYSCALL(0x188);
+SYSCALL(0x189);
+SYSCALL(0x18A);
+SYSCALL(0x18B);
+SYSCALL(0x18C);
+SYSCALL(0x18D);
+SYSCALL(0x18E);
+SYSCALL(0x18F);
+SYSCALL(0x190);
+SYSCALL(0x191);
+SYSCALL(0x192);
+SYSCALL(0x193);
+SYSCALL(0x194);
+SYSCALL(0x195);
+SYSCALL(0x196);
+SYSCALL(0x197);
+SYSCALL(0x198);
+SYSCALL(0x199);
+SYSCALL(0x19A);
+SYSCALL(0x19B);
+SYSCALL(0x19C);
+SYSCALL(0x19D);
+SYSCALL(0x19E);
+SYSCALL(0x19F);
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
index 3c946b751b9..862b0d0c8de 100644
--- a/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
+++ b/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
@@ -27,21 +27,30 @@
/*******************************************************************************
* Header Files *
*******************************************************************************/
+#define IPRT_NT_MAP_TO_ZW
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "../SUPDrvInternal.h"
#include <excpt.h>
#include <ntimage.h>
#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/ctype.h>
#include <iprt/initterm.h>
#include <iprt/mem.h>
#include <iprt/process.h>
#include <iprt/power.h>
+#include <iprt/spinlock.h>
#include <iprt/string.h>
#include <VBox/log.h>
+#include <VBox/err.h>
#include <iprt/asm-amd64-x86.h>
+#ifdef VBOX_WITH_HARDENING
+# include "SUPHardenedVerify-win.h"
+#endif
+
/*******************************************************************************
* Defined Constants And Macros *
@@ -65,6 +74,22 @@
/** Win Symlink name for user access. */
#define DEVICE_NAME_DOS_USR L"\\DosDevices\\VBoxDrvU"
+#ifdef VBOX_WITH_HARDENING
+/** Win32 device name for hardened stub access. */
+# define DEVICE_NAME_STUB "\\\\.\\VBoxDrvStub"
+/** NT device name for hardened stub access. */
+# define DEVICE_NAME_NT_STUB L"\\Device\\VBoxDrvStub"
+///** Win Symlink name for hardened stub access. */
+//# define DEVICE_NAME_DOS_STUB L"\\DosDevices\\VBoxDrvStub"
+
+
+/** Macro for checking for deflecting calls to the stub device. */
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) \
+ do { if ((a_pDevObj) == g_pDevObjStub) supdrvNtCompleteRequest(STATUS_ACCESS_DENIED, a_pIrp); } while (0)
+#else
+# define VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(a_pDevObj, a_pIrp) do {} while (0)
+#endif
+
/** Enables the fast I/O control code path. */
#define VBOXDRV_WITH_FAST_IO
@@ -72,15 +97,6 @@
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
-#if 0 //def RT_ARCH_AMD64
-typedef struct SUPDRVEXECMEM
-{
- PMDL pMdl;
- void *pvMapping;
- void *pvAllocation;
-} SUPDRVEXECMEM, *PSUPDRVEXECMEM;
-#endif
-
/**
* Device extension used by VBoxDrvU.
*/
@@ -103,6 +119,122 @@ typedef SUPDRVDEVEXTUSR *PSUPDRVDEVEXTUSR;
? (PSUPDRVDEVEXT)pDevObj->DeviceExtension \
: ((PSUPDRVDEVEXTUSR)pDevObj->DeviceExtension)->pMainDrvExt )
+#ifdef VBOX_WITH_HARDENING
+
+/**
+ * Device extension used by VBoxDrvS.
+ */
+typedef struct SUPDRVDEVEXTSTUB
+{
+ /** Common header. */
+ SUPDRVDEVEXTUSR Common;
+} SUPDRVDEVEXTSTUB;
+/** Pointer to the VBoxDrvS device extension. */
+typedef SUPDRVDEVEXTSTUB *PSUPDRVDEVEXTSTUB;
+/** Value of SUPDRVDEVEXTSTUB::Common.u32Cookie. */
+#define SUPDRVDEVEXTSTUB_COOKIE UINT32_C(0x90abcdef)
+
+
+/**
+ * The kind of process we're protecting.
+ */
+typedef enum SUPDRVNTPROTECTKIND
+{
+ kSupDrvNtProtectKind_Invalid = 0,
+
+ /** Stub process protection while performing process verification.
+ * Next: StubSpawning (or free) */
+ kSupDrvNtProtectKind_StubUnverified,
+ /** Stub process protection before it creates the VM process.
+ * Next: StubParent, StubDead. */
+ kSupDrvNtProtectKind_StubSpawning,
+ /** Stub process protection while having a VM process as child.
+ * Next: StubDead */
+ kSupDrvNtProtectKind_StubParent,
+ /** Dead stub process. */
+ kSupDrvNtProtectKind_StubDead,
+
+ /** Potential VM process.
+ * Next: VmProcessConfirmed, VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessUnconfirmed,
+ /** Confirmed VM process.
+ * Next: VmProcessDead. */
+ kSupDrvNtProtectKind_VmProcessConfirmed,
+ /** Dead VM process. */
+ kSupDrvNtProtectKind_VmProcessDead,
+
+ /** End of valid protection kinds. */
+ kSupDrvNtProtectKind_End
+} SUPDRVNTPROTECTKIND;
+
+/**
+ * A NT process protection structure.
+ */
+typedef struct SUPDRVNTPROTECT
+{
+ /** The AVL node core structure. The process ID is the pid. */
+ AVLPVNODECORE AvlCore;
+ /** Magic value (SUPDRVNTPROTECT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The kind of process we're protecting. */
+ SUPDRVNTPROTECTKIND volatile enmProcessKind;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fFirstThreadCreateHandle : 1;
+ /** 8.1: Hack to allow more rights to the handle returned by
+ * NtCreateUserProcess. Only applicable to VmProcessUnconfirmed. */
+ bool fCsrssFirstProcessCreateHandle : 1;
+ /** Vista, 7 & 8: Hack to allow more rights to the handle duplicated by CSR
+ * during process creation. Only applicable to VmProcessUnconfirmed. */
+ bool fCsrssFirstProcessDuplicateHandle : 1;
+ /** 7,: Hack to allow the supid themes service duplicate handle privileges to
+ * our process. */
+ bool fThemesFirstProcessCreateHandle : 1;
+
+ /** The parent PID for VM processes, otherwise NULL. */
+ HANDLE hParentPid;
+ /** The PID of the CSRSS process associated with this process. */
+ HANDLE hCsrssPid;
+ /** Pointer to the CSRSS process structure (referenced). */
+ PEPROCESS pCsrssProcess;
+ /** State dependent data. */
+ union
+ {
+ /** A stub process in the StubParent state will keep a reference to a child
+ * while it's in the VmProcessUnconfirmed state so that it can be cleaned up
+ * correctly if things doesn't work out. */
+ struct SUPDRVNTPROTECT *pChild;
+ /** A process in the VmProcessUnconfirmed state will keep a weak
+ * reference to the parent's protection structure so it can clean up the pChild
+ * refernece the parent has to it. */
+ struct SUPDRVNTPROTECT *pParent;
+ } u;
+} SUPDRVNTPROTECT;
+/** Pointer to a NT process protection record. */
+typedef SUPDRVNTPROTECT *PSUPDRVNTPROTECT;
+/** The SUPDRVNTPROTECT::u32Magic value (Robert A. Heinlein). */
+# define SUPDRVNTPROTECT_MAGIC UINT32_C(0x19070707)
+/** The SUPDRVNTPROTECT::u32Magic value of a dead structure. */
+# define SUPDRVNTPROTECT_MAGIC_DEAD UINT32_C(0x19880508)
+
+/** Pointer to ObRegisterCallbacks. */
+typedef NTSTATUS (NTAPI *PFNOBREGISTERCALLBACKS)(POB_CALLBACK_REGISTRATION, PVOID *);
+/** Pointer to ObUnregisterCallbacks. */
+typedef VOID (NTAPI *PFNOBUNREGISTERCALLBACKS)(PVOID);
+/** Pointer to PsSetCreateProcessNotifyRoutineEx. */
+typedef NTSTATUS (NTAPI *PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)(PCREATE_PROCESS_NOTIFY_ROUTINE_EX, BOOLEAN);
+/** Pointer to PsReferenceProcessFilePointer. */
+typedef NTSTATUS (NTAPI *PFNPSREFERENCEPROCESSFILEPOINTER)(PEPROCESS, PFILE_OBJECT *);
+/** Pointer to PsIsProtectedProcessLight. */
+typedef BOOLEAN (NTAPI *PFNPSISPROTECTEDPROCESSLIGHT)(PEPROCESS);
+
+#endif /* VBOX_WITH_HARDENINIG */
+
/*******************************************************************************
* Internal Functions *
@@ -122,6 +254,18 @@ static NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj
static VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1, PVOID pArgument2);
static NTSTATUS _stdcall VBoxDrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
static NTSTATUS VBoxDrvNtErr2NtStatus(int rc);
+#ifdef VBOX_WITH_HARDENING
+static NTSTATUS supdrvNtProtectInit(void);
+static void supdrvNtProtectTerm(void);
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid,
+ SUPDRVNTPROTECTKIND enmProcessKind, bool fLink);
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect);
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid);
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect);
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect);
+
+static bool supdrvNtIsDebuggerAttached(void);
+#endif
/*******************************************************************************
@@ -174,6 +318,41 @@ static FAST_IO_DISPATCH const g_VBoxDrvFastIoDispatch =
};
#endif /* VBOXDRV_WITH_FAST_IO */
+#ifdef VBOX_WITH_HARDENING
+/** Pointer to the stub device instance. */
+static PDEVICE_OBJECT g_pDevObjStub = NULL;
+/** Spinlock protecting g_NtProtectTree as well as the releasing of protection
+ * structures. */
+static RTSPINLOCK g_hNtProtectLock = NIL_RTSPINLOCK;
+/** AVL tree of SUPDRVNTPROTECT structures. */
+static AVLPVTREE g_NtProtectTree = NULL;
+/** Cookie returned by ObRegisterCallbacks for the callbacks. */
+static PVOID g_pvObCallbacksCookie = NULL;
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
+uint32_t g_uNtVerCombined = 0;
+/** Pointer to ObRegisterCallbacks if available.. */
+static PFNOBREGISTERCALLBACKS g_pfnObRegisterCallbacks = NULL;
+/** Pointer to ObUnregisterCallbacks if available.. */
+static PFNOBUNREGISTERCALLBACKS g_pfnObUnRegisterCallbacks = NULL;
+/** Pointer to PsSetCreateProcessNotifyRoutineEx if available.. */
+static PFNPSSETCREATEPROCESSNOTIFYROUTINEEX g_pfnPsSetCreateProcessNotifyRoutineEx = NULL;
+/** Pointer to PsReferenceProcessFilePointer if available. */
+static PFNPSREFERENCEPROCESSFILEPOINTER g_pfnPsReferenceProcessFilePointer = NULL;
+/** Pointer to PsIsProtectedProcessLight. */
+static PFNPSISPROTECTEDPROCESSLIGHT g_pfnPsIsProtectedProcessLight = NULL;
+
+# ifdef RT_ARCH_AMD64
+extern "C" {
+/** Pointer to KiServiceLinkage (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64). */
+PFNRT g_pfnKiServiceLinkage = NULL;
+/** Pointer to KiServiceInternal (used to fake missing ZwQueryVirtualMemory on
+ * XP64 / W2K3-64) */
+PFNRT g_pfnKiServiceInternal = NULL;
+}
+# endif
+#endif
+
/**
* Takes care of creating the devices and their symbolic links.
@@ -191,9 +370,9 @@ static NTSTATUS vboxdrvNtCreateDevices(PDRIVER_OBJECT pDrvObj)
NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjSys);
if (NT_SUCCESS(rcNt))
{
- UNICODE_STRING DosName;
- RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS_SYS);
- rcNt = IoCreateSymbolicLink(&DosName, &DevName);
+ UNICODE_STRING DosNameSys;
+ RtlInitUnicodeString(&DosNameSys, DEVICE_NAME_DOS_SYS);
+ rcNt = IoCreateSymbolicLink(&DosNameSys, &DevName);
if (NT_SUCCESS(rcNt))
{
/*
@@ -203,24 +382,49 @@ static NTSTATUS vboxdrvNtCreateDevices(PDRIVER_OBJECT pDrvObj)
rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTUSR), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjUsr);
if (NT_SUCCESS(rcNt))
{
- UNICODE_STRING DosName;
- RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS_USR);
- rcNt = IoCreateSymbolicLink(&DosName, &DevName);
+ UNICODE_STRING DosNameUsr;
+ RtlInitUnicodeString(&DosNameUsr, DEVICE_NAME_DOS_USR);
+ rcNt = IoCreateSymbolicLink(&DosNameUsr, &DevName);
if (NT_SUCCESS(rcNt))
{
PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
pDevExtUsr->pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
pDevExtUsr->u32Cookie = SUPDRVDEVEXTUSR_COOKIE;
- /* Done. */
- return rcNt;
- }
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Hardened stub device.
+ */
+ RtlInitUnicodeString(&DevName, DEVICE_NAME_NT_STUB);
+ rcNt = IoCreateDevice(pDrvObj, sizeof(SUPDRVDEVEXTSTUB), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_pDevObjStub);
+ if (NT_SUCCESS(rcNt))
+ {
+ //UNICODE_STRING DosNameStub;
+ //RtlInitUnicodeString(&DosNameStub, DEVICE_NAME_DOS_STUB);
+ //rcNt = IoCreateSymbolicLink(&DosNameStub, &DevName);
+ if (NT_SUCCESS(rcNt))
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ pDevExtStub->Common.u32Cookie = SUPDRVDEVEXTSTUB_COOKIE;
+#endif
+
+ /* Done. */
+ return rcNt;
+#ifdef VBOX_WITH_HARDENING
+ }
- /* Bail out. */
+ /* Bail out. */
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjUsr = NULL;
+ }
+ IoDeleteSymbolicLink(&DosNameUsr);
+#endif
+ }
IoDeleteDevice(g_pDevObjUsr);
g_pDevObjUsr = NULL;
}
- IoDeleteSymbolicLink(&DosName);
+ IoDeleteSymbolicLink(&DosNameSys);
}
IoDeleteDevice(g_pDevObjSys);
g_pDevObjSys = NULL;
@@ -240,9 +444,28 @@ static void vboxdrvNtDestroyDevices(void)
RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS_USR);
rcNt = IoDeleteSymbolicLink(&DosName);
- PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
- pDevExtUsr->pMainDrvExt = NULL;
+#ifdef VBOX_WITH_HARDENING
+ //RtlInitUnicodeString(&DosName, DEVICE_NAME_DOS_STUB);
+ //rcNt = IoDeleteSymbolicLink(&DosName);
+#endif
+ if (g_pDevObjUsr)
+ {
+ PSUPDRVDEVEXTUSR pDevExtUsr = (PSUPDRVDEVEXTUSR)g_pDevObjUsr->DeviceExtension;
+ pDevExtUsr->pMainDrvExt = NULL;
+ }
+#ifdef VBOX_WITH_HARDENING
+ if (g_pDevObjStub)
+ {
+ PSUPDRVDEVEXTSTUB pDevExtStub = (PSUPDRVDEVEXTSTUB)g_pDevObjStub->DeviceExtension;
+ pDevExtStub->Common.pMainDrvExt = NULL;
+ }
+#endif
+
+#ifdef VBOX_WITH_HARDENING
+ IoDeleteDevice(g_pDevObjStub);
+ g_pDevObjStub = NULL;
+#endif
IoDeleteDevice(g_pDevObjUsr);
g_pDevObjUsr = NULL;
IoDeleteDevice(g_pDevObjSys);
@@ -272,74 +495,94 @@ ULONG _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
#endif
/*
- * Create device.
- * (That means creating a device object and a symbolic link so the DOS
- * subsystems (OS/2, win32, ++) can access the device.)
+ * Initialize the runtime (IPRT).
*/
- NTSTATUS rcNt = vboxdrvNtCreateDevices(pDrvObj);
- if (NT_SUCCESS(rcNt))
+ NTSTATUS rcNt;
+ int vrc = RTR0Init(0);
+ if (RT_SUCCESS(vrc))
{
- int vrc = RTR0Init(0);
- if (RT_SUCCESS(vrc))
- {
- Log(("VBoxDrv::DriverEntry\n"));
+ Log(("VBoxDrv::DriverEntry\n"));
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Initialize process protection.
+ */
+ rcNt = supdrvNtProtectInit();
+ if (NT_SUCCESS(rcNt))
+#endif
+ {
/*
- * Initialize the device extension.
+ * Create device.
+ * (That means creating a device object and a symbolic link so the DOS
+ * subsystems (OS/2, win32, ++) can access the device.)
*/
- PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
- memset(pDevExt, 0, sizeof(*pDevExt));
-
- vrc = supdrvInitDevExt(pDevExt, sizeof(SUPDRVSESSION));
- if (!vrc)
+ rcNt = vboxdrvNtCreateDevices(pDrvObj);
+ if (NT_SUCCESS(rcNt))
{
/*
- * Setup the driver entry points in pDrvObj.
+ * Initialize the device extension.
*/
- pDrvObj->DriverUnload = VBoxDrvNtUnload;
- pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxDrvNtCreate;
- pDrvObj->MajorFunction[IRP_MJ_CLEANUP] = VBoxDrvNtCleanup;
- pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxDrvNtClose;
- pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxDrvNtDeviceControl;
- pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxDrvNtInternalDeviceControl;
- pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxDrvNtNotSupportedStub;
- pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxDrvNtNotSupportedStub;
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
- /* more? */
+ vrc = supdrvInitDevExt(pDevExt, sizeof(SUPDRVSESSION));
+ if (!vrc)
+ {
+ /*
+ * Setup the driver entry points in pDrvObj.
+ */
+ pDrvObj->DriverUnload = VBoxDrvNtUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxDrvNtCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLEANUP] = VBoxDrvNtCleanup;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxDrvNtClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxDrvNtDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxDrvNtInternalDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_READ] = VBoxDrvNtNotSupportedStub;
+ pDrvObj->MajorFunction[IRP_MJ_WRITE] = VBoxDrvNtNotSupportedStub;
#ifdef VBOXDRV_WITH_FAST_IO
- /* Fast I/O to speed up guest execution roundtrips. */
- pDrvObj->FastIoDispatch = (PFAST_IO_DISPATCH)&g_VBoxDrvFastIoDispatch;
+ /* Fast I/O to speed up guest execution roundtrips. */
+ pDrvObj->FastIoDispatch = (PFAST_IO_DISPATCH)&g_VBoxDrvFastIoDispatch;
#endif
- /* Register ourselves for power state changes. */
- UNICODE_STRING CallbackName;
- OBJECT_ATTRIBUTES Attr;
+ /*
+ * Register ourselves for power state changes. We don't
+ * currently care if this fails.
+ */
+ UNICODE_STRING CallbackName;
+ RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
- RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
- InitializeObjectAttributes(&Attr, &CallbackName, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ OBJECT_ATTRIBUTES Attr;
+ InitializeObjectAttributes(&Attr, &CallbackName, OBJ_CASE_INSENSITIVE, NULL, NULL);
- rcNt = ExCreateCallback(&pDevExt->pObjPowerCallback, &Attr, TRUE, TRUE);
- if (rcNt == STATUS_SUCCESS)
- pDevExt->hPowerCallback = ExRegisterCallback(pDevExt->pObjPowerCallback, VBoxPowerDispatchCallback,
- g_pDevObjSys);
+ rcNt = ExCreateCallback(&pDevExt->pObjPowerCallback, &Attr, TRUE, TRUE);
+ if (rcNt == STATUS_SUCCESS)
+ pDevExt->hPowerCallback = ExRegisterCallback(pDevExt->pObjPowerCallback,
+ VBoxPowerDispatchCallback,
+ g_pDevObjSys);
- Log(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n"));
- return STATUS_SUCCESS;
- }
+ /*
+ * Done! Returning success!
+ */
+ Log(("VBoxDrv::DriverEntry returning STATUS_SUCCESS\n"));
+ return STATUS_SUCCESS;
+ }
- Log(("supdrvInitDevExit failed with vrc=%d!\n", vrc));
- rcNt = VBoxDrvNtErr2NtStatus(vrc);
+ Log(("supdrvInitDevExit failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
- RTR0Term();
- }
- else
- {
- Log(("RTR0Init failed with vrc=%d!\n", vrc));
- rcNt = VBoxDrvNtErr2NtStatus(vrc);
+ vboxdrvNtDestroyDevices();
+ }
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
}
-
- vboxdrvNtDestroyDevices();
+ RTR0Term();
+ }
+ else
+ {
+ Log(("RTR0Init failed with vrc=%d!\n", vrc));
+ rcNt = VBoxDrvNtErr2NtStatus(vrc);
}
if (NT_SUCCESS(rcNt))
rcNt = STATUS_INVALID_PARAMETER;
@@ -368,6 +611,9 @@ void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj)
* We ASSUME that it's not possible to unload a driver with open handles.
*/
supdrvDeleteDevExt(pDevExt);
+#ifdef VBOX_WITH_HARDENING
+ supdrvNtProtectTerm();
+#endif
RTR0Term();
vboxdrvNtDestroyDevices();
@@ -376,6 +622,37 @@ void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj)
/**
+ * For simplifying request completion into a simple return statement, extended
+ * version.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param uInfo Extra info value.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) supdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
+{
+ pIrp->IoStatus.Status = rcNt;
+ pIrp->IoStatus.Information = uInfo;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return rcNt;
+}
+
+
+/**
+ * For simplifying request completion into a simple return statement.
+ *
+ * @returns rcNt
+ * @param rcNt The status code.
+ * @param pIrp The IRP.
+ */
+DECLINLINE(NTSTATUS) supdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
+{
+ return supdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
+}
+
+
+/**
* Create (i.e. Open) file entry point.
*
* @param pDevObj Device object.
@@ -384,49 +661,180 @@ void _stdcall VBoxDrvNtUnload(PDRIVER_OBJECT pDrvObj)
NTSTATUS _stdcall VBoxDrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
Log(("VBoxDrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
- const bool fUnrestricted = pDevObj == g_pDevObjSys;
- PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
PFILE_OBJECT pFileObj = pStack->FileObject;
- PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
/*
* We are not remotely similar to a directory...
* (But this is possible.)
*/
if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
- {
- pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
- pIrp->IoStatus.Information = 0;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
- return STATUS_NOT_A_DIRECTORY;
- }
+ return supdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
/*
* Don't create a session for kernel clients, they'll close the handle
* immediately and work with the file object via
- * VBoxDrvNtInternalDeviceControl. The first request will there be one
- * to create a session.
+ * VBoxDrvNtInternalDeviceControl. The first request will be one to
+ * create a session.
*/
NTSTATUS rcNt;
if (pIrp->RequestorMode == KernelMode)
- rcNt = STATUS_SUCCESS;
+ {
+ if (pDevObj == g_pDevObjSys)
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ rcNt = STATUS_ACCESS_DENIED;
+ }
else
{
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
/*
- * Call common code for the rest.
+ * Make sure no debuggers are attached to non-user processes.
*/
- pFileObj->FsContext = NULL;
- PSUPDRVSESSION pSession;
- int rc = supdrvCreateSession(pDevExt, true /*fUser*/, fUnrestricted, &pSession);
- if (!rc)
- pFileObj->FsContext = pSession;
- rcNt = pIrp->IoStatus.Status = VBoxDrvNtErr2NtStatus(rc);
- }
+ if ( pDevObj != g_pDevObjUsr
+ && supdrvNtIsDebuggerAttached())
+ {
+ LogRel(("vboxdrv: Process %p is being debugged, access to vboxdrv / vboxdrvu declined.\n",
+ PsGetProcessId(PsGetCurrentProcess())));
+ rcNt = STATUS_TRUST_FAILURE;
+ }
+ else
+#endif
+ {
+ int rc = VINF_SUCCESS;
- pIrp->IoStatus.Information = 0;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+#ifdef VBOX_WITH_HARDENING
+ /*
+ * Access to the stub device is only granted to processes which
+ * passes verification.
+ *
+ * Note! The stub device has no need for a SUPDRVSESSION structure,
+ * so the it uses the SUPDRVNTPROTECT directly instead.
+ */
+ if (pDevObj == g_pDevObjStub)
+ {
+ PSUPDRVNTPROTECT pNtProtect = NULL;
+ rc = supdrvNtProtectCreate(&pNtProtect, PsGetProcessId(PsGetCurrentProcess()),
+ kSupDrvNtProtectKind_StubUnverified, true /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvNtProtectFindAssociatedCsrss(pNtProtect);
+ if (RT_SUCCESS(rc))
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
- return rcNt;
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ LogRel(("vboxdrv: Declined %p access to VBoxDrvStub: rc=%d\n", PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ /*
+ * Unrestricted access is only granted to a process in the
+ * VmProcessUnconfirmed state that checks out correctly and is
+ * allowed to transition to VmProcessConfirmed. Again, only one
+ * session per process.
+ */
+ else if (pDevObj != g_pDevObjUsr)
+ {
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(PsGetCurrentProcess()));
+ if (pNtProtect)
+ {
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ rc = supdrvNtProtectVerifyProcess(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a session. */
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/,
+ &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pSession->pNtProtect = pNtProtect; /* Keeps reference. */
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+
+ /* No second attempt. */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ LogRel(("vboxdrv: supdrvCreateSession failed for process %p: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ LogRel(("vboxdrv: Process %p failed process verification: rc=%d.\n",
+ PsGetProcessId(PsGetCurrentProcess()), rc));
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process (enmProcessKind=%d).\n",
+ PsGetProcessId(PsGetCurrentProcess()), pNtProtect->enmProcessKind));
+ rc = VERR_ACCESS_DENIED;
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+ else
+ {
+ LogRel(("vboxdrv: %p is not a budding VM process.\n", PsGetProcessId(PsGetCurrentProcess())));
+ rc = VERR_ACCESS_DENIED;
+ }
+ }
+ /*
+ * Call common code to create an unprivileged session.
+ */
+ else
+ {
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, false /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ {
+ pFileObj->FsContext = pSession; /* Keeps reference. No race. */
+ pSession->pNtProtect = NULL;
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+ }
+ }
+ }
+
+#else /* !VBOX_WITH_HARDENING */
+ /*
+ * Call common code to create a session.
+ */
+ pFileObj->FsContext = NULL;
+ PSUPDRVSESSION pSession;
+ rc = supdrvCreateSession(pDevExt, true /*fUser*/, pDevObj == g_pDevObjSys /*fUnrestricted*/, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ rc = supdrvSessionHashTabInsert(pDevExt, pSession, (PSUPDRVSESSION *)&pFileObj->FsContext, NULL);
+ supdrvSessionRelease(pSession);
+ if (RT_SUCCESS(rc))
+ return supdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
+
+ }
+#endif /* !VBOX_WITH_HARDENING */
+
+ /* bail out */
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+ }
+ }
+
+ Assert(!NT_SUCCESS(rcNt));
+ pFileObj->FsContext = NULL;
+ return supdrvNtCompleteRequest(rcNt, pIrp); /* Note. the IoStatus is completely ignored on error. */
}
@@ -441,20 +849,32 @@ NTSTATUS _stdcall VBoxDrvNtCleanup(PDEVICE_OBJECT pDevObj, PIRP pIrp)
PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
PFILE_OBJECT pFileObj = pStack->FileObject;
- PSUPDRVSESSION pSession = (PSUPDRVSESSION)pFileObj->FsContext;
- Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
- if (pSession)
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
{
- supdrvSessionRelease(pSession);
- pFileObj->FsContext = NULL;
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
}
- pIrp->IoStatus.Information = 0;
- pIrp->IoStatus.Status = STATUS_SUCCESS;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
-
- return STATUS_SUCCESS;
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
}
@@ -469,21 +889,232 @@ NTSTATUS _stdcall VBoxDrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
PFILE_OBJECT pFileObj = pStack->FileObject;
- PSUPDRVSESSION pSession = (PSUPDRVSESSION)pFileObj->FsContext;
- Log(("VBoxDrvNtClose: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
- if (pSession)
+#ifdef VBOX_WITH_HARDENING
+ if (pDevObj == g_pDevObjStub)
{
- supdrvSessionRelease(pSession);
- pFileObj->FsContext = NULL;
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)pFileObj->FsContext;
+ Log(("VBoxDrvNtClose: pDevExt=%p pFileObj=%p pNtProtect=%p\n", pDevExt, pFileObj, pNtProtect));
+ if (pNtProtect)
+ {
+ supdrvNtProtectRelease(pNtProtect);
+ pFileObj->FsContext = NULL;
+ }
+ }
+ else
+#endif
+ {
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ Log(("VBoxDrvNtCleanup: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pSession));
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
}
- pIrp->IoStatus.Information = 0;
- pIrp->IoStatus.Status = STATUS_SUCCESS;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return supdrvNtCompleteRequest(STATUS_SUCCESS, pIrp);
+}
+
- return STATUS_SUCCESS;
+#ifdef VBOXDRV_WITH_FAST_IO
+/**
+ * Fast I/O device control callback.
+ *
+ * This performs no buffering, neither on the way in or out.
+ *
+ * @returns TRUE if handled, FALSE if the normal I/O control routine should be
+ * called.
+ * @param pFileObj The file object.
+ * @param fWait Whether it's a blocking call
+ * @param pvInput The input buffer as specified by the user.
+ * @param cbInput The size of the input buffer.
+ * @param pvOutput The output buffer as specfied by the user.
+ * @param cbOutput The size of the output buffer.
+ * @param uFunction The function.
+ * @param pIoStatus Where to return the status of the operation.
+ * @param pDevObj The device object..
+ */
+static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOLEAN fWait, PVOID pvInput, ULONG cbInput,
+ PVOID pvOutput, ULONG cbOutput, ULONG uCmd,
+ PIO_STATUS_BLOCK pIoStatus, PDEVICE_OBJECT pDevObj)
+{
+ PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
+
+ /*
+ * Check the input a little bit and get a the session references.
+ */
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pFileObj->FsContext);
+ if (!pSession)
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ return TRUE;
+ }
+
+ if (pSession->fUnrestricted)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ pIoStatus->Status = STATUS_TRUST_FAILURE;
+ pIoStatus->Information = 0;
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+#endif
+
+ /*
+ * Deal with the 2-3 high-speed IOCtl that takes their arguments from
+ * the session and iCmd, and does not return anything.
+ */
+ if ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
+ || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
+ || uCmd == SUP_IOCTL_FAST_DO_NOP)
+ {
+ int rc = supdrvIOCtlFast(uCmd, (unsigned)(uintptr_t)pvOutput/* VMCPU id */, pDevExt, pSession);
+ pIoStatus->Status = RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
+ pIoStatus->Information = 0; /* Could be used to pass rc if we liked. */
+ supdrvSessionRelease(pSession);
+ return TRUE;
+ }
+ }
+
+ /*
+ * The normal path.
+ */
+ NTSTATUS rcNt;
+ unsigned cbOut = 0;
+ int rc = 0;
+ Log2(("VBoxDrvNtFastIoDeviceControl(%p): ioctl=%#x pvIn=%p cbIn=%#x pvOut=%p cbOut=%#x pSession=%p\n",
+ pDevExt, uCmd, pvInput, cbInput, pvOutput, cbOutput, pSession));
+
+# ifdef RT_ARCH_AMD64
+ /* Don't allow 32-bit processes to do any I/O controls. */
+ if (!IoIs32bitProcess(NULL))
+# endif
+ {
+ /*
+ * In this fast I/O device control path we have to do our own buffering.
+ */
+ /* Verify that the I/O control function matches our pattern. */
+ if ((uCmd & 0x3) == METHOD_BUFFERED)
+ {
+ /* Get the header so we can validate it a little bit against the
+ parameters before allocating any memory kernel for the reqest. */
+ SUPREQHDR Hdr;
+ if (cbInput >= sizeof(Hdr) && cbOutput >= sizeof(Hdr))
+ {
+ __try
+ {
+ RtlCopyMemory(&Hdr, pvInput, sizeof(Hdr));
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ }
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Verify that the sizes in the request header are correct. */
+ ULONG cbBuf = RT_MAX(cbInput, cbOutput);
+ if ( cbInput == Hdr.cbIn
+ && cbOutput == Hdr.cbOut
+ && cbBuf < _1M*16)
+ {
+ /* Allocate a buffer and copy all the input into it. */
+ PSUPREQHDR pHdr = (PSUPREQHDR)ExAllocatePoolWithTag(NonPagedPool, cbBuf, 'VBox');
+ if (pHdr)
+ {
+ __try
+ {
+ RtlCopyMemory(pHdr, pvInput, cbInput);
+ if (cbInput < cbBuf)
+ RtlZeroMemory((uint8_t *)pHdr + cbInput, cbBuf - cbInput);
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ }
+ else
+ rcNt = STATUS_NO_MEMORY;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Now call the common code to do the real work.
+ */
+ rc = supdrvIOCtl(uCmd, pDevExt, pSession, pHdr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy back the result.
+ */
+ cbOut = pHdr->cbOut;
+ if (cbOut > cbOutput)
+ {
+ cbOut = cbOutput;
+ OSDBGPRINT(("VBoxDrvNtFastIoDeviceControl: too much output! %#x > %#x; uCmd=%#x!\n",
+ pHdr->cbOut, cbOut, uCmd));
+ }
+ if (cbOut)
+ {
+ __try
+ {
+ RtlCopyMemory(pvOutput, pHdr, cbOut);
+ rcNt = STATUS_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ }
+ else
+ rcNt = STATUS_SUCCESS;
+ }
+ else if (rc == VERR_INVALID_PARAMETER)
+ rcNt = STATUS_INVALID_PARAMETER;
+ else
+ rcNt = STATUS_NOT_SUPPORTED;
+ Log2(("VBoxDrvNtFastIoDeviceControl: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
+ }
+ ExFreePoolWithTag(pHdr, 'VBox');
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
+ uCmd, Hdr.cbIn, Hdr.cbOut, cbInput, cbOutput));
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: not buffered request (%#x) - not supported\n", uCmd));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+ }
+# ifdef RT_ARCH_AMD64
+ else
+ {
+ Log(("VBoxDrvNtFastIoDeviceControl: WOW64 req - not supported\n"));
+ rcNt = STATUS_NOT_SUPPORTED;
+ }
+# endif
+
+ /* complete the request. */
+ pIoStatus->Status = rcNt;
+ pIoStatus->Information = cbOut;
+ supdrvSessionRelease(pSession);
+ return TRUE; /* handled. */
}
+#endif /* VBOXDRV_WITH_FAST_IO */
#ifdef VBOXDRV_WITH_FAST_IO
@@ -678,26 +1309,41 @@ static BOOLEAN _stdcall VBoxDrvNtFastIoDeviceControl(PFILE_OBJECT pFileObj, BOOL
*/
NTSTATUS _stdcall VBoxDrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(pDevObj, pIrp);
+
PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
- PSUPDRVSESSION pSession = (PSUPDRVSESSION)pStack->FileObject->FsContext;
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, RTProcSelf(), RTR0ProcHandleSelf(),
+ (PSUPDRVSESSION *)&pStack->FileObject->FsContext);
+
+ if (!RT_VALID_PTR(pSession))
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
/*
* Deal with the 2-3 high-speed IOCtl that takes their arguments from
* the session and iCmd, and does not return anything.
*/
- ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
- if ( ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
+ if (pSession->fUnrestricted)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(VBOX_WITHOUT_DEBUGGER_CHECKS)
+ if (supdrvNtIsDebuggerAttached())
+ {
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
+ }
+#endif
+
+ ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
+ if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
|| ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
|| ulCmd == SUP_IOCTL_FAST_DO_NOP)
- && pSession->fUnrestricted == true)
- {
- int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
+ {
+ int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
- /* Complete the I/O request. */
- NTSTATUS rcNt = pIrp->IoStatus.Status = RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
- return rcNt;
+ /* Complete the I/O request. */
+ supdrvSessionRelease(pSession);
+ return supdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
+ }
}
return VBoxDrvNtDeviceControlSlow(pDevExt, pSession, pIrp, pStack);
@@ -786,6 +1432,7 @@ static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSes
/* complete the request. */
pIrp->IoStatus.Status = rcNt;
pIrp->IoStatus.Information = cbOut;
+ supdrvSessionRelease(pSession);
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return rcNt;
}
@@ -799,6 +1446,8 @@ static int VBoxDrvNtDeviceControlSlow(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSes
*/
NTSTATUS _stdcall VBoxDrvNtInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
+ VBOXDRV_COMPLETE_IRP_AND_RETURN_IF_STUB_DEV(pDevObj, pIrp);
+
PSUPDRVDEVEXT pDevExt = SUPDRVNT_GET_DEVEXT(pDevObj);
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
PFILE_OBJECT pFileObj = pStack ? pStack->FileObject : NULL;
@@ -928,6 +1577,36 @@ VOID _stdcall VBoxPowerDispatchCallback(PVOID pCallbackContext, PVOID pArgument1
/**
+ * Called to clean up the session structure before it's freed.
+ *
+ * @param pDevExt The device globals.
+ * @param pSession The session that's being cleaned up.
+ */
+void VBOXCALL supdrvOSCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
+{
+#ifdef VBOX_WITH_HARDENING
+ if (pSession->pNtProtect)
+ {
+ supdrvNtProtectRelease(pSession->pNtProtect);
+ pSession->pNtProtect = NULL;
+ }
+#endif
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabInserted(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+void VBOXCALL supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser)
+{
+ NOREF(pDevExt); NOREF(pSession); NOREF(pvUser);
+}
+
+
+/**
* Initializes any OS specific object creator fields.
*/
void VBOXCALL supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
@@ -1081,10 +1760,10 @@ int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, c
case /* 0xC0000263 */ STATUS_DRIVER_ENTRYPOINT_NOT_FOUND:
rc = VERR_LDR_IMPORTED_SYMBOL_NOT_FOUND;
break;
- case 0xC0000428 /* STATUS_INVALID_IMAGE_HASH */ :
+ case /* 0xC0000428 */ STATUS_INVALID_IMAGE_HASH:
rc = VERR_LDR_IMAGE_HASH;
break;
- case 0xC000010E /* STATUS_IMAGE_ALREADY_LOADED */ :
+ case /* 0xC000010E */ STATUS_IMAGE_ALREADY_LOADED:
Log(("WARNING: see @bugref{4853} for cause of this failure on Windows 7 x64\n"));
rc = VERR_ALREADY_LOADED;
break;
@@ -1508,6 +2187,12 @@ static NTSTATUS VBoxDrvNtErr2NtStatus(int rc)
case VERR_VERSION_MISMATCH: return STATUS_REVISION_MISMATCH;
}
+ if (rc < 0)
+ {
+ if (((uint32_t)rc & UINT32_C(0xffff0000)) == UINT32_C(0xffff0000))
+ return (NTSTATUS)( ((uint32_t)rc & UINT32_C(0xffff))
+ | UINT32_C(0xe9860000) ); /* STATUS_SEVERITY_ERROR + C-bit + facility 0x986 */
+ }
return STATUS_UNSUCCESSFUL;
}
@@ -1546,3 +2231,1779 @@ SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...)
return 0;
}
#endif
+
+
+#ifdef VBOX_WITH_HARDENING
+
+/** @name Identifying Special Processes: CSRSS.EXE
+ * @{ */
+
+
+/**
+ * Checks if the process is a system32 process by the given name.
+ *
+ * @returns true / false.
+ * @param pProcess The process to check.
+ * @param pszName The lower case process name (no path!).
+ */
+static bool supdrvNtProtectIsSystem32ProcessMatch(PEPROCESS pProcess, const char *pszName)
+{
+ Assert(strlen(pszName) < 16); /* see buffer below */
+
+ /*
+ * This test works on XP+.
+ */
+ const char *pszImageFile = (const char *)PsGetProcessImageFileName(pProcess);
+ if (!pszImageFile)
+ return false;
+
+ if (RTStrICmp(pszImageFile, pszName) != 0)
+ return false;
+
+ /*
+ * This test requires a Vista+ API.
+ */
+ if (g_pfnPsReferenceProcessFilePointer)
+ {
+ PFILE_OBJECT pFile = NULL;
+ NTSTATUS rcNt = g_pfnPsReferenceProcessFilePointer(pProcess, &pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ union
+ {
+ OBJECT_NAME_INFORMATION Info;
+ uint8_t abBuffer[sizeof(g_System32NtPath) + 16 * sizeof(WCHAR)];
+ } Buf;
+ ULONG cbIgn;
+ rcNt = ObQueryNameString(pFile, &Buf.Info, sizeof(Buf) - sizeof(WCHAR), &cbIgn);
+ ObDereferenceObject(pFile);
+ if (!NT_SUCCESS(rcNt))
+ return false;
+
+ /* Terminate the name. */
+ PRTUTF16 pwszName = Buf.Info.Name.Buffer;
+ pwszName[Buf.Info.Name.Length / sizeof(RTUTF16)] = '\0';
+
+ /* Match the name against the system32 directory path. */
+ uint32_t cbSystem32 = g_System32NtPath.UniStr.Length;
+ if (Buf.Info.Name.Length < cbSystem32)
+ return false;
+ if (memcmp(pwszName, g_System32NtPath.UniStr.Buffer, cbSystem32))
+ return false;
+ pwszName += cbSystem32 / sizeof(RTUTF16);
+ if (*pwszName++ != '\\')
+ return false;
+
+ /* Compare the name. */
+ const char *pszRight = pszName;
+ for (;;)
+ {
+ WCHAR wchLeft = *pwszName++;
+ char chRight = *pszRight++;
+ Assert(chRight == RT_C_TO_LOWER(chRight));
+
+ if ( wchLeft != chRight
+ && RT_C_TO_LOWER(wchLeft) != chRight)
+ return false;
+ if (!chRight)
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the current process is likely to be CSRSS.
+ *
+ * @returns true/false.
+ * @param pProcess The process.
+ */
+static bool supdrvNtProtectIsCsrssByProcess(PEPROCESS pProcess)
+{
+ /*
+ * On Windows 8.1 CSRSS.EXE is a protected process.
+ */
+ if (g_pfnPsIsProtectedProcessLight)
+ {
+ if (!g_pfnPsIsProtectedProcessLight(pProcess))
+ return false;
+ }
+
+ /*
+ * The name tests.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pProcess, "csrss.exe"))
+ return false;
+
+ /** @todo Could extend the CSRSS.EXE check with that the TokenUser of the
+ * current process must be "NT AUTHORITY\SYSTEM" (S-1-5-18). */
+
+ return true;
+}
+
+
+/**
+ * Called in the context of VBoxDrvNtCreate to determin the CSRSS for the
+ * current process.
+ *
+ * The Client/Server Runtime Subsystem (CSRSS) process needs to be allowed some
+ * additional access right so we need to make 101% sure we correctly identify
+ * the CSRSS process a process is associated with.
+ *
+ * @returns IPRT status code.
+ * @param pNtProtect The NT protected process structure. The
+ * hCsrssPid member will be updated on success.
+ */
+static int supdrvNtProtectFindAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect)
+{
+ Assert(pNtProtect->AvlCore.Key == PsGetCurrentProcessId());
+ Assert(pNtProtect->pCsrssProcess == NULL);
+ Assert(pNtProtect->hCsrssPid == NULL);
+
+ /*
+ * We'll try use the ApiPort LPC object for the session we're in to track
+ * down the CSRSS process. So, we start by constructing a path to it.
+ */
+ int rc;
+ uint32_t uSessionId = PsGetProcessSessionId(PsGetCurrentProcess());
+ WCHAR wszApiPort[48];
+ if (uSessionId == 0)
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ else
+ {
+ char szTmp[64];
+ ssize_t cchTmp = RTStrFormatU32(szTmp, sizeof(szTmp), uSessionId, 10, 0, 0, 0);
+ AssertReturn(cchTmp > 0, (int)cchTmp);
+ rc = RTUtf16CopyAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Sessions\\");
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), szTmp);
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CatAscii(wszApiPort, RT_ELEMENTS(wszApiPort), "\\Windows\\ApiPort");
+ }
+ AssertRCReturn(rc, rc);
+
+ UNICODE_STRING ApiPortStr;
+ ApiPortStr.Buffer = wszApiPort;
+ ApiPortStr.Length = (USHORT)(RTUtf16Len(wszApiPort) * sizeof(RTUTF16));
+ ApiPortStr.MaximumLength = ApiPortStr.Length + sizeof(RTUTF16);
+
+ /*
+ * The object cannot be opened, but we can reference it by name.
+ */
+ void *pvApiPortObj = NULL;
+ NTSTATUS rcNt = ObReferenceObjectByName(&ApiPortStr,
+ 0,
+ NULL /*pAccessState*/,
+ STANDARD_RIGHTS_READ,
+ *LpcPortObjectType,
+ KernelMode,
+ NULL /*pvParseContext*/,
+ &pvApiPortObj);
+ if (!NT_SUCCESS(rcNt))
+ {
+ SUPR0Printf("vboxdrv: Error opening '%ls': %#x\n", wszApiPort, rcNt);
+ return VERR_SUPDRV_APIPORT_OPEN_ERROR;
+ }
+
+ /*
+ * Query the processes in the system so we can locate CSRSS.EXE candidates.
+ * Note! Attempts at using SystemSessionProcessInformation failed with
+ * STATUS_ACCESS_VIOLATION.
+ */
+ ULONG cbNeeded = _64K;
+ uint32_t cbBuf;
+ uint8_t *pbBuf = NULL;
+ do
+ {
+ cbBuf = RT_ALIGN(cbNeeded + _4K, _64K);
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ break;
+
+ cbNeeded = 0;
+#if 0 /* doesn't work. */
+ SYSTEM_SESSION_PROCESS_INFORMATION Req;
+ Req.SessionId = uSessionId;
+ Req.BufferLength = cbBuf;
+ Req.Buffer = pbBuf;
+ rcNt = NtQuerySystemInformation(SystemSessionProcessInformation, &Req, sizeof(Req), &cbNeeded);
+#else
+ rcNt = NtQuerySystemInformation(SystemProcessInformation, pbBuf, cbBuf, &cbNeeded);
+#endif
+ if (NT_SUCCESS(rcNt))
+ break;
+
+ RTMemFree(pbBuf);
+ pbBuf = NULL;
+ } while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbNeeded < 32U*_1M);
+
+ if ( pbBuf
+ && NT_SUCCESS(rcNt)
+ && cbNeeded >= sizeof(SYSTEM_PROCESS_INFORMATION))
+ {
+ /*
+ * Walk the returned data and look for the process associated with the
+ * ApiPort object. The ApiPort object keeps the EPROCESS address of
+ * the owner process (i.e. CSRSS) relatively early in the structure. On
+ * 64-bit windows 8.1 it's at offset 0x18. So, obtain the EPROCESS
+ * pointer to likely CSRSS processes and check for a match in the first
+ * 0x40 bytes of the ApiPort object.
+ */
+ rc = VERR_SUPDRV_CSRSS_NOT_FOUND;
+ for (uint32_t offBuf = 0; offBuf <= cbNeeded - sizeof(SYSTEM_PROCESS_INFORMATION);)
+ {
+ PRTNT_SYSTEM_PROCESS_INFORMATION pProcInfo = (PRTNT_SYSTEM_PROCESS_INFORMATION)&pbBuf[offBuf];
+ if ( pProcInfo->ProcessName.Length == 9 * sizeof(WCHAR)
+ && pProcInfo->NumberOfThreads > 2 /* Very low guess. */
+ && pProcInfo->HandleCount > 32 /* Very low guess, I hope. */
+ && (uintptr_t)pProcInfo->ProcessName.Buffer - (uintptr_t)pbBuf < cbNeeded
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[0]) == 'c'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[1]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[2]) == 'r'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[3]) == 's'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[4]) == 's'
+ && pProcInfo->ProcessName.Buffer[5] == '.'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[6]) == 'e'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[7]) == 'x'
+ && RT_C_TO_LOWER(pProcInfo->ProcessName.Buffer[8]) == 'e' )
+ {
+
+ /* Get the process structure and perform some more thorough
+ process checks. */
+ PEPROCESS pProcess;
+ rcNt = PsLookupProcessByProcessId(pProcInfo->UniqueProcessId, &pProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (supdrvNtProtectIsCsrssByProcess(pProcess))
+ {
+ if (PsGetProcessSessionId(pProcess) == uSessionId)
+ {
+ /* Final test, check the ApiPort.
+ Note! The old LPC (pre Vista) objects has the PID
+ much earlier in the structure. Might be
+ worth looking for it instead. */
+ bool fThatsIt = false;
+ __try
+ {
+ PEPROCESS *ppPortProc = (PEPROCESS *)pvApiPortObj;
+ uint32_t cTests = g_uNtVerCombined >= SUP_NT_VER_VISTA ? 16 : 38; /* ALPC since Vista. */
+ do
+ {
+ fThatsIt = *ppPortProc == pProcess;
+ ppPortProc++;
+ } while (!fThatsIt && --cTests > 0);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fThatsIt = false;
+ }
+ if (fThatsIt)
+ {
+ /* Ok, we found it! Keep the process structure
+ reference as well as the PID so we can
+ safely identify it later on. */
+ pNtProtect->hCsrssPid = pProcInfo->UniqueProcessId;
+ pNtProtect->pCsrssProcess = pProcess;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ ObDereferenceObject(pProcess);
+ }
+ }
+
+ /* Advance. */
+ if (!pProcInfo->NextEntryOffset)
+ break;
+ offBuf += pProcInfo->NextEntryOffset;
+ }
+ }
+ else
+ rc = VERR_SUPDRV_SESSION_PROCESS_ENUM_ERROR;
+ RTMemFree(pbBuf);
+ ObDereferenceObject(pvApiPortObj);
+ return rc;
+}
+
+
+/**
+ * Checks that the given process is the CSRSS process associated with protected
+ * process.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pCsrss The process structure of the alleged CSRSS.EXE
+ * process.
+ */
+static bool supdrvNtProtectIsAssociatedCsrss(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pCsrss)
+{
+ if (pNtProtect->pCsrssProcess == pCsrss)
+ {
+ if (pNtProtect->hCsrssPid == PsGetProcessId(pCsrss))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Checks if the given process is the stupid themes service.
+ *
+ * The caller does some screening of access masks and what not. We do the rest.
+ *
+ * @returns true / false.
+ * @param pNtProtect The NT protection structure.
+ * @param pAnnoyingProcess The process structure of an process that might
+ * happen to be the annoying themes process.
+ */
+static bool supdrvNtProtectIsFrigginThemesService(PSUPDRVNTPROTECT pNtProtect, PEPROCESS pAnnoyingProcess)
+{
+ /*
+ * Check the process name.
+ */
+ if (!supdrvNtProtectIsSystem32ProcessMatch(pAnnoyingProcess, "svchost.exe"))
+ return false;
+
+ /** @todo Come up with more checks. */
+
+ return true;
+}
+
+
+/** @} */
+
+
+/** @name Process Creation Callbacks.
+ * @{ */
+
+/**
+ * Common worker used by the process creation hooks as well as the process
+ * handle creation hooks to check if a VM process is being created.
+ *
+ * @returns true if likely to be a VM process, false if not.
+ * @param pNtStub The NT protection structure for the possible
+ * stub process.
+ * @param hParentPid The parent pid.
+ * @param hChildPid The child pid.
+ */
+static bool supdrvNtProtectIsSpawningStubProcess(PSUPDRVNTPROTECT pNtStub, HANDLE hParentPid, HANDLE hChildPid)
+{
+ bool fRc = false;
+ if (pNtStub->AvlCore.Key == hParentPid) /* paranoia */
+ {
+ if (pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ /* Compare short names. */
+ PEPROCESS pStubProcess;
+ NTSTATUS rcNt = PsLookupProcessByProcessId(hParentPid, &pStubProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ PEPROCESS pChildProcess;
+ rcNt = PsLookupProcessByProcessId(hChildPid, &pChildProcess);
+ if (NT_SUCCESS(rcNt))
+ {
+ const char *pszStub = (const char *)PsGetProcessImageFileName(pStubProcess);
+ const char *pszChild = (const char *)PsGetProcessImageFileName(pChildProcess);
+ fRc = pszStub != NULL
+ && pszChild != NULL
+ && strcmp(pszStub, pszChild) == 0;
+
+ /** @todo check that the full image names matches. */
+
+ ObDereferenceObject(pChildProcess);
+ }
+ ObDereferenceObject(pStubProcess);
+ }
+ }
+ }
+ return fRc;
+}
+
+
+/**
+ * Common code used by the notifies to protect a child process.
+ *
+ * @returns VBox status code.
+ * @param pNtStub The NT protect structure for the parent.
+ * @param hChildPid The child pid.
+ */
+static int supdrvNtProtectProtectNewStubChild(PSUPDRVNTPROTECT pNtParent, HANDLE hChildPid)
+{
+ /*
+ * Create a child protection struction.
+ */
+ PSUPDRVNTPROTECT pNtChild;
+ int rc = supdrvNtProtectCreate(&pNtChild, hChildPid, kSupDrvNtProtectKind_VmProcessUnconfirmed, false /*fLink*/);
+ if (RT_SUCCESS(rc))
+ {
+ pNtChild->fFirstProcessCreateHandle = true;
+ pNtChild->fFirstThreadCreateHandle = true;
+ pNtChild->fCsrssFirstProcessCreateHandle = true;
+ pNtChild->fCsrssFirstProcessDuplicateHandle = true;
+ pNtChild->fThemesFirstProcessCreateHandle = true;
+ pNtChild->hParentPid = pNtParent->AvlCore.Key;
+ pNtChild->hCsrssPid = pNtParent->hCsrssPid;
+ pNtChild->pCsrssProcess = pNtParent->pCsrssProcess;
+ if (pNtChild->pCsrssProcess)
+ ObReferenceObject(pNtChild->pCsrssProcess);
+
+ /*
+ * Take the spinlock, recheck parent conditions and link things.
+ */
+ RTSpinlockAcquire(g_hNtProtectLock);
+ if (pNtParent->enmProcessKind == kSupDrvNtProtectKind_StubSpawning)
+ {
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtChild->AvlCore);
+ if (fSuccess)
+ {
+ pNtParent->u.pChild = pNtChild; /* Parent keeps the initial reference. */
+ pNtParent->enmProcessKind = kSupDrvNtProtectKind_StubParent;
+ pNtChild->u.pParent = pNtParent;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtChild);
+ }
+ return rc;
+}
+
+
+/**
+ * Common process termination code.
+ *
+ * Transitions protected process to the dead states, protecting against handle
+ * PID reuse (esp. with unconfirmed VM processes) and handle cleanup issues.
+ *
+ * @param hDeadPid The PID of the dead process.
+ */
+static void supdrvNtProtectUnprotectDeadProcess(HANDLE hDeadPid)
+{
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hDeadPid);
+ if (pNtProtect)
+ {
+ PSUPDRVNTPROTECT pNtChild = NULL;
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /*
+ * If this is an unconfirmed VM process, we must release the reference
+ * the parent structure holds.
+ */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ PSUPDRVNTPROTECT pNtParent = pNtProtect->u.pParent;
+ AssertRelease(pNtParent); AssertRelease(pNtParent->u.pChild == pNtProtect);
+ pNtParent->u.pChild = NULL;
+ pNtProtect->u.pParent = NULL;
+ pNtChild = pNtProtect;
+ }
+ /*
+ * If this is a stub exitting before the VM process gets confirmed,
+ * release the protection of the potential VM process as this is not
+ * the prescribed behavior.
+ */
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtProtect->u.pChild)
+ {
+ pNtChild = pNtProtect->u.pChild;
+ pNtProtect->u.pChild = NULL;
+ pNtChild->u.pParent = NULL;
+ pNtChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /*
+ * Transition it to the dead state to prevent it from opening the
+ * support driver again or be posthumously abused as a vm process parent.
+ */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessConfirmed)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ else if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubSpawning
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_StubDead;
+
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ supdrvNtProtectRelease(pNtProtect);
+ supdrvNtProtectRelease(pNtChild);
+
+ /*
+ * Do session cleanups.
+ */
+ AssertReturnVoid((HANDLE)(uintptr_t)RTProcSelf() == hDeadPid);
+ if (g_pDevObjSys)
+ {
+ PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)g_pDevObjSys->DeviceExtension;
+ PSUPDRVSESSION pSession = supdrvSessionHashTabLookup(pDevExt, (RTPROCESS)(uintptr_t)hDeadPid,
+ RTR0ProcHandleSelf(), NULL);
+ if (pSession)
+ {
+ supdrvSessionHashTabRemove(pDevExt, pSession, NULL);
+ supdrvSessionRelease(pSession); /* Drops the reference from supdrvSessionHashTabLookup. */
+ }
+ }
+ }
+}
+
+
+/**
+ * Common worker for the process creation callback that verifies a new child
+ * being created by the handle creation callback code.
+ *
+ * @param pNtStub The parent.
+ * @param pNtVm The child.
+ * @param fCallerChecks The result of any additional tests the caller made.
+ * This is in order to avoid duplicating the failure
+ * path code.
+ */
+static void supdrvNtProtectVerifyNewChildProtection(PSUPDRVNTPROTECT pNtStub, PSUPDRVNTPROTECT pNtVm, bool fCallerChecks)
+{
+ if ( fCallerChecks
+ && pNtStub->enmProcessKind == kSupDrvNtProtectKind_StubParent
+ && pNtVm->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtVm->u.pParent == pNtStub
+ && pNtStub->u.pChild == pNtVm)
+ {
+ /* Fine, nothing to do. */
+ return;
+ }
+
+ LogRel(("vboxdrv: Misdetected vm stub; hParentPid=%p hChildPid=%p\n", pNtStub->AvlCore.Key, pNtVm->AvlCore.Key));
+ if (pNtStub->enmProcessKind != kSupDrvNtProtectKind_VmProcessConfirmed)
+ supdrvNtProtectUnprotectDeadProcess(pNtVm->AvlCore.Key);
+}
+
+
+/**
+ * Old style callback (since forever).
+ *
+ * @param hParentPid The parent PID.
+ * @param hNewPid The PID of the new child.
+ * @param fCreated TRUE if it's a creation notification,
+ * FALSE if termination.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotify(HANDLE hParentPid, HANDLE hNewPid, BOOLEAN fCreated)
+{
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (fCreated)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ else
+ {
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm, true);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+}
+
+
+/**
+ * New style callback (Vista SP1+ / w2k8).
+ *
+ * @param pNewProcess The new process.
+ * @param hNewPid The PID of the new process.
+ * @param pInfo Process creation details. NULL if process
+ * termination notification.
+ * @remarks ASSUMES this arrives before the handle creation callback.
+ */
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessCreateNotifyEx(PEPROCESS pNewProcess, HANDLE hNewPid, PPS_CREATE_NOTIFY_INFO pInfo)
+{
+ /*
+ * Is it a new process that needs protection?
+ */
+ if (pInfo)
+ {
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(pInfo->CreatingThreadId.UniqueProcess);
+
+ Log(("vboxdrv/NewProcessEx: ctx=%04zx/%p pid=%04zx ppid=%04zx ctor=%04zx/%04zx rcNt=%#x %.*ls\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ hNewPid, pInfo->ParentProcessId,
+ pInfo->CreatingThreadId.UniqueProcess, pInfo->CreatingThreadId.UniqueThread, pInfo->CreationStatus,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? (size_t)pInfo->ImageFileName->Length / 2 : 0,
+ pInfo->FileOpenNameAvailable && pInfo->ImageFileName ? pInfo->ImageFileName->Buffer : NULL));
+
+ if (pNtStub)
+ {
+ PSUPDRVNTPROTECT pNtVm = supdrvNtProtectLookup(hNewPid);
+ if (!pNtVm)
+ {
+ /* Parent must be creator. */
+ if (pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, pInfo->ParentProcessId, hNewPid))
+ supdrvNtProtectProtectNewStubChild(pNtStub, hNewPid);
+ }
+ }
+ else
+ {
+ /* Parent must be creator (as above). */
+ supdrvNtProtectVerifyNewChildProtection(pNtStub, pNtVm,
+ pInfo->CreatingThreadId.UniqueProcess == pInfo->ParentProcessId);
+ supdrvNtProtectRelease(pNtVm);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ /*
+ * Process termination, do clean ups.
+ */
+ else
+ supdrvNtProtectUnprotectDeadProcess(hNewPid);
+}
+
+/** @} */
+
+
+/** @name Process Handle Callbacks.
+ * @{ */
+
+/** Process rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOW_PROCESS_RIGHTS \
+ ( PROCESS_TERMINATE \
+ | PROCESS_VM_READ \
+ | PROCESS_QUERY_INFORMATION \
+ | PROCESS_QUERY_LIMITED_INFORMATION \
+ | PROCESS_SUSPEND_RESUME \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+
+/** Evil process rights. */
+# define SUPDRV_NT_EVIL_PROCESS_RIGHTS \
+ ( PROCESS_CREATE_THREAD \
+ | PROCESS_SET_SESSIONID /*?*/ \
+ | PROCESS_VM_OPERATION \
+ | PROCESS_VM_WRITE \
+ | PROCESS_DUP_HANDLE \
+ | PROCESS_CREATE_PROCESS /*?*/ \
+ | PROCESS_SET_QUOTA /*?*/ \
+ | PROCESS_SET_INFORMATION \
+ | PROCESS_SET_LIMITED_INFORMATION /*?*/ \
+ | 0)
+AssertCompile((SUPDRV_NT_ALLOW_PROCESS_RIGHTS & SUPDRV_NT_EVIL_PROCESS_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ProcessHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ /*
+ * Protected? Kludge required for NtOpenProcess calls comming in before
+ * the create process hook triggers on Windows 8.1 (possibly others too).
+ */
+ HANDLE hObjPid = PsGetProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(hObjPid);
+ if (!pNtProtect)
+ {
+ HANDLE hParentPid = PsGetProcessInheritedFromUniqueProcessId((PEPROCESS)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtStub = supdrvNtProtectLookup(hParentPid);
+ if (pNtStub)
+ {
+ if (supdrvNtProtectIsSpawningStubProcess(pNtStub, hParentPid, hObjPid))
+ {
+ supdrvNtProtectProtectNewStubChild(pNtStub, hObjPid);
+ pNtProtect = supdrvNtProtectLookup(hObjPid);
+ }
+ supdrvNtProtectRelease(pNtStub);
+ }
+ }
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ /*
+ * Ok, it's a protected process. Strip rights as required or possible.
+ */
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOW_PROCESS_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing itself. */
+ if ((PEPROCESS)pOpInfo->Object == PsGetCurrentProcess())
+ {
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstProcessCreateHandle = false;
+
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ }
+ else
+ {
+ /* Special case 1 on Vista, 7 & 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here.
+
+ Special case 2 on 8.1:
+ The CreateProcess code requires additional rights for
+ something, we'll drop these in the stub code. */
+ if ( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstProcessCreateHandle
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess()))
+ {
+ if ( !pOpInfo->KernelHandle
+ && pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ if (g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3))
+ fAllowedRights |= s_fCsrssStupidDesires;
+ else
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_SET_INFORMATION
+ | PROCESS_SET_LIMITED_INFORMATION
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstProcessCreateHandle = false;
+ }
+
+ /* Special case 3 on 8.1:
+ The interaction between the CreateProcess code and CSRSS.EXE
+ has changed to the better with Windows 8.1. CSRSS.EXE no
+ longer duplicates the process (thread too) handle, but opens
+ it, thus allowing us to do our job. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fCsrssFirstProcessCreateHandle
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess())
+ )
+ {
+ pNtProtect->fCsrssFirstProcessCreateHandle = false;
+ if (pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for CreateProcess/VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ /* Special case 4, Windows 7, Vista, possibly 8, but not 8.1:
+ The Themes service requires PROCESS_DUP_HANDLE access to our
+ process or we won't get any menus and dialogs will be half
+ unreadable. This is _very_ unfortunate and more work will
+ go into making this more secure. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == 0x1478 /* 6.1.7600.16385 (win7_rtm.090713-1255) */
+ && pNtProtect->fThemesFirstProcessCreateHandle
+ && supdrvNtProtectIsFrigginThemesService(pNtProtect, PsGetCurrentProcess()))
+ {
+ pNtProtect->fThemesFirstProcessCreateHandle = true; /* Only once! */
+ fAllowedRights |= PROCESS_DUP_HANDLE;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p wants %#x to %p/pid=%04zx [%d], allow %#x => %#x; %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess())));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing itself. */
+ if ( (PEPROCESS)pOpInfo->Object == PsGetCurrentProcess()
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pOpInfo->Object)
+ {
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ /* Special case 5 on Vista, 7 & 8:
+ This is the CSRSS.EXE end of special case #1. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fCsrssFirstProcessDuplicateHandle
+ && pNtProtect->hParentPid
+ == PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess())
+ )
+ {
+ pNtProtect->fCsrssFirstProcessDuplicateHandle = false;
+ if (pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ /* Not needed: PROCESS_CREATE_THREAD, PROCESS_SET_SESSIONID,
+ PROCESS_CREATE_PROCESS, PROCESS_DUP_HANDLE */
+ fAllowedRights = fAllowedRights
+ | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE
+ | PROCESS_DUP_HANDLE /* Needed for launching VBoxTestOGL. */
+ | 0;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ }
+
+ Log(("vboxdrv/ProcessHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ProcessHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsProcessType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE
+ ? pOpInfo->Parameters->CreateHandleInformation.GrantedAccess
+ : pOpInfo->Parameters->DuplicateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | PROCESS_UNKNOWN_4000 /* Seen set on win 8.1 */
+ /*| PROCESS_UNKNOWN_8000 */ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOW_PROCESS_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOW_PROCESS_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOW_PROCESS_RIGHTS
+
+/** @} */
+
+
+/** @name Thread Handle Callbacks
+ * @{ */
+
+/* From ntifs.h */
+extern "C" NTKERNELAPI PEPROCESS __stdcall IoThreadToProcess(PETHREAD);
+
+/** Thread rights that we allow for handles to stub and VM processes. */
+# define SUPDRV_NT_ALLOWED_THREAD_RIGHTS \
+ ( THREAD_TERMINATE \
+ | THREAD_GET_CONTEXT \
+ | THREAD_QUERY_INFORMATION \
+ | THREAD_QUERY_LIMITED_INFORMATION \
+ | DELETE \
+ | READ_CONTROL \
+ | SYNCHRONIZE)
+/** @todo consider THREAD_SET_LIMITED_INFORMATION & THREAD_RESUME */
+
+/** Evil thread rights.
+ * @remarks THREAD_RESUME is not included as it seems to be forced upon us by
+ * Windows 8.1, at least for some processes. We dont' actively
+ * allow it though, just tollerate it when forced to. */
+# define SUPDRV_NT_EVIL_THREAD_RIGHTS \
+ ( THREAD_SUSPEND_RESUME \
+ | THREAD_SET_CONTEXT \
+ | THREAD_SET_INFORMATION \
+ | THREAD_SET_LIMITED_INFORMATION /*?*/ \
+ | THREAD_SET_THREAD_TOKEN /*?*/ \
+ | THREAD_IMPERSONATE /*?*/ \
+ | THREAD_DIRECT_IMPERSONATION /*?*/ \
+ /*| THREAD_RESUME - see remarks. */ \
+ | 0)
+AssertCompile((SUPDRV_NT_EVIL_THREAD_RIGHTS & SUPDRV_NT_ALLOWED_THREAD_RIGHTS) == 0);
+
+
+static OB_PREOP_CALLBACK_STATUS __stdcall
+supdrvNtProtectCallback_ThreadHandlePre(PVOID pvUser, POB_PRE_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ PEPROCESS pProcess = IoThreadToProcess((PETHREAD)pOpInfo->Object);
+ PSUPDRVNTPROTECT pNtProtect = supdrvNtProtectLookup(PsGetProcessId(pProcess));
+ pOpInfo->CallContext = pNtProtect; /* Just for reference. */
+ if (pNtProtect)
+ {
+ static ACCESS_MASK const s_fCsrssStupidDesires = 0x1fffff;
+ ACCESS_MASK fAllowedRights = SUPDRV_NT_ALLOWED_THREAD_RIGHTS;
+
+ if (pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE)
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if (pProcess == PsGetCurrentProcess())
+ {
+ Log(("vboxdrv/ThreadHandlePre: ctx=%04zx/%p wants %#x to %p in pid=%04zx [%d] self\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+ else
+ {
+ /* Special case 1 on Vista, 7, 8:
+ The CreateProcess code passes the handle over to CSRSS.EXE
+ and the code inBaseSrvCreateProcess will duplicate the
+ handle with 0x1fffff as access mask. NtDuplicateObject will
+ fail this call before it ever gets down here. */
+ if ( g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed
+ && pNtProtect->fFirstThreadCreateHandle
+ && pNtProtect->hParentPid == PsGetProcessId(PsGetCurrentProcess()))
+ {
+ if ( !pOpInfo->KernelHandle
+ && pOpInfo->Parameters->CreateHandleInformation.DesiredAccess == s_fCsrssStupidDesires)
+ {
+ fAllowedRights |= s_fCsrssStupidDesires;
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+ pNtProtect->fFirstThreadCreateHandle = false;
+ }
+
+ /* Special case 2 on 8.1, possibly also Vista, 7, 8:
+ When creating a process like VBoxTestOGL from the VM process,
+ CSRSS.EXE will try talk to the calling thread and, it
+ appears, impersonate it. We unfortunately need to allow
+ this or there will be no 3D support. Typical DbgPrint:
+ "SXS: BasepCreateActCtx() Calling csrss server failed. Status = 0xc00000a5" */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: ctx=%04zx/%p wants %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess())));
+
+ pOpInfo->Parameters->CreateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+ else
+ {
+ /* Don't restrict the process accessing its own threads. */
+ if ( pProcess == PsGetCurrentProcess()
+ && (PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == pProcess)
+ {
+ Log(("vboxdrv/ThreadHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d] self\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+ pOpInfo->CallContext = NULL; /* don't assert */
+ }
+ else
+ {
+ /* Special case 3 on Vista, 7, 8:
+ This is the follow up to special case 1. */
+ SUPDRVNTPROTECTKIND enmProcessKind;
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 0, 0, 0)
+ && ( (enmProcessKind = pNtProtect->enmProcessKind) == kSupDrvNtProtectKind_VmProcessConfirmed
+ || enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ && pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess == PsGetCurrentProcess()
+ && supdrvNtProtectIsAssociatedCsrss(pNtProtect, PsGetCurrentProcess()) )
+ {
+ fAllowedRights |= THREAD_IMPERSONATE;
+ fAllowedRights |= THREAD_DIRECT_IMPERSONATION;
+ //fAllowedRights |= THREAD_SET_LIMITED_INFORMATION; - try without this one
+ pOpInfo->CallContext = NULL; /* don't assert this. */
+ }
+
+ Log(("vboxdrv/ThreadHandlePre: ctx=%04zx/%p[%p] dup from %04zx/%p with %#x to %p in pid=%04zx [%d], allow %#x => %#x; %s\n",
+ PsGetProcessId(PsGetCurrentProcess()), PsGetCurrentProcess(),
+ pOpInfo->Parameters->DuplicateHandleInformation.TargetProcess,
+ PsGetProcessId((PEPROCESS)pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess),
+ pOpInfo->Parameters->DuplicateHandleInformation.SourceProcess,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess,
+ pOpInfo->Object, pNtProtect->AvlCore.Key, pNtProtect->enmProcessKind, fAllowedRights,
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess & fAllowedRights,
+ PsGetProcessImageFileName(PsGetCurrentProcess()) ));
+
+ pOpInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= fAllowedRights;
+ }
+ }
+
+ supdrvNtProtectRelease(pNtProtect);
+ }
+
+ return OB_PREOP_SUCCESS;
+}
+
+
+static VOID __stdcall
+supdrvNtProtectCallback_ThreadHandlePost(PVOID pvUser, POB_POST_OPERATION_INFORMATION pOpInfo)
+{
+ Assert(pvUser == NULL);
+ Assert(pOpInfo->Operation == OB_OPERATION_HANDLE_CREATE || pOpInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE);
+ Assert(pOpInfo->ObjectType == *PsThreadType);
+
+ if ( pOpInfo->CallContext
+ && NT_SUCCESS(pOpInfo->ReturnStatus))
+ {
+ ACCESS_MASK const fGrantedAccess = pOpInfo->Parameters->CreateHandleInformation.GrantedAccess;
+ AssertReleaseMsg( !(fGrantedAccess & ~( SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+ | WRITE_OWNER | WRITE_DAC /* these two might be forced upon us */
+ | THREAD_RESUME /* This seems to be force upon us too with 8.1. */
+ ) )
+ || pOpInfo->KernelHandle,
+ ("GrantedAccess=%#x - we allow %#x - we did not allow %#x\n",
+ fGrantedAccess, SUPDRV_NT_ALLOWED_THREAD_RIGHTS, fGrantedAccess & ~SUPDRV_NT_ALLOWED_THREAD_RIGHTS));
+ }
+}
+
+# undef SUPDRV_NT_ALLOWED_THREAD_RIGHTS
+
+/** @} */
+
+
+/**
+ * Creates a new process protection structure.
+ *
+ * @returns VBox status code.
+ * @param ppNtProtect Where to return the pointer to the structure
+ * on success.
+ * @param hPid The process ID of the process to protect.
+ * @param enmProcessKind The kind of process we're protecting.
+ * @param fLink Whether to link the structure into the tree.
+ */
+static int supdrvNtProtectCreate(PSUPDRVNTPROTECT *ppNtProtect, HANDLE hPid, SUPDRVNTPROTECTKIND enmProcessKind, bool fLink)
+{
+ AssertReturn(g_hNtProtectLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+
+ PSUPDRVNTPROTECT pNtProtect = (PSUPDRVNTPROTECT)RTMemAllocZ(sizeof(*pNtProtect));
+ if (!pNtProtect)
+ return VERR_NO_MEMORY;
+
+ pNtProtect->AvlCore.Key = hPid;
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC;
+ pNtProtect->cRefs = 1;
+ pNtProtect->enmProcessKind = enmProcessKind;
+ pNtProtect->hParentPid = NULL;
+ pNtProtect->hCsrssPid = NULL;
+ pNtProtect->pCsrssProcess = NULL;
+
+ if (fLink)
+ {
+ RTSpinlockAcquire(g_hNtProtectLock);
+ bool fSuccess = RTAvlPVInsert(&g_NtProtectTree, &pNtProtect->AvlCore);
+ RTSpinlockRelease(g_hNtProtectLock);
+
+ if (!fSuccess)
+ {
+ /* Duplicate entry, fail. */
+ pNtProtect->u32Magic = SUPDRVNTPROTECT_MAGIC_DEAD;
+ RTMemFree(pNtProtect);
+ return VERR_ACCESS_DENIED;
+ }
+ }
+
+ *ppNtProtect = pNtProtect;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a reference to a NT protection structure.
+ *
+ * @param pNtProtect The NT protection structure.
+ */
+static void supdrvNtProtectRelease(PSUPDRVNTPROTECT pNtProtect)
+{
+ if (!pNtProtect)
+ return;
+ AssertReturnVoid(pNtProtect->u32Magic == SUPDRVNTPROTECT_MAGIC);
+
+ RTSpinlockAcquire(g_hNtProtectLock);
+ uint32_t cRefs = ASMAtomicDecU32(&pNtProtect->cRefs);
+ if (cRefs != 0)
+ RTSpinlockRelease(g_hNtProtectLock);
+ else
+ {
+ /*
+ * That was the last reference. Remove it from the tree, invalidate it
+ * and free the resources associated with it. Also, release any
+ * child/parent references related to this protection structure.
+ */
+ ASMAtomicWriteU32(&pNtProtect->u32Magic, SUPDRVNTPROTECT_MAGIC_DEAD);
+ PSUPDRVNTPROTECT pRemoved = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pNtProtect->AvlCore.Key);
+
+ PSUPDRVNTPROTECT pRemovedChild = NULL;
+ PSUPDRVNTPROTECT pChild = NULL;
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubParent)
+ {
+ pChild = pNtProtect->u.pChild;
+ if (pChild)
+ {
+ pNtProtect->u.pChild = NULL;
+ pChild->u.pParent = NULL;
+ pChild->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ uint32_t cChildRefs = ASMAtomicIncU32(&pChild->cRefs);
+ if (!cChildRefs)
+ pRemovedChild = (PSUPDRVNTPROTECT)RTAvlPVRemove(&g_NtProtectTree, pChild->AvlCore.Key);
+ else
+ pChild = NULL;
+ }
+ }
+ else
+ AssertRelease(pNtProtect->enmProcessKind != kSupDrvNtProtectKind_VmProcessUnconfirmed);
+
+ RTSpinlockRelease(g_hNtProtectLock);
+ Assert(pRemoved == pNtProtect);
+ Assert(pRemovedChild == pChild);
+
+ if (pNtProtect->pCsrssProcess)
+ {
+ ObDereferenceObject(pNtProtect->pCsrssProcess);
+ pNtProtect->pCsrssProcess = NULL;
+ }
+
+ RTMemFree(pNtProtect);
+ if (pChild)
+ RTMemFree(pChild);
+ }
+}
+
+
+/**
+ * Looks up a PID in the NT protect tree.
+ *
+ * @returns Pointer to a NT protection structure (with a referenced) on success,
+ * NULL if not found.
+ * @param hPid The process ID.
+ */
+static PSUPDRVNTPROTECT supdrvNtProtectLookup(HANDLE hPid)
+{
+ RTSpinlockAcquire(g_hNtProtectLock);
+ PSUPDRVNTPROTECT pFound = (PSUPDRVNTPROTECT)RTAvlPVGet(&g_NtProtectTree, hPid);
+ if (pFound)
+ ASMAtomicIncU32(&pFound->cRefs);
+ RTSpinlockRelease(g_hNtProtectLock);
+ return pFound;
+}
+
+
+/**
+ * Worker for supdrvNtProtectVerifyProcess that verifies the handles to a VM
+ * process and its thread.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure for getting information
+ * about special processes.
+ */
+static int supdrvNtProtectRestrictHandlesToProcessAndThread(PSUPDRVNTPROTECT pNtProtect)
+{
+ /*
+ * What to protect.
+ */
+ PEPROCESS pProtectedProcess = PsGetCurrentProcess();
+ HANDLE hProtectedPid = PsGetProcessId(pProtectedProcess);
+ PETHREAD pProtectedThread = PsGetCurrentThread();
+ AssertReturn(pNtProtect->AvlCore.Key == hProtectedPid, VERR_INTERNAL_ERROR_5);
+
+ /*
+ * Take a snapshot of all the handles in the system.
+ */
+ uint32_t cbBuf = _256K;
+ uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ ULONG cbNeeded = cbBuf;
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ if (!NT_SUCCESS(rcNt))
+ {
+ while ( rcNt == STATUS_INFO_LENGTH_MISMATCH
+ && cbNeeded > cbBuf
+ && cbBuf <= 32U*_1M)
+ {
+ cbBuf = RT_ALIGN_32(cbNeeded + _4K, _64K);
+ RTMemFree(pbBuf);
+ pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ if (!pbBuf)
+ return VERR_NO_MEMORY;
+ rcNt = NtQuerySystemInformation(SystemExtendedHandleInformation, pbBuf, cbBuf, &cbNeeded);
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMemFree(pbBuf);
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+ }
+
+ /*
+ * Walk the information and look for handles to the two objects we're protecting.
+ */
+ int rc = VINF_SUCCESS;
+
+ uint32_t cCsrssProcessHandles = 0;
+ uint32_t cSystemProcessHandles = 0;
+ uint32_t cEvilProcessHandles = 0;
+ uint32_t cBenignProcessHandles = 0;
+
+ uint32_t cCsrssThreadHandles = 0;
+ uint32_t cEvilThreadHandles = 0;
+ uint32_t cBenignThreadHandles = 0;
+
+ SYSTEM_HANDLE_INFORMATION_EX const *pInfo = (SYSTEM_HANDLE_INFORMATION_EX const *)pbBuf;
+ ULONG_PTR i = pInfo->NumberOfHandles;
+ AssertRelease(RT_OFFSETOF(SYSTEM_HANDLE_INFORMATION_EX, Handles[i]) == cbNeeded);
+ while (i-- > 0)
+ {
+ const char *pszType;
+ SYSTEM_HANDLE_ENTRY_INFO_EX const *pHandleInfo = &pInfo->Handles[i];
+ if (pHandleInfo->Object == pProtectedProcess)
+ {
+ /* Handles within the protected process is fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_PROCESS_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignProcessHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil process handle.
+ See the special cases in the hook code. */
+ if ( cCsrssProcessHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssProcessHandles++;
+ continue;
+ }
+
+ /* The system process is allowed having one open process handle in
+ Windows 8.1 and later. */
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 3)
+ && cSystemProcessHandles < 1
+ && pHandleInfo->UniqueProcessId == PsGetProcessId(PsInitialSystemProcess))
+ {
+ cSystemProcessHandles++;
+ continue;
+ }
+
+ cEvilProcessHandles++;
+ pszType = "process";
+ }
+ else if (pHandleInfo->Object == pProtectedThread)
+ {
+ /* Handles within the protected process is fine. */
+ if ( !(pHandleInfo->GrantedAccess & SUPDRV_NT_EVIL_THREAD_RIGHTS)
+ || pHandleInfo->UniqueProcessId == hProtectedPid)
+ {
+ cBenignThreadHandles++;
+ continue;
+ }
+
+ /* CSRSS is allowed to have one evil handle to the primary thread
+ for LPC purposes. See the hook for special case. */
+ if ( cCsrssThreadHandles < 1
+ && pHandleInfo->UniqueProcessId == pNtProtect->hCsrssPid)
+ {
+ cCsrssThreadHandles++;
+ continue;
+ }
+
+ cEvilThreadHandles++;
+ pszType = "thread";
+ }
+ else
+ continue;
+
+ /* Found evil handle. Currently ignoring on pre-Vista. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if ( g_uNtVerCombined >= SUP_NT_VER_VISTA
+# else
+ if ( g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0)
+# endif
+ || g_pfnObRegisterCallbacks)
+ {
+ LogRel(("vboxdrv: Found evil handle to budding VM process: pid=%p h=%p acc=%#x attr=%#x type=%s\n",
+ pHandleInfo->UniqueProcessId, pHandleInfo->HandleValue,
+ pHandleInfo->GrantedAccess, pHandleInfo->HandleAttributes, pszType));
+ rc = VERR_SUPDRV_HARDENING_EVIL_HANDLE;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Checks if the current process checks out as a VM process stub.
+ *
+ * @returns VBox status code.
+ * @param pNtProtect The NT protect structure. This is upgraded to a
+ * final protection kind (state) on success.
+ */
+static int supdrvNtProtectVerifyProcess(PSUPDRVNTPROTECT pNtProtect)
+{
+ AssertReturn(PsGetProcessId(PsGetCurrentProcess()) == pNtProtect->AvlCore.Key, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Do the verification. The handle restriction checks are only preformed
+ * on VM processes.
+ */
+ int rc = VINF_SUCCESS;
+ if (pNtProtect->enmProcessKind >= kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ rc = supdrvNtProtectRestrictHandlesToProcessAndThread(pNtProtect);
+ if (RT_SUCCESS(rc))
+ {
+ char szErr[256];
+ RT_ZERO(szErr);
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, szErr, sizeof(szErr));
+
+ rc = supHardenedWinVerifyProcess(NtCurrentProcess(), NtCurrentThread(), &ErrInfo);
+ if (RT_FAILURE(rc))
+ RTLogWriteDebugger(szErr, strlen(szErr));
+ }
+
+ /*
+ * Upgrade and return.
+ */
+ RTSpinlockAcquire(g_hNtProtectLock);
+
+ /* Stub process verficiation is pretty much straight forward. */
+ if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubUnverified)
+ pNtProtect->enmProcessKind = RT_SUCCESS(rc) ? kSupDrvNtProtectKind_StubSpawning : kSupDrvNtProtectKind_StubDead;
+
+ /* The VM process verification is a little bit more complicated
+ because we need to drop the parent process reference as well. */
+ else if (pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessUnconfirmed)
+ {
+ AssertRelease(pNtProtect->cRefs >= 2); /* Parent + Caller */
+ PSUPDRVNTPROTECT pParent = pNtProtect->u.pParent;
+ AssertRelease(pParent);
+ AssertRelease(pParent->u.pParent == pNtProtect);
+ AssertRelease(pParent->enmProcessKind == kSupDrvNtProtectKind_StubParent);
+ pParent->u.pParent = NULL;
+
+ pNtProtect->u.pParent = NULL;
+ ASMAtomicDecU32(&pNtProtect->cRefs);
+
+ if (RT_SUCCESS(rc))
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessConfirmed;
+ else
+ pNtProtect->enmProcessKind = kSupDrvNtProtectKind_VmProcessDead;
+ }
+
+ /* Since the stub and VM processes are only supposed to have one thread,
+ we're not supposed to be subject to any races from within the processes.
+
+ There is a race between VM process verification and the stub process
+ exiting, though. We require the stub process to be alive until the new
+ VM process has made it thru the validation. So, when the stub
+ terminates the notification handler will change the state of both stub
+ and VM process to dead.
+
+ Also, I'm not entirely certain where the process
+ termination notification is triggered from, so that can theorically
+ create a race in both cases. */
+ else
+ {
+ AssertReleaseMsg( pNtProtect->enmProcessKind == kSupDrvNtProtectKind_StubDead
+ || pNtProtect->enmProcessKind == kSupDrvNtProtectKind_VmProcessDead,
+ ("enmProcessKind=%d rc=%Rrc\n", pNtProtect->enmProcessKind, rc));
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE; /* There should be no races here. */
+ }
+
+ RTSpinlockRelease(g_hNtProtectLock);
+ return rc;
+}
+
+
+# ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+
+/**
+ * Checks if the current process is being debugged.
+ * @return @c true if debugged, @c false if not.
+ */
+static bool supdrvNtIsDebuggerAttached(void)
+{
+ return PsIsProcessBeingDebugged(PsGetCurrentProcess()) != FALSE;
+}
+
+# endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/**
+ * Terminates the hardening bits.
+ */
+static void supdrvNtProtectTerm(void)
+{
+ /*
+ * Stop intercepting process and thread handle creation calls.
+ */
+ if (g_pvObCallbacksCookie)
+ {
+ g_pfnObUnRegisterCallbacks(g_pvObCallbacksCookie);
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Stop intercepting process creation and termination notifications.
+ */
+ NTSTATUS rcNt;
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ AssertMsg(NT_SUCCESS(rcNt), ("rcNt=%#x\n", rcNt));
+
+ Assert(g_NtProtectTree == NULL);
+
+ /*
+ * Clean up globals.
+ */
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+
+ supHardenedWinTermImageVerifier();
+}
+
+# ifdef RT_ARCH_X86
+DECLASM(void) supdrvNtQueryVirtualMemory_0xAF(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB0(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB1(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB2(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB3(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB4(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB5(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB6(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB7(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB8(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xB9(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBA(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBB(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBC(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBD(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0xBE(void);
+# elif defined(RT_ARCH_AMD64)
+DECLASM(void) supdrvNtQueryVirtualMemory_0x1F(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x20(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x21(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x22(void);
+DECLASM(void) supdrvNtQueryVirtualMemory_0x23(void);
+extern "C" NTSYSAPI NTSTATUS NTAPI ZwRequestWaitReplyPort(HANDLE, PVOID, PVOID);
+# endif
+
+/**
+ * Initalizes the hardening bits.
+ *
+ * @returns NT status code.
+ */
+static NTSTATUS supdrvNtProtectInit(void)
+{
+ /*
+ * Initialize the globals.
+ */
+
+ /* The NT version. */
+ ULONG uMajor, uMinor, uBuild;
+ PsGetVersion(&uMajor, &uMinor, &uBuild, NULL);
+ g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(uMajor, uMinor, uBuild, 0, 0);
+
+ /* Resolve methods we want but isn't available everywhere. */
+ UNICODE_STRING RoutineName;
+
+ RtlInitUnicodeString(&RoutineName, L"ObRegisterCallbacks");
+ g_pfnObRegisterCallbacks = (PFNOBREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ObUnRegisterCallbacks");
+ g_pfnObUnRegisterCallbacks = (PFNOBUNREGISTERCALLBACKS)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsSetCreateProcessNotifyRoutineEx");
+ g_pfnPsSetCreateProcessNotifyRoutineEx = (PFNPSSETCREATEPROCESSNOTIFYROUTINEEX)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsReferenceProcessFilePointer");
+ g_pfnPsReferenceProcessFilePointer = (PFNPSREFERENCEPROCESSFILEPOINTER)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"PsIsProtectedProcessLight");
+ g_pfnPsIsProtectedProcessLight = (PFNPSISPROTECTEDPROCESSLIGHT)MmGetSystemRoutineAddress(&RoutineName);
+
+ RtlInitUnicodeString(&RoutineName, L"ZwQueryVirtualMemory"); /* Yes, using Zw version here. */
+ g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)MmGetSystemRoutineAddress(&RoutineName);
+ if (!g_pfnNtQueryVirtualMemory && g_uNtVerCombined < SUP_NT_VER_VISTA)
+ {
+ /* XP & W2K3 doesn't have this function exported, so we've cooked up a
+ few alternative in the assembly helper file that uses the code in
+ ZwQueryVolumeInformationFile with a different eax value. */
+# ifdef RT_ARCH_X86
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwQueryVolumeInformationFile;
+ if (*pbCode == 0xb8) /* mov eax, dword */
+ switch (*(uint32_t const *)&pbCode[1])
+ {
+ case 0xb0: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xAF; break; /* just in case */
+ case 0xb1: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB0; break; /* just in case */
+ case 0xb2: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB1; break; /* just in case */
+ case 0xb3: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* XP SP3 */
+ case 0xb4: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB2; break; /* just in case */
+ case 0xb5: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB3; break; /* just in case */
+ case 0xb6: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB4; break; /* just in case */
+ case 0xb7: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB5; break; /* just in case */
+ case 0xb8: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB6; break; /* just in case */
+ case 0xb9: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB7; break; /* just in case */
+ case 0xba: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xB8; break; /* just in case */
+ case 0xbb: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBA; break; /* W2K3 R2 SP2 */
+ case 0xbc: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBB; break; /* just in case */
+ case 0xbd: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBC; break; /* just in case */
+ case 0xbe: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBD; break; /* just in case */
+ case 0xbf: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0xBE; break; /* just in case */
+ }
+# elif defined(RT_ARCH_AMD64)
+ uint8_t const *pbCode = (uint8_t const *)(uintptr_t)ZwRequestWaitReplyPort;
+ if ( pbCode[ 0] == 0x48 /* mov rax, rsp */
+ && pbCode[ 1] == 0x8b
+ && pbCode[ 2] == 0xc4
+ && pbCode[ 3] == 0xfa /* cli */
+ && pbCode[ 4] == 0x48 /* sub rsp, 10h */
+ && pbCode[ 5] == 0x83
+ && pbCode[ 6] == 0xec
+ && pbCode[ 7] == 0x10
+ && pbCode[ 8] == 0x50 /* push rax */
+ && pbCode[ 9] == 0x9c /* pushfq */
+ && pbCode[10] == 0x6a /* push 10 */
+ && pbCode[11] == 0x10
+ && pbCode[12] == 0x48 /* lea rax, [nt!KiServiceLinkage] */
+ && pbCode[13] == 0x8d
+ && pbCode[14] == 0x05
+ && pbCode[19] == 0x50 /* push rax */
+ && pbCode[20] == 0xb8 /* mov eax,1fh <- the syscall no. */
+ /*&& pbCode[21] == 0x1f*/
+ && pbCode[22] == 0x00
+ && pbCode[23] == 0x00
+ && pbCode[24] == 0x00
+ && pbCode[25] == 0xe9 /* jmp KiServiceInternal */
+ )
+ {
+ uint8_t const *pbKiServiceInternal = &pbCode[30] + *(int32_t const *)&pbCode[26];
+ uint8_t const *pbKiServiceLinkage = &pbCode[19] + *(int32_t const *)&pbCode[15];
+ if (*pbKiServiceLinkage == 0xc3)
+ {
+ g_pfnKiServiceInternal = (PFNRT)pbKiServiceInternal;
+ g_pfnKiServiceLinkage = (PFNRT)pbKiServiceLinkage;
+ switch (pbCode[21])
+ {
+ case 0x1e: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x1F; break;
+ case 0x1f: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x20; break;
+ case 0x20: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x21; break;
+ case 0x21: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x22; break;
+ case 0x22: g_pfnNtQueryVirtualMemory = (PFNNTQUERYVIRTUALMEMORY)supdrvNtQueryVirtualMemory_0x23; break;
+ }
+ }
+ }
+# endif
+ }
+ if (!g_pfnNtQueryVirtualMemory)
+ {
+ LogRel(("vboxdrv: Cannot locate ZwQueryVirtualMemory in ntoskrnl, nor were we able to cook up a replacement.\n"));
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+
+ /* The spinlock protecting our structures. */
+ int rc = RTSpinlockCreate(&g_hNtProtectLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "NtProtectLock");
+ if (RT_FAILURE(rc))
+ return VBoxDrvNtErr2NtStatus(rc);
+ g_NtProtectTree = NULL;
+
+ /* Image stuff + certificates. */
+ NTSTATUS rcNt;
+ rc = supHardenedWinInitImageVerifier(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Intercept process creation and termination.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ rcNt = g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, FALSE /*fRemove*/);
+ else
+ rcNt = PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, FALSE /*fRemove*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Intercept process and thread handle creation calls.
+ * The preferred method is only available on Vista SP1+.
+ */
+ if (g_pfnObRegisterCallbacks && g_pfnObUnRegisterCallbacks)
+ {
+ static OB_OPERATION_REGISTRATION s_aObOperations[] =
+ {
+ {
+ PsProcessType,
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ProcessHandlePre,
+ supdrvNtProtectCallback_ProcessHandlePost,
+ },
+ {
+ PsThreadType,
+ OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
+ supdrvNtProtectCallback_ThreadHandlePre,
+ supdrvNtProtectCallback_ThreadHandlePost,
+ },
+ };
+ static OB_CALLBACK_REGISTRATION s_ObCallbackReg =
+ {
+ /* .Version = */ OB_FLT_REGISTRATION_VERSION,
+ /* .OperationRegistrationCount = */ RT_ELEMENTS(s_aObOperations),
+ /* .Altitude.Length = */ 0,
+ /* .Altitude.MaximumLength = */ 0,
+ /* .Altitude.Buffer = */ NULL,
+ /* .RegistrationContext = */ NULL,
+ /* .OperationRegistration = */ &s_aObOperations[0]
+ };
+ static WCHAR const *s_apwszAltitudes[] = /** @todo get a valid number */
+ {
+ L"48596.98940", L"46935.19485", L"49739.39704", L"40334.74976",
+ L"66667.98940", L"69888.19485", L"69889.39704", L"60364.74976",
+ L"85780.98940", L"88978.19485", L"89939.39704", L"80320.74976",
+ L"329879.98940", L"326787.19485", L"328915.39704", L"320314.74976",
+ };
+
+ rcNt = STATUS_FLT_INSTANCE_ALTITUDE_COLLISION;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszAltitudes) && rcNt == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION; i++)
+ {
+ s_ObCallbackReg.Altitude.Buffer = (WCHAR *)s_apwszAltitudes[i];
+ s_ObCallbackReg.Altitude.Length = (uint16_t)RTUtf16Len(s_apwszAltitudes[i]) * sizeof(WCHAR);
+ s_ObCallbackReg.Altitude.MaximumLength = s_ObCallbackReg.Altitude.Length + sizeof(WCHAR);
+
+ rcNt = g_pfnObRegisterCallbacks(&s_ObCallbackReg, &g_pvObCallbacksCookie);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Happy ending.
+ */
+ return STATUS_SUCCESS;
+ }
+ }
+ LogRel(("vboxdrv: ObRegisterCallbacks failed with rcNt=%#x\n", rcNt));
+ g_pvObCallbacksCookie = NULL;
+ }
+ else
+ {
+ /*
+ * For the time being, we do not implement extra process
+ * protection on pre-Vista-SP1 systems as they are lacking
+ * necessary KPIs. XP is end of life, we do not wish to
+ * spend more time on it, so we don't put up a fuss there.
+ * Vista users without SP1 can install SP1 (or later), darn it,
+ * so refuse to load.
+ */
+ /** @todo Hack up an XP solution - will require hooking kernel APIs or doing bad
+ * stuff to a couple of object types. */
+# ifndef VBOX_WITH_VISTA_NO_SP
+ if (g_uNtVerCombined >= SUP_NT_VER_VISTA)
+# else
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_COMBINED(6, 0, 6001, 0, 0))
+# endif
+ {
+ DbgPrint("vboxdrv: ObRegisterCallbacks was not found. Please make sure you got the latest updates and service packs installed\n");
+ rcNt = STATUS_SXS_VERSION_CONFLICT;
+ }
+ else
+ {
+ Log(("vboxdrv: ObRegisterCallbacks was not found; ignored pre-Vista\n"));
+ return rcNt = STATUS_SUCCESS;
+ }
+ g_pvObCallbacksCookie = NULL;
+ }
+
+ /*
+ * Drop process create/term notifications.
+ */
+ if (g_pfnPsSetCreateProcessNotifyRoutineEx)
+ g_pfnPsSetCreateProcessNotifyRoutineEx(supdrvNtProtectCallback_ProcessCreateNotifyEx, TRUE /*fRemove*/);
+ else
+ PsSetCreateProcessNotifyRoutine(supdrvNtProtectCallback_ProcessCreateNotify, TRUE /*fRemove*/);
+ }
+ else
+ LogRel(("vboxdrv: PsSetCreateProcessNotifyRoutine%s failed with rcNt=%#x\n",
+ g_pfnPsSetCreateProcessNotifyRoutineEx ? "Ex" : "", rcNt));
+ supHardenedWinTermImageVerifier();
+ }
+ else
+ rcNt = VBoxDrvNtErr2NtStatus(rc);
+
+ RTSpinlockDestroy(g_hNtProtectLock);
+ g_NtProtectTree = NIL_RTSPINLOCK;
+ return rcNt;
+}
+
+#endif /* VBOX_WITH_HARDENING */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm
index 6c5412a5eb9..a6ae8d63482 100644
--- a/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm
+++ b/src/VBox/HostDrivers/Support/win/SUPDrvA-win.asm
@@ -4,7 +4,7 @@
;
;
-; Copyright (C) 2006-2010 Oracle Corporation
+; Copyright (C) 2006-2014 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
@@ -44,3 +44,76 @@ BEGINPROC SUPR0Printf
ENDPROC SUPR0Printf
%endif
+
+%ifdef RT_ARCH_X86
+;
+; Faking up ZwQueryVirtualMemory on XP and W2K3 where it's not exported.
+; Using ZwQueryVolumeInformationFile as a helper.
+;
+extern IMPNAME(ZwQueryVolumeInformationFile@20)
+
+BEGINPROC supdrvNtQueryVirtualMemory_Xxx
+ %macro NtQueryVirtualMemorySyscall 1
+ GLOBALNAME supdrvNtQueryVirtualMemory_ %+ %1
+ mov eax, %1
+ jmp supdrvNtQueryVirtualMemory_Jump
+ %endm
+ NtQueryVirtualMemorySyscall 0xAF
+ NtQueryVirtualMemorySyscall 0xB0
+ NtQueryVirtualMemorySyscall 0xB1
+ NtQueryVirtualMemorySyscall 0xB2
+ NtQueryVirtualMemorySyscall 0xB3
+ NtQueryVirtualMemorySyscall 0xB4
+ NtQueryVirtualMemorySyscall 0xB5
+ NtQueryVirtualMemorySyscall 0xB6
+ NtQueryVirtualMemorySyscall 0xB7
+ NtQueryVirtualMemorySyscall 0xB8
+ NtQueryVirtualMemorySyscall 0xB9
+ NtQueryVirtualMemorySyscall 0xBA
+ NtQueryVirtualMemorySyscall 0xBB
+ NtQueryVirtualMemorySyscall 0xBC
+ NtQueryVirtualMemorySyscall 0xBD
+ NtQueryVirtualMemorySyscall 0xBE
+
+supdrvNtQueryVirtualMemory_Jump:
+ mov edx, IMP2(ZwQueryVolumeInformationFile@20)
+ lea edx, [edx + 5]
+ jmp edx
+ENDPROC supdrvNtQueryVirtualMemory_Xxx
+
+%endif
+
+%ifdef RT_ARCH_AMD64
+;
+; Faking up ZwQueryVirtualMemory on XP64 and W2K3-64 where it's not exported.
+; The C code locates and verifies the essentials in ZwRequestWaitReplyPort.
+;
+extern NAME(g_pfnKiServiceLinkage)
+extern NAME(g_pfnKiServiceInternal)
+BEGINPROC supdrvNtQueryVirtualMemory_Xxx
+ %macro NtQueryVirtualMemorySyscall 1
+ GLOBALNAME supdrvNtQueryVirtualMemory_ %+ %1
+ mov eax, %1
+ jmp supdrvNtQueryVirtualMemory_Jump
+ %endm
+
+ NtQueryVirtualMemorySyscall 0x1F
+ NtQueryVirtualMemorySyscall 0x20
+ NtQueryVirtualMemorySyscall 0x21
+ NtQueryVirtualMemorySyscall 0x22
+ NtQueryVirtualMemorySyscall 0x23
+
+supdrvNtQueryVirtualMemory_Jump:
+ cli
+ mov r10, rsp ; save call frame pointer.
+ mov r11, [NAME(g_pfnKiServiceLinkage) wrt rip]
+ push 0
+ push 0
+ push r10 ; call frame pointer (incoming rsp).
+ pushfq
+ push 10h
+ push r11 ; r11 = KiServiceLinkage (ret w/ unwind info)
+ jmp qword [NAME(g_pfnKiServiceInternal) wrt rip]
+ENDPROC supdrvNtQueryVirtualMemory_Xxx
+%endif
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h b/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h
new file mode 100644
index 00000000000..7ab444b49e2
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h
@@ -0,0 +1,149 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef ___win_SUPHardenedVerify_win_h
+#define ___win_SUPHardenedVerify_win_h
+
+#include <iprt/types.h>
+#include <iprt/crypto/x509.h>
+
+RT_C_DECLS_BEGIN
+
+#ifndef SUP_CERTIFICATES_ONLY
+# ifdef RT_OS_WINDOWS
+DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo);
+DECLHIDDEN(void) supHardenedWinTermImageVerifier(void);
+DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo);
+
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, bool *pfCacheable, PRTERRINFO pErrInfo);
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo);
+/** @name SUPHNTVI_F_XXX - Flags for supHardenedWinVerifyImageByHandle.
+ * @{ */
+/** The signing certificate must be the same as the one the VirtualBox build
+ * was signed with. */
+# define SUPHNTVI_F_REQUIRE_BUILD_CERT RT_BIT(0)
+/** Require kernel code signing level. */
+# define SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING RT_BIT(1)
+/** Require the image to force the memory mapper to do signature checking. */
+# define SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT RT_BIT(2)
+/** Whether to allow image verification by catalog file. */
+# define SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION RT_BIT(3)
+/** Raw-mode context image, always 32-bit. */
+# define SUPHNTVI_F_RC_IMAGE RT_BIT(31)
+/** @} */
+
+/** Which directory under the system root to get. */
+typedef enum SUPHARDNTSYSROOTDIR
+{
+ kSupHardNtSysRootDir_System32 = 0,
+ kSupHardNtSysRootDir_WinSxS,
+} SUPHARDNTSYSROOTDIR;
+
+DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo);
+
+# ifndef SUPHNTVI_NO_NT_STUFF
+
+/** Typical system root directory buffer. */
+typedef struct SUPSYSROOTDIRBUF
+{
+ UNICODE_STRING UniStr;
+ WCHAR awcBuffer[260];
+} SUPSYSROOTDIRBUF;
+extern SUPSYSROOTDIRBUF g_System32NtPath;
+extern SUPSYSROOTDIRBUF g_WinSxSNtPath;
+extern SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
+extern uint32_t g_offSupLibHardenedExeNtName;
+
+/** Pointer to NtQueryVirtualMemory. */
+typedef NTSTATUS (NTAPI *PFNNTQUERYVIRTUALMEMORY)(HANDLE, void const *, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+extern PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory;
+
+# endif /* SUPHNTVI_NO_NT_STUFF */
+
+/** Creates a combined NT version number for simple comparisons. */
+#define SUP_MAKE_NT_VER_COMBINED(a_uMajor, a_uMinor, a_uBuild, a_uSpMajor, a_uSpMinor) \
+ ( ((uint32_t)((a_uMajor) & UINT32_C(0xf)) << 28) \
+ | ((uint32_t)((a_uMinor) & UINT32_C(0xf)) << 24) \
+ | ((uint32_t)((a_uBuild) & UINT32_C(0xffff)) << 8) \
+ | ((uint32_t)((a_uSpMajor) & UINT32_C(0xf)) << 4) \
+ | RT_MIN((uint32_t)(a_uSpMinor), UINT32_C(0xf)) )
+/** Simple version of SUP_MAKE_NT_VER_COMBINED. */
+#define SUP_MAKE_NT_VER_SIMPLE(a_uMajor, a_uMinor) SUP_MAKE_NT_VER_COMBINED(a_uMajor, a_uMinor, 0, 0, 0)
+extern uint32_t g_uNtVerCombined;
+
+/** Combined NT version number for XP. */
+#define SUP_NT_VER_XP SUP_MAKE_NT_VER_SIMPLE(5,1)
+/** Combined NT version number for Vista. */
+#define SUP_NT_VER_VISTA SUP_MAKE_NT_VER_SIMPLE(6,0)
+/** Combined NT version number for Windows 7. */
+#define SUP_NT_VER_W70 SUP_MAKE_NT_VER_SIMPLE(6,1)
+/** Combined NT version number for Windows 8.0. */
+#define SUP_NT_VER_W80 SUP_MAKE_NT_VER_SIMPLE(6,2)
+/** Combined NT version number for Windows 8.1. */
+#define SUP_NT_VER_W81 SUP_MAKE_NT_VER_SIMPLE(6,3)
+
+# endif
+
+# ifndef IN_SUP_HARDENED_R3
+# include <iprt/mem.h>
+# include <iprt/string.h>
+
+# define suplibHardenedAllocZ RTMemAllocZ
+# define suplibHardenedReAlloc RTMemRealloc
+# define suplibHardenedFree RTMemFree
+# define suplibHardenedMemComp memcmp
+# define suplibHardenedMemCopy memcpy
+# define suplibHardenedMemSet memset
+# define suplibHardenedStrCopy strcpy
+# define suplibHardenedStrLen strlen
+# define suplibHardenedStrCat strcat
+# define suplibHardenedStrCmp strcmp
+# define suplibHardenedStrNCmp strncmp
+# define suplibHardenedStrICmp stricmp
+# else /* IN_SUP_HARDENED_R3 */
+# include <iprt/mem.h>
+#if 0
+# define memcmp suplibHardenedMemComp
+# define memcpy suplibHardenedMemCopy
+# define memset suplibHardenedMemSet
+# define strcpy suplibHardenedStrCopy
+# define strlen suplibHardenedStrLen
+# define strcat suplibHardenedStrCat
+# define strcmp suplibHardenedStrCmp
+# define strncmp suplibHardenedStrNCmp
+# define stricmp suplibHardenedStrICmp
+#endif
+DECLHIDDEN(void *) suplibHardenedAllocZ(size_t cb);
+DECLHIDDEN(void *) suplibHardenedReAlloc(void *pvOld, size_t cbNew);
+DECLHIDDEN(void) suplibHardenedFree(void *pv);
+# endif /* IN_SUP_HARDENED_R3 */
+
+#endif /* SUP_CERTIFICATES_ONLY */
+
+RT_C_DECLS_END
+
+#endif
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp
new file mode 100644
index 00000000000..a66839c4410
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp
@@ -0,0 +1,1873 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Image Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#ifdef IN_RING0
+# define IPRT_NT_MAP_TO_ZW
+# include <iprt/nt/nt.h>
+# include <ntimage.h>
+#else
+# include <iprt/nt/nt-and-windows.h>
+# include "Wintrust.h"
+# include "Softpub.h"
+# include "mscat.h"
+# ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
+# endif
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/crypto/pkcs7.h>
+#include <iprt/crypto/store.h>
+
+#ifdef IN_RING0
+# include "SUPDrvInternal.h"
+#else
+# include "SUPLibInternal.h"
+#endif
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The size of static hash (output) buffers.
+ * Avoids dynamic allocations and cleanups for of small buffers as well as extra
+ * calls for getting the appropriate buffer size. The largest digest in regular
+ * use by current windows version is SHA-512, we double this and hope it's
+ * enough a good while. */
+#define SUPHARDNTVI_MAX_CAT_HASH_SIZE 128
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * SUP image verifier loader reader instance.
+ */
+typedef struct SUPHNTVIRDR
+{
+ /** The core reader structure. */
+ RTLDRREADER Core;
+ /** The file handle . */
+ HANDLE hFile;
+ /** Current file offset. */
+ RTFOFF off;
+ /** The file size. */
+ RTFOFF cbFile;
+ /** Flags for the verification callback, SUPHNTVI_F_XXX. */
+ uint32_t fFlags;
+ /** The executable timstamp in second since unix epoch. */
+ uint64_t uTimestamp;
+ /** Log name. */
+ char szFilename[1];
+} SUPHNTVIRDR;
+/** Pointer to an SUP image verifier loader reader instance. */
+typedef SUPHNTVIRDR *PSUPHNTVIRDR;
+
+
+#ifdef IN_RING3
+typedef LONG (WINAPI * PFNWINVERIFYTRUST)(HWND hwnd, GUID const *pgActionID, PVOID pWVTData);
+typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, DWORD dwFlags);
+typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT2)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, PCWSTR pwszHashAlgorithm,
+ struct _CERT_STRONG_SIGN_PARA const *pStrongHashPolicy, DWORD dwFlags);
+typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE)(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags);
+typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2)(HCATADMIN hCatAdmin, HANDLE hFile, DWORD *pcbHash,
+ BYTE *pbHash, DWORD dwFlags);
+typedef HCATINFO (WINAPI *PFNCRYPTCATADMINENUMCATALOGFROMHASH)(HCATADMIN hCatAdmin, BYTE *pbHash, DWORD cbHash,
+ DWORD dwFlags, HCATINFO *phPrevCatInfo);
+typedef BOOL (WINAPI * PFNCRYPTCATADMINRELEASECATALOGCONTEXT)(HCATADMIN hCatAdmin, HCATINFO hCatInfo, DWORD dwFlags);
+typedef BOOL (WINAPI * PFNCRYPTCATDADMINRELEASECONTEXT)(HCATADMIN hCatAdmin, DWORD dwFlags);
+typedef BOOL (WINAPI * PFNCRYPTCATCATALOGINFOFROMCONTEXT)(HCATINFO hCatInfo, CATALOG_INFO *psCatInfo, DWORD dwFlags);
+
+typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
+ DWORD dwFlags, const void *pvParam);
+typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags);
+typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
+#endif
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The build certificate. */
+static RTCRX509CERTIFICATE g_BuildX509Cert;
+
+/** Store for root software publisher certificates. */
+static RTCRSTORE g_hSpcRootStore = NIL_RTCRSTORE;
+/** Store for root NT kernel certificates. */
+static RTCRSTORE g_hNtKernelRootStore = NIL_RTCRSTORE;
+
+/** Store containing SPC, NT kernel signing, and timestamp root certificates. */
+static RTCRSTORE g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
+/** Store for supplemental certificates for use with
+ * g_hSpcAndNtKernelRootStore. */
+static RTCRSTORE g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
+
+/** The full \\SystemRoot\\System32 path. */
+SUPSYSROOTDIRBUF g_System32NtPath;
+/** The full \\SystemRoot\\WinSxS path. */
+SUPSYSROOTDIRBUF g_WinSxSNtPath;
+
+/** Set after we've retrived other SPC root certificates from the system. */
+static bool g_fHaveOtherRoots = false;
+
+#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED and
+ * SUP_MAKE_NT_VER_SIMPLE. */
+uint32_t g_uNtVerCombined;
+#endif
+
+#ifdef IN_RING3
+/** Pointer to WinVerifyTrust. */
+PFNWINVERIFYTRUST g_pfnWinVerifyTrust;
+/** Pointer to CryptCATAdminAcquireContext. */
+PFNCRYPTCATADMINACQUIRECONTEXT g_pfnCryptCATAdminAcquireContext;
+/** Pointer to CryptCATAdminAcquireContext2 if available. */
+PFNCRYPTCATADMINACQUIRECONTEXT2 g_pfnCryptCATAdminAcquireContext2;
+/** Pointer to CryptCATAdminCalcHashFromFileHandle. */
+PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE g_pfnCryptCATAdminCalcHashFromFileHandle;
+/** Pointer to CryptCATAdminCalcHashFromFileHandle2. */
+PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2 g_pfnCryptCATAdminCalcHashFromFileHandle2;
+/** Pointer to CryptCATAdminEnumCatalogFromHash. */
+PFNCRYPTCATADMINENUMCATALOGFROMHASH g_pfnCryptCATAdminEnumCatalogFromHash;
+/** Pointer to CryptCATAdminReleaseCatalogContext. */
+PFNCRYPTCATADMINRELEASECATALOGCONTEXT g_pfnCryptCATAdminReleaseCatalogContext;
+/** Pointer to CryptCATAdminReleaseContext. */
+PFNCRYPTCATDADMINRELEASECONTEXT g_pfnCryptCATAdminReleaseContext;
+/** Pointer to CryptCATCatalogInfoFromContext. */
+PFNCRYPTCATCATALOGINFOFROMCONTEXT g_pfnCryptCATCatalogInfoFromContext;
+#endif
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+#ifdef IN_RING3
+static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust);
+static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust);
+#endif
+
+
+
+
+/** @copydoc RTLDRREADER::pfnRead */
+static DECLCALLBACK(int) supHardNtViRdrRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ if ((ULONG)cb != cb)
+ return VERR_OUT_OF_RANGE;
+
+
+ /*
+ * For some reason I'm getting occational read error in an XP VM with
+ * STATUS_FAILED_DRIVER_ENTRY. Redoing the call again works in the
+ * debugger, so try do that automatically.
+ */
+ for (uint32_t iTry = 0;; iTry++)
+ {
+ LARGE_INTEGER offNt;
+ offNt.QuadPart = off;
+
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile(pNtViRdr->hFile,
+ NULL /*hEvent*/,
+ NULL /*ApcRoutine*/,
+ NULL /*ApcContext*/,
+ &Ios,
+ pvBuf,
+ (ULONG)cb,
+ &offNt,
+ NULL);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ if (Ios.Information == cb)
+ {
+ pNtViRdr->off = off + cb;
+ return VINF_SUCCESS;
+ }
+#ifdef IN_RING3
+ supR3HardenedError(VERR_READ_ERROR, false,
+ "supHardNtViRdrRead: Only got %#zx bytes when requesting %#zx bytes at %#llx in '%s'.\n",
+ Ios.Information, off, cb, pNtViRdr->szFilename);
+#endif
+ pNtViRdr->off = -1;
+ return VERR_READ_ERROR;
+ }
+
+ /*
+ * Delay a little before we retry?
+ */
+#ifdef IN_RING3
+ if (iTry == 0)
+ NtYieldExecution();
+ else if (iTry >= 1)
+ {
+ LARGE_INTEGER Time;
+ Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
+ NtDelayExecution(TRUE, &Time);
+ }
+#endif
+ /*
+ * Before we give up, we'll try split up the request in case the
+ * kernel is low on memory or similar. For simplicity reasons, we do
+ * this in a recursion fashion.
+ */
+ if (iTry >= 2)
+ {
+ if (cb >= _8K)
+ {
+ size_t const cbBlock = RT_ALIGN_Z(cb / 4, 512);
+ while (cb > 0)
+ {
+ size_t cbThisRead = RT_MIN(cb, cbBlock);
+ int rc = supHardNtViRdrRead(&pNtViRdr->Core, pvBuf, cbThisRead, off);
+ if (RT_FAILURE(rc))
+ return rc;
+ off += cbThisRead;
+ cb -= cbThisRead;
+ pvBuf = (uint8_t *)pvBuf + cbThisRead;
+ }
+ return VINF_SUCCESS;
+ }
+
+#ifdef IN_RING3
+ supR3HardenedError(VERR_READ_ERROR, false, "supHardNtViRdrRead: Error %#x reading %#zx bytes at %#llx in '%s'.\n",
+ rcNt, off, cb, pNtViRdr->szFilename);
+#endif
+ pNtViRdr->off = -1;
+ return VERR_READ_ERROR;
+ }
+ }
+}
+
+
+/** @copydoc RTLDRREADER::pfnTell */
+static DECLCALLBACK(RTFOFF) supHardNtViRdrTell(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ return pNtViRdr->off;
+}
+
+
+/** @copydoc RTLDRREADER::pfnSize */
+static DECLCALLBACK(RTFOFF) supHardNtViRdrSize(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+ return pNtViRdr->cbFile;
+}
+
+
+/** @copydoc RTLDRREADER::pfnLogName */
+static DECLCALLBACK(const char *) supHardNtViRdrLogName(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ return pNtViRdr->szFilename;
+}
+
+
+/** @copydoc RTLDRREADER::pfnMap */
+static DECLCALLBACK(int) supHardNtViRdrMap(PRTLDRREADER pReader, const void **ppvBits)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/** @copydoc RTLDRREADER::pfnUnmap */
+static DECLCALLBACK(int) supHardNtViRdrUnmap(PRTLDRREADER pReader, const void *pvBits)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/** @copydoc RTLDRREADER::pfnDestroy */
+static DECLCALLBACK(int) supHardNtViRdrDestroy(PRTLDRREADER pReader)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ pNtViRdr->Core.uMagic = ~RTLDRREADER_MAGIC;
+ pNtViRdr->hFile = NULL;
+
+ RTMemFree(pNtViRdr);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates a loader reader instance for the given NT file handle.
+ *
+ * @returns iprt status code.
+ * @param hFile Native NT file handle.
+ * @param pwszName Optional file name.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param ppNtViRdr Where to store the reader instance on success.
+ */
+static int supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr)
+{
+ /*
+ * Try determine the size of the file.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_STANDARD_INFORMATION StdInfo;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
+ return VERR_LDRVI_FILE_LENGTH_ERROR;
+
+ /*
+ * Calc the file name length and allocate memory for the reader instance.
+ */
+ size_t cchFilename = 0;
+ if (pwszName)
+ cchFilename = RTUtf16CalcUtf8Len(pwszName);
+
+ int rc = VERR_NO_MEMORY;
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)RTMemAllocZ(sizeof(*pNtViRdr) + cchFilename);
+ if (!pNtViRdr)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the structure.
+ */
+ if (cchFilename)
+ {
+ char *pszName = &pNtViRdr->szFilename[0];
+ rc = RTUtf16ToUtf8Ex(pwszName, RTSTR_MAX, &pszName, cchFilename + 1, NULL);
+ AssertStmt(RT_SUCCESS(rc), pNtViRdr->szFilename[0] = '\0');
+ }
+ else
+ pNtViRdr->szFilename[0] = '\0';
+
+ pNtViRdr->Core.uMagic = RTLDRREADER_MAGIC;
+ pNtViRdr->Core.pfnRead = supHardNtViRdrRead;
+ pNtViRdr->Core.pfnTell = supHardNtViRdrTell;
+ pNtViRdr->Core.pfnSize = supHardNtViRdrSize;
+ pNtViRdr->Core.pfnLogName = supHardNtViRdrLogName;
+ pNtViRdr->Core.pfnMap = supHardNtViRdrMap;
+ pNtViRdr->Core.pfnUnmap = supHardNtViRdrUnmap;
+ pNtViRdr->Core.pfnDestroy = supHardNtViRdrDestroy;
+ pNtViRdr->hFile = hFile;
+ pNtViRdr->off = 0;
+ pNtViRdr->cbFile = StdInfo.EndOfFile.QuadPart;
+ pNtViRdr->fFlags = fFlags;
+ *ppNtViRdr = pNtViRdr;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII path compare.
+ *
+ * @returns true if equal, false if not.
+ * @param pwszLeft The UTF-16 path string.
+ * @param pszRight The ascii string.
+ */
+static bool supHardViUtf16PathIsEqual(PCRTUTF16 pwszLeft, const char *pszRight)
+{
+ for (;;)
+ {
+ RTUTF16 wc = *pwszLeft++;
+ uint8_t b = *pszRight++;
+ if (b != wc)
+ {
+ if (wc >= 0x80)
+ return false;
+ wc = RT_C_TO_LOWER(wc);
+ if (wc != b)
+ {
+ b = RT_C_TO_LOWER(b);
+ if (wc != b)
+ {
+ if (wc == '/')
+ wc = '\\';
+ if (b == '/')
+ b = '\\';
+ if (wc != b)
+ return false;
+ }
+ }
+ }
+ if (!b)
+ return true;
+ }
+}
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII ends-with path predicate.
+ *
+ * @returns true if equal, false if not.
+ * @param pwsz The UTF-16 path string.
+ * @param pszSuffix The ascii suffix string.
+ */
+static bool supHardViUtf16PathEndsWith(PCRTUTF16 pwsz, const char *pszSuffix)
+{
+ size_t cwc = RTUtf16Len(pwsz);
+ size_t cchSuffix = strlen(pszSuffix);
+ if (cwc >= cchSuffix)
+ return supHardViUtf16PathIsEqual(pwsz + cwc - cchSuffix, pszSuffix);
+ return false;
+}
+
+
+/**
+ * Simple case insensitive UTF-16 / ASCII starts-with path predicate.
+ *
+ * @returns true if starts with given string, false if not.
+ * @param pwsz The UTF-16 path string.
+ * @param pszPrefix The ascii prefix string.
+ */
+static bool supHardViUtf16PathStartsWith(PCRTUTF16 pwszLeft, const char *pszRight)
+{
+ for (;;)
+ {
+ RTUTF16 wc = *pwszLeft++;
+ uint8_t b = *pszRight++;
+ if (b != wc)
+ {
+ if (!b)
+ return true;
+ if (wc >= 0x80 || wc == 0)
+ return false;
+ wc = RT_C_TO_LOWER(wc);
+ if (wc != b)
+ {
+ b = RT_C_TO_LOWER(b);
+ if (wc != b)
+ {
+ if (wc == '/')
+ wc = '\\';
+ if (b == '/')
+ b = '\\';
+ if (wc != b)
+ return false;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Counts slashes in the given UTF-8 path string.
+ *
+ * @returns Number of slashes.
+ * @param pwsz The UTF-16 path string.
+ */
+static uint32_t supHardViUtf16PathCountSlashes(PCRTUTF16 pwsz)
+{
+ uint32_t cSlashes = 0;
+ RTUTF16 wc;
+ while ((wc = *pwsz++) != '\0')
+ if (wc == '/' || wc == '\\')
+ cSlashes++;
+ return cSlashes;
+}
+
+
+/**
+ * Checks if the unsigned DLL is fine or not.
+ *
+ * @returns VINF_LDRVI_NOT_SIGNED or @a rc.
+ * @param hLdrMod The loader module handle.
+ * @param pwszName The NT name of the DLL/EXE.
+ * @param fFlags Flags.
+ * @param rc The status code..
+ */
+static int supHardNtViCheckIfNotSignedOk(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, uint32_t fFlags, int rc)
+{
+ if (fFlags & (SUPHNTVI_F_REQUIRE_BUILD_CERT | SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING))
+ return rc;
+
+ /*
+ * Version macros.
+ */
+ uint32_t const uNtVer = g_uNtVerCombined;
+#define IS_XP() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 2) )
+#define IS_W2K3() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 3) )
+#define IS_VISTA() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 0) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 1) )
+#define IS_W70() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 2) )
+#define IS_W80() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 3) )
+#define IS_W81() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 3) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) )
+
+ /*
+ * The System32 directory.
+ *
+ * System32 is full of unsigned DLLs shipped by microsoft, graphics
+ * hardware vendors, input device/method vendors and whatnot else that
+ * actually needs to be loaded into a process for it to work correctly.
+ * We have to ASSUME that anything our process attempts to load from
+ * System32 is trustworthy and that the Windows system with the help of
+ * anti-virus software make sure there is nothing evil lurking in System32
+ * or being loaded from it.
+ *
+ * A small measure of protection is to list DLLs we know should be signed
+ * and decline loading unsigned versions of them, assuming they have been
+ * replaced by an adversary with evil intentions.
+ */
+ PCRTUTF16 pwsz;
+ uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
+ uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
+ if ( cwcName > cwcOther
+ && RTPATH_IS_SLASH(pwszName[cwcOther])
+ && memcmp(pwszName, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0)
+ {
+ pwsz = pwszName + cwcOther + 1;
+
+ /* Core DLLs. */
+ if (supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
+ return uNtVer < SUP_NT_VER_VISTA ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
+ return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "kernelbase.dll"))
+ return IS_W80() || IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "apisetschema.dll"))
+ return IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
+ if (supHardViUtf16PathIsEqual(pwsz, "apphelp.dll"))
+ return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
+
+#ifndef IN_RING0
+ /* The ATI drivers load system drivers into the process, allow this,
+ but reject anything else from a subdirectory. */
+ uint32_t cSlashes = supHardViUtf16PathCountSlashes(pwsz);
+ if (cSlashes > 0)
+ {
+ if ( cSlashes == 1
+ && supHardViUtf16PathStartsWith(pwsz, "drivers\\ati")
+ && ( supHardViUtf16PathEndsWith(pwsz, ".sys")
+ || supHardViUtf16PathEndsWith(pwsz, ".dll") ) )
+ return VINF_LDRVI_NOT_SIGNED;
+ return rc;
+ }
+
+ /* Check that this DLL isn't supposed to be signed on this windows
+ version. If it should, it's likely to be a fake. */
+ /** @todo list of signed dlls for various windows versions. */
+
+ /** @todo check file permissions? TrustedInstaller is supposed to be involved
+ * with all of them. */
+ return VINF_LDRVI_NOT_SIGNED;
+#else
+ return rc;
+#endif
+ }
+
+#ifndef IN_RING0
+ /*
+ * The WinSxS white list.
+ *
+ * Just like with System32 there are potentially a number of DLLs that
+ * could be required from WinSxS. However, so far only comctl32.dll
+ * variations have been required. So, we limit ourselves to explicit
+ * whitelisting of unsigned families of DLLs.
+ */
+ cwcOther = g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR);
+ if ( cwcName > cwcOther
+ && RTPATH_IS_SLASH(pwszName[cwcOther])
+ && memcmp(pwszName, g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length) == 0)
+ {
+ pwsz = pwszName + cwcOther + 1;
+ cwcName -= cwcOther + 1;
+
+ /* The WinSxS layout means everything worth loading is exactly one level down. */
+ uint32_t cSlashes = supHardViUtf16PathCountSlashes(pwsz);
+ if (cSlashes != 1)
+ return rc;
+
+# if 0 /* See below */
+ /* The common controls mess. */
+# ifdef RT_ARCH_AMD64
+ if (supHardViUtf16PathStartsWith(pwsz, "amd64_microsoft.windows.common-controls_"))
+# elif defined(RT_ARCH_X86)
+ if (supHardViUtf16PathStartsWith(pwsz, "x86_microsoft.windows.common-controls_"))
+# else
+# error "Unsupported architecture"
+# endif
+ {
+ if (supHardViUtf16PathEndsWith(pwsz, "\\comctl32.dll"))
+ return VINF_LDRVI_NOT_SIGNED;
+ }
+# endif
+
+ /* Allow anything slightly microsoftish from WinSxS. W2K3 wanted winhttp.dll early on... */
+# ifdef RT_ARCH_AMD64
+ if (supHardViUtf16PathStartsWith(pwsz, "amd64_microsoft."))
+# elif defined(RT_ARCH_X86)
+ if (supHardViUtf16PathStartsWith(pwsz, "x86_microsoft."))
+# else
+# error "Unsupported architecture"
+# endif
+ {
+ return VINF_LDRVI_NOT_SIGNED;
+ }
+
+ return rc;
+ }
+#endif
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
+ * Standard code signing. Use this for Microsoft SPC.}
+ */
+static DECLCALLBACK(int) supHardNtViCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ /*
+ * If there is no certificate path build & validator associated with this
+ * callback, it must be because of the build certificate. We trust the
+ * build certificate without any second thoughts.
+ */
+ if (hCertPaths == NIL_RTCRX509CERTPATHS)
+ {
+ if (RTCrX509Certificate_Compare(pCert, &g_BuildX509Cert) == 0) /* healthy paranoia */
+ return VINF_SUCCESS;
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_BUILD_CERT_IPE, "Not valid kernel code signature.");
+ }
+
+ /*
+ * Standard code signing capabilites required.
+ */
+ int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, NULL, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If kernel signing, a valid certificate path must be anchored by the
+ * microsoft kernel signing root certificate.
+ */
+ if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
+ {
+ uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
+ uint32_t cFound = 0;
+ uint32_t cValid = 0;
+ for (uint32_t iPath = 0; iPath < cPaths; iPath++)
+ {
+ bool fTrusted;
+ PCRTCRX509NAME pSubject;
+ PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
+ int rcVerify;
+ rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
+ NULL, NULL /*pCertCtx*/, &rcVerify);
+ AssertRCBreak(rc);
+
+ if (RT_SUCCESS(rcVerify))
+ {
+ Assert(fTrusted);
+ cValid++;
+
+ /*
+ * Search the kernel signing root store for a matching anchor.
+ */
+ RTCRSTORECERTSEARCH Search;
+ rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(g_hNtKernelRootStore, pSubject, &Search);
+ AssertRCBreak(rc);
+
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(g_hNtKernelRootStore, &Search)) != NULL)
+ {
+ if (RTCrX509SubjectPublicKeyInfo_Compare(&pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo,
+ pPublicKeyInfo) == 0)
+ cFound++;
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(g_hNtKernelRootStore, &Search); AssertRC(rc2);
+ }
+ }
+ if (RT_SUCCESS(rc) && cFound == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE, "Not valid kernel code signature.");
+ if (RT_SUCCESS(rc) && cValid != 2 && g_fHaveOtherRoots)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNEXPECTED_VALID_PATH_COUNT,
+ "Expected exactly %u valid paths, not %u.", 2, cValid);
+ }
+ }
+
+ /*
+ * More requirements? NT5 build lab?
+ */
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) supHardNtViCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature,
+ void const *pvSignature, size_t cbSignature,
+ PRTERRINFO pErrInfo, void *pvUser)
+{
+ /*
+ * Check out the input.
+ */
+ PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
+ Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
+
+ AssertReturn(cbSignature == sizeof(RTCRPKCS7CONTENTINFO), VERR_INTERNAL_ERROR_5);
+ PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature;
+ AssertReturn(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo), VERR_INTERNAL_ERROR_5);
+ AssertReturn(pContentInfo->u.pSignedData->SignerInfos.cItems == 1, VERR_INTERNAL_ERROR_5);
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = &pContentInfo->u.pSignedData->SignerInfos.paItems[0];
+
+ /*
+ * If special certificate requirements, check them out before validating
+ * the signature.
+ */
+ if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
+ {
+ if (!RTCrX509Certificate_MatchIssuerAndSerialNumber(&g_BuildX509Cert,
+ &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber))
+ return RTErrInfoSet(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT, "Not signed with the build certificate.");
+ }
+
+ /*
+ * Verify the signature.
+ */
+ RTTIMESPEC ValidationTime;
+ RTTimeSpecSetSeconds(&ValidationTime, pNtViRdr->uTimestamp);
+
+ return RTCrPkcs7VerifySignedData(pContentInfo, 0, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore, &ValidationTime,
+ supHardNtViCertVerifyCallback, pNtViRdr, pErrInfo);
+}
+
+
+/**
+ * Checks if it's safe to call WinVerifyTrust or whether we might end up in an
+ * infinite recursion.
+ *
+ * @returns true if ok, false if not.
+ * @param hFile The file name.
+ * @param pwszName The executable name.
+ */
+static bool supR3HardNtViCanCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName)
+{
+ /*
+ * Recursion preventions hacks:
+ * - Don't try call WinVerifyTrust on Wintrust.dll when called from the
+ * create section hook. CRYPT32.DLL tries to load WinTrust.DLL in some cases.
+ */
+ size_t cwcName = RTUtf16Len(pwszName);
+ if ( hFile != NULL
+ && cwcName > g_System32NtPath.UniStr.Length / sizeof(WCHAR)
+ && !memcmp(pwszName, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length)
+ && supHardViUtf16PathIsEqual(&pwszName[g_System32NtPath.UniStr.Length / sizeof(WCHAR)], "\\wintrust.dll"))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Verifies the given executable image.
+ *
+ * @returns IPRT status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for dealing
+ * with unsigned system dlls as well as for error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pfCacheable Where to return whether the result can be cached. A
+ * valid value is always returned. Optional.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags,
+ bool *pfCacheable, PRTERRINFO pErrInfo)
+{
+ /* Clear the cacheable indicator as it needs to be valid in all return paths. */
+ if (pfCacheable)
+ *pfCacheable = false;
+
+ /*
+ * Create a reader instance.
+ */
+ PSUPHNTVIRDR pNtViRdr;
+ int rc = supHardNtViRdrCreate(hFile, pwszName, fFlags, &pNtViRdr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the image.
+ */
+ RTLDRMOD hLdrMod;
+ rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION,
+ fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST,
+ &hLdrMod, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Verify it.
+ *
+ * The PKCS #7 SignedData signature is checked in the callback. Any
+ * signing certificate restrictions are also enforced there.
+ *
+ * For the time being, we use the executable timestamp as the
+ * certificate validation date. We must query that first to avoid
+ * potential issues re-entering the loader code from the callback.
+ */
+ rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pNtViRdr->uTimestamp, sizeof(pNtViRdr->uTimestamp));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
+
+ /*
+ * Microsoft doesn't sign a whole bunch of DLLs, so we have to
+ * ASSUME that a bunch of system DLLs are fine.
+ */
+ if (rc == VERR_LDRVI_NOT_SIGNED)
+ rc = supHardNtViCheckIfNotSignedOk(hLdrMod, pwszName, fFlags, rc);
+ if (RT_FAILURE(rc))
+ RTErrInfoAddF(pErrInfo, rc, ": %ls", pwszName);
+
+ /*
+ * Check for the signature checking enforcement, if requested to do so.
+ */
+ if (RT_SUCCESS(rc) && (fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT))
+ {
+ bool fEnforced = false;
+ int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
+ if (RT_FAILURE(rc2))
+ rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
+ pwszName, rc2);
+ else if (!fEnforced)
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SIGNATURE_CHECKS_NOT_ENFORCED,
+ "The image '%ls' was not linked with /IntegrityCheck.", pwszName);
+ }
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rc, "RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %ls: %Rrc", pwszName, rc);
+
+ int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
+
+#ifdef IN_RING3
+ /*
+ * Call the windows verify trust API if we've resolved it.
+ */
+ if ( g_pfnWinVerifyTrust
+ && supR3HardNtViCanCallWinVerifyTrust(hFile, pwszName))
+ {
+ if (pfCacheable)
+ *pfCacheable = g_pfnWinVerifyTrust != NULL;
+ if (rc != VERR_LDRVI_NOT_SIGNED)
+ {
+ if (rc == VINF_LDRVI_NOT_SIGNED)
+ {
+ if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
+ {
+ int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo,
+ g_pfnWinVerifyTrust);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
+ rc = rc2;
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_LDRVI_NOT_SIGNED;
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
+ else
+ {
+ int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
+ AssertMsg(RT_FAILURE_NP(rc2),
+ ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
+ }
+ }
+ }
+#else
+ if (pfCacheable)
+ *pfCacheable = true;
+#endif /* IN_RING3 */
+ }
+ else
+ supHardNtViRdrDestroy(&pNtViRdr->Core);
+ }
+ SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)\n", rc, pwszName));
+ return rc;
+}
+
+
+#ifdef IN_RING3
+/**
+ * supHardenedWinVerifyImageByHandle version without the name.
+ *
+ * The name is derived from the handle.
+ *
+ * @returns IPRT status code.
+ * @param hFile File handle to the executable file.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /*
+ * Determine the NT name and call the verification function.
+ */
+ union
+ {
+ UNICODE_STRING UniStr;
+ uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
+ } uBuf;
+
+ ULONG cbIgn;
+ NTSTATUS rcNt = NtQueryObject(hFile,
+ ObjectNameInformation,
+ &uBuf,
+ sizeof(uBuf) - sizeof(WCHAR),
+ &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
+ else
+ uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
+
+ return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, NULL /*pfCacheable*/, pErrInfo);
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Retrieves the full official path to the system root or one of it's sub
+ * directories.
+ *
+ * This code is also used by the support driver.
+ *
+ * @returns VBox status code.
+ * @param pvBuf The output buffer. This will contain a
+ * UNICODE_STRING followed (at the kernel's
+ * discretion) the string buffer.
+ * @param cbBuf The size of the buffer @a pvBuf points to.
+ * @param enmDir Which directory under the system root we're
+ * interested in.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
+{
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING NtName;
+ switch (enmDir)
+ {
+ case kSupHardNtSysRootDir_System32:
+ {
+ static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
+ NtName.Buffer = (PWSTR)s_wszNameSystem32;
+ NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
+ NtName.MaximumLength = sizeof(s_wszNameSystem32);
+ break;
+ }
+ case kSupHardNtSysRootDir_WinSxS:
+ {
+ static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
+ NtName.Buffer = (PWSTR)s_wszNameWinSxS;
+ NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
+ NtName.MaximumLength = sizeof(s_wszNameWinSxS);
+ break;
+ }
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (NT_SUCCESS(rcNt))
+ {
+ ULONG cbIgn;
+ rcNt = NtQueryObject(hFile,
+ ObjectNameInformation,
+ pvBuf,
+ cbBuf - sizeof(WCHAR),
+ &cbIgn);
+ NtClose(hFile);
+ if (NT_SUCCESS(rcNt))
+ {
+ PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
+ if (pUniStr->Length > 0)
+ {
+ /* Make sure it's terminated so it can safely be printed.*/
+ pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ return VINF_SUCCESS;
+ }
+
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
+ "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
+}
+
+
+/**
+ * Initialize one certificate entry.
+ *
+ * @returns VBox status code.
+ * @param pCert The X.509 certificate representation to init.
+ * @param pabCert The raw DER encoded certificate.
+ * @param cbCert The size of the raw certificate.
+ * @param pErrInfo Where to return extended error info. Optional.
+ * @param pszErrorTag Error tag.
+ */
+static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(cbCert > 16 && cbCert < _128K,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
+ AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
+ RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
+
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
+ int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
+ return rc;
+}
+
+
+static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
+{
+ for (uint32_t i = 0; i < cCerts; i++)
+ {
+ int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize a certificate table.
+ *
+ * @param phStore Where to return the store pointer.
+ * @param paCerts1 Pointer to the first certificate table.
+ * @param cCerts1 Entries in the first certificate table.
+ * @param paCerts2 Pointer to the second certificate table.
+ * @param cCerts2 Entries in the second certificate table.
+ * @param paCerts3 Pointer to the third certificate table.
+ * @param cCerts3 Entries in the third certificate table.
+ * @param pErrInfo Where to return extended error info. Optional.
+ * @param pszErrorTag Error tag.
+ */
+static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
+ PCSUPTAENTRY paCerts1, unsigned cCerts1,
+ PCSUPTAENTRY paCerts2, unsigned cCerts2,
+ PCSUPTAENTRY paCerts3, unsigned cCerts3,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
+
+ int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
+
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
+ return rc;
+}
+
+
+/**
+ * This initializes the certificates globals so we don't have to reparse them
+ * every time we need to verify an image.
+ *
+ * @returns IPRT status code.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
+{
+ AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
+
+ /*
+ * Get the system root paths.
+ */
+ int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize it, leaving the cleanup to the termination call.
+ */
+ rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
+ NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
+ NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
+ g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
+ g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
+ g_aSUPTimestampTAs, g_cSUPTimestampTAs,
+ pErrInfo, "SpcAndNtKernelRoot");
+ if (RT_SUCCESS(rc))
+ rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
+ NULL, 0, NULL, 0, NULL, 0,
+ pErrInfo, "SpcAndNtKernelSupplemental");
+
+ /* If the build certificate is a test singing certificate, it must be a
+ trusted root or we'll fail to validate anything. */
+ if ( RT_SUCCESS(rc)
+ && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
+ rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ supHardenedWinTermImageVerifier();
+ }
+ return rc;
+}
+
+
+/**
+ * Releases resources allocated by supHardenedWinInitImageVerifier.
+ */
+DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
+{
+ if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
+ RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
+
+ RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
+ g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
+ RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
+ g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
+
+ RTCrStoreRelease(g_hNtKernelRootStore);
+ g_hNtKernelRootStore = NIL_RTCRSTORE;
+ RTCrStoreRelease(g_hSpcRootStore);
+ g_hSpcRootStore = NIL_RTCRSTORE;
+}
+
+#ifdef IN_RING3
+
+/**
+ * This is a hardcoded list of certificates we thing we might need.
+ *
+ * @returns true if wanted, false if not.
+ * @param pCert The certificate.
+ */
+static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
+{
+ /*
+ * Check that it's a plausible root certificate.
+ */
+ if (!RTCrX509Certificate_IsSelfSigned(pCert))
+ return false;
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
+ {
+ if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
+ && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
+ return false;
+ if ( pCert->TbsCertificate.T3.pBasicConstraints
+ && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
+ return false;
+ }
+ if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
+ return false;
+
+ /*
+ * Array of names and key clues of the certificates we want.
+ */
+ static struct
+ {
+ uint64_t u64KeyId;
+ const char *pszName;
+ } const s_aWanted[] =
+ {
+ /* SPC */
+ { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
+ { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
+ { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
+
+ /* TS */
+ { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
+ { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
+ { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
+
+ /* Additional Windows 8.1 list: */
+ { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
+ { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
+ { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
+ { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
+ { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
+ { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
+ { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
+ { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
+ { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
+ { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
+ { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
+ { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
+ { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
+ { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
+ { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
+ { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
+ { UINT64_C(0x0ff3891b54348328), "C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netSecure Server Certification Authority" },
+ { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
+ { UINT64_C(0xd45980fbf0a0ac00), "C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA" },
+ { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
+ { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
+ { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
+ { UINT64_C(0x16e64d2a56ccf200), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certificates.starfieldtech.com/repository/, CN=Starfield Services Root Certificate Authority" },
+ { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
+ { UINT64_C(0xb28612a94b4dad00), "O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netCertification Authority (2048)" },
+ { UINT64_C(0x357a29080824af00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G5" },
+ { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
+ { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, Email=info@valicert.com" },
+ { UINT64_C(0x1f78fc529cbacb00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 1999 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G3" },
+ { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
+ { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
+ };
+
+
+ uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
+ uint32_t i = RT_ELEMENTS(s_aWanted);
+ while (i-- > 0)
+ if ( s_aWanted[i].u64KeyId == u64KeyId
+ || s_aWanted[i].u64KeyId == UINT64_MAX)
+ if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
+ return true;
+
+#ifdef DEBUG_bird
+ char szTmp[512];
+ szTmp[sizeof(szTmp) - 1] = '\0';
+ RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Issuer, szTmp, sizeof(szTmp) - 1, NULL);
+ SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: %#llx %s\n", u64KeyId, szTmp));
+#endif
+ return false;
+}
+
+/**
+ * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
+ * import selected root CAs from the system certificate store.
+ *
+ * These certificates permits us to correctly validate third party DLLs.
+ *
+ * @param fLoadLibraryFlags The LoadLibraryExW flags that the caller
+ * found to work. Avoids us having to retry on
+ * ERROR_INVALID_PARAMETER.
+ */
+static void supR3HardenedWinRetrieveTrustedRootCAs(DWORD fLoadLibraryFlags)
+{
+ uint32_t cAdded = 0;
+
+ /*
+ * Load crypt32.dll and resolve the APIs we need.
+ */
+ HMODULE hCrypt32 = LoadLibraryExW(L"\\\\.\\GLOBALROOT\\SystemRoot\\System32\\crypt32.dll", NULL, fLoadLibraryFlags);
+ if (!hCrypt32)
+ supR3HardenedFatal("Error loading 'crypt32.dll': %u", GetLastError());
+
+#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
+ a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
+ if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", GetLastError())
+ RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
+ RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
+ RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
+#undef RESOLVE_CRYPT32_API
+
+ /*
+ * Open the root store and look for the certificates we wish to use.
+ */
+ DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
+ HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
+ if (!hStore)
+ hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
+ if (hStore)
+ {
+ PCCERT_CONTEXT pCurCtx = NULL;
+ while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
+ {
+ if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
+ {
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/,
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
+ RTCRX509CERTIFICATE MyCert;
+ int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (supR3HardenedWinIsDesiredRootCA(&MyCert))
+ {
+ rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
+ AssertRC(rc);
+
+ rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
+ AssertRC(rc);
+ cAdded++;
+ }
+
+ RTCrX509Certificate_Delete(&MyCert);
+ }
+ }
+ }
+ pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ g_fHaveOtherRoots = true;
+ }
+ SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
+}
+
+
+/**
+ * Resolves the WinVerifyTrust API after the process has been verified and
+ * installs a thread creation hook.
+ *
+ * The WinVerifyTrust API is used in addition our own Authenticode verification
+ * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
+ * set, it will be checked again by the kernel. All our image has this flag set
+ * and we require all VBox extensions to have it set as well. In effect, the
+ * authenticode signature will be checked two or three times.
+ */
+DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(void)
+{
+# ifdef IN_SUP_HARDENED_R3
+ /*
+ * Load our the support library DLL that does the thread hooking as the
+ * security API may trigger the creation of COM worker threads (or
+ * whatever they are).
+ *
+ * The thread creation hook makes the threads very slippery to debuggers by
+ * irreversably disabling most (if not all) debug events for them.
+ */
+ char szPath[RTPATH_MAX];
+ supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
+ suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
+ HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/);
+ if (hSupLibMod == NULL)
+ supR3HardenedFatal("Error loading '%s': %u", szPath, GetLastError());
+# endif
+
+ /*
+ * Resolve it.
+ */
+ DWORD fFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
+ HMODULE hWintrust = LoadLibraryExW(L"\\\\.\\GLOBALROOT\\SystemRoot\\System32\\Wintrust.dll", NULL, fFlags);
+ if ( hWintrust == NULL
+ && fFlags
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ fFlags = 0;
+ hWintrust = LoadLibraryExW(L"\\\\.\\GLOBALROOT\\SystemRoot\\System32\\Wintrust.dll", NULL, fFlags);
+ }
+ if (hWintrust == NULL)
+ supR3HardenedFatal("Error loading 'Wintrust.dll': %u", GetLastError());
+
+#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
+ do { \
+ g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
+ if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
+ supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", GetLastError()); \
+ } while (0)
+
+ PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
+ if (!pfnWinVerifyTrust)
+ supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", GetLastError());
+
+ RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
+ RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
+
+ RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
+ RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
+
+ /*
+ * Call it on ourselves and ntdll to make sure it loads all the providers
+ * now, we would otherwise geting into recursive trouble in the
+ * NtCreateSection hook.
+ */
+# ifdef IN_SUP_HARDENED_R3
+ RTERRINFOSTATIC ErrInfoStatic;
+ RTErrInfoInitStatic(&ErrInfoStatic);
+ int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
+ &ErrInfoStatic.Core, pfnWinVerifyTrust);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatal("WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
+# endif
+
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* ntdll isn't signed on XP, assuming this is the case on W2K3 for now. */
+ supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
+ supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
+
+ g_pfnWinVerifyTrust = pfnWinVerifyTrust;
+
+ /*
+ * Now, get trusted root CAs so we can verify a broader scope of signatures.
+ */
+ supR3HardenedWinRetrieveTrustedRootCAs(fFlags);
+}
+
+
+static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
+ PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
+{
+ static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
+
+ if (*pwszNtName != '\\' && *pwszNtName != '/')
+ return VERR_PATH_DOES_NOT_START_WITH_ROOT;
+
+ size_t cwcNtName = RTUtf16Len(pwszNtName);
+ if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
+ return VERR_FILENAME_TOO_LONG;
+
+ memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
+ memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
+ *ppwszWinPath = pwszWinPathBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calls WinVerifyTrust to verify an PE image.
+ *
+ * @returns VBox status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ * @param pfnWinVerifyTrust Pointer to the API.
+ */
+static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust)
+{
+ /*
+ * Convert the name into a Windows name.
+ */
+ RTUTF16 wszWinPathBuf[MAX_PATH];
+ PCRTUTF16 pwszWinPath;
+ int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
+
+ /*
+ * Construct input parameters and call the API.
+ */
+ WINTRUST_FILE_INFO FileInfo;
+ RT_ZERO(FileInfo);
+ FileInfo.cbStruct = sizeof(FileInfo);
+ FileInfo.pcwszFilePath = pwszWinPath;
+ FileInfo.hFile = hFile;
+
+ GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+
+ WINTRUST_DATA TrustData;
+ RT_ZERO(TrustData);
+ TrustData.cbStruct = sizeof(TrustData);
+ TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
+ TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+ TrustData.dwUIChoice = WTD_UI_NONE;
+ TrustData.dwProvFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+ else
+ TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
+ TrustData.dwUnionChoice = WTD_CHOICE_FILE;
+ TrustData.pFile = &FileInfo;
+
+ HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
+ if (hrc == S_OK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Failed. Format a nice error message.
+ */
+# ifdef DEBUG_bird
+ __debugbreak();
+# endif
+ const char *pszErrConst = NULL;
+ switch (hrc)
+ {
+ case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
+ case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
+ case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
+ case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
+ case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
+ case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
+ case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
+ case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
+ case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
+ case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
+ case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
+ case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
+ case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
+ case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
+ case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
+ }
+ if (pszErrConst)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
+ "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
+ "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
+ }
+
+ /* clean up state data. */
+ TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ FileInfo.hFile = NULL;
+ hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
+
+ return rc;
+}
+
+
+/**
+ * Calls WinVerifyTrust to verify an PE image via catalog files.
+ *
+ * @returns VBox status code.
+ * @param hFile File handle to the executable file.
+ * @param pwszName Full NT path to the DLL in question, used for
+ * dealing with unsigned system dlls as well as for
+ * error/logging.
+ * @param fFlags Flags, SUPHNTVI_F_XXX.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ * @param pfnWinVerifyTrust Pointer to the API.
+ */
+static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
+ PFNWINVERIFYTRUST pfnWinVerifyTrust)
+{
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
+
+ /*
+ * Convert the name into a Windows name.
+ */
+ RTUTF16 wszWinPathBuf[MAX_PATH];
+ PCRTUTF16 pwszWinPath;
+ int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
+
+ /*
+ * Open the file if we didn't get a handle.
+ */
+ HANDLE hFileClose = NULL;
+ if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
+ {
+ hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ UNICODE_STRING NtName;
+ NtName.Buffer = (PWSTR)pwszName;
+ NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
+ NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ FILE_READ_DATA | 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))
+ return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
+ "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
+ hFileClose = hFile;
+ }
+
+ /*
+ * On Windows 8.0 and later there are more than one digest choice.
+ */
+ rc = VERR_LDRVI_NOT_SIGNED;
+ static struct
+ {
+ /** The digest algorithm name. */
+ const WCHAR *pszAlgorithm;
+ /** Cached catalog admin handle. */
+ HCATADMIN volatile hCachedCatAdmin;
+ } s_aHashes[] =
+ {
+ { NULL, NULL },
+ { L"SHA256", NULL },
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
+ {
+ /*
+ * Another loop for dealing with different trust provider policies
+ * required for successfully validating different catalog signatures.
+ */
+ bool fTryNextPolicy;
+ uint32_t iPolicy = 0;
+ static const GUID s_aPolicies[] =
+ {
+ DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
+ WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
+ };
+ do
+ {
+ /*
+ * Create a context.
+ */
+ fTryNextPolicy = false;
+ BOOL fRc;
+ HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
+ if (hCatAdmin)
+ fRc = TRUE;
+ else if (g_pfnCryptCATAdminAcquireContext2)
+ fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
+ NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
+ else
+ fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
+ if (fRc)
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
+
+ /*
+ * Hash the file.
+ */
+ BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
+ DWORD cbHash = sizeof(abHash);
+ if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
+ fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
+ else
+ fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
+ if (fRc)
+ {
+ /* Produce a string version of it that we can pass to WinVerifyTrust. */
+ RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
+ int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
+ if (RT_SUCCESS(rc2))
+ {
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
+
+ /*
+ * Enumerate catalog information that matches the hash.
+ */
+ uint32_t iCat = 0;
+ HCATINFO hCatInfoPrev = NULL;
+ do
+ {
+ /* Get the next match. */
+ HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
+ if (!hCatInfo)
+ {
+ if (iCat == 0)
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", GetLastError()));
+ break;
+ }
+ Assert(hCatInfoPrev == NULL);
+ hCatInfoPrev = hCatInfo;
+
+ /*
+ * Call WinVerifyTrust.
+ */
+ CATALOG_INFO CatInfo;
+ CatInfo.cbStruct = sizeof(CatInfo);
+ CatInfo.wszCatalogFile[0] = '\0';
+ if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
+ {
+ WINTRUST_CATALOG_INFO WtCatInfo;
+ RT_ZERO(WtCatInfo);
+ WtCatInfo.cbStruct = sizeof(WtCatInfo);
+ WtCatInfo.dwCatalogVersion = 0;
+ WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
+ WtCatInfo.pcwszMemberTag = wszDigest;
+ WtCatInfo.pcwszMemberFilePath = pwszWinPath;
+ WtCatInfo.pbCalculatedFileHash = abHash;
+ WtCatInfo.cbCalculatedFileHash = cbHash;
+ WtCatInfo.pcCatalogContext = NULL;
+
+ WINTRUST_DATA TrustData;
+ RT_ZERO(TrustData);
+ TrustData.cbStruct = sizeof(TrustData);
+ TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
+ TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+ TrustData.dwUIChoice = WTD_UI_NONE;
+ TrustData.dwProvFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+ else
+ TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
+ TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
+ TrustData.pCatalog = &WtCatInfo;
+
+ HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat=%ls\n", hrc, CatInfo.wszCatalogFile));
+
+ if (SUCCEEDED(hrc))
+ rc = VINF_SUCCESS;
+ else if (hrc == TRUST_E_NOSIGNATURE)
+ { /* ignore because it's useless. */ }
+ else if (hrc == ERROR_INVALID_PARAMETER)
+ { /* This is returned if the given file isn't found in the catalog, it seems. */ }
+ else
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
+ "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
+ hrc, pwszWinPath, CatInfo.wszCatalogFile);
+ fTryNextPolicy = (hrc == CERT_E_UNTRUSTEDROOT);
+ }
+
+ /* clean up state data. */
+ TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
+ Assert(SUCCEEDED(hrc));
+ }
+ else
+ {
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
+ "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
+ GetLastError(), pwszName);
+ SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
+ }
+ iCat++;
+ } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
+
+ if (hCatInfoPrev != NULL)
+ if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
+ AssertFailed();
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
+ "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", GetLastError(), pwszName);
+
+ if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
+ if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
+ AssertFailed();
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
+ "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", GetLastError(), pwszName);
+ iPolicy++;
+ } while ( fTryNextPolicy
+ && iPolicy < RT_ELEMENTS(s_aPolicies));
+
+ /*
+ * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
+ */
+ if (!g_pfnCryptCATAdminAcquireContext2)
+ break;
+ if (rc != VERR_LDRVI_NOT_SIGNED)
+ break;
+ }
+
+ if (hFileClose != NULL)
+ NtClose(hFileClose);
+
+ return rc;
+}
+
+
+/**
+ * Initializes g_uNtVerCombined and g_NtVerInfo.
+ * Called from suplibHardenedWindowsMain and suplibOsInit.
+ */
+DECLHIDDEN(void) supR3HardenedWinInitVersion(void)
+{
+ /*
+ * Get the windows version. Use RtlGetVersion as GetVersionExW and
+ * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
+ * the application manifest).
+ */
+ OSVERSIONINFOEXW NtVerInfo;
+
+ suplibHardenedMemSet(&NtVerInfo, 0, sizeof(NtVerInfo));
+ NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+ if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
+ {
+ suplibHardenedMemSet(&NtVerInfo, 0, sizeof(NtVerInfo));
+ NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
+ if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
+ {
+ NtVerInfo.dwOSVersionInfoSize = sizeof(NtVerInfo);
+ if (!GetVersionExW((OSVERSIONINFOW *)&NtVerInfo))
+ {
+ suplibHardenedMemSet(&NtVerInfo, 0, sizeof(NtVerInfo));
+ DWORD dwVer = GetVersion();
+ NtVerInfo.dwMajorVersion = RT_BYTE1(dwVer);
+ NtVerInfo.dwMinorVersion = RT_BYTE2(dwVer);
+ NtVerInfo.dwBuildNumber = RT_BIT_32(31) & dwVer ? 0 : RT_HI_U16(dwVer);
+ }
+ }
+ }
+ g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
+ NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
+}
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
new file mode 100644
index 00000000000..8477352daf2
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
@@ -0,0 +1,1196 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library/Driver - Hardened Process Verification, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#ifdef IN_RING0
+# define IPRT_NT_MAP_TO_ZW
+# include <iprt/nt/nt.h>
+# include <ntimage.h>
+#else
+# include <iprt/nt/nt-and-windows.h>
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/param.h>
+
+#ifdef IN_RING0
+# include "SUPDrvInternal.h"
+#else
+# include "SUPLibInternal.h"
+#endif
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Virtual address space region.
+ */
+typedef struct SUPHNTVPREGION
+{
+ /** The RVA of the region. */
+ uint32_t uRva;
+ /** The size of the region. */
+ uint32_t cb;
+ /** The protection of the region. */
+ uint32_t fProt;
+} SUPHNTVPREGION;
+/** Pointer to a virtual address space region. */
+typedef SUPHNTVPREGION *PSUPHNTVPREGION;
+
+/**
+ * Virtual address space image information.
+ */
+typedef struct SUPHNTVPIMAGE
+{
+ /** The base address of the image. */
+ uintptr_t uImageBase;
+ /** The size of the image mapping. */
+ uintptr_t cbImage;
+
+ /** The name from the allowed lists. */
+ const char *pszName;
+ /** Name structure for NtQueryVirtualMemory/MemorySectionName. */
+ struct
+ {
+ /** The full unicode name. */
+ UNICODE_STRING UniStr;
+ /** Buffer space. */
+ WCHAR awcBuffer[260];
+ } Name;
+
+ /** The number of mapping regions. */
+ uint32_t cRegions;
+ /** Mapping regions. */
+ SUPHNTVPREGION aRegions[16];
+
+ /** The image characteristics from the FileHeader. */
+ uint16_t fImageCharecteristics;
+ /** The DLL characteristics from the OptionalHeader. */
+ uint16_t fDllCharecteristics;
+
+ /** Set if this is the DLL. */
+ bool fDll;
+ /** Set if the image is NTDLL an the verficiation code needs to watch out for
+ * the NtCreateSection patch. */
+ bool fNtCreateSectionPatch;
+ /** Whether the API set schema hack needs to be applied when verifying memory
+ * content. The hack means that we only check if the 1st section is mapped. */
+ bool fApiSetSchemaOnlySection1;
+} SUPHNTVPIMAGE;
+/** Pointer to image info from the virtual address space scan. */
+typedef SUPHNTVPIMAGE *PSUPHNTVPIMAGE;
+
+/**
+ * Virtual address space scanning state.
+ */
+typedef struct SUPHNTVPSTATE
+{
+ /** Number of images in aImages. */
+ uint32_t cImages;
+ /** Images found in the process.
+ * The array is large enough to hold the executable, all allowed DLLs, and one
+ * more so we can get the image name of the first unwanted DLL. */
+ SUPHNTVPIMAGE aImages[1+5+1];
+ /** Memory compare scratch buffer.*/
+ uint8_t abMemory[_4K];
+ /** File compare scratch buffer.*/
+ uint8_t abFile[_4K];
+ /** Section headers for use when comparing file and loaded image. */
+ IMAGE_SECTION_HEADER aSecHdrs[16];
+
+} SUPHNTVPSTATE;
+/** Pointer to stat information of a virtual address space scan. */
+typedef SUPHNTVPSTATE *PSUPHNTVPSTATE;
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/**
+ * System DLLs allowed to be loaded into the process.
+ * @remarks supHardNtVpCheckDlls assumes these are lower case.
+ */
+static const char *g_apszSupNtVpAllowedDlls[] =
+{
+ "ntdll.dll",
+ "kernel32.dll",
+ "kernelbase.dll",
+ "apphelp.dll",
+ "apisetschema.dll"
+};
+
+/**
+ * VBox executables allowed to start VMs.
+ * @remarks Remember to keep in sync with SUPR3HardenedVerify.cpp.
+ */
+static const char *g_apszSupNtVpAllowedVmExes[] =
+{
+ "VBoxHeadless.exe",
+ "VirtualBox.exe",
+ "VBoxSDL.exe",
+ "VBoxNetDHCP.exe",
+ "VBoxNetNAT.exe",
+};
+
+/** Pointer to NtQueryVirtualMemory. Initialized by SUPDrv-win.cpp in
+ * ring-0, in ring-3 it's just a slightly confusing define. */
+#ifdef IN_RING0
+PFNNTQUERYVIRTUALMEMORY g_pfnNtQueryVirtualMemory = NULL;
+#else
+# define g_pfnNtQueryVirtualMemory NtQueryVirtualMemory
+#endif
+
+
+/**
+ * Fills in error information.
+ *
+ * @returns @a rc.
+ * @param pErrInfo Pointer to the extened error info structure.
+ * Can be NULL.
+ * @param pszErr Where to return error details.
+ * @param cbErr Size of the buffer @a pszErr points to.
+ * @param rc The status to return.
+ * @param pszMsg The format string for the message.
+ * @param ... The arguments for the format string.
+ */
+static int supHardNtVpSetInfo(PRTERRINFO pErrInfo, int rc, const char *pszMsg, ...)
+{
+ va_list va;
+#ifdef IN_RING3
+ va_start(va, pszMsg);
+ supR3HardenedError(rc, false /*fFatal*/, "%N\n", pszMsg, &va);
+ va_end(va);
+#endif
+
+ va_start(va, pszMsg);
+ RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
+ va_end(va);
+
+ return rc;
+}
+
+
+static NTSTATUS supHardNtVpReadFile(HANDLE hFile, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ if ((ULONG)cbRead != cbRead)
+ return STATUS_INTEGER_OVERFLOW;
+
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile(hFile,
+ NULL /*hEvent*/,
+ NULL /*ApcRoutine*/,
+ NULL /*ApcContext*/,
+ &Ios,
+ pvBuf,
+ (ULONG)cbRead,
+ (PLARGE_INTEGER)&off,
+ NULL);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ return rcNt;
+}
+
+
+static NTSTATUS supHardNtVpReadMem(HANDLE hProcess, uintptr_t uPtr, void *pvBuf, size_t cbRead)
+{
+#ifdef IN_RING0
+ /* ASSUMES hProcess is the current process. */
+ /** @todo use MmCopyVirtualMemory where available! */
+ int rc = RTR0MemUserCopyFrom(pvBuf, uPtr, cbRead);
+ if (RT_SUCCESS(rc))
+ return STATUS_SUCCESS;
+ return STATUS_ACCESS_DENIED;
+#else
+ SIZE_T cbIgn;
+ NTSTATUS rcNt = NtReadVirtualMemory(hProcess, (PVOID)uPtr, pvBuf, cbRead, &cbIgn);
+ if (NT_SUCCESS(rcNt) && cbIgn != cbRead)
+ rcNt = STATUS_IO_DEVICE_ERROR;
+ return rcNt;
+#endif
+}
+
+
+static int supHardNtVpFileMemCompare(const void *pvFile, const void *pvMemory, size_t cbToCompare,
+ PSUPHNTVPIMAGE pImage, uint32_t uRva, PRTERRINFO pErrInfo)
+{
+ if (suplibHardenedMemComp(pvFile, pvMemory, cbToCompare) == 0)
+ return VINF_SUCCESS;
+
+ /* Find the exact location. */
+ const uint8_t *pbFile = (const uint8_t *)pvFile;
+ const uint8_t *pbMemory = (const uint8_t *)pvMemory;
+ while (cbToCompare > 0 && *pbFile == *pbMemory)
+ {
+ cbToCompare--;
+ pbFile++;
+ pbMemory++;
+ uRva++;
+ }
+
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH,
+ "%s: memory compare at %#x failed: %#x != %#x\n", pImage->pszName, uRva, *pbFile, *pbMemory);
+}
+
+
+static int supHardNtVpCheckSectionProtection(PSUPHNTVPIMAGE pImage, uint32_t uRva, uint32_t cb, uint32_t fProt,
+ PRTERRINFO pErrInfo)
+{
+ uint32_t const cbOrg = cb;
+ if (!cb)
+ return VINF_SUCCESS;
+
+ for (uint32_t i = 0; i < pImage->cRegions; i++)
+ {
+ uint32_t offRegion = uRva - pImage->aRegions[i].uRva;
+ if (offRegion < pImage->aRegions[i].cb)
+ {
+ uint32_t cbLeft = pImage->aRegions[i].cb - offRegion;
+ if ( pImage->aRegions[i].fProt != fProt
+ && ( fProt != PAGE_READWRITE
+ || pImage->aRegions[i].fProt != PAGE_WRITECOPY))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_SECTION_PROTECTION_MISMATCH,
+ "%s: RVA range %#x-%#x protection is %#x, expected %#x. (cb=%#x)",
+ pImage->pszName, uRva, uRva + cbLeft - 1, pImage->aRegions[i].fProt, fProt, cb);
+ if (cbLeft >= cb)
+ return VINF_SUCCESS;
+ cb -= cbLeft;
+ uRva += cbLeft;
+
+#if 0 /* This shouldn't ever be necessary. */
+ if ( i + 1 < pImage->cRegions
+ && uRva < pImage->aRegions[i + 1].uRva)
+ {
+ cbLeft = pImage->aRegions[i + 1].uRva - uRva;
+ if (cbLeft >= cb)
+ return VINF_SUCCESS;
+ cb -= cbLeft;
+ uRva += cbLeft;
+ }
+#endif
+ }
+ }
+
+ return supHardNtVpSetInfo(pErrInfo, cbOrg == cb ? VERR_SUP_VP_SECTION_NOT_MAPPED : VERR_SUP_VP_SECTION_NOT_FULLY_MAPPED,
+ "%s: RVA range %#x-%#x is not mapped?", pImage->pszName, uRva, uRva + cb - 1);
+}
+
+
+/**
+ * Compares process memory with the disk content.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure (for the
+ * two scratch buffers).
+ * @param pImage The image data collected during the address
+ * space scan.
+ * @param hProcess Handle to the process.
+ * @param hFile Handle to the image file.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpVerifyImageCompareMemory(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, HANDLE hFile,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Read and find the file headers.
+ */
+ NTSTATUS rcNt = supHardNtVpReadFile(hFile, 0, pThis->abFile, sizeof(pThis->abFile));
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_IMAGE_HDR_READ_ERROR,
+ "%s: Error reading image header: %#x", pImage->pszName, rcNt);
+
+ uint32_t offNtHdrs = 0;
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)&pThis->abFile[0];
+ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ offNtHdrs = pDosHdr->e_lfanew;
+ if (offNtHdrs > 512 || offNtHdrs < sizeof(*pDosHdr))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_MZ_OFFSET,
+ "%s: Unexpected e_lfanew value: %#x", pImage->pszName, offNtHdrs);
+ }
+ PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)&pThis->abFile[offNtHdrs];
+ if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_IMAGE_SIGNATURE,
+ "%s: No PE signature at %#x: %#x", pImage->pszName, offNtHdrs, pNtHdrs->Signature);
+
+ /*
+ * Do basic header validation.
+ */
+#ifdef RT_ARCH_AMD64
+ if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
+#else
+ if (pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
+#endif
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_UNEXPECTED_IMAGE_MACHINE,
+ "%s: Unexpected machine: %#x", pImage->pszName, pNtHdrs->FileHeader.Machine);
+
+ if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected optional header size: %#x",
+ pImage->pszName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
+
+ if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected optional header magic: %#x", pImage->pszName, pNtHdrs->OptionalHeader.Magic);
+ if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_OPTIONAL_HEADER,
+ "%s: Unexpected data dirs: %#x", pImage->pszName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
+
+ /*
+ * Before we start comparing things, store what we need to know from the headers.
+ */
+ uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
+ if (cSections > RT_ELEMENTS(pThis->aSecHdrs))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_MANY_SECTIONS,
+ "%s: Too many section headers: %#x", pImage->pszName, cSections);
+ suplibHardenedMemCopy(pThis->aSecHdrs, pNtHdrs + 1, cSections * sizeof(IMAGE_SECTION_HEADER));
+
+ uintptr_t const uImageBase = pNtHdrs->OptionalHeader.ImageBase;
+ if (uImageBase & PAGE_OFFSET_MASK)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_IMAGE_BASE,
+ "%s: Invalid image base: %p", pImage->pszName, uImageBase);
+
+ uint32_t const cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
+ if (RT_ALIGN_32(pImage->cbImage, PAGE_SIZE) != RT_ALIGN_32(cbImage, PAGE_SIZE) && !pImage->fApiSetSchemaOnlySection1)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_IMAGE_SIZE,
+ "%s: SizeOfImage (%#x) isn't close enough to the mapping size (%#x)",
+ pImage->pszName, cbImage, pImage->cbImage);
+
+ uint32_t const cbSectAlign = pNtHdrs->OptionalHeader.SectionAlignment;
+ if ( !RT_IS_POWER_OF_TWO(cbSectAlign)
+ || cbSectAlign < PAGE_SIZE
+ || cbSectAlign > (pImage->fApiSetSchemaOnlySection1 ? _64K : (uint32_t)PAGE_SIZE) )
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SECTION_ALIGNMENT_VALUE,
+ "%s: Unexpected SectionAlignment value: %#x", pImage->pszName, cbSectAlign);
+
+ uint32_t const cbFileAlign = pNtHdrs->OptionalHeader.FileAlignment;
+ if (!RT_IS_POWER_OF_TWO(cbFileAlign) || cbFileAlign < 512 || cbFileAlign > PAGE_SIZE || cbFileAlign > cbSectAlign)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_FILE_ALIGNMENT_VALUE,
+ "%s: Unexpected FileAlignment value: %#x (cbSectAlign=%#x)",
+ pImage->pszName, cbFileAlign, cbSectAlign);
+
+ uint32_t const cbHeaders = pNtHdrs->OptionalHeader.SizeOfHeaders;
+ uint32_t const cbMinHdrs = offNtHdrs + sizeof(*pNtHdrs) + sizeof(IMAGE_SECTION_HEADER) * cSections;
+ if (cbHeaders < cbMinHdrs)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
+ "%s: Headers are too small: %#x < %#x (cSections=%#x)",
+ pImage->pszName, cbHeaders, cbMinHdrs, cSections);
+ uint32_t const cbHdrsFile = RT_ALIGN_32(cbHeaders, cbFileAlign);
+ if (cbHdrsFile > sizeof(pThis->abFile))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SIZE_OF_HEADERS,
+ "%s: Headers are larger than expected: %#x/%#x (expected max %zx)",
+ pImage->pszName, cbHeaders, cbHdrsFile, sizeof(pThis->abFile));
+
+ /*
+ * Compare the file header with the loaded bits. The loader will fiddle
+ * with image base, changing it to the actual load address.
+ */
+ int rc;
+ if (!pImage->fApiSetSchemaOnlySection1)
+ {
+ rcNt = supHardNtVpReadMem(hProcess, pImage->uImageBase, pThis->abMemory, cbHdrsFile);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_MEMORY_READ_ERROR,
+ "%s: Error reading image header from memory: %#x", pImage->pszName, rcNt);
+ if (uImageBase != pImage->uImageBase)
+ pNtHdrs->OptionalHeader.ImageBase = pImage->uImageBase;
+
+ rc = supHardNtVpFileMemCompare(pThis->abFile, pThis->abMemory, cbHeaders, pImage, 0 /*uRva*/, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = supHardNtVpCheckSectionProtection(pImage, 0 /*uRva*/, cbHdrsFile, PAGE_READONLY, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Save some header fields we might be using later on.
+ */
+ pImage->fImageCharecteristics = pNtHdrs->FileHeader.Characteristics;
+ pImage->fDllCharecteristics = pNtHdrs->OptionalHeader.DllCharacteristics;
+
+ /*
+ * Validate sections and check them against the mapping regions.
+ */
+ uint32_t uRva = cbHdrsFile;
+ for (uint32_t i = 0; i < cSections; i++)
+ {
+ /* Validate the section. */
+ uint32_t uSectRva = pThis->aSecHdrs[i].VirtualAddress;
+ if (uSectRva < uRva || uSectRva > cbImage || RT_ALIGN_32(uSectRva, cbSectAlign) != uSectRva)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SECTION_RVA,
+ "%s: Section %u: Invalid virtual address: %#x (uRva=%#x, cbImage=%#x, cbSectAlign=%#x)",
+ pImage->pszName, i, uSectRva, uRva, cbImage, cbSectAlign);
+ uint32_t cbMap = pThis->aSecHdrs[i].Misc.VirtualSize;
+ if (cbMap > cbImage || uRva + cbMap > cbImage)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SECTION_VIRTUAL_SIZE,
+ "%s: Section %u: Invalid virtual size: %#x (uSectRva=%#x, uRva=%#x, cbImage=%#x)",
+ pImage->pszName, i, cbMap, uSectRva, uRva, cbImage);
+ uint32_t cbFile = pThis->aSecHdrs[i].SizeOfRawData;
+ if (cbFile != RT_ALIGN_32(cbFile, cbFileAlign) || cbFile > RT_ALIGN_32(cbMap, cbSectAlign))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_BAD_SECTION_FILE_SIZE,
+ "%s: Section %u: Invalid file size: %#x (cbMap=%#x, uSectRva=%#x)",
+ pImage->pszName, i, cbFile, cbMap, uSectRva);
+
+ /* Validate the protection. */
+ if (!pImage->fApiSetSchemaOnlySection1 || i == 0)
+ {
+ if (pImage->fApiSetSchemaOnlySection1)
+ {
+ pImage->uImageBase -= uSectRva;
+ pImage->cbImage += uSectRva;
+ pImage->aRegions[i].uRva = uSectRva;
+ }
+
+ uint32_t fProt;
+ switch (pThis->aSecHdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
+ {
+ case IMAGE_SCN_MEM_READ:
+ fProt = PAGE_READONLY;
+ break;
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE:
+ fProt = PAGE_READWRITE;
+ if (!suplibHardenedMemComp(pThis->aSecHdrs[i].Name, ".mrdata", 8)) /* w8.1, ntdll */
+ fProt = PAGE_READONLY;
+ break;
+ case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE:
+ fProt = PAGE_EXECUTE_READ;
+ break;
+ case IMAGE_SCN_MEM_EXECUTE:
+ fProt = PAGE_EXECUTE;
+ break;
+ default:
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_UNEXPECTED_SECTION_FLAGS,
+ "%s: Section %u: Unexpected characteristics: %#x (uSectRva=%#x, cbMap=%#x)",
+ pImage->pszName, i, pThis->aSecHdrs[i].Characteristics, uSectRva, cbMap);
+
+ }
+ rc = supHardNtVpCheckSectionProtection(pImage, uSectRva, RT_ALIGN_32(cbMap, PAGE_SIZE), fProt, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Advance the RVA. */
+ uRva = uSectRva + RT_ALIGN_32(cbMap, cbSectAlign);
+ }
+
+ /*
+ * Check the mapping regions with the image to make sure someone didn't
+ * fill executable code into some gap in the image.
+ */
+ /** @todo not vital. */
+
+
+ /*
+ * Compare executable code. If we're not loaded at the link address, we
+ * need to load base relocations and apply them while making the compare.
+ * A special case
+ */
+ /** @todo not vital. */
+
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies the signature of the given image on disk, then checks if the memory
+ * mapping matches what we verified.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure (for the
+ * two scratch buffers).
+ * @param pImage The image data collected during the address
+ * space scan.
+ * @param hProcess Handle to the process.
+ * @param hFile Handle to the image file.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpVerifyImage(PSUPHNTVPSTATE pThis, PSUPHNTVPIMAGE pImage, HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+ /*
+ * Open the image.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &pImage->Name.UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ GENERIC_READ,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_IMAGE_FILE_OPEN_ERROR,
+ "Error opening image for scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
+
+ /*
+ * Validate the signature, then make an attempt at comparing memory and
+ * disk content.
+ */
+ int rc = supHardenedWinVerifyImageByHandle(hFile, pImage->Name.UniStr.Buffer,
+ pImage->fDll ? 0 : SUPHNTVI_F_REQUIRE_BUILD_CERT,
+ NULL /*pfCacheable*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpVerifyImageCompareMemory(pThis, pImage, hProcess, hFile, pErrInfo);
+
+ /*
+ * Clean up and return.
+ */
+ rcNt = NtClose(hFile);
+ if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
+ rc = supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_IMAGE_FILE_CLOSE_ERROR,
+ "Error closing image after scanning: %#x (name %ls)", rcNt, pImage->Name.UniStr.Buffer);
+ return rc;
+}
+
+
+/**
+ * Verifies that there is only one thread in the process.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process.
+ * @param hThread The thread.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpThread(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
+{
+ /*
+ * Use the ThreadAmILastThread request to check that there is only one
+ * thread in the process.
+ */
+ ULONG cbIgn = 0;
+ ULONG fAmI = 0;
+ NTSTATUS rcNt = NtQueryInformationThread(hThread, ThreadAmILastThread, &fAmI, sizeof(fAmI), &cbIgn);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_THREAD_ERROR,
+ "NtQueryInformationThread/ThreadAmILastThread -> %#x", rcNt);
+ if (!fAmI)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_THREAD_NOT_ALONE,
+ "More than one thread in process");
+
+ /** @todo Would be nice to verify the relation ship between hProcess and hThread
+ * as well... */
+ return VINF_SUCCESS;
+}
+
+
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+/**
+ * Verifies that there isn't a debugger attached to the process.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpDebugger(HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+ /*
+ * Use the ProcessDebugPort request to check there is no debugger
+ * currently attached to the process.
+ */
+ ULONG cbIgn = 0;
+ uintptr_t uPtr = ~(uintptr_t)0;
+ NTSTATUS rcNt = NtQueryInformationProcess(hProcess,
+ ProcessDebugPort,
+ &uPtr, sizeof(uPtr), &cbIgn);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_DBG_PORT_ERROR,
+ "NtQueryInformationProcess/ProcessDebugPort -> %#x", rcNt);
+ if (uPtr != 0)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_DEBUGGED,
+ "Debugger attached (%#zx)", uPtr);
+ return VINF_SUCCESS;
+}
+#endif /* !VBOX_WITHOUT_DEBUGGER_CHECKS */
+
+
+/**
+ * Allocates and initalizes a process stat structure for process virtual memory
+ * scanning.
+ *
+ * @returns Pointer to the state structure on success, NULL on failure.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static PSUPHNTVPSTATE supHardNtVpCreateState(PRTERRINFO pErrInfo)
+{
+ /*
+ * Allocate the memory.
+ */
+ PSUPHNTVPSTATE pThis = (PSUPHNTVPSTATE)suplibHardenedAllocZ(sizeof(*pThis));
+ if (pThis)
+ return pThis;
+ supHardNtVpSetInfo(pErrInfo, VERR_NO_MEMORY, "Failed to allocate %zu bytes for state structures.", sizeof(*pThis));
+ return NULL;
+}
+
+
+/**
+ * Matches two UNICODE_STRING structures in a case sensitive fashion.
+ *
+ * @returns true if equal, false if not.
+ * @param pUniStr1 The first unicode string.
+ * @param pUniStr2 The first unicode string.
+ */
+static bool supHardNtVpAreUniStringsEqual(PCUNICODE_STRING pUniStr1, PCUNICODE_STRING pUniStr2)
+{
+ if (pUniStr1->Length != pUniStr2->Length)
+ return false;
+ return suplibHardenedMemComp(pUniStr1->Buffer, pUniStr2->Buffer, pUniStr1->Length) == 0;
+}
+
+
+/**
+ * Performs a case insensitive comparison of an ASCII and an UTF-16 file name.
+ *
+ * @returns true / false
+ * @param pszName1 The ASCII name.
+ * @param pwszName2 The UTF-16 name.
+ */
+static bool supHardNtVpAreNamesEqual(const char *pszName1, PCRTUTF16 pwszName2)
+{
+ for (;;)
+ {
+ char ch1 = *pszName1++;
+ RTUTF16 wc2 = *pwszName2++;
+ if (ch1 != wc2)
+ {
+ ch1 = RT_C_TO_LOWER(ch1);
+ wc2 = wc2 < 0x80 ? RT_C_TO_LOWER(wc2) : wc2;
+ if (ch1 != wc2)
+ return false;
+ }
+ if (!ch1)
+ return true;
+ }
+}
+
+
+/**
+ * Records an additional memory region for an image.
+ *
+ * @returns VBox status code.
+ * @param pImage The new image structure. Only the unicode name
+ * buffer is valid.
+ * @param pMemInfo The memory information for the image.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpNewImage(PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo, PRTERRINFO pErrInfo)
+{
+ /*
+ * Extract the final component.
+ */
+ unsigned cwcDirName = pImage->Name.UniStr.Length / sizeof(WCHAR);
+ PCRTUTF16 pwszFilename = &pImage->Name.UniStr.Buffer[cwcDirName];
+ while ( cwcDirName > 0
+ && pwszFilename[-1] != '\\'
+ && pwszFilename[-1] != '/'
+ && pwszFilename[-1] != ':')
+ {
+ pwszFilename--;
+ cwcDirName--;
+ }
+ if (!*pwszFilename)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NO_IMAGE_MAPPING_NAME,
+ "Empty filename (len=%u) for image at %p.", pImage->Name.UniStr.Length, pMemInfo->BaseAddress);
+
+ /*
+ * Drop trailing slashes from the directory name.
+ */
+ while ( cwcDirName > 0
+ && ( pImage->Name.UniStr.Buffer[cwcDirName - 1] == '\\'
+ || pImage->Name.UniStr.Buffer[cwcDirName - 1] == '/'))
+ cwcDirName--;
+
+ /*
+ * Match it against known DLLs.
+ */
+ pImage->pszName = NULL;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls); i++)
+ if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedDlls[i], pwszFilename))
+ {
+ pImage->pszName = g_apszSupNtVpAllowedDlls[i];
+ pImage->fDll = true;
+
+ /* The directory name must match the one we've got for System32. */
+ if ( cwcDirName * sizeof(WCHAR) != g_System32NtPath.UniStr.Length
+ || suplibHardenedMemComp(pImage->Name.UniStr.Buffer,
+ g_System32NtPath.UniStr.Buffer,
+ cwcDirName * sizeof(WCHAR)))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NON_SYSTEM32_DLL,
+ "Expected %ls to be loaded from %ls.",
+ pImage->Name.UniStr.Buffer, g_System32NtPath.UniStr.Buffer);
+
+ break;
+ }
+ if (!pImage->pszName)
+ {
+ /*
+ * Not a known DLL, executable?
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSupNtVpAllowedVmExes); i++)
+ if (supHardNtVpAreNamesEqual(g_apszSupNtVpAllowedVmExes[i], pwszFilename))
+ {
+ pImage->pszName = g_apszSupNtVpAllowedVmExes[i];
+ pImage->fDll = false;
+ break;
+ }
+ }
+ if (!pImage->pszName)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NOT_KNOWN_DLL_OR_EXE,
+ "Unknown image file %ls at %p.", pImage->Name.UniStr.Buffer, pMemInfo->BaseAddress);
+
+ /*
+ * Since it's a new image, we expect to be at the start of the mapping now.
+ */
+ if (pMemInfo->AllocationBase != pMemInfo->BaseAddress)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_IMAGE_MAPPING_BASE_ERROR,
+ "Invalid AllocationBase/BaseAddress for %s: %p vs %p.",
+ pImage->pszName, pMemInfo->AllocationBase, pMemInfo->BaseAddress);
+
+ /*
+ * Check for size/rva overflow.
+ */
+ if (pMemInfo->RegionSize >= _2G)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_LARGE_REGION,
+ "Region 0 of image %s is too large: %p.", pImage->pszName, pMemInfo->RegionSize);
+
+ /*
+ * Fill in details from the memory info.
+ */
+ pImage->uImageBase = (uintptr_t)pMemInfo->AllocationBase;
+ pImage->cbImage = pMemInfo->RegionSize;
+ pImage->cRegions = 1;
+ pImage->aRegions[0].uRva = 0;
+ pImage->aRegions[0].cb = (uint32_t)pMemInfo->RegionSize;
+ pImage->aRegions[0].fProt = pMemInfo->Protect;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Records an additional memory region for an image.
+ *
+ * @returns VBox status code.
+ * @param pImage The image.
+ * @param pMemInfo The memory information for the region.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpAddRegion(PSUPHNTVPIMAGE pImage, PMEMORY_BASIC_INFORMATION pMemInfo, PRTERRINFO pErrInfo)
+{
+ /*
+ * Make sure the base address matches.
+ */
+ if (pImage->uImageBase != (uintptr_t)pMemInfo->AllocationBase)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUPLIB_NT_PROCESS_UNTRUSTED_3,
+ "Base address mismatch for %s: have %p, found %p for region %p LB %#zx.",
+ pImage->pszName, pImage->uImageBase, pMemInfo->AllocationBase,
+ pMemInfo->BaseAddress, pMemInfo->RegionSize);
+
+ /*
+ * Check for size and rva overflows.
+ */
+ uintptr_t uRva = (uintptr_t)pMemInfo->BaseAddress - pImage->uImageBase;
+ if (pMemInfo->RegionSize >= _2G)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_LARGE_REGION,
+ "Region %u of image %s is too large: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
+ if (uRva >= _2G)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_HIGH_REGION_RVA,
+ "Region %u of image %s is too high: %p/%p.", pImage->pszName, pMemInfo->RegionSize, uRva);
+
+
+ /*
+ * Record the region.
+ */
+ uint32_t iRegion = pImage->cRegions;
+ if (iRegion + 1 >= RT_ELEMENTS(pImage->aRegions))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_MANY_IMAGE_REGIONS,
+ "Too many regions for %s.", pImage->pszName);
+ pImage->aRegions[iRegion].uRva = (uint32_t)uRva;
+ pImage->aRegions[iRegion].cb = (uint32_t)pMemInfo->RegionSize;
+ pImage->aRegions[iRegion].fProt = pMemInfo->Protect;
+ pImage->cbImage = pImage->aRegions[iRegion].uRva + pImage->aRegions[iRegion].cb;
+ pImage->cRegions++;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Scans the virtual memory of the process.
+ *
+ * This collects the locations of DLLs and the EXE, and verifies that executable
+ * memory is only associated with these.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this.
+ * @param hProcess The process to verify.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpScanVirtualMemory(PSUPHNTVPSTATE pThis, HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+ uint32_t cXpExceptions = 0;
+ uintptr_t cbAdvance = 0;
+ uintptr_t uPtrWhere = 0;
+ for (uint32_t i = 0; i < 1024; i++)
+ {
+ SIZE_T cbActual = 0;
+ MEMORY_BASIC_INFORMATION MemInfo = { 0, 0, 0, 0, 0, 0, 0 };
+ NTSTATUS rcNt = g_pfnNtQueryVirtualMemory(hProcess,
+ (void const *)uPtrWhere,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if (rcNt == STATUS_INVALID_PARAMETER)
+ return VINF_SUCCESS;
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_ERROR,
+ "NtQueryVirtualMemory failed for %p: %#x", uPtrWhere, rcNt);
+ }
+
+ /*
+ * Record images.
+ */
+ if ( MemInfo.Type == SEC_IMAGE
+ || MemInfo.Type == SEC_PROTECTED_IMAGE
+ || MemInfo.Type == (SEC_IMAGE | SEC_PROTECTED_IMAGE))
+ {
+ uint32_t iImg = pThis->cImages;
+ rcNt = g_pfnNtQueryVirtualMemory(hProcess,
+ (void const *)uPtrWhere,
+ MemorySectionName,
+ &pThis->aImages[iImg].Name,
+ sizeof(pThis->aImages[iImg].Name) - sizeof(WCHAR),
+ &cbActual);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_VIRTUAL_MEMORY_NM_ERROR,
+ "NtQueryVirtualMemory/MemorySectionName failed for %p: %#x", uPtrWhere, rcNt);
+ pThis->aImages[iImg].Name.UniStr.Buffer[pThis->aImages[iImg].Name.UniStr.Length / sizeof(WCHAR)] = '\0';
+
+ /* New or existing image? */
+ bool fNew = true;
+ uint32_t iSearch = iImg;
+ while (iSearch-- > 0)
+ if (supHardNtVpAreUniStringsEqual(&pThis->aImages[iSearch].Name.UniStr, &pThis->aImages[iImg].Name.UniStr))
+ {
+ int rc = supHardNtVpAddRegion(&pThis->aImages[iSearch], &MemInfo, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ fNew = false;
+ break;
+ }
+ else if (pThis->aImages[iSearch].uImageBase == (uintptr_t)MemInfo.AllocationBase)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_MAPPING_NAME_CHANGED,
+ "Unexpected base address match");
+
+ if (fNew)
+ {
+ int rc = supHardNtVpNewImage(&pThis->aImages[iImg], &MemInfo, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ pThis->cImages++;
+ if (pThis->cImages >= RT_ELEMENTS(pThis->aImages))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_MANY_DLLS_LOADED,
+ "Internal error: aImages is full.\n");
+ }
+ }
+ /*
+ * XP, W2K3: Ignore the CSRSS read-only region as best we can.
+ */
+ else if ( (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
+ == PAGE_EXECUTE_READ
+ && cXpExceptions == 0
+ && (uintptr_t)MemInfo.BaseAddress >= UINT32_C(0x78000000)
+ /* && MemInfo.BaseAddress == pPeb->ReadOnlySharedMemoryBase */
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 0) )
+ cXpExceptions++;
+ /*
+ * Executable memory?
+ */
+ else if (MemInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_FOUND_EXEC_MEMORY,
+ "Found executable memory at %p (%p LB %#zx): type=%#x prot=%#x state=%#x aprot=%#x abase=%p",
+ uPtrWhere,
+ MemInfo.BaseAddress,
+ MemInfo.RegionSize,
+ MemInfo.Type,
+ MemInfo.Protect,
+ MemInfo.State,
+ MemInfo.AllocationBase,
+ MemInfo.AllocationProtect);
+
+ /*
+ * Advance.
+ */
+ cbAdvance = MemInfo.RegionSize;
+ if (uPtrWhere + cbAdvance <= uPtrWhere)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_EMPTY_REGION_TOO_LARGE,
+ "Empty region at %p.", uPtrWhere);
+ uPtrWhere += MemInfo.RegionSize;
+ }
+
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_TOO_MANY_MEMORY_REGIONS,
+ "Too many virtual memory regions.\n");
+}
+
+
+/**
+ * Check the integrity of the executable of the process.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this.
+ * @param hProcess The process to verify.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpCheckExe(PSUPHNTVPSTATE pThis, HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+ /*
+ * Make sure there is exactly one executable image.
+ */
+ unsigned cExecs = 0;
+ unsigned iExe = ~0U;
+ unsigned i = pThis->cImages;
+ while (i-- > 0)
+ {
+ if (!pThis->aImages[i].fDll)
+ {
+ cExecs++;
+ iExe = i;
+ }
+ }
+ if (cExecs == 0)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NO_FOUND_NO_EXE_MAPPING,
+ "No executable mapping found in the virtual address space.");
+ if (cExecs != 1)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_FOUND_MORE_THAN_ONE_EXE_MAPPING,
+ "Found more than one executable mapping in the virtual address space.");
+ PSUPHNTVPIMAGE pImage = &pThis->aImages[iExe];
+
+ /*
+ * Check that it matches the executable image of the process.
+ */
+ int rc;
+ ULONG cbUniStr = sizeof(UNICODE_STRING) + RTPATH_MAX * sizeof(RTUTF16);
+ PUNICODE_STRING pUniStr = (PUNICODE_STRING)suplibHardenedAllocZ(cbUniStr);
+ if (!pUniStr)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NO_MEMORY,
+ "Error allocating %zu bytes for process name.", cbUniStr);
+ ULONG cbIgn = 0;
+ NTSTATUS rcNt = NtQueryInformationProcess(hProcess, ProcessImageFileName, pUniStr, cbUniStr - sizeof(WCHAR), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (supHardNtVpAreUniStringsEqual(pUniStr, &pImage->Name.UniStr))
+ rc = VINF_SUCCESS;
+ else
+ {
+ pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ rc = supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_EXE_VS_PROC_NAME_MISMATCH,
+ "Process image name does not match the exectuable we found: %ls vs %ls.",
+ pUniStr->Buffer, pImage->Name.UniStr.Buffer);
+ }
+ }
+ else
+ rc = supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_NM_ERROR,
+ "NtQueryInformationProcess/ProcessImageFileName failed: %#x", rcNt);
+ suplibHardenedFree(pUniStr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Validate the signing of the executable image.
+ * This will load the fDllCharecteristics and fImageCharecteristics members we use below.
+ */
+ rc = supHardNtVpVerifyImage(pThis, pImage, hProcess, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check linking requirements.
+ */
+ SECTION_IMAGE_INFORMATION ImageInfo;
+ rcNt = NtQueryInformationProcess(hProcess, ProcessImageInformation, &ImageInfo, sizeof(ImageInfo), NULL);
+ if (!NT_SUCCESS(rcNt))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NT_QI_PROCESS_IMG_INFO_ERROR,
+ "NtQueryInformationProcess/ProcessImageInformation failed: %#x", rcNt);
+ if ( !(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_EXE_MISSING_FORCE_INTEGRITY,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY to be set.",
+ ImageInfo.DllCharacteristics);
+ if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_EXE_MISSING_DYNAMIC_BASE,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE to be set.",
+ ImageInfo.DllCharacteristics);
+ if (!(ImageInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT))
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_EXE_MISSING_NX_COMPAT,
+ "EXE DllCharacteristics=%#x, expected IMAGE_DLLCHARACTERISTICS_NX_COMPAT to be set.",
+ ImageInfo.DllCharacteristics);
+
+ if (pImage->fDllCharecteristics != ImageInfo.DllCharacteristics)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
+ "EXE Info.DllCharacteristics=%#x fDllCharecteristics=%#x.",
+ ImageInfo.DllCharacteristics, pImage->fDllCharecteristics);
+
+ if (pImage->fImageCharecteristics != ImageInfo.ImageCharacteristics)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_DLL_CHARECTERISTICS_MISMATCH,
+ "EXE Info.ImageCharacteristics=%#x fImageCharecteristics=%#x.",
+ ImageInfo.ImageCharacteristics, pImage->fImageCharecteristics);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Check the integrity of the DLLs found in the process.
+ *
+ * @returns VBox status code.
+ * @param pThis The process scanning state structure. Details
+ * about images are added to this.
+ * @param hProcess The process to verify.
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+static int supHardNtVpCheckDlls(PSUPHNTVPSTATE pThis, HANDLE hProcess, PRTERRINFO pErrInfo)
+{
+ /*
+ * Check for duplicate entries.
+ */
+ uint32_t i = pThis->cImages;
+ while (i-- > 1)
+ {
+ const char *pszName = pThis->aImages[i].pszName;
+ uint32_t j = i;
+ while (j-- > 0)
+ if (pThis->aImages[j].pszName == pszName)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_DUPLICATE_DLL_MAPPING,
+ "Duplicate image entries for %s: %ls and %ls",
+ pszName, pThis->aImages[i].Name.UniStr.Buffer, pThis->aImages[j].Name.UniStr.Buffer);
+ }
+
+ /*
+ * Check that both ntdll and kernel32 are present.
+ * ASSUMES the entries in g_apszSupNtVpAllowedDlls are all lower case.
+ */
+ uint32_t iNtDll = UINT32_MAX;
+ uint32_t iKernel32 = UINT32_MAX;
+ uint32_t iApiSetSchema = UINT32_MAX;
+ i = pThis->cImages;
+ while (i-- > 0)
+ if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "ntdll.dll") == 0)
+ iNtDll = i;
+ else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "kernel32.dll") == 0)
+ iKernel32 = i;
+ else if (suplibHardenedStrCmp(pThis->aImages[i].pszName, "apisetschema.dll") == 0)
+ iApiSetSchema = i;
+ if (iNtDll == UINT32_MAX)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NO_NTDLL_MAPPING,
+ "The process has no NTDLL.DLL.");
+ if (iKernel32 == UINT32_MAX)
+ return supHardNtVpSetInfo(pErrInfo, VERR_SUP_VP_NO_KERNEL32_MAPPING,
+ "The process has no KERNEL32.DLL.");
+
+
+ /*
+ * Verify that the DLLs are correctly signed (by MS).
+ */
+ i = pThis->cImages;
+ while (i-- > 0)
+ {
+ pThis->aImages[i].fNtCreateSectionPatch = i == iNtDll;
+ pThis->aImages[i].fApiSetSchemaOnlySection1 = i == iApiSetSchema && pThis->aImages[i].cRegions == 1;
+
+ int rc = supHardNtVpVerifyImage(pThis, &pThis->aImages[i], hProcess, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies the given process.
+ *
+ * The following requirements are checked:
+ * - The process only has one thread, the calling thread.
+ * - The process has no debugger attached.
+ * - The executable image of the process is verified to be signed with
+ * certificate known to this code at build time.
+ * - The executable image is one of a predefined set.
+ * - The process has only a very limited set of system DLLs loaded.
+ * - The system DLLs signatures check out fine.
+ * - The only executable memory in the process belongs to the system DLLs and
+ * the executable image.
+ *
+ * @returns VBox status code.
+ * @param hProcess The process to verify.
+ * @param hThread A thread in the process (the caller).
+ * @param pErrInfo Pointer to error info structure. Optional.
+ */
+DECLHIDDEN(int) supHardenedWinVerifyProcess(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
+{
+ int rc = supHardNtVpThread(hProcess, hThread, pErrInfo);
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpDebugger(hProcess, pErrInfo);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ PSUPHNTVPSTATE pThis = supHardNtVpCreateState(pErrInfo);
+ if (pThis)
+ {
+ rc = supHardNtVpScanVirtualMemory(pThis, hProcess, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpCheckExe(pThis, hProcess, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = supHardNtVpCheckDlls(pThis, hProcess, pErrInfo);
+
+ suplibHardenedFree(pThis);
+ }
+ else
+ rc = VERR_SUP_VP_NO_MEMORY_STATE;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp
index 336724548c9..3881efa76aa 100644
--- a/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp
+++ b/src/VBox/HostDrivers/Support/win/SUPLib-win.cpp
@@ -30,6 +30,7 @@
#define LOG_GROUP LOG_GROUP_SUP
#ifdef IN_SUP_HARDENED_R3
# undef DEBUG /* Warning: disables RT_STRICT */
+# undef LOG_DISABLED
# define LOG_DISABLED
/** @todo RTLOGREL_DISABLED */
# include <iprt/log.h>
@@ -38,11 +39,7 @@
#endif
#define USE_NT_DEVICE_IO_CONTROL_FILE
-#ifdef USE_NT_DEVICE_IO_CONTROL_FILE
-# include <iprt/nt/nt-and-windows.h>
-#else
-# include <Windows.h>
-#endif
+#include <iprt/nt/nt-and-windows.h>
#include <VBox/sup.h>
#include <VBox/types.h>
@@ -54,6 +51,9 @@
#include <iprt/string.h>
#include "../SUPLibInternal.h"
#include "../SUPDrvIOC.h"
+#ifdef VBOX_WITH_HARDENING
+# include "win/SUPHardenedVerify-win.h"
+#endif
/*******************************************************************************
@@ -74,11 +74,13 @@
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
+#ifndef IN_SUP_HARDENED_R3
static int suplibOsCreateService(void);
//unused: static int suplibOsUpdateService(void);
static int suplibOsDeleteService(void);
static int suplibOsStartService(void);
static int suplibOsStopService(void);
+#endif
#ifdef USE_NT_DEVICE_IO_CONTROL_FILE
static int suplibConvertNtStatus(NTSTATUS rcNt);
#else
@@ -86,64 +88,104 @@ static int suplibConvertWin32Err(int);
#endif
-
-
int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, bool fUnrestricted)
{
/*
- * Nothing to do if pre-inited.
+ * Almost nothing to do if pre-inited.
*/
if (fPreInited)
+ {
+#if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_HARDENED_R3)
+# ifdef IN_SUP_R3_STATIC
+ return VERR_NOT_SUPPORTED;
+# else
+ supR3HardenedWinInitVersion();
+ return supHardenedWinInitImageVerifier(NULL);
+# endif
+#else
return VINF_SUCCESS;
+#endif
+ }
/*
* Try open the device.
*/
- HANDLE hDevice = CreateFile(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR,
- GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
- NULL);
- if (hDevice == INVALID_HANDLE_VALUE)
- {
#ifndef IN_SUP_HARDENED_R3
- /*
- * Try start the service and retry opening it.
- */
- suplibOsStartService();
-
- hDevice = CreateFile(fUnrestricted ? DEVICE_NAME_SYS : DEVICE_NAME_USR,
- GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
- NULL);
- if (hDevice == INVALID_HANDLE_VALUE)
-#endif /* !IN_SUP_HARDENED_R3 */
+ uint32_t cTry = 0;
+#endif
+ HANDLE hDevice;
+ for (;;)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ static const WCHAR s_wszName[] = L"\\Device\\VBoxDrvU";
+ UNICODE_STRING NtName;
+ NtName.Buffer = (PWSTR)s_wszName;
+ NtName.Length = sizeof(s_wszName) - sizeof(WCHAR) * (fUnrestricted ? 2 : 1);
+ NtName.MaximumLength = NtName.Length;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+
+ hDevice = RTNT_INVALID_HANDLE_VALUE;
+
+ NTSTATUS rcNt = NtCreateFile(&hDevice,
+ GENERIC_READ | GENERIC_WRITE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
{
- int rc = GetLastError();
- switch (rc)
+#ifndef IN_SUP_HARDENED_R3
+ /*
+ * Failed to open, try starting the service and reopen the device
+ * exactly once.
+ */
+ if (cTry == 0 && !NT_SUCCESS(rcNt))
+ {
+ cTry++;
+ suplibOsStartService();
+ continue;
+ }
+#endif
+ switch (rcNt)
{
/** @todo someone must test what is actually returned. */
- case ERROR_DEV_NOT_EXIST:
- case ERROR_DEVICE_NOT_CONNECTED:
- case ERROR_BAD_DEVICE:
- case ERROR_DEVICE_REMOVED:
- case ERROR_DEVICE_NOT_AVAILABLE:
+ case STATUS_DEVICE_DOES_NOT_EXIST:
+ case STATUS_DEVICE_NOT_CONNECTED:
+ //case ERROR_BAD_DEVICE:
+ case STATUS_DEVICE_REMOVED:
+ //case ERROR_DEVICE_NOT_AVAILABLE:
return VERR_VM_DRIVER_LOAD_ERROR;
- case ERROR_PATH_NOT_FOUND:
- case ERROR_FILE_NOT_FOUND:
+ case STATUS_OBJECT_PATH_NOT_FOUND:
+ case STATUS_NO_SUCH_DEVICE:
+ case STATUS_NO_SUCH_FILE:
+ case STATUS_OBJECT_NAME_NOT_FOUND:
return VERR_VM_DRIVER_NOT_INSTALLED;
- case ERROR_ACCESS_DENIED:
- case ERROR_SHARING_VIOLATION:
+ case STATUS_ACCESS_DENIED:
+ case STATUS_SHARING_VIOLATION:
return VERR_VM_DRIVER_NOT_ACCESSIBLE;
+ case STATUS_UNSUCCESSFUL:
+ return VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0;
+ case STATUS_TRUST_FAILURE:
+ return VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1;
+ case STATUS_TOO_LATE:
+ return VERR_SUPDRV_HARDENING_EVIL_HANDLE;
default:
+
+ return rcNt;
return VERR_VM_DRIVER_OPEN_ERROR;
}
-
- return -1 /** @todo define proper error codes for suplibOsInit failure. */;
}
+ break;
}
/*
@@ -154,7 +196,6 @@ int suplibOsInit(PSUPLIBDATA pThis, bool fPreInited, bool fUnrestricted)
return VINF_SUCCESS;
}
-
#ifndef IN_SUP_HARDENED_R3
int suplibOsInstall(void)
@@ -439,7 +480,9 @@ static int suplibOsStartService(void)
*/
fRc = StartService(hService, 0, NULL);
DWORD LastError = GetLastError(); NOREF(LastError);
+#ifndef DEBUG_bird
AssertMsg(fRc, ("StartService failed with LastError=%Rwa\n", LastError));
+#endif
}
/*
@@ -651,6 +694,10 @@ static int suplibConvertNtStatus(NTSTATUS rcNt)
case STATUS_REVISION_MISMATCH: return VERR_VERSION_MISMATCH;
}
+ /* See VBoxDrvNtErr2NtStatus. */
+ if (((uint32_t)rcNt & 0xffff0000) == UINT32_C(0xe9860000)) /** @todo defines for these? */
+ return (int)((uint32_t)rcNt | UINT32_C(0xffff0000));
+
/* Fall back on IPRT for the rest. */
return RTErrConvertFromNtStatus(rcNt);
}
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
new file mode 100644
index 00000000000..2700b778c14
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
@@ -0,0 +1,1650 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), windows bits.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+#include <AccCtrl.h>
+#include <AclApi.h>
+#ifndef PROCESS_SET_LIMITED_INFORMATION
+# define PROCESS_SET_LIMITED_INFORMATION 0x2000
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+
+#include "SUPLibInternal.h"
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The first argument of a respawed stub argument.
+ * This just needs to be unique enough to avoid most confusion with real
+ * executable names, there are other checks in place to make sure we've respanwed. */
+#define SUPR3_RESPAWN_ARG0 "81954AF5-4D2F-31EB-A142-B7AF187A1C41-suplib-2ndchild"
+
+/** Unconditional assertion. */
+#define SUPR3HARDENED_ASSERT(a_Expr) \
+ do { \
+ if (!(a_Expr)) \
+ supR3HardenedFatal("%s: %s", __FUNCTION__, #a_Expr); \
+ } while (0)
+
+/** Unconditional assertion of NT_SUCCESS. */
+#define SUPR3HARDENED_ASSERT_NT_SUCCESS(a_Expr) \
+ do { \
+ NTSTATUS rcNtAssert = (a_Expr); \
+ if (!NT_SUCCESS(rcNtAssert)) \
+ supR3HardenedFatal("%s: %s -> %#x", __FUNCTION__, #a_Expr, rcNtAssert); \
+ } while (0)
+
+/** Unconditional assertion of a WIN32 API returning non-FALSE. */
+#define SUPR3HARDENED_ASSERT_WIN32_SUCCESS(a_Expr) \
+ do { \
+ BOOL fRcAssert = (a_Expr); \
+ if (fRcAssert == FALSE) \
+ supR3HardenedFatal("%s: %s -> %#x", __FUNCTION__, #a_Expr, GetLastError()); \
+ } while (0)
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Security descriptor cleanup structure.
+ */
+typedef struct MYSECURITYCLEANUP
+{
+ union
+ {
+ SID Sid;
+ uint8_t abPadding[SECURITY_MAX_SID_SIZE];
+ } Everyone, Owner, User, Login;
+ union
+ {
+ ACL AclHdr;
+ uint8_t abPadding[1024];
+ } Acl;
+ PSECURITY_DESCRIPTOR pSecDesc;
+} MYSECURITYCLEANUP;
+/** Pointer to security cleanup structure. */
+typedef MYSECURITYCLEANUP *PMYSECURITYCLEANUP;
+
+
+/**
+ * Image verifier cache entry.
+ */
+typedef struct VERIFIERCACHEENTRY
+{
+ /** Pointer to the next entry with the same hash value. */
+ struct VERIFIERCACHEENTRY * volatile pNext;
+ /** The file handle. */
+ HANDLE hFile;
+ /** If fIndexNumber is set, this is an file system internal file identifier. */
+ LARGE_INTEGER IndexNumber;
+ /** The path hash value. */
+ uint32_t uHash;
+ /** The verification result. */
+ int rc;
+ /** Whether IndexNumber is valid */
+ bool fIndexNumberValid;
+ /** cwcPath * sizeof(RTUTF16). */
+ uint16_t cbPath;
+ /** The full path of this entry (variable size). */
+ RTUTF16 wszPath[1];
+} VERIFIERCACHEENTRY;
+/** Pointer to an image verifier path entry. */
+typedef VERIFIERCACHEENTRY *PVERIFIERCACHEENTRY;
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** @name Global variables initialized by suplibHardenedWindowsMain.
+ * @{ */
+/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED. */
+uint32_t g_uNtVerCombined = 0;
+/** Count calls to the special main function for linking santity checks. */
+static uint32_t volatile g_cSuplibHardenedWindowsMainCalls;
+/** The UTF-16 windows path to the executable. */
+RTUTF16 g_wszSupLibHardenedExePath[1024];
+/** The NT path of the executable. */
+SUPSYSROOTDIRBUF g_SupLibHardenedExeNtPath;
+/** The offset into g_SupLibHardenedExeNtPath of the executable name (WCHAR,
+ * not byte). This also gives the length of the exectuable directory path,
+ * including a trailing slash. */
+uint32_t g_offSupLibHardenedExeNtName;
+/** @} */
+
+/** @name Hook related variables.
+ * @{ */
+/** The jump back address of the patched NtCreateSection. */
+extern "C" PFNRT g_pfnNtCreateSectionJmpBack = NULL;
+/** Pointer to the bit of assembly code that will perform the original
+ * NtCreateSection operation. */
+static NTSTATUS (NTAPI * g_pfnNtCreateSectionReal)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
+ PLARGE_INTEGER, ULONG, ULONG, HANDLE);
+/** The hash table of verifier cache . */
+static VERIFIERCACHEENTRY * volatile g_apVerifierCache[128];
+/** @ */
+
+/** Static error info structure used during init. */
+static RTERRINFOSTATIC g_ErrInfoStatic;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+#ifdef RT_ARCH_AMD64
+# define SYSCALL(a_Num) DECLASM(void) RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num)(void)
+# include "NtCreateSection-template-amd64-syscall-type-1.h"
+# undef SYSCALL
+#endif
+#ifdef RT_ARCH_X86
+# define SYSCALL(a_Num) DECLASM(void) RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num)(void)
+# include "NtCreateSection-template-x86-syscall-type-1.h"
+# undef SYSCALL
+#endif
+
+
+
+/**
+ * Simple wide char search routine.
+ *
+ * @returns Pointer to the first location of @a wcNeedle in @a pwszHaystack.
+ * NULL if not found.
+ * @param pwszHaystack Pointer to the string that should be searched.
+ * @param wcNeedle The character to search for.
+ */
+static PRTUTF16 suplibHardenedWStrChr(PCRTUTF16 pwszHaystack, RTUTF16 wcNeedle)
+{
+ for (;;)
+ {
+ RTUTF16 wcCur = *pwszHaystack;
+ if (wcCur == wcNeedle)
+ return (PRTUTF16)pwszHaystack;
+ if (wcCur == '\0')
+ return NULL;
+ pwszHaystack++;
+ }
+}
+
+
+/**
+ * Simple wide char string length routine.
+ *
+ * @returns The number of characters in the given string. (Excludes the
+ * terminator.)
+ * @param pwsz The string.
+ */
+static size_t suplibHardenedWStrLen(PCRTUTF16 pwsz)
+{
+ PCRTUTF16 pwszCur = pwsz;
+ while (*pwszCur != '\0')
+ pwszCur++;
+ return pwszCur - pwsz;
+}
+
+
+/**
+ * Allocate zero filled memory on the heap.
+ *
+ * @returns Pointer to the memory. Will never return NULL, triggers a fatal
+ * error instead.
+ * @param cb The number of bytes to allocate.
+ */
+DECLHIDDEN(void *) suplibHardenedAllocZ(size_t cb)
+{
+ void *pv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
+ if (!pv)
+ supR3HardenedFatal("HeapAlloc failed to allocate %zu bytes.\n", cb);
+ return pv;
+}
+
+
+/**
+ * Reallocates memory on the heap.
+ *
+ * @returns Pointer to the resized memory block. Will never return NULL,
+ * triggers a fatal error instead.
+ * @param pvOld The old memory block.
+ * @param cbNew The new block size.
+ */
+DECLHIDDEN(void *) suplibHardenedReAlloc(void *pvOld, size_t cbNew)
+{
+ if (!pvOld)
+ return suplibHardenedAllocZ(cbNew);
+ void *pv = HeapReAlloc(GetProcessHeap(), 0 /*dwFlags*/, pvOld, cbNew);
+ if (!pv)
+ supR3HardenedFatal("HeapReAlloc failed to allocate %zu bytes.\n", cbNew);
+ return pv;
+}
+
+
+/**
+ * Frees memory allocated by suplibHardenedAlloc, suplibHardenedAllocZ or
+ * suplibHardenedReAlloc.
+ *
+ * @param pv Pointer to the memeory to be freed.
+ */
+DECLHIDDEN(void) suplibHardenedFree(void *pv)
+{
+ if (pv)
+ HeapFree(GetProcessHeap(), 0 /* dwFlags*/, pv);
+}
+
+
+/**
+ * Wrapper around LoadLibraryEx that deals with the UTF-8 to UTF-16 conversion
+ * and supplies the right flags.
+ *
+ * @returns Module handle on success, NULL on failure.
+ * @param pszName The full path to the DLL.
+ * @param fSystem32Only Whether to only look for imports in the system32
+ * directory. If set to false, the application
+ * directory is also searched.
+ */
+DECLHIDDEN(void *) supR3HardenedWinLoadLibrary(const char *pszName, bool fSystem32Only)
+{
+ WCHAR wszPath[RTPATH_MAX];
+ PRTUTF16 pwszPath = wszPath;
+ int rc = RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszPath, RT_ELEMENTS(wszPath), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ while (*pwszPath)
+ {
+ if (*pwszPath == '/')
+ *pwszPath = '\\';
+ pwszPath++;
+ }
+
+ DWORD fFlags = 0;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
+ {
+ fFlags |= LOAD_LIBRARY_SEARCH_SYSTEM32;
+ if (!fSystem32Only)
+ fFlags |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
+ }
+
+ void *pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, fFlags);
+
+ /* Vista, W7, W2K8R might not work without KB2533623, so retry with no flags. */
+ if ( !pvRet
+ && fFlags
+ && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
+ && GetLastError() == ERROR_INVALID_PARAMETER)
+ pvRet = (void *)LoadLibraryExW(wszPath, NULL /*hFile*/, 0);
+
+ return pvRet;
+ }
+ supR3HardenedFatal("RTStrToUtf16Ex failed on '%s': %Rrc", pszName, rc);
+ return NULL;
+}
+
+
+/**
+ * Gets the internal index number of the file.
+ *
+ * @returns True if we got an index number, false if not.
+ * @param hFile The file in question.
+ * @param pIndexNumber where to return the index number.
+ */
+static bool supR3HardenedWinVerifyCacheGetIndexNumber(HANDLE hFile, PLARGE_INTEGER pIndexNumber)
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pIndexNumber, sizeof(*pIndexNumber), FileInternalInformation);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+#ifdef DEBUG_bird
+ if (!NT_SUCCESS(rcNt))
+ __debugbreak();
+#endif
+ return NT_SUCCESS(rcNt) && pIndexNumber->QuadPart != 0;
+}
+
+
+/**
+ * Calculates the hash value for the given UTF-16 string.
+ *
+ * @returns Hash value.
+ * @param pUniStr String to hash.
+ */
+static uint32_t supR3HardenedWinVerifyCacheHashPath(PCUNICODE_STRING pUniStr)
+{
+ uint32_t uHash = 0;
+ unsigned cwcLeft = pUniStr->Length / sizeof(WCHAR);
+ PRTUTF16 pwc = pUniStr->Buffer;
+
+ while (cwcLeft-- > 0)
+ {
+ RTUTF16 wc = *pwc++;
+ uHash = wc + (uHash << 6) + (uHash << 16) - uHash;
+ }
+ return uHash;
+}
+
+
+/**
+ * Inserts the given verifier result into the cache.
+ *
+ * @param pUniStr The full path of the image.
+ * @param hFile The file handle - must either be entered into
+ * the cache or closed.
+ * @param rc The verifier result.
+ * @param fCacheable Whether this is a cacheable result. Passed in
+ * here instead of being handled by the caller to
+ * save code duplication.
+ */
+static void supR3HardenedWinVerifyCacheInsert(PCUNICODE_STRING pUniStr, HANDLE hFile, int rc, bool fCacheable)
+{
+ /*
+ * Don't cache anything until we've got the WinVerifyTrust API up and running.
+ */
+ if ( g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_VERIFY_TRUST_READY
+ && fCacheable)
+ {
+ /*
+ * Allocate and initalize a new entry.
+ */
+ PVERIFIERCACHEENTRY pEntry = (PVERIFIERCACHEENTRY)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof(VERIFIERCACHEENTRY) + pUniStr->Length);
+ if (pEntry)
+ {
+ pEntry->pNext = NULL;
+ pEntry->hFile = hFile;
+ pEntry->rc = rc;
+ pEntry->uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
+ pEntry->cbPath = pUniStr->Length;
+ memcpy(pEntry->wszPath, pUniStr->Buffer, pUniStr->Length);
+ pEntry->wszPath[pUniStr->Length / sizeof(WCHAR)] = '\0';
+ pEntry->fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &pEntry->IndexNumber);
+
+ /*
+ * Try insert it, careful with concurrent code as well as potential duplicates.
+ */
+ uint32_t iHashTab = pEntry->uHash % RT_ELEMENTS(g_apVerifierCache);
+ VERIFIERCACHEENTRY * volatile *ppEntry = &g_apVerifierCache[iHashTab];
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgPtr(ppEntry, pEntry, NULL))
+ return;
+ PVERIFIERCACHEENTRY pOther = *ppEntry;
+ if (!pOther)
+ continue;
+ if ( pOther->uHash == pEntry->uHash
+ && pOther->cbPath == pEntry->cbPath
+ && memcmp(pOther->wszPath, pEntry->wszPath, pEntry->cbPath) == 0)
+ break;
+ ppEntry = &pOther->pNext;
+ }
+
+ /* Duplicate entry. */
+#ifdef DEBUG_bird
+ __debugbreak();
+#endif
+ HeapFree(GetProcessHeap(), 0 /* dwFlags*/, pEntry);
+ }
+ }
+ NtClose(hFile);
+}
+
+
+/**
+ * Looks up an entry in the verifier hash table.
+ *
+ * @return Pointer to the entry on if found, NULL if not.
+ * @param pUniStr The full path of the image.
+ * @param hFile The file handle.
+ */
+static PVERIFIERCACHEENTRY supR3HardenedWinVerifyCacheLookup(PCUNICODE_STRING pUniStr, HANDLE hFile)
+{
+ PRTUTF16 const pwszPath = pUniStr->Buffer;
+ uint16_t const cbPath = pUniStr->Length;
+ uint32_t uHash = supR3HardenedWinVerifyCacheHashPath(pUniStr);
+ uint32_t iHashTab = uHash % RT_ELEMENTS(g_apVerifierCache);
+ PVERIFIERCACHEENTRY pCur = g_apVerifierCache[iHashTab];
+ while (pCur)
+ {
+ if ( pCur->uHash == uHash
+ && pCur->cbPath == cbPath
+ && memcmp(pCur->wszPath, pwszPath, cbPath) == 0)
+ {
+
+ if (!pCur->fIndexNumberValid)
+ return pCur;
+ LARGE_INTEGER IndexNumber;
+ bool fIndexNumberValid = supR3HardenedWinVerifyCacheGetIndexNumber(hFile, &IndexNumber);
+ if ( fIndexNumberValid
+ && IndexNumber.QuadPart == pCur->IndexNumber.QuadPart)
+ return pCur;
+#ifdef DEBUG_bird
+ __debugbreak();
+#endif
+ }
+ pCur = pCur->pNext;
+ }
+ return NULL;
+}
+
+
+/**
+ * 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)
+ {
+ /*
+ * Query the name of the file, making sure to zero terminator the
+ * string. (2nd half of buffer is used for error info, see below.)
+ */
+ union
+ {
+ UNICODE_STRING UniStr;
+ uint8_t abBuffer[sizeof(UNICODE_STRING) + 2048 * sizeof(WCHAR)];
+ } uBuf;
+ RT_ZERO(uBuf);
+ ULONG cbNameBuf;
+ NTSTATUS rcNt = NtQueryObject(hFile, ObjectNameInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR) - 128, &cbNameBuf);
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: NtQueryObject -> %#x (fImage=%d fExecMap=%d fExecProt=%d)\n",
+ fImage, fExecMap, fExecProt);
+ return rcNt;
+ }
+
+ /*
+ * Check the cache.
+ */
+ PVERIFIERCACHEENTRY pCacheHit = supR3HardenedWinVerifyCacheLookup(&uBuf.UniStr, hFile);
+ if (pCacheHit)
+ {
+ SUP_DPRINTF(("supR3HardenedMonitor_NtCreateSection: cache hit (%Rrc) on %ls\n", pCacheHit->rc, pCacheHit->wszPath));
+ if (RT_SUCCESS(pCacheHit->rc))
+ return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: cached rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls\n",
+ pCacheHit->rc, fImage, fExecMap, fExecProt, uBuf.UniStr.Buffer);
+ return STATUS_TRUST_FAILURE;
+ }
+
+ /*
+ * On XP the loader might hand us handles with just FILE_EXECUTE and
+ * SYNCRHONIZE, the means reading will fail later on. So, we might
+ * have to reopen the file here in order to validate it - annoying.
+ */
+ HANDLE hMyFile = NULL;
+ rcNt = NtDuplicateObject(NtCurrentProcess(), hFile, NtCurrentProcess(),
+ &hMyFile,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 0 /* Handle attributes*/, 0 /* Options */);
+ if (!NT_SUCCESS(rcNt))
+ {
+ if (rcNt == STATUS_ACCESS_DENIED)
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ 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 | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: Failed to duplicate and open the file: rcNt=%#x hFile=%p %ls\n",
+ rcNt, hFile, uBuf.UniStr.Buffer);
+ return rcNt;
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+
+ supR3HardenedError(VINF_SUCCESS, false, "supR3HardenedMonitor_NtCreateSection: NtDuplicateObject(,%#x,) failed: %#x\n", 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(("supR3HardenedMonitor_NtCreateSection: Applying the drop-exec-kludge for '%ls'\n", uBuf.UniStr.Buffer));
+ if (fAccess & SECTION_MAP_EXECUTE)
+ fAccess = (fAccess & ~SECTION_MAP_EXECUTE) | SECTION_MAP_READ;
+ if (fProtect & PAGE_EXECUTE)
+ fProtect = (fProtect & ~PAGE_EXECUTE) | PAGE_READONLY;
+ fProtect = (fProtect & ~UINT32_C(0xf0)) | ((fProtect & UINT32_C(0xe0)) >> 4);
+ return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
+ }
+ }
+
+ /*
+ * Check the path. We don't allow DLLs to be loaded from just anywhere:
+ * 1. System32 - normal code or cat signing.
+ * 2. WinSxS - normal code or cat signing.
+ * 3. VirtualBox - kernel code signing and integrity checks.
+ */
+ bool fSystem32 = false;
+ Assert(g_SupLibHardenedExeNtPath.UniStr.Buffer[g_offSupLibHardenedExeNtName - 1] == '\\');
+ uint32_t fFlags = 0;
+ if ( uBuf.UniStr.Length > g_System32NtPath.UniStr.Length
+ && memcmp(uBuf.UniStr.Buffer, g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length) == 0
+ && uBuf.UniStr.Buffer[g_System32NtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
+ {
+ fSystem32 = true;
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION;
+ }
+ else if ( uBuf.UniStr.Length > g_WinSxSNtPath.UniStr.Length
+ && memcmp(uBuf.UniStr.Buffer, g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length) == 0
+ && uBuf.UniStr.Buffer[g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR)] == '\\')
+ fFlags |= SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION;
+ else if ( uBuf.UniStr.Length > g_offSupLibHardenedExeNtName
+ && memcmp(uBuf.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Buffer,
+ g_offSupLibHardenedExeNtName * sizeof(WCHAR)) == 0)
+ fFlags |= SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING | SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT;
+ else
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: Not a trusted location: '%ls' (fImage=%d fExecMap=%d fExecProt=%d)\n",
+ uBuf.UniStr.Buffer, fImage, fExecMap, fExecProt);
+ if (hMyFile != hFile)
+ NtClose(hMyFile);
+ return STATUS_TRUST_FAILURE;
+ }
+
+ /*
+ * Do the verification. For better error message we borrow what's
+ * left of the path buffer for an RTERRINFO buffer.
+ */
+ RTERRINFO ErrInfo;
+ RTErrInfoInit(&ErrInfo, (char *)&uBuf.abBuffer[cbNameBuf], sizeof(uBuf) - cbNameBuf);
+
+ bool fCacheable = true;
+ int rc = supHardenedWinVerifyImageByHandle(hMyFile, uBuf.UniStr.Buffer, fFlags, &fCacheable, &ErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ supR3HardenedError(VINF_SUCCESS, false,
+ "supR3HardenedMonitor_NtCreateSection: rc=%Rrc fImage=%d fExecMap=%d fExecProt=%d %ls: %s\n",
+ rc, fImage, fExecMap, fExecProt, uBuf.UniStr.Buffer, ErrInfo.pszMsg);
+ if (hMyFile != hFile)
+ NtClose(hMyFile);
+ return STATUS_TRUST_FAILURE;
+ }
+ if (hMyFile != hFile)
+ supR3HardenedWinVerifyCacheInsert(&uBuf.UniStr, hMyFile, rc, fCacheable);
+ }
+ }
+
+ /*
+ * Call checked out OK, call the original.
+ */
+ return g_pfnNtCreateSectionReal(phSection, fAccess, pObjAttribs, pcbSection, fProtect, fAttribs, hFile);
+}
+
+
+#ifdef RT_ARCH_AMD64
+/**
+ * Tries to allocate memory between @a uStart and @a uEnd.
+ *
+ * @returns Pointer to the memory on success. NULL on failure.
+ * @param uStart The start address.
+ * @param uEnd The end address. This is lower than @a uStart
+ * if @a iDirection is negative, and higher if
+ * positive.
+ * @param iDirection The search direction.
+ * @param cbAlloc The number of bytes to allocate.
+ */
+static void *supR3HardenedWinAllocHookMemory(uintptr_t uStart, uintptr_t uEnd, intptr_t iDirection, size_t cbAlloc)
+{
+ size_t const cbAllocGranularity = _64K;
+ size_t const uAllocGranularityMask = ~(cbAllocGranularity - 1);
+ HANDLE const hProc = GetCurrentProcess();
+
+ /*
+ * Make uEnd the last valid return address.
+ */
+ if (iDirection > 0)
+ {
+ SUPR3HARDENED_ASSERT(uEnd > cbAlloc);
+ uEnd -= cbAlloc;
+ uEnd &= uAllocGranularityMask;
+ }
+ else
+ uEnd = RT_ALIGN_Z(uEnd, cbAllocGranularity);
+
+ /*
+ * Search for free memory.
+ */
+ uintptr_t uCur = uStart & uAllocGranularityMask;
+ for (;;)
+ {
+ /*
+ * Examine the memory at this address, if it's free, try make the allocation here.
+ */
+ SIZE_T cbIgn;
+ MEMORY_BASIC_INFORMATION MemInfo;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtQueryVirtualMemory(hProc,
+ (void *)uCur,
+ MemoryBasicInformation,
+ &MemInfo,
+ sizeof(MemInfo),
+ &cbIgn));
+ if ( MemInfo.State == MEM_FREE
+ && MemInfo.RegionSize >= cbAlloc)
+ {
+ for (;;)
+ {
+ SUPR3HARDENED_ASSERT((uintptr_t)MemInfo.BaseAddress <= uCur);
+ void *pvMem = VirtualAllocEx(hProc, (void *)uCur, cbAlloc, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (pvMem)
+ {
+ if ( iDirection > 0
+ ? (uintptr_t)pvMem >= uStart
+ && (uintptr_t)pvMem <= uEnd
+ : (uintptr_t)pvMem >= uEnd
+ && (uintptr_t)pvMem <= uStart)
+ return pvMem;
+ VirtualFreeEx(hProc, pvMem, cbAlloc, MEM_RELEASE);
+ }
+
+ /* Advance within the free area and try again? */
+ uintptr_t uNext = iDirection > 0 ? uCur + cbAllocGranularity : uCur - cbAllocGranularity;
+ uNext &= uAllocGranularityMask;
+ if ( iDirection > 0
+ ? uNext <= uCur
+ || uNext > uEnd
+ || uNext - (uintptr_t)MemInfo.BaseAddress > MemInfo.RegionSize
+ || MemInfo.RegionSize - (uNext - (uintptr_t)MemInfo.BaseAddress) < cbAlloc
+ : uNext >= uCur
+ || uNext < uEnd
+ || uNext < (uintptr_t)MemInfo.BaseAddress)
+ break;
+ uCur = uNext;
+ }
+ }
+
+ /*
+ * Advance to the next memory region.
+ */
+ if (iDirection > 0)
+ {
+ uCur = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize;
+ uCur = RT_ALIGN_Z(uCur, cbAllocGranularity);
+ if (uCur >= uEnd)
+ break;
+ }
+ else
+ {
+ uCur = (uintptr_t)(MemInfo.AllocationBase ? MemInfo.AllocationBase : MemInfo.BaseAddress);
+ if (uCur > uEnd)
+ uCur -= cbAlloc;
+ uCur &= uAllocGranularityMask;
+ if (uCur < uEnd)
+ break;
+ }
+ }
+ return NULL;
+}
+#endif
+
+
+/**
+ * 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.
+ */
+DECLHIDDEN(void) supR3HardenedWinInstallHooks(void)
+{
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Install a anti debugging hack before we continue. This prevents most
+ * notifications from ending up in the debugger.
+ */
+ NTSTATUS rcNt = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
+ if (!NT_SUCCESS(rcNt))
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
+ "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
+#endif
+
+ /*
+ * Locate the routine.
+ */
+ HMODULE hmodNtDll = GetModuleHandleW(L"NTDLL");
+ SUPR3HARDENED_ASSERT(hmodNtDll != NULL);
+
+ FARPROC pfnNtCreateSection = GetProcAddress(hmodNtDll, "NtCreateSection");
+ SUPR3HARDENED_ASSERT(pfnNtCreateSection != NULL);
+ SUPR3HARDENED_ASSERT(pfnNtCreateSection == (FARPROC)NtCreateSection);
+
+ uint8_t * const pbNtCreateSection = (uint8_t *)(uintptr_t)pfnNtCreateSection;
+
+#ifdef RT_ARCH_AMD64
+ /*
+ * For 64-bit hosts we need some memory within a +/-2GB range of the
+ * actual function to be able to patch it.
+ */
+ size_t cbMem = _4K;
+ void *pvMem = supR3HardenedWinAllocHookMemory((uintptr_t)pfnNtCreateSection,
+ (uintptr_t)pfnNtCreateSection - _2G + PAGE_SIZE,
+ -1, cbMem);
+ if (!pvMem)
+ {
+ pvMem = supR3HardenedWinAllocHookMemory((uintptr_t)pfnNtCreateSection,
+ (uintptr_t)pfnNtCreateSection + _2G - PAGE_SIZE,
+ 1, cbMem);
+ if (!pvMem)
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
+ "Failed to allocate memory within the +/-2GB range from NTDLL.\n");
+ }
+ uintptr_t *puJmpTab = (uintptr_t *)pvMem;
+
+ /*
+ * Patch 64-bit hosts.
+ */
+ PFNRT pfnCallReal = NULL;
+ uint8_t offJmpBack = UINT8_MAX;
+
+ /* 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 */
+ if ( pbNtCreateSection[ 0] == 0x4c /* mov r10, rcx */
+ && pbNtCreateSection[ 1] == 0x8b
+ && pbNtCreateSection[ 2] == 0xd1
+ && pbNtCreateSection[ 3] == 0xb8 /* mov eax, 000000xxh */
+ && pbNtCreateSection[ 5] == 0x00
+ && pbNtCreateSection[ 6] == 0x00
+ && pbNtCreateSection[ 7] == 0x00
+ && pbNtCreateSection[ 8] == 0x0f /* syscall */
+ && pbNtCreateSection[ 9] == 0x05
+ && pbNtCreateSection[10] == 0xc3 /* ret */
+ )
+ {
+ offJmpBack = 8; /* the 3rd instruction (syscall). */
+ switch (pbNtCreateSection[4])
+ {
+# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
+# include "NtCreateSection-template-amd64-syscall-type-1.h"
+# undef SYSCALL
+ }
+ }
+
+ if (pfnCallReal)
+ {
+ g_pfnNtCreateSectionJmpBack = (PFNRT)(uintptr_t)(pbNtCreateSection + offJmpBack);
+ *(PFNRT *)&g_pfnNtCreateSectionReal = pfnCallReal;
+ *puJmpTab = (uintptr_t)supR3HardenedMonitor_NtCreateSection;
+
+ DWORD dwOldProt;
+ SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
+ PAGE_EXECUTE_READWRITE, &dwOldProt));
+ pbNtCreateSection[0] = 0xff;
+ pbNtCreateSection[1] = 0x25;
+ *(uint32_t *)&pbNtCreateSection[2] = (uint32_t)((uintptr_t)puJmpTab - (uintptr_t)&pbNtCreateSection[2+4]);
+
+ SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
+ PAGE_EXECUTE_READ, &dwOldProt));
+ return;
+ }
+
+#else
+ /*
+ * Patch 32-bit hosts.
+ */
+ PFNRT pfnCallReal = NULL;
+ uint8_t offJmpBack = UINT8_MAX;
+
+ /* 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
+ Note! One nice thing here is that we can share code pattern #1. */
+
+ if ( pbNtCreateSection[ 0] == 0xb8 /* mov eax, 000000xxh*/
+ && pbNtCreateSection[ 2] <= 0x02
+ && pbNtCreateSection[ 3] == 0x00
+ && pbNtCreateSection[ 4] == 0x00
+ ( ( pbNtCreateSection[ 5] == 0xba /* mov edx, offset SharedUserData!SystemCallStub */
+ && pbNtCreateSection[ 6] == 0x00
+ && pbNtCreateSection[ 7] == 0x03
+ && pbNtCreateSection[ 8] == 0xfe
+ && pbNtCreateSection[ 9] == 0x7f
+ && pbNtCreateSection[10] == 0xff /* call [edx] */
+ && pbNtCreateSection[11] == 0x12
+ && pbNtCreateSection[12] == 0xc2 /* ret 1ch */
+ && pbNtCreateSection[13] == 0x1c
+ && pbNtCreateSection[14] == 0x00)
+
+ || ( pbNtCreateSection[ 5] == 0xe8 /* call [$+3] */
+ && RT_ABS(*(int32_t *)&pbNtCreateSection[6]) < 0x10
+ && pbNtCreateSection[10] == 0xc2 /* ret 1ch */
+ && pbNtCreateSection[11] == 0x1c
+ && pbNtCreateSection[12] == 0x00 )
+ )
+ )
+ {
+ offJmpBack = 5; /* the 2nd instruction. */
+ switch (*(uint32_t const *)&pbNtCreateSection[1])
+ {
+# define SYSCALL(a_Num) case a_Num: pfnCallReal = RT_CONCAT(supR3HardenedJmpBack_NtCreateSection_,a_Num); break;
+# include "NtCreateSection-template-x86-syscall-type-1.h"
+# undef SYSCALL
+ }
+ }
+
+ if (pfnCallReal)
+ {
+ g_pfnNtCreateSectionJmpBack = (PFNRT)(uintptr_t)(pbNtCreateSection + offJmpBack);
+ *(PFNRT *)&g_pfnNtCreateSectionReal = pfnCallReal;
+
+ DWORD dwOldProt;
+ SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
+ PAGE_EXECUTE_READWRITE, &dwOldProt));
+ pbNtCreateSection[0] = 0xe9;
+ *(uint32_t *)&pbNtCreateSection[1] = (uintptr_t)supR3HardenedMonitor_NtCreateSection
+ - (uintptr_t)&pbNtCreateSection[1+4];
+
+ SUPR3HARDENED_ASSERT_WIN32_SUCCESS(VirtualProtectEx(GetCurrentProcess(), pbNtCreateSection, 16,
+ PAGE_EXECUTE_READ, &dwOldProt));
+ return;
+ }
+#endif
+
+ supR3HardenedFatalMsg("supR3HardenedWinInstallHooks", kSupInitOp_Misc, VERR_NO_MEMORY,
+ "Failed to install NtCreateSection 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
+ ,
+ pbNtCreateSection[0], pbNtCreateSection[1], pbNtCreateSection[2], pbNtCreateSection[3],
+ pbNtCreateSection[4], pbNtCreateSection[5], pbNtCreateSection[6], pbNtCreateSection[7],
+ pbNtCreateSection[8], pbNtCreateSection[9], pbNtCreateSection[10], pbNtCreateSection[11],
+ pbNtCreateSection[12], pbNtCreateSection[13], pbNtCreateSection[14], pbNtCreateSection[15]);
+}
+
+
+/**
+ * Verifies the process integrity.
+ */
+DECLHIDDEN(void) supR3HardenedWinVerifyProcess(void)
+{
+ RTErrInfoInitStatic(&g_ErrInfoStatic);
+ int rc = supHardenedWinVerifyProcess(GetCurrentProcess(), GetCurrentThread(), &g_ErrInfoStatic.Core);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedWinVerifyProcess", kSupInitOp_Integrity, rc,
+ "Failed to verify process integrity: %s", g_ErrInfoStatic.szMsg);
+}
+
+
+/**
+ * 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 supR3HardenedGetUserAndLogSids(PSID pSidUser, ULONG cbSidUser, PSID pSidLogin, ULONG cbSidLogin)
+{
+ HANDLE hToken;
+ SUPR3HARDENED_ASSERT_NT_SUCCESS(NtOpenProcessToken(GetCurrentProcess(), 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 supR3HardenedInitSecAttrs(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 | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL;
+ 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 = supR3HardenedGetUserAndLogSids(&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)suplibHardenedAllocZ(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).
+ */
+static PRTUTF16 supR3HardenedWinConstructCmdLine(void)
+{
+ /*
+ * Get the command line and skip the executable name.
+ */
+ PCRTUTF16 pwszArgs = GetCommandLineW();
+
+ /* Skip leading space (shouldn't be any, but whatever). */
+ while (suplibCommandLineIsArgSeparator(*pwszArgs))
+ pwszArgs++;
+ SUPR3HARDENED_ASSERT(*pwszArgs != '\0');
+
+ /* Walk to the end of it. */
+ int fQuoted = false;
+ do
+ {
+ if (*pwszArgs == '"')
+ {
+ fQuoted = !fQuoted;
+ pwszArgs++;
+ }
+ else if (*pwszArgs != '\\' || (pwszArgs[1] != '\\' && pwszArgs[1] != '"'))
+ pwszArgs++;
+ else
+ {
+ unsigned cSlashes = 0;
+ do
+ cSlashes++;
+ while (*++pwszArgs == '\\');
+ if (*pwszArgs == '"' && (cSlashes & 1))
+ pwszArgs++; /* odd number of slashes == escaped quote */
+ }
+ } while (*pwszArgs && (fQuoted || !suplibCommandLineIsArgSeparator(*pwszArgs)));
+
+ /* Skip trailing spaces. */
+ while (suplibCommandLineIsArgSeparator(*pwszArgs))
+ pwszArgs++;
+
+ /*
+ * Allocate a new buffer.
+ */
+ size_t cwcArgs = suplibHardenedWStrLen(pwszArgs);
+ size_t cwcCmdLine = (sizeof(SUPR3_RESPAWN_ARG0) - 1) / sizeof(SUPR3_RESPAWN_ARG0[0]) /* Respawn exe name. */
+ + !!cwcArgs + cwcArgs; /* if arguments present, add space + arguments. */
+ PRTUTF16 pwszCmdLine = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0 /* dwFlags*/, (cwcCmdLine + 1) * sizeof(RTUTF16));
+ SUPR3HARDENED_ASSERT(pwszCmdLine != NULL);
+
+ /*
+ * Construct the new command line.
+ */
+ PRTUTF16 pwszDst = pwszCmdLine;
+ for (const char *pszSrc = SUPR3_RESPAWN_ARG0; *pszSrc; pszSrc++)
+ *pwszDst++ = *pszSrc;
+
+ if (cwcArgs)
+ {
+ *pwszDst++ = ' ';
+ suplibHardenedMemCopy(pwszDst, pwszArgs, cwcArgs * sizeof(RTUTF16));
+ pwszDst += cwcArgs;
+ }
+
+ *pwszDst = '\0';
+ SUPR3HARDENED_ASSERT(pwszDst - pwszCmdLine == cwcCmdLine);
+
+ return pwszCmdLine;
+}
+
+
+/**
+ * Does the actually respawning.
+ *
+ * @returns Exit code (if we get that far).
+ */
+static int supR3HardenedWinDoReSpawn(void)
+{
+ SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
+
+ /*
+ * Configure the startup info and creation flags.
+ */
+ DWORD dwCreationFlags = 0;
+
+ 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 |= STARTF_USESTDHANDLES;
+ SiEx.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ SiEx.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ SiEx.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ /*
+ * Set up security descriptors.
+ */
+ SECURITY_ATTRIBUTES ProcessSecAttrs;
+ MYSECURITYCLEANUP ProcessSecAttrsCleanup;
+ supR3HardenedInitSecAttrs(&ProcessSecAttrs, &ProcessSecAttrsCleanup, true /*fProcess*/);
+
+ SECURITY_ATTRIBUTES ThreadSecAttrs;
+ MYSECURITYCLEANUP ThreadSecAttrsCleanup;
+ supR3HardenedInitSecAttrs(&ThreadSecAttrs, &ThreadSecAttrsCleanup, false /*fProcess*/);
+
+ /*
+ * Construct the command line and launch the process.
+ */
+ PRTUTF16 pwszCmdLine = supR3HardenedWinConstructCmdLine();
+
+ PROCESS_INFORMATION ProcessInfo;
+ if (!CreateProcessW(g_wszSupLibHardenedExePath,
+ pwszCmdLine,
+ &ProcessSecAttrs,
+ &ThreadSecAttrs,
+ TRUE /*fInheritHandles*/,
+ dwCreationFlags,
+ NULL /*pwszzEnvironment*/,
+ NULL /*pwszCurDir*/,
+ &SiEx.StartupInfo,
+ &ProcessInfo))
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
+ "Error relaunching VirtualBox VM process: %u\n"
+ "Command line: '%ls'",
+ GetLastError(), pwszCmdLine);
+
+ /*
+ * 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.
+ */
+ SUPR3HARDENED_ASSERT(CloseHandle(ProcessInfo.hThread));
+ ProcessInfo.hThread = NULL;
+
+ HANDLE hProcWait;
+ DWORD dwRights = SYNCHRONIZE;
+ if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
+ dwRights |= PROCESS_QUERY_LIMITED_INFORMATION;
+ else
+ dwRights |= PROCESS_QUERY_INFORMATION;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ ProcessInfo.hProcess,
+ GetCurrentProcess(),
+ &hProcWait,
+ SYNCHRONIZE,
+ FALSE /*fInheritHandle*/,
+ 0))
+ {
+ /* This is unacceptable, kill the process. */
+ DWORD dwErr = GetLastError();
+ TerminateProcess(ProcessInfo.hProcess, RTEXITCODE_FAILURE);
+ supR3HardenedError(dwErr, false /*fFatal*/, "DuplicateHandle failed on child process handle: %u\n", dwErr);
+
+ DWORD dwExit;
+ BOOL fExitOk = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit)
+ && dwExit != STILL_ACTIVE;
+ if (!fExitOk)
+ {
+ DWORD dwStartTick = GetTickCount();
+ DWORD dwWait;
+ do
+ {
+ TerminateProcess(ProcessInfo.hProcess, RTEXITCODE_FAILURE);
+ dwWait = WaitForSingleObject(ProcessInfo.hProcess, 1000);
+ fExitOk = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit)
+ && dwExit != STILL_ACTIVE;
+ } while ( !fExitOk
+ && (dwWait == WAIT_TIMEOUT || dwWait == WAIT_IO_COMPLETION)
+ && GetTickCount() - dwStartTick < 60 * 1000);
+ if (fExitOk)
+ supR3HardenedError(dwErr, false /*fFatal*/,
+ "DuplicateHandle failed and we failed to kill child: dwErr=%u dwWait=%u err=%u hProcess=%p\n",
+ dwErr, dwWait, GetLastError(), ProcessInfo.hProcess);
+ }
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
+ "DuplicateHandle failed on child process handle: %u\n", dwErr);
+ }
+
+ SUPR3HARDENED_ASSERT(CloseHandle(ProcessInfo.hProcess));
+ ProcessInfo.hProcess = NULL;
+
+ /*
+ * Wait for the process to terminate and proxy the termination code.
+ */
+ for (;;)
+ {
+ SetLastError(NO_ERROR);
+ DWORD dwWait = WaitForSingleObject(hProcWait, INFINITE);
+ if ( dwWait == WAIT_OBJECT_0
+ || dwWait == WAIT_ABANDONED_0)
+ break;
+ if ( dwWait != WAIT_TIMEOUT
+ && dwWait != WAIT_IO_COMPLETION)
+ supR3HardenedFatal("WaitForSingleObject returned %#x (last error %#x)\n", dwWait, GetLastError());
+ }
+
+ DWORD dwExit;
+ if ( !GetExitCodeProcess(hProcWait, &dwExit)
+ || dwExit == STILL_ACTIVE)
+ dwExit = RTEXITCODE_FAILURE;
+
+ CloseHandle(hProcWait);
+ suplibHardenedExit((RTEXITCODE)dwExit);
+}
+
+
+/**
+ * Called by the main code if supR3HardenedWinIsReSpawnNeeded returns @c true.
+ *
+ * @returns Program exit code.
+ */
+DECLHIDDEN(int) supR3HardenedWinReSpawn(void)
+{
+ /*
+ * Open the stub device.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ static const WCHAR s_wszName[] = L"\\Device\\VBoxDrvStub";
+ 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*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ GENERIC_READ | GENERIC_WRITE,
+ &ObjAttr,
+ &Ios,
+ NULL /* Allocation Size*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ rcNt = Ios.Status;
+ if (!NT_SUCCESS(rcNt))
+ {
+ int rc;
+ if ((rcNt & UINT32_C(0xffff0000)) == 0xe9860000) /* See VBoxDrvNtErr2NtStatus. */ /** @todo #defines for VBoxDrvNtErr2NtStatus mangling */
+ rc = (int)(rcNt | UINT32_C(0xffff0000));
+ else
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
+ "NtCreateFile(%ls) failed: %#x\n", s_wszName, rcNt);
+ supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Driver, VERR_OPEN_FAILED,
+ "NtCreateFile(%ls) failed: %Rrc (rcNt=%#x)\n", s_wszName, rc, rcNt);
+ }
+
+ /*
+ * Respawn the process with kernel protection for the new process.
+ */
+ return supR3HardenedWinDoReSpawn();
+}
+
+
+/**
+ * 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 cArgs The number of arguments.
+ * @param papszArgs Pointer to the argument vector.
+ */
+DECLHIDDEN(bool) supR3HardenedWinIsReSpawnNeeded(int cArgs, char **papszArgs)
+{
+ SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
+
+ if (cArgs < 1)
+ return true;
+ if (suplibHardenedStrCmp(papszArgs[0], SUPR3_RESPAWN_ARG0))
+ return true;
+
+ /* Replace the argument. */
+ papszArgs[0] = g_szSupLibHardenedExePath;
+ return false;
+}
+
+
+/**
+ * Initializes the windows verficiation bits.
+ * @param fFlags The main flags.
+ */
+DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags)
+{
+ RTErrInfoInitStatic(&g_ErrInfoStatic);
+ int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
+ if (RT_FAILURE(rc))
+ supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
+ "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
+ 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 pwszCmdLine The UTF-16 windows command line to parse.
+ * @param pcArgs Where to return the number of arguments.
+ */
+static char **suplibCommandLineToArgvWStub(PCRTUTF16 pwszCmdLine, int *pcArgs)
+{
+ /*
+ * Convert the command line string to UTF-8.
+ */
+ int cbNeeded = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, pwszCmdLine, -1, NULL /*pszDst*/, 0 /*cbDst*/,
+ NULL /*pchDefChar*/, NULL /* pfUsedDefChar */);
+ SUPR3HARDENED_ASSERT(cbNeeded > 0);
+ int cbAllocated = cbNeeded + 16;
+ char *pszCmdLine = (char *)suplibHardenedAllocZ(cbAllocated);
+
+ SUPR3HARDENED_ASSERT(WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, pwszCmdLine, -1,
+ pszCmdLine, cbAllocated - 1,
+ NULL /*pchDefChar*/, NULL /* pfUsedDefChar */) == cbNeeded);
+
+ /*
+ * Parse the command line, carving argument strings out of it.
+ */
+ int cArgs = 0;
+ int cArgsAllocated = 4;
+ char **papszArgs = (char **)suplibHardenedAllocZ(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 **)suplibHardenedReAlloc(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;
+}
+
+
+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++;
+
+ /*
+ * Init g_uNtVerCombined. (The code is shared with SUPR3.lib and lives in
+ * SUPHardenedVerfiyImage-win.cpp.)
+ */
+ supR3HardenedWinInitVersion();
+
+ /*
+ * Get the executable name.
+ */
+ DWORD cwcExecName = GetModuleFileNameW(GetModuleHandle(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(GetLastError()),
+ "Error opening the executable: %u (%ls).", GetLastError());
+ 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);
+ CloseHandle(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--;
+
+ /*
+ * Convert the arguments to UTF-8 and call the C/C++ main function.
+ */
+ int cArgs;
+ char **papszArgs = suplibCommandLineToArgvWStub(GetCommandLineW(), &cArgs);
+
+ rcExit = (RTEXITCODE)main(cArgs, papszArgs, NULL);
+
+ /*
+ * Exit the process (never return).
+ */
+ suplibHardenedExit(rcExit);
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm
new file mode 100644
index 00000000000..282ce6a7b2a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm
@@ -0,0 +1,76 @@
+; $Id$
+;; @file
+; VirtualBox Support Library - Hardened main(), Windows assembly bits.
+;
+
+;
+; Copyright (C) 2012-2014 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+
+
+; External data.
+extern NAME(g_pfnNtCreateSectionJmpBack)
+
+
+BEGINCODE
+
+;
+; 64-bit
+;
+%ifdef RT_ARCH_AMD64
+ %macro supR3HardenedJmpBack_NtCreateSection_Xxx 1
+ BEGINPROC supR3HardenedJmpBack_NtCreateSection_ %+ %1
+ ; The code we replaced.
+ mov r10, rcx
+ mov eax, %1
+
+ ; Jump back to the original code.
+ jmp [NAME(g_pfnNtCreateSectionJmpBack) wrt RIP]
+ ENDPROC supR3HardenedJmpBack_NtCreateSection_ %+ %1
+ %endm
+ %define SYSCALL(a_Num) supR3HardenedJmpBack_NtCreateSection_Xxx a_Num
+ %include "NtCreateSection-template-amd64-syscall-type-1.h"
+
+%endif
+
+
+;
+; 32-bit.
+;
+%ifdef RT_ARCH_X86
+ %macro supR3HardenedJmpBack_NtCreateSection_Xxx 1
+ BEGINPROC supR3HardenedJmpBack_NtCreateSection_ %+ %1
+ ; The code we replaced.
+ mov eax, %1
+
+ ; Jump back to the original code.
+ jmp [NAME(g_pfnNtCreateSectionJmpBack)]
+ ENDPROC supR3HardenedJmpBack_NtCreateSection_ %+ %1
+ %endm
+ %define SYSCALL(a_Num) supR3HardenedJmpBack_NtCreateSection_Xxx a_Num
+ %include "NtCreateSection-template-x86-syscall-type-1.h"
+
+%endif
+
diff --git a/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp b/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp
new file mode 100644
index 00000000000..ea644ff2df9
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp
@@ -0,0 +1,191 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Support Library - Hardened main(), windows bits.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+#include <AccCtrl.h>
+#include <AclApi.h>
+#ifndef PROCESS_SET_LIMITED_INFORMATION
+# define PROCESS_SET_LIMITED_INFORMATION 0x2000
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/mem.h>
+
+#include "SUPLibInternal.h"
+#include "win/SUPHardenedVerify-win.h"
+
+
+/*
+ * assert.cpp
+ */
+
+RTDATADECL(char) g_szRTAssertMsg1[1024];
+RTDATADECL(char) g_szRTAssertMsg2[4096];
+RTDATADECL(const char * volatile) g_pszRTAssertExpr;
+RTDATADECL(const char * volatile) g_pszRTAssertFile;
+RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
+RTDATADECL(const char * volatile) g_pszRTAssertFunction;
+
+RTDECL(bool) RTAssertMayPanic(void)
+{
+ return true;
+}
+
+
+RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ /*
+ * Fill in the globals.
+ */
+ g_pszRTAssertExpr = pszExpr;
+ g_pszRTAssertFile = pszFile;
+ g_pszRTAssertFunction = pszFunction;
+ g_u32RTAssertLine = uLine;
+ RTStrPrintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
+ "\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%d) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
+{
+ RTStrPrintfV(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, va);
+ if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN)
+ supR3HardenedFatalMsg(g_pszRTAssertExpr, kSupInitOp_Misc, VERR_INTERNAL_ERROR,
+ "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+ else
+ supR3HardenedError(VERR_INTERNAL_ERROR, false/*fFatal*/, "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
+
+/*
+ * Memory allocator.
+ */
+
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) RT_NO_THROW
+{
+ return RTMemAllocTag(cb, pszTag);
+}
+
+
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW
+{
+ return RTMemAllocZTag(cb, pszTag);
+}
+
+
+RTDECL(void) RTMemTmpFree(void *pv) RT_NO_THROW
+{
+ RTMemFree(pv);
+}
+
+
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag) RT_NO_THROW
+{
+ return suplibHardenedAllocZ(cb);
+}
+
+
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW
+{
+ return suplibHardenedAllocZ(cb);
+}
+
+
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocTag(cbAligned, pszTag);
+}
+
+
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocZTag(cbAligned, pszTag);
+}
+
+
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW
+{
+ return suplibHardenedReAlloc(pvOld, cbNew);
+}
+
+
+RTDECL(void) RTMemFree(void *pv) RT_NO_THROW
+{
+ suplibHardenedFree(pv);
+}
+
+
+/*
+ * Simplified version of RTMemWipeThoroughly that avoids dragging in the
+ * random number code.
+ */
+
+RTDECL(void) RTMemWipeThoroughly(void *pv, size_t cb, size_t cMinPasses) RT_NO_THROW
+{
+ size_t cPasses = RT_MIN(cMinPasses, 6);
+ static const uint32_t s_aPatterns[] = { 0x00, 0xaa, 0x55, 0xff, 0xf0, 0x0f, 0xcc, 0x3c, 0xc3 };
+ uint32_t iPattern = 0;
+ do
+ {
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+
+ memset(pv, s_aPatterns[iPattern], cb);
+ iPattern = (iPattern + 1) % RT_ELEMENTS(s_aPatterns);
+ ASMMemoryFence();
+ } while (cPasses-- > 0);
+
+ memset(pv, 0xff, cb);
+ ASMMemoryFence();
+}
+
diff --git a/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp b/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp
new file mode 100644
index 00000000000..ed307a35949
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/win/VBoxSupLib-win.cpp
@@ -0,0 +1,92 @@
+/* $Id$ */
+/** @file
+ * IPRT - VBoxSupLib.dll, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/path.h>
+
+
+/**
+ * The Dll main entry point.
+ */
+extern "C"
+BOOL __stdcall DllMainEntrypoint(HANDLE hModule, DWORD dwReason, PVOID pvReserved)
+{
+ switch (dwReason)
+ {
+ /*
+ * Make sure the DLL isn't ever unloaded.
+ */
+ case DLL_PROCESS_ATTACH:
+ {
+ WCHAR wszName[RTPATH_MAX];
+ SetLastError(NO_ERROR);
+ if ( GetModuleFileNameW((HMODULE)hModule, wszName, RT_ELEMENTS(wszName)) > 0
+ && GetLastError() == NO_ERROR)
+ {
+ int cExtraLoads = 2;
+ while (cExtraLoads-- > 0)
+ LoadLibraryW(wszName);
+ }
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ {
+#ifndef VBOX_WITHOUT_DEBUGGER_CHECKS
+ /*
+ * Anti debugging hack that prevents most debug notifications from
+ * ending up in the debugger.
+ */
+ NTSTATUS rcNt = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
+ if (!NT_SUCCESS(rcNt))
+ {
+ __debugbreak();
+ return FALSE;
+ }
+#endif
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ /* Nothing to do. */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Nothing to do. */
+ break;
+
+ default:
+ /* ignore */
+ break;
+ }
+ return TRUE;
+}
+