ImportDir.h (3712B)
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 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 */ 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