fune/browser/app/winlauncher/freestanding/SharedSection.h
Toshihito Kikuchi 7545ed9378 Bug 1684532 - Detect injected dependent modules in NtMapViewOfSection. r=mhowell
This patch is to improve the way to detect an injected dependent module for
automatic DLL blocking (bug 1659438).

In the previous version, we created a list of dependent modules in the launcher
process and shared it with other processes via the shared section.  However, it
was not compatible with third-party applications who tamper the Import Table and
revert it in the injected module's DllMain (bug 1682834) because we parsed the
Import Table in the launcher process after it was reverted.

With this patch, we check the Import Table in `patched_NtMapViewOfSection`,
so we can see tampering before it's reverted.  More specifically, we create
a list of dependent modules in the browser process as below.

1. The launcher process creates a section object and initializes
   the kernel32.dll's functions in it.

2. The launcher process transfers a writable handle of the shared
   section to the browser process.

3. In the browser process, if an injected dependent module is being
   mapped by `NtMapViewOfSection`, we add its NT path to the shared
   section and block it with `REDIRECT_TO_NOOP_ENTRYPOINT`.

4. The `main` function of the browser process converts the writable
   handle of the shared section into a readonly handle.

5. The browser process transfers a readonly handle of the shared
   section to a sandbox process.

Since automatic DLL blocking may still cause a compat issue like bug 1682304,
we activate it only in Nightly for now.

Differential Revision: https://phabricator.services.mozilla.com/D101460
2021-01-13 15:13:18 +00:00

118 lines
4.6 KiB
C++

/* -*- 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/. */
#ifndef mozilla_freestanding_SharedSection_h
#define mozilla_freestanding_SharedSection_h
#include "mozilla/NativeNt.h"
#include "mozilla/interceptor/MMPolicies.h"
namespace mozilla {
namespace freestanding {
// This class calculates RVAs of kernel32's functions and transfers them
// to a target process, where the transferred RVAs are resolved into
// function addresses so that the target process can use them after
// kernel32.dll is loaded and before IAT is resolved.
class MOZ_TRIVIAL_CTOR_DTOR Kernel32ExportsSolver final
: public interceptor::MMPolicyInProcessEarlyStage::Kernel32Exports {
enum class State {
Uninitialized,
Initialized,
Resolved,
} mState;
static ULONG NTAPI ResolveOnce(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
PVOID*);
void ResolveInternal();
public:
Kernel32ExportsSolver() = default;
Kernel32ExportsSolver(const Kernel32ExportsSolver&) = delete;
Kernel32ExportsSolver(Kernel32ExportsSolver&&) = delete;
Kernel32ExportsSolver& operator=(const Kernel32ExportsSolver&) = delete;
Kernel32ExportsSolver& operator=(Kernel32ExportsSolver&&) = delete;
bool IsInitialized() const;
bool IsResolved() const;
void Init();
void Resolve(RTL_RUN_ONCE& aRunOnce);
};
// This class manages a section which is created in the launcher process and
// mapped in the browser process and the sandboxed processes. The section's
// layout is represented as SharedSection::Layout.
//
// (1) Kernel32's functions required for MMPolicyInProcessEarlyStage
// Formatted as Kernel32ExportsSolver.
//
// (2) Array of NT paths of the executable's dependent modules
// Formatted as a null-delimited wide-character string set ending with
// an empty string.
//
// +--------------------------------------------------------------+
// | (1) | FlushInstructionCache |
// | | GetModuleHandleW |
// | | GetSystemInfo |
// | | VirtualProtect |
// | | State [Uninitialized|Initialized|Resolved] |
// +--------------------------------------------------------------+
// | (2) | L"NT path 1" |
// | | L"NT path 2" |
// | | ... |
// | | L"" |
// +--------------------------------------------------------------+
class MOZ_TRIVIAL_CTOR_DTOR SharedSection final {
// As we define a global variable of this class and use it in our blocklist
// which is excuted in a process's early stage. If we have a complex dtor,
// the static initializer tries to register that dtor with onexit() of
// ucrtbase.dll which is not loaded yet, resulting in crash. Thus, we have
// a raw handle and a pointer as a static variable and manually release them
// by calling Reset() where possible.
static HANDLE sSectionHandle;
static void* sWriteCopyView;
static constexpr size_t kSharedViewSize = 0x1000;
public:
struct Layout final {
Kernel32ExportsSolver mK32Exports;
wchar_t mModulePathArray[1];
Layout() = delete; // disallow instantiation
};
// Replace |sSectionHandle| with a given handle.
static void Reset(HANDLE aNewSecionObject = sSectionHandle);
// Replace |sSectionHandle| with a new readonly handle.
static void ConvertToReadOnly();
// Create a new writable section and initialize the Kernel32ExportsSolver
// part.
static LauncherVoidResult Init(const nt::PEHeaders& aPEHeaders);
// Append a new string to the |sSectionHandle|
static LauncherVoidResult AddDepenentModule(PCUNICODE_STRING aNtPath);
// Map |sSectionHandle| to a copy-on-write page and return its address.
static LauncherResult<Layout*> GetView();
// Transfer |sSectionHandle| to a process associated with |aTransferMgr|.
static LauncherVoidResult TransferHandle(
nt::CrossExecTransferManager& aTransferMgr, DWORD aDesiredAccess,
HANDLE* aDestinationAddress = &sSectionHandle);
};
extern SharedSection gSharedSection;
extern RTL_RUN_ONCE gK32ExportsResolveOnce;
} // namespace freestanding
} // namespace mozilla
#endif // mozilla_freestanding_SharedSection_h