/src/mozilla-central/dom/plugins/ipc/FunctionHook.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "FunctionHook.h" |
8 | | #include "FunctionBroker.h" |
9 | | #include "nsClassHashtable.h" |
10 | | #include "mozilla/ClearOnShutdown.h" |
11 | | |
12 | | #if defined(XP_WIN) |
13 | | #include <shlobj.h> |
14 | | #endif |
15 | | |
16 | | namespace mozilla { |
17 | | namespace plugins { |
18 | | |
19 | | StaticAutoPtr<FunctionHookArray> FunctionHook::sFunctionHooks; |
20 | | |
21 | 0 | bool AlwaysHook(int) { return true; } |
22 | | |
23 | | FunctionHookArray* |
24 | | FunctionHook::GetHooks() |
25 | 0 | { |
26 | 0 | if (sFunctionHooks) { |
27 | 0 | return sFunctionHooks; |
28 | 0 | } |
29 | 0 | |
30 | 0 | // sFunctionHooks is the StaticAutoPtr to the singleton array of FunctionHook |
31 | 0 | // objects. We free it by clearing the StaticAutoPtr on shutdown. |
32 | 0 | sFunctionHooks = new FunctionHookArray(); |
33 | 0 | ClearOnShutdown(&sFunctionHooks); |
34 | 0 | sFunctionHooks->SetLength(ID_FunctionHookCount); |
35 | 0 |
|
36 | 0 | AddFunctionHooks(*sFunctionHooks); |
37 | 0 | AddBrokeredFunctionHooks(*sFunctionHooks); |
38 | 0 | return sFunctionHooks; |
39 | 0 | } |
40 | | |
41 | | void |
42 | | FunctionHook::HookFunctions(int aQuirks) |
43 | 0 | { |
44 | 0 | MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin); |
45 | 0 | FunctionHookArray* hooks = FunctionHook::GetHooks(); |
46 | 0 | MOZ_ASSERT(hooks); |
47 | 0 | for(size_t i=0; i < hooks->Length(); ++i) { |
48 | 0 | FunctionHook* mhb = hooks->ElementAt(i); |
49 | 0 | // Check that the FunctionHook array is in the same order as the |
50 | 0 | // FunctionHookId enum. |
51 | 0 | MOZ_ASSERT((size_t)mhb->FunctionId() == i); |
52 | 0 | mhb->Register(aQuirks); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | #if defined(XP_WIN) |
57 | | |
58 | | // This cache is created when a DLL is registered with a FunctionHook. |
59 | | // It is cleared on a call to ClearDllInterceptorCache(). It |
60 | | // must be freed before exit to avoid leaks. |
61 | | typedef nsClassHashtable<nsStringHashKey, WindowsDllInterceptor> DllInterceptors; |
62 | | DllInterceptors* sDllInterceptorCache = nullptr; |
63 | | |
64 | | WindowsDllInterceptor* |
65 | | FunctionHook::GetDllInterceptorFor(const char* aModuleName) |
66 | | { |
67 | | if (!sDllInterceptorCache) { |
68 | | sDllInterceptorCache = new DllInterceptors(); |
69 | | } |
70 | | |
71 | | MOZ_ASSERT(NS_IsAscii(aModuleName), "Non-ASCII module names are not supported"); |
72 | | NS_ConvertASCIItoUTF16 moduleName(aModuleName); |
73 | | |
74 | | WindowsDllInterceptor* ret = |
75 | | sDllInterceptorCache->LookupOrAdd(moduleName); |
76 | | MOZ_ASSERT(ret); |
77 | | ret->Init(moduleName.get()); |
78 | | return ret; |
79 | | } |
80 | | |
81 | | void |
82 | | FunctionHook::ClearDllInterceptorCache() |
83 | | { |
84 | | delete sDllInterceptorCache; |
85 | | sDllInterceptorCache = nullptr; |
86 | | } |
87 | | |
88 | | /* GetWindowInfo */ |
89 | | |
90 | | typedef BasicFunctionHook<ID_GetWindowInfo, decltype(GetWindowInfo)> GetWindowInfoFH; |
91 | | |
92 | | template<> |
93 | | ShouldHookFunc* const |
94 | | GetWindowInfoFH::mShouldHook = &CheckQuirks<QUIRK_FLASH_HOOK_GETWINDOWINFO>; |
95 | | |
96 | | static const wchar_t * kMozillaWindowClass = L"MozillaWindowClass"; |
97 | | static HWND sBrowserHwnd = nullptr; |
98 | | |
99 | | |
100 | | BOOL WINAPI |
101 | | GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi) |
102 | | { |
103 | | if (!pwi) { |
104 | | return FALSE; |
105 | | } |
106 | | |
107 | | MOZ_ASSERT(ID_GetWindowInfo < FunctionHook::GetHooks()->Length()); |
108 | | GetWindowInfoFH* functionHook = |
109 | | static_cast<GetWindowInfoFH*>(FunctionHook::GetHooks()->ElementAt(ID_GetWindowInfo)); |
110 | | if (!functionHook->OriginalFunction()) { |
111 | | NS_ASSERTION(FALSE, "Something is horribly wrong in PHGetWindowInfoHook!"); |
112 | | return FALSE; |
113 | | } |
114 | | |
115 | | if (!sBrowserHwnd) { |
116 | | wchar_t szClass[20]; |
117 | | // GetClassNameW returns the length it copied w/o null terminator. |
118 | | // Therefore, if the name and null-terminator fit then it returns a |
119 | | // value less than the buffer's length. |
120 | | int nameLen = GetClassNameW(hWnd, szClass, ArrayLength(szClass)); |
121 | | if ((nameLen < (int)ArrayLength(szClass)) && |
122 | | !wcscmp(szClass, kMozillaWindowClass)) { |
123 | | sBrowserHwnd = hWnd; |
124 | | } |
125 | | } |
126 | | |
127 | | // Oddity: flash does strange rect comparisons for mouse input destined for |
128 | | // it's internal settings window. Post removing sub widgets for tabs, touch |
129 | | // this up so they get the rect they expect. |
130 | | // XXX potentially tie this to a specific major version? |
131 | | typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi); |
132 | | GetWindowInfoPtr gwiFunc = |
133 | | static_cast<GetWindowInfoPtr>(functionHook->OriginalFunction()); |
134 | | BOOL result = gwiFunc(hWnd, pwi); |
135 | | if (sBrowserHwnd && sBrowserHwnd == hWnd) { |
136 | | pwi->rcWindow = pwi->rcClient; |
137 | | } |
138 | | return result; |
139 | | } |
140 | | |
141 | | /* PrintDlgW */ |
142 | | |
143 | | typedef BasicFunctionHook<ID_PrintDlgW, decltype(PrintDlgW)> PrintDlgWFH; |
144 | | |
145 | | template<> |
146 | | ShouldHookFunc* const |
147 | | PrintDlgWFH::mShouldHook = &CheckQuirks<QUIRK_FLASH_HOOK_PRINTDLGW>; |
148 | | |
149 | | BOOL WINAPI PrintDlgWHook(LPPRINTDLGW aDlg) |
150 | | { |
151 | | // Zero out the HWND supplied by the plugin. We are sacrificing window |
152 | | // parentage for the ability to run in the NPAPI sandbox. |
153 | | HWND hwnd = aDlg->hwndOwner; |
154 | | aDlg->hwndOwner = 0; |
155 | | MOZ_ASSERT(ID_PrintDlgW < FunctionHook::GetHooks()->Length()); |
156 | | PrintDlgWFH* functionHook = |
157 | | static_cast<PrintDlgWFH*>(FunctionHook::GetHooks()->ElementAt(ID_PrintDlgW)); |
158 | | MOZ_ASSERT(functionHook); |
159 | | BOOL ret = functionHook->OriginalFunction()(aDlg); |
160 | | aDlg->hwndOwner = hwnd; |
161 | | return ret; |
162 | | } |
163 | | |
164 | | // Hooking CreateFileW for protected-mode magic |
165 | | static WindowsDllInterceptor sKernel32Intercept; |
166 | | typedef HANDLE (WINAPI *CreateFileWPtr)(LPCWSTR aFname, DWORD aAccess, |
167 | | DWORD aShare, |
168 | | LPSECURITY_ATTRIBUTES aSecurity, |
169 | | DWORD aCreation, DWORD aFlags, |
170 | | HANDLE aFTemplate); |
171 | | static WindowsDllInterceptor::FuncHookType<CreateFileWPtr> sCreateFileWStub; |
172 | | typedef HANDLE (WINAPI *CreateFileAPtr)(LPCSTR aFname, DWORD aAccess, |
173 | | DWORD aShare, |
174 | | LPSECURITY_ATTRIBUTES aSecurity, |
175 | | DWORD aCreation, DWORD aFlags, |
176 | | HANDLE aFTemplate); |
177 | | static WindowsDllInterceptor::FuncHookType<CreateFileAPtr> sCreateFileAStub; |
178 | | |
179 | | // Windows 8 RTM (kernelbase's version is 6.2.9200.16384) doesn't call |
180 | | // CreateFileW from CreateFileA. |
181 | | // So we hook CreateFileA too to use CreateFileW hook. |
182 | | static HANDLE WINAPI |
183 | | CreateFileAHookFn(LPCSTR aFname, DWORD aAccess, DWORD aShare, |
184 | | LPSECURITY_ATTRIBUTES aSecurity, DWORD aCreation, DWORD aFlags, |
185 | | HANDLE aFTemplate) |
186 | | { |
187 | | while (true) { // goto out |
188 | | // Our hook is for mms.cfg into \Windows\System32\Macromed\Flash |
189 | | // We don't require supporting too long path. |
190 | | WCHAR unicodeName[MAX_PATH]; |
191 | | size_t len = strlen(aFname); |
192 | | |
193 | | if (len >= MAX_PATH) { |
194 | | break; |
195 | | } |
196 | | |
197 | | // We call to CreateFileW for workaround of Windows 8 RTM |
198 | | int newLen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, aFname, |
199 | | len, unicodeName, MAX_PATH); |
200 | | if (newLen == 0 || newLen >= MAX_PATH) { |
201 | | break; |
202 | | } |
203 | | unicodeName[newLen] = '\0'; |
204 | | |
205 | | return CreateFileW(unicodeName, aAccess, aShare, aSecurity, aCreation, aFlags, aFTemplate); |
206 | | } |
207 | | |
208 | | return sCreateFileAStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags, |
209 | | aFTemplate); |
210 | | } |
211 | | |
212 | | static bool |
213 | | GetLocalLowTempPath(size_t aLen, LPWSTR aPath) |
214 | | { |
215 | | NS_NAMED_LITERAL_STRING(tempname, "\\Temp"); |
216 | | LPWSTR path; |
217 | | if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, |
218 | | nullptr, &path))) { |
219 | | if (wcslen(path) + tempname.Length() < aLen) { |
220 | | wcscpy(aPath, path); |
221 | | wcscat(aPath, tempname.get()); |
222 | | CoTaskMemFree(path); |
223 | | return true; |
224 | | } |
225 | | CoTaskMemFree(path); |
226 | | } |
227 | | |
228 | | // XP doesn't support SHGetKnownFolderPath and LocalLow |
229 | | if (!GetTempPathW(aLen, aPath)) { |
230 | | return false; |
231 | | } |
232 | | return true; |
233 | | } |
234 | | |
235 | | HANDLE WINAPI |
236 | | CreateFileWHookFn(LPCWSTR aFname, DWORD aAccess, DWORD aShare, |
237 | | LPSECURITY_ATTRIBUTES aSecurity, DWORD aCreation, DWORD aFlags, |
238 | | HANDLE aFTemplate) |
239 | | { |
240 | | static const WCHAR kConfigFile[] = L"mms.cfg"; |
241 | | static const size_t kConfigLength = ArrayLength(kConfigFile) - 1; |
242 | | |
243 | | while (true) { // goto out, in sheep's clothing |
244 | | size_t len = wcslen(aFname); |
245 | | if (len < kConfigLength) { |
246 | | break; |
247 | | } |
248 | | if (wcscmp(aFname + len - kConfigLength, kConfigFile) != 0) { |
249 | | break; |
250 | | } |
251 | | |
252 | | // This is the config file we want to rewrite |
253 | | WCHAR tempPath[MAX_PATH+1]; |
254 | | if (GetLocalLowTempPath(MAX_PATH, tempPath) == 0) { |
255 | | break; |
256 | | } |
257 | | WCHAR tempFile[MAX_PATH+1]; |
258 | | if (GetTempFileNameW(tempPath, L"fx", 0, tempFile) == 0) { |
259 | | break; |
260 | | } |
261 | | HANDLE replacement = |
262 | | sCreateFileWStub(tempFile, GENERIC_READ | GENERIC_WRITE, aShare, |
263 | | aSecurity, TRUNCATE_EXISTING, |
264 | | FILE_ATTRIBUTE_TEMPORARY | |
265 | | FILE_FLAG_DELETE_ON_CLOSE, |
266 | | nullptr); |
267 | | if (replacement == INVALID_HANDLE_VALUE) { |
268 | | break; |
269 | | } |
270 | | |
271 | | HANDLE original = sCreateFileWStub(aFname, aAccess, aShare, aSecurity, |
272 | | aCreation, aFlags, aFTemplate); |
273 | | if (original != INVALID_HANDLE_VALUE) { |
274 | | // copy original to replacement |
275 | | static const size_t kBufferSize = 1024; |
276 | | char buffer[kBufferSize]; |
277 | | DWORD bytes; |
278 | | while (ReadFile(original, buffer, kBufferSize, &bytes, NULL)) { |
279 | | if (bytes == 0) { |
280 | | break; |
281 | | } |
282 | | DWORD wbytes; |
283 | | WriteFile(replacement, buffer, bytes, &wbytes, NULL); |
284 | | if (bytes < kBufferSize) { |
285 | | break; |
286 | | } |
287 | | } |
288 | | CloseHandle(original); |
289 | | } |
290 | | static const char kSettingString[] = "\nProtectedMode=0\n"; |
291 | | DWORD wbytes; |
292 | | WriteFile(replacement, static_cast<const void*>(kSettingString), |
293 | | sizeof(kSettingString) - 1, &wbytes, NULL); |
294 | | SetFilePointer(replacement, 0, NULL, FILE_BEGIN); |
295 | | return replacement; |
296 | | } |
297 | | return sCreateFileWStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags, |
298 | | aFTemplate); |
299 | | } |
300 | | |
301 | | void FunctionHook::HookProtectedMode() |
302 | | { |
303 | | // Legacy code. Uses the nsWindowsDLLInterceptor directly instead of |
304 | | // using the FunctionHook |
305 | | sKernel32Intercept.Init("kernel32.dll"); |
306 | | MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin); |
307 | | sCreateFileWStub.Set(sKernel32Intercept, "CreateFileW", &CreateFileWHookFn); |
308 | | sCreateFileAStub.Set(sKernel32Intercept, "CreateFileA", &CreateFileAHookFn); |
309 | | } |
310 | | |
311 | | #endif // defined(XP_WIN) |
312 | | |
313 | | #define FUN_HOOK(x) static_cast<FunctionHook*>(x) |
314 | | |
315 | | void |
316 | | FunctionHook::AddFunctionHooks(FunctionHookArray& aHooks) |
317 | 0 | { |
318 | 0 | // We transfer ownership of the FunctionHook objects to the array. |
319 | | #if defined(XP_WIN) |
320 | | aHooks[ID_GetWindowInfo] = |
321 | | FUN_HOOK(new GetWindowInfoFH("user32.dll", "GetWindowInfo", |
322 | | &GetWindowInfo, &GetWindowInfoHook)); |
323 | | aHooks[ID_PrintDlgW] = |
324 | | FUN_HOOK(new PrintDlgWFH("comdlg32.dll", "PrintDlgW", &PrintDlgW, |
325 | | PrintDlgWHook)); |
326 | | #endif // defined(XP_WIN) |
327 | | } |
328 | | |
329 | | #undef FUN_HOOK |
330 | | |
331 | | } // namespace plugins |
332 | | } // namespace mozilla |