summaryrefslogtreecommitdiffstats
path: root/src/VBox/Installer/win/Stub
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Installer/win/Stub
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Installer/win/Stub')
-rw-r--r--src/VBox/Installer/win/Stub/Makefile.kmk144
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStub.cpp1197
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStub.h30
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStub.manifest22
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStub.rc56
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp142
-rw-r--r--src/VBox/Installer/win/Stub/VBoxStubCertUtil.h27
-rw-r--r--src/VBox/Installer/win/Stub/crypt32.def25
-rw-r--r--src/VBox/Installer/win/Stub/msi.def23
-rw-r--r--src/VBox/Installer/win/Stub/resource.h32
-rw-r--r--src/VBox/Installer/win/Stub/shell32.def22
-rw-r--r--src/VBox/Installer/win/Stub/user32.def23
-rw-r--r--src/VBox/Installer/win/Stub/ws2_32.def22
13 files changed, 1765 insertions, 0 deletions
diff --git a/src/VBox/Installer/win/Stub/Makefile.kmk b/src/VBox/Installer/win/Stub/Makefile.kmk
new file mode 100644
index 00000000..5a221381
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/Makefile.kmk
@@ -0,0 +1,144 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the stub installer.
+#
+
+#
+# Copyright (C) 2009-2019 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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# This has to be 32-bit, so don't include it in the 64-bit build.
+if "$(KBUILD_TARGET_ARCH)" == "x86"
+
+ TEMPLATE_VBOXSTUB = Drop the signing, we will sign it later.
+ TEMPLATE_VBOXSTUB_EXTENDS = VBOXR3STATIC
+ TEMPLATE_VBOXSTUB_POST_CMDS = $(NO_SUCH_VARIABLE)
+
+ PROGRAMS += VBoxStub
+ VBoxStub_TEMPLATE= VBOXSTUB
+ VBoxStub_DEFS = _WIN32_WINNT=0x0501 IN_RT_R3
+
+ VBoxStub_SOURCES = \
+ VBoxStub.cpp \
+ VBoxStub.rc
+
+ VBoxStub_SDKS += \
+ VBOX_NTDLL
+ VBoxStub_LIBS = \
+ $(VBOX_LIB_RUNTIME_STATIC) \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/delayimp.lib
+ VBoxStub_LDFLAGS = \
+ -DelayLoad:comctl32.dll
+ VBoxStub_POST_CMDS = $(VBOX_CHECK_IMPORTS) --image $(out) ntdll.dll kernel32.dll
+ VBoxStub_LNK_DEPS = $(VBOX_CHECK_IMPORTS)
+
+ VBoxStub.cpp_DEFS += VBOX_SVN_REV=$(VBOX_SVN_REV)
+ VBoxStub.cpp_DEPS = $(VBOX_SVN_REV_KMK)
+
+ # If signing mode is enabled, then add the possibility to install the code
+ # signing certificates (public key only) automatically in /silent mode.
+ ifdef VBOX_SIGNING_MODE
+
+ VBoxStub_SOURCES += VBoxStubCertUtil.cpp
+ VBoxStub_CLEAN += $(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h
+ VBoxStub.cpp_DEPS += $(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h
+ VBoxStub.cpp_INCS += $(VBoxStub_0_OUTDIR)
+ VBoxStub.cpp_DEFS += VBOX_WITH_CODE_SIGNING
+
+ $$(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h: | $$(dir $$@) $(VBOX_RTSIGNTOOL) $(PATH_STAGE_SYS)/VBoxDrv.sys
+ $(RM) -f -- "$@" "$@.cer0" "$@.cer1" "$@.cer2" "$@.array"
+
+ $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 0 --exe "$(PATH_STAGE_SYS)/VBoxDrv.sys" --output "$@.cer0" --der
+ $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert0 "$@.cer0" $@
+ $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert0, sizeof(g_abVBoxStubTrustedCert0) }, "
+ if defined(VBOX_CERTIFICATE_SHA2_SUBJECT_NAME) && (!defined(VBOX_WITH_CORP_CODE_SIGNING) || "$(VBOX_WITH_CORP_CODE_SIGNING)" != "all")
+ $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 1 --exe "$(PATH_STAGE_SYS)/VBoxDrv.sys" --output "$@.cer1" --der
+ $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert1 "$@.cer1" $@
+ $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert1, sizeof(g_abVBoxStubTrustedCert1) }, "
+ endif
+ if defined(VBOX_WITH_CORP_CODE_SIGNING) && "$(VBOX_WITH_CORP_CODE_SIGNING)" != "all" && "$(VBOX_SIGNING_MODE)" == "release"
+ $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 1 --exe "$(VBOX_RTSIGNTOOL)" --output "$@.cer2" --der
+ $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert2 "$@.cer2" $@
+ $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert2, sizeof(g_abVBoxStubTrustedCert2) }, "
+ endif
+ $(APPEND) -n "$@" \
+ "" \
+ "struct { uint8_t const *pab; uint32_t cb; }" "g_aVBoxStubTrustedCerts[] = " \
+ "{"
+ $(SED) --append "$@" -e "" "$@.array"
+ $(APPEND) -n "$@" \
+ "};"
+ $(RM) -f -- "$@.cer0" "$@.cer1" "$@.cer2" "$@.array"
+
+ VBoxStubPublicCert.h:: $$(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h
+
+ endif
+
+ # The icon location is configurable.
+ VBoxStub.rc_INCS += $(VBoxStub_0_OUTDIR)
+ VBoxStub.rc_DEPS += \
+ $(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc \
+ $(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc
+ VBoxStub.rc_CLEAN = \
+ $(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc \
+ $(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc
+
+ # Icon include file.
+ $$(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc: $(VBOX_WINDOWS_ICON_FILE) $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(APPEND) -t $@ 'IDI_VIRTUALBOX ICON DISCARDABLE "$(subst /,\\,$(VBOX_WINDOWS_ICON_FILE))"'
+
+ # Manifest.
+ VBOX_STUB_MANIFEST_FILE := $(PATH_SUB_CURRENT)/VBoxStub.manifest
+ $$(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc: $(VBOX_STUB_MANIFEST_FILE) $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(APPEND) -t $@ 'APP_MANIFEST RT_MANIFEST "$(subst /,\\,$(VBOX_STUB_MANIFEST_FILE))"'
+
+ # Dynamic import no. 1: MSI.DLL
+ VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/MsiLazyLoad.asm
+ $$(VBoxStub_0_OUTDIR)/MsiLazyLoad.asm: $(PATH_SUB_CURRENT)/msi.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@)
+ $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@)
+ $(QUIET)$(RM) -f -- "$@"
+ $(VBOX_DEF_2_LAZY_LOAD) --system --library MSI.DLL --output "$@" $(filter %.def, $^)
+
+ # Dynamic import no. 2: CRYPTO32.DLL
+ VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Crypt32LazyLoad.asm
+ $$(VBoxStub_0_OUTDIR)/Crypt32LazyLoad.asm: $(PATH_SUB_CURRENT)/crypt32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@)
+ $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@)
+ $(QUIET)$(RM) -f -- "$@"
+ $(VBOX_DEF_2_LAZY_LOAD) --system --library CRYPT32.DLL --output "$@" $(filter %.def, $^)
+
+ # Dynamic import no. 3: WS2_32.DLL
+ VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Ws232LazyLoad.asm
+ $$(VBoxStub_0_OUTDIR)/Ws232LazyLoad.asm: $(PATH_SUB_CURRENT)/ws2_32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@)
+ $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@)
+ $(QUIET)$(RM) -f -- "$@"
+ $(VBOX_DEF_2_LAZY_LOAD) --system --library WS2_32.DLL --output "$@" $(filter %.def, $^)
+
+ # Dynamic import no. 4: USER32.DLL
+ VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/User32LazyLoad.asm
+ $$(VBoxStub_0_OUTDIR)/User32LazyLoad.asm: $(PATH_SUB_CURRENT)/user32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@)
+ $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@)
+ $(QUIET)$(RM) -f -- "$@"
+ $(VBOX_DEF_2_LAZY_LOAD) --system --library USER32.DLL --output "$@" $(filter %.def, $^)
+
+ # Dynamic import no. 5: SHELL32.DLL
+ VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Shell32LazyLoad.asm
+ $$(VBoxStub_0_OUTDIR)/Shell32LazyLoad.asm: $(PATH_SUB_CURRENT)/shell32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@)
+ $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@)
+ $(QUIET)$(RM) -f -- "$@"
+ $(VBOX_DEF_2_LAZY_LOAD) --system --library SHELL32.DLL --output "$@" $(filter %.def, $^)
+
+
+endif # x86 only
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/Installer/win/Stub/VBoxStub.cpp b/src/VBox/Installer/win/Stub/VBoxStub.cpp
new file mode 100644
index 00000000..32647468
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStub.cpp
@@ -0,0 +1,1197 @@
+/* $Id: VBoxStub.cpp $ */
+/** @file
+ * VBoxStub - VirtualBox's Windows installer stub.
+ */
+
+/*
+ * Copyright (C) 2010-2019 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0501
+# undef _WIN32_WINNT
+# define _WIN32_WINNT 0x0501 /* AttachConsole() / FreeConsole(). */
+#endif
+
+#include <iprt/win/windows.h>
+#include <commctrl.h>
+#include <fcntl.h>
+#include <io.h>
+#include <lmerr.h>
+#include <msiquery.h>
+#include <iprt/win/objbase.h>
+
+#include <iprt/win/shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strsafe.h>
+
+#include <VBox/version.h>
+
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+
+#include "VBoxStub.h"
+#include "../StubBld/VBoxStubBld.h"
+#include "resource.h"
+
+#ifdef VBOX_WITH_CODE_SIGNING
+# include "VBoxStubCertUtil.h"
+# include "VBoxStubPublicCert.h"
+#endif
+
+#ifndef TARGET_NT4
+/* Use an own console window if run in verbose mode. */
+# define VBOX_STUB_WITH_OWN_CONSOLE
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define MY_UNICODE_SUB(str) L ##str
+#define MY_UNICODE(str) MY_UNICODE_SUB(str)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Cleanup record.
+ */
+typedef struct STUBCLEANUPREC
+{
+ /** List entry. */
+ RTLISTNODE ListEntry;
+ /** True if file, false if directory. */
+ bool fFile;
+ /** The path to the file or directory to clean up. */
+ char szPath[1];
+} STUBCLEANUPREC;
+/** Pointer to a cleanup record. */
+typedef STUBCLEANUPREC *PSTUBCLEANUPREC;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether it's a silent or interactive GUI driven install. */
+static bool g_fSilent = false;
+/** List of temporary files. */
+static RTLISTANCHOR g_TmpFiles;
+/** Verbosity flag. */
+static int g_iVerbosity = 0;
+
+
+
+/**
+ * Shows an error message box with a printf() style formatted string.
+ *
+ * @returns RTEXITCODE_FAILURE
+ * @param pszFmt Printf-style format string to show in the message box body.
+ *
+ */
+static RTEXITCODE ShowError(const char *pszFmt, ...)
+{
+ char *pszMsg;
+ va_list va;
+
+ va_start(va, pszFmt);
+ if (RTStrAPrintfV(&pszMsg, pszFmt, va))
+ {
+ if (g_fSilent)
+ RTMsgError("%s", pszMsg);
+ else
+ {
+ PRTUTF16 pwszMsg;
+ int rc = RTStrToUtf16(pszMsg, &pwszMsg);
+ if (RT_SUCCESS(rc))
+ {
+ MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR);
+ RTUtf16Free(pwszMsg);
+ }
+ else
+ MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
+ }
+ RTStrFree(pszMsg);
+ }
+ else /* Should never happen! */
+ AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
+ va_end(va);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Shows a message box with a printf() style formatted string.
+ *
+ * @param uType Type of the message box (see MSDN).
+ * @param pszFmt Printf-style format string to show in the message box body.
+ *
+ */
+static void ShowInfo(const char *pszFmt, ...)
+{
+ char *pszMsg;
+ va_list va;
+ va_start(va, pszFmt);
+ int rc = RTStrAPrintfV(&pszMsg, pszFmt, va);
+ va_end(va);
+ if (rc >= 0)
+ {
+ if (g_fSilent)
+ RTPrintf("%s\n", pszMsg);
+ else
+ {
+ PRTUTF16 pwszMsg;
+ int rc = RTStrToUtf16(pszMsg, &pwszMsg);
+ if (RT_SUCCESS(rc))
+ {
+ MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION);
+ RTUtf16Free(pwszMsg);
+ }
+ else
+ MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
+ }
+ }
+ else /* Should never happen! */
+ AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
+ RTStrFree(pszMsg);
+}
+
+
+/**
+ * Finds the specified in the resource section of the executable.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pszDataName Name of resource to read.
+ * @param ppvResource Where to return the pointer to the data.
+ * @param pdwSize Where to return the size of the data (if found).
+ * Optional.
+ */
+static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize)
+{
+ AssertReturn(pszDataName, VERR_INVALID_PARAMETER);
+ HINSTANCE hInst = NULL; /* indicates the executable image */
+
+ /* Find our resource. */
+ PRTUTF16 pwszDataName;
+ int rc = RTStrToUtf16(pszDataName, &pwszDataName);
+ AssertRCReturn(rc, rc);
+ HRSRC hRsrc = FindResourceExW(hInst,
+ (LPWSTR)RT_RCDATA,
+ pwszDataName,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+ RTUtf16Free(pwszDataName);
+ AssertReturn(hRsrc, VERR_IO_GEN_FAILURE);
+
+ /* Get resource size. */
+ DWORD cb = SizeofResource(hInst, hRsrc);
+ AssertReturn(cb > 0, VERR_NO_DATA);
+ if (pdwSize)
+ *pdwSize = cb;
+
+ /* Get pointer to resource. */
+ HGLOBAL hData = LoadResource(hInst, hRsrc);
+ AssertReturn(hData, VERR_IO_GEN_FAILURE);
+
+ /* Lock resource. */
+ *ppvResource = LockResource(hData);
+ AssertReturn(*ppvResource, VERR_IO_GEN_FAILURE);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Finds the header for the given package.
+ *
+ * @returns Pointer to the package header on success. On failure NULL is
+ * returned after ShowError has been invoked.
+ * @param iPackage The package number.
+ */
+static PVBOXSTUBPKG FindPackageHeader(unsigned iPackage)
+{
+ char szHeaderName[32];
+ RTStrPrintf(szHeaderName, sizeof(szHeaderName), "HDR_%02d", iPackage);
+
+ PVBOXSTUBPKG pPackage;
+ int rc = FindData(szHeaderName, (PVOID *)&pPackage, NULL);
+ if (RT_FAILURE(rc))
+ {
+ ShowError("Internal error: Could not find package header #%u: %Rrc", iPackage, rc);
+ return NULL;
+ }
+
+ /** @todo validate it. */
+ return pPackage;
+}
+
+
+
+/**
+ * Constructs a full temporary file path from the given parameters.
+ *
+ * @returns iprt status code.
+ *
+ * @param pszTempPath The pure path to use for construction.
+ * @param pszTargetFileName The pure file name to use for construction.
+ * @param ppszTempFile Pointer to the constructed string. Must be freed
+ * using RTStrFree().
+ */
+static int GetTempFileAlloc(const char *pszTempPath,
+ const char *pszTargetFileName,
+ char **ppszTempFile)
+{
+ if (RTStrAPrintf(ppszTempFile, "%s\\%s", pszTempPath, pszTargetFileName) >= 0)
+ return VINF_SUCCESS;
+ return VERR_NO_STR_MEMORY;
+}
+
+
+/**
+ * Extracts a built-in resource to disk.
+ *
+ * @returns iprt status code.
+ *
+ * @param pszResourceName The resource name to extract.
+ * @param pszTempFile The full file path + name to extract the resource to.
+ *
+ */
+static int ExtractFile(const char *pszResourceName,
+ const char *pszTempFile)
+{
+#if 0 /* Another example of how unnecessarily complicated things get with
+ do-break-while-false and you end up with buggy code using uninitialized
+ variables. */
+ int rc;
+ RTFILE fh;
+ BOOL bCreatedFile = FALSE;
+
+ do
+ {
+ AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n")); /* rc is not initialized here, we'll return garbage. */
+ AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!")); /* Ditto. */
+
+ /* Read the data of the built-in resource. */
+ PVOID pvData = NULL;
+ DWORD dwDataSize = 0;
+ rc = FindData(pszResourceName, &pvData, &dwDataSize);
+ AssertMsgRCBreak(rc, ("Could not read resource data!\n"));
+
+ /* Create new (and replace an old) file. */
+ rc = RTFileOpen(&fh, pszTempFile,
+ RTFILE_O_CREATE_REPLACE
+ | RTFILE_O_WRITE
+ | RTFILE_O_DENY_NOT_DELETE
+ | RTFILE_O_DENY_WRITE);
+ AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
+ bCreatedFile = TRUE;
+
+ /* Write contents to new file. */
+ size_t cbWritten = 0;
+ rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten);
+ AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
+ AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n"));
+
+ } while (0);
+
+ if (RTFileIsValid(fh)) /* fh is unused uninitalized (MSC agrees) */
+ RTFileClose(fh);
+
+ if (RT_FAILURE(rc))
+ {
+ if (bCreatedFile)
+ RTFileDelete(pszTempFile);
+ }
+
+#else /* This is exactly the same as above, except no bug and better assertion
+ message. Note only the return-success statment is indented, indicating
+ that the whole do-break-while-false approach was totally unnecessary. */
+
+ AssertPtrReturn(pszResourceName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszTempFile, VERR_INVALID_POINTER);
+
+ /* Read the data of the built-in resource. */
+ PVOID pvData = NULL;
+ DWORD dwDataSize = 0;
+ int rc = FindData(pszResourceName, &pvData, &dwDataSize);
+ AssertMsgRCReturn(rc, ("Could not read resource data: %Rrc\n", rc), rc);
+
+ /* Create new (and replace an old) file. */
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszTempFile,
+ RTFILE_O_CREATE_REPLACE
+ | RTFILE_O_WRITE
+ | RTFILE_O_DENY_NOT_DELETE
+ | RTFILE_O_DENY_WRITE);
+ AssertMsgRCReturn(rc, ("Could not open '%s' for writing: %Rrc\n", pszTempFile, rc), rc);
+
+ /* Write contents to new file. */
+ size_t cbWritten = 0;
+ rc = RTFileWrite(hFile, pvData, dwDataSize, &cbWritten);
+ AssertMsgStmt(cbWritten == dwDataSize || RT_FAILURE_NP(rc), ("%#zx vs %#x\n", cbWritten, dwDataSize), rc = VERR_WRITE_ERROR);
+
+ int rc2 = RTFileClose(hFile);
+ AssertRC(rc2);
+
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTFileDelete(pszTempFile);
+
+#endif
+ return rc;
+}
+
+
+/**
+ * Extracts a built-in resource to disk.
+ *
+ * @returns iprt status code.
+ *
+ * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
+ * @param pszTempFile The full file path + name to extract the resource to.
+ */
+static int Extract(const PVBOXSTUBPKG pPackage,
+ const char *pszTempFile)
+{
+ return ExtractFile(pPackage->szResourceName, pszTempFile);
+}
+
+
+/**
+ * Detects whether we're running on a 32- or 64-bit platform and returns the result.
+ *
+ * @returns TRUE if we're running on a 64-bit OS, FALSE if not.
+ */
+static BOOL IsWow64(void)
+{
+ BOOL fIsWow64 = TRUE;
+ fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
+ if (NULL != fnIsWow64Process)
+ {
+ if (!fnIsWow64Process(GetCurrentProcess(), &fIsWow64))
+ {
+ /* Error in retrieving process type - assume that we're running on 32bit. */
+ return FALSE;
+ }
+ }
+ return fIsWow64;
+}
+
+
+/**
+ * Decides whether we need a specified package to handle or not.
+ *
+ * @returns @c true if we need to handle the specified package, @c false if not.
+ *
+ * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
+ */
+static bool PackageIsNeeded(PVBOXSTUBPKG pPackage)
+{
+ if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
+ return true;
+ VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86;
+ return pPackage->byArch == enmArch;
+}
+
+
+/**
+ * Adds a cleanup record.
+ *
+ * @returns Fully complained boolean success indicator.
+ * @param pszPath The path to the file or directory to clean up.
+ * @param fFile @c true if file, @c false if directory.
+ */
+static bool AddCleanupRec(const char *pszPath, bool fFile)
+{
+ size_t cchPath = strlen(pszPath); Assert(cchPath > 0);
+ PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_UOFFSETOF_DYN(STUBCLEANUPREC, szPath[cchPath + 1]));
+ if (!pRec)
+ {
+ ShowError("Out of memory!");
+ return false;
+ }
+ pRec->fFile = fFile;
+ memcpy(pRec->szPath, pszPath, cchPath + 1);
+
+ RTListPrepend(&g_TmpFiles, &pRec->ListEntry);
+ return true;
+}
+
+
+/**
+ * Cleans up all the extracted files and optionally removes the package
+ * directory.
+ *
+ * @param pszPkgDir The package directory, NULL if it shouldn't be
+ * removed.
+ */
+static void CleanUp(const char *pszPkgDir)
+{
+ for (int i = 0; i < 5; i++)
+ {
+ int rc;
+ bool fFinalTry = i == 4;
+
+ PSTUBCLEANUPREC pCur, pNext;
+ RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
+ {
+ if (pCur->fFile)
+ rc = RTFileDelete(pCur->szPath);
+ else
+ {
+ rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR);
+ if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
+ rc = VINF_SUCCESS;
+ }
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ else if (fFinalTry)
+ {
+ if (pCur->fFile)
+ ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc);
+ else
+ ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc);
+ }
+ }
+
+ if (RTListIsEmpty(&g_TmpFiles) || fFinalTry)
+ {
+ if (!pszPkgDir)
+ return;
+ rc = RTDirRemove(pszPkgDir);
+ if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry)
+ return;
+ }
+
+ /* Delay a little and try again. */
+ RTThreadSleep(i == 0 ? 100 : 3000);
+ }
+}
+
+
+/**
+ * Processes an MSI package.
+ *
+ * @returns Fully complained exit code.
+ * @param pszMsi The path to the MSI to process.
+ * @param pszMsiArgs Any additional installer (MSI) argument
+ * @param fLogging Whether to enable installer logging.
+ */
+static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging)
+{
+ int rc;
+
+ /*
+ * Set UI level.
+ */
+ INSTALLUILEVEL enmDesiredUiLevel = g_fSilent ? INSTALLUILEVEL_NONE : INSTALLUILEVEL_FULL;
+ INSTALLUILEVEL enmRet = MsiSetInternalUI(enmDesiredUiLevel, NULL);
+ if (enmRet == INSTALLUILEVEL_NOCHANGE /* means error */)
+ return ShowError("Internal error: MsiSetInternalUI failed.");
+
+ /*
+ * Enable logging?
+ */
+ if (fLogging)
+ {
+ char szLogFile[RTPATH_MAX];
+ rc = RTStrCopy(szLogFile, sizeof(szLogFile), pszMsi);
+ if (RT_SUCCESS(rc))
+ {
+ RTPathStripFilename(szLogFile);
+ rc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxInstallLog.txt");
+ }
+ if (RT_FAILURE(rc))
+ return ShowError("Internal error: Filename path too long.");
+
+ PRTUTF16 pwszLogFile;
+ rc = RTStrToUtf16(szLogFile, &pwszLogFile);
+ if (RT_FAILURE(rc))
+ return ShowError("RTStrToUtf16 failed on '%s': %Rrc", szLogFile, rc);
+
+ UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE,
+ pwszLogFile,
+ INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
+ RTUtf16Free(pwszLogFile);
+ if (uLogLevel != ERROR_SUCCESS)
+ return ShowError("MsiEnableLogW failed");
+ }
+
+ /*
+ * Initialize the common controls (extended version). This is necessary to
+ * run the actual .MSI installers with the new fancy visual control
+ * styles (XP+). Also, an integrated manifest is required.
+ */
+ INITCOMMONCONTROLSEX ccEx;
+ ccEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ ccEx.dwICC = ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS |
+ ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES |
+ ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES;
+ InitCommonControlsEx(&ccEx); /* Ignore failure. */
+
+ /*
+ * Convert both strings to UTF-16 and start the installation.
+ */
+ PRTUTF16 pwszMsi;
+ rc = RTStrToUtf16(pszMsi, &pwszMsi);
+ if (RT_FAILURE(rc))
+ return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
+ PRTUTF16 pwszMsiArgs;
+ rc = RTStrToUtf16(pszMsiArgs, &pwszMsiArgs);
+ if (RT_FAILURE(rc))
+ {
+ RTUtf16Free(pwszMsi);
+ return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
+ }
+
+ UINT uStatus = MsiInstallProductW(pwszMsi, pwszMsiArgs);
+ RTUtf16Free(pwszMsi);
+ RTUtf16Free(pwszMsiArgs);
+
+ if (uStatus == ERROR_SUCCESS)
+ return RTEXITCODE_SUCCESS;
+ if (uStatus == ERROR_SUCCESS_REBOOT_REQUIRED)
+ return RTEXITCODE_SUCCESS; /* we currently don't indicate this */
+
+ /*
+ * Installation failed. Figure out what to say.
+ */
+ switch (uStatus)
+ {
+ case ERROR_INSTALL_USEREXIT:
+ /* Don't say anything? */
+ break;
+
+ case ERROR_INSTALL_PACKAGE_VERSION:
+ ShowError("This installation package cannot be installed by the Windows Installer service.\n"
+ "You must install a Windows service pack that contains a newer version of the Windows Installer service.");
+ break;
+
+ case ERROR_INSTALL_PLATFORM_UNSUPPORTED:
+ ShowError("This installation package is not supported on this platform.");
+ break;
+
+ default:
+ {
+ /*
+ * Try get windows to format the message.
+ */
+ DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_SYSTEM;
+ HMODULE hModule = NULL;
+ if (uStatus >= NERR_BASE && uStatus <= MAX_NERR)
+ {
+ hModule = LoadLibraryExW(L"netmsg.dll",
+ NULL,
+ LOAD_LIBRARY_AS_DATAFILE);
+ if (hModule != NULL)
+ dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ PWSTR pwszMsg;
+ if (FormatMessageW(dwFormatFlags,
+ hModule, /* If NULL, load system stuff. */
+ uStatus,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (PWSTR)&pwszMsg,
+ 0,
+ NULL) > 0)
+ {
+ ShowError("Installation failed! Error: %ls", pwszMsg);
+ LocalFree(pwszMsg);
+ }
+ else /* If text lookup failed, show at least the error number. */
+ ShowError("Installation failed! Error: %u", uStatus);
+
+ if (hModule)
+ FreeLibrary(hModule);
+ break;
+ }
+ }
+
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Processes a package.
+ *
+ * @returns Fully complained exit code.
+ * @param iPackage The package number.
+ * @param pszPkgDir The package directory (aka extraction dir).
+ * @param pszMsiArgs Any additional installer (MSI) argument
+ * @param fLogging Whether to enable installer logging.
+ */
+static RTEXITCODE ProcessPackage(unsigned iPackage, const char *pszPkgDir, const char *pszMsiArgs, bool fLogging)
+{
+ /*
+ * Get the package header and check if it's needed.
+ */
+ PVBOXSTUBPKG pPackage = FindPackageHeader(iPackage);
+ if (pPackage == NULL)
+ return RTEXITCODE_FAILURE;
+
+ if (!PackageIsNeeded(pPackage))
+ return RTEXITCODE_SUCCESS;
+
+ /*
+ * Deal with the file based on it's extension.
+ */
+ char szPkgFile[RTPATH_MAX];
+ int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName);
+ if (RT_FAILURE(rc))
+ return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
+ RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */
+
+ RTEXITCODE rcExit;
+ const char *pszSuff = RTPathSuffix(szPkgFile);
+ if (RTStrICmp(pszSuff, ".msi") == 0)
+ rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging);
+ else if (RTStrICmp(pszSuff, ".cab") == 0)
+ rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */
+ else
+ rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName);
+
+ return rcExit;
+}
+
+
+#ifdef VBOX_WITH_CODE_SIGNING
+/**
+ * Install the public certificate into TrustedPublishers so the installer won't
+ * prompt the user during silent installs.
+ *
+ * @returns Fully complained exit code.
+ */
+static RTEXITCODE InstallCertificates(void)
+{
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aVBoxStubTrustedCerts); i++)
+ {
+ if (!addCertToStore(CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ "TrustedPublisher",
+ g_aVBoxStubTrustedCerts[i].pab,
+ g_aVBoxStubTrustedCerts[i].cb))
+ return ShowError("Failed to construct install certificate.");
+ }
+ return RTEXITCODE_SUCCESS;
+}
+#endif /* VBOX_WITH_CODE_SIGNING */
+
+
+/**
+ * Copies the "<exepath>.custom" directory to the extraction path if it exists.
+ *
+ * This is used by the MSI packages from the resource section.
+ *
+ * @returns Fully complained exit code.
+ * @param pszDstDir The destination directory.
+ */
+static RTEXITCODE CopyCustomDir(const char *pszDstDir)
+{
+ char szSrcDir[RTPATH_MAX];
+ int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir));
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom");
+ if (RT_FAILURE(rc))
+ return ShowError("Failed to construct '.custom' dir path: %Rrc", rc);
+
+ if (RTDirExists(szSrcDir))
+ {
+ /*
+ * Use SHFileOperation w/ FO_COPY to do the job. This API requires an
+ * extra zero at the end of both source and destination paths.
+ */
+ size_t cwc;
+ RTUTF16 wszSrcDir[RTPATH_MAX + 1];
+ PRTUTF16 pwszSrcDir = wszSrcDir;
+ rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc);
+ if (RT_FAILURE(rc))
+ return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", szSrcDir, rc);
+ wszSrcDir[cwc] = '\0';
+
+ RTUTF16 wszDstDir[RTPATH_MAX + 1];
+ PRTUTF16 pwszDstDir = wszSrcDir;
+ rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc);
+ if (RT_FAILURE(rc))
+ return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", pszDstDir, rc);
+ wszDstDir[cwc] = '\0';
+
+ SHFILEOPSTRUCTW FileOp;
+ RT_ZERO(FileOp); /* paranoia */
+ FileOp.hwnd = NULL;
+ FileOp.wFunc = FO_COPY;
+ FileOp.pFrom = wszSrcDir;
+ FileOp.pTo = wszDstDir;
+ FileOp.fFlags = FOF_SILENT
+ | FOF_NOCONFIRMATION
+ | FOF_NOCONFIRMMKDIR
+ | FOF_NOERRORUI;
+ FileOp.fAnyOperationsAborted = FALSE;
+ FileOp.hNameMappings = NULL;
+ FileOp.lpszProgressTitle = NULL;
+
+ rc = SHFileOperationW(&FileOp);
+ if (rc != 0) /* Not a Win32 status code! */
+ return ShowError("Copying the '.custom' dir failed: %#x", rc);
+
+ /*
+ * Add a cleanup record for recursively deleting the destination
+ * .custom directory. We should actually add this prior to calling
+ * SHFileOperationW since it may partially succeed...
+ */
+ char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom");
+ if (!pszDstSubDir)
+ return ShowError("Out of memory!");
+ bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/);
+ RTStrFree(pszDstSubDir);
+ if (!fRc)
+ return RTEXITCODE_FAILURE;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir)
+{
+ int rc;
+
+ /*
+ * Make sure the directory exists.
+ */
+ *pfCreatedExtractDir = false;
+ if (!RTDirExists(pszDstDir))
+ {
+ rc = RTDirCreate(pszDstDir, 0700, 0);
+ if (RT_FAILURE(rc))
+ return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc);
+ *pfCreatedExtractDir = true;
+ }
+
+ /*
+ * Extract files.
+ */
+ for (unsigned k = 0; k < cPackages; k++)
+ {
+ PVBOXSTUBPKG pPackage = FindPackageHeader(k);
+ if (!pPackage)
+ return RTEXITCODE_FAILURE; /* Done complaining already. */
+
+ if (fExtractOnly || PackageIsNeeded(pPackage))
+ {
+ char szDstFile[RTPATH_MAX];
+ rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
+ if (RT_FAILURE(rc))
+ return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
+
+ rc = Extract(pPackage, szDstFile);
+ if (RT_FAILURE(rc))
+ return ShowError("Error extracting package #%u: %Rrc", k, rc);
+
+ if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/))
+ {
+ RTFileDelete(szDstFile);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+int WINAPI WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ char *lpCmdLine,
+ int nCmdShow)
+{
+ RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
+ char **argv = __argv;
+ int argc = __argc;
+
+ /*
+ * Init IPRT. This is _always_ the very first thing we do.
+ */
+ int vrc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_STANDALONE_APP);
+ if (RT_FAILURE(vrc))
+ return RTMsgInitFailure(vrc);
+
+ /*
+ * Check if we're already running and jump out if so.
+ *
+ * Note! Do not use a global namespace ("Global\\") for mutex name here,
+ * will blow up NT4 compatibility!
+ */
+ HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxStubInstaller");
+ if ( hMutexAppRunning != NULL
+ && GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ /* Close the mutex for this application instance. */
+ CloseHandle(hMutexAppRunning);
+ hMutexAppRunning = NULL;
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Parse arguments.
+ */
+
+ /* Parameter variables. */
+ bool fExtractOnly = false;
+ bool fEnableLogging = false;
+#ifdef VBOX_WITH_CODE_SIGNING
+ bool fEnableSilentCert = true;
+#endif
+ char szExtractPath[RTPATH_MAX] = {0};
+ char szMSIArgs[_4K] = {0};
+
+ /* Parameter definitions. */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ /** @todo Replace short parameters with enums since they're not
+ * used (and not documented to the public). */
+ { "--extract", 'x', RTGETOPT_REQ_NOTHING },
+ { "-extract", 'x', RTGETOPT_REQ_NOTHING },
+ { "/extract", 'x', RTGETOPT_REQ_NOTHING },
+ { "--silent", 's', RTGETOPT_REQ_NOTHING },
+ { "-silent", 's', RTGETOPT_REQ_NOTHING },
+ { "/silent", 's', RTGETOPT_REQ_NOTHING },
+#ifdef VBOX_WITH_CODE_SIGNING
+ { "--no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
+ { "-no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
+ { "/no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
+#endif
+ { "--logging", 'l', RTGETOPT_REQ_NOTHING },
+ { "-logging", 'l', RTGETOPT_REQ_NOTHING },
+ { "/logging", 'l', RTGETOPT_REQ_NOTHING },
+ { "--path", 'p', RTGETOPT_REQ_STRING },
+ { "-path", 'p', RTGETOPT_REQ_STRING },
+ { "/path", 'p', RTGETOPT_REQ_STRING },
+ { "--msiparams", 'm', RTGETOPT_REQ_STRING },
+ { "-msiparams", 'm', RTGETOPT_REQ_STRING },
+ { "--reinstall", 'f', RTGETOPT_REQ_NOTHING },
+ { "-reinstall", 'f', RTGETOPT_REQ_NOTHING },
+ { "/reinstall", 'f', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "-verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "/verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ { "-version", 'V', RTGETOPT_REQ_NOTHING },
+ { "/version", 'V', RTGETOPT_REQ_NOTHING },
+ { "-v", 'V', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING },
+ { "-help", 'h', RTGETOPT_REQ_NOTHING },
+ { "/help", 'h', RTGETOPT_REQ_NOTHING },
+ { "/?", 'h', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+
+ /* Parse the parameters. */
+ int ch;
+ bool fExitEarly = false;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && rcExit == RTEXITCODE_SUCCESS
+ && !fExitEarly)
+ {
+ switch (ch)
+ {
+ case 'f': /* Force re-installation. */
+ if (szMSIArgs[0])
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
+ if (RT_SUCCESS(vrc))
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs),
+ "REINSTALLMODE=vomus REINSTALL=ALL");
+ if (RT_FAILURE(vrc))
+ rcExit = ShowError("MSI parameters are too long.");
+ break;
+
+ case 'x':
+ fExtractOnly = true;
+ break;
+
+ case 's':
+ g_fSilent = true;
+ break;
+
+#ifdef VBOX_WITH_CODE_SIGNING
+ case 'c':
+ fEnableSilentCert = false;
+ break;
+#endif
+ case 'l':
+ fEnableLogging = true;
+ break;
+
+ case 'p':
+ vrc = RTStrCopy(szExtractPath, sizeof(szExtractPath), ValueUnion.psz);
+ if (RT_FAILURE(vrc))
+ rcExit = ShowError("Extraction path is too long.");
+ break;
+
+ case 'm':
+ if (szMSIArgs[0])
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
+ if (RT_SUCCESS(vrc))
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
+ if (RT_FAILURE(vrc))
+ rcExit = ShowError("MSI parameters are too long.");
+ break;
+
+ case 'V':
+ ShowInfo("Version: %d.%d.%d.%d",
+ VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD,
+ VBOX_SVN_REV);
+ fExitEarly = true;
+ break;
+
+ case 'v':
+ g_iVerbosity++;
+ break;
+
+ case 'h':
+ ShowInfo("-- %s v%d.%d.%d.%d --\n"
+ "\n"
+ "Command Line Parameters:\n\n"
+ "--extract - Extract file contents to temporary directory\n"
+ "--help - Print this help and exit\n"
+ "--logging - Enables installer logging\n"
+ "--msiparams <parameters> - Specifies extra parameters for the MSI installers\n"
+ "--no-silent-cert - Do not install VirtualBox Certificate automatically when --silent option is specified\n"
+ "--path - Sets the path of the extraction directory\n"
+ "--reinstall - Forces VirtualBox to get re-installed\n"
+ "--silent - Enables silent mode installation\n"
+ "--version - Print version number and exit\n\n"
+ "Examples:\n"
+ "%s --msiparams INSTALLDIR=C:\\VBox\n"
+ "%s --extract -path C:\\VBox",
+ VBOX_STUB_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
+ argv[0], argv[0]);
+ fExitEarly = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* Are (optional) MSI parameters specified and this is the last
+ * parameter? Append everything to the MSI parameter list then. */
+ if (szMSIArgs[0])
+ {
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
+ if (RT_SUCCESS(vrc))
+ vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
+ if (RT_FAILURE(vrc))
+ rcExit = ShowError("MSI parameters are too long.");
+ continue;
+ }
+ /* Fall through is intentional. */
+
+ default:
+ if (g_fSilent)
+ rcExit = RTGetOptPrintError(ch, &ValueUnion);
+ if (ch == VERR_GETOPT_UNKNOWN_OPTION)
+ rcExit = ShowError("Unknown option \"%s\"\n"
+ "Please refer to the command line help by specifying \"/?\"\n"
+ "to get more information.", ValueUnion.psz);
+ else
+ rcExit = ShowError("Parameter parsing error: %Rrc\n"
+ "Please refer to the command line help by specifying \"/?\"\n"
+ "to get more information.", ch);
+ break;
+ }
+ }
+
+ /* Check if we can bail out early. */
+ if (fExitEarly)
+ return rcExit;
+
+ if (rcExit != RTEXITCODE_SUCCESS)
+ vrc = VERR_PARSE_ERROR;
+
+/** @todo
+ *
+ * Split the remainder up in functions and simplify the code flow!!
+ *
+ * */
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
+# ifdef VBOX_STUB_WITH_OWN_CONSOLE /* Use an own console window if run in debug mode. */
+ if ( RT_SUCCESS(vrc)
+ && g_iVerbosity)
+ {
+ if (!AllocConsole())
+ {
+ DWORD dwErr = GetLastError();
+ ShowError("Unable to allocate console, error = %ld\n",
+ dwErr);
+
+ /* Close the mutex for this application instance. */
+ CloseHandle(hMutexAppRunning);
+ hMutexAppRunning = NULL;
+ return RTEXITCODE_FAILURE;
+ }
+
+ freopen("CONOUT$", "w", stdout);
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ freopen("CONOUT$", "w", stderr);
+ }
+# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
+#endif
+
+ if ( RT_SUCCESS(vrc)
+ && g_iVerbosity)
+ {
+ RTPrintf("Silent installation : %RTbool\n", g_fSilent);
+ RTPrintf("Logging enabled : %RTbool\n", fEnableLogging);
+#ifdef VBOX_WITH_CODE_SIGNING
+ RTPrintf("Certificate installation : %RTbool\n", fEnableSilentCert);
+#endif
+ RTPrintf("Additional MSI parameters: %s\n",
+ szMSIArgs[0] ? szMSIArgs : "<None>");
+ }
+
+ /*
+ * 32-bit is not officially supported any more.
+ */
+ if ( !fExtractOnly
+ && !g_fSilent
+ && !IsWow64())
+ {
+ rcExit = ShowError("32-bit Windows hosts are not supported by this VirtualBox release.");
+ vrc = VERR_NOT_SUPPORTED;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Determine the extration path if not given by the user, and gather some
+ * other bits we'll be needing later.
+ */
+ if (szExtractPath[0] == '\0')
+ {
+ vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath));
+ if (RT_SUCCESS(vrc))
+ vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox");
+ if (RT_FAILURE(vrc))
+ ShowError("Failed to determine extraction path (%Rrc)", vrc);
+
+ }
+ else
+ {
+ /** @todo should check if there is a .custom subdirectory there or not. */
+ }
+ RTPathChangeToDosSlashes(szExtractPath,
+ true /* Force conversion. */); /* MSI requirement. */
+ }
+
+ /* Read our manifest. */
+ if (RT_SUCCESS(vrc))
+ {
+ PVBOXSTUBPKGHEADER pHeader;
+ vrc = FindData("MANIFEST", (PVOID *)&pHeader, NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ /** @todo If we could, we should validate the header. Only the magic isn't
+ * commonly defined, nor the version number... */
+
+ RTListInit(&g_TmpFiles);
+
+ /*
+ * Up to this point, we haven't done anything that requires any cleanup.
+ * From here on, we do everything in function so we can counter clean up.
+ */
+ bool fCreatedExtractDir;
+ rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath,
+ fExtractOnly, &fCreatedExtractDir);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ if (fExtractOnly)
+ ShowInfo("Files were extracted to: %s", szExtractPath);
+ else
+ {
+ rcExit = CopyCustomDir(szExtractPath);
+#ifdef VBOX_WITH_CODE_SIGNING
+ if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent)
+ rcExit = InstallCertificates();
+#endif
+ unsigned iPackage = 0;
+ while ( iPackage < pHeader->byCntPkgs
+ && rcExit == RTEXITCODE_SUCCESS)
+ {
+ rcExit = ProcessPackage(iPackage, szExtractPath,
+ szMSIArgs, fEnableLogging);
+ iPackage++;
+ }
+
+ /* Don't fail if cleanup fail. At least for now. */
+ CleanUp( !fEnableLogging
+ && fCreatedExtractDir ? szExtractPath : NULL);
+ }
+ }
+
+ /* Free any left behind cleanup records (not strictly needed). */
+ PSTUBCLEANUPREC pCur, pNext;
+ RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ }
+ else
+ rcExit = ShowError("Internal package error: Manifest not found (%Rrc)", vrc);
+ }
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
+# ifdef VBOX_STUB_WITH_OWN_CONSOLE
+ if (g_iVerbosity)
+ FreeConsole();
+# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
+#endif
+
+ /*
+ * Release instance mutex.
+ */
+ if (hMutexAppRunning != NULL)
+ {
+ CloseHandle(hMutexAppRunning);
+ hMutexAppRunning = NULL;
+ }
+
+ return rcExit;
+}
+
diff --git a/src/VBox/Installer/win/Stub/VBoxStub.h b/src/VBox/Installer/win/Stub/VBoxStub.h
new file mode 100644
index 00000000..1676ec60
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStub.h
@@ -0,0 +1,30 @@
+/* $Id: VBoxStub.h $ */
+/** @file
+ * VBoxStub - VirtualBox's Windows installer stub.
+ */
+
+/*
+ * Copyright (C) 2009-2019 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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Stub_VBoxStub_h
+#define VBOX_INCLUDED_SRC_Stub_VBoxStub_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOX_STUB_TITLE "VirtualBox Installer"
+
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+LPFN_ISWOW64PROCESS fnIsWow64Process;
+
+#endif /* !VBOX_INCLUDED_SRC_Stub_VBoxStub_h */
+
diff --git a/src/VBox/Installer/win/Stub/VBoxStub.manifest b/src/VBox/Installer/win/Stub/VBoxStub.manifest
new file mode 100644
index 00000000..f2076fee
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStub.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="VBoxStub.exe"
+ type="win32"
+/>
+<description>VirtualBox Windows Installer</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/src/VBox/Installer/win/Stub/VBoxStub.rc b/src/VBox/Installer/win/Stub/VBoxStub.rc
new file mode 100644
index 00000000..ce0a9db7
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStub.rc
@@ -0,0 +1,56 @@
+/* $Id: VBoxStub.rc $*/
+/** @file
+ * Resource file for the Windows install stub program.
+ */
+
+/*
+ * Copyright (C) 2009-2019 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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+#include "resource.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "VirtualBox\0"
+ VALUE "InternalName", "VirtualBox\0"
+ VALUE "OriginalFilename", "VirtualBox.exe\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#include "VBoxStub-icon.rc"
+#include "VBoxStub-manifest.rc"
+
diff --git a/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp
new file mode 100644
index 00000000..cfaefa93
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp
@@ -0,0 +1,142 @@
+/* $Id: VBoxStubCertUtil.cpp $ */
+/** @file
+ * VBoxStub - VirtualBox's Windows installer stub (certificate manipulations).
+ *
+ * NOTE: The content of this file is partly
+ * grabbed from src/VBox/Additions/WINNT/tools/VBoxCertUtil.cpp
+ */
+
+/*
+ * Copyright (C) 2012-2019 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <Wincrypt.h>
+
+#include <iprt/errcore.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/**
+ * Reads a certificate from a (const char []) buffer, returning a context
+ * or a the handle to a temporary memory store.
+ *
+ * @returns true on success, false on failure (error message written).
+ * @param kpCertBuf The pointer to the buffer containing the
+ * certificates.
+ * @param cbCertBuf Size of @param kpCertBuf in bytes.
+ * @param ppOutCtx Where to return the handle to the temporary
+ * memory store.
+ */
+static bool readCertBuf(const unsigned char kpCertBuf[], DWORD cbCertBuf, PCCERT_CONTEXT *ppOutCtx)
+{
+ *ppOutCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ (PBYTE)kpCertBuf, cbCertBuf);
+ if (*ppOutCtx)
+ return true;
+
+ return false;
+}
+
+/**
+ * Opens a certificate store.
+ *
+ * @returns true on success, false on failure (error message written).
+ * @param dwDst The destination, like
+ * CERT_SYSTEM_STORE_LOCAL_MACHINE or
+ * CERT_SYSTEM_STORE_CURRENT_USER.
+ * @param pszStoreNm The store name.
+ */
+static HCERTSTORE openCertStore(DWORD dwDst, const char *pszStoreNm)
+{
+ HCERTSTORE hStore = NULL;
+ PRTUTF16 pwszStoreNm;
+ int rc = RTStrToUtf16(pszStoreNm, &pwszStoreNm);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Make sure CERT_STORE_OPEN_EXISTING_FLAG is not set. This causes Windows XP
+ * to return ACCESS_DENIED when installing TrustedPublisher certificates via
+ * CertAddCertificateContextToStore() if the TrustedPublisher store never has
+ * been used (through certmgr.exe and friends) yet.
+ *
+ * According to MSDN, if neither CERT_STORE_OPEN_EXISTING_FLAG nor
+ * CERT_STORE_CREATE_NEW_FLAG is set, the store will be either opened or
+ * created accordingly.
+ */
+ dwDst &= ~CERT_STORE_OPEN_EXISTING_FLAG;
+
+ hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */,
+ dwDst,
+ pwszStoreNm);
+
+ RTUtf16Free(pwszStoreNm);
+ }
+ return hStore;
+}
+
+/**
+ * Adds a certificate to a store.
+ *
+ * @returns true on success, false on failure (error message written).
+ * @param dwDst The destination, like
+ * CERT_SYSTEM_STORE_LOCAL_MACHINE or
+ * CERT_SYSTEM_STORE_CURRENT_USER.
+ * @param pszStoreNm The store name.
+ * @param kpCertBuf Buffer that contains a certificate
+ * @param cbCertBuf Size of @param kpCertBuf in bytes
+ */
+bool addCertToStore(DWORD dwDst, const char *pszStoreNm, const unsigned char kpCertBuf[], DWORD cbCertBuf)
+{
+ /*
+ * Get certificate from buffer.
+ */
+ PCCERT_CONTEXT pSrcCtx = NULL;
+ bool fRc = false;
+
+ if (!readCertBuf(kpCertBuf, cbCertBuf, &pSrcCtx))
+ {
+ RTMsgError("Unable to get certificate context: %d", GetLastError());
+ return fRc;
+ }
+
+ /*
+ * Open the certificates store.
+ */
+ HCERTSTORE hDstStore = openCertStore(dwDst, pszStoreNm);
+ if (hDstStore)
+ {
+ /*
+ * Finally, add certificate to store
+ */
+ if (CertAddCertificateContextToStore(hDstStore, pSrcCtx, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
+ fRc = true;
+ else
+ RTMsgError("Unable to install certificate: %d", GetLastError());
+
+ CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ }
+ else
+ RTMsgError("Unable to open certificates store: %d", GetLastError());
+
+ /* Release resources */
+ CertFreeCertificateContext(pSrcCtx);
+
+ return fRc;
+}
diff --git a/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h
new file mode 100644
index 00000000..197c5865
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h
@@ -0,0 +1,27 @@
+/* $Id: VBoxStubCertUtil.h $ */
+/** @file
+ * VBoxStub - VirtualBox's Windows installer stub (certificate manipulations).
+ */
+
+/*
+ * Copyright (C) 2012-2019 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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h
+#define VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+extern bool addCertToStore(DWORD dwDst, const char *pszStoreNm, const unsigned char kpCertBuf[], DWORD cbCertBuf);
+
+#endif /* !VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h */
+
diff --git a/src/VBox/Installer/win/Stub/crypt32.def b/src/VBox/Installer/win/Stub/crypt32.def
new file mode 100644
index 00000000..c65806f3
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/crypt32.def
@@ -0,0 +1,25 @@
+; $Id: crypt32.def $
+;; @file
+; Definitions for CRYPT32.DLL lazy import library.
+;
+
+;
+; Copyright (C) 2009-2019 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.
+;
+
+LIBRARY CRYPT32.DLL
+EXPORTS
+ _CertCreateCertificateContext@12
+ _CertOpenStore@20
+ _CertFreeCertificateContext@4
+ _CertAddCertificateContextToStore@16
+ _CertCloseStore@8
+
diff --git a/src/VBox/Installer/win/Stub/msi.def b/src/VBox/Installer/win/Stub/msi.def
new file mode 100644
index 00000000..64b661a5
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/msi.def
@@ -0,0 +1,23 @@
+; $Id: msi.def $
+;; @file
+; Definitions for MSI.DLL lazy import library.
+;
+
+;
+; Copyright (C) 2009-2019 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.
+;
+
+LIBRARY MSI.DLL
+EXPORTS
+ _MsiInstallProductW@8
+ _MsiEnableLogW@12
+ _MsiSetInternalUI@8
+
diff --git a/src/VBox/Installer/win/Stub/resource.h b/src/VBox/Installer/win/Stub/resource.h
new file mode 100644
index 00000000..0a201ff9
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/resource.h
@@ -0,0 +1,32 @@
+/* $Id: resource.h $ */
+/** @file
+ * VBoxStub - resource header file.
+ */
+
+/*
+ * Copyright (C) 2009-2019 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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Stub_resource_h
+#define VBOX_INCLUDED_SRC_Stub_resource_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define IDI_VIRTUALBOX 101
+
+#ifndef RT_MANIFEST
+# define RT_MANIFEST 24
+#endif
+#define APP_MANIFEST 1
+
+#endif /* !VBOX_INCLUDED_SRC_Stub_resource_h */
+
diff --git a/src/VBox/Installer/win/Stub/shell32.def b/src/VBox/Installer/win/Stub/shell32.def
new file mode 100644
index 00000000..b888a760
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/shell32.def
@@ -0,0 +1,22 @@
+; $Id: shell32.def $
+;; @file
+; Definitions for SHELL32.DLL lazy import library.
+;
+
+;
+; Copyright (C) 2009-2019 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.
+;
+
+LIBRARY SHELL32.DLL
+EXPORTS
+ _CommandLineToArgvW@8
+ _SHFileOperationW@4
+
diff --git a/src/VBox/Installer/win/Stub/user32.def b/src/VBox/Installer/win/Stub/user32.def
new file mode 100644
index 00000000..6850543e
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/user32.def
@@ -0,0 +1,23 @@
+; $Id: user32.def $
+;; @file
+; Definitions for USER32.DLL lazy import library.
+;
+
+;
+; Copyright (C) 2009-2019 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.
+;
+
+LIBRARY USER32.DLL
+EXPORTS
+ _MessageBoxW@16
+ _MessageBoxA@16
+ _GetDesktopWindow@0
+
diff --git a/src/VBox/Installer/win/Stub/ws2_32.def b/src/VBox/Installer/win/Stub/ws2_32.def
new file mode 100644
index 00000000..09b434fc
--- /dev/null
+++ b/src/VBox/Installer/win/Stub/ws2_32.def
@@ -0,0 +1,22 @@
+; $Id: ws2_32.def $
+;; @file
+; Definitions for WS2_32.DLL lazy import library.
+;
+
+;
+; Copyright (C) 2009-2019 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.
+;
+
+LIBRARY WS2_32.DLL
+EXPORTS
+ _WSASetLastError@4
+ _WSAGetLastError@0
+