VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Installer/Loader/VBoxWindowsAdditions.cpp@ 106239

Last change on this file since 106239 was 106239, checked in by vboxsync, 7 weeks ago

Additions/NT/Installer: Check that what installer selector is about to launch looks like an installer and is correctly signed with the build certificate. This needs more testing. bugref:10771

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1/* $Id: VBoxWindowsAdditions.cpp 106239 2024-10-08 14:47:35Z vboxsync $ */
2/** @file
3 * VBoxWindowsAdditions - The Windows Guest Additions Loader.
4 *
5 * This is STUB which select whether to install 32-bit or 64-bit additions.
6 */
7
8/*
9 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define UNICODE /* For resource related macros. */
35#include <iprt/cdefs.h>
36#include <iprt/win/windows.h>
37#include <Wintrust.h>
38#include <Softpub.h>
39#ifndef ERROR_ELEVATION_REQUIRED /* Windows Vista and later. */
40# define ERROR_ELEVATION_REQUIRED 740
41#endif
42
43#include <iprt/string.h>
44#include <iprt/utf16.h>
45
46#include "NoCrtOutput.h"
47
48#ifdef VBOX_SIGNING_MODE
49# include "BuildCert.h"
50#endif
51
52
53#ifdef VBOX_SIGNING_MODE
54
55/**
56 * Checks the file signatures of both this stub program and the actual installer
57 * binary, making sure they use the same certificate as at build time and that
58 * the signature verifies correctly.
59 *
60 * @returns 0 on success, non-zero exit code on failure.
61 */
62static int CheckFileSignatures(wchar_t const *pwszExePath, HANDLE hFileExe, wchar_t const *pwszSelfPath, HANDLE hFileSelf)
63{
64 /*
65 * Check the OS version (bypassing shims).
66 *
67 * The RtlGetVersion API was added in windows 2000, so it's precense is a
68 * provides a minimum OS version indicator already.
69 */
70 LONG (__stdcall *pfnRtlGetVersion)(OSVERSIONINFOEXW *);
71 *(FARPROC *)&pfnRtlGetVersion = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
72 if (!pfnRtlGetVersion)
73 {
74 /* double check it. */
75 DWORD const dwVersion = GetVersion();
76 if ((dwVersion & 0xff) < 5)
77 return 0;
78 return ErrorMsgRcSUS(40, "RtlGetVersion not present while Windows version is 5.0 or higher (", dwVersion, ")");
79 }
80 OSVERSIONINFOEXW WinOsInfoEx = { sizeof(WinOsInfoEx) };
81 NTSTATUS const rcNt = pfnRtlGetVersion(&WinOsInfoEx);
82 if (!RT_SUCCESS(rcNt))
83 return ErrorMsgRcSU(41, "RtlGetVersion failed: ", rcNt);
84
85 /* Skip both of these checks if pre-XP. */
86 if ( (WinOsInfoEx.dwMajorVersion == 5 && WinOsInfoEx.dwMinorVersion < 1)
87 || WinOsInfoEx.dwMajorVersion < 4)
88 return 0;
89
90 /*
91 * We need to find the system32 directory to load the WinVerifyTrust API.
92 */
93 static wchar_t const s_wszSlashWinTrustDll[] = L"\\Wintrust.dll";
94
95 /* Call GetSystemWindowsDirectoryW/GetSystemDirectoryW. */
96 wchar_t wszSysDll[MAX_PATH + sizeof(s_wszSlashWinTrustDll)] = { 0 };
97 UINT const cwcSystem32 = GetSystemDirectoryW(wszSysDll, MAX_PATH);
98 if (!cwcSystem32 || cwcSystem32 >= MAX_PATH)
99 return ErrorMsgRc(42, "GetSystemDirectoryW failed");
100
101 /* Load it: */
102 memcpy(&wszSysDll[cwcSystem32], s_wszSlashWinTrustDll, sizeof(s_wszSlashWinTrustDll));
103 DWORD fLoadFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
104 HMODULE hModWinTrustDll = LoadLibraryExW(wszSysDll, NULL, fLoadFlags);
105 if (!hModWinTrustDll && GetLastError() == ERROR_INVALID_PARAMETER)
106 {
107 fLoadFlags = 0;
108 hModWinTrustDll = LoadLibraryExW(wszSysDll, NULL, fLoadFlags);
109 }
110 if (!hModWinTrustDll)
111 return ErrorMsgRcSWSU(43, "Failed to load '", wszSysDll, "': ", GetLastError());
112
113 /* Resolve API: */
114 decltype(WinVerifyTrust) * const pfnWinVerifyTrust
115 = (decltype(WinVerifyTrust) *)GetProcAddress(hModWinTrustDll, "WinVerifyTrust");
116 if (!pfnWinVerifyTrust)
117 return ErrorMsgRc(44, "WinVerifyTrust not found");
118
119 /*
120 * We also need the Crypt32.dll for CryptQueryObject and CryptMsgGetParam.
121 */
122 /* Load it: */
123 static wchar_t const s_wszSlashCrypt32Dll[] = L"\\Crypt32.dll";
124 AssertCompile(sizeof(s_wszSlashCrypt32Dll) <= sizeof(s_wszSlashWinTrustDll));
125 memcpy(&wszSysDll[cwcSystem32], s_wszSlashCrypt32Dll, sizeof(s_wszSlashCrypt32Dll));
126 HMODULE const hModCrypt32Dll = LoadLibraryExW(wszSysDll, NULL, fLoadFlags);
127 if (!hModCrypt32Dll)
128 return ErrorMsgRcSWSU(45, "Failed to load '", wszSysDll, "': ", GetLastError());
129
130 /* Resolve APIs: */
131 decltype(CryptQueryObject) * const pfnCryptQueryObject
132 = (decltype(CryptQueryObject) *)GetProcAddress(hModCrypt32Dll, "CryptQueryObject");
133 if (!pfnCryptQueryObject)
134 return ErrorMsgRc(46, "CryptQueryObject not found");
135
136 decltype(CryptMsgClose) * const pfnCryptMsgClose
137 = (decltype(CryptMsgClose) *)GetProcAddress(hModCrypt32Dll, "CryptMsgClose");
138 if (!pfnCryptQueryObject)
139 return ErrorMsgRc(47, "CryptMsgClose not found");
140
141 decltype(CryptMsgGetParam) * const pfnCryptMsgGetParam
142 = (decltype(CryptMsgGetParam) *)GetProcAddress(hModCrypt32Dll, "CryptMsgGetParam");
143 if (!pfnCryptQueryObject)
144 return ErrorMsgRc(48, "CryptMsgGetParam not found");
145
146 /*
147 * We'll verify the primary signer certificate first as that's something that
148 * should work even if SHA-256 isn't supported by the Windows crypto code.
149 */
150 struct
151 {
152 HANDLE hFile;
153 wchar_t const *pwszFile;
154
155 DWORD fEncoding;
156 DWORD dwContentType;
157 DWORD dwFormatType;
158 HCERTSTORE hCertStore;
159 HCRYPTMSG hMsg;
160
161 DWORD cbCert;
162 uint8_t *pbCert;
163 } aExes[] =
164 {
165 { hFileSelf, pwszSelfPath, 0, 0, 0, NULL, NULL, 0, NULL },
166 { hFileExe, pwszExePath, 0, 0, 0, NULL, NULL, 0, NULL },
167 };
168
169 HANDLE const hHeap = GetProcessHeap();
170 int rcExit = 0;
171 for (unsigned i = 0; i < RT_ELEMENTS(aExes) && rcExit == 0; i++)
172 {
173 if (!pfnCryptQueryObject(CERT_QUERY_OBJECT_FILE,
174 aExes[i].pwszFile,
175 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
176 CERT_QUERY_FORMAT_FLAG_BINARY,
177 0 /*fFlags*/,
178 &aExes[i].fEncoding,
179 &aExes[i].dwContentType,
180 &aExes[i].dwFormatType,
181 &aExes[i].hCertStore,
182 &aExes[i].hMsg,
183 NULL /*ppvContext*/))
184 rcExit = ErrorMsgRcSWSU(50 + i*4, "CryptQueryObject/FILE on '", aExes[i].pwszFile, "': ", GetLastError());
185 else if (!pfnCryptMsgGetParam(aExes[i].hMsg, CMSG_CERT_PARAM, 0, NULL, &aExes[i].cbCert))
186 rcExit = ErrorMsgRcSWSU(51 + i*4, "CryptMsgGetParam/CMSG_CERT_PARAM/size failed on '",
187 aExes[i].pwszFile, "': ", GetLastError());
188 else
189 {
190 DWORD const cbCert = aExes[i].cbCert;
191 aExes[i].pbCert = (uint8_t *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbCert);
192 if (!aExes[i].pbCert)
193 rcExit = ErrorMsgRcSUS(52 + i*4, "Out of memory (", cbCert, " bytes) for signing certificate information");
194 else if (!pfnCryptMsgGetParam(aExes[i].hMsg, CMSG_CERT_PARAM, 0, aExes[i].pbCert, &aExes[i].cbCert))
195 rcExit = ErrorMsgRcSWSU(53 + i*4, "CryptMsgGetParam/CMSG_CERT_PARAM failed on '", aExes[i].pwszFile, "': ",
196 GetLastError());
197 }
198 }
199
200 if (rcExit == 0)
201 {
202 /* Do the two match? */
203 if ( aExes[0].cbCert != aExes[1].cbCert
204 || memcmp(aExes[0].pbCert, aExes[1].pbCert, aExes[0].cbCert) != 0)
205 rcExit = ErrorMsgRcSWS(58, "The certificate on '", pwszExePath, "' does not match.");
206 /* The two match, now do they match the one we're expecting to use? */
207 else if ( aExes[0].cbCert != g_cbBuildCert
208 || memcmp(aExes[0].pbCert, g_abBuildCert, g_cbBuildCert) != 0)
209 rcExit = ErrorMsgRcSWS(59, "The signing certificate of '", pwszExePath, "' differs from the build certificate");
210 /* else: it all looks fine */
211 }
212
213 /* cleanup */
214 for (unsigned i = 0; i < RT_ELEMENTS(aExes); i++)
215 {
216 if (aExes[i].pbCert)
217 {
218 HeapFree(hHeap, 0, aExes[i].pbCert);
219 aExes[i].pbCert = NULL;
220 }
221 if (aExes[i].hMsg)
222 {
223 pfnCryptMsgClose(aExes[i].hMsg);
224 aExes[i].hMsg = NULL;
225 }
226 }
227 if (rcExit != 0)
228 return rcExit;
229
230 /*
231 * ASSUMING we're using SHA-256 for signing, we do a windows OS cutoff at Windows 7.
232 * For Windows Vista and older we skip this step.
233 */
234 if ( (WinOsInfoEx.dwMajorVersion == 6 && WinOsInfoEx.dwMinorVersion == 0)
235 || WinOsInfoEx.dwMajorVersion < 6)
236 return 0;
237
238 /*
239 * Construct input WinVerifyTrust parameters and call it on each of the executables in turn.
240 * This code was borrowed from SUPHardNt.
241 */
242 for (unsigned i = 0; i < RT_ELEMENTS(aExes); i++)
243 {
244 WINTRUST_FILE_INFO FileInfo = { 0 };
245 FileInfo.cbStruct = sizeof(FileInfo);
246 FileInfo.pcwszFilePath = aExes[i].pwszFile;
247 FileInfo.hFile = aExes[i].hFile;
248
249 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
250
251 WINTRUST_DATA TrustData = { 0 };
252 TrustData.cbStruct = sizeof(TrustData);
253 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
254 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
255 TrustData.dwUIChoice = WTD_UI_NONE;
256 TrustData.dwProvFlags = 0;
257 if (WinOsInfoEx.dwMajorVersion >= 6)
258 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
259 else
260 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
261 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
262 TrustData.pFile = &FileInfo;
263
264 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
265 if (hrc != S_OK)
266 {
267 /* Translate the eror constant */
268 const char *pszErrConst = NULL;
269 switch (hrc)
270 {
271 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
272 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
273 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
274 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
275 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
276 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
277 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
278 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
279 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
280 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
281 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
282 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
283 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
284 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
285 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
286 case CERT_E_EXPIRED: pszErrConst = "CERT_E_EXPIRED"; break;
287 case CERT_E_VALIDITYPERIODNESTING: pszErrConst = "CERT_E_VALIDITYPERIODNESTING"; break;
288 case CERT_E_ROLE: pszErrConst = "CERT_E_ROLE"; break;
289 case CERT_E_PATHLENCONST: pszErrConst = "CERT_E_PATHLENCONST"; break;
290 case CERT_E_CRITICAL: pszErrConst = "CERT_E_CRITICAL"; break;
291 case CERT_E_PURPOSE: pszErrConst = "CERT_E_PURPOSE"; break;
292 case CERT_E_ISSUERCHAINING: pszErrConst = "CERT_E_ISSUERCHAINING"; break;
293 case CERT_E_MALFORMED: pszErrConst = "CERT_E_MALFORMED"; break;
294 case CERT_E_UNTRUSTEDROOT: pszErrConst = "CERT_E_UNTRUSTEDROOT"; break;
295 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
296 case CERT_E_REVOKED: pszErrConst = "CERT_E_REVOKED"; break;
297 case CERT_E_UNTRUSTEDTESTROOT: pszErrConst = "CERT_E_UNTRUSTEDTESTROOT"; break;
298 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
299 case CERT_E_CN_NO_MATCH: pszErrConst = "CERT_E_CN_NO_MATCH"; break;
300 case CERT_E_WRONG_USAGE: pszErrConst = "CERT_E_WRONG_USAGE"; break;
301 case CERT_E_UNTRUSTEDCA: pszErrConst = "CERT_E_UNTRUSTEDCA"; break;
302 case CERT_E_INVALID_POLICY: pszErrConst = "CERT_E_INVALID_POLICY"; break;
303 case CERT_E_INVALID_NAME: pszErrConst = "CERT_E_INVALID_NAME"; break;
304 case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
305 case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
306 }
307 if (pszErrConst)
308 rcExit = ErrorMsgRcSWSS(60 + i, "WinVerifyTrust failed on '", pwszExePath, "': ", pszErrConst);
309 else
310 rcExit = ErrorMsgRcSWSX(60 + i, "WinVerifyTrust failed on '", pwszExePath, "': ", hrc);
311 }
312
313 /* clean up state data. */
314 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
315 FileInfo.hFile = NULL;
316 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
317 }
318
319 return rcExit;
320}
321
322#endif /* VBOX_SIGNING_MODE */
323
324/**
325 * strstr for haystacks w/o null termination.
326 */
327static const char *MyStrStrN(const char *pchHaystack, size_t cbHaystack, const char *pszNeedle)
328{
329 size_t const cchNeedle = strlen(pszNeedle);
330 char const chFirst = *pszNeedle;
331 if (cbHaystack >= cchNeedle)
332 {
333 cbHaystack -= cchNeedle - 1;
334 while (cbHaystack > 0)
335 {
336 const char *pchHit = (const char *)memchr(pchHaystack, chFirst, cbHaystack);
337 if (pchHit)
338 {
339 if (memcmp(pchHit, pszNeedle, cchNeedle) == 0)
340 return pchHit;
341 pchHit++;
342 cbHaystack -= pchHit - pchHaystack;
343 pchHaystack = pchHit;
344 }
345 else
346 break;
347 }
348 }
349 return NULL;
350}
351
352/**
353 * Check that the executable file is "related" the one for the current process.
354 */
355static int CheckThatFileIsRelated(wchar_t const *pwszExePath, wchar_t const *pwszSelfPath)
356{
357 /*
358 * Start by checking version info.
359 */
360 /*
361 * Query the version info for the files:
362 */
363 DWORD const cbExeVerInfo = GetFileVersionInfoSizeW(pwszExePath, NULL);
364 if (!cbExeVerInfo)
365 return ErrorMsgRcSWSU(20, "GetFileVersionInfoSizeW failed on '", pwszExePath, "': ", GetLastError());
366
367 DWORD const cbSelfVerInfo = GetFileVersionInfoSizeW(pwszSelfPath, NULL);
368 if (!cbSelfVerInfo)
369 return ErrorMsgRcSWSU(21, "GetFileVersionInfoSizeW failed on '", pwszSelfPath, "': ", GetLastError());
370
371 HANDLE const hHeap = GetProcessHeap();
372 DWORD const cbBothVerInfo = RT_ALIGN_32(cbExeVerInfo, 64) + RT_ALIGN_32(cbSelfVerInfo, 64);
373 void * const pvExeVerInfo = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbBothVerInfo);
374 void * const pvSelfVerInfo = (uint8_t *)pvExeVerInfo + RT_ALIGN_32(cbExeVerInfo, 64);
375 if (!pvExeVerInfo)
376 return ErrorMsgRcSUS(22, "Out of memory (", cbBothVerInfo, " bytes) for version info");
377
378 int rcExit = 0;
379 if (!GetFileVersionInfoW(pwszExePath, 0, cbExeVerInfo, pvExeVerInfo))
380 rcExit = ErrorMsgRcSWSU(23, "GetFileVersionInfoW failed on '", pwszExePath, "': ", GetLastError());
381 else if (!GetFileVersionInfoW(pwszSelfPath, 0, cbSelfVerInfo, pvSelfVerInfo))
382 rcExit = ErrorMsgRcSWSU(24, "GetFileVersionInfoW failed on '", pwszSelfPath, "': ", GetLastError());
383
384 /*
385 * Compare the product and company strings, which should be identical.
386 */
387 static struct
388 {
389 wchar_t const *pwszQueryItem;
390 const char *pszQueryErrorMsg1;
391 const char *pszCompareErrorMsg1;
392 } const s_aIdenticalItems[] =
393 {
394 { L"\\StringFileInfo\\040904b0\\ProductName", "VerQueryValueW/ProductName failed on '", "Product string of '" },
395 { L"\\StringFileInfo\\040904b0\\CompanyName", "VerQueryValueW/CompanyName failed on '", "Company string of '" },
396 };
397
398 for (unsigned i = 0; i < RT_ELEMENTS(s_aIdenticalItems) && rcExit == 0; i++)
399 {
400 void *pvExeInfoItem = NULL;
401 UINT cbExeInfoItem = 0;
402 void *pvSelfInfoItem = NULL;
403 UINT cbSelfInfoItem = 0;
404 if (!VerQueryValueW(pvExeVerInfo, s_aIdenticalItems[i].pwszQueryItem, &pvExeInfoItem, &cbExeInfoItem))
405 rcExit = ErrorMsgRcSWSU(25 + i*3, s_aIdenticalItems[i].pszQueryErrorMsg1 , pwszExePath, "': ", GetLastError());
406 else if (!VerQueryValueW(pvSelfVerInfo, s_aIdenticalItems[i].pwszQueryItem, &pvSelfInfoItem, &cbSelfInfoItem))
407 rcExit = ErrorMsgRcSWSU(26 + i*3, s_aIdenticalItems[i].pszQueryErrorMsg1, pwszSelfPath, "': ", GetLastError());
408 else if ( cbExeInfoItem != cbSelfInfoItem
409 || memcmp(pvExeInfoItem, pvSelfInfoItem, cbSelfInfoItem) != 0)
410 rcExit = ErrorMsgRcSWS(27 + i*3, s_aIdenticalItems[i].pszCompareErrorMsg1, pwszExePath, "' does not match");
411 }
412
413 HeapFree(hHeap, 0, pvExeVerInfo);
414
415 /*
416 * Check that the file has a manifest that looks like it may belong to
417 * an NSIS installer.
418 */
419 if (rcExit == 0)
420 {
421 HMODULE hMod = LoadLibraryExW(pwszExePath, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
422 if (!hMod && GetLastError() == ERROR_INVALID_PARAMETER)
423 hMod = LoadLibraryExW(pwszExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
424 if (hMod)
425 {
426 HRSRC const hRsrcMt = FindResourceExW(hMod, RT_MANIFEST, MAKEINTRESOURCEW(1),
427 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
428 if (hRsrcMt)
429 {
430 DWORD const cbManifest = SizeofResource(hMod, hRsrcMt);
431 HGLOBAL const hGlobalMt = LoadResource(hMod, hRsrcMt);
432 if (hGlobalMt)
433 {
434 const char * const pchManifest = (const char *)LockResource(hGlobalMt);
435 if (pchManifest)
436 {
437 /* Just look for a few strings we expect to always find the manifest. */
438 if (!MyStrStrN(pchManifest, cbManifest, "Nullsoft.NSIS.exehead"))
439 rcExit = 36;
440 else if (!MyStrStrN(pchManifest, cbManifest, "requestedPrivileges"))
441 rcExit = 37;
442 else if (!MyStrStrN(pchManifest, cbManifest, "highestAvailable"))
443 rcExit = 38;
444 if (rcExit)
445 rcExit = ErrorMsgRcSWSU(rcExit, "Manifest check of '", pwszExePath, "' failed: ", rcExit);
446 }
447 else
448 rcExit = ErrorMsgRc(35, "LockResource/Manifest failed");
449 }
450 else
451 rcExit = ErrorMsgRcSU(34, "LoadResource/Manifest failed: ", GetLastError());
452 }
453 else
454 rcExit = ErrorMsgRcSU(33, "FindResourceExW/Manifest failed: ", GetLastError());
455 }
456 else
457 rcExit = ErrorMsgRcSWSU(32, "LoadLibraryExW of '", pwszExePath, "' as datafile failed: ", GetLastError());
458 }
459
460 return rcExit;
461}
462
463static BOOL IsWow64(void)
464{
465 BOOL fIsWow64 = FALSE;
466 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
467 LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process");
468 if (fnIsWow64Process != NULL)
469 {
470 if (!fnIsWow64Process(GetCurrentProcess(), &fIsWow64))
471 {
472 ErrorMsgLastErr("Unable to determine the process type!");
473
474 /* Error in retrieving process type - assume that we're running on 32bit. */
475 fIsWow64 = FALSE;
476 }
477 }
478 return fIsWow64;
479}
480
481static int WaitForProcess2(HANDLE hProcess)
482{
483 /*
484 * Wait for the process, make sure the deal with messages.
485 */
486 for (;;)
487 {
488 DWORD dwRc = MsgWaitForMultipleObjects(1, &hProcess, FALSE, 5000/*ms*/, QS_ALLEVENTS);
489
490 MSG Msg;
491 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
492 {
493 TranslateMessage(&Msg);
494 DispatchMessageW(&Msg);
495 }
496
497 if (dwRc == WAIT_OBJECT_0)
498 break;
499 if ( dwRc != WAIT_TIMEOUT
500 && dwRc != WAIT_OBJECT_0 + 1)
501 {
502 ErrorMsgLastErrSUR("MsgWaitForMultipleObjects failed: ", dwRc);
503 break;
504 }
505 }
506
507 /*
508 * Collect the process info.
509 */
510 DWORD dwExitCode;
511 if (GetExitCodeProcess(hProcess, &dwExitCode))
512 return (int)dwExitCode;
513 return ErrorMsgRcLastErr(16, "GetExitCodeProcess failed");
514}
515
516static int WaitForProcess(HANDLE hProcess)
517{
518 DWORD WaitRc = WaitForSingleObjectEx(hProcess, INFINITE, TRUE);
519 while ( WaitRc == WAIT_IO_COMPLETION
520 || WaitRc == WAIT_TIMEOUT)
521 WaitRc = WaitForSingleObjectEx(hProcess, INFINITE, TRUE);
522 if (WaitRc == WAIT_OBJECT_0)
523 {
524 DWORD dwExitCode;
525 if (GetExitCodeProcess(hProcess, &dwExitCode))
526 return (int)dwExitCode;
527 return ErrorMsgRcLastErr(16, "GetExitCodeProcess failed");
528 }
529 return ErrorMsgRcLastErrSUR(16, "MsgWaitForMultipleObjects failed: ", WaitRc);
530}
531
532#ifndef IPRT_NO_CRT
533int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
534#else
535int main()
536#endif
537{
538#ifndef IPRT_NO_CRT
539 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
540#endif
541
542 /*
543 * Gather the parameters of the real installer program.
544 */
545 SetLastError(NO_ERROR);
546 WCHAR wszCurDir[MAX_PATH] = { 0 };
547 DWORD cwcCurDir = GetCurrentDirectoryW(sizeof(wszCurDir), wszCurDir);
548 if (cwcCurDir == 0 || cwcCurDir >= sizeof(wszCurDir))
549 return ErrorMsgRcLastErrSUR(12, "GetCurrentDirectoryW failed: ", cwcCurDir);
550
551 SetLastError(NO_ERROR);
552 WCHAR wszExePath[MAX_PATH] = { 0 };
553 DWORD cwcExePath = GetModuleFileNameW(NULL, wszExePath, sizeof(wszExePath));
554 if (cwcExePath == 0 || cwcExePath >= sizeof(wszExePath))
555 return ErrorMsgRcLastErrSUR(13, "GetModuleFileNameW failed: ", cwcExePath);
556
557 WCHAR wszSelfPath[MAX_PATH];
558 memcpy(wszSelfPath, wszExePath, sizeof(wszSelfPath));
559
560 /*
561 * Strip the extension off the module name and construct the arch specific
562 * one of the real installer program.
563 */
564 DWORD off = cwcExePath - 1;
565 while ( off > 0
566 && ( wszExePath[off] != '/'
567 && wszExePath[off] != '\\'
568 && wszExePath[off] != ':'))
569 {
570 if (wszExePath[off] == '.')
571 {
572 wszExePath[off] = '\0';
573 cwcExePath = off;
574 break;
575 }
576 off--;
577 }
578
579 WCHAR const *pwszSuff = IsWow64() ? L"-amd64.exe" : L"-x86.exe";
580 int rc = RTUtf16Copy(&wszExePath[cwcExePath], RT_ELEMENTS(wszExePath) - cwcExePath, pwszSuff);
581 if (RT_FAILURE(rc))
582 return ErrorMsgRc(14, "Real installer name is too long!");
583 cwcExePath += RTUtf16Len(&wszExePath[cwcExePath]);
584
585 /*
586 * Replace the first argument of the argument list.
587 */
588 PWCHAR pwszNewCmdLine = NULL;
589 LPCWSTR pwszOrgCmdLine = GetCommandLineW();
590 if (pwszOrgCmdLine) /* Dunno if this can be NULL, but whatever. */
591 {
592 /* Skip the first argument in the original. */
593 /** @todo Is there some ISBLANK or ISSPACE macro/function in Win32 that we could
594 * use here, if it's correct wrt. command line conventions? */
595 WCHAR wch;
596 while ((wch = *pwszOrgCmdLine) == L' ' || wch == L'\t')
597 pwszOrgCmdLine++;
598 if (wch == L'"')
599 {
600 pwszOrgCmdLine++;
601 while ((wch = *pwszOrgCmdLine) != L'\0')
602 {
603 pwszOrgCmdLine++;
604 if (wch == L'"')
605 break;
606 }
607 }
608 else
609 {
610 while ((wch = *pwszOrgCmdLine) != L'\0')
611 {
612 pwszOrgCmdLine++;
613 if (wch == L' ' || wch == L'\t')
614 break;
615 }
616 }
617 while ((wch = *pwszOrgCmdLine) == L' ' || wch == L'\t')
618 pwszOrgCmdLine++;
619
620 /* Join up "wszExePath" with the remainder of the original command line. */
621 size_t cwcOrgCmdLine = RTUtf16Len(pwszOrgCmdLine);
622 size_t cwcNewCmdLine = 1 + cwcExePath + 1 + 1 + cwcOrgCmdLine + 1;
623 PWCHAR pwsz = pwszNewCmdLine = (PWCHAR)LocalAlloc(LPTR, cwcNewCmdLine * sizeof(WCHAR));
624 if (!pwsz)
625 return ErrorMsgRcSUS(15, "Out of memory (", cwcNewCmdLine * sizeof(WCHAR), " bytes)");
626 *pwsz++ = L'"';
627 memcpy(pwsz, wszExePath, cwcExePath * sizeof(pwsz[0]));
628 pwsz += cwcExePath;
629 *pwsz++ = L'"';
630 if (cwcOrgCmdLine)
631 {
632 *pwsz++ = L' ';
633 memcpy(pwsz, pwszOrgCmdLine, cwcOrgCmdLine * sizeof(pwsz[0]));
634 }
635 else
636 {
637 *pwsz = L'\0';
638 pwszOrgCmdLine = NULL;
639 }
640 }
641
642 /*
643 * Open the executable for this process.
644 */
645 HANDLE hFileSelf = CreateFileW(wszSelfPath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecAttr*/, OPEN_EXISTING,
646 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
647 if (hFileSelf == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
648 hFileSelf = CreateFileW(wszSelfPath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecAttr*/, OPEN_EXISTING,
649 FILE_ATTRIBUTE_NORMAL, NULL);
650 if (hFileSelf == INVALID_HANDLE_VALUE)
651 {
652 if (GetLastError() == ERROR_FILE_NOT_FOUND)
653 return ErrorMsgRcSW(17, "File not found: ", wszSelfPath);
654 return ErrorMsgRcSWSU(17, "Error opening '", wszSelfPath, "' for reading: ", GetLastError());
655 }
656
657
658 /*
659 * Open the file we're about to execute.
660 */
661 HANDLE hFileExe = CreateFileW(wszExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecAttr*/, OPEN_EXISTING,
662 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
663 if (hFileExe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
664 hFileExe = CreateFileW(wszExePath, GENERIC_READ, FILE_SHARE_READ, NULL /*pSecAttr*/, OPEN_EXISTING,
665 FILE_ATTRIBUTE_NORMAL, NULL);
666 if (hFileExe == INVALID_HANDLE_VALUE)
667 {
668 if (GetLastError() == ERROR_FILE_NOT_FOUND)
669 return ErrorMsgRcSW(18, "File not found: ", wszExePath);
670 return ErrorMsgRcSWSU(18, "Error opening '", wszExePath, "' for reading: ", GetLastError());
671 }
672
673 /*
674 * Check that the file we're about to launch is related to us and safe to start.
675 */
676 int rcExit = CheckThatFileIsRelated(wszExePath, wszSelfPath);
677 if (rcExit != 0)
678 return rcExit;
679
680#ifdef VBOX_SIGNING_MODE
681 rcExit = CheckFileSignatures(wszExePath, hFileExe, wszSelfPath, hFileSelf);
682 if (rcExit != 0)
683 return rcExit;
684#endif
685
686 /*
687 * Start the process.
688 */
689 STARTUPINFOW StartupInfo = { sizeof(StartupInfo), 0 };
690 PROCESS_INFORMATION ProcInfo = { 0 };
691 SetLastError(740);
692 BOOL fOk = CreateProcessW(wszExePath,
693 pwszNewCmdLine,
694 NULL /*pProcessAttributes*/,
695 NULL /*pThreadAttributes*/,
696 TRUE /*fInheritHandles*/,
697 0 /*dwCreationFlags*/,
698 NULL /*pEnvironment*/,
699 NULL /*pCurrentDirectory*/,
700 &StartupInfo,
701 &ProcInfo);
702 if (fOk)
703 {
704 /* Wait for the process to finish. */
705 CloseHandle(ProcInfo.hThread);
706 rcExit = WaitForProcess(ProcInfo.hProcess);
707 CloseHandle(ProcInfo.hProcess);
708 }
709 else if (GetLastError() == ERROR_ELEVATION_REQUIRED)
710 {
711 /*
712 * Elevation is required. That can be accomplished via ShellExecuteEx
713 * and the runas atom.
714 */
715 MSG Msg;
716 PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE);
717 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
718
719 SHELLEXECUTEINFOW ShExecInfo = { 0 };
720 ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
721 ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
722 ShExecInfo.hwnd = NULL;
723 ShExecInfo.lpVerb = L"runas" ;
724 ShExecInfo.lpFile = wszExePath;
725 ShExecInfo.lpParameters = pwszOrgCmdLine; /* pass only args here!!! */
726 ShExecInfo.lpDirectory = wszCurDir;
727 ShExecInfo.nShow = SW_NORMAL;
728 ShExecInfo.hProcess = INVALID_HANDLE_VALUE;
729 if (ShellExecuteExW(&ShExecInfo))
730 {
731 if (ShExecInfo.hProcess != INVALID_HANDLE_VALUE)
732 {
733 rcExit = WaitForProcess2(ShExecInfo.hProcess);
734 CloseHandle(ShExecInfo.hProcess);
735 }
736 else
737 rcExit = ErrorMsgRc(1, "ShellExecuteExW did not return a valid process handle!");
738 }
739 else
740 rcExit = ErrorMsgRcLastErrSWSR(9, "Failed to execute '", wszExePath, "' via ShellExecuteExW!");
741 }
742 else
743 rcExit = ErrorMsgRcLastErrSWSR(8, "Failed to execute '", wszExePath, "' via CreateProcessW!");
744
745 if (pwszNewCmdLine)
746 LocalFree(pwszNewCmdLine);
747
748 return rcExit;
749}
750
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette