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 "mozilla/NativeNt.h"
8 #include "mozilla/WinHeaderOnlyUtils.h"
9 
10 namespace mozilla {
11 namespace detail {
12 
GetImageDirectoryViaFileIo(const nsAutoHandle & aImageFile,const uint32_t aOurImportDirectoryRva)13 inline LauncherResult<nt::DataDirectoryEntry> GetImageDirectoryViaFileIo(
14     const nsAutoHandle& aImageFile, const uint32_t aOurImportDirectoryRva) {
15   OVERLAPPED ov = {};
16   ov.Offset = aOurImportDirectoryRva;
17 
18   DWORD bytesRead;
19   nt::DataDirectoryEntry result;
20   if (!::ReadFile(aImageFile, &result, sizeof(result), &bytesRead, &ov) ||
21       bytesRead != sizeof(result)) {
22     return LAUNCHER_ERROR_FROM_LAST();
23   }
24 
25   return result;
26 }
27 
28 }  // namespace detail
29 
30 /**
31  * This function ensures that the import directory of a loaded binary image
32  * matches the version that is found in the original file on disk. We do this
33  * to prevent tampering by third-party code.
34  *
35  * Yes, this function may perform file I/O on the critical path during
36  * startup. A mitigating factor here is that this function must be called
37  * immediately after creating a process using the image specified by
38  * |aFullImagePath|; by this point, the system has already paid the price of
39  * pulling the image file's contents into the page cache.
40  *
41  * @param aFullImagePath Wide-character string containing the absolute path
42  *                       to the binary whose import directory we are touching.
43  * @param aTransferMgr   Encapsulating the transfer from the current process to
44  *                       the child process whose import table we are touching.
45  */
RestoreImportDirectory(const wchar_t * aFullImagePath,nt::CrossExecTransferManager & aTransferMgr)46 inline LauncherVoidResult RestoreImportDirectory(
47     const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr) {
48   uint32_t importDirEntryRva;
49   PIMAGE_DATA_DIRECTORY importDirEntry =
50       aTransferMgr.LocalPEHeaders().GetImageDirectoryEntryPtr(
51           IMAGE_DIRECTORY_ENTRY_IMPORT, &importDirEntryRva);
52   if (!importDirEntry) {
53     return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
54   }
55 
56   nsAutoHandle file(::CreateFileW(aFullImagePath, GENERIC_READ, FILE_SHARE_READ,
57                                   nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
58                                   nullptr));
59   if (file.get() == INVALID_HANDLE_VALUE) {
60     return LAUNCHER_ERROR_FROM_LAST();
61   }
62 
63   // Why do we use file I/O here instead of a memory mapping? The simple reason
64   // is that we do not want any kernel-mode drivers to start tampering with file
65   // contents under the belief that the file is being mapped for execution.
66   // Windows 8 supports creation of file mappings using the SEC_IMAGE_NO_EXECUTE
67   // flag, which may help to mitigate this, but we might as well just support
68   // a single implementation that works everywhere.
69   LauncherResult<nt::DataDirectoryEntry> realImportDirectory =
70       detail::GetImageDirectoryViaFileIo(file, importDirEntryRva);
71   if (realImportDirectory.isErr()) {
72     return realImportDirectory.propagateErr();
73   }
74 
75   nt::DataDirectoryEntry toWrite = realImportDirectory.unwrap();
76 
77   {  // Scope for prot
78     AutoVirtualProtect prot = aTransferMgr.Protect(
79         importDirEntry, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE);
80     if (!prot) {
81       return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError());
82     }
83 
84     LauncherVoidResult writeResult = aTransferMgr.Transfer(
85         importDirEntry, &toWrite, sizeof(IMAGE_DATA_DIRECTORY));
86     if (writeResult.isErr()) {
87       return writeResult.propagateErr();
88     }
89   }
90 
91   return Ok();
92 }
93 
94 }  // namespace mozilla
95