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