1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
8 #define NS_WINDOWS_DLL_INTERCEPTOR_H_
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/Atomics.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/CheckedInt.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/NativeNt.h"
23 #include "mozilla/Tuple.h"
24 #include "mozilla/Types.h"
25 #include "mozilla/UniquePtr.h"
26 #include "mozilla/Vector.h"
27 #include "mozilla/interceptor/MMPolicies.h"
28 #include "mozilla/interceptor/PatcherDetour.h"
29 #include "mozilla/interceptor/PatcherNopSpace.h"
30 #include "mozilla/interceptor/VMSharingPolicies.h"
31 #include "nsWindowsHelpers.h"
34 * Simple function interception.
36 * We have two separate mechanisms for intercepting a function: We can use the
37 * built-in nop space, if it exists, or we can create a detour.
39 * Using the built-in nop space works as follows: On x86-32, DLL functions
40 * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
43 * When we detect a function with this prelude, we do the following:
45 * 1. Write a long jump to our interceptor function into the five bytes of NOPs
46 * before the function.
48 * 2. Write a short jump -5 into the two-byte nop at the beginning of the
51 * This mechanism is nice because it's thread-safe. It's even safe to do if
52 * another thread is currently running the function we're modifying!
54 * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
55 * but not the long jump, so re-intercepting the same function won't work,
56 * because its prelude won't match.
59 * Unfortunately nop space patching doesn't work on functions which don't have
60 * this magic prelude (and in particular, x86-64 never has the prelude). So
61 * when we can't use the built-in nop space, we fall back to using a detour,
62 * which works as follows:
64 * 1. Save first N bytes of OrigFunction to trampoline, where N is a
65 * number of bytes >= 5 that are instruction aligned.
67 * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
70 * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
71 * continue original program flow.
73 * 4. Hook function needs to call the trampoline during its execution,
74 * to invoke the original function (so address of trampoline is
77 * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
78 * patched again to jump directly to the trampoline instead of going through
79 * the hook function. As such, re-intercepting the same function won't work, as
80 * jump instructions are not supported.
82 * Note that this is not thread-safe. Sad day.
86 #if defined(_M_IX86) && defined(__clang__) && __has_declspec_attribute(guard)
87 // On x86, nop-space patches return to the second instruction of their target.
88 // This is a deliberate violation of Control Flow Guard, so disable the check.
89 # define INTERCEPTOR_DISABLE_CFGUARD __declspec(guard(nocf))
91 # define INTERCEPTOR_DISABLE_CFGUARD /* nothing */
95 namespace interceptor
{
98 struct OriginalFunctionPtrTraits
;
100 template <typename R
, typename
... Args
>
101 struct OriginalFunctionPtrTraits
<R (*)(Args
...)> {
102 using ReturnType
= R
;
106 template <typename R
, typename
... Args
>
107 struct OriginalFunctionPtrTraits
<R(__stdcall
*)(Args
...)> {
108 using ReturnType
= R
;
111 template <typename R
, typename
... Args
>
112 struct OriginalFunctionPtrTraits
<R(__fastcall
*)(Args
...)> {
113 using ReturnType
= R
;
115 #endif // defined(_M_IX86)
117 template <typename InterceptorT
, typename FuncPtrT
>
118 class FuncHook final
{
120 using ThisType
= FuncHook
<InterceptorT
, FuncPtrT
>;
121 using ReturnType
= typename OriginalFunctionPtrTraits
<FuncPtrT
>::ReturnType
;
123 constexpr FuncHook() : mOrigFunc(nullptr), mInitOnce(INIT_ONCE_STATIC_INIT
) {}
125 ~FuncHook() = default;
127 bool Set(InterceptorT
& aInterceptor
, const char* aName
, FuncPtrT aHookDest
) {
128 LPVOID addHookOk
= nullptr;
129 InitOnceContext
ctx(this, &aInterceptor
, aName
, aHookDest
, false);
131 return ::InitOnceExecuteOnce(&mInitOnce
, &InitOnceCallback
, &ctx
,
136 bool SetDetour(InterceptorT
& aInterceptor
, const char* aName
,
137 FuncPtrT aHookDest
) {
138 LPVOID addHookOk
= nullptr;
139 InitOnceContext
ctx(this, &aInterceptor
, aName
, aHookDest
, true);
141 return ::InitOnceExecuteOnce(&mInitOnce
, &InitOnceCallback
, &ctx
,
146 explicit operator bool() const { return !!mOrigFunc
; }
148 template <typename
... ArgsType
>
149 INTERCEPTOR_DISABLE_CFGUARD ReturnType
operator()(ArgsType
&&... aArgs
) const {
150 return mOrigFunc(std::forward
<ArgsType
>(aArgs
)...);
153 FuncPtrT
GetStub() const { return mOrigFunc
; }
155 // One-time init stuff cannot be moved or copied
156 FuncHook(const FuncHook
&) = delete;
157 FuncHook(FuncHook
&&) = delete;
158 FuncHook
& operator=(const FuncHook
&) = delete;
159 FuncHook
& operator=(FuncHook
&& aOther
) = delete;
162 struct MOZ_RAII InitOnceContext final
{
163 InitOnceContext(ThisType
* aHook
, InterceptorT
* aInterceptor
,
164 const char* aName
, FuncPtrT aHookDest
, bool aForceDetour
)
166 mInterceptor(aInterceptor
),
168 mHookDest(reinterpret_cast<void*>(aHookDest
)),
169 mForceDetour(aForceDetour
) {}
172 InterceptorT
* mInterceptor
;
179 bool Apply(InterceptorT
* aInterceptor
, const char* aName
, void* aHookDest
) {
180 return aInterceptor
->AddHook(aName
, reinterpret_cast<intptr_t>(aHookDest
),
181 reinterpret_cast<void**>(&mOrigFunc
));
184 bool ApplyDetour(InterceptorT
* aInterceptor
, const char* aName
,
186 return aInterceptor
->AddDetour(aName
, reinterpret_cast<intptr_t>(aHookDest
),
187 reinterpret_cast<void**>(&mOrigFunc
));
190 static BOOL CALLBACK
InitOnceCallback(PINIT_ONCE aInitOnce
, PVOID aParam
,
191 PVOID
* aOutContext
) {
192 MOZ_ASSERT(aOutContext
);
195 auto ctx
= reinterpret_cast<InitOnceContext
*>(aParam
);
196 if (ctx
->mForceDetour
) {
197 result
= ctx
->mHook
->ApplyDetour(ctx
->mInterceptor
, ctx
->mName
,
200 result
= ctx
->mHook
->Apply(ctx
->mInterceptor
, ctx
->mName
, ctx
->mHookDest
);
204 result
? reinterpret_cast<PVOID
>(1U << INIT_ONCE_CTX_RESERVED_BITS
)
214 template <typename InterceptorT
, typename FuncPtrT
>
215 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS FuncHookCrossProcess final
{
217 using ThisType
= FuncHookCrossProcess
<InterceptorT
, FuncPtrT
>;
218 using ReturnType
= typename OriginalFunctionPtrTraits
<FuncPtrT
>::ReturnType
;
221 FuncHookCrossProcess() {}
222 #endif // defined(DEBUG)
224 bool Set(nt::CrossExecTransferManager
& aTransferMgr
,
225 InterceptorT
& aInterceptor
, const char* aName
, FuncPtrT aHookDest
) {
227 if (!aInterceptor
.AddHook(aName
, reinterpret_cast<intptr_t>(aHookDest
),
228 reinterpret_cast<void**>(&origFunc
))) {
232 return CopyStubToChildProcess(aTransferMgr
, aInterceptor
, origFunc
);
235 bool SetDetour(nt::CrossExecTransferManager
& aTransferMgr
,
236 InterceptorT
& aInterceptor
, const char* aName
,
237 FuncPtrT aHookDest
) {
239 if (!aInterceptor
.AddDetour(aName
, reinterpret_cast<intptr_t>(aHookDest
),
240 reinterpret_cast<void**>(&origFunc
))) {
244 return CopyStubToChildProcess(aTransferMgr
, aInterceptor
, origFunc
);
247 explicit operator bool() const { return !!mOrigFunc
; }
250 * NB: This operator is only meaningful when invoked in the target process!
252 template <typename
... ArgsType
>
253 ReturnType
operator()(ArgsType
&&... aArgs
) const {
254 return mOrigFunc(std::forward
<ArgsType
>(aArgs
)...);
258 FuncHookCrossProcess(const FuncHookCrossProcess
&) = delete;
259 FuncHookCrossProcess(FuncHookCrossProcess
&&) = delete;
260 FuncHookCrossProcess
& operator=(const FuncHookCrossProcess
&) = delete;
261 FuncHookCrossProcess
& operator=(FuncHookCrossProcess
&& aOther
) = delete;
262 #endif // defined(DEBUG)
265 bool CopyStubToChildProcess(nt::CrossExecTransferManager
& aTransferMgr
,
266 InterceptorT
& aInterceptor
, FuncPtrT aStub
) {
267 LauncherVoidResult writeResult
=
268 aTransferMgr
.Transfer(&mOrigFunc
, &aStub
, sizeof(FuncPtrT
));
269 if (writeResult
.isErr()) {
270 #ifdef MOZ_USE_LAUNCHER_ERROR
271 const mozilla::WindowsError
& err
= writeResult
.inspectErr().mError
;
273 const mozilla::WindowsError
& err
= writeResult
.inspectErr();
275 aInterceptor
.SetLastDetourError(FUNCHOOKCROSSPROCESS_COPYSTUB_ERROR
,
286 template <typename MMPolicyT
, typename InterceptorT
>
289 template <typename InterceptorT
>
290 struct TypeResolver
<mozilla::interceptor::MMPolicyInProcess
, InterceptorT
> {
291 template <typename FuncPtrT
>
292 using FuncHookType
= FuncHook
<InterceptorT
, FuncPtrT
>;
295 template <typename InterceptorT
>
296 struct TypeResolver
<mozilla::interceptor::MMPolicyOutOfProcess
, InterceptorT
> {
297 template <typename FuncPtrT
>
298 using FuncHookType
= FuncHookCrossProcess
<InterceptorT
, FuncPtrT
>;
301 template <typename VMPolicy
= mozilla::interceptor::VMSharingPolicyShared
>
302 class WindowsDllInterceptor final
303 : public TypeResolver
<typename
VMPolicy::MMPolicyT
,
304 WindowsDllInterceptor
<VMPolicy
>> {
305 typedef WindowsDllInterceptor
<VMPolicy
> ThisType
;
307 interceptor::WindowsDllDetourPatcher
<VMPolicy
> mDetourPatcher
;
309 interceptor::WindowsDllNopSpacePatcher
<typename
VMPolicy::MMPolicyT
>
311 #endif // defined(_M_IX86)
316 template <typename
... Args
>
317 explicit WindowsDllInterceptor(Args
&&... aArgs
)
318 : mDetourPatcher(std::forward
<Args
>(aArgs
)...)
321 mNopSpacePatcher(std::forward
<Args
>(aArgs
)...)
322 #endif // defined(_M_IX86)
327 WindowsDllInterceptor(const WindowsDllInterceptor
&) = delete;
328 WindowsDllInterceptor(WindowsDllInterceptor
&&) = delete;
329 WindowsDllInterceptor
& operator=(const WindowsDllInterceptor
&) = delete;
330 WindowsDllInterceptor
& operator=(WindowsDllInterceptor
&&) = delete;
332 ~WindowsDllInterceptor() { Clear(); }
335 void Init(const char (&aModuleName
)[N
]) {
336 wchar_t moduleName
[N
];
338 for (size_t i
= 0; i
< N
; ++i
) {
339 MOZ_ASSERT(!(aModuleName
[i
] & 0x80),
340 "Use wide-character overload for non-ASCII module names");
341 moduleName
[i
] = aModuleName
[i
];
347 void Init(const wchar_t* aModuleName
) {
352 mModule
= ::LoadLibraryW(aModuleName
);
355 /** Force a specific configuration for testing purposes. NOT to be used in
357 void TestOnlyDetourInit(const wchar_t* aModuleName
, DetourFlags aFlags
) {
359 mDetourPatcher
.Init(aFlags
);
368 mNopSpacePatcher
.Clear();
369 #endif // defined(_M_IX86)
370 mDetourPatcher
.Clear();
372 // NB: We intentionally leak mModule
375 #if defined(NIGHTLY_BUILD)
376 const Maybe
<DetourError
>& GetLastDetourError() const {
377 return mDetourPatcher
.GetLastDetourError();
379 #endif // defined(NIGHTLY_BUILD)
380 template <typename
... Args
>
381 void SetLastDetourError(Args
&&... aArgs
) {
382 return mDetourPatcher
.SetLastDetourError(std::forward
<Args
>(aArgs
)...);
385 constexpr static uint32_t GetWorstCaseRequiredBytesToPatch() {
386 return WindowsDllDetourPatcherPrimitive
<
387 typename
VMPolicy::MMPolicyT
>::GetWorstCaseRequiredBytesToPatch();
392 * Hook/detour the method aName from the DLL we set in Init so that it calls
393 * aHookDest instead. Returns the original method pointer in aOrigFunc
394 * and returns true if successful.
396 * IMPORTANT: If you use this method, please add your case to the
397 * TestDllInterceptor in order to detect future failures. Even if this
398 * succeeds now, updates to the hooked DLL could cause it to fail in
401 bool AddHook(const char* aName
, intptr_t aHookDest
, void** aOrigFunc
) {
402 // Use a nop space patch if possible, otherwise fall back to a detour.
403 // This should be the preferred method for adding hooks.
405 mDetourPatcher
.SetLastDetourError(DetourResultCode::INTERCEPTOR_MOD_NULL
);
409 if (!mDetourPatcher
.IsPageAccessible(
410 nt::PEHeaders::HModuleToBaseAddr
<uintptr_t>(mModule
))) {
411 mDetourPatcher
.SetLastDetourError(
412 DetourResultCode::INTERCEPTOR_MOD_INACCESSIBLE
);
416 FARPROC proc
= mDetourPatcher
.GetProcAddress(mModule
, aName
);
418 mDetourPatcher
.SetLastDetourError(
419 DetourResultCode::INTERCEPTOR_PROC_NULL
);
423 if (!mDetourPatcher
.IsPageAccessible(reinterpret_cast<uintptr_t>(proc
))) {
424 mDetourPatcher
.SetLastDetourError(
425 DetourResultCode::INTERCEPTOR_PROC_INACCESSIBLE
);
430 if (mNopSpacePatcher
.AddHook(proc
, aHookDest
, aOrigFunc
)) {
433 #endif // defined(_M_IX86)
435 return AddDetour(proc
, aHookDest
, aOrigFunc
);
439 * Detour the method aName from the DLL we set in Init so that it calls
440 * aHookDest instead. Returns the original method pointer in aOrigFunc
441 * and returns true if successful.
443 * IMPORTANT: If you use this method, please add your case to the
444 * TestDllInterceptor in order to detect future failures. Even if this
445 * succeeds now, updates to the detoured DLL could cause it to fail in
448 bool AddDetour(const char* aName
, intptr_t aHookDest
, void** aOrigFunc
) {
449 // Generally, code should not call this method directly. Use AddHook unless
450 // there is a specific need to avoid nop space patches.
452 mDetourPatcher
.SetLastDetourError(DetourResultCode::INTERCEPTOR_MOD_NULL
);
456 if (!mDetourPatcher
.IsPageAccessible(
457 nt::PEHeaders::HModuleToBaseAddr
<uintptr_t>(mModule
))) {
458 mDetourPatcher
.SetLastDetourError(
459 DetourResultCode::INTERCEPTOR_MOD_INACCESSIBLE
);
463 FARPROC proc
= mDetourPatcher
.GetProcAddress(mModule
, aName
);
465 mDetourPatcher
.SetLastDetourError(
466 DetourResultCode::INTERCEPTOR_PROC_NULL
);
470 if (!mDetourPatcher
.IsPageAccessible(reinterpret_cast<uintptr_t>(proc
))) {
471 mDetourPatcher
.SetLastDetourError(
472 DetourResultCode::INTERCEPTOR_PROC_INACCESSIBLE
);
476 return AddDetour(proc
, aHookDest
, aOrigFunc
);
479 bool AddDetour(FARPROC aProc
, intptr_t aHookDest
, void** aOrigFunc
) {
480 MOZ_ASSERT(mModule
&& aProc
);
482 if (!mDetourPatcher
.Initialized()) {
483 DetourFlags flags
= DetourFlags::eDefault
;
485 // NTDLL hooks should attempt to use a 10-byte patch because some
486 // injected DLLs do the same and interfere with our stuff.
487 bool needs10BytePatch
= (mModule
== ::GetModuleHandleW(L
"ntdll.dll"));
489 bool isWin8Or81
= IsWin8OrLater() && (!IsWin10OrLater());
490 bool isWin8
= IsWin8OrLater() && (!IsWin8Point1OrLater());
492 bool isKernel32Dll
= (mModule
== ::GetModuleHandleW(L
"kernel32.dll"));
494 bool isDuplicateHandle
= (reinterpret_cast<void*>(aProc
) ==
495 reinterpret_cast<void*>(&::DuplicateHandle
));
497 // CloseHandle on Windows 8/8.1 only accomodates 10-byte patches.
498 needs10BytePatch
|= isWin8Or81
&& isKernel32Dll
&&
499 (reinterpret_cast<void*>(aProc
) ==
500 reinterpret_cast<void*>(&CloseHandle
));
502 // CreateFileA and DuplicateHandle on Windows 8 require 10-byte patches.
503 needs10BytePatch
|= isWin8
&& isKernel32Dll
&&
504 ((reinterpret_cast<void*>(aProc
) ==
505 reinterpret_cast<void*>(&::CreateFileA
)) ||
508 if (needs10BytePatch
) {
509 flags
|= DetourFlags::eEnable10BytePatch
;
512 if (isWin8
&& isDuplicateHandle
) {
513 // Because we can't detour Win8's KERNELBASE!DuplicateHandle,
514 // we detour kernel32!DuplicateHandle (See bug 1659398).
515 flags
|= DetourFlags::eDontResolveRedirection
;
517 #endif // defined(_M_X64)
519 mDetourPatcher
.Init(flags
);
522 return mDetourPatcher
.AddHook(aProc
, aHookDest
, aOrigFunc
);
526 template <typename InterceptorT
, typename FuncPtrT
>
527 friend class FuncHook
;
529 template <typename InterceptorT
, typename FuncPtrT
>
530 friend class FuncHookCrossProcess
;
534 * IAT patching is intended for use when we only want to intercept a function
535 * call originating from a specific module.
537 class WindowsIATPatcher final
{
539 template <typename FuncPtrT
>
540 using FuncHookType
= FuncHook
<WindowsIATPatcher
, FuncPtrT
>;
543 static bool CheckASCII(const char* aInStr
) {
545 if (*aInStr
& 0x80) {
553 static bool AddHook(HMODULE aFromModule
, const char* aToModuleName
,
554 const char* aTargetFnName
, void* aHookDest
,
555 Atomic
<void*>* aOutOrigFunc
) {
556 if (!aFromModule
|| !aToModuleName
|| !aTargetFnName
|| !aOutOrigFunc
) {
560 // PE Spec requires ASCII names for imported module names
561 const bool isModuleNameAscii
= CheckASCII(aToModuleName
);
562 MOZ_ASSERT(isModuleNameAscii
);
563 if (!isModuleNameAscii
) {
567 // PE Spec requires ASCII names for imported function names
568 const bool isTargetFnNameAscii
= CheckASCII(aTargetFnName
);
569 MOZ_ASSERT(isTargetFnNameAscii
);
570 if (!isTargetFnNameAscii
) {
574 nt::PEHeaders
headers(aFromModule
);
579 PIMAGE_IMPORT_DESCRIPTOR impDesc
=
580 headers
.GetImportDescriptor(aToModuleName
);
581 if (!nt::PEHeaders::IsValid(impDesc
)) {
582 // Either aFromModule does not import aToModuleName at load-time, or
583 // aToModuleName is a (currently unsupported) delay-load import.
587 // Resolve the import name table (INT).
588 auto firstINTThunk
= headers
.template RVAToPtr
<PIMAGE_THUNK_DATA
>(
589 impDesc
->OriginalFirstThunk
);
590 if (!nt::PEHeaders::IsValid(firstINTThunk
)) {
594 Maybe
<ptrdiff_t> thunkIndex
;
596 // Scan the INT for the location of the thunk for the function named
598 for (PIMAGE_THUNK_DATA curINTThunk
= firstINTThunk
;
599 nt::PEHeaders::IsValid(curINTThunk
); ++curINTThunk
) {
600 if (IMAGE_SNAP_BY_ORDINAL(curINTThunk
->u1
.Ordinal
)) {
601 // Currently not supporting import by ordinal; this isn't hard to add,
602 // but we won't bother unless necessary.
606 PIMAGE_IMPORT_BY_NAME curThunkFnName
=
607 headers
.template RVAToPtr
<PIMAGE_IMPORT_BY_NAME
>(
608 curINTThunk
->u1
.AddressOfData
);
609 MOZ_ASSERT(curThunkFnName
);
610 if (!curThunkFnName
) {
611 // Looks like we have a bad name descriptor. Try to continue.
615 // Function name checks MUST be case-sensitive!
616 if (!strcmp(aTargetFnName
, curThunkFnName
->Name
)) {
617 // We found the thunk. Save the index of this thunk, as the IAT thunk
618 // is located at the same index in that table as in the INT.
619 thunkIndex
= Some(curINTThunk
- firstINTThunk
);
624 if (thunkIndex
.isNothing()) {
625 // We never found a thunk for that function. Perhaps it's not imported?
629 if (thunkIndex
.value() < 0) {
630 // That's just wrong.
635 headers
.template RVAToPtr
<PIMAGE_THUNK_DATA
>(impDesc
->FirstThunk
);
636 if (!nt::PEHeaders::IsValid(firstIATThunk
)) {
640 // Resolve the IAT thunk for the function we want
641 PIMAGE_THUNK_DATA targetThunk
= &firstIATThunk
[thunkIndex
.value()];
642 if (!nt::PEHeaders::IsValid(targetThunk
)) {
646 auto fnPtr
= reinterpret_cast<Atomic
<void*>*>(&targetThunk
->u1
.Function
);
648 // Now we can just change out its pointer with our hook function.
649 AutoVirtualProtect
prot(fnPtr
, sizeof(void*), PAGE_EXECUTE_READWRITE
);
654 // We do the exchange this way to ensure that *aOutOrigFunc is always valid
655 // once the atomic exchange has taken place.
661 } while (!fnPtr
->compareExchange(tmp
, aHookDest
));
666 template <typename InterceptorT
, typename FuncPtrT
>
667 friend class FuncHook
;
670 template <typename FuncPtrT
>
671 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS
672 FuncHook
<WindowsIATPatcher
, FuncPtrT
>
675 using ThisType
= FuncHook
<WindowsIATPatcher
, FuncPtrT
>;
676 using ReturnType
= typename OriginalFunctionPtrTraits
<FuncPtrT
>::ReturnType
;
679 : mInitOnce(INIT_ONCE_STATIC_INIT
),
680 mFromModule(nullptr),
681 mOrigFunc(nullptr) {}
684 ~FuncHook() = default;
685 #endif // defined(DEBUG)
687 bool Set(const wchar_t* aFromModuleName
, const char* aToModuleName
,
688 const char* aFnName
, FuncPtrT aHookDest
) {
689 nsModuleHandle
fromModule(::LoadLibraryW(aFromModuleName
));
694 return Set(fromModule
, aToModuleName
, aFnName
, aHookDest
);
697 // We offer this overload in case the client wants finer-grained control over
698 // loading aFromModule.
699 bool Set(nsModuleHandle
& aFromModule
, const char* aToModuleName
,
700 const char* aFnName
, FuncPtrT aHookDest
) {
701 LPVOID addHookOk
= nullptr;
702 InitOnceContext
ctx(this, aFromModule
, aToModuleName
, aFnName
, aHookDest
);
704 bool result
= ::InitOnceExecuteOnce(&mInitOnce
, &InitOnceCallback
, &ctx
,
711 // If we successfully set the hook then we must retain a strong reference
712 // to the module that we modified.
713 mFromModule
= aFromModule
.disown();
717 explicit operator bool() const { return !!mOrigFunc
; }
719 template <typename
... ArgsType
>
720 ReturnType
operator()(ArgsType
&&... aArgs
) const {
721 return mOrigFunc(std::forward
<ArgsType
>(aArgs
)...);
724 FuncPtrT
GetStub() const { return mOrigFunc
; }
727 // One-time init stuff cannot be moved or copied
728 FuncHook(const FuncHook
&) = delete;
729 FuncHook(FuncHook
&&) = delete;
730 FuncHook
& operator=(const FuncHook
&) = delete;
731 FuncHook
& operator=(FuncHook
&& aOther
) = delete;
732 #endif // defined(DEBUG)
735 struct MOZ_RAII InitOnceContext final
{
736 InitOnceContext(ThisType
* aHook
, const nsModuleHandle
& aFromModule
,
737 const char* aToModuleName
, const char* aFnName
,
740 mFromModule(aFromModule
),
741 mToModuleName(aToModuleName
),
743 mHookDest(reinterpret_cast<void*>(aHookDest
)) {}
746 const nsModuleHandle
& mFromModule
;
747 const char* mToModuleName
;
753 bool Apply(const nsModuleHandle
& aFromModule
, const char* aToModuleName
,
754 const char* aFnName
, void* aHookDest
) {
755 return WindowsIATPatcher::AddHook(
756 aFromModule
, aToModuleName
, aFnName
, aHookDest
,
757 reinterpret_cast<Atomic
<void*>*>(&mOrigFunc
));
760 static BOOL CALLBACK
InitOnceCallback(PINIT_ONCE aInitOnce
, PVOID aParam
,
761 PVOID
* aOutContext
) {
762 MOZ_ASSERT(aOutContext
);
764 auto ctx
= reinterpret_cast<InitOnceContext
*>(aParam
);
765 bool result
= ctx
->mHook
->Apply(ctx
->mFromModule
, ctx
->mToModuleName
,
766 ctx
->mFnName
, ctx
->mHookDest
);
769 result
? reinterpret_cast<PVOID
>(1U << INIT_ONCE_CTX_RESERVED_BITS
)
776 HMODULE mFromModule
; // never freed
781 * This class applies an irreversible patch to jump to a target function
782 * without backing up the original function.
784 class WindowsDllEntryPointInterceptor final
{
785 using DllMainFn
= BOOL(WINAPI
*)(HINSTANCE
, DWORD
, LPVOID
);
786 using MMPolicyT
= MMPolicyInProcessEarlyStage
;
791 explicit WindowsDllEntryPointInterceptor(
792 const MMPolicyT::Kernel32Exports
& aK32Exports
)
793 : mMMPolicy(aK32Exports
) {}
795 bool Set(const nt::PEHeaders
& aHeaders
, DllMainFn aDestination
) {
800 WindowsDllDetourPatcherPrimitive
<MMPolicyT
> patcher
;
801 return patcher
.AddIrreversibleHook(
802 mMMPolicy
, aHeaders
.GetEntryPoint(),
803 reinterpret_cast<uintptr_t>(aDestination
));
807 } // namespace interceptor
809 using WindowsDllInterceptor
= interceptor::WindowsDllInterceptor
<>;
811 using CrossProcessDllInterceptor
= interceptor::WindowsDllInterceptor
<
812 mozilla::interceptor::VMSharingPolicyUnique
<
813 mozilla::interceptor::MMPolicyOutOfProcess
>>;
815 using WindowsIATPatcher
= interceptor::WindowsIATPatcher
;
817 } // namespace mozilla
819 #endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */