1 // Main.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/MyWindows.h"
6
7 #include "../../../Common/MyInitGuid.h"
8
9 #include "../../../Common/CommandLineParser.h"
10 #include "../../../Common/StringConvert.h"
11 #include "../../../Common/TextConfig.h"
12
13 #include "../../../Windows/DLL.h"
14 #include "../../../Windows/ErrorMsg.h"
15 #include "../../../Windows/FileDir.h"
16 #include "../../../Windows/FileFind.h"
17 #include "../../../Windows/FileIO.h"
18 #include "../../../Windows/FileName.h"
19 #include "../../../Windows/NtCheck.h"
20 #include "../../../Windows/ResourceString.h"
21
22 #include "../../UI/Explorer/MyMessages.h"
23
24 #include "ExtractEngine.h"
25
26 #include "../../../../C/DllSecur.h"
27
28 #include "resource.h"
29
30 using namespace NWindows;
31 using namespace NFile;
32 using namespace NDir;
33
34 HINSTANCE g_hInstance;
35
36 static CFSTR const kTempDirPrefix = FTEXT("7zS");
37
38 #define _SHELL_EXECUTE
39
ReadDataString(CFSTR fileName,LPCSTR startID,LPCSTR endID,AString & stringResult)40 static bool ReadDataString(CFSTR fileName, LPCSTR startID,
41 LPCSTR endID, AString &stringResult)
42 {
43 stringResult.Empty();
44 NIO::CInFile inFile;
45 if (!inFile.Open(fileName))
46 return false;
47 const int kBufferSize = (1 << 12);
48
49 Byte buffer[kBufferSize];
50 int signatureStartSize = MyStringLen(startID);
51 int signatureEndSize = MyStringLen(endID);
52
53 UInt32 numBytesPrev = 0;
54 bool writeMode = false;
55 UInt64 posTotal = 0;
56 for (;;)
57 {
58 if (posTotal > (1 << 20))
59 return (stringResult.IsEmpty());
60 UInt32 numReadBytes = kBufferSize - numBytesPrev;
61 UInt32 processedSize;
62 if (!inFile.Read(buffer + numBytesPrev, numReadBytes, processedSize))
63 return false;
64 if (processedSize == 0)
65 return true;
66 UInt32 numBytesInBuffer = numBytesPrev + processedSize;
67 UInt32 pos = 0;
68 for (;;)
69 {
70 if (writeMode)
71 {
72 if (pos > numBytesInBuffer - signatureEndSize)
73 break;
74 if (memcmp(buffer + pos, endID, signatureEndSize) == 0)
75 return true;
76 char b = buffer[pos];
77 if (b == 0)
78 return false;
79 stringResult += b;
80 pos++;
81 }
82 else
83 {
84 if (pos > numBytesInBuffer - signatureStartSize)
85 break;
86 if (memcmp(buffer + pos, startID, signatureStartSize) == 0)
87 {
88 writeMode = true;
89 pos += signatureStartSize;
90 }
91 else
92 pos++;
93 }
94 }
95 numBytesPrev = numBytesInBuffer - pos;
96 posTotal += pos;
97 memmove(buffer, buffer + pos, numBytesPrev);
98 }
99 }
100
101 static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 };
102 static char kEndID[] = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 };
103
104 struct CInstallIDInit
105 {
CInstallIDInitCInstallIDInit106 CInstallIDInit()
107 {
108 kStartID[0] = ';';
109 kEndID[0] = ';';
110 };
111 } g_CInstallIDInit;
112
113
114 #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1;
115
ShowErrorMessageSpec(const UString & name)116 static void ShowErrorMessageSpec(const UString &name)
117 {
118 UString message = NError::MyFormatMessage(::GetLastError());
119 int pos = message.Find(L"%1");
120 if (pos >= 0)
121 {
122 message.Delete(pos, 2);
123 message.Insert(pos, name);
124 }
125 ShowErrorMessage(NULL, message);
126 }
127
128 /* BEGIN Mozilla customizations */
129
130 static char const *
FindStrInBuf(char const * buf,size_t bufLen,char const * str)131 FindStrInBuf(char const * buf, size_t bufLen, char const * str)
132 {
133 size_t index = 0;
134 while (index < bufLen) {
135 char const * result = strstr(buf + index, str);
136 if (result) {
137 return result;
138 }
139 while ((buf[index] != '\0') && (index < bufLen)) {
140 index++;
141 }
142 index++;
143 }
144 return NULL;
145 }
146
147 static bool
ReadPostSigningDataFromView(char const * view,DWORD size,AString & data)148 ReadPostSigningDataFromView(char const * view, DWORD size, AString& data)
149 {
150 // Find the offset and length of the certificate table,
151 // so we know the valid range to look for the token.
152 if (size < (0x3c + sizeof(UInt32))) {
153 return false;
154 }
155 UInt32 PEHeaderOffset = *(UInt32*)(view + 0x3c);
156 UInt32 optionalHeaderOffset = PEHeaderOffset + 24;
157 UInt32 certDirEntryOffset = 0;
158 if (size < (optionalHeaderOffset + sizeof(UInt16))) {
159 return false;
160 }
161 UInt16 magic = *(UInt16*)(view + optionalHeaderOffset);
162 if (magic == 0x010b) {
163 // 32-bit executable
164 certDirEntryOffset = optionalHeaderOffset + 128;
165 } else if (magic == 0x020b) {
166 // 64-bit executable; certain header fields are wider
167 certDirEntryOffset = optionalHeaderOffset + 144;
168 } else {
169 // Unknown executable
170 return false;
171 }
172 if (size < certDirEntryOffset + 8) {
173 return false;
174 }
175 UInt32 certTableOffset = *(UInt32*)(view + certDirEntryOffset);
176 UInt32 certTableLen = *(UInt32*)(view + certDirEntryOffset + sizeof(UInt32));
177 if (certTableOffset == 0 || certTableLen == 0 ||
178 size < (certTableOffset + certTableLen)) {
179 return false;
180 }
181
182 char const token[] = "__MOZCUSTOM__:";
183 // We're searching for a string inside a binary blob,
184 // so a normal strstr that bails on the first NUL won't work.
185 char const * tokenPos = FindStrInBuf(view + certTableOffset,
186 certTableLen, token);
187 if (tokenPos) {
188 size_t tokenLen = (sizeof(token) / sizeof(token[0])) - 1;
189 data = AString(tokenPos + tokenLen);
190 return true;
191 }
192 return false;
193 }
194
195 static bool
ReadPostSigningData(UString exePath,AString & data)196 ReadPostSigningData(UString exePath, AString& data)
197 {
198 bool retval = false;
199 HANDLE exeFile = CreateFileW(exePath, GENERIC_READ, FILE_SHARE_READ, NULL,
200 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
201 if (exeFile != INVALID_HANDLE_VALUE) {
202 HANDLE mapping = CreateFileMapping(exeFile, NULL, PAGE_READONLY, 0, 0, NULL);
203 if (mapping != INVALID_HANDLE_VALUE) {
204 // MSDN claims the return value on failure is NULL,
205 // but I've also seen it returned on success, so double-check.
206 if (mapping || GetLastError() == ERROR_SUCCESS) {
207 char * view = (char*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
208 if (view) {
209 DWORD fileSize = GetFileSize(exeFile, NULL);
210 retval = ReadPostSigningDataFromView(view, fileSize, data);
211 }
212 CloseHandle(mapping);
213 }
214 }
215 CloseHandle(exeFile);
216 }
217 return retval;
218 }
219
220 // Delayed load libraries are loaded when the first symbol is used.
221 // The following ensures that we load the delayed loaded libraries from the
222 // system directory.
223 struct AutoLoadSystemDependencies
224 {
AutoLoadSystemDependenciesAutoLoadSystemDependencies225 AutoLoadSystemDependencies()
226 {
227 HMODULE module = ::GetModuleHandleW(L"kernel32.dll");
228 if (module) {
229 // SetDefaultDllDirectories is always available on Windows 8 and above. It
230 // is also available on Windows Vista, Windows Server 2008, and
231 // Windows 7 when MS KB2533623 has been applied.
232 typedef BOOL (WINAPI *SetDefaultDllDirectoriesType)(DWORD);
233 SetDefaultDllDirectoriesType setDefaultDllDirectories =
234 (SetDefaultDllDirectoriesType) GetProcAddress(module, "SetDefaultDllDirectories");
235 if (setDefaultDllDirectories) {
236 setDefaultDllDirectories(0x0800 /* LOAD_LIBRARY_SEARCH_SYSTEM32 */ );
237 return;
238 }
239 }
240
241 static LPCWSTR delayDLLs[] = { L"uxtheme.dll", L"userenv.dll",
242 L"setupapi.dll", L"apphelp.dll",
243 L"propsys.dll", L"dwmapi.dll",
244 L"cryptbase.dll", L"oleacc.dll",
245 L"clbcatq.dll" };
246 WCHAR systemDirectory[MAX_PATH + 1] = { L'\0' };
247 // If GetSystemDirectory fails we accept that we'll load the DLLs from the
248 // normal search path.
249 GetSystemDirectoryW(systemDirectory, MAX_PATH + 1);
250 size_t systemDirLen = wcslen(systemDirectory);
251
252 // Make the system directory path terminate with a slash
253 if (systemDirectory[systemDirLen - 1] != L'\\' && systemDirLen) {
254 systemDirectory[systemDirLen] = L'\\';
255 ++systemDirLen;
256 // No need to re-NULL terminate
257 }
258
259 // For each known DLL ensure it is loaded from the system32 directory
260 for (size_t i = 0; i < sizeof(delayDLLs) / sizeof(delayDLLs[0]); ++i) {
261 size_t fileLen = wcslen(delayDLLs[i]);
262 wcsncpy(systemDirectory + systemDirLen, delayDLLs[i],
263 MAX_PATH - systemDirLen);
264 if (systemDirLen + fileLen <= MAX_PATH) {
265 systemDirectory[systemDirLen + fileLen] = L'\0';
266 } else {
267 systemDirectory[MAX_PATH] = L'\0';
268 }
269 LPCWSTR fullModulePath = systemDirectory; // just for code readability
270 LoadLibraryW(fullModulePath);
271 }
272 }
273 } loadDLLs;
274
275 BOOL
RemoveCurrentDirFromSearchPath()276 RemoveCurrentDirFromSearchPath()
277 {
278 // kernel32.dll is in the knownDLL list so it is safe to load without a full path
279 HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
280 if (!kernel32) {
281 return FALSE;
282 }
283
284 typedef BOOL (WINAPI *SetDllDirectoryType)(LPCWSTR);
285 SetDllDirectoryType SetDllDirectoryFn =
286 (SetDllDirectoryType)GetProcAddress(kernel32, "SetDllDirectoryW");
287 if (!SetDllDirectoryFn) {
288 FreeLibrary(kernel32);
289 return FALSE;
290 }
291
292 // If this call fails we can't do much about it, so ignore it.
293 // It is unlikely to fail and this is just a precaution anyway.
294 SetDllDirectoryFn(L"");
295 FreeLibrary(kernel32);
296 return TRUE;
297 }
298
299 /* END Mozilla customizations */
300
WinMain(HINSTANCE hInstance,HINSTANCE,LPWSTR,int)301 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
302 #ifdef UNDER_CE
303 LPWSTR
304 #else
305 LPSTR
306 #endif
307 /* lpCmdLine */,int /* nCmdShow */)
308 {
309 /* BEGIN Mozilla customizations */
310 // Disable current directory from being in the search path.
311 // This call does not help with implicitly loaded DLLs.
312 if (!RemoveCurrentDirFromSearchPath()) {
313 WCHAR minOSTitle[512] = { '\0' };
314 WCHAR minOSText[512] = { '\0' };
315 LoadStringW(NULL, IDS_MIN_OS_TITLE, minOSTitle,
316 sizeof(minOSTitle) / sizeof(minOSTitle[0]));
317 LoadStringW(NULL, IDS_MIN_OS_TEXT, minOSText,
318 sizeof(minOSText) / sizeof(minOSText[0]));
319 MessageBoxW(NULL, minOSText, minOSTitle, MB_OK | MB_ICONERROR);
320 return 1;
321 }
322 /* END Mozilla customizations */
323
324 g_hInstance = (HINSTANCE)hInstance;
325
326 NT_CHECK
327
328 // BEGIN Mozilla customizations
329 // Our AutoLoadSystemDependencies (see above) does the same job as the
330 // LoadSecurityDlls function, but slightly better because it runs as a static
331 // initializer, and it doesn't include LOAD_LIBRARY_SEARCH_USER_DIRS in
332 // the search path, which partially defeats the purpose of calling
333 // SetDefaultDllDirectories at all.
334 //#ifdef _WIN32
335 //LoadSecurityDlls();
336 //#endif
337 // END Mozilla customizations
338
339 // InitCommonControls();
340
341 UString archiveName, switches;
342 #ifdef _SHELL_EXECUTE
343 UString executeFile, executeParameters;
344 #endif
345 NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches);
346
347 FString fullPath;
348 NDLL::MyGetModuleFileName(fullPath);
349
350 switches.Trim();
351 bool assumeYes = false;
352 if (switches.IsPrefixedBy_Ascii_NoCase("-y"))
353 {
354 assumeYes = true;
355 switches = switches.Ptr(2);
356 switches.Trim();
357 }
358
359 AString config;
360 if (!ReadDataString(fullPath, kStartID, kEndID, config))
361 {
362 if (!assumeYes)
363 ShowErrorMessage(L"Can't load config info");
364 return 1;
365 }
366
367 UString dirPrefix ("." STRING_PATH_SEPARATOR);
368 UString appLaunched;
369 bool showProgress = true;
370
371 /* BEGIN Mozilla customizations */
372 bool extractOnly = false;
373 if (switches.IsPrefixedBy_NoCase(L"/extractdir=")) {
374 assumeYes = true;
375 showProgress = false;
376 extractOnly = true;
377 } else if (!switches.IsEmpty()) {
378 showProgress = false;
379 }
380 /* END Mozilla customizations */
381
382 if (!config.IsEmpty())
383 {
384 CObjectVector<CTextConfigPair> pairs;
385 if (!GetTextConfig(config, pairs))
386 {
387 if (!assumeYes)
388 ShowErrorMessage(L"Config failed");
389 return 1;
390 }
391 UString friendlyName = GetTextConfigValue(pairs, "Title");
392 UString installPrompt = GetTextConfigValue(pairs, "BeginPrompt");
393 UString progress = GetTextConfigValue(pairs, "Progress");
394 if (progress.IsEqualTo_Ascii_NoCase("no"))
395 showProgress = false;
396 int index = FindTextConfigItem(pairs, "Directory");
397 if (index >= 0)
398 dirPrefix = pairs[index].String;
399 if (!installPrompt.IsEmpty() && !assumeYes)
400 {
401 if (MessageBoxW(0, installPrompt, friendlyName, MB_YESNO |
402 MB_ICONQUESTION) != IDYES)
403 return 0;
404 }
405 appLaunched = GetTextConfigValue(pairs, "RunProgram");
406
407 #ifdef _SHELL_EXECUTE
408 executeFile = GetTextConfigValue(pairs, "ExecuteFile");
409 executeParameters = GetTextConfigValue(pairs, "ExecuteParameters");
410 #endif
411 }
412
413 CTempDir tempDir;
414 /* Mozilla customizations - Added !extractOnly */
415 if (!extractOnly && !tempDir.Create(kTempDirPrefix))
416 {
417 if (!assumeYes)
418 ShowErrorMessage(L"Can not create temp folder archive");
419 return 1;
420 }
421
422 CCodecs *codecs = new CCodecs;
423 CMyComPtr<IUnknown> compressCodecsInfo = codecs;
424 {
425 HRESULT result = codecs->Load();
426 if (result != S_OK)
427 {
428 ShowErrorMessage(L"Can not load codecs");
429 return 1;
430 }
431 }
432
433 /* BEGIN Mozilla customizations - added extractOnly parameter support */
434 const FString tempDirPath = extractOnly ? switches.Ptr(12) : GetUnicodeString(tempDir.GetPath());
435 /* END Mozilla customizations */
436 // tempDirPath = L"M:\\1\\"; // to test low disk space
437 {
438 bool isCorrupt = false;
439 UString errorMessage;
440 HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress,
441 isCorrupt, errorMessage);
442
443 if (result != S_OK)
444 {
445 if (!assumeYes)
446 {
447 if (result == S_FALSE || isCorrupt)
448 {
449 NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage);
450 result = E_FAIL;
451 }
452 if (result != E_ABORT)
453 {
454 if (errorMessage.IsEmpty())
455 errorMessage = NError::MyFormatMessage(result);
456 ::MessageBoxW(0, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR);
457 }
458 }
459 return 1;
460 }
461 }
462
463 /* BEGIN Mozilla customizations */
464 // Retrieve and store any data added to this file after signing.
465 {
466 AString postSigningData;
467 if (ReadPostSigningData(fullPath, postSigningData)) {
468 FString postSigningDataFilePath(tempDirPath);
469 NFile::NName::NormalizeDirPathPrefix(postSigningDataFilePath);
470 postSigningDataFilePath += L"postSigningData";
471
472 NFile::NIO::COutFile postSigningDataFile;
473 postSigningDataFile.Create(postSigningDataFilePath, true);
474
475 UInt32 written = 0;
476 postSigningDataFile.Write(postSigningData, postSigningData.Len(), written);
477 }
478 }
479
480 if (extractOnly) {
481 return 0;
482 }
483 /* END Mozilla customizations */
484
485 #ifndef UNDER_CE
486 CCurrentDirRestorer currentDirRestorer;
487 if (!SetCurrentDir(tempDirPath))
488 return 1;
489 #endif
490
491 HANDLE hProcess = 0;
492 #ifdef _SHELL_EXECUTE
493 if (!executeFile.IsEmpty())
494 {
495 CSysString filePath (GetSystemString(executeFile));
496 SHELLEXECUTEINFO execInfo;
497 execInfo.cbSize = sizeof(execInfo);
498 execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
499 #ifndef UNDER_CE
500 | SEE_MASK_FLAG_DDEWAIT
501 #endif
502 ;
503 execInfo.hwnd = NULL;
504 execInfo.lpVerb = NULL;
505 execInfo.lpFile = filePath;
506
507 if (!switches.IsEmpty())
508 {
509 executeParameters.Add_Space_if_NotEmpty();
510 executeParameters += switches;
511 }
512
513 CSysString parametersSys (GetSystemString(executeParameters));
514 if (parametersSys.IsEmpty())
515 execInfo.lpParameters = NULL;
516 else
517 execInfo.lpParameters = parametersSys;
518
519 execInfo.lpDirectory = NULL;
520 execInfo.nShow = SW_SHOWNORMAL;
521 execInfo.hProcess = 0;
522 /* BOOL success = */ ::ShellExecuteEx(&execInfo);
523 UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp;
524 if (result <= 32)
525 {
526 if (!assumeYes)
527 ShowErrorMessage(L"Can not open file");
528 return 1;
529 }
530 hProcess = execInfo.hProcess;
531 }
532 else
533 #endif
534 {
535 if (appLaunched.IsEmpty())
536 {
537 appLaunched = L"setup.exe";
538 if (!NFind::DoesFileExist(us2fs(appLaunched)))
539 {
540 if (!assumeYes)
541 ShowErrorMessage(L"Can not find setup.exe");
542 return 1;
543 }
544 }
545
546 {
547 FString s2 = tempDirPath;
548 NName::NormalizeDirPathPrefix(s2);
549 appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2));
550 }
551
552 UString appNameForError = appLaunched; // actually we need to rtemove parameters also
553
554 appLaunched.Replace(L"%%T", fs2us(tempDirPath));
555
556 if (!switches.IsEmpty())
557 {
558 appLaunched.Add_Space();
559 appLaunched += switches;
560 }
561 STARTUPINFO startupInfo;
562 startupInfo.cb = sizeof(startupInfo);
563 startupInfo.lpReserved = 0;
564 startupInfo.lpDesktop = 0;
565 startupInfo.lpTitle = 0;
566 startupInfo.dwFlags = 0;
567 startupInfo.cbReserved2 = 0;
568 startupInfo.lpReserved2 = 0;
569
570 PROCESS_INFORMATION processInformation;
571
572 CSysString appLaunchedSys (GetSystemString(dirPrefix + appLaunched));
573
574 BOOL createResult = CreateProcess(NULL, (LPTSTR)(LPCTSTR)appLaunchedSys,
575 NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */,
576 &startupInfo, &processInformation);
577 if (createResult == 0)
578 {
579 if (!assumeYes)
580 {
581 // we print name of exe file, if error message is
582 // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application".
583 ShowErrorMessageSpec(appNameForError);
584 }
585 return 1;
586 }
587 ::CloseHandle(processInformation.hThread);
588 hProcess = processInformation.hProcess;
589 }
590 if (hProcess != 0)
591 {
592 WaitForSingleObject(hProcess, INFINITE);
593 ::CloseHandle(hProcess);
594 }
595 return 0;
596 }
597