tor-browser

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

DllBlocklistInit.cpp (9190B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsWindowsDllInterceptor.h"
      8 #include "mozilla/ImportDir.h"
      9 #include "mozilla/NativeNt.h"
     10 #include "mozilla/PolicyChecks.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/WindowsDllBlocklist.h"
     13 #include "mozilla/WindowsStackCookie.h"
     14 #include "mozilla/WinHeaderOnlyUtils.h"
     15 
     16 #include "DllBlocklistInit.h"
     17 #include "freestanding/DllBlocklist.h"
     18 #include "freestanding/SharedSection.h"
     19 
     20 namespace mozilla {
     21 
     22 #if defined(MOZ_ASAN) || defined(_M_ARM64)
     23 
     24 // This DLL blocking code is incompatible with ASAN because
     25 // it is able to execute before ASAN itself has even initialized.
     26 // Also, AArch64 has not been tested with this.
     27 LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
     28    const wchar_t* aFullImagePath, HANDLE aChildProcess,
     29    const IMAGE_THUNK_DATA*, const GeckoProcessType aProcessType) {
     30  return mozilla::Ok();
     31 }
     32 
     33 LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
     34    const wchar_t* aFullImagePath, HANDLE aChildProcess,
     35    const bool aDisableDynamicBlocklist,
     36    Maybe<std::wstring> aBlocklistFileName) {
     37  return mozilla::Ok();
     38 }
     39 
     40 #else
     41 
     42 static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
     43    const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr,
     44    const IMAGE_THUNK_DATA* aCachedNtdllThunk,
     45    const GeckoProcessType aProcessType) {
     46  CrossProcessDllInterceptor intcpt(aTransferMgr.RemoteProcess());
     47  intcpt.Init(L"ntdll.dll");
     48 
     49 #  if defined(DEBUG) && defined(_M_X64) && !defined(__MINGW64__)
     50  // This debug check preserves compatibility with third-parties (see bug
     51  // 1733532).
     52  MOZ_ASSERT(!HasStackCookieCheck(
     53      reinterpret_cast<uintptr_t>(&freestanding::patched_NtMapViewOfSection)));
     54 #  endif  // #if defined(DEBUG) && defined(_M_X64) && !defined(__MINGW64__)
     55 
     56  bool ok = freestanding::stub_NtMapViewOfSection.SetDetour(
     57      aTransferMgr, intcpt, "NtMapViewOfSection",
     58      &freestanding::patched_NtMapViewOfSection);
     59  if (!ok) {
     60    return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError());
     61  }
     62 
     63  ok = freestanding::stub_LdrLoadDll.SetDetour(
     64      aTransferMgr, intcpt, "LdrLoadDll", &freestanding::patched_LdrLoadDll);
     65  if (!ok) {
     66    return LAUNCHER_ERROR_FROM_DETOUR_ERROR(intcpt.GetLastDetourError());
     67  }
     68 
     69  // Because aChildProcess has just been created in a suspended state, its
     70  // dynamic linker has not yet been initialized, thus its executable has
     71  // not yet been linked with ntdll.dll. If the blocklist hook intercepts a
     72  // library load prior to the link, the hook will be unable to invoke any
     73  // ntdll.dll functions.
     74  //
     75  // We know that the executable for our *current* process's binary is already
     76  // linked into ntdll, so we obtain the IAT from our own executable and graft
     77  // it onto the child process's IAT, thus enabling the child process's hook to
     78  // safely make its ntdll calls.
     79 
     80  const nt::PEHeaders& ourExeImage = aTransferMgr.LocalPEHeaders();
     81 
     82  // As part of our mitigation of binary tampering, copy our import directory
     83  // from the original in our executable file.
     84  LauncherVoidResult importDirRestored =
     85      RestoreImportDirectory(aFullImagePath, aTransferMgr);
     86  if (importDirRestored.isErr()) {
     87    return importDirRestored;
     88  }
     89 
     90  mozilla::nt::PEHeaders ntdllImage(::GetModuleHandleW(L"ntdll.dll"));
     91  if (!ntdllImage) {
     92    return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
     93  }
     94 
     95  // If we have a cached IAT i.e. |aCachedNtdllThunk| is non-null, we can
     96  // safely copy it to |aChildProcess| even if the local IAT has been modified.
     97  // If |aCachedNtdllThunk| is null, we've failed to cache the IAT or we're in
     98  // the launcher process where there is no chance to cache the IAT.  In those
     99  // cases, we retrieve the IAT with the boundary check to avoid a modified IAT
    100  // from being copied into |aChildProcess|.
    101  Maybe<Span<IMAGE_THUNK_DATA> > ntdllThunks;
    102  if (aCachedNtdllThunk) {
    103    ntdllThunks = ourExeImage.GetIATThunksForModule("ntdll.dll");
    104  } else {
    105    Maybe<Range<const uint8_t> > ntdllBoundaries = ntdllImage.GetBounds();
    106    if (!ntdllBoundaries) {
    107      return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
    108    }
    109 
    110    // We can use GetIATThunksForModule() to check whether IAT is modified
    111    // or not because no functions exported from ntdll.dll is forwarded.
    112    ntdllThunks =
    113        ourExeImage.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr());
    114  }
    115  if (!ntdllThunks) {
    116    return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA);
    117  }
    118 
    119  {  // Scope for prot
    120    PIMAGE_THUNK_DATA firstIatThunkDst = ntdllThunks.value().data();
    121    const IMAGE_THUNK_DATA* firstIatThunkSrc =
    122        aCachedNtdllThunk ? aCachedNtdllThunk : firstIatThunkDst;
    123    SIZE_T iatLength = ntdllThunks.value().LengthBytes();
    124 
    125    AutoVirtualProtect prot =
    126        aTransferMgr.Protect(firstIatThunkDst, iatLength, PAGE_READWRITE);
    127    if (!prot) {
    128      return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError());
    129    }
    130 
    131    LauncherVoidResult writeResult =
    132        aTransferMgr.Transfer(firstIatThunkDst, firstIatThunkSrc, iatLength);
    133    if (writeResult.isErr()) {
    134      return writeResult.propagateErr();
    135    }
    136  }
    137 
    138  // Tell the mozglue blocklist that we have bootstrapped
    139  uint32_t newFlags = eDllBlocklistInitFlagWasBootstrapped;
    140 
    141  if (gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
    142    // If we ourselves were bootstrapped, then we are starting a child process
    143    // and need to set the appropriate flag.
    144    newFlags |= eDllBlocklistInitFlagIsChildProcess;
    145  }
    146 
    147  SetDllBlocklistProcessTypeFlags(newFlags, aProcessType);
    148 
    149  LauncherVoidResult writeResult =
    150      aTransferMgr.Transfer(&gBlocklistInitFlags, &newFlags, sizeof(newFlags));
    151  if (writeResult.isErr()) {
    152    return writeResult.propagateErr();
    153  }
    154 
    155  return Ok();
    156 }
    157 
    158 LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
    159    const wchar_t* aFullImagePath, HANDLE aChildProcess,
    160    const IMAGE_THUNK_DATA* aCachedNtdllThunk,
    161    const GeckoProcessType aProcessType) {
    162  nt::CrossExecTransferManager transferMgr(aChildProcess);
    163  if (!transferMgr) {
    164    return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
    165  }
    166 
    167  // We come here when the browser process launches a sandbox process.
    168  // If the launcher process already failed to bootstrap the browser process,
    169  // we should not attempt to bootstrap a child process because it's likely
    170  // to fail again.  Instead, we only restore the import directory entry.
    171  if (!(gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
    172    return RestoreImportDirectory(aFullImagePath, transferMgr);
    173  }
    174 
    175  // Transfer a readonly handle to the child processes because all information
    176  // are already written to the section by the launcher and main process.
    177  LauncherVoidResult transferResult =
    178      freestanding::gSharedSection.TransferHandle(transferMgr, GENERIC_READ);
    179  if (transferResult.isErr()) {
    180    return transferResult.propagateErr();
    181  }
    182 
    183  return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr,
    184                                           aCachedNtdllThunk, aProcessType);
    185 }
    186 
    187 LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
    188    const wchar_t* aFullImagePath, HANDLE aChildProcess,
    189    const bool aDisableDynamicBlocklist,
    190    Maybe<std::wstring> aBlocklistFileName) {
    191  nt::CrossExecTransferManager transferMgr(aChildProcess);
    192  if (!transferMgr) {
    193    return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
    194  }
    195 
    196  // The launcher process initializes a section object, whose handle is
    197  // transferred to the browser process, and that transferred handle in
    198  // the browser process is transferred to the sandbox processes.
    199  LauncherVoidResultWithLineInfo result = freestanding::gSharedSection.Init();
    200  if (result.isErr()) {
    201    return result;
    202  }
    203 
    204  if (aBlocklistFileName.isSome() &&
    205      !PolicyCheckBoolean(L"DisableThirdPartyModuleBlocking")) {
    206    DynamicBlockList blockList(aBlocklistFileName->c_str());
    207    result = freestanding::gSharedSection.SetBlocklist(
    208        blockList, aDisableDynamicBlocklist);
    209    if (result.isErr()) {
    210      return result;
    211    }
    212  }
    213 
    214  // Transfer a writable handle to the main process because it needs to append
    215  // dependent module paths to the section.
    216  LauncherVoidResult transferResult =
    217      freestanding::gSharedSection.TransferHandle(transferMgr,
    218                                                  GENERIC_READ | GENERIC_WRITE);
    219  if (transferResult.isErr()) {
    220    return transferResult.propagateErr();
    221  }
    222 
    223  auto clearInstance = MakeScopeExit([]() {
    224    // After transfer, the launcher process does not need the object anymore.
    225    freestanding::gSharedSection.Reset(nullptr);
    226  });
    227  return InitializeDllBlocklistOOPInternal(aFullImagePath, transferMgr, nullptr,
    228                                           GeckoProcessType_Default);
    229 }
    230 
    231 #endif  // defined(MOZ_ASAN) || defined(_M_ARM64)
    232 
    233 }  // namespace mozilla