Index: /trunk/include/iprt/nt/nt.h
===================================================================
--- /trunk/include/iprt/nt/nt.h	(revision 52946)
+++ /trunk/include/iprt/nt/nt.h	(revision 52947)
@@ -1518,4 +1518,14 @@
 } SECTION_INFORMATION_CLASS;
 NTSYSAPI NTSTATUS NTAPI NtQuerySection(HANDLE, SECTION_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+
+NTSYSAPI NTSTATUS NTAPI NtCreateSymbolicLinkObject(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING pTarget);
+NTSYSAPI NTSTATUS NTAPI NtOpenSymbolicLinkObject(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
+NTSYSAPI NTSTATUS NTAPI NtQuerySymbolicLinkObject(HANDLE, PUNICODE_STRING, PULONG);
+#ifndef SYMBOLIC_LINK_QUERY
+# define SYMBOLIC_LINK_QUERY        UINT32_C(0x00000001)
+#endif
+#ifndef SYMBOLIC_LINK_ALL_ACCESS
+# define SYMBOLIC_LINK_ALL_ACCESS   (STANDARD_RIGHTS_REQUIRED | SYMBOLIC_LINK_QUERY)
+#endif
 
 NTSYSAPI NTSTATUS NTAPI NtQueryInformationThread(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
Index: /trunk/src/VBox/HostDrivers/Support/Makefile.kmk
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/Makefile.kmk	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/Makefile.kmk	(revision 52947)
@@ -224,4 +224,5 @@
 	$(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrEx.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/common/ldr/ldrPE.cpp \
+	$(VBOX_PATH_RUNTIME_SRC)/common/alloc/heapsimple.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-basics.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/common/asn1/asn1-cursor.cpp \
@@ -326,4 +327,5 @@
 	$(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNCmp.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/common/string/RTStrNLen.cpp \
+	$(VBOX_PATH_RUNTIME_SRC)/common/string/RTUtf16NLenEx.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/common/string/strchr.asm \
 	$(VBOX_PATH_RUNTIME_SRC)/common/string/strcmp.asm \
@@ -345,4 +347,7 @@
 	$(VBOX_PATH_RUNTIME_SRC)/common/time/time.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/generic/RTAssertShouldPanic-generic.cpp \
+	$(VBOX_PATH_RUNTIME_SRC)/generic/RTPathAbs-generic.cpp \
+	$(VBOX_PATH_RUNTIME_SRC)/generic/RTPathGetCurrentDrive-generic.cpp \
+	$(VBOX_PATH_RUNTIME_SRC)/generic/RTPathGetCurrentOnDrive-generic.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/generic/memsafer-generic.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/generic/uuid-generic.cpp \
@@ -359,4 +364,5 @@
  	win/SUPR3HardenedNoCrt-win.cpp \
  	$(VBOX_PATH_RUNTIME_SRC)/nt/RTErrConvertFromNtStatus.cpp \
+ 	$(VBOX_PATH_RUNTIME_SRC)/r3/nt/pathint-nt.cpp \
  	$(VBOX_PATH_RUNTIME_SRC)/win/RTErrConvertFromWin32.cpp \
 	$(VBOX_PATH_RUNTIME_SRC)/win/errmsgwin.cpp
Index: /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp	(revision 52947)
@@ -404,5 +404,6 @@
             suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
 # ifdef RT_OS_WINDOWS
-            OutputDebugString(pBuf->szBuf);
+            if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+                OutputDebugString(pBuf->szBuf);
 # endif
             pBuf->off = 0;
@@ -981,19 +982,18 @@
             if (g_hStartupLog == NULL)
             {
-                PRTUTF16 pwszPath;
-                int rc = RTStrToUtf16(pszLogFile, &pwszPath);
+                int rc = RTNtPathOpen(pszLogFile,
+                                      GENERIC_WRITE | SYNCHRONIZE,
+                                      FILE_ATTRIBUTE_NORMAL,
+                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                      FILE_OPEN_IF,
+                                      FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                                      OBJ_CASE_INSENSITIVE,
+                                      &g_hStartupLog,
+                                      NULL);
                 if (RT_SUCCESS(rc))
-                {
-                    g_hStartupLog = CreateFileW(pwszPath,
-                                                GENERIC_WRITE,
-                                                FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                                NULL,
-                                                OPEN_ALWAYS,
-                                                FILE_ATTRIBUTE_NORMAL /*| FILE_FLAG_WRITE_THROUGH*/,
-                                                NULL);
-                    RTUtf16Free(pwszPath);
-                }
-                SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
-                             VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
+                    SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
+                                 VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
+                else
+                    g_hStartupLog = NULL;
             }
 #else
@@ -1708,14 +1708,12 @@
      * to basic CRT functions that everyone agree upon.
      */
-    g_pszSupLibHardenedProgName = pszProgName;
-    g_fSupHardenedMain          = fFlags;
+    g_pszSupLibHardenedProgName   = pszProgName;
+    g_fSupHardenedMain            = fFlags;
+    g_SupPreInitData.u32Magic     = SUPPREINITDATA_MAGIC;
+    g_SupPreInitData.u32EndMagic  = SUPPREINITDATA_MAGIC;
 #ifdef RT_OS_WINDOWS
     if (!g_fSupEarlyVmProcessInit)
 #endif
-    {
-        g_SupPreInitData.u32Magic     = SUPPREINITDATA_MAGIC;
         g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
-        g_SupPreInitData.u32EndMagic  = SUPPREINITDATA_MAGIC;
-    }
 
 #ifdef SUP_HARDENED_SUID
Index: /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerify-win.h	(revision 52947)
@@ -138,4 +138,8 @@
     /** Set if verified. */
     bool                fVerified;
+    /** Whether we've got valid cacheable image bit.s */
+    bool                fValidBits;
+    /** The image base address. */
+    uintptr_t           uImageBase;
 } SUPHNTLDRCACHEENTRY;
 /** Pointer to a loader cache entry. */
@@ -143,5 +147,6 @@
 DECLHIDDEN(int)  supHardNtLdrCacheOpen(const char *pszName, PSUPHNTLDRCACHEENTRY *ppEntry);
 DECLHIDDEN(int)  supHardNtLdrCacheEntryVerify(PSUPHNTLDRCACHEENTRY pEntry, PCRTUTF16 pwszName, PRTERRINFO pErrInfo);
-DECLHIDDEN(int)  supHardNtLdrCacheEntryAllocBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits, PRTERRINFO pErrInfo);
+DECLHIDDEN(int)  supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits, RTLDRADDR uBaseAddress,
+                                               PFNRTLDRIMPORT pfnGetImport, void *pvUser, PRTERRINFO pErrInfo);
 
 
Index: /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp	(revision 52947)
@@ -810,13 +810,12 @@
      */
     uint8_t *pbBits;
-    rc = supHardNtLdrCacheEntryAllocBits(pImage->pCacheEntry, &pbBits, pThis->pErrInfo);
+    if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
+        rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis,
+                                           pThis->pErrInfo);
+    else
+        rc = supHardNtLdrCacheEntryGetBits(pImage->pCacheEntry, &pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis,
+                                           pThis->pErrInfo);
     if (RT_FAILURE(rc))
         return rc;
-    if (pThis->enmKind == SUPHARDNTVPKIND_CHILD_PURIFICATION)
-        rc = RTLdrGetBits(pImage->pCacheEntry->hLdrMod, pbBits, pImage->uImageBase, NULL /*pfnGetImport*/, pThis);
-    else
-        rc = RTLdrGetBits(pImage->pCacheEntry->hLdrMod, pbBits, pImage->uImageBase, supHardNtVpGetImport, pThis);
-    if (RT_FAILURE(rc))
-        return supHardNtVpSetInfo2(pThis, rc, "%s: RTLdrGetBits failed: %Rrc", pImage->pszName, rc);
 
     /* XP SP3 does not set ImageBase to load address. It fixes up the image on load time though. */
@@ -862,5 +861,5 @@
                 return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'LdrInitializeThunk': %Rrc", pImage->pszName, rc);
             aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
-            aSkipAreas[cSkipAreas++].cb = 10;
+            aSkipAreas[cSkipAreas++].cb = 14;
         }
 
@@ -1586,5 +1585,5 @@
 
 /**
- * Allocates a image bits buffer for use with RTLdrGetBits.
+ * Allocates a image bits buffer and calls RTLdrGetBits on them.
  *
  * An assumption here is that there won't ever be concurrent use of the cache.
@@ -1595,8 +1594,18 @@
  * @param   pEntry              The loader cache entry.
  * @param   ppbBits             Where to return the pointer to the allocation.
- * @param   pErRInfo            Where to return extened error information.
- */
-DECLHIDDEN(int) supHardNtLdrCacheEntryAllocBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits, PRTERRINFO pErrInfo)
-{
+ * @param   uBaseAddress        The image base address, see RTLdrGetBits.
+ * @param   pfnGetImport        Import getter, see RTLdrGetBits.
+ * @param   pvUser              The user argument for @a pfnGetImport.
+ * @param   pErrInfo            Where to return extened error information.
+ */
+DECLHIDDEN(int) supHardNtLdrCacheEntryGetBits(PSUPHNTLDRCACHEENTRY pEntry, uint8_t **ppbBits,
+                                              RTLDRADDR uBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+                                              PRTERRINFO pErrInfo)
+{
+    int rc;
+
+    /*
+     * First time around we have to allocate memory before we can get the image bits.
+     */
     if (!pEntry->pbBits)
     {
@@ -1610,7 +1619,35 @@
             return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "Failed to allocate %zu bytes for image %s.",
                                        cbBits, pEntry->pszName);
-    }
-
-    /** @todo Try cache RTLdrGetBits calls too. */
+
+        pEntry->fValidBits = false; /* paranoia */
+
+        rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
+        if (RT_FAILURE(rc))
+            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
+                                       pEntry->pszName, rc);
+        pEntry->uImageBase = uBaseAddress;
+        pEntry->fValidBits = pfnGetImport == NULL;
+
+    }
+    /*
+     * Cache hit? No?
+     *
+     * Note! We cannot currently cache image bits for images with imports as we
+     *       don't control the way they're resolved.  Fortunately, NTDLL and
+     *       the VM process images all have no imports.
+     */
+    else if (   !pEntry->fValidBits
+             || pEntry->uImageBase != uBaseAddress
+             || pfnGetImport)
+    {
+        pEntry->fValidBits = false;
+
+        rc = RTLdrGetBits(pEntry->hLdrMod, pEntry->pbBits, uBaseAddress, pfnGetImport, pvUser);
+        if (RT_FAILURE(rc))
+            return supHardNtVpSetInfo1(pErrInfo, VERR_SUP_VP_NO_MEMORY, "RTLdrGetBits failed on image %s: %Rrc",
+                                       pEntry->pszName, rc);
+        pEntry->uImageBase = uBaseAddress;
+        pEntry->fValidBits = pfnGetImport == NULL;
+    }
 
     *ppbBits = pEntry->pbBits;
@@ -1651,6 +1688,8 @@
     }
 
-    pEntry->pszName   = NULL;
-    pEntry->fVerified = false;
+    pEntry->pszName    = NULL;
+    pEntry->fVerified  = false;
+    pEntry->fValidBits = false;
+    pEntry->uImageBase = 0;
 }
 
@@ -1760,10 +1799,12 @@
      * Fill in the cache entry.
      */
-    pEntry->pszName   = pszName;
-    pEntry->hLdrMod   = hLdrMod;
-    pEntry->pNtViRdr  = pNtViRdr;
-    pEntry->hFile     = hFile;
-    pEntry->pbBits    = NULL;
-    pEntry->fVerified = false;
+    pEntry->pszName    = pszName;
+    pEntry->hLdrMod    = hLdrMod;
+    pEntry->pNtViRdr   = pNtViRdr;
+    pEntry->hFile      = hFile;
+    pEntry->pbBits     = NULL;
+    pEntry->fVerified  = false;
+    pEntry->fValidBits = false;
+    pEntry->uImageBase = ~(uintptr_t)0;
 
 #ifdef IN_SUP_HARDENED_R3
@@ -2042,5 +2083,5 @@
         return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_NTDLL_MAPPING,
                                    "The process has no NTDLL.DLL.");
-    if (iKernel32 == UINT32_MAX && pThis->enmKind != SUPHARDNTVPKIND_CHILD_PURIFICATION)
+    if (iKernel32 == UINT32_MAX && pThis->enmKind == SUPHARDNTVPKIND_SELF_PURIFICATION)
         return supHardNtVpSetInfo2(pThis, VERR_SUP_VP_NO_KERNEL32_MAPPING,
                                    "The process has no KERNEL32.DLL.");
Index: /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp	(revision 52947)
@@ -240,5 +240,10 @@
 /** Queue of cached images which needs their imports checked. */
 static PVERIFIERCACHEIMPORT volatile g_pVerifierCacheTodoImports = NULL;
+
+/** The windows path to dir \\SystemRoot\\System32 directory (technically
+ *  this whatever \KnownDlls\KnownDllPath points to). */
+SUPSYSROOTDIRBUF            g_System32WinPath;
 /** @ */
+
 
 /** Static error info structure used during init. */
@@ -305,4 +310,6 @@
 # undef SYSCALL
 #endif
+
+DECLASM(void) supR3HardenedVmProcessInitThunk(void);
 
 
@@ -1807,13 +1814,6 @@
              * a search on the API level, all VBox calls will have full paths.
              */
-            cwc = GetSystemDirectoryW(wszPath, RT_ELEMENTS(wszPath) - 32);
-            if (!cwc)
-            {
-                supR3HardenedError(VINF_SUCCESS, false,
-                                   "supR3HardenedMonitor_LdrLoadDll: GetSystemDirectoryW failed: %u\n", RtlGetLastWin32Error());
-                SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_UNEXPECTED_IO_ERROR));
-                RtlRestoreLastWin32Error(dwSavedLastError);
-                return STATUS_UNEXPECTED_IO_ERROR;
-            }
+            AssertCompile(sizeof(g_System32WinPath.awcBuffer) <= sizeof(wszPath));
+            cwc = g_System32WinPath.UniStr.Length / sizeof(RTUTF16); Assert(cwc > 2);
             if (cwc + 1 + cwcName + fNeedDllSuffix * 4 >= RT_ELEMENTS(wszPath))
             {
@@ -1824,4 +1824,5 @@
                 return STATUS_NAME_TOO_LONG;
             }
+            memcpy(wszPath, g_System32WinPath.UniStr.Buffer, cwc * sizeof(RTUTF16));
             wszPath[cwc++] = '\\';
             memcpy(&wszPath[cwc], pawcName, cwcName * sizeof(WCHAR));
@@ -1853,7 +1854,35 @@
          * and the real API can come up with a fitting status code for it.
          */
-        HANDLE hFile = CreateFileW(wszPath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecurityAttributes*/,
-                                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
-        if (hFile != INVALID_HANDLE_VALUE)
+        HANDLE          hRootDir;
+        UNICODE_STRING  NtPathUniStr;
+        int rc = RTNtPathFromWinUtf16Ex(&NtPathUniStr, &hRootDir, wszPath, RTSTR_MAX);
+        if (RT_FAILURE(rc))
+        {
+            supR3HardenedError(rc, false,
+                               "supR3HardenedMonitor_LdrLoadDll: RTNtPathFromWinUtf16Ex failed on '%ls': %Rrc\n", wszPath, rc);
+            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: returns rcNt=%#x\n", STATUS_OBJECT_NAME_INVALID));
+            RtlRestoreLastWin32Error(dwSavedLastError);
+            return STATUS_OBJECT_NAME_INVALID;
+        }
+
+        HANDLE              hFile = RTNT_INVALID_HANDLE_VALUE;
+        IO_STATUS_BLOCK     Ios   = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+        OBJECT_ATTRIBUTES   ObjAttr;
+        InitializeObjectAttributes(&ObjAttr, &NtPathUniStr, OBJ_CASE_INSENSITIVE, hRootDir, NULL /*pSecDesc*/);
+
+        rcNt = NtCreateFile(&hFile,
+                            FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
+                            &ObjAttr,
+                            &Ios,
+                            NULL /* Allocation Size*/,
+                            FILE_ATTRIBUTE_NORMAL,
+                            FILE_SHARE_READ,
+                            FILE_OPEN,
+                            FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+                            NULL /*EaBuffer*/,
+                            0 /*EaLength*/);
+        if (NT_SUCCESS(rcNt))
+            rcNt = Ios.Status;
+        if (NT_SUCCESS(rcNt))
         {
             ULONG fAccess = 0;
@@ -1881,6 +1910,8 @@
         {
             DWORD dwErr = RtlGetLastWin32Error();
-            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u\n", wszPath, dwErr));
+            SUP_DPRINTF(("supR3HardenedMonitor_LdrLoadDll: error opening '%ls': %u (NtPath=%.*ls)\n",
+                         wszPath, dwErr, NtPathUniStr.Length / sizeof(RTUTF16), NtPathUniStr.Buffer));
         }
+        RTNtPathFree(&NtPathUniStr, &hRootDir);
     }
 
@@ -3655,6 +3686,10 @@
 
 
-static int supR3HardenedWinPurifyChild(HANDLE hProcess, HANDLE hThread, PRTERRINFO pErrInfo)
-{
+static int supR3HardenedWinPurifyChild(HANDLE hProcess, HANDLE hThread, uintptr_t *puChildNtDllAddr, uintptr_t *puChildExeAddr,
+                                       PRTERRINFO pErrInfo)
+{
+    *puChildNtDllAddr = 0;
+    *puChildExeAddr = 0;
+
     /*
      * Initialize the purifier instance data.
@@ -3695,4 +3730,7 @@
     supR3HardNtPuChFindNtdll(&This);
 
+    *puChildNtDllAddr = This.uNtDllAddr;
+    *puChildExeAddr   = (uintptr_t)This.Peb.ImageBaseAddress;
+
     /*
      * Do the work, the last bit we tag along with the process verfication code.
@@ -3711,4 +3749,219 @@
 
 /**
+ * Terminates the child process.
+ *
+ * @param   hProcess            The process handle.
+ * @param   pszWhere            Who's having child rasing troubles.
+ * @param   rc                  The status code to report.
+ * @param   pszFormat           The message format string.
+ * @param   ...                 Message format arguments.
+ */
+static void supR3HardenedWinKillChild(HANDLE hProcess, const char *pszWhere, int rc, const char *pszFormat, ...)
+{
+    /*
+     * Terminate the process ASAP and display error.
+     */
+    NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
+
+    va_list va;
+    va_start(va, pszFormat);
+    supR3HardenedErrorV(rc, false /*fFatal*/, pszFormat, va);
+    va_end(va);
+
+    /*
+     * Wait for the process to really go away.
+     */
+    PROCESS_BASIC_INFORMATION BasicInfo;
+    NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+    bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
+    if (!fExitOk)
+    {
+        NTSTATUS rcNtWait;
+        DWORD dwStartTick = GetTickCount();
+        do
+        {
+            NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
+
+            LARGE_INTEGER Timeout;
+            Timeout.QuadPart = -20000000; /* 2 second */
+            rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
+
+            rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
+            fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
+        } while (   !fExitOk
+                 && (   rcNtWait == STATUS_TIMEOUT
+                     || rcNtWait == STATUS_USER_APC
+                     || rcNtWait == STATUS_ALERTED)
+                 && GetTickCount() - dwStartTick < 60 * 1000);
+        if (fExitOk)
+            supR3HardenedError(rc, false /*fFatal*/,
+                               "NtDuplicateObject failed and we failed to kill child: rc=%u (%#x) rcNtWait=%#x hProcess=%p\n",
+                               rc, rc, rcNtWait, hProcess);
+    }
+
+    /*
+     * Final error message.
+     */
+    va_start(va, pszFormat);
+    supR3HardenedFatalMsgV(pszWhere, kSupInitOp_Misc, rc, pszFormat, va);
+    va_end(va);
+}
+
+
+/**
+ * Checks the child process for error when the parent event semaphore is
+ * signaled.
+ *
+ * If there is an error pending, this function will not return.
+ *
+ * @param   hProcWait       The child process handle.
+ * @param   uChildExeAddr   The address of the executable in the child process.
+ * @param   phEvtParent     Pointer to the parent event semaphore handle.  We
+ *                          may close the event semaphore and set it to NULL.
+ * @param   phEvtChild      Pointer to the child event semaphore handle.  We may
+ *                          close the event semaphore and set it to NULL.
+ */
+static void supR3HardenedWinCheckVmChild(HANDLE hProcWait, uintptr_t uChildExeAddr, HANDLE *phEvtParent, HANDLE *phEvtChild)
+{
+    /*
+     * Read the process parameters from the child.
+     */
+    uintptr_t           uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+    SIZE_T              cbIgnored;
+    SUPR3WINPROCPARAMS  ChildProcParams;
+    RT_ZERO(ChildProcParams);
+    NTSTATUS rcNt = NtReadVirtualMemory(hProcWait, (PVOID)uChildAddr, &ChildProcParams, sizeof(ChildProcParams), &cbIgnored);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckVmChild", rcNt,
+                                  "NtReadVirtualMemory(,%p,) failed reading child process status: %#x\n", uChildAddr, rcNt);
+
+    /*
+     * Signal the child to get on with whatever it's doing.
+     */
+    rcNt = NtSetEvent(*phEvtChild, NULL);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckVmChild", rcNt, "NtSetEvent failed: %#x\n", rcNt);
+
+    /*
+     * Close the event semaphore handles.
+     */
+    rcNt = NtClose(*phEvtParent);
+    if (NT_SUCCESS(rcNt))
+        rcNt = NtClose(*phEvtChild);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcWait, "supR3HardenedWinCheckVmChild", rcNt, "NtClose failed on event sem: %#x\n", rcNt);
+    *phEvtChild = NULL;
+    *phEvtParent = NULL;
+
+    /*
+     * Process the information we read.
+     */
+    if (ChildProcParams.rc == VINF_SUCCESS)
+        return;
+
+    /* An error occurred, report it. */
+    ChildProcParams.szErrorMsg[sizeof(ChildProcParams.szErrorMsg) - 1] = '\0';
+    supR3HardenedFatalMsg("supR3HardenedWinCheckVmChild", kSupInitOp_Misc, ChildProcParams.rc, "%s", ChildProcParams.szErrorMsg);
+}
+
+
+static void supR3HardenedWinInitVmChild(HANDLE hProcess, HANDLE hThread, uintptr_t uChildNtDllAddr, uintptr_t uChildExeAddr,
+                                        HANDLE hEvtChild, HANDLE hEvtParent)
+{
+    /*
+     * Plant the process parameters.  This ASSUMES the handle inheritance is
+     * performed when creating the child process.
+     */
+    SUPR3WINPROCPARAMS ChildProcParams;
+    RT_ZERO(ChildProcParams);
+    ChildProcParams.hEvtChild  = hEvtChild;
+    ChildProcParams.hEvtParent = hEvtParent;
+    ChildProcParams.uNtDllAddr = uChildNtDllAddr;
+    ChildProcParams.rc         = VINF_SUCCESS;
+
+    uintptr_t uChildAddr = uChildExeAddr + ((uintptr_t)&g_ProcParams - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+    SIZE_T    cbIgnored;
+    NTSTATUS  rcNt = NtWriteVirtualMemory(hProcess, (PVOID)uChildAddr, &ChildProcParams, sizeof(ChildProcParams), &cbIgnored);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rcNt,
+                                  "NtWriteVirtualMemory(,%p,) failed writing child process parameters: %#x\n", uChildAddr, rcNt);
+
+    /*
+     * Locate the LdrInitializeThunk address in the child as well as pristine
+     * code bits for it.
+     */
+    PSUPHNTLDRCACHEENTRY pLdrEntry;
+    int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
+    if (RT_FAILURE(rc))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rc,
+                                  "supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
+
+    uint8_t *pbChildNtDllBits;
+    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbChildNtDllBits, uChildNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
+    if (RT_FAILURE(rc))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rc,
+                                  "supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
+
+    RTLDRADDR uLdrInitThunk;
+    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbChildNtDllBits, uChildNtDllAddr, UINT32_MAX,
+                          "LdrInitializeThunk", &uLdrInitThunk);
+    if (RT_FAILURE(rc))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rc,
+                                  "Error locating LdrInitializeThunk in NTDLL: %Rrc", rc);
+    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uLdrInitThunk;
+    SUP_DPRINTF(("supR3HardenedWinInitVmChild: uLdrInitThunk=%p\n", (uintptr_t)uLdrInitThunk));
+
+    /*
+     * Calculate the address of our code in the child process.
+     */
+    uintptr_t uEarlyVmProcInitEP = uChildExeAddr + (  (uintptr_t)&supR3HardenedVmProcessInitThunk
+                                                    - (uintptr_t)NtCurrentPeb()->ImageBaseAddress);
+
+    /*
+     * Compose the LdrInitializeThunk replacement bytes.
+     */
+    uint8_t abNew[16];
+    memcpy(abNew, pbChildNtDllBits + ((uintptr_t)uLdrInitThunk - uChildNtDllAddr), sizeof(abNew));
+#ifdef RT_ARCH_AMD64
+    abNew[0] = 0xff;
+    abNew[1] = 0x25;
+    *(uint32_t *)&abNew[2] = 0;
+    *(uint64_t *)&abNew[6] = uEarlyVmProcInitEP;
+#elif defined(RT_ARCH_X86)
+    abNew[0] = 0xe9;
+    *(uint32_t *)&abNew[1] = uEarlyVmProcInitEP - ((uint32_t)uLdrInitThunk + 5);
+#else
+# error "Unsupported arch."
+#endif
+
+    /*
+     * Install the LdrInitializeThunk replacement code in the child process.
+     */
+    PVOID   pvProt = pvLdrInitThunk;
+    SIZE_T  cbProt = sizeof(abNew);
+    ULONG   fOldProt;
+    rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, PAGE_EXECUTE_READWRITE, &fOldProt);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rcNt,
+                                  "NtProtectVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+    rcNt = NtWriteVirtualMemory(hProcess, pvLdrInitThunk, abNew, sizeof(abNew), &cbIgnored);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rcNt,
+                                  "NtWriteVirtualMemory/LdrInitializeThunk failed: %#x", rcNt);
+
+    pvProt = pvLdrInitThunk;
+    cbProt = sizeof(abNew);
+    rcNt = NtProtectVirtualMemory(hProcess, &pvProt, &cbProt, fOldProt, &fOldProt);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinInitVmChild", rcNt,
+                                  "NtProtectVirtualMemory/LdrInitializeThunk[restore] failed: %#x", rcNt);
+
+    /* Caller starts child execution. */
+    SUP_DPRINTF(("supR3HardenedWinInitVmChild: Start child.\n"));
+}
+
+
+/**
  * Does the actually respawning.
  *
@@ -3726,4 +3979,18 @@
 
     SUPR3HARDENED_ASSERT(g_cSuplibHardenedWindowsMainCalls == 1);
+
+    /*
+     * Set up VM child communication event semaphores.
+     */
+    HANDLE hEvtChild = NULL;
+    HANDLE hEvtParent = NULL;
+    if (iWhich >= 2)
+    {
+        OBJECT_ATTRIBUTES ObjAttrs;
+        InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+        SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&hEvtChild, EVENT_ALL_ACCESS, &ObjAttrs, NotificationEvent, FALSE));
+        InitializeObjectAttributes(&ObjAttrs, NULL /*pName*/, OBJ_INHERIT, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+        SUPR3HARDENED_ASSERT_NT_SUCCESS(NtCreateEvent(&hEvtParent, EVENT_ALL_ACCESS, &ObjAttrs, NotificationEvent, FALSE));
+    }
 
     /*
@@ -3854,10 +4121,8 @@
      * supR3HardenedWinInstallHooks.)
      */
-    rcNt = NtSetInformationThread(NtCurrentThread(), ThreadHideFromDebugger, NULL, 0);
+    rcNt = NtSetInformationThread(hThread, ThreadHideFromDebugger, NULL, 0);
     if (!NT_SUCCESS(rcNt))
-    {
-        NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
-        supR3HardenedError(rcNt, true /*fFatal*/, "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
-    }
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", rcNt,
+                                  "NtSetInformationThread/ThreadHideFromDebugger failed: %#x\n", rcNt);
 #endif
 
@@ -3865,14 +4130,17 @@
      * Clean up the process.
      */
-    int rc = supR3HardenedWinPurifyChild(hProcess, hThread, RTErrInfoInitStatic(&g_ErrInfoStatic));
+    uintptr_t uChildNtDllAddr;
+    uintptr_t uChildExeAddr;
+    int rc = supR3HardenedWinPurifyChild(hProcess, hThread, &uChildNtDllAddr, &uChildExeAddr,
+                                         RTErrInfoInitStatic(&g_ErrInfoStatic));
     if (RT_FAILURE(rc))
-    {
-        NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
-        supR3HardenedError(rc, true /*fFatal*/, "%s", g_ErrInfoStatic.szMsg);
-    }
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", rc, "%s", g_ErrInfoStatic.szMsg);
 
     /*
      * Start the process execution.
      */
+    if (iWhich >= 2)
+        supR3HardenedWinInitVmChild(hProcess, hThread, uChildNtDllAddr, uChildExeAddr, hEvtChild, hEvtParent);
+
     ULONG cSuspendCount = 0;
     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtResumeThread(hThread, &cSuspendCount));
@@ -3887,9 +4155,11 @@
     PROCESS_BASIC_INFORMATION BasicInfo;
     HANDLE hProcWait;
-    ULONG fRights = SYNCHRONIZE;
+    ULONG fRights = SYNCHRONIZE | PROCESS_TERMINATE;
     if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* Introduced in Vista. */
         fRights |= PROCESS_QUERY_LIMITED_INFORMATION;
     else
         fRights |= PROCESS_QUERY_INFORMATION;
+    if (iWhich == 2)
+        fRights |= PROCESS_VM_READ;
     rcNt = NtDuplicateObject(NtCurrentProcess(), hProcess,
                              NtCurrentProcess(), &hProcWait,
@@ -3900,41 +4170,20 @@
                                  SYNCHRONIZE, 0 /*HandleAttributes*/, 0);
     if (!NT_SUCCESS(rcNt))
-    {
-        /* Failure is unacceptable, kill the process. */
-        NtTerminateProcess(hProcess, RTEXITCODE_FAILURE);
-        supR3HardenedError(rcNt, false /*fFatal*/, "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
-
-        NTSTATUS rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
-        bool fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
-        if (!fExitOk)
-        {
-            NTSTATUS rcNtWait;
-            DWORD dwStartTick = GetTickCount();
-            do
-            {
-                NtTerminateProcess(hProcess, DBG_TERMINATE_PROCESS);
-
-                LARGE_INTEGER Timeout;
-                Timeout.QuadPart = -20000000; /* 2 second */
-                rcNtWait = NtWaitForSingleObject(hProcess, TRUE /*Alertable*/, &Timeout);
-
-                rcNtExit = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
-                fExitOk = NT_SUCCESS(rcNtExit) && BasicInfo.ExitStatus != STATUS_PENDING;
-            } while (   !fExitOk
-                     && (   rcNtWait == STATUS_TIMEOUT
-                         || rcNtWait == STATUS_USER_APC
-                         || rcNtWait == STATUS_ALERTED)
-                     && GetTickCount() - dwStartTick < 60 * 1000);
-            if (fExitOk)
-                supR3HardenedError(rcNt, false /*fFatal*/,
-                                   "NtDuplicateObject failed and we failed to kill child: rcNt=%u rcNtWait=%u hProcess=%p\n",
-                                   rcNt, rcNtWait, hProcess);
-        }
-        supR3HardenedFatalMsg("supR3HardenedWinReSpawn", kSupInitOp_Misc, VERR_INVALID_NAME,
-                              "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
-    }
+        supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
+                                  "NtDuplicateObject failed on child process handle: %#x\n", rcNt);
 
     SUPR3HARDENED_ASSERT_NT_SUCCESS(NtClose(hProcess));
     hProcess = NULL;
+
+    /*
+     * Signal the VM child that we've closed the unrestricted handles.
+     */
+    if (iWhich >= 2)
+    {
+        rcNt = NtSetEvent(hEvtChild, NULL);
+        if (!NT_SUCCESS(rcNt))
+            supR3HardenedWinKillChild(hProcess, "supR3HardenedWinReSpawn", VERR_INVALID_NAME,
+                                      "NtSetEvent failed on child process handle: %#x\n", rcNt);
+    }
 
     /*
@@ -3972,39 +4221,28 @@
     }
 
+    for (;;)
+    {
+        HANDLE ahHandles[3];
+        ULONG  cHandles = 1;
+        ahHandles[0] = hProcWait;
+        if (hEvtParent != NULL)
+            ahHandles[cHandles++] = hEvtParent;
+        if (hParent != NULL)
+            ahHandles[cHandles++] = hParent;
+
+        rcNt = NtWaitForMultipleObjects(cHandles, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*pTimeout*/);
+        if (rcNt == STATUS_WAIT_0 + 1 && hEvtParent != NULL)
+            supR3HardenedWinCheckVmChild(hProcWait, uChildExeAddr, &hEvtParent, &hEvtChild);
+        else if (   (ULONG)rcNt - (ULONG)STATUS_WAIT_0           < cHandles
+                 || (ULONG)rcNt - (ULONG)STATUS_ABANDONED_WAIT_0 < cHandles)
+            break;
+        else if (   rcNt != STATUS_TIMEOUT
+                 && rcNt != STATUS_USER_APC
+                 && rcNt != STATUS_ALERTED)
+            supR3HardenedFatal("NtWaitForMultipleObjects returned %#x\n", rcNt);
+    }
+
     if (hParent != NULL)
-    {
-        for (;;)
-        {
-            HANDLE ahHandles[2] = { hProcWait, hParent };
-            rcNt = NtWaitForMultipleObjects(2, &ahHandles[0], WaitAnyObject, TRUE /*Alertable*/, NULL /*pTimeout*/);
-            if (   rcNt == STATUS_WAIT_0
-                || rcNt == STATUS_WAIT_0 + 1
-                || rcNt == STATUS_ABANDONED_WAIT_0
-                || rcNt == STATUS_ABANDONED_WAIT_0 + 1)
-                break;
-            if (   rcNt != STATUS_TIMEOUT
-                && rcNt != STATUS_USER_APC
-                && rcNt != STATUS_ALERTED)
-                supR3HardenedFatal("NtWaitForMultipleObjects returned %#x\n", rcNt);
-        }
         NtClose(hParent);
-    }
-    else
-    {
-        /*
-         * Wait for the process to terminate.
-         */
-        for (;;)
-        {
-            rcNt = NtWaitForSingleObject(hProcWait, TRUE /*Alertable*/, NULL /*pTimeout*/);
-            if (   rcNt == STATUS_WAIT_0
-                || rcNt == STATUS_ABANDONED_WAIT_0)
-                break;
-            if (   rcNt != STATUS_TIMEOUT
-                && rcNt != STATUS_USER_APC
-                && rcNt != STATUS_ALERTED)
-                supR3HardenedFatal("NtWaitForSingleObject returned %#x\n", rcNt);
-        }
-    }
 
     /*
@@ -4012,7 +4250,12 @@
      */
     NTSTATUS rcNt2 = NtQueryInformationProcess(hProcWait, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
-    if (   !NT_SUCCESS(rcNt2)
-        || BasicInfo.ExitStatus == STATUS_PENDING)
+    if (!NT_SUCCESS(rcNt2))
         BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
+    else if (BasicInfo.ExitStatus == STATUS_PENDING)
+    {
+        if (hEvtParent)
+            NtTerminateProcess(hProcWait, RTEXITCODE_FAILURE);
+        BasicInfo.ExitStatus = RTEXITCODE_FAILURE;
+    }
 
     NtClose(hProcWait);
@@ -4392,4 +4635,7 @@
 DECLHIDDEN(void) supR3HardenedWinInit(uint32_t fFlags, bool fAvastKludge)
 {
+    /*
+     * Init the verifier.
+     */
     RTErrInfoInitStatic(&g_ErrInfoStatic);
     int rc = supHardenedWinInitImageVerifier(&g_ErrInfoStatic.Core);
@@ -4397,4 +4643,26 @@
         supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rc,
                               "supHardenedWinInitImageVerifier failed: %s", g_ErrInfoStatic.szMsg);
+
+    /*
+     * Get the windows system directory from the KnownDlls dir.
+     */
+    HANDLE              hSymlink = INVALID_HANDLE_VALUE;
+    UNICODE_STRING      UniStr = RTNT_CONSTANT_UNISTR(L"\\KnownDlls\\KnownDllPath");
+    OBJECT_ATTRIBUTES   ObjAttrs;
+    InitializeObjectAttributes(&ObjAttrs, &UniStr, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
+    NTSTATUS rcNt = NtOpenSymbolicLinkObject(&hSymlink, SYMBOLIC_LINK_QUERY, &ObjAttrs);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error opening '%ls': %#x", UniStr.Buffer, rcNt);
+
+    g_System32WinPath.UniStr.Buffer = g_System32WinPath.awcBuffer;
+    g_System32WinPath.UniStr.Length = 0;
+    g_System32WinPath.UniStr.MaximumLength = sizeof(g_System32WinPath.awcBuffer) - sizeof(RTUTF16);
+    rcNt = NtQuerySymbolicLinkObject(hSymlink, &g_System32WinPath.UniStr, NULL);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedFatalMsg("supR3HardenedWinInit", kSupInitOp_Misc, rcNt, "Error querying '%ls': %#x", UniStr.Buffer, rcNt);
+    g_System32WinPath.UniStr.Buffer[g_System32WinPath.UniStr.Length / sizeof(RTUTF16)] = '\0';
+
+    SUP_DPRINTF(("KnownDllPath: %ls\n", g_System32WinPath.UniStr.Buffer));
+    NtClose(hSymlink);
 
     if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
@@ -4445,6 +4713,6 @@
                 /* Log the KiOpPrefetchPatchCount value if available, hoping it might sched some light on spider38's case. */
                 ULONG cPatchCount = 0;
-                NTSTATUS rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
-                                                         &cPatchCount, sizeof(cPatchCount), NULL);
+                rcNt = NtQuerySystemInformation(SystemInformation_KiOpPrefetchPatchCount,
+                                                &cPatchCount, sizeof(cPatchCount), NULL);
                 if (NT_SUCCESS(rcNt))
                     SUP_DPRINTF(("supR3HardenedWinInit: cFixes=%u g_fSupAdversaries=%#x cPatchCount=%#u\n",
@@ -5250,5 +5518,6 @@
 
     /*
-     * Wait on the parent process to dispose of the full access process handle.
+     * Wait on the parent process to dispose of the full access process and
+     * thread handles.
      */
     LARGE_INTEGER Timeout;
@@ -5299,4 +5568,5 @@
      * Open the driver.
      */
+    SUP_DPRINTF(("supR3HardenedVmProcessInit: Opening vboxdrv...\n"));
     supR3HardenedMainOpenDevice();
     g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_EARLY_DEVICE_OPENED;
@@ -5306,4 +5576,5 @@
      * normally when we return.
      */
+    SUP_DPRINTF(("supR3HardenedVmProcessInit: Restoring LdrIntiailizeThunk...\n"));
     PSUPHNTLDRCACHEENTRY pLdrEntry;
     int rc = supHardNtLdrCacheOpen("ntdll.dll", &pLdrEntry);
@@ -5311,14 +5582,20 @@
         supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheOpen failed on NTDLL: %Rrc\n", rc);
 
+    uint8_t *pbBits;
+    rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, g_ProcParams.uNtDllAddr, NULL, NULL, NULL /*pErrInfo*/);
+    if (RT_FAILURE(rc))
+        supR3HardenedFatal("supR3HardenedVmProcessInit: supHardNtLdrCacheEntryGetBits failed on NTDLL: %Rrc\n", rc);
+
     RTLDRADDR uValue;
-    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pLdrEntry->pbBits, 0, UINT32_MAX, "LdrInitializeThunk", &uValue);
+    rc = RTLdrGetSymbolEx(pLdrEntry->hLdrMod, pbBits, g_ProcParams.uNtDllAddr, UINT32_MAX, "LdrInitializeThunk", &uValue);
     if (RT_FAILURE(rc))
         supR3HardenedFatal("supR3HardenedVmProcessInit: Failed to find LdrInitializeThunk (%Rrc).\n", rc);
 
-    PVOID pvLdrInitThunk = (uint8_t *)g_ProcParams.uNtDllAddr + (uint32_t)uValue;
+    PVOID pvLdrInitThunk = (PVOID)(uintptr_t)uValue;
     SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READWRITE));
-    memcpy(pvLdrInitThunk, pLdrEntry->pbBits + (uint32_t)uValue, 16);
+    memcpy(pvLdrInitThunk, pbBits + ((uintptr_t)uValue - g_ProcParams.uNtDllAddr), 16);
     SUPR3HARDENED_ASSERT_NT_SUCCESS(supR3HardenedWinProtectMemory(pvLdrInitThunk, 16, PAGE_EXECUTE_READ));
 
+    SUP_DPRINTF(("supR3HardenedVmProcessInit: Returning to LdrIntiailizeThunk...\n"));
     return (uintptr_t)pvLdrInitThunk;
 }
Index: /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp	(revision 52947)
@@ -641,19 +641,15 @@
             {
                 uint8_t *pbBits;
-                rc = supHardNtLdrCacheEntryAllocBits(pLdrEntry, &pbBits, NULL);
+                rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
+                                                   NULL /*pErrInfo*/);
                 if (RT_SUCCESS(rc))
                 {
-                    rc = RTLdrGetBits(pLdrEntry->hLdrMod, pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL);
-                    if (RT_SUCCESS(rc))
-                        for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
-                            supR3HardenedDirectSyscall(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
-                                                       &g_aSupNtImpDlls[iDll].paSyscalls[i], pLdrEntry, pbBits);
-                    else
-                        SUPHNTIMP_ERROR(19, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
-                                        "%ls: RTLdrGetBits failed: %Rrc '%s'.", g_aSupNtImpDlls[iDll].pwszName, rc);
+                    for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
+                        supR3HardenedDirectSyscall(&g_aSupNtImpDlls[iDll], &g_aSupNtImpDlls[iDll].paImports[i],
+                                                   &g_aSupNtImpDlls[iDll].paSyscalls[i], pLdrEntry, pbBits);
                 }
                 else
                     SUPHNTIMP_ERROR(20, "supR3HardenedWinInitImports", kSupInitOp_Misc, rc,
-                                    "%ls: supHardNtLdrCacheEntryAllocBits failed: %Rrc '%s'.", g_aSupNtImpDlls[iDll].pwszName, rc);
+                                    "%ls: supHardNtLdrCacheEntryGetBits failed: %Rrc '%s'.", g_aSupNtImpDlls[iDll].pwszName, rc);
             }
             else
@@ -674,5 +670,6 @@
             {
                 uint8_t *pbBits;
-                rc = supHardNtLdrCacheEntryAllocBits(pLdrEntry, &pbBits, NULL);
+                rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
+                                                   NULL /*pErrInfo*/);
                 if (RT_SUCCESS(rc))
                     for (uint32_t i = 0; i < g_aSupNtImpDlls[iDll].cImports; i++)
@@ -712,4 +709,5 @@
         if (RTStrICmp(g_aSupNtImpDlls[iDll].pszName, pszDll) == 0)
         {
+
             PSUPHNTLDRCACHEENTRY pLdrEntry;
             int rc = supHardNtLdrCacheOpen(g_aSupNtImpDlls[iDll].pszName, &pLdrEntry);
@@ -717,5 +715,6 @@
             {
                 uint8_t *pbBits;
-                rc = supHardNtLdrCacheEntryAllocBits(pLdrEntry, &pbBits, NULL);
+                rc = supHardNtLdrCacheEntryGetBits(pLdrEntry, &pbBits, (uintptr_t)g_aSupNtImpDlls[iDll].pbImageBase, NULL, NULL,
+                                                   NULL /*pErrInfo*/);
                 if (RT_SUCCESS(rc))
                 {
@@ -736,5 +735,7 @@
 
             /* Complications, just call GetProcAddress. */
-            return (PFNRT)GetProcAddress(GetModuleHandleW(g_aSupNtImpDlls[iDll].pwszName), pszProcedure);
+            if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+                return (PFNRT)GetProcAddress(GetModuleHandleW(g_aSupNtImpDlls[iDll].pwszName), pszProcedure);
+            return NULL;
         }
 
Index: /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedNoCrt-win.cpp	(revision 52947)
@@ -39,7 +39,9 @@
 #include <iprt/assert.h>
 #include <iprt/ctype.h>
+#include <iprt/heap.h>
 #include <iprt/string.h>
 #include <iprt/initterm.h>
 #include <iprt/param.h>
+#include <iprt/path.h>
 #include <iprt/mem.h>
 
@@ -97,6 +99,115 @@
  */
 
-/** The heap we're using. */
-static HANDLE g_hSupR3HardenedHeap = NULL;
+/** The handle of the heap we're using. */
+static HANDLE       g_hSupR3HardenedHeap = NULL;
+/** Number of heaps used during early process init. */
+static uint32_t     g_cSupR3HardenedEarlyHeaps = 0;
+/** Early process init heaps. */
+static struct
+{
+    /** The heap handle. */
+    RTHEAPSIMPLE    hHeap;
+    /** The heap block pointer. */
+    void           *pvBlock;
+    /** The size of the heap block. */
+    size_t          cbBlock;
+    /** Number of active allocations on this heap. */
+    size_t          cAllocations;
+} g_aSupR3HardenedEarlyHeaps[8];
+
+
+static uint32_t supR3HardenedEarlyFind(void *pv)
+{
+    uint32_t iHeap = g_cSupR3HardenedEarlyHeaps;
+    while (iHeap-- > 0)
+        if ((uintptr_t)pv - (uintptr_t)g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock < g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock)
+            return iHeap;
+    return UINT32_MAX;
+}
+
+
+static void supR3HardenedEarlyCompact(void)
+{
+    uint32_t iHeap = g_cSupR3HardenedEarlyHeaps;
+    while (iHeap-- > 0)
+        if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations == 0)
+        {
+            PVOID  pvMem = g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock;
+            SIZE_T cbMem = g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock;
+            if (iHeap + 1 < g_cSupR3HardenedEarlyHeaps)
+                g_aSupR3HardenedEarlyHeaps[iHeap] = g_aSupR3HardenedEarlyHeaps[g_cSupR3HardenedEarlyHeaps - 1];
+            g_cSupR3HardenedEarlyHeaps--;
+
+            NTSTATUS rcNt = NtFreeVirtualMemory(NtCurrentProcess(), &pvMem, &cbMem, MEM_RELEASE);
+            Assert(NT_SUCCESS(rcNt));
+            SUP_DPRINTF(("supR3HardenedEarlyCompact: Removed heap %#u (%#p LB %#zx)\n", iHeap, pvMem, cbMem));
+        }
+}
+
+
+static void *supR3HardenedEarlyAlloc(size_t cb, bool fZero)
+{
+    /*
+     * Try allocate on existing heaps.
+     */
+    void    *pv;
+    uint32_t iHeap = 0;
+    while (iHeap < g_cSupR3HardenedEarlyHeaps)
+    {
+        if (fZero)
+            pv = RTHeapSimpleAllocZ(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, cb, 0);
+        else
+            pv = RTHeapSimpleAlloc(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, cb, 0);
+        if (pv)
+        {
+            g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations++;
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+            SUP_DPRINTF(("Early heap: %p LB %#zx - alloc\n", pv, cb));
+#endif
+            return pv;
+        }
+        iHeap++;
+    }
+
+    /*
+     * Add another heap.
+     */
+    if (iHeap == RT_ELEMENTS(g_aSupR3HardenedEarlyHeaps))
+        supR3HardenedFatal("Early heap table is full (cb=%#zx).\n", cb);
+    SIZE_T cbBlock = iHeap == 0 ? _1M : g_aSupR3HardenedEarlyHeaps[iHeap - 1].cbBlock * 2;
+    while (cbBlock <= cb * 2)
+        cbBlock *= 2;
+
+    PVOID pvBlock = NULL;
+    NTSTATUS rcNt = NtAllocateVirtualMemory(NtCurrentProcess(), &pvBlock, 0 /*ZeroBits*/, &cbBlock, MEM_COMMIT, PAGE_READWRITE);
+    if (!NT_SUCCESS(rcNt))
+        supR3HardenedFatal("NtAllocateVirtualMemory(,,,%#zx,,) failed: rcNt=%#x\n", cbBlock, rcNt);
+    SUP_DPRINTF(("New simple heap: #%u %p LB %#zx (for %zu allocation)\n", iHeap, pvBlock, cbBlock, cb));
+
+    RTHEAPSIMPLE hHeap;
+    int rc = RTHeapSimpleInit(&hHeap, pvBlock, cbBlock);
+    if (RT_FAILURE(rc))
+        supR3HardenedFatal("RTHeapSimpleInit(,%p,%#zx) failed: rc=%#x\n", pvBlock, cbBlock, rc);
+
+    if (fZero)
+        pv = RTHeapSimpleAllocZ(hHeap, cb, 0);
+    else
+        pv = RTHeapSimpleAlloc(hHeap, cb, 0);
+    if (!pv)
+        supR3HardenedFatal("RTHeapSimpleAlloc[Z] failed allocating %#zx bytes on a %#zu heap.\n", cb, cbBlock);
+
+    g_aSupR3HardenedEarlyHeaps[iHeap].pvBlock      = pvBlock;
+    g_aSupR3HardenedEarlyHeaps[iHeap].cbBlock      = cbBlock;
+    g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations = 1;
+    g_aSupR3HardenedEarlyHeaps[iHeap].hHeap        = hHeap;
+
+    Assert(g_cSupR3HardenedEarlyHeaps == iHeap);
+    g_cSupR3HardenedEarlyHeaps                     = iHeap + 1;
+
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+    SUP_DPRINTF(("Early heap: %p LB %#zx - alloc\n", pv, cb));
+#endif
+    return pv;
+}
 
 
@@ -108,4 +219,5 @@
 static HANDLE supR3HardenedHeapInit(void)
 {
+    Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED);
     HANDLE hHeap = RtlCreateHeap(HEAP_GROWABLE | HEAP_CLASS_PRIVATE, NULL /*HeapBase*/,
                                  0 /*ReserveSize*/, 0 /*CommitSize*/,  NULL /*Lock*/, NULL /*Parameters*/);
@@ -129,4 +241,5 @@
         RtlCompactHeap(g_hSupR3HardenedHeap, 0 /*dwFlags*/);
     RtlCompactHeap(GetProcessHeap(), 0 /*dwFlags*/);
+    supR3HardenedEarlyCompact();
 }
 
@@ -155,5 +268,11 @@
     HANDLE hHeap = g_hSupR3HardenedHeap;
     if (!hHeap)
+    {
+        if (   g_fSupEarlyVmProcessInit
+            && g_enmSupR3HardenedMainState <= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED)
+            return supR3HardenedEarlyAlloc(cb, false /*fZero*/);
         hHeap = supR3HardenedHeapInit();
+    }
+
     void *pv = RtlAllocateHeap(hHeap, 0 /*fFlags*/, cb);
     if (!pv)
@@ -167,5 +286,11 @@
     HANDLE hHeap = g_hSupR3HardenedHeap;
     if (!hHeap)
+    {
+        if (   g_fSupEarlyVmProcessInit
+            && g_enmSupR3HardenedMainState <= SUPR3HARDENEDMAINSTATE_WIN_EP_CALLED)
+            return supR3HardenedEarlyAlloc(cb, true /*fZero*/);
         hHeap = supR3HardenedHeapInit();
+    }
+
     void *pv = RtlAllocateHeap(hHeap, HEAP_ZERO_MEMORY, cb);
     if (!pv)
@@ -202,7 +327,54 @@
         return RTMemAllocZTag(cbNew, pszTag);
 
+    void *pv;
+    if (g_fSupEarlyVmProcessInit)
+    {
+        uint32_t iHeap = supR3HardenedEarlyFind(pvOld);
+        if (iHeap != UINT32_MAX)
+        {
+#if 0 /* RTHeapSimpleRealloc is not implemented */
+            /* If this is before we can use a regular heap, we try resize
+               within the simple heap.  (There are a lot of array growing in
+               the ASN.1 code.) */
+            if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+            {
+                pv = RTHeapSimpleRealloc(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld, cbNew, 0);
+                if (pv)
+                {
+# ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+                    SUP_DPRINTF(("Early heap: %p LB %#zx, was %p - realloc\n", pvNew, cbNew, pvOld));
+# endif
+                    return pv;
+                }
+            }
+#endif
+
+            /* Either we can't reallocate it on the same simple heap, or we're
+               past hardened main and wish to migrate everything over on the
+               real heap. */
+            size_t cbOld = RTHeapSimpleSize(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld);
+            pv = RTMemAllocTag(cbNew, pszTag);
+            if (pv)
+            {
+                memcpy(pv, pvOld, RT_MIN(cbOld, cbNew));
+                RTHeapSimpleFree(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pvOld);
+                if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations)
+                    g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations--;
+                if (   !g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations
+                    && g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+                    supR3HardenedEarlyCompact();
+            }
+# ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+            SUP_DPRINTF(("Early heap: %p LB %#zx, was %p %LB %#zx - realloc\n", pv, cbNew, pvOld, cbOld));
+# endif
+            return pv;
+        }
+        Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED);
+    }
+
+    /* Allocate from the regular heap. */
     HANDLE hHeap = g_hSupR3HardenedHeap;
     Assert(hHeap != NULL);
-    void *pv = RtlReAllocateHeap(hHeap, 0 /*dwFlags*/, pvOld, cbNew);
+    pv = RtlReAllocateHeap(hHeap, 0 /*dwFlags*/, pvOld, cbNew);
     if (!pv)
         supR3HardenedFatal("RtlReAllocateHeap failed to allocate %zu bytes.\n", cbNew);
@@ -215,4 +387,23 @@
     if (pv)
     {
+        if (g_fSupEarlyVmProcessInit)
+        {
+            uint32_t iHeap = supR3HardenedEarlyFind(pv);
+            if (iHeap != UINT32_MAX)
+            {
+#ifdef SUPR3HARDENED_EARLY_HEAP_TRACE
+                SUP_DPRINTF(("Early heap: %p - free\n", pv));
+#endif
+                RTHeapSimpleFree(g_aSupR3HardenedEarlyHeaps[iHeap].hHeap, pv);
+                if (g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations)
+                    g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations--;
+                if (   !g_aSupR3HardenedEarlyHeaps[iHeap].cAllocations
+                    && g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+                    supR3HardenedEarlyCompact();
+                return;
+            }
+            Assert(g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED);
+        }
+
         HANDLE hHeap = g_hSupR3HardenedHeap;
         Assert(hHeap != NULL);
@@ -251,2 +442,36 @@
 }
 
+
+
+/*
+ * path-win.cpp
+ */
+
+RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cbPath)
+{
+    int rc;
+    if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
+/** @todo Rainy day: improve this by checking the process parameter block
+ *        (needs to be normalized). */
+        rc = RTStrCopy(pszPath, cbPath, "C:\\");
+    else
+    {
+        /*
+         * GetCurrentDirectory may in some cases omit the drive letter, according
+         * to MSDN, thus the GetFullPathName call.
+         */
+        RTUTF16 wszCurPath[RTPATH_MAX];
+        if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
+        {
+            RTUTF16 wszFullPath[RTPATH_MAX];
+            if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
+                rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL);
+            else
+                rc = RTErrConvertFromWin32(RtlGetLastWin32Error());
+        }
+        else
+            rc = RTErrConvertFromWin32(RtlGetLastWin32Error());
+    }
+    return rc;
+}
+
Index: /trunk/src/VBox/HostDrivers/Support/win/import-template-kernel32.h
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/import-template-kernel32.h	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/import-template-kernel32.h	(revision 52947)
@@ -3,4 +3,6 @@
 SUPHARNT_IMPORT_STDCALL(ExitProcess, 4)
 SUPHARNT_IMPORT_STDCALL(GetFullPathNameA, 16)
+SUPHARNT_IMPORT_STDCALL(GetFullPathNameW, 16)
+SUPHARNT_IMPORT_STDCALL(GetCurrentDirectoryW, 8)
 SUPHARNT_IMPORT_STDCALL(GetModuleFileNameW, 12)
 SUPHARNT_IMPORT_STDCALL(GetModuleHandleA, 4)
Index: /trunk/src/VBox/HostDrivers/Support/win/import-template-ntdll.h
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/win/import-template-ntdll.h	(revision 52946)
+++ /trunk/src/VBox/HostDrivers/Support/win/import-template-ntdll.h	(revision 52947)
@@ -4,4 +4,5 @@
 SUPHARNT_IMPORT_SYSCALL(NtCreateEvent, 20)
 SUPHARNT_IMPORT_SYSCALL(NtCreateFile, 44)
+SUPHARNT_IMPORT_SYSCALL(NtCreateSymbolicLinkObject, 16)
 SUPHARNT_IMPORT_SYSCALL(NtDelayExecution, 8)
 SUPHARNT_IMPORT_SYSCALL(NtDeviceIoControlFile, 40)
@@ -15,4 +16,5 @@
 SUPHARNT_IMPORT_SYSCALL(NtOpenProcess, 16)
 SUPHARNT_IMPORT_SYSCALL(NtOpenProcessToken, 12)
+SUPHARNT_IMPORT_SYSCALL(NtOpenSymbolicLinkObject, 12)
 SUPHARNT_IMPORT_SYSCALL(NtOpenThread, 16)
 SUPHARNT_IMPORT_SYSCALL(NtOpenThreadToken, 16)
@@ -27,4 +29,5 @@
 SUPHARNT_IMPORT_SYSCALL(NtQueryObject, 20)
 SUPHARNT_IMPORT_SYSCALL(NtQuerySecurityObject, 20)
+SUPHARNT_IMPORT_SYSCALL(NtQuerySymbolicLinkObject, 12)
 SUPHARNT_IMPORT_SYSCALL(NtQuerySystemInformation, 16)
 SUPHARNT_IMPORT_SYSCALL(NtQueryTimerResolution, 12)
Index: /trunk/src/VBox/Runtime/r3/win/ntdll-mini-implib.def
===================================================================
--- /trunk/src/VBox/Runtime/r3/win/ntdll-mini-implib.def	(revision 52946)
+++ /trunk/src/VBox/Runtime/r3/win/ntdll-mini-implib.def	(revision 52947)
@@ -38,4 +38,5 @@
     NtCreateFile                          ;;= _NtCreateFile@44
     NtCreateSection                       ;;= _NtCreateSection@28
+    NtCreateSymbolicLinkObject            ;;= _NtCreateSymbolicLinkObject@16
     NtDelayExecution                      ;;= _NtDelayExecution@8
     NtDeviceIoControlFile                 ;;= _NtDeviceIoControlFile@40
@@ -49,4 +50,5 @@
     NtOpenProcess                         ;;= _NtOpenProcess@16
     NtOpenProcessToken                    ;;= _NtOpenProcessToken@12
+    NtOpenSymbolicLinkObject              ;;= _NtOpenSymbolicLinkObject@12
     NtOpenThread                          ;;= _NtOpenThread@16
     NtOpenThreadToken                     ;;= _NtOpenThreadToken@16
@@ -61,4 +63,5 @@
     NtQueryObject                         ;;= _NtQueryObject@20
     NtQuerySecurityObject                 ;;= _NtQuerySecurityObject@20
+    NtQuerySymbolicLinkObject             ;;= _NtQuerySymbolicLinkObject@12
     NtQuerySystemInformation              ;;= _NtQuerySystemInformation@16
     NtQueryTimerResolution                ;;= _NtQueryTimerResolution@12
