mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 20:01:50 +00:00
Bug 1671316 - Part1. Introduce CrossExecTransferManager. r=mhowell
This patch introduces a class `CrossExecTransferManager` to manage the data transfer from the current process to a remote process via `WriteProcessMemory`. The class also encapsulates a logic to bridge the gap between two executable's imagebase. Differential Revision: https://phabricator.services.mozilla.com/D94652
This commit is contained in:
parent
656917f124
commit
83d95e2106
@ -20,22 +20,6 @@
|
||||
#include "freestanding/DllBlocklist.h"
|
||||
#include "freestanding/FunctionTableResolver.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
T* GetRemoteAddress(T* aLocalAddress, HMODULE aLocal, HMODULE aRemote) {
|
||||
ptrdiff_t diff =
|
||||
mozilla::nt::PEHeaders::HModuleToBaseAddr<uint8_t*>(aRemote) -
|
||||
mozilla::nt::PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocal);
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(aLocalAddress) + diff);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if defined(MOZ_ASAN) || defined(_M_ARM64)
|
||||
@ -91,33 +75,16 @@ static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
|
||||
// it onto the child process's IAT, thus enabling the child process's hook to
|
||||
// safely make its ntdll calls.
|
||||
|
||||
HMODULE ourModule;
|
||||
# if defined(_MSC_VER)
|
||||
ourModule = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
# else
|
||||
ourModule = ::GetModuleHandleW(nullptr);
|
||||
# endif // defined(_MSC_VER)
|
||||
|
||||
mozilla::nt::PEHeaders ourExeImage(ourModule);
|
||||
if (!ourExeImage) {
|
||||
nt::CrossExecTransferManager transferMgr(aChildProcess);
|
||||
if (!transferMgr) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
|
||||
}
|
||||
|
||||
// We used to pass ourModule to RestoreImportDirectory based on the assumption
|
||||
// that the executable is mapped onto the same address in a different process,
|
||||
// but our telemetry told us it was not guaranteed. That's why we retrieve
|
||||
// HMODULE from |aChildProcess|.
|
||||
LauncherResult<HMODULE> remoteImageBaseResult =
|
||||
nt::GetProcessExeModule(aChildProcess);
|
||||
if (remoteImageBaseResult.isErr()) {
|
||||
return remoteImageBaseResult.propagateErr();
|
||||
}
|
||||
HMODULE remoteImageBase = remoteImageBaseResult.unwrap();
|
||||
const nt::PEHeaders& ourExeImage = transferMgr.LocalPEHeaders();
|
||||
|
||||
// As part of our mitigation of binary tampering, copy our import directory
|
||||
// from the original in our executable file.
|
||||
LauncherVoidResult importDirRestored = RestoreImportDirectory(
|
||||
aFullImagePath, ourExeImage, aChildProcess, remoteImageBase);
|
||||
LauncherVoidResult importDirRestored =
|
||||
RestoreImportDirectory(aFullImagePath, transferMgr);
|
||||
if (importDirRestored.isErr()) {
|
||||
return importDirRestored;
|
||||
}
|
||||
@ -151,27 +118,22 @@ static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
|
||||
return LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
}
|
||||
|
||||
SIZE_T bytesWritten;
|
||||
|
||||
{ // Scope for prot
|
||||
PIMAGE_THUNK_DATA firstIatThunkDst = ntdllThunks.value().data();
|
||||
const IMAGE_THUNK_DATA* firstIatThunkSrc =
|
||||
aCachedNtdllThunk ? aCachedNtdllThunk : firstIatThunkDst;
|
||||
SIZE_T iatLength = ntdllThunks.value().LengthBytes();
|
||||
|
||||
firstIatThunkDst =
|
||||
GetRemoteAddress(firstIatThunkDst, ourModule, remoteImageBase);
|
||||
|
||||
AutoVirtualProtect prot(firstIatThunkDst, iatLength, PAGE_READWRITE,
|
||||
aChildProcess);
|
||||
AutoVirtualProtect prot =
|
||||
transferMgr.Protect(firstIatThunkDst, iatLength, PAGE_READWRITE);
|
||||
if (!prot) {
|
||||
return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError());
|
||||
}
|
||||
|
||||
ok = !!::WriteProcessMemory(aChildProcess, firstIatThunkDst,
|
||||
firstIatThunkSrc, iatLength, &bytesWritten);
|
||||
if (!ok || bytesWritten != iatLength) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
LauncherVoidResult writeResult =
|
||||
transferMgr.Transfer(firstIatThunkDst, firstIatThunkSrc, iatLength);
|
||||
if (writeResult.isErr()) {
|
||||
return writeResult.propagateErr();
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,12 +146,10 @@ static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
|
||||
newFlags |= eDllBlocklistInitFlagIsChildProcess;
|
||||
}
|
||||
|
||||
ok = !!::WriteProcessMemory(
|
||||
aChildProcess,
|
||||
GetRemoteAddress(&gBlocklistInitFlags, ourModule, remoteImageBase),
|
||||
&newFlags, sizeof(newFlags), &bytesWritten);
|
||||
if (!ok || bytesWritten != sizeof(newFlags)) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
LauncherVoidResult writeResult =
|
||||
transferMgr.Transfer(&gBlocklistInitFlags, &newFlags, sizeof(newFlags));
|
||||
if (writeResult.isErr()) {
|
||||
return writeResult.propagateErr();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
@ -203,19 +163,12 @@ LauncherVoidResultWithLineInfo InitializeDllBlocklistOOP(
|
||||
// we should not attempt to bootstrap a child process because it's likely
|
||||
// to fail again. Instead, we only restore the import directory entry.
|
||||
if (!(gBlocklistInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
|
||||
HMODULE exeImageBase;
|
||||
# if defined(_MSC_VER)
|
||||
exeImageBase = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
# else
|
||||
exeImageBase = ::GetModuleHandleW(nullptr);
|
||||
# endif // defined(_MSC_VER)
|
||||
|
||||
mozilla::nt::PEHeaders localImage(exeImageBase);
|
||||
if (!localImage) {
|
||||
nt::CrossExecTransferManager transferMgr(aChildProcess);
|
||||
if (!transferMgr) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
|
||||
}
|
||||
|
||||
return RestoreImportDirectory(aFullImagePath, localImage, aChildProcess);
|
||||
return RestoreImportDirectory(aFullImagePath, transferMgr);
|
||||
}
|
||||
|
||||
return InitializeDllBlocklistOOPInternal(aFullImagePath, aChildProcess,
|
||||
|
194
browser/app/winlauncher/test/TestCrossProcessWin.cpp
Normal file
194
browser/app/winlauncher/test/TestCrossProcessWin.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define MOZ_USE_LAUNCHER_ERROR
|
||||
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
|
||||
const wchar_t kChildArg[] = L"--child";
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
template <typename T, int N>
|
||||
void PrintLauncherError(const LauncherResult<T>& aResult,
|
||||
const char (&aMsg)[N]) {
|
||||
const LauncherError& err = aResult.inspectErr();
|
||||
printf("TEST-FAILED | TestCrossProcessWin | %s - %lx at %s:%d\n", aMsg,
|
||||
err.mError.AsHResult(), err.mFile, err.mLine);
|
||||
}
|
||||
|
||||
class ChildProcess final {
|
||||
nsAutoHandle mChildProcess;
|
||||
nsAutoHandle mChildMainThread;
|
||||
DWORD mProcessId;
|
||||
|
||||
public:
|
||||
// The following variables are updated from the parent process via
|
||||
// WriteProcessMemory while the process is suspended as a part of
|
||||
// TestWithChildProcess().
|
||||
//
|
||||
// Having both a non-const and a const is important because a constant
|
||||
// is separately placed in the .rdata section which is read-only, so
|
||||
// the region's attribute needs to be changed before modifying data via
|
||||
// WriteProcessMemory.
|
||||
// The keyword "volatile" is needed for a constant, otherwise the compiler
|
||||
// evaluates a constant as a literal without fetching data from memory.
|
||||
static HMODULE sExecutableImageBase;
|
||||
static volatile const DWORD sReadOnlyProcessId;
|
||||
|
||||
static int Main() {
|
||||
if (sExecutableImageBase != ::GetModuleHandle(nullptr)) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"sExecutableImageBase is expected to be %p, but actually was %p.\n",
|
||||
::GetModuleHandle(nullptr), sExecutableImageBase);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sReadOnlyProcessId != ::GetCurrentProcessId()) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"sReadOnlyProcessId is expected to be %lx, but actually was %lx.\n",
|
||||
::GetCurrentProcessId(), sReadOnlyProcessId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption)
|
||||
: mProcessId(0) {
|
||||
const wchar_t* childArgv[] = {aExecutable, aOption};
|
||||
auto cmdLine(
|
||||
mozilla::MakeCommandLine(mozilla::ArrayLength(childArgv), childArgv));
|
||||
|
||||
STARTUPINFOW si = {sizeof(si)};
|
||||
PROCESS_INFORMATION pi;
|
||||
BOOL ok =
|
||||
::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, FALSE,
|
||||
CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
|
||||
if (!ok) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"CreateProcessW falied - %08lx.\n",
|
||||
GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
mProcessId = pi.dwProcessId;
|
||||
|
||||
mChildProcess.own(pi.hProcess);
|
||||
mChildMainThread.own(pi.hThread);
|
||||
}
|
||||
|
||||
~ChildProcess() { ::TerminateProcess(mChildProcess, 0); }
|
||||
|
||||
operator HANDLE() const { return mChildProcess; }
|
||||
DWORD GetProcessId() const { return mProcessId; }
|
||||
|
||||
bool ResumeAndWaitUntilExit() {
|
||||
if (::ResumeThread(mChildMainThread) == 0xffffffff) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"ResumeThread failed - %08lx\n",
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::WaitForSingleObject(mChildProcess, 60000) != WAIT_OBJECT_0) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"Unexpected result from WaitForSingleObject\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exitCode;
|
||||
if (!::GetExitCodeProcess(mChildProcess, &exitCode)) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"GetExitCodeProcess failed - %08lx\n",
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
};
|
||||
|
||||
HMODULE ChildProcess::sExecutableImageBase = 0;
|
||||
volatile const DWORD ChildProcess::sReadOnlyProcessId = 0;
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
printf("Process: %-8lx Base: %p\n", ::GetCurrentProcessId(),
|
||||
::GetModuleHandle(nullptr));
|
||||
|
||||
if (argc == 2 && wcscmp(argv[1], kChildArg) == 0) {
|
||||
return ChildProcess::Main();
|
||||
}
|
||||
|
||||
ChildProcess childProcess(argv[0], kChildArg);
|
||||
if (!childProcess) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LauncherResult<HMODULE> remoteImageBase =
|
||||
nt::GetProcessExeModule(childProcess);
|
||||
if (remoteImageBase.isErr()) {
|
||||
PrintLauncherError(remoteImageBase, "nt::GetProcessExeModule failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nt::CrossExecTransferManager transferMgr(childProcess);
|
||||
if (!transferMgr) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"CrossExecTransferManager instantiation failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LauncherVoidResult writeResult =
|
||||
transferMgr.Transfer(&ChildProcess::sExecutableImageBase,
|
||||
&remoteImageBase.inspect(), sizeof(HMODULE));
|
||||
if (writeResult.isErr()) {
|
||||
PrintLauncherError(writeResult,
|
||||
"ChildProcess::WriteData(Imagebase) failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD childPid = childProcess.GetProcessId();
|
||||
|
||||
DWORD* readOnlyData = const_cast<DWORD*>(&ChildProcess::sReadOnlyProcessId);
|
||||
writeResult = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
|
||||
if (writeResult.isOk()) {
|
||||
printf(
|
||||
"TEST-UNEXPECTED | TestCrossProcessWin | "
|
||||
"A constant was located in a writable section.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
AutoVirtualProtect prot =
|
||||
transferMgr.Protect(readOnlyData, sizeof(uint32_t), PAGE_READWRITE);
|
||||
if (!prot) {
|
||||
printf(
|
||||
"TEST-FAILED | TestCrossProcessWin | "
|
||||
"VirtualProtect failed - %08lx\n",
|
||||
prot.GetError().AsHResult());
|
||||
return 1;
|
||||
}
|
||||
|
||||
writeResult = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
|
||||
if (writeResult.isErr()) {
|
||||
PrintLauncherError(writeResult, "ChildProcess::WriteData(PID) failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!childProcess.ResumeAndWaitUntilExit()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -8,6 +8,7 @@ DisableStlWrapping()
|
||||
|
||||
GeckoCppUnitTests(
|
||||
[
|
||||
"TestCrossProcessWin",
|
||||
"TestSafeThreadLocal",
|
||||
"TestSameBinary",
|
||||
],
|
||||
|
@ -40,23 +40,15 @@ inline LauncherResult<nt::DataDirectoryEntry> GetImageDirectoryViaFileIo(
|
||||
*
|
||||
* @param aFullImagePath Wide-character string containing the absolute path
|
||||
* to the binary whose import directory we are touching.
|
||||
* @param aLocalExeImage The binary's PE headers that have been loaded into our
|
||||
* process for examination.
|
||||
* @param aTargetProcess Handle to the child process whose import table we are
|
||||
* touching.
|
||||
* @param aRemoteExeImage HMODULE referencing the child process's executable
|
||||
* binary that we are touching. This value is used to
|
||||
* determine the base address of the binary within the
|
||||
* target process. If nullptr is passed, we use
|
||||
* |aTargetProcess| to retrieve a value.
|
||||
* @param aTransferMgr Encapsulating the transfer from the current process to
|
||||
* the child process whose import table we are touching.
|
||||
*/
|
||||
inline LauncherVoidResult RestoreImportDirectory(
|
||||
const wchar_t* aFullImagePath, const nt::PEHeaders& aLocalExeImage,
|
||||
HANDLE aTargetProcess, HMODULE aRemoteExeImage = nullptr) {
|
||||
const wchar_t* aFullImagePath, nt::CrossExecTransferManager& aTransferMgr) {
|
||||
uint32_t importDirEntryRva;
|
||||
PIMAGE_DATA_DIRECTORY importDirEntry =
|
||||
aLocalExeImage.GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT,
|
||||
&importDirEntryRva);
|
||||
aTransferMgr.LocalPEHeaders().GetImageDirectoryEntryPtr(
|
||||
IMAGE_DIRECTORY_ENTRY_IMPORT, &importDirEntryRva);
|
||||
if (!importDirEntry) {
|
||||
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
|
||||
}
|
||||
@ -82,31 +74,17 @@ inline LauncherVoidResult RestoreImportDirectory(
|
||||
|
||||
nt::DataDirectoryEntry toWrite = realImportDirectory.unwrap();
|
||||
|
||||
if (!aRemoteExeImage) {
|
||||
LauncherResult<HMODULE> remoteImageBase =
|
||||
nt::GetProcessExeModule(aTargetProcess);
|
||||
if (remoteImageBase.isErr()) {
|
||||
return remoteImageBase.propagateErr();
|
||||
}
|
||||
aRemoteExeImage = remoteImageBase.unwrap();
|
||||
}
|
||||
|
||||
void* remoteAddress =
|
||||
nt::PEHeaders::HModuleToBaseAddr<char*>(aRemoteExeImage) +
|
||||
importDirEntryRva;
|
||||
|
||||
{ // Scope for prot
|
||||
AutoVirtualProtect prot(remoteAddress, sizeof(IMAGE_DATA_DIRECTORY),
|
||||
PAGE_READWRITE, aTargetProcess);
|
||||
AutoVirtualProtect prot = aTransferMgr.Protect(
|
||||
importDirEntry, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE);
|
||||
if (!prot) {
|
||||
return LAUNCHER_ERROR_FROM_MOZ_WINDOWS_ERROR(prot.GetError());
|
||||
}
|
||||
|
||||
SIZE_T bytesWritten;
|
||||
if (!::WriteProcessMemory(aTargetProcess, remoteAddress, &toWrite,
|
||||
sizeof(IMAGE_DATA_DIRECTORY), &bytesWritten) ||
|
||||
bytesWritten != sizeof(IMAGE_DATA_DIRECTORY)) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
LauncherVoidResult writeResult = aTransferMgr.Transfer(
|
||||
importDirEntry, &toWrite, sizeof(IMAGE_DATA_DIRECTORY));
|
||||
if (writeResult.isErr()) {
|
||||
return writeResult.propagateErr();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,7 +558,14 @@ class MOZ_RAII PEHeaders final {
|
||||
return Some(Range(base, imageSize));
|
||||
}
|
||||
|
||||
PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() {
|
||||
bool IsWithinImage(const void* aAddress) const {
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress);
|
||||
uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader);
|
||||
uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit);
|
||||
return addr >= imageBase && addr <= imageLimit;
|
||||
}
|
||||
|
||||
PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const {
|
||||
// If the import directory is already tampered, we skip bounds check
|
||||
// because it could be located outside the mapped image.
|
||||
return mIsImportDirectoryTampered
|
||||
@ -627,7 +634,7 @@ class MOZ_RAII PEHeaders final {
|
||||
}
|
||||
|
||||
PIMAGE_IMPORT_DESCRIPTOR
|
||||
GetImportDescriptor(const char* aModuleNameASCII) {
|
||||
GetImportDescriptor(const char* aModuleNameASCII) const {
|
||||
for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
|
||||
IsValid(curImpDesc); ++curImpDesc) {
|
||||
auto curName = mIsImportDirectoryTampered
|
||||
@ -675,7 +682,7 @@ class MOZ_RAII PEHeaders final {
|
||||
*/
|
||||
Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule(
|
||||
const char* aModuleNameASCII,
|
||||
const Range<const uint8_t>* aBoundaries = nullptr) {
|
||||
const Range<const uint8_t>* aBoundaries = nullptr) const {
|
||||
PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII);
|
||||
if (!impDesc) {
|
||||
return Nothing();
|
||||
@ -1282,6 +1289,103 @@ inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
|
||||
return static_cast<HMODULE>(baseAddress);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
#endif
|
||||
|
||||
// This class manages data transfer from the local process's executable
|
||||
// to another process's executable via WriteProcessMemory.
|
||||
// Bug 1662560 told us the same executable may be mapped onto a different
|
||||
// address in a different process. This means when we transfer data within
|
||||
// the mapped executable such as a global variable or IAT from the current
|
||||
// process to another process, we need to shift its address by the difference
|
||||
// between two executable's mapped imagebase.
|
||||
class CrossExecTransferManager final {
|
||||
HANDLE mRemoteProcess;
|
||||
uint8_t* mLocalImagebase;
|
||||
PEHeaders mLocalExec;
|
||||
uint8_t* mRemoteImagebase;
|
||||
|
||||
static HMODULE GetLocalExecModule() {
|
||||
#if defined(_MSC_VER)
|
||||
return reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
#else
|
||||
return ::GetModuleHandleW(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
LauncherVoidResult EnsureRemoteImagebase() {
|
||||
if (!mRemoteImagebase) {
|
||||
LauncherResult<HMODULE> remoteImageBaseResult =
|
||||
GetProcessExeModule(mRemoteProcess);
|
||||
if (remoteImageBaseResult.isErr()) {
|
||||
return remoteImageBaseResult.propagateErr();
|
||||
}
|
||||
|
||||
mRemoteImagebase =
|
||||
reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* LocalExecToRemoteExec(T* aLocalAddress) const {
|
||||
MOZ_ASSERT(mRemoteImagebase);
|
||||
MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
|
||||
|
||||
if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
|
||||
return aLocalAddress;
|
||||
}
|
||||
|
||||
uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
|
||||
reinterpret_cast<uintptr_t>(mLocalImagebase);
|
||||
return reinterpret_cast<T*>(mRemoteImagebase + offset);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CrossExecTransferManager(HANDLE aRemoteProcess)
|
||||
: mRemoteProcess(aRemoteProcess),
|
||||
mLocalImagebase(
|
||||
PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
|
||||
mLocalExec(mLocalImagebase),
|
||||
mRemoteImagebase(nullptr) {}
|
||||
|
||||
CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
|
||||
: mRemoteProcess(aRemoteProcess),
|
||||
mLocalImagebase(
|
||||
PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
|
||||
mLocalExec(mLocalImagebase),
|
||||
mRemoteImagebase(nullptr) {}
|
||||
|
||||
explicit operator bool() const { return !!mLocalExec; }
|
||||
HANDLE RemoteProcess() const { return mRemoteProcess; }
|
||||
const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
|
||||
|
||||
AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
|
||||
DWORD aProtFlags) {
|
||||
// If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
|
||||
Unused << EnsureRemoteImagebase();
|
||||
return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
|
||||
aProtFlags, mRemoteProcess);
|
||||
}
|
||||
|
||||
LauncherVoidResult Transfer(LPVOID aDestinationAddress,
|
||||
LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
|
||||
LauncherVoidResult result = EnsureRemoteImagebase();
|
||||
if (result.isErr()) {
|
||||
return result.propagateErr();
|
||||
}
|
||||
|
||||
if (!::WriteProcessMemory(mRemoteProcess,
|
||||
LocalExecToRemoteExec(aDestinationAddress),
|
||||
aBufferToWrite, aBufferSize, nullptr)) {
|
||||
return LAUNCHER_ERROR_FROM_LAST();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
|
||||
|
@ -336,10 +336,11 @@ bool SandboxBroker::LaunchApp(const wchar_t* aPath, const wchar_t* aArguments,
|
||||
nsModuleHandle moduleHandle(
|
||||
::LoadLibraryExW(aPath, nullptr, LOAD_LIBRARY_AS_DATAFILE));
|
||||
if (moduleHandle) {
|
||||
nt::PEHeaders exeImage(moduleHandle.get());
|
||||
if (!!exeImage) {
|
||||
nt::CrossExecTransferManager transferMgr(targetInfo.hProcess,
|
||||
moduleHandle);
|
||||
if (!!transferMgr) {
|
||||
LauncherVoidResult importsRestored =
|
||||
RestoreImportDirectory(aPath, exeImage, targetInfo.hProcess);
|
||||
RestoreImportDirectory(aPath, transferMgr);
|
||||
if (importsRestored.isErr()) {
|
||||
RefPtr<DllServices> dllSvc(DllServices::Get());
|
||||
dllSvc->HandleLauncherError(
|
||||
@ -883,8 +884,7 @@ bool SandboxBroker::SetSecurityLevelForRDDProcess() {
|
||||
"SetIntegrityLevel should never fail with these "
|
||||
"arguments, what happened?");
|
||||
|
||||
result =
|
||||
mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
|
||||
result = mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
|
||||
SANDBOX_ENSURE_SUCCESS(result,
|
||||
"SetDelayedIntegrityLevel should never fail with "
|
||||
"these arguments, what happened?");
|
||||
|
@ -14,6 +14,8 @@
|
||||
[TestCompactPair]
|
||||
[TestCountPopulation]
|
||||
[TestCountZeroes]
|
||||
[TestCrossProcessWin]
|
||||
skip-if = os != 'win'
|
||||
[TestDefineEnum]
|
||||
[TestDoublyLinkedList]
|
||||
[TestDllInterceptor]
|
||||
|
Loading…
x
Reference in New Issue
Block a user