tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SfxSetup.cpp (17997B)


      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 
     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 {
    106  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 
    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 *
    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 ((index < bufLen) && (buf[index] != '\0')) {
    140      index++;
    141    }
    142    index++;
    143  }
    144  return NULL;
    145 }
    146 
    147 static bool
    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
    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 {
    225  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
    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 
    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 }