DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
/* -*- 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 "FunctionHook.h"
#include "FunctionBroker.h"
#include "nsClassHashtable.h"
#include "mozilla/ClearOnShutdown.h"

#if defined(XP_WIN)
#include <shlobj.h>
#endif

namespace mozilla {
namespace 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(NS_IsAscii(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;


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>;

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)
{
  NS_NAMED_LITERAL_STRING(tempname, "\\Temp");
  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);
}

#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));
#endif // defined(XP_WIN)
}

#undef FUN_HOOK

} // namespace plugins
} // namespace mozilla