forked from Floorp-Projects/Floorp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1432653: Refactor the DLL interceptor and parameterize its memory…
… operations; r=handyman MozReview-Commit-ID: EYxVsQ1kicy
- Loading branch information
Showing
17 changed files
with
2,876 additions
and
1,508 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
/* -*- 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_interceptor_MMPolicies_h | ||
#define mozilla_interceptor_MMPolicies_h | ||
|
||
#include "mozilla/Assertions.h" | ||
#include "mozilla/Types.h" | ||
|
||
#include <windows.h> | ||
|
||
namespace mozilla { | ||
namespace interceptor { | ||
|
||
class MMPolicyBase | ||
{ | ||
public: | ||
static DWORD ComputeAllocationSize(const uint32_t aRequestedSize) | ||
{ | ||
MOZ_ASSERT(aRequestedSize); | ||
DWORD result = aRequestedSize; | ||
|
||
const uint32_t granularity = GetAllocGranularity(); | ||
|
||
uint32_t mod = aRequestedSize % granularity; | ||
if (mod) { | ||
result += (granularity - mod); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
static DWORD GetAllocGranularity() | ||
{ | ||
static const DWORD kAllocGranularity = []() -> DWORD { | ||
SYSTEM_INFO sysInfo; | ||
::GetSystemInfo(&sysInfo); | ||
return sysInfo.dwAllocationGranularity; | ||
}(); | ||
|
||
return kAllocGranularity; | ||
} | ||
|
||
static DWORD GetPageSize() | ||
{ | ||
static const DWORD kPageSize = []() -> DWORD { | ||
SYSTEM_INFO sysInfo; | ||
::GetSystemInfo(&sysInfo); | ||
return sysInfo.dwPageSize; | ||
}(); | ||
|
||
return kPageSize; | ||
} | ||
}; | ||
|
||
class MMPolicyInProcess : public MMPolicyBase | ||
{ | ||
public: | ||
typedef MMPolicyInProcess MMPolicyT; | ||
|
||
explicit MMPolicyInProcess() | ||
: mBase(nullptr) | ||
, mReservationSize(0) | ||
, mCommitOffset(0) | ||
{ | ||
} | ||
|
||
MMPolicyInProcess(const MMPolicyInProcess&) = delete; | ||
MMPolicyInProcess& operator=(const MMPolicyInProcess&) = delete; | ||
|
||
MMPolicyInProcess(MMPolicyInProcess&& aOther) | ||
: mBase(nullptr) | ||
, mReservationSize(0) | ||
, mCommitOffset(0) | ||
{ | ||
*this = Move(aOther); | ||
} | ||
|
||
MMPolicyInProcess& operator=(MMPolicyInProcess&& aOther) | ||
{ | ||
mBase = aOther.mBase; | ||
aOther.mBase = nullptr; | ||
|
||
mCommitOffset = aOther.mCommitOffset; | ||
aOther.mCommitOffset = 0; | ||
|
||
mReservationSize = aOther.mReservationSize; | ||
aOther.mReservationSize = 0; | ||
|
||
return *this; | ||
} | ||
|
||
// We always leak mBase | ||
~MMPolicyInProcess() = default; | ||
|
||
explicit operator bool() const | ||
{ | ||
return !!mBase; | ||
} | ||
|
||
/** | ||
* Should we unhook everything upon destruction? | ||
*/ | ||
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; | ||
} | ||
|
||
bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags, | ||
uint32_t* aPrevProtFlags) const | ||
{ | ||
MOZ_ASSERT(aPrevProtFlags); | ||
BOOL ok = ::VirtualProtect(aVAddress, aSize, aProtFlags, | ||
reinterpret_cast<PDWORD>(aPrevProtFlags)); | ||
MOZ_ASSERT(ok); | ||
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.State == MEM_COMMIT && | ||
mbi.Protect != PAGE_NOACCESS; | ||
} | ||
|
||
bool FlushInstructionCache() const | ||
{ | ||
return !!::FlushInstructionCache(::GetCurrentProcess(), nullptr, 0); | ||
} | ||
|
||
protected: | ||
uint8_t* GetLocalView() const | ||
{ | ||
return mBase; | ||
} | ||
|
||
uintptr_t GetRemoteView() const | ||
{ | ||
// Same as local view for in-process | ||
return reinterpret_cast<uintptr_t>(mBase); | ||
} | ||
|
||
/** | ||
* @return the effective number of bytes reserved, or 0 on failure | ||
*/ | ||
uint32_t Reserve(const uint32_t aSize) | ||
{ | ||
if (!aSize) { | ||
return 0; | ||
} | ||
|
||
if (mBase) { | ||
MOZ_ASSERT(mReservationSize >= aSize); | ||
return mReservationSize; | ||
} | ||
|
||
mReservationSize = ComputeAllocationSize(aSize); | ||
mBase = static_cast<uint8_t*>(::VirtualAlloc(nullptr, mReservationSize, | ||
MEM_RESERVE, PAGE_NOACCESS)); | ||
if (!mBase) { | ||
return 0; | ||
} | ||
return mReservationSize; | ||
} | ||
|
||
bool MaybeCommitNextPage(const uint32_t aRequestedOffset, | ||
const uint32_t aRequestedLength) | ||
{ | ||
if (!(*this)) { | ||
return false; | ||
} | ||
|
||
uint32_t limit = aRequestedOffset + aRequestedLength - 1; | ||
if (limit < mCommitOffset) { | ||
// No commit required | ||
return true; | ||
} | ||
|
||
MOZ_DIAGNOSTIC_ASSERT(mCommitOffset < mReservationSize); | ||
if (mCommitOffset >= mReservationSize) { | ||
return false; | ||
} | ||
|
||
PVOID local = ::VirtualAlloc(mBase + mCommitOffset, GetPageSize(), | ||
MEM_COMMIT, PAGE_EXECUTE_READ); | ||
if (!local) { | ||
return false; | ||
} | ||
|
||
mCommitOffset += GetPageSize(); | ||
return true; | ||
} | ||
|
||
private: | ||
uint8_t* mBase; | ||
uint32_t mReservationSize; | ||
uint32_t mCommitOffset; | ||
}; | ||
|
||
} // namespace interceptor | ||
} // namespace mozilla | ||
|
||
#endif // mozilla_interceptor_MMPolicies_h | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* -*- 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_interceptor_PatcherBase_h | ||
#define mozilla_interceptor_PatcherBase_h | ||
|
||
#include "mozilla/interceptor/TargetFunction.h" | ||
|
||
namespace mozilla { | ||
namespace interceptor { | ||
|
||
template <typename VMPolicy> | ||
class WindowsDllPatcherBase | ||
{ | ||
protected: | ||
typedef typename VMPolicy::MMPolicyT MMPolicyT; | ||
|
||
template <typename... Args> | ||
explicit WindowsDllPatcherBase(Args... aArgs) | ||
: mVMPolicy(mozilla::Forward<Args>(aArgs)...) | ||
{ | ||
} | ||
|
||
ReadOnlyTargetFunction<MMPolicyT> | ||
ResolveRedirectedAddress(const void* aOriginalFunction) | ||
{ | ||
ReadOnlyTargetFunction<MMPolicyT> origFn(mVMPolicy, aOriginalFunction); | ||
// If function entry is jmp rel8 stub to the internal implementation, we | ||
// resolve redirected address from the jump target. | ||
if (origFn[0] == 0xeb) { | ||
int8_t offset = (int8_t)(origFn[1]); | ||
if (offset <= 0) { | ||
// Bail out for negative offset: probably already patched by some | ||
// third-party code. | ||
return Move(origFn); | ||
} | ||
|
||
for (int8_t i = 0; i < offset; i++) { | ||
if (origFn[2 + i] != 0x90) { | ||
// Bail out on insufficient nop space. | ||
return Move(origFn); | ||
} | ||
} | ||
|
||
origFn += 2 + offset; | ||
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, origFn.GetAddress()); | ||
} | ||
|
||
#if defined(_M_IX86) | ||
// If function entry is jmp [disp32] such as used by kernel32, | ||
// we resolve redirected address from import table. | ||
if (origFn[0] == 0xff && origFn[1] == 0x25) { | ||
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, | ||
reinterpret_cast<const void*>((origFn + 2).template ChasePointer<uintptr_t*>())); | ||
} | ||
#elif defined(_M_X64) | ||
// If function entry is jmp [disp32] such as used by kernel32, | ||
// we resolve redirected address from import table. | ||
if (origFn[0] == 0x48 && origFn[1] == 0xff && origFn[2] == 0x25) { | ||
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, | ||
reinterpret_cast<const void*>((origFn + 3).ChasePointerFromDisp())); | ||
} | ||
|
||
if (origFn[0] == 0xe9) { | ||
// require for TestDllInterceptor with --disable-optimize | ||
uintptr_t abstarget = (origFn + 1).ReadDisp32AsAbsolute(); | ||
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, abstarget); | ||
} | ||
#endif | ||
|
||
return Move(origFn); | ||
} | ||
|
||
protected: | ||
VMPolicy mVMPolicy; | ||
}; | ||
|
||
} // namespace interceptor | ||
} // namespace mozilla | ||
|
||
#endif // mozilla_interceptor_PatcherBase_h |
Oops, something went wrong.