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/. */
6
7 #ifndef dom_plugins_ipc_functionhook_h
8 #define dom_plugins_ipc_functionhook_h 1
9
10 #include "IpdlTuple.h"
11 #include "base/process.h"
12 #include "mozilla/Atomics.h"
13
14 #if defined(XP_WIN)
15 # include "nsWindowsDllInterceptor.h"
16 #endif
17
18 namespace mozilla {
19 namespace plugins {
20
21 // "PluginHooks" logging helpers
22 extern mozilla::LazyLogModule sPluginHooksLog;
23 #define HOOK_LOG(lvl, msg) MOZ_LOG(mozilla::plugins::sPluginHooksLog, lvl, msg);
SuccessMsg(bool aVal)24 inline const char* SuccessMsg(bool aVal) {
25 return aVal ? "succeeded" : "failed";
26 }
27
28 class FunctionHook;
29 class FunctionHookArray;
30
31 class FunctionHook {
32 public:
33 virtual ~FunctionHook() = default;
34
35 virtual FunctionHookId FunctionId() const = 0;
36
37 /**
38 * Register to hook the function represented by this class.
39 * Returns false if we should have hooked but didn't.
40 */
41 virtual bool Register(int aQuirks) = 0;
42
43 /**
44 * Run the original function with parameters stored in a tuple.
45 * This is only supported on server-side and for auto-brokered methods.
46 */
47 virtual bool RunOriginalFunction(base::ProcessId aClientId,
48 const IPC::IpdlTuple& aInTuple,
49 IPC::IpdlTuple* aOutTuple) const = 0;
50
51 /**
52 * Hook the Win32 methods needed by the plugin process.
53 */
54 static void HookFunctions(int aQuirks);
55
56 static FunctionHookArray* GetHooks();
57
58 #if defined(XP_WIN)
59 /**
60 * Special handler for hooking some kernel32.dll methods that we use to
61 * disable Flash protected mode.
62 */
63 static void HookProtectedMode();
64
65 /**
66 * Get the WindowsDllInterceptor for the given module. Creates a cache of
67 * WindowsDllInterceptors by name.
68 */
69 static WindowsDllInterceptor* GetDllInterceptorFor(const char* aModuleName);
70
71 /**
72 * Must be called to clear the cache created by calls to GetDllInterceptorFor.
73 */
74 static void ClearDllInterceptorCache();
75 #endif // defined(XP_WIN)
76
77 private:
78 static StaticAutoPtr<FunctionHookArray> sFunctionHooks;
79 static void AddFunctionHooks(FunctionHookArray& aHooks);
80 };
81
82 // The FunctionHookArray deletes its FunctionHook objects when freed.
83 class FunctionHookArray : public nsTArray<FunctionHook*> {
84 public:
~FunctionHookArray()85 ~FunctionHookArray() {
86 for (uint32_t idx = 0; idx < Length(); ++idx) {
87 FunctionHook* elt = ElementAt(idx);
88 MOZ_ASSERT(elt);
89 delete elt;
90 }
91 }
92 };
93
94 // Type of function that returns true if a function should be hooked according
95 // to quirks.
96 typedef bool(ShouldHookFunc)(int aQuirks);
97
98 template <FunctionHookId functionId, typename FunctionType>
99 class BasicFunctionHook : public FunctionHook {
100 #if defined(XP_WIN)
101 using FuncHookType = WindowsDllInterceptor::FuncHookType<FunctionType*>;
102 #endif // defined(XP_WIN)
103
104 public:
BasicFunctionHook(const char * aModuleName,const char * aFunctionName,FunctionType * aOldFunction,FunctionType * aNewFunction)105 BasicFunctionHook(const char* aModuleName, const char* aFunctionName,
106 FunctionType* aOldFunction, FunctionType* aNewFunction)
107 : mOldFunction(aOldFunction),
108 mRegistration(UNREGISTERED),
109 mModuleName(aModuleName),
110 mFunctionName(aFunctionName),
111 mNewFunction(aNewFunction) {
112 MOZ_ASSERT(mOldFunction);
113 MOZ_ASSERT(mNewFunction);
114 }
115
116 /**
117 * Hooks the function if we haven't already and if ShouldHook() says to.
118 */
119 bool Register(int aQuirks) override;
120
121 /**
122 * Can be specialized to perform "extra" operations when running the
123 * function on the server side.
124 */
RunOriginalFunction(base::ProcessId aClientId,const IPC::IpdlTuple & aInTuple,IPC::IpdlTuple * aOutTuple)125 bool RunOriginalFunction(base::ProcessId aClientId,
126 const IPC::IpdlTuple& aInTuple,
127 IPC::IpdlTuple* aOutTuple) const override {
128 return false;
129 }
130
FunctionId()131 FunctionHookId FunctionId() const override { return functionId; }
132
OriginalFunction()133 FunctionType* OriginalFunction() const { return mOldFunction; }
134
135 protected:
136 // Once the function is hooked, this field will take the value of a pointer to
137 // a function that performs the old behavior. Before that, it is a pointer to
138 // the original function.
139 Atomic<FunctionType*> mOldFunction;
140 #if defined(XP_WIN)
141 FuncHookType mStub;
142 #endif // defined(XP_WIN)
143
144 enum RegistrationStatus { UNREGISTERED, FAILED, SUCCEEDED };
145 RegistrationStatus mRegistration;
146
147 // The name of the module containing the function to hook. E.g. "user32.dll".
148 const nsCString mModuleName;
149 // The name of the function in the module.
150 const nsCString mFunctionName;
151 // The function that we should replace functionName with. The signature of
152 // newFunction must match that of functionName.
153 FunctionType* const mNewFunction;
154 static ShouldHookFunc* const mShouldHook;
155 };
156
157 // Default behavior is to hook every registered function.
158 extern bool AlwaysHook(int);
159 template <FunctionHookId functionId, typename FunctionType>
160 ShouldHookFunc* const BasicFunctionHook<functionId, FunctionType>::mShouldHook =
161 AlwaysHook;
162
163 template <FunctionHookId functionId, typename FunctionType>
Register(int aQuirks)164 bool BasicFunctionHook<functionId, FunctionType>::Register(int aQuirks) {
165 MOZ_RELEASE_ASSERT(XRE_IsPluginProcess());
166
167 // If we have already attempted to hook this function or if quirks tell us
168 // not to then don't hook.
169 if (mRegistration != UNREGISTERED || !mShouldHook(aQuirks)) {
170 return true;
171 }
172
173 bool isHooked = false;
174 mRegistration = FAILED;
175
176 #if defined(XP_WIN)
177 WindowsDllInterceptor* dllInterceptor =
178 FunctionHook::GetDllInterceptorFor(mModuleName.Data());
179 if (!dllInterceptor) {
180 return false;
181 }
182
183 isHooked = mStub.Set(*dllInterceptor, mFunctionName.Data(), mNewFunction);
184 #endif
185
186 if (isHooked) {
187 #if defined(XP_WIN)
188 mOldFunction = mStub.GetStub();
189 #endif
190 mRegistration = SUCCEEDED;
191 }
192
193 HOOK_LOG(LogLevel::Debug, ("Registering to intercept function '%s' : '%s'",
194 mFunctionName.Data(), SuccessMsg(isHooked)));
195
196 return isHooked;
197 }
198
199 } // namespace plugins
200 } // namespace mozilla
201
202 #endif // dom_plugins_ipc_functionhook_h
203