summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/FunctionHook.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/plugins/ipc/FunctionHook.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/dom/plugins/ipc/FunctionHook.cpp b/dom/plugins/ipc/FunctionHook.cpp
new file mode 100644
index 0000000000..b9a0ed9e3e
--- /dev/null
+++ b/dom/plugins/ipc/FunctionHook.cpp
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/TextUtils.h"
+
+#include "FunctionHook.h"
+#include "FunctionBroker.h"
+#include "nsClassHashtable.h"
+#include "mozilla/ClearOnShutdown.h"
+
+#if defined(XP_WIN)
+# include <shlobj.h>
+# include "PluginModuleChild.h"
+#endif
+
+namespace mozilla::plugins {
+
+StaticAutoPtr<FunctionHookArray> FunctionHook::sFunctionHooks;
+
+bool AlwaysHook(int) { return true; }
+
+FunctionHookArray* FunctionHook::GetHooks() {
+ if (sFunctionHooks) {
+ return sFunctionHooks;
+ }
+
+ // sFunctionHooks is the StaticAutoPtr to the singleton array of FunctionHook
+ // objects. We free it by clearing the StaticAutoPtr on shutdown.
+ sFunctionHooks = new FunctionHookArray();
+ ClearOnShutdown(&sFunctionHooks);
+ sFunctionHooks->SetLength(ID_FunctionHookCount);
+
+ AddFunctionHooks(*sFunctionHooks);
+ AddBrokeredFunctionHooks(*sFunctionHooks);
+ return sFunctionHooks;
+}
+
+void FunctionHook::HookFunctions(int aQuirks) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin);
+ FunctionHookArray* hooks = FunctionHook::GetHooks();
+ MOZ_ASSERT(hooks);
+ for (size_t i = 0; i < hooks->Length(); ++i) {
+ FunctionHook* mhb = hooks->ElementAt(i);
+ // Check that the FunctionHook array is in the same order as the
+ // FunctionHookId enum.
+ MOZ_ASSERT((size_t)mhb->FunctionId() == i);
+ mhb->Register(aQuirks);
+ }
+}
+
+#if defined(XP_WIN)
+
+// This cache is created when a DLL is registered with a FunctionHook.
+// It is cleared on a call to ClearDllInterceptorCache(). It
+// must be freed before exit to avoid leaks.
+typedef nsClassHashtable<nsStringHashKey, WindowsDllInterceptor>
+ DllInterceptors;
+DllInterceptors* sDllInterceptorCache = nullptr;
+
+WindowsDllInterceptor* FunctionHook::GetDllInterceptorFor(
+ const char* aModuleName) {
+ if (!sDllInterceptorCache) {
+ sDllInterceptorCache = new DllInterceptors();
+ }
+
+ MOZ_ASSERT(IsAsciiNullTerminated(aModuleName),
+ "Non-ASCII module names are not supported");
+ NS_ConvertASCIItoUTF16 moduleName(aModuleName);
+
+ WindowsDllInterceptor* ret = sDllInterceptorCache->LookupOrAdd(moduleName);
+ MOZ_ASSERT(ret);
+ ret->Init(moduleName.get());
+ return ret;
+}
+
+void FunctionHook::ClearDllInterceptorCache() {
+ delete sDllInterceptorCache;
+ sDllInterceptorCache = nullptr;
+}
+
+/* GetWindowInfo */
+
+typedef BasicFunctionHook<ID_GetWindowInfo, decltype(GetWindowInfo)>
+ GetWindowInfoFH;
+
+template <>
+ShouldHookFunc* const GetWindowInfoFH::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_GETWINDOWINFO>;
+
+static const wchar_t* kMozillaWindowClass = L"MozillaWindowClass";
+static HWND sBrowserHwnd = nullptr;
+
+INTERCEPTOR_DISABLE_CFGUARD BOOL WINAPI GetWindowInfoHook(HWND hWnd,
+ PWINDOWINFO pwi) {
+ if (!pwi) {
+ return FALSE;
+ }
+
+ MOZ_ASSERT(ID_GetWindowInfo < FunctionHook::GetHooks()->Length());
+ GetWindowInfoFH* functionHook = static_cast<GetWindowInfoFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_GetWindowInfo));
+ if (!functionHook->OriginalFunction()) {
+ NS_ASSERTION(FALSE, "Something is horribly wrong in PHGetWindowInfoHook!");
+ return FALSE;
+ }
+
+ if (!sBrowserHwnd) {
+ wchar_t szClass[20];
+ // GetClassNameW returns the length it copied w/o null terminator.
+ // Therefore, if the name and null-terminator fit then it returns a
+ // value less than the buffer's length.
+ int nameLen = GetClassNameW(hWnd, szClass, ArrayLength(szClass));
+ if ((nameLen < (int)ArrayLength(szClass)) &&
+ !wcscmp(szClass, kMozillaWindowClass)) {
+ sBrowserHwnd = hWnd;
+ }
+ }
+
+ // Oddity: flash does strange rect comparisons for mouse input destined for
+ // it's internal settings window. Post removing sub widgets for tabs, touch
+ // this up so they get the rect they expect.
+ // XXX potentially tie this to a specific major version?
+ typedef BOOL(WINAPI * GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
+ GetWindowInfoPtr gwiFunc =
+ static_cast<GetWindowInfoPtr>(functionHook->OriginalFunction());
+ BOOL result = gwiFunc(hWnd, pwi);
+ if (sBrowserHwnd && sBrowserHwnd == hWnd) {
+ pwi->rcWindow = pwi->rcClient;
+ }
+ return result;
+}
+
+/* PrintDlgW */
+
+typedef BasicFunctionHook<ID_PrintDlgW, decltype(PrintDlgW)> PrintDlgWFH;
+
+template <>
+ShouldHookFunc* const PrintDlgWFH::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_PRINTDLGW>;
+
+INTERCEPTOR_DISABLE_CFGUARD BOOL WINAPI PrintDlgWHook(LPPRINTDLGW aDlg) {
+ // Zero out the HWND supplied by the plugin. We are sacrificing window
+ // parentage for the ability to run in the NPAPI sandbox.
+ HWND hwnd = aDlg->hwndOwner;
+ aDlg->hwndOwner = 0;
+ MOZ_ASSERT(ID_PrintDlgW < FunctionHook::GetHooks()->Length());
+ PrintDlgWFH* functionHook = static_cast<PrintDlgWFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_PrintDlgW));
+ MOZ_ASSERT(functionHook);
+ BOOL ret = functionHook->OriginalFunction()(aDlg);
+ aDlg->hwndOwner = hwnd;
+ return ret;
+}
+
+// Hooking CreateFileW for protected-mode magic
+static WindowsDllInterceptor sKernel32Intercept;
+typedef HANDLE(WINAPI* CreateFileWPtr)(LPCWSTR aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate);
+static WindowsDllInterceptor::FuncHookType<CreateFileWPtr> sCreateFileWStub;
+typedef HANDLE(WINAPI* CreateFileAPtr)(LPCSTR aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate);
+static WindowsDllInterceptor::FuncHookType<CreateFileAPtr> sCreateFileAStub;
+
+// Windows 8 RTM (kernelbase's version is 6.2.9200.16384) doesn't call
+// CreateFileW from CreateFileA.
+// So we hook CreateFileA too to use CreateFileW hook.
+static HANDLE WINAPI CreateFileAHookFn(LPCSTR aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate) {
+ while (true) { // goto out
+ // Our hook is for mms.cfg into \Windows\System32\Macromed\Flash
+ // We don't require supporting too long path.
+ WCHAR unicodeName[MAX_PATH];
+ size_t len = strlen(aFname);
+
+ if (len >= MAX_PATH) {
+ break;
+ }
+
+ // We call to CreateFileW for workaround of Windows 8 RTM
+ int newLen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, aFname, len,
+ unicodeName, MAX_PATH);
+ if (newLen == 0 || newLen >= MAX_PATH) {
+ break;
+ }
+ unicodeName[newLen] = '\0';
+
+ return CreateFileW(unicodeName, aAccess, aShare, aSecurity, aCreation,
+ aFlags, aFTemplate);
+ }
+
+ return sCreateFileAStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags,
+ aFTemplate);
+}
+
+static bool GetLocalLowTempPath(size_t aLen, LPWSTR aPath) {
+ constexpr auto tempname = u"\\Temp"_ns;
+ LPWSTR path;
+ if (SUCCEEDED(
+ SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, nullptr, &path))) {
+ if (wcslen(path) + tempname.Length() < aLen) {
+ wcscpy(aPath, path);
+ wcscat(aPath, tempname.get());
+ CoTaskMemFree(path);
+ return true;
+ }
+ CoTaskMemFree(path);
+ }
+
+ // XP doesn't support SHGetKnownFolderPath and LocalLow
+ if (!GetTempPathW(aLen, aPath)) {
+ return false;
+ }
+ return true;
+}
+
+HANDLE WINAPI CreateFileWHookFn(LPCWSTR aFname, DWORD aAccess, DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate) {
+ static const WCHAR kConfigFile[] = L"mms.cfg";
+ static const size_t kConfigLength = ArrayLength(kConfigFile) - 1;
+
+ while (true) { // goto out, in sheep's clothing
+ size_t len = wcslen(aFname);
+ if (len < kConfigLength) {
+ break;
+ }
+ if (wcscmp(aFname + len - kConfigLength, kConfigFile) != 0) {
+ break;
+ }
+
+ // This is the config file we want to rewrite
+ WCHAR tempPath[MAX_PATH + 1];
+ if (GetLocalLowTempPath(MAX_PATH, tempPath) == 0) {
+ break;
+ }
+ WCHAR tempFile[MAX_PATH + 1];
+ if (GetTempFileNameW(tempPath, L"fx", 0, tempFile) == 0) {
+ break;
+ }
+ HANDLE replacement = sCreateFileWStub(
+ tempFile, GENERIC_READ | GENERIC_WRITE, aShare, aSecurity,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+ nullptr);
+ if (replacement == INVALID_HANDLE_VALUE) {
+ break;
+ }
+
+ HANDLE original = sCreateFileWStub(aFname, aAccess, aShare, aSecurity,
+ aCreation, aFlags, aFTemplate);
+ if (original != INVALID_HANDLE_VALUE) {
+ // copy original to replacement
+ static const size_t kBufferSize = 1024;
+ char buffer[kBufferSize];
+ DWORD bytes;
+ while (ReadFile(original, buffer, kBufferSize, &bytes, NULL)) {
+ if (bytes == 0) {
+ break;
+ }
+ DWORD wbytes;
+ WriteFile(replacement, buffer, bytes, &wbytes, NULL);
+ if (bytes < kBufferSize) {
+ break;
+ }
+ }
+ CloseHandle(original);
+ }
+ static const char kSettingString[] = "\nProtectedMode=0\n";
+ DWORD wbytes;
+ WriteFile(replacement, static_cast<const void*>(kSettingString),
+ sizeof(kSettingString) - 1, &wbytes, NULL);
+ SetFilePointer(replacement, 0, NULL, FILE_BEGIN);
+ return replacement;
+ }
+ return sCreateFileWStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags,
+ aFTemplate);
+}
+
+void FunctionHook::HookProtectedMode() {
+ // Legacy code. Uses the nsWindowsDLLInterceptor directly instead of
+ // using the FunctionHook
+ sKernel32Intercept.Init("kernel32.dll");
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin);
+ sCreateFileWStub.Set(sKernel32Intercept, "CreateFileW", &CreateFileWHookFn);
+ sCreateFileAStub.Set(sKernel32Intercept, "CreateFileA", &CreateFileAHookFn);
+}
+
+# if defined(MOZ_SANDBOX)
+
+/* GetFileAttributesW */
+
+typedef BasicFunctionHook<ID_GetFileAttributesW, decltype(GetFileAttributesW)>
+ GetFileAttributesWFH;
+
+INTERCEPTOR_DISABLE_CFGUARD DWORD WINAPI
+GetFileAttributesWHook(LPCWSTR aFilename) {
+ MOZ_ASSERT(ID_GetFileAttributesW < FunctionHook::GetHooks()->Length());
+ GetFileAttributesWFH* functionHook = static_cast<GetFileAttributesWFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_GetFileAttributesW));
+ if (!functionHook->OriginalFunction()) {
+ NS_ASSERTION(FALSE,
+ "Something is horribly wrong in GetFileAttributesWHook!");
+ return FALSE;
+ }
+
+ DWORD ret = functionHook->OriginalFunction()(aFilename);
+ if (ret != INVALID_FILE_ATTRIBUTES) {
+ return ret;
+ }
+
+ // If aFilename is a parent of PluginModuleChild::GetFlashRoamingPath then
+ // assume it was blocked by the sandbox and just report it as a plain
+ // directory.
+ size_t len = wcslen(aFilename);
+ std::wstring roamingPath = PluginModuleChild::GetFlashRoamingPath();
+ bool isParent = (len > 0) && (aFilename[len - 1] == L'\\') &&
+ (_wcsnicmp(aFilename, roamingPath.c_str(), len) == 0);
+ if (!isParent) {
+ return ret;
+ }
+ return FILE_ATTRIBUTE_DIRECTORY;
+}
+
+# endif // defined(MOZ_SANDBOX)
+
+#endif // defined(XP_WIN)
+
+#define FUN_HOOK(x) static_cast<FunctionHook*>(x)
+
+void FunctionHook::AddFunctionHooks(FunctionHookArray& aHooks) {
+ // We transfer ownership of the FunctionHook objects to the array.
+#if defined(XP_WIN)
+ aHooks[ID_GetWindowInfo] = FUN_HOOK(new GetWindowInfoFH(
+ "user32.dll", "GetWindowInfo", &GetWindowInfo, &GetWindowInfoHook));
+ aHooks[ID_PrintDlgW] = FUN_HOOK(
+ new PrintDlgWFH("comdlg32.dll", "PrintDlgW", &PrintDlgW, PrintDlgWHook));
+# if defined(MOZ_SANDBOX)
+ aHooks[ID_GetFileAttributesW] = FUN_HOOK(
+ new GetFileAttributesWFH("kernel32.dll", "GetFileAttributesW",
+ &GetFileAttributesW, &GetFileAttributesWHook));
+# endif // defined(MOZ_SANDBOX)
+#endif // defined(XP_WIN)
+}
+
+#undef FUN_HOOK
+
+} // namespace mozilla::plugins