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