Backed out 8 changesets (bug 1603974) for causing build bustage

CLOSED TREE

Backed out changeset ee3fb8271709 (bug 1603974)
Backed out changeset 28ef741f8f65 (bug 1603974)
Backed out changeset 631725404fb8 (bug 1603974)
Backed out changeset 484a45d16149 (bug 1603974)
Backed out changeset 5d4cd3237ec0 (bug 1603974)
Backed out changeset c2601b5bdd3e (bug 1603974)
Backed out changeset fe96d48d5b14 (bug 1603974)
Backed out changeset 9467dffe8d04 (bug 1603974)
This commit is contained in:
Daniel Varga 2020-04-07 18:35:04 +03:00
parent 64d557e45a
commit 2617f15d0c
21 changed files with 197 additions and 833 deletions

View File

@ -16,7 +16,6 @@
#include "DllBlocklistInit.h"
#include "freestanding/DllBlocklist.h"
#include "freestanding/FunctionTableResolver.h"
#if defined(_MSC_VER)
extern "C" IMAGE_DOS_HEADER __ImageBase;
@ -43,11 +42,6 @@ LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPFromLauncher(
static LauncherVoidResultWithLineInfo InitializeDllBlocklistOOPInternal(
const wchar_t* aFullImagePath, HANDLE aChildProcess) {
freestanding::gK32.Init();
if (freestanding::gK32.IsInitialized()) {
freestanding::gK32.Transfer(aChildProcess, &freestanding::gK32);
}
CrossProcessDllInterceptor intcpt(aChildProcess);
intcpt.Init(L"ntdll.dll");

View File

@ -12,7 +12,6 @@
#include "mozilla/WindowsDllBlocklist.h"
#include "DllBlocklist.h"
#include "FunctionTableResolver.h"
#include "LoaderPrivateAPI.h"
#include "ModuleLoadFrame.h"
@ -40,6 +39,8 @@ DLL_BLOCKLIST_DEFINITIONS_BEGIN
DLL_BLOCKLIST_DEFINITIONS_END
#endif
static const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
class MOZ_STATIC_CLASS MOZ_TRIVIAL_CTOR_DTOR NativeNtBlockSet final {
struct NativeNtBlockSetEntry {
NativeNtBlockSetEntry() = default;
@ -155,11 +156,9 @@ enum class BlockAction {
SubstituteLSP,
Error,
Deny,
NoOpEntryPoint,
};
static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo,
const mozilla::nt::PEHeaders& aHeaders,
static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo, void* aBaseAddress,
uint64_t& aVersion) {
aVersion = DllBlockInfo::ALL_VERSIONS;
@ -196,13 +195,14 @@ static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo,
return BlockAction::Deny;
}
if (!aHeaders) {
mozilla::nt::PEHeaders headers(aBaseAddress);
if (!headers) {
return BlockAction::Error;
}
if (aInfo->mFlags & DllBlockInfo::USE_TIMESTAMP) {
DWORD timestamp;
if (!aHeaders.GetTimeStamp(timestamp)) {
if (!headers.GetTimeStamp(timestamp)) {
return BlockAction::Error;
}
@ -215,7 +215,7 @@ static BlockAction CheckBlockInfo(const DllBlockInfo* aInfo,
// Else we try to get the file version information. Note that we don't have
// access to GetFileVersionInfo* APIs.
if (!aHeaders.GetVersionInfo(aVersion)) {
if (!headers.GetVersionInfo(aVersion)) {
return BlockAction::Error;
}
@ -238,10 +238,8 @@ struct DllBlockInfoComparator {
PCUNICODE_STRING mTarget;
};
static BOOL WINAPI NoOp_DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
static BlockAction DetermineBlockAction(const UNICODE_STRING& aLeafName,
void* aBaseAddress) {
static BlockAction IsDllAllowed(const UNICODE_STRING& aLeafName,
void* aBaseAddress) {
if (mozilla::nt::Contains12DigitHexString(aLeafName) ||
mozilla::nt::IsFileNameAtLeast16HexDigits(aLeafName)) {
return BlockAction::Deny;
@ -259,37 +257,10 @@ static BlockAction DetermineBlockAction(const UNICODE_STRING& aLeafName,
const DllBlockInfo& entry = info[match];
mozilla::nt::PEHeaders headers(aBaseAddress);
uint64_t version;
BlockAction checkResult = CheckBlockInfo(&entry, headers, version);
if (checkResult == BlockAction::Allow) {
return checkResult;
}
gBlockSet.Add(entry.mName, version);
if (entry.mFlags & DllBlockInfo::REDIRECT_TO_NOOP_ENTRYPOINT) {
// For modules with REDIRECT_TO_NOOP_ENTRYPOINT, we allow a module to be
// loaded but detour the entrypoint to NoOp_DllMain so that the module has
// no chance to interact with our code. We need this technique to safely
// block a module injected by IAT tampering because blocking such a module
// makes a process fail to launch.
// If we fail to detour a module's entrypoint, we reluctantly allow the
// module for free.
static RTL_RUN_ONCE sRunOnce = RTL_RUN_ONCE_INIT;
mozilla::freestanding::gK32.Resolve(sRunOnce);
if (!mozilla::freestanding::gK32.IsResolved()) {
return BlockAction::Allow;
}
mozilla::interceptor::WindowsDllEntryPointInterceptor interceptor(
mozilla::freestanding::gK32);
if (!interceptor.Set(headers, NoOp_DllMain)) {
return BlockAction::Allow;
}
return BlockAction::NoOpEntryPoint;
BlockAction checkResult = CheckBlockInfo(&entry, aBaseAddress, version);
if (checkResult != BlockAction::Allow) {
gBlockSet.Add(entry.mName, version);
}
return checkResult;
@ -326,7 +297,7 @@ NTSTATUS NTAPI patched_NtMapViewOfSection(
return stubStatus;
}
if (aProcess != nt::kCurrentProcess) {
if (aProcess != kCurrentProcess) {
// We're only interested in mapping for the current process.
return stubStatus;
}
@ -359,7 +330,7 @@ NTSTATUS NTAPI patched_NtMapViewOfSection(
nt::GetLeafName(&leafOnStack, sectionFileName);
// Check blocklist
BlockAction blockAction = DetermineBlockAction(leafOnStack, *aBaseAddress);
BlockAction blockAction = IsDllAllowed(leafOnStack, *aBaseAddress);
if (blockAction == BlockAction::Allow) {
if (nt::RtlGetProcessHeap()) {
@ -382,10 +353,6 @@ NTSTATUS NTAPI patched_NtMapViewOfSection(
ModuleLoadFrame::NotifyLSPSubstitutionRequired(&leafOnStack);
}
if (blockAction == BlockAction::NoOpEntryPoint) {
return stubStatus;
}
::NtUnmapViewOfSection(aProcess, *aBaseAddress);
return STATUS_ACCESS_DENIED;
}

View File

@ -1,113 +0,0 @@
/* -*- 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/. */
#include "FunctionTableResolver.h"
namespace mozilla {
namespace freestanding {
Kernel32ExportsSolver gK32;
bool Kernel32ExportsSolver::IsInitialized() const {
return mState == State::Initialized || IsResolved();
}
bool Kernel32ExportsSolver::IsResolved() const {
return mState == State::Resolved;
}
// Why don't we use ::GetProcAddress?
// If the export table of kernel32.dll is tampered in the current process,
// we cannot transfer an RVA because the function pointed by the RVA may not
// exist in a target process.
// We can use ::GetProcAddress with additional check to detect tampering, but
// FindExportAddressTableEntry fits perfectly here because it returns nullptr
// if the target entry is outside the image, which means it's tampered or
// forwarded to another DLL.
#define INIT_FUNCTION(exports, name) \
do { \
auto rvaToFunction = exports.FindExportAddressTableEntry(#name); \
if (!rvaToFunction) { \
return; \
} \
mOffsets.m##name = *rvaToFunction; \
} while (0)
#define RESOLVE_FUNCTION(base, name) \
m##name = reinterpret_cast<decltype(m##name)>(base + mOffsets.m##name)
void Kernel32ExportsSolver::Init() {
if (mState == State::Initialized || mState == State::Resolved) {
return;
}
interceptor::MMPolicyInProcess policy;
auto k32Exports = nt::PEExportSection<interceptor::MMPolicyInProcess>::Get(
::GetModuleHandleW(L"kernel32.dll"), policy);
if (!k32Exports) {
return;
}
// Please make sure these functions are not forwarded to another DLL.
INIT_FUNCTION(k32Exports, FlushInstructionCache);
INIT_FUNCTION(k32Exports, GetSystemInfo);
INIT_FUNCTION(k32Exports, VirtualProtect);
mState = State::Initialized;
}
void Kernel32ExportsSolver::ResolveInternal() {
if (mState == State::Resolved) {
return;
}
MOZ_RELEASE_ASSERT(mState == State::Initialized);
UNICODE_STRING k32Name;
::RtlInitUnicodeString(&k32Name, L"kernel32.dll");
// We cannot use GetModuleHandleW because this code can be called
// before IAT is resolved.
auto k32Module = nt::GetModuleHandleFromLeafName(k32Name);
MOZ_RELEASE_ASSERT(k32Module.isOk());
uintptr_t k32Base =
nt::PEHeaders::HModuleToBaseAddr<uintptr_t>(k32Module.unwrap());
RESOLVE_FUNCTION(k32Base, FlushInstructionCache);
RESOLVE_FUNCTION(k32Base, GetSystemInfo);
RESOLVE_FUNCTION(k32Base, VirtualProtect);
mState = State::Resolved;
}
/* static */
ULONG NTAPI Kernel32ExportsSolver::ResolveOnce(PRTL_RUN_ONCE aRunOnce,
PVOID aParameter, PVOID*) {
reinterpret_cast<Kernel32ExportsSolver*>(aParameter)->ResolveInternal();
return TRUE;
}
void Kernel32ExportsSolver::Resolve(RTL_RUN_ONCE& aRunOnce) {
::RtlRunOnceExecuteOnce(&aRunOnce, &ResolveOnce, this, nullptr);
}
void Kernel32ExportsSolver::Transfer(
HANDLE aTargetProcess, Kernel32ExportsSolver* aTargetAddress) const {
SIZE_T bytesWritten = 0;
BOOL ok = ::WriteProcessMemory(aTargetProcess, &aTargetAddress->mOffsets,
&mOffsets, sizeof(mOffsets), &bytesWritten);
if (!ok) {
return;
}
State stateInChild = State::Initialized;
::WriteProcessMemory(aTargetProcess, &aTargetAddress->mState, &stateInChild,
sizeof(stateInChild), &bytesWritten);
}
} // namespace freestanding
} // namespace mozilla

View File

@ -1,60 +0,0 @@
/* -*- 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_FunctionTableResolver_h
#define mozilla_freestanding_FunctionTableResolver_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;
struct FunctionOffsets {
uint32_t mFlushInstructionCache;
uint32_t mGetSystemInfo;
uint32_t mVirtualProtect;
} mOffsets;
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);
void Transfer(HANDLE aTargetProcess,
Kernel32ExportsSolver* aTargetAddress) const;
};
extern Kernel32ExportsSolver gK32;
} // namespace freestanding
} // namespace mozilla
#endif // mozilla_freestanding_FunctionTableResolver_h

View File

@ -15,7 +15,6 @@ NO_PGO = True
UNIFIED_SOURCES += [
'DllBlocklist.cpp',
'FunctionTableResolver.cpp',
'LoaderPrivateAPI.cpp',
'ModuleLoadFrame.cpp',
]

View File

@ -48,10 +48,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'winmm.dll',
'user32.dll',
]
OS_LIBS += [
'ntdll',
]
DELAYLOAD_DLLS += [
'xul.dll',

View File

@ -50,10 +50,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'user32.dll',
]
OS_LIBS += [
'ntdll',
]
DELAYLOAD_DLLS += [
'xul.dll',
]

View File

@ -41,7 +41,6 @@ struct DllBlockInfoT {
USE_TIMESTAMP = 1 << 2,
CHILD_PROCESSES_ONLY = 1 << 3,
BROWSER_PROCESS_ONLY = 1 << 4,
REDIRECT_TO_NOOP_ENTRYPOINT = 1 << 5,
} mFlags;
bool IsVersionBlocked(const uint64_t aOther) const {

View File

@ -15,8 +15,7 @@
# DLL block.
#
# The currently supported blocklist entry types are:
# DllBlocklistEntry, A11yBlocklistEntry, LspBlocklistEntry,
# RedirectToNoOpEntryPoint
# DllBlocklistEntry, A11yBlocklistEntry, LspBlocklistEntry
# (See gen_dll_blocklist_defs.py for their documentation.)
#
# Example:
@ -227,9 +226,9 @@ ALL_PROCESSES += [
DllBlocklistEntry("pdzipmenu64.dll", UNVERSIONED),
DllBlocklistEntry("pdzipmenu32.dll", UNVERSIONED),
# Old versions of Digital Guardian, bug 1318858 and bug 1603974
RedirectToNoOpEntryPoint("dgapi.dll", (7, 5, 0, 0xffff)),
RedirectToNoOpEntryPoint("dgapi64.dll", (7, 5, 0, 0xffff)),
# Old versions of Digital Guardian, bug 1318858
DllBlocklistEntry("dgapi.dll", (7, 5, 0, 0xffff)),
DllBlocklistEntry("dgapi64.dll", (7, 5, 0, 0xffff)),
# Old versions of COMODO Internet Security, bug 1608048
DllBlocklistEntry("IseGuard32.dll", (1, 6, 13835, 184)),
@ -244,8 +243,6 @@ ALL_PROCESSES_TESTS += [
DllBlocklistEntry("testdllblocklist_matchbyname.dll", ALL_VERSIONS),
DllBlocklistEntry("testdllblocklist_matchbyversion.dll", (5, 5, 5, 5)),
DllBlocklistEntry("testdllblocklist_allowbyversion.dll", (5, 5, 5, 5)),
RedirectToNoOpEntryPoint("testdllblocklist_noopentrypoint.dll",
(5, 5, 5, 5)),
]
BROWSER_PROCESS += [

View File

@ -61,7 +61,6 @@ USE_TIMESTAMP = 'USE_TIMESTAMP'
CHILD_PROCESSES_ONLY = 'CHILD_PROCESSES_ONLY'
BROWSER_PROCESS_ONLY = 'BROWSER_PROCESS_ONLY'
SUBSTITUTE_LSP_PASSTHROUGH = 'SUBSTITUTE_LSP_PASSTHROUGH'
REDIRECT_TO_NOOP_ENTRYPOINT = 'REDIRECT_TO_NOOP_ENTRYPOINT'
# Only these flags are available in the input script
INPUT_ONLY_FLAGS = {
@ -566,27 +565,6 @@ class A11yBlocklistEntry(DllBlocklistEntry):
super(A11yBlocklistEntry, self).__init__(name, ver, flags, **kwargs)
class RedirectToNoOpEntryPoint(DllBlocklistEntry):
""" Represents a blocklist entry to hook the entrypoint into a function
just returning TRUE to keep a module alive and harmless.
This entry is intended to block a DLL which is injected by IAT patching
which is planted by a kernel callback routine for LoadImage because
blocking such a DLL makes a process fail to launch.
"""
def __init__(self, name, ver, flags=(), **kwargs):
"""These arguments are identical to DllBlocklistEntry.__init__
"""
super(RedirectToNoOpEntryPoint, self).__init__(name, ver, flags, **kwargs)
def get_flags_list(self):
flags = super(RedirectToNoOpEntryPoint, self).get_flags_list()
# RedirectToNoOpEntryPoint items always include the following flag
flags.add(REDIRECT_TO_NOOP_ENTRYPOINT)
return flags
class LspBlocklistEntry(DllBlocklistEntry):
""" Represents a blocklist entry for a WinSock Layered Service Provider (LSP).
"""
@ -683,7 +661,6 @@ def gen_blocklists(first_fd, defs_filename):
'A11yBlocklistEntry': A11yBlocklistEntry,
'DllBlocklistEntry': DllBlocklistEntry,
'LspBlocklistEntry': LspBlocklistEntry,
'RedirectToNoOpEntryPoint': RedirectToNoOpEntryPoint,
# Add the special version types
'ALL_VERSIONS': Version.ALL_VERSIONS,
'UNVERSIONED': Version.UNVERSIONED,

View File

@ -89,9 +89,6 @@ VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock);
VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
VOID NTAPI RtlSetLastWin32Error(DWORD aError);
NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
PVOID aBuffer, SIZE_T aNumBytesToRead,
PSIZE_T aNumBytesRead);
@ -548,7 +545,7 @@ class MOZ_RAII PEHeaders final {
IMAGE_DIRECTORY_ENTRY_IMPORT);
}
PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const {
PIMAGE_RESOURCE_DIRECTORY GetResourceTable() {
return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
IMAGE_DIRECTORY_ENTRY_RESOURCE);
}
@ -578,7 +575,7 @@ class MOZ_RAII PEHeaders final {
return dirEntry;
}
bool GetVersionInfo(uint64_t& aOutVersion) const {
bool GetVersionInfo(uint64_t& aOutVersion) {
// RT_VERSION == 16
// Version resources require an id of 1
auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
@ -596,7 +593,7 @@ class MOZ_RAII PEHeaders final {
return true;
}
bool GetTimeStamp(DWORD& aResult) const {
bool GetTimeStamp(DWORD& aResult) {
if (!(*this)) {
return false;
}
@ -670,7 +667,7 @@ class MOZ_RAII PEHeaders final {
* If aLangId == 0, we just resolve the first entry regardless of language.
*/
template <typename T>
T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) {
PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
if (!topLevel) {
return nullptr;
@ -764,17 +761,11 @@ class MOZ_RAII PEHeaders final {
void SetImportDirectoryTampered() { mIsImportDirectoryTampered = true; }
FARPROC GetEntryPoint() const {
// Use the unchecked version because the entrypoint may be tampered.
return RVAToPtrUnchecked<FARPROC>(
mPeHeader->OptionalHeader.AddressOfEntryPoint);
}
private:
enum class BoundsCheckPolicy { Default, Skip };
template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) {
PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
if (!dirEntry) {
return nullptr;
@ -803,7 +794,7 @@ class MOZ_RAII PEHeaders final {
}
PIMAGE_RESOURCE_DIRECTORY_ENTRY
FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) {
// Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
// of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
// searches by ID, we need to skip past any named entries before iterating.
@ -820,7 +811,7 @@ class MOZ_RAII PEHeaders final {
}
PIMAGE_RESOURCE_DIRECTORY_ENTRY
FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) {
// Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
// of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
// entry, regardless of whether it is indexed by name or by id.
@ -835,7 +826,7 @@ class MOZ_RAII PEHeaders final {
return dirEnt;
}
VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) {
WORD length = aVerInfo->wLength;
if (length < sizeof(VS_VERSIONINFO_HEADER)) {
return nullptr;
@ -1108,8 +1099,6 @@ inline DWORD RtlGetCurrentThreadId() {
0xFFFFFFFFUL);
}
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
inline LauncherResult<DWORD> GetParentProcessId() {
struct PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
@ -1120,6 +1109,7 @@ inline LauncherResult<DWORD> GetParentProcessId() {
ULONG_PTR InheritedFromUniqueProcessId;
};
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
ULONG returnLength;
PROCESS_BASIC_INFORMATION pbi = {};
NTSTATUS status =
@ -1132,30 +1122,6 @@ inline LauncherResult<DWORD> GetParentProcessId() {
return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
}
inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen) {
#if defined(MOZILLA_INTERNAL_API)
return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
#else
SIZE_T returnedLength;
NTSTATUS status = ::NtQueryVirtualMemory(
aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
aMemInfoLen, &returnedLength);
if (!NT_SUCCESS(status)) {
::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
returnedLength = 0;
}
return returnedLength;
#endif // defined(MOZILLA_INTERNAL_API)
}
inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen) {
return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
}
struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
@ -1243,34 +1209,6 @@ inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
#if !defined(MOZILLA_INTERNAL_API)
inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
const UNICODE_STRING& aTarget) {
auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
if (maybePeb.isErr()) {
return LAUNCHER_ERROR_FROM_RESULT(maybePeb);
}
const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
if (!peb->Ldr) {
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
}
auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
const auto currentTableEntry =
CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
UNICODE_STRING leafName;
nt::GetLeafName(&leafName, &currentTableEntry->FullDllName);
if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
}
}
return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
public:
constexpr SRWLock() : mLock(SRWLOCK_INIT) {}

View File

@ -74,64 +74,9 @@ PVOID WINAPI MapViewOfFile3(HANDLE FileMapping, HANDLE Process,
extern "C" errno_t rand_s(unsigned int* randomValue);
#endif // !defined(_CRT_RAND_S)
// Declaring only the functions we need in NativeNt.h. To include the entire
// NativeNt.h causes circular dependency.
namespace mozilla {
namespace nt {
SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen);
SIZE_T WINAPI VirtualQuery(LPCVOID aAddress, PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen);
} // namespace nt
} // namespace mozilla
namespace mozilla {
namespace interceptor {
// This class implements memory operations not involving any kernel32's
// functions, so that derived classes can use them.
class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcessPrimitive {
protected:
bool ProtectInternal(decltype(&::VirtualProtect) aVirtualProtect,
void* aVAddress, size_t aSize, uint32_t aProtFlags,
uint32_t* aPrevProtFlags) const {
MOZ_ASSERT(aPrevProtFlags);
BOOL ok = aVirtualProtect(aVAddress, aSize, aProtFlags,
reinterpret_cast<PDWORD>(aPrevProtFlags));
if (!ok && aPrevProtFlags) {
// VirtualProtect can fail but still set valid protection flags.
// Let's clear those upon failure.
*aPrevProtFlags = 0;
}
return !!ok;
}
public:
bool Read(void* aToPtr, const void* aFromPtr, size_t aLen) const {
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}
bool Write(void* aToPtr, const void* aFromPtr, size_t aLen) const {
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}
/**
* @return true if the page that hosts aVAddress is accessible.
*/
bool IsPageAccessible(void* aVAddress) const {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = nt::VirtualQuery(aVAddress, &mbi, sizeof(mbi));
return result && mbi.AllocationProtect && (mbi.Type & MEM_IMAGE) &&
mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS;
}
};
class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
protected:
static uintptr_t AlignDown(const uintptr_t aUnaligned,
@ -149,7 +94,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
}
public:
DWORD ComputeAllocationSize(const uint32_t aRequestedSize) const {
static DWORD ComputeAllocationSize(const uint32_t aRequestedSize) {
MOZ_ASSERT(aRequestedSize);
DWORD result = aRequestedSize;
@ -163,7 +108,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
return result;
}
DWORD GetAllocGranularity() const {
static DWORD GetAllocGranularity() {
static const DWORD kAllocGranularity = []() -> DWORD {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
@ -173,7 +118,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
return kAllocGranularity;
}
DWORD GetPageSize() const {
static DWORD GetPageSize() {
static const DWORD kPageSize = []() -> DWORD {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
@ -183,7 +128,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
return kPageSize;
}
uintptr_t GetMaxUserModeAddress() const {
static uintptr_t GetMaxUserModeAddress() {
static const uintptr_t kMaxUserModeAddr = []() -> uintptr_t {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
@ -216,9 +161,9 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
* lower and upper bounds. This function converts from the former format to
* the latter format.
*/
Maybe<Span<const uint8_t>> SpanFromPivotAndDistance(
static Maybe<Span<const uint8_t>> SpanFromPivotAndDistance(
const uint32_t aSize, const uintptr_t aPivotAddr,
const uint32_t aMaxDistanceFromPivot) const {
const uint32_t aMaxDistanceFromPivot) {
if (!aPivotAddr || !aMaxDistanceFromPivot) {
return Nothing();
}
@ -295,8 +240,8 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
* virtual memory space for a block of unallocated memory that is sufficiently
* large.
*/
PVOID FindRegion(HANDLE aProcess, const size_t aDesiredBytesLen,
const uint8_t* aRangeMin, const uint8_t* aRangeMax) const {
static PVOID FindRegion(HANDLE aProcess, const size_t aDesiredBytesLen,
const uint8_t* aRangeMin, const uint8_t* aRangeMax) {
const DWORD kGranularity = GetAllocGranularity();
MOZ_ASSERT(aDesiredBytesLen >= kGranularity);
if (!aDesiredBytesLen) {
@ -331,7 +276,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
// Scan the range for a free chunk that is at least as large as
// aDesiredBytesLen
while (address <= kMaxPtr &&
nt::VirtualQueryEx(aProcess, address, &mbi, len)) {
::VirtualQueryEx(aProcess, address, &mbi, len)) {
if (mbi.State == MEM_FREE && mbi.RegionSize >= aDesiredBytesLen) {
return mbi.BaseAddress;
}
@ -359,10 +304,10 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
* to use for reserving the memory by calling |FindRegion|.
*/
template <typename ReserveFnT, typename ReserveRangeFnT>
PVOID Reserve(HANDLE aProcess, const uint32_t aSize,
const ReserveFnT& aReserveFn,
const ReserveRangeFnT& aReserveRangeFn,
const Maybe<Span<const uint8_t>>& aBounds) const {
static PVOID Reserve(HANDLE aProcess, const uint32_t aSize,
const ReserveFnT& aReserveFn,
const ReserveRangeFnT& aReserveRangeFn,
const Maybe<Span<const uint8_t>>& aBounds) {
if (!aBounds) {
// No restrictions, let the OS choose the base address
return aReserveFn(aProcess, nullptr, aSize);
@ -408,9 +353,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
}
};
class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess
: public MMPolicyInProcessPrimitive,
public MMPolicyBase {
class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess : public MMPolicyBase {
public:
typedef MMPolicyInProcess MMPolicyT;
@ -445,6 +388,16 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess
*/
bool ShouldUnhookUponDestruction() const { return true; }
bool Read(void* aToPtr, const void* aFromPtr, size_t aLen) const {
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}
bool Write(void* aToPtr, const void* aFromPtr, size_t aLen) const {
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}
#if defined(_M_IX86)
bool WriteAtomic(void* aDestPtr, const uint16_t aValue) const {
*static_cast<uint16_t*>(aDestPtr) = aValue;
@ -454,8 +407,27 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess
bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags,
uint32_t* aPrevProtFlags) const {
return ProtectInternal(::VirtualProtect, aVAddress, aSize, aProtFlags,
aPrevProtFlags);
MOZ_ASSERT(aPrevProtFlags);
BOOL ok = ::VirtualProtect(aVAddress, aSize, aProtFlags,
reinterpret_cast<PDWORD>(aPrevProtFlags));
if (!ok && aPrevProtFlags) {
// VirtualProtect can fail but still set valid protection flags.
// Let's clear those upon failure.
*aPrevProtFlags = 0;
}
return !!ok;
}
/**
* @return true if the page that hosts aVAddress is accessible.
*/
bool IsPageAccessible(void* aVAddress) const {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = ::VirtualQuery(aVAddress, &mbi, sizeof(mbi));
return result && mbi.AllocationProtect && (mbi.Type & MEM_IMAGE) &&
mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS;
}
bool FlushInstructionCache() const {
@ -566,51 +538,6 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess
uint32_t mCommitOffset;
};
// This class manages in-process memory access without using functions
// imported from kernel32.dll. Instead, it uses functions in its own
// function table that are provided from outside.
class MMPolicyInProcessEarlyStage : public MMPolicyInProcessPrimitive {
public:
struct Kernel32Exports {
decltype(&::FlushInstructionCache) mFlushInstructionCache;
decltype(&::GetSystemInfo) mGetSystemInfo;
decltype(&::VirtualProtect) mVirtualProtect;
};
private:
static DWORD GetPageSize(const Kernel32Exports& aK32Exports) {
SYSTEM_INFO sysInfo;
aK32Exports.mGetSystemInfo(&sysInfo);
return sysInfo.dwPageSize;
}
const Kernel32Exports& mK32Exports;
const DWORD mPageSize;
public:
explicit MMPolicyInProcessEarlyStage(const Kernel32Exports& aK32Exports)
: mK32Exports(aK32Exports), mPageSize(GetPageSize(mK32Exports)) {}
// The pattern of constructing a local static variable with a lambda,
// which can be seen in MMPolicyBase, is compiled into code with the
// critical section APIs like EnterCriticalSection imported from kernel32.dll.
// Because this class needs to be able to run in a process's early stage
// when IAT is not yet resolved, we cannot use that patten, thus simply
// caching a value as a local member in the class.
DWORD GetPageSize() const { return mPageSize; }
bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags,
uint32_t* aPrevProtFlags) const {
return ProtectInternal(mK32Exports.mVirtualProtect, aVAddress, aSize,
aProtFlags, aPrevProtFlags);
}
bool FlushInstructionCache() const {
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
return !!mK32Exports.mFlushInstructionCache(kCurrentProcess, nullptr, 0);
}
};
class MMPolicyOutOfProcess : public MMPolicyBase {
public:
typedef MMPolicyOutOfProcess MMPolicyT;
@ -769,7 +696,7 @@ class MMPolicyOutOfProcess : public MMPolicyBase {
*/
bool IsPageAccessible(void* aVAddress) const {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = nt::VirtualQueryEx(mProcess, aVAddress, &mbi, sizeof(mbi));
SIZE_T result = ::VirtualQueryEx(mProcess, aVAddress, &mbi, sizeof(mbi));
return result && mbi.AllocationProtect && (mbi.Type & MEM_IMAGE) &&
mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS;

View File

@ -40,85 +40,10 @@ enum class DetourFlags : uint32_t {
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DetourFlags)
// This class is responsible to do tasks which depend on MMPolicy, decoupled
// from VMPolicy. We already have WindowsDllPatcherBase, but it needs to
// depend on VMPolicy to hold an instance of VMPolicy as a member.
template <typename MMPolicyT>
class WindowsDllDetourPatcherPrimitive {
protected:
constexpr static uint32_t GetWorstCaseRequiredBytesToPatch() {
#if defined(_M_IX86)
return 5;
#elif defined(_M_X64)
return 13;
#elif defined(_M_ARM64)
return 16;
#else
# error "Unsupported processor architecture"
#endif
}
static void ApplyDefaultPatch(WritableTargetFunction<MMPolicyT>& target,
intptr_t aDest) {
#if defined(_M_IX86)
target.WriteByte(0xe9); // jmp
target.WriteDisp32(aDest); // hook displacement
#elif defined(_M_X64)
// mov r11, address
target.WriteByte(0x49);
target.WriteByte(0xbb);
target.WritePointer(aDest);
// jmp r11
target.WriteByte(0x41);
target.WriteByte(0xff);
target.WriteByte(0xe3);
#elif defined(_M_ARM64)
// The default patch requires 16 bytes
// LDR x16, .+8
target.WriteLong(kLdrX16Plus8);
// BR x16
target.WriteLong(arm64::BuildUnconditionalBranchToRegister(16));
target.WritePointer(aDest);
#else
# error "Unsupported processor architecture"
#endif
}
public:
WindowsDllDetourPatcherPrimitive() = default;
WindowsDllDetourPatcherPrimitive(const WindowsDllDetourPatcherPrimitive&) =
delete;
WindowsDllDetourPatcherPrimitive(WindowsDllDetourPatcherPrimitive&&) = delete;
WindowsDllDetourPatcherPrimitive& operator=(
const WindowsDllDetourPatcherPrimitive&) = delete;
WindowsDllDetourPatcherPrimitive& operator=(
WindowsDllDetourPatcherPrimitive&&) = delete;
bool AddIrreversibleHook(const MMPolicyT& aMMPolicy, FARPROC aTargetFn,
intptr_t aHookDest) {
ReadOnlyTargetFunction<MMPolicyT> targetReadOnly(aMMPolicy, aTargetFn);
WritableTargetFunction<MMPolicyT> targetWritable(
targetReadOnly.Promote(GetWorstCaseRequiredBytesToPatch()));
if (!targetWritable) {
return false;
}
ApplyDefaultPatch(targetWritable, aHookDest);
return targetWritable.Commit();
}
};
template <typename VMPolicy>
class WindowsDllDetourPatcher final
: public WindowsDllDetourPatcherPrimitive<typename VMPolicy::MMPolicyT>,
public WindowsDllPatcherBase<VMPolicy> {
using MMPolicyT = typename VMPolicy::MMPolicyT;
using TrampPoolT = typename VMPolicy::PoolType;
using PrimitiveT = WindowsDllDetourPatcherPrimitive<MMPolicyT>;
class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
typedef typename VMPolicy::MMPolicyT MMPolicyT;
typedef typename VMPolicy::PoolType TrampPoolT;
Maybe<DetourFlags> mFlags;
#if defined(_M_ARM64)
@ -784,88 +709,6 @@ class WindowsDllDetourPatcher final
return false;
}
#if defined(_M_ARM64)
bool Apply4BytePatch(TrampPoolT* aTrampPool, void* aTrampPtr,
WritableTargetFunction<MMPolicyT>& target,
intptr_t aDest) {
MOZ_ASSERT(aTrampPool);
if (!aTrampPool) {
return false;
}
uintptr_t hookDest = arm64::MakeVeneer(*aTrampPool, aTrampPtr, aDest);
if (!hookDest) {
return false;
}
Maybe<uint32_t> branchImm = arm64::BuildUnconditionalBranchImm(
target.GetCurrentAddress(), hookDest);
if (!branchImm) {
return false;
}
target.WriteLong(branchImm.value());
return true;
}
#endif // defined(_M_ARM64)
#if defined(_M_X64)
bool Apply10BytePatch(TrampPoolT* aTrampPool, void* aTrampPtr,
WritableTargetFunction<MMPolicyT>& target,
intptr_t aDest) {
// Note: Even if the target function is also below 2GB, we still use an
// intermediary trampoline so that we consistently have a 64-bit pointer
// that we can use to reset the trampoline upon interceptor shutdown.
Maybe<Trampoline<MMPolicyT>> maybeCallTramp(
aTrampPool->GetNextTrampoline());
if (!maybeCallTramp) {
return false;
}
Trampoline<MMPolicyT> callTramp(std::move(maybeCallTramp.ref()));
// Write a null instance so that Clear() does not consider this tramp to
// be a normal tramp to be torn down.
callTramp.WriteEncodedPointer(nullptr);
// Use the second pointer slot to store a pointer to the primary tramp
callTramp.WriteEncodedPointer(aTrampPtr);
callTramp.StartExecutableCode();
// mov r11, address
callTramp.WriteByte(0x49);
callTramp.WriteByte(0xbb);
callTramp.WritePointer(aDest);
// jmp r11
callTramp.WriteByte(0x41);
callTramp.WriteByte(0xff);
callTramp.WriteByte(0xe3);
void* callTrampStart = callTramp.EndExecutableCode();
if (!callTrampStart) {
return false;
}
target.WriteByte(0xB8); // MOV EAX, IMM32
// Assert that the topmost 33 bits are 0
MOZ_ASSERT(
!(reinterpret_cast<uintptr_t>(callTrampStart) & (~0x7FFFFFFFULL)));
target.WriteLong(static_cast<uint32_t>(
reinterpret_cast<uintptr_t>(callTrampStart) & 0x7FFFFFFFU));
target.WriteByte(0x48); // REX.W
target.WriteByte(0x63); // MOVSXD r64, r/m32
// dest: rax, src: eax
target.WriteByte(BuildModRmByte(kModReg, kRegAx, kRegAx));
target.WriteByte(0xFF); // JMP /4
target.WriteByte(BuildModRmByte(kModReg, 4, kRegAx)); // rax
return true;
}
#endif // defined(_M_X64)
void CreateTrampoline(ReadOnlyTargetFunction<MMPolicyT>& origBytes,
TrampPoolT* aTrampPool, Trampoline<MMPolicyT>& aTramp,
intptr_t aDest, void** aOutTramp) {
@ -912,12 +755,9 @@ class WindowsDllDetourPatcher final
tramp.StartExecutableCode();
constexpr uint32_t kWorstCaseBytesRequired =
PrimitiveT::GetWorstCaseRequiredBytesToPatch();
#if defined(_M_IX86)
int pJmp32 = -1;
while (origBytes.GetOffset() < kWorstCaseBytesRequired) {
while (origBytes.GetOffset() < 5) {
// Understand some simple instructions that might be found in a
// prologue; we might need to extend this as necessary.
//
@ -1026,8 +866,7 @@ class WindowsDllDetourPatcher final
bool use10BytePatch =
(mFlags.value() & DetourFlags::eTestOnlyForceShortPatch) ==
DetourFlags::eTestOnlyForceShortPatch;
const uint32_t bytesRequired =
use10BytePatch ? 10 : kWorstCaseBytesRequired;
const uint32_t bytesRequired = use10BytePatch ? 10 : 13;
while (origBytes.GetOffset() < bytesRequired) {
// If we found JMP 32bit offset, we require that the next bytes must
@ -1470,6 +1309,7 @@ class WindowsDllDetourPatcher final
// we can branch within +/- 128MB of the current location, requiring only
// 4 bytes. In the worst case, we need 16 bytes to load an absolute address
// into a register and then branch to it.
const uint32_t kWorstCaseBytesRequired = 16;
const uint32_t bytesRequiredFromDecode =
(mFlags.value() & DetourFlags::eTestOnlyForceShortPatch)
? 4
@ -1594,30 +1434,107 @@ class WindowsDllDetourPatcher final
return;
}
do {
// Now patch the original function.
// When we're instructed to apply a non-default patch, apply it and exit.
// If non-default patching fails, bail out, no fallback.
// Otherwise, we go straight to the default patch.
#if defined(_M_X64)
if (use10BytePatch) {
if (!Apply10BytePatch(aTrampPool, trampPtr, target, aDest)) {
return;
}
break;
#if defined(_M_IX86)
// now modify the original bytes
target.WriteByte(0xe9); // jmp
target.WriteDisp32(aDest); // hook displacement
#elif defined(_M_X64)
if (use10BytePatch) {
// Okay, now we can write the actual tramp.
// Note: Even if the target function is also below 2GB, we still use an
// intermediary trampoline so that we consistently have a 64-bit pointer
// that we can use to reset the trampoline upon interceptor shutdown.
Maybe<Trampoline<MMPolicyT>> maybeCallTramp(
aTrampPool->GetNextTrampoline());
if (!maybeCallTramp) {
return;
}
Trampoline<MMPolicyT> callTramp(std::move(maybeCallTramp.ref()));
// Write a null instance so that Clear() does not consider this tramp to
// be a normal tramp to be torn down.
callTramp.WriteEncodedPointer(nullptr);
// Use the second pointer slot to store a pointer to the primary tramp
callTramp.WriteEncodedPointer(trampPtr);
callTramp.StartExecutableCode();
// mov r11, address
callTramp.WriteByte(0x49);
callTramp.WriteByte(0xbb);
callTramp.WritePointer(aDest);
// jmp r11
callTramp.WriteByte(0x41);
callTramp.WriteByte(0xff);
callTramp.WriteByte(0xe3);
void* callTrampStart = callTramp.EndExecutableCode();
if (!callTrampStart) {
return;
}
target.WriteByte(0xB8); // MOV EAX, IMM32
// Assert that the topmost 33 bits are 0
MOZ_ASSERT(
!(reinterpret_cast<uintptr_t>(callTrampStart) & (~0x7FFFFFFFULL)));
target.WriteLong(static_cast<uint32_t>(
reinterpret_cast<uintptr_t>(callTrampStart) & 0x7FFFFFFFU));
target.WriteByte(0x48); // REX.W
target.WriteByte(0x63); // MOVSXD r64, r/m32
// dest: rax, src: eax
target.WriteByte(BuildModRmByte(kModReg, kRegAx, kRegAx));
target.WriteByte(0xFF); // JMP /4
target.WriteByte(BuildModRmByte(kModReg, 4, kRegAx)); // rax
} else {
// mov r11, address
target.WriteByte(0x49);
target.WriteByte(0xbb);
target.WritePointer(aDest);
// jmp r11
target.WriteByte(0x41);
target.WriteByte(0xff);
target.WriteByte(0xe3);
}
#elif defined(_M_ARM64)
if (numBytesForPatching < kWorstCaseBytesRequired) {
if (!Apply4BytePatch(aTrampPool, trampPtr, target, aDest)) {
return;
}
break;
}
#endif
PrimitiveT::ApplyDefaultPatch(target, aDest);
} while (false);
// Now patch the original function
if (numBytesForPatching < kWorstCaseBytesRequired) {
// Let's try a 4 byte patch
MOZ_ASSERT(aTrampPool);
if (!aTrampPool) {
return;
}
uintptr_t hookDest = arm64::MakeVeneer(*aTrampPool, trampPtr, aDest);
if (!hookDest) {
return;
}
Maybe<uint32_t> branchImm = arm64::BuildUnconditionalBranchImm(
target.GetCurrentAddress(), hookDest);
if (!branchImm) {
return;
}
target.WriteLong(branchImm.value());
} else {
// The default patch requires 16 bytes
// LDR x16, .+8
target.WriteLong(kLdrX16Plus8);
// BR x16
target.WriteLong(arm64::BuildUnconditionalBranchToRegister(16));
target.WritePointer(aDest);
}
#else
# error "Unsupported processor architecture"
#endif
if (!target.Commit()) {
return;

View File

@ -58,7 +58,7 @@ class MOZ_STACK_CLASS WritableTargetFunction final {
AutoProtect(const MMPolicy& aMMPolicy, uintptr_t aAddr, size_t aNumBytes,
uint32_t aNewProt)
: mMMPolicy(aMMPolicy) {
const uint32_t pageSize = mMMPolicy.GetPageSize();
const uint32_t pageSize = MMPolicy::GetPageSize();
const uintptr_t limit = aAddr + aNumBytes - 1;
const uintptr_t limitPageNum = limit / pageSize;
const uintptr_t basePageNum = aAddr / pageSize;
@ -101,7 +101,7 @@ class MOZ_STACK_CLASS WritableTargetFunction final {
private:
void Clear() {
const uint32_t pageSize = mMMPolicy.GetPageSize();
const uint32_t pageSize = MMPolicy::GetPageSize();
for (auto&& entry : mProtects) {
uint32_t prevProt;
DebugOnly<bool> ok =
@ -398,9 +398,12 @@ class MOZ_STACK_CLASS WritableTargetFunction final {
};
template <typename MMPolicy>
class ReadOnlyTargetBytes {
class ReadOnlyTargetBytes;
template <>
class ReadOnlyTargetBytes<MMPolicyInProcess> {
public:
ReadOnlyTargetBytes(const MMPolicy& aMMPolicy, const void* aBase)
ReadOnlyTargetBytes(const MMPolicyInProcess& aMMPolicy, const void* aBase)
: mMMPolicy(aMMPolicy), mBase(reinterpret_cast<const uint8_t*>(aBase)) {}
ReadOnlyTargetBytes(ReadOnlyTargetBytes&& aOther)
@ -454,13 +457,13 @@ class ReadOnlyTargetBytes {
*/
uintptr_t GetBase() const { return reinterpret_cast<uintptr_t>(mBase); }
const MMPolicy& GetMMPolicy() const { return mMMPolicy; }
const MMPolicyInProcess& GetMMPolicy() const { return mMMPolicy; }
ReadOnlyTargetBytes& operator=(const ReadOnlyTargetBytes&) = delete;
ReadOnlyTargetBytes& operator=(ReadOnlyTargetBytes&&) = delete;
private:
const MMPolicy& mMMPolicy;
const MMPolicyInProcess& mMMPolicy;
uint8_t const* const mBase;
};
@ -617,12 +620,15 @@ class ReadOnlyTargetBytes<MMPolicyOutOfProcess> {
uint8_t const* const mBase;
};
template <typename MMPolicy>
class TargetBytesPtr {
public:
typedef TargetBytesPtr<MMPolicy> Type;
template <typename TargetMMPolicy>
class TargetBytesPtr;
static Type Make(const MMPolicy& aMMPolicy, const void* aFunc) {
template <>
class TargetBytesPtr<MMPolicyInProcess> {
public:
typedef TargetBytesPtr<MMPolicyInProcess> Type;
static Type Make(const MMPolicyInProcess& aMMPolicy, const void* aFunc) {
return TargetBytesPtr(aMMPolicy, aFunc);
}
@ -631,7 +637,7 @@ class TargetBytesPtr {
return TargetBytesPtr(aOther, aOffsetFromOther);
}
ReadOnlyTargetBytes<MMPolicy>* operator->() { return &mTargetBytes; }
ReadOnlyTargetBytes<MMPolicyInProcess>* operator->() { return &mTargetBytes; }
TargetBytesPtr(TargetBytesPtr&& aOther)
: mTargetBytes(std::move(aOther.mTargetBytes)) {}
@ -643,13 +649,13 @@ class TargetBytesPtr {
TargetBytesPtr& operator=(TargetBytesPtr&&) = delete;
private:
TargetBytesPtr(const MMPolicy& aMMPolicy, const void* aFunc)
TargetBytesPtr(const MMPolicyInProcess& aMMPolicy, const void* aFunc)
: mTargetBytes(aMMPolicy, aFunc) {}
TargetBytesPtr(const TargetBytesPtr& aOther, const uint32_t aOffsetFromOther)
: mTargetBytes(aOther.mTargetBytes, aOffsetFromOther) {}
ReadOnlyTargetBytes<MMPolicy> mTargetBytes;
ReadOnlyTargetBytes<MMPolicyInProcess> mTargetBytes;
};
template <>

View File

@ -712,33 +712,6 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS
FuncPtrT mOrigFunc;
};
/**
* This class applies an irreversible patch to jump to a target function
* without backing up the original function.
*/
class WindowsDllEntryPointInterceptor final {
using DllMainFn = BOOL(WINAPI*)(HINSTANCE, DWORD, LPVOID);
using MMPolicyT = MMPolicyInProcessEarlyStage;
MMPolicyT mMMPolicy;
public:
explicit WindowsDllEntryPointInterceptor(
const MMPolicyT::Kernel32Exports& aK32Exports)
: mMMPolicy(aK32Exports) {}
bool Set(const nt::PEHeaders& aHeaders, DllMainFn aDestination) {
if (!aHeaders) {
return false;
}
WindowsDllDetourPatcherPrimitive<MMPolicyT> patcher;
return patcher.AddIrreversibleHook(
mMMPolicy, aHeaders.GetEntryPoint(),
reinterpret_cast<uintptr_t>(aDestination));
}
};
} // namespace interceptor
using WindowsDllInterceptor = interceptor::WindowsDllInterceptor<>;

View File

@ -41,35 +41,6 @@ const char kFailFmt[] =
using namespace mozilla;
using namespace mozilla::nt;
bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
MEMORY_BASIC_INFORMATION info1 = {}, info2 = {};
SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)),
result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2,
sizeof(info2));
if (result1 != result2) {
printf("TEST-FAILED | NativeNt | The returned values mismatch\n");
return false;
}
if (!result1) {
// Both APIs failed.
return true;
}
if (memcmp(&info1, &info2, result1) != 0) {
printf("TEST-FAILED | NativeNt | The returned structures mismatch\n");
return false;
}
return true;
}
LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) {
UNICODE_STRING name;
::RtlInitUnicodeString(&name, aName);
return nt::GetModuleHandleFromLeafName(name);
}
// Need a non-inline function to bypass compiler optimization that the thread
// local storage pointer is cached in a register before accessing a thread-local
// variable.
@ -255,40 +226,6 @@ int wmain(int argc, wchar_t* argv[]) {
return 1;
}
// To test the Ex version of API, we purposely get a real handle
// instead of a pseudo handle.
nsAutoHandle process(
::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()));
if (!process) {
printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n",
::GetLastError());
return 1;
}
// Test Null page, Heap, Mapped image, and Invalid handle
if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) ||
!TestVirtualQuery(process, kNormal) ||
!TestVirtualQuery(nullptr, kNormal)) {
return 1;
}
auto moduleResult = GetModuleHandleFromLeafName(kKernel32);
if (moduleResult.isErr() ||
moduleResult.inspect() != k32headers.template RVAToPtr<HMODULE>(0)) {
printf(
"TEST-FAILED | NativeNt | "
"GetModuleHandleFromLeafName returns a wrong value.\n");
return 1;
}
moduleResult = GetModuleHandleFromLeafName(L"invalid");
if (moduleResult.isOk()) {
printf(
"TEST-FAILED | NativeNt | "
"GetModuleHandleFromLeafName unexpectedly returns a value.\n");
return 1;
}
printf("TEST-PASS | NativeNt | All tests ran successfully\n");
return 0;
}

View File

@ -70,19 +70,6 @@ TEST(TestDllBlocklist, AllowDllByVersion)
EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
}
TEST(TestDllBlocklist, NoOpEntryPoint)
{
// DllMain of this dll has MOZ_RELEASE_ASSERT. This test makes sure we load
// the module successfully without running DllMain.
NS_NAMED_LITERAL_STRING(kLeafName, "TestDllBlocklist_NoOpEntryPoint.dll");
nsString dllPath = GetFullPath(kLeafName);
nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
EXPECT_TRUE(!!hDll);
EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
}
#define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__},
#define DLL_BLOCKLIST_STRING_TYPE const char*
#include "mozilla/WindowsDllBlocklistLegacyDefs.h"

View File

@ -1,12 +0,0 @@
/* 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 http://mozilla.org/MPL/2.0/. */
#include <windows.h>
#include "mozilla/Assertions.h"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) {
MOZ_RELEASE_ASSERT(0);
return TRUE;
}

View File

@ -1,42 +0,0 @@
/* 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 http://mozilla.org/MPL/2.0/. */
#include <winver.h>
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,5,5,5
PRODUCTVERSION 5,5,5,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "mozilla.org"
VALUE "FileDescription", L"Test DLL"
VALUE "FileVersion", "1.0"
VALUE "InternalName", "Test DLL"
VALUE "OriginalFilename", "TestDllBlocklist_NoOpEntryPoint.dll"
VALUE "ProductName", "Test DLL"
VALUE "ProductVersion", "1.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END

View File

@ -1,18 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# 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 http://mozilla.org/MPL/2.0/.
DIST_INSTALL = False
SharedLibrary('TestDllBlocklist_NoOpEntryPoint')
UNIFIED_SOURCES = [
'TestDllBlocklist_NoOpEntryPoint.cpp',
]
RCFILE = 'TestDllBlocklist_NoOpEntryPoint.rc'
RESFILE = 'TestDllBlocklist_NoOpEntryPoint.res'
if CONFIG['COMPILE_ENVIRONMENT']:
TEST_HARNESS_FILES.gtest += ['!TestDllBlocklist_NoOpEntryPoint.dll']

View File

@ -12,6 +12,5 @@ TEST_DIRS += [
'TestDllBlocklist_AllowByVersion',
'TestDllBlocklist_MatchByName',
'TestDllBlocklist_MatchByVersion',
'TestDllBlocklist_NoOpEntryPoint',
]