Backed out 2 changesets (bug 1495512) for Windows build bustages CLOSED TREE

Backed out changeset 0cd30c3d9b30 (bug 1495512)
Backed out changeset 7ae82cae37d9 (bug 1495512)
This commit is contained in:
Bogdan Tara 2018-11-14 05:09:12 +02:00
parent a2091796e4
commit 0f6f614304
6 changed files with 54 additions and 516 deletions

View File

@ -8,28 +8,14 @@
#define mozilla_interceptor_MMPolicies_h
#include "mozilla/Assertions.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/Types.h"
#include "mozilla/WindowsMapRemoteView.h"
#include <windows.h>
// _CRT_RAND_S is not defined everywhere, but we need it.
#if !defined(_CRT_RAND_S)
extern "C" errno_t rand_s(unsigned int* randomValue);
#endif // !defined(_CRT_RAND_S)
namespace mozilla {
namespace interceptor {
enum class ReservationFlags : uint32_t
{
eDefault = 0,
eForceFirst2GB = 1,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ReservationFlags)
class MMPolicyBase
{
public:
@ -69,100 +55,6 @@ public:
return kPageSize;
}
#if defined(_M_X64)
/**
* This function locates a virtual memory region of |aDesiredBytesLen| that
* resides in the lowest 2GB of address space. We do this by scanning the
* virtual memory space for a block of unallocated memory that is sufficiently
* large. We must stay below the 2GB mark because a 10-byte patch uses movsxd
* (ie, sign extension) to expand the pointer to 64-bits, so bit 31 of the
* found region must be 0.
*/
static PVOID FindLowRegion(HANDLE aProcess, const size_t aDesiredBytesLen)
{
const DWORD granularity = GetAllocGranularity();
MOZ_ASSERT(aDesiredBytesLen / granularity > 0);
if (!aDesiredBytesLen) {
return nullptr;
}
// Generate a randomized base address that falls within the interval
// [1MiB, 2GiB - aDesiredBytesLen]
unsigned int rnd = 0;
rand_s(&rnd);
// Reduce rnd to a value that falls within the acceptable range
const uint64_t kMinAddress = 0x0000000000100000ULL;
const uint64_t kMaxAddress = 0x0000000080000000ULL;
uint64_t maxOffset = (kMaxAddress - kMinAddress - aDesiredBytesLen) /
granularity;
uint64_t offset = (uint64_t(rnd) % maxOffset) * granularity;
// Start searching at this address
char* address = reinterpret_cast<char*>(kMinAddress) + offset;
// The max address needs to incorporate the desired length
char* const kMaxPtr = reinterpret_cast<char*>(kMaxAddress) -
aDesiredBytesLen;
MOZ_DIAGNOSTIC_ASSERT(address <= kMaxPtr);
MEMORY_BASIC_INFORMATION mbi = {};
SIZE_T len = sizeof(mbi);
// Scan the range for a free chunk that is at least as large as
// aDesiredBytesLen
while (address <= kMaxPtr &&
::VirtualQueryEx(aProcess, address, &mbi, len)) {
if (mbi.State == MEM_FREE && mbi.RegionSize >= aDesiredBytesLen) {
return mbi.BaseAddress;
}
address = reinterpret_cast<char*>(mbi.BaseAddress) + mbi.RegionSize;
}
return nullptr;
}
#endif // defined(_M_X64)
template <typename ReserveFnT>
static PVOID Reserve(HANDLE aProcess, const uint32_t aSize,
const ReserveFnT& aReserveFn,
const ReservationFlags aFlags)
{
#if defined(_M_X64)
if (aFlags & ReservationFlags::eForceFirst2GB) {
size_t curAttempt = 0;
const size_t kMaxAttempts = 8;
// We loop here because |FindLowRegion| may return a base address that
// is reserved elsewhere before we have had a chance to reserve it
// ourselves.
while (curAttempt < kMaxAttempts) {
PVOID base = FindLowRegion(aProcess, aSize);
if (!base) {
return nullptr;
}
PVOID result = aReserveFn(aProcess, base, aSize);
if (result) {
return result;
}
++curAttempt;
}
// If we run out of attempts, we fall through to the default case where
// the system chooses any base address it wants. In that case, the hook
// will be set on a best-effort basis.
}
#endif // defined(_M_X64)
return aReserveFn(aProcess, nullptr, aSize);
}
};
class MMPolicyInProcess : public MMPolicyBase
@ -275,14 +167,6 @@ public:
return PAGE_EXECUTE_READWRITE;
}
#if defined(_M_X64)
bool IsTrampolineSpaceInLowest2GB() const
{
return (mBase + mReservationSize) <=
reinterpret_cast<uint8_t*>(0x0000000080000000ULL);
}
#endif // defined(_M_X64)
protected:
uint8_t* GetLocalView() const
{
@ -298,8 +182,7 @@ protected:
/**
* @return the effective number of bytes reserved, or 0 on failure
*/
uint32_t Reserve(const uint32_t aSize,
const ReservationFlags aFlags)
uint32_t Reserve(const uint32_t aSize)
{
if (!aSize) {
return 0;
@ -311,19 +194,11 @@ protected:
}
mReservationSize = ComputeAllocationSize(aSize);
auto reserveFn = [](HANDLE aProcess, PVOID aBase, uint32_t aSize) -> PVOID {
return ::VirtualAlloc(aBase, aSize, MEM_RESERVE, PAGE_NOACCESS);
};
mBase = static_cast<uint8_t*>(
MMPolicyBase::Reserve(::GetCurrentProcess(), mReservationSize, reserveFn,
aFlags));
mBase = static_cast<uint8_t*>(::VirtualAlloc(nullptr, mReservationSize,
MEM_RESERVE, PAGE_NOACCESS));
if (!mBase) {
return 0;
}
return mReservationSize;
}
@ -513,13 +388,6 @@ public:
return PAGE_READWRITE;
}
#if defined(_M_X64)
bool IsTrampolineSpaceInLowest2GB() const
{
return (GetRemoteView() + mReservationSize) <= 0x0000000080000000ULL;
}
#endif // defined(_M_X64)
protected:
uint8_t* GetLocalView() const
{
@ -534,8 +402,7 @@ protected:
/**
* @return the effective number of bytes reserved, or 0 on failure
*/
uint32_t Reserve(const uint32_t aSize,
const ReservationFlags aFlags)
uint32_t Reserve(const uint32_t aSize)
{
if (!aSize || !mProcess) {
return 0;
@ -561,14 +428,8 @@ protected:
return 0;
}
auto reserveFn = [mapping = mMapping](HANDLE aProcess, PVOID aBase,
uint32_t aSize) -> PVOID {
return mozilla::MapRemoteViewOfFile(mapping, aProcess, 0ULL, aBase, 0, 0,
PAGE_EXECUTE_READ);
};
mRemoteView = MMPolicyBase::Reserve(mProcess, mReservationSize, reserveFn,
aFlags);
mRemoteView = MapRemoteViewOfFile(mMapping, mProcess, 0ULL,
nullptr, 0, 0, PAGE_EXECUTE_READ);
if (!mRemoteView) {
return 0;
}

View File

@ -11,7 +11,6 @@
#include "mozilla/interceptor/Trampoline.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/TypedEnumBits.h"
#define COPY_CODES(NBYTES) do { \
tramp.CopyFrom(origBytes.GetAddress(), NBYTES); \
@ -21,26 +20,15 @@
namespace mozilla {
namespace interceptor {
enum class DetourFlags : uint32_t
{
eDefault = 0,
eEnable10BytePatch = 1, // Allow 10-byte patches when conditions allow
eTestOnlyForce10BytePatch = 3, // Force 10-byte patches at all times (testing only)
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DetourFlags)
template <typename VMPolicy>
class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy>
{
typedef typename VMPolicy::MMPolicyT MMPolicyT;
DetourFlags mFlags;
public:
template <typename... Args>
explicit WindowsDllDetourPatcher(Args... aArgs)
: WindowsDllPatcherBase<VMPolicy>(std::forward<Args>(aArgs)...)
, mFlags(DetourFlags::eDefault)
{
}
@ -128,19 +116,25 @@ public:
if (!origBytes) {
continue;
}
origBytes.Commit();
#elif defined(_M_X64)
if (opcode1 == 0x49) {
if (!Clear13BytePatch(origBytes, tramp.GetCurrentRemoteAddress())) {
continue;
}
} else if (opcode1 == 0xB8) {
if (!Clear10BytePatch(origBytes)) {
continue;
}
} else {
MOZ_ASSERT_UNREACHABLE("Unrecognized patch!");
// Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
MOZ_ASSERT(opcode1 == 0x49);
if (opcode1 != 0x49) {
continue;
}
Maybe<uint8_t> maybeOpcode2 = origBytes.ReadByte();
if (!maybeOpcode2) {
continue;
}
uint8_t opcode2 = maybeOpcode2.value();
if (opcode2 != 0xBB) {
continue;
}
origBytes.WritePointer(tramp.GetCurrentRemoteAddress());
if (!origBytes) {
continue;
}
#elif defined(_M_ARM64)
@ -148,85 +142,19 @@ public:
#else
#error "Unknown processor type"
#endif
origBytes.Commit();
}
this->mVMPolicy.Clear();
}
bool Clear13BytePatch(WritableTargetFunction<MMPolicyT>& aOrigBytes,
const uintptr_t aResetToAddress)
{
Maybe<uint8_t> maybeOpcode2 = aOrigBytes.ReadByte();
if (!maybeOpcode2) {
return false;
}
uint8_t opcode2 = maybeOpcode2.value();
if (opcode2 != 0xBB) {
return false;
}
aOrigBytes.WritePointer(aResetToAddress);
if (!aOrigBytes) {
return false;
}
return aOrigBytes.Commit();
}
bool Clear10BytePatch(WritableTargetFunction<MMPolicyT>& aOrigBytes)
{
Maybe<uint32_t> maybePtr32 = aOrigBytes.ReadLong();
if (!maybePtr32) {
return false;
}
uint32_t ptr32 = maybePtr32.value();
// We expect the high bit to be clear
if (ptr32 & 0x80000000) {
return false;
}
uintptr_t trampPtr = ptr32;
// trampPtr points to an intermediate trampoline that contains a 13-byte
// patch. We back up by sizeof(uintptr_t) so that we can access the pointer
// to the stub trampoline.
WritableTargetFunction<MMPolicyT> writableIntermediate(this->mVMPolicy,
trampPtr - sizeof(uintptr_t), 13 + sizeof(uintptr_t));
if (!writableIntermediate) {
return false;
}
Maybe<uintptr_t> stubTramp = writableIntermediate.ReadEncodedPtr();
if (!stubTramp || !stubTramp.value()) {
return false;
}
Maybe<uint8_t> maybeOpcode1 = writableIntermediate.ReadByte();
if (!maybeOpcode1) {
return false;
}
// We expect this opcode to be the beginning of our normal mov r11, ptr
// patch sequence.
uint8_t opcode1 = maybeOpcode1.value();
if (opcode1 != 0x49) {
return false;
}
// Now we can just delegate the rest to our normal 13-byte patch clearing.
return Clear13BytePatch(writableIntermediate, stubTramp.value());
}
void Init(DetourFlags aFlags = DetourFlags::eDefault, int aNumHooks = 0)
void Init(int aNumHooks = 0)
{
if (Initialized()) {
return;
}
mFlags = aFlags;
if (aNumHooks == 0) {
// Win32 allocates VM addresses at a 64KiB granularity, so by default we
// might as well utilize that entire 64KiB reservation instead of
@ -234,12 +162,7 @@ public:
aNumHooks = this->mVMPolicy.GetAllocGranularity() / kHookSize;
}
ReservationFlags resFlags = ReservationFlags::eDefault;
if (aFlags & DetourFlags::eEnable10BytePatch) {
resFlags |= ReservationFlags::eForceFirst2GB;
}
this->mVMPolicy.Reserve(aNumHooks, resFlags);
this->mVMPolicy.Reserve(aNumHooks);
}
bool Initialized() const
@ -622,15 +545,8 @@ protected:
}
#elif defined(_M_X64)
bool foundJmp = false;
// |use10BytePatch| should always default to |false| in production. It is
// not set to true unless we detect that a 10-byte patch is necessary.
// OTOH, for testing purposes, if we want to force a 10-byte patch, we
// always initialize |use10BytePatch| to |true|.
bool use10BytePatch = (mFlags & DetourFlags::eTestOnlyForce10BytePatch) ==
DetourFlags::eTestOnlyForce10BytePatch;
const uint32_t bytesRequired = use10BytePatch ? 10 : 13;
while (origBytes.GetOffset() < bytesRequired) {
while (origBytes.GetOffset() < 13) {
// If we found JMP 32bit offset, we require that the next bytes must
// be NOP or INT3. There is no reason to copy them.
// TODO: This used to trigger for Je as well. Now that I allow
@ -644,15 +560,6 @@ protected:
++origBytes;
continue;
}
// If our trampoline space is located in the lowest 2GB, we can do a ten
// byte patch instead of a thirteen byte patch.
if (this->mVMPolicy.IsTrampolineSpaceInLowest2GB() &&
origBytes.GetOffset() >= 10) {
use10BytePatch = true;
break;
}
MOZ_ASSERT_UNREACHABLE("Opcode sequence includes commands after JMP");
return;
}
@ -1043,18 +950,6 @@ protected:
} else if ((origBytes[1] & (kMaskMod|kMaskReg)) == BuildModRmByte(kModReg, 2, 0)) {
// CALL reg (ff nn)
COPY_CODES(2);
} else if (((origBytes[1] & kMaskReg) >> kRegFieldShift) == 4) {
// JMP r/m
int len = CountModRmSib(origBytes + 1);
if (len < 0) {
// RIP-relative not yet supported
MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
return;
}
COPY_CODES(len + 1);
foundJmp = true;
} else {
MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
return;
@ -1110,8 +1005,8 @@ protected:
#endif
// The trampoline is now complete.
void* trampPtr = tramp.EndExecutableCode();
if (!trampPtr) {
*aOutTramp = tramp.EndExecutableCode();
if (!(*aOutTramp)) {
return;
}
@ -1125,69 +1020,18 @@ protected:
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.
Trampoline<MMPolicyT> callTramp(this->mVMPolicy.GetNextTrampoline());
if (!callTramp) {
return;
}
// mov r11, address
target.WriteByte(0x49);
target.WriteByte(0xbb);
target.WritePointer(aDest);
// 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);
}
// jmp r11
target.WriteByte(0x41);
target.WriteByte(0xff);
target.WriteByte(0xe3);
#endif
if (!target.Commit()) {
return;
}
// Output the trampoline, thus signalling that this call was a success
*aOutTramp = trampPtr;
target.Commit();
}
};

View File

@ -41,10 +41,6 @@ CommitAndWriteShortInternal<MMPolicyOutOfProcess>(
#endif // defined(_M_IX86)
// Forward declaration
template <typename MMPolicy>
class ReadOnlyTargetFunction;
template <typename MMPolicy>
class MOZ_STACK_CLASS WritableTargetFunction final
{
@ -246,48 +242,6 @@ public:
return Some(value);
}
Maybe<uintptr_t> ReadEncodedPtr()
{
// Reading is only permitted prior to any writing
MOZ_ASSERT(mOffset == mStartWriteOffset);
if (mOffset > mStartWriteOffset) {
mAccumulatedStatus = false;
return Nothing();
}
uintptr_t value;
if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
sizeof(uintptr_t))) {
mAccumulatedStatus = false;
return Nothing();
}
mOffset += sizeof(uintptr_t);
mStartWriteOffset += sizeof(uintptr_t);
return Some(ReadOnlyTargetFunction<MMPolicy>::DecodePtr(value));
}
Maybe<uint32_t> ReadLong()
{
// Reading is only permitted prior to any writing
MOZ_ASSERT(mOffset == mStartWriteOffset);
if (mOffset > mStartWriteOffset) {
mAccumulatedStatus = false;
return Nothing();
}
uint32_t value;
if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
sizeof(uint32_t))) {
mAccumulatedStatus = false;
return Nothing();
}
mOffset += sizeof(uint32_t);
mStartWriteOffset += sizeof(uint32_t);
return Some(value);
}
void WriteShort(const uint16_t& aValue)
{
if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aValue),
@ -349,19 +303,6 @@ public:
mOffset += sizeof(int32_t);
}
#if defined(_M_X64)
void WriteLong(const uint32_t aValue)
{
if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aValue),
sizeof(uint32_t))) {
mAccumulatedStatus = false;
return;
}
mOffset += sizeof(uint32_t);
}
#endif // defined(_M_X64)
void WritePointer(const uintptr_t aAbsTarget)
{
if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aAbsTarget),

View File

@ -24,10 +24,10 @@ public:
{
}
bool Reserve(uint32_t aCount, const ReservationFlags aFlags)
bool Reserve(uint32_t aCount)
{
MOZ_ASSERT(aCount);
uint32_t bytesReserved = MMPolicy::Reserve(aCount * kChunkSize, aFlags);
uint32_t bytesReserved = MMPolicy::Reserve(aCount * kChunkSize);
return !!bytesReserved;
}
@ -127,10 +127,10 @@ public:
return sUniqueVM.ShouldUnhookUponDestruction();
}
bool Reserve(uint32_t aCount, const ReservationFlags aFlags)
bool Reserve(uint32_t aCount)
{
AutoCriticalSection lock(&sCS);
return sUniqueVM.Reserve(aCount, aFlags);
return sUniqueVM.Reserve(aCount);
}
bool IsPageAccessible(void* aVAddress) const
@ -139,14 +139,6 @@ public:
return sUniqueVM.IsPageAccessible(aVAddress);
}
#if defined(_M_X64)
bool IsTrampolineSpaceInLowest2GB() const
{
AutoCriticalSection lock(&sCS);
return sUniqueVM.IsTrampolineSpaceInLowest2GB();
}
#endif // defined(_M_X64)
Trampoline<MMPolicyInProcess> GetNextTrampoline()
{
AutoCriticalSection lock(&sCS);

View File

@ -371,18 +371,6 @@ public:
mNHooks = aNumHooks;
}
/** Force a specific configuration for testing purposes. NOT to be used in
production code! **/
void TestOnlyDetourInit(const wchar_t* aModuleName, DetourFlags aFlags,
int aNumHooks = 0)
{
Init(aModuleName, aNumHooks);
if (!mDetourPatcher.Initialized()) {
mDetourPatcher.Init(aFlags, mNHooks);
}
}
void Clear()
{
if (!mModule) {
@ -467,16 +455,7 @@ private:
#endif
if (!mDetourPatcher.Initialized()) {
DetourFlags flags = DetourFlags::eDefault;
#if defined(_M_X64)
if (mModule == ::GetModuleHandleW(L"ntdll.dll")) {
// NTDLL hooks should attempt to use a 10-byte patch because some
// injected DLLs do the same and interfere with our stuff.
flags |= DetourFlags::eEnable10BytePatch;
}
#endif // defined(_M_X64)
mDetourPatcher.Init(flags, mNHooks);
mDetourPatcher.Init(mNHooks);
}
return mDetourPatcher.AddHook(aProc, aHookDest, aOrigFunc);

View File

@ -32,20 +32,6 @@ NTSTATUS NTAPI NtWriteFileGather(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
NTSTATUS NTAPI NtQueryFullAttributesFile(POBJECT_ATTRIBUTES, PVOID);
NTSTATUS NTAPI LdrLoadDll(PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
NTSTATUS NTAPI LdrUnloadDll(HMODULE);
enum SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
};
NTSTATUS NTAPI
NtMapViewOfSection(HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress,
ULONG_PTR aZeroBits, SIZE_T aCommitSize,
PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
ULONG aProtectionFlags);
// These pointers are disguised as PVOID to avoid pulling in obscure headers
PVOID NTAPI LdrResolveDelayLoadedAPI(PVOID, PVOID, PVOID, PVOID, PVOID, ULONG);
void CALLBACK ProcessCaretEvents(HWINEVENTHOOK, DWORD, HWND, LONG, LONG, DWORD, DWORD);
@ -96,11 +82,11 @@ template <typename CallableT>
bool TestFunction(CallableT aFunc);
#define DEFINE_TEST_FUNCTION(calling_convention) \
template <template <typename I, typename F> class FuncHookT, typename InterceptorT, typename R, typename... Args, typename... TestArgs> \
bool TestFunction(FuncHookT<InterceptorT, R (calling_convention *)(Args...)>&& aFunc, \
template <typename R, typename... Args, typename... TestArgs> \
bool TestFunction(WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>&& aFunc, \
bool (* aPred)(R), TestArgs... aArgs) \
{ \
using FuncHookType = FuncHookT<InterceptorT, R (calling_convention *)(Args...)>; \
using FuncHookType = WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>; \
using ArgTuple = Tuple<Args...>; \
using Indices = std::index_sequence_for<Args...>; \
ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
@ -108,11 +94,11 @@ bool TestFunction(CallableT aFunc);
} \
\
/* Specialization for functions returning void */ \
template <template <typename I, typename F> class FuncHookT, typename InterceptorT, typename... Args, typename PredicateT, typename... TestArgs> \
bool TestFunction(FuncHookT<InterceptorT, void (calling_convention *)(Args...)>&& aFunc, \
template <typename... Args, typename PredicateT, typename... TestArgs> \
bool TestFunction(WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>&& aFunc, \
PredicateT&& aPred, TestArgs... aArgs) \
{ \
using FuncHookType = FuncHookT<InterceptorT, void (calling_convention *)(Args...)>; \
using FuncHookType = WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>; \
using ArgTuple = Tuple<Args...>; \
using Indices = std::index_sequence_for<Args...>; \
ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
@ -135,11 +121,11 @@ DEFINE_TEST_FUNCTION(__fastcall)
// Test the hooked function against the supplied predicate
template <typename OrigFuncT, typename PredicateT, typename... Args>
bool CheckHook(OrigFuncT &aOrigFunc,
bool CheckHook(WindowsDllInterceptor::FuncHookType<OrigFuncT> &aOrigFunc,
const char* aDllName, const char* aFuncName, PredicateT&& aPred,
Args... aArgs)
{
if (TestFunction(std::forward<OrigFuncT>(aOrigFunc), std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...)) {
if (TestFunction(std::forward<WindowsDllInterceptor::FuncHookType<OrigFuncT>>(aOrigFunc), std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...)) {
printf("TEST-PASS | WindowsDllInterceptor | "
"Executed hooked function %s from %s\n", aFuncName, aDllName);
return true;
@ -413,70 +399,6 @@ bool ShouldTestTipTsf()
return true;
}
#if defined(_M_X64)
// Use VMSharingPolicyUnique for the TenByteInterceptor, as it needs to
// reserve its trampoline memory in a special location.
using TenByteInterceptor =
mozilla::interceptor::WindowsDllInterceptor<
mozilla::interceptor::VMSharingPolicyUnique<
mozilla::interceptor::MMPolicyInProcess,
mozilla::interceptor::kDefaultTrampolineSize>>;
static TenByteInterceptor::FuncHookType<decltype(&::NtMapViewOfSection)>
orig_NtMapViewOfSection;
#endif // defined(_M_X64)
bool
TestTenByteDetour()
{
#if defined(_M_X64)
auto pNtMapViewOfSection =
reinterpret_cast<decltype(&::NtMapViewOfSection)>(
::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtMapViewOfSection"));
if (!pNtMapViewOfSection) {
printf("TEST-FAILED | WindowsDllInterceptor | "
"Failed to resolve ntdll!NtMapViewOfSection\n");
return false;
}
{ // Scope for tenByteInterceptor
TenByteInterceptor tenByteInterceptor;
tenByteInterceptor.TestOnlyDetourInit(L"ntdll.dll",
mozilla::interceptor::DetourFlags::eTestOnlyForce10BytePatch);
if (!orig_NtMapViewOfSection.SetDetour(tenByteInterceptor,
"NtMapViewOfSection", nullptr)) {
printf("TEST-FAILED | WindowsDllInterceptor | "
"Failed to hook ntdll!NtMapViewOfSection via 10-byte patch\n");
return false;
}
auto pred = &Predicates<decltype(&::NtMapViewOfSection)>::Ignore<((NTSTATUS)0)>;
if (!CheckHook(orig_NtMapViewOfSection, "ntdll.dll", "NtMapViewOfSection", pred)) {
// CheckHook has already printed the error message for us
return false;
}
}
// Now ensure that our hook cleanup worked
NTSTATUS status = pNtMapViewOfSection(nullptr, nullptr, nullptr, 0, 0, nullptr,
nullptr, ((SECTION_INHERIT)0), 0, 0);
if (NT_SUCCESS(status)) {
printf("TEST-FAILED | WindowsDllInterceptor | "
"Unexpected successful call to ntdll!NtMapViewOfSection after removing 10-byte hook\n");
return false;
}
printf("TEST-PASS | WindowsDllInterceptor | "
"Successfully unhooked ntdll!NtMapViewOfSection via 10-byte patch\n");
return true;
#else
return true;
#endif
}
extern "C"
int wmain(int argc, wchar_t* argv[])
{
@ -615,8 +537,7 @@ int wmain(int argc, wchar_t* argv[])
TEST_HOOK(sspicli.dll, FreeCredentialsHandle, NotEquals, SEC_E_OK) &&
TEST_DETOUR_SKIP_EXEC(kernel32.dll, BaseThreadInitThunk) &&
TEST_DETOUR_SKIP_EXEC(ntdll.dll, LdrLoadDll) &&
TestTenByteDetour()) {
TEST_DETOUR_SKIP_EXEC(ntdll.dll, LdrLoadDll)) {
printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
LARGE_INTEGER end, freq;