michael@0: /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #pragma warning( push ) michael@0: #pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h michael@0: #include michael@0: #pragma warning( pop ) michael@0: michael@0: #define MOZ_NO_MOZALLOC michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include "nsWindowsDllInterceptor.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: #include "nsWindowsHelpers.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define ALL_VERSIONS ((unsigned long long)-1LL) michael@0: michael@0: // DLLs sometimes ship without a version number, particularly early michael@0: // releases. Blocking "version <= 0" has the effect of blocking unversioned michael@0: // DLLs (since the call to get version info fails), but not blocking michael@0: // any versioned instance. michael@0: #define UNVERSIONED ((unsigned long long)0LL) michael@0: michael@0: // Convert the 4 (decimal) components of a DLL version number into a michael@0: // single unsigned long long, as needed by the blocklist michael@0: #define MAKE_VERSION(a,b,c,d)\ michael@0: ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL) michael@0: michael@0: struct DllBlockInfo { michael@0: // The name of the DLL -- in LOWERCASE! It will be compared to michael@0: // a lowercase version of the DLL name only. michael@0: const char *name; michael@0: michael@0: // If maxVersion is ALL_VERSIONS, we'll block all versions of this michael@0: // dll. Otherwise, we'll block all versions less than or equal to michael@0: // the given version, as queried by GetFileVersionInfo and michael@0: // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields. michael@0: // michael@0: // Note that the version is usually 4 components, which is A.B.C.D michael@0: // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity), michael@0: // but it's not required to be of that format. michael@0: // michael@0: // If the USE_TIMESTAMP flag is set, then we use the timestamp from michael@0: // the IMAGE_FILE_HEADER in lieu of a version number. michael@0: unsigned long long maxVersion; michael@0: michael@0: enum { michael@0: FLAGS_DEFAULT = 0, michael@0: BLOCK_WIN8PLUS_ONLY = 1, michael@0: BLOCK_XP_ONLY = 2, michael@0: USE_TIMESTAMP = 4, michael@0: } flags; michael@0: }; michael@0: michael@0: static DllBlockInfo sWindowsDllBlocklist[] = { michael@0: // EXAMPLE: michael@0: // { "uxtheme.dll", ALL_VERSIONS }, michael@0: // { "uxtheme.dll", 0x0000123400000000ULL }, michael@0: // The DLL name must be in lowercase! michael@0: michael@0: // NPFFAddon - Known malware michael@0: { "npffaddon.dll", ALL_VERSIONS}, michael@0: michael@0: // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted michael@0: {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)}, michael@0: michael@0: // calc.dll - Suspected malware michael@0: {"calc.dll", MAKE_VERSION(1,0,0,1)}, michael@0: michael@0: // hook.dll - Suspected malware michael@0: {"hook.dll", ALL_VERSIONS}, michael@0: michael@0: // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances michael@0: // of this DLL cause crashes michael@0: {"googledesktopnetwork3.dll", UNVERSIONED}, michael@0: michael@0: // rdolib.dll - Suspected malware michael@0: {"rdolib.dll", MAKE_VERSION(6,0,88,4)}, michael@0: michael@0: // fgjk4wvb.dll - Suspected malware michael@0: {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)}, michael@0: michael@0: // radhslib.dll - Naomi internet filter - unmaintained since 2006 michael@0: {"radhslib.dll", UNVERSIONED}, michael@0: michael@0: // Music download filter for vkontakte.ru - old instances michael@0: // of this DLL cause crashes michael@0: {"vksaver.dll", MAKE_VERSION(2,2,2,0)}, michael@0: michael@0: // Topcrash in Firefox 4.0b1 michael@0: {"rlxf.dll", MAKE_VERSION(1,2,323,1)}, michael@0: michael@0: // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox michael@0: // Adobe photoshop library, now redundant in later installations michael@0: {"psicon.dll", ALL_VERSIONS}, michael@0: michael@0: // Topcrash in Firefox 4 betas (bug 618899) michael@0: {"accelerator.dll", MAKE_VERSION(3,2,1,6)}, michael@0: michael@0: // Topcrash with Roboform in Firefox 8 (bug 699134) michael@0: {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)}, michael@0: {"roboform.dll", MAKE_VERSION(7,6,1,0)}, michael@0: michael@0: // Topcrash with Babylon Toolbar on FF16+ (bug 721264) michael@0: {"babyfox.dll", ALL_VERSIONS}, michael@0: michael@0: // sprotector.dll crashes, bug 957258 michael@0: {"sprotector.dll", ALL_VERSIONS}, michael@0: michael@0: // Topcrash with Websense Endpoint, bug 828184 michael@0: {"qipcap.dll", MAKE_VERSION(7, 6, 815, 1)}, michael@0: michael@0: // leave these two in always for tests michael@0: { "mozdllblockingtest.dll", ALL_VERSIONS }, michael@0: { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL }, michael@0: michael@0: // Windows Media Foundation FLAC decoder and type sniffer (bug 839031). michael@0: { "mfflac.dll", ALL_VERSIONS }, michael@0: michael@0: // Older Relevant Knowledge DLLs cause us to crash (bug 904001). michael@0: { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) }, michael@0: { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) }, michael@0: { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) }, michael@0: { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) }, michael@0: michael@0: // Older belgian ID card software causes Firefox to crash or hang on michael@0: // shutdown, bug 831285 and 918399. michael@0: { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) }, michael@0: michael@0: // bug 925459, bitguard crashes michael@0: { "bitguard.dll", ALL_VERSIONS }, michael@0: michael@0: // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed michael@0: // Software is discontinued/unsupported michael@0: { "atkdx11disp.dll", ALL_VERSIONS }, michael@0: michael@0: // Topcrash with Conduit SearchProtect, bug 944542 michael@0: { "spvc32.dll", ALL_VERSIONS }, michael@0: michael@0: // XP topcrash with F-Secure, bug 970362 michael@0: { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY }, michael@0: michael@0: // Topcrash with V-bates, bug 1002748 and bug 1023239 michael@0: { "libinject.dll", UNVERSIONED }, michael@0: { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP }, michael@0: { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP }, michael@0: michael@0: // Crashes with RoboForm2Go written against old SDK, bug 988311 michael@0: { "rf-firefox-22.dll", ALL_VERSIONS }, michael@0: michael@0: { nullptr, 0 } michael@0: }; michael@0: michael@0: #ifndef STATUS_DLL_NOT_FOUND michael@0: #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L) michael@0: #endif michael@0: michael@0: // define this for very verbose dll load debug spew michael@0: #undef DEBUG_very_verbose michael@0: michael@0: static const char kBlockedDllsParameter[] = "BlockedDllList="; michael@0: static const int kBlockedDllsParameterLen = michael@0: sizeof(kBlockedDllsParameter) - 1; michael@0: michael@0: static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n"; michael@0: static const int kBlocklistInitFailedParameterLen = michael@0: sizeof(kBlocklistInitFailedParameter) - 1; michael@0: michael@0: static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n"; michael@0: static const int kUser32BeforeBlocklistParameterLen = michael@0: sizeof(kUser32BeforeBlocklistParameter) - 1; michael@0: michael@0: static DWORD sThreadLoadingXPCOMModule; michael@0: static bool sBlocklistInitFailed; michael@0: static bool sUser32BeforeBlocklist; michael@0: michael@0: // Duplicated from xpcom glue. Ideally this should be shared. michael@0: void michael@0: printf_stderr(const char *fmt, ...) michael@0: { michael@0: if (IsDebuggerPresent()) { michael@0: char buf[2048]; michael@0: va_list args; michael@0: va_start(args, fmt); michael@0: vsnprintf(buf, sizeof(buf), fmt, args); michael@0: buf[sizeof(buf) - 1] = '\0'; michael@0: va_end(args); michael@0: OutputDebugStringA(buf); michael@0: } michael@0: michael@0: FILE *fp = _fdopen(_dup(2), "a"); michael@0: if (!fp) michael@0: return; michael@0: michael@0: va_list args; michael@0: va_start(args, fmt); michael@0: vfprintf(fp, fmt, args); michael@0: va_end(args); michael@0: michael@0: fclose(fp); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle); michael@0: michael@0: static LdrLoadDll_func stub_LdrLoadDll = 0; michael@0: michael@0: template michael@0: struct RVAMap { michael@0: RVAMap(HANDLE map, DWORD offset) { michael@0: SYSTEM_INFO info; michael@0: GetSystemInfo(&info); michael@0: michael@0: DWORD alignedOffset = (offset / info.dwAllocationGranularity) * michael@0: info.dwAllocationGranularity; michael@0: michael@0: MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf"); michael@0: michael@0: mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset, michael@0: sizeof(T) + (offset - alignedOffset)); michael@0: michael@0: mMappedView = mRealView ? reinterpret_cast((char*)mRealView + (offset - alignedOffset)) : michael@0: nullptr; michael@0: } michael@0: ~RVAMap() { michael@0: if (mRealView) { michael@0: ::UnmapViewOfFile(mRealView); michael@0: } michael@0: } michael@0: operator const T*() const { return mMappedView; } michael@0: const T* operator->() const { return mMappedView; } michael@0: private: michael@0: const T* mMappedView; michael@0: void* mRealView; michael@0: }; michael@0: michael@0: bool michael@0: CheckASLR(const wchar_t* path) michael@0: { michael@0: bool retval = false; michael@0: michael@0: HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, michael@0: nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, michael@0: nullptr); michael@0: if (file != INVALID_HANDLE_VALUE) { michael@0: HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, michael@0: nullptr); michael@0: if (map) { michael@0: RVAMap peHeader(map, 0); michael@0: if (peHeader) { michael@0: RVAMap ntHeader(map, peHeader->e_lfanew); michael@0: if (ntHeader) { michael@0: // If the DLL has no code, permit it regardless of ASLR status. michael@0: if (ntHeader->OptionalHeader.SizeOfCode == 0) { michael@0: retval = true; michael@0: } michael@0: // Check to see if the DLL supports ASLR michael@0: else if ((ntHeader->OptionalHeader.DllCharacteristics & michael@0: IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) { michael@0: retval = true; michael@0: } michael@0: } michael@0: } michael@0: ::CloseHandle(map); michael@0: } michael@0: ::CloseHandle(file); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: DWORD michael@0: GetTimestamp(const wchar_t* path) michael@0: { michael@0: DWORD timestamp = 0; michael@0: michael@0: HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, michael@0: nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, michael@0: nullptr); michael@0: if (file != INVALID_HANDLE_VALUE) { michael@0: HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, michael@0: nullptr); michael@0: if (map) { michael@0: RVAMap peHeader(map, 0); michael@0: if (peHeader) { michael@0: RVAMap ntHeader(map, peHeader->e_lfanew); michael@0: if (ntHeader) { michael@0: timestamp = ntHeader->FileHeader.TimeDateStamp; michael@0: } michael@0: } michael@0: ::CloseHandle(map); michael@0: } michael@0: ::CloseHandle(file); michael@0: } michael@0: michael@0: return timestamp; michael@0: } michael@0: michael@0: // This lock protects both the reentrancy sentinel and the crash reporter michael@0: // data structures. michael@0: static CRITICAL_SECTION sLock; michael@0: michael@0: /** michael@0: * Some versions of Windows call LoadLibraryEx to get the version information michael@0: * for a DLL, which causes our patched LdrLoadDll implementation to re-enter michael@0: * itself and cause infinite recursion and a stack-exhaustion crash. We protect michael@0: * against reentrancy by allowing recursive loads of the same DLL. michael@0: * michael@0: * Note that we don't use __declspec(thread) because that doesn't work in DLLs michael@0: * loaded via LoadLibrary and there can be a limited number of TLS slots, so michael@0: * we roll our own. michael@0: */ michael@0: class ReentrancySentinel michael@0: { michael@0: public: michael@0: explicit ReentrancySentinel(const char* dllName) michael@0: { michael@0: DWORD currentThreadId = GetCurrentThreadId(); michael@0: AutoCriticalSection lock(&sLock); michael@0: mPreviousDllName = (*sThreadMap)[currentThreadId]; michael@0: michael@0: // If there is a DLL currently being loaded and it has the same name michael@0: // as the current attempt, we're re-entering. michael@0: mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName); michael@0: (*sThreadMap)[currentThreadId] = dllName; michael@0: } michael@0: michael@0: ~ReentrancySentinel() michael@0: { michael@0: DWORD currentThreadId = GetCurrentThreadId(); michael@0: AutoCriticalSection lock(&sLock); michael@0: (*sThreadMap)[currentThreadId] = mPreviousDllName; michael@0: } michael@0: michael@0: bool BailOut() const michael@0: { michael@0: return mReentered; michael@0: }; michael@0: michael@0: static void InitializeStatics() michael@0: { michael@0: InitializeCriticalSection(&sLock); michael@0: sThreadMap = new std::map; michael@0: } michael@0: michael@0: private: michael@0: static std::map* sThreadMap; michael@0: michael@0: const char* mPreviousDllName; michael@0: bool mReentered; michael@0: }; michael@0: michael@0: std::map* ReentrancySentinel::sThreadMap; michael@0: michael@0: /** michael@0: * This is a linked list of DLLs that have been blocked. It doesn't use michael@0: * mozilla::LinkedList because this is an append-only list and doesn't need michael@0: * to be doubly linked. michael@0: */ michael@0: class DllBlockSet michael@0: { michael@0: public: michael@0: static void Add(const char* name, unsigned long long version); michael@0: michael@0: // Write the list of blocked DLLs to a file HANDLE. This method is run after michael@0: // a crash occurs and must therefore not use the heap, etc. michael@0: static void Write(HANDLE file); michael@0: michael@0: private: michael@0: DllBlockSet(const char* name, unsigned long long version) michael@0: : mName(name) michael@0: , mVersion(version) michael@0: , mNext(nullptr) michael@0: { michael@0: } michael@0: michael@0: const char* mName; // points into the sWindowsDllBlocklist string michael@0: unsigned long long mVersion; michael@0: DllBlockSet* mNext; michael@0: michael@0: static DllBlockSet* gFirst; michael@0: }; michael@0: michael@0: DllBlockSet* DllBlockSet::gFirst; michael@0: michael@0: void michael@0: DllBlockSet::Add(const char* name, unsigned long long version) michael@0: { michael@0: AutoCriticalSection lock(&sLock); michael@0: for (DllBlockSet* b = gFirst; b; b = b->mNext) { michael@0: if (0 == strcmp(b->mName, name) && b->mVersion == version) { michael@0: return; michael@0: } michael@0: } michael@0: // Not already present michael@0: DllBlockSet* n = new DllBlockSet(name, version); michael@0: n->mNext = gFirst; michael@0: gFirst = n; michael@0: } michael@0: michael@0: void michael@0: DllBlockSet::Write(HANDLE file) michael@0: { michael@0: AutoCriticalSection lock(&sLock); michael@0: DWORD nBytes; michael@0: michael@0: // Because this method is called after a crash occurs, and uses heap memory, michael@0: // protect this entire block with a structured exception handler. michael@0: MOZ_SEH_TRY { michael@0: for (DllBlockSet* b = gFirst; b; b = b->mNext) { michael@0: // write name[,v.v.v.v]; michael@0: WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr); michael@0: if (b->mVersion != -1) { michael@0: WriteFile(file, ",", 1, &nBytes, nullptr); michael@0: uint16_t parts[4]; michael@0: parts[0] = b->mVersion >> 48; michael@0: parts[1] = (b->mVersion >> 32) & 0xFFFF; michael@0: parts[2] = (b->mVersion >> 16) & 0xFFFF; michael@0: parts[3] = b->mVersion & 0xFFFF; michael@0: for (int p = 0; p < 4; ++p) { michael@0: char buf[32]; michael@0: ltoa(parts[p], buf, 10); michael@0: WriteFile(file, buf, strlen(buf), &nBytes, nullptr); michael@0: if (p != 3) { michael@0: WriteFile(file, ".", 1, &nBytes, nullptr); michael@0: } michael@0: } michael@0: } michael@0: WriteFile(file, ";", 1, &nBytes, nullptr); michael@0: } michael@0: } michael@0: MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { } michael@0: } michael@0: michael@0: static michael@0: wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) michael@0: { michael@0: // In Windows 8, the first parameter seems to be used for more than just the michael@0: // path name. For example, its numerical value can be 1. Passing a non-valid michael@0: // pointer to SearchPathW will cause a crash, so we need to check to see if we michael@0: // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW. michael@0: PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? nullptr : filePath; michael@0: michael@0: // figure out the length of the string that we need michael@0: DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, michael@0: nullptr); michael@0: if (pathlen == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: wchar_t* full_fname = new wchar_t[pathlen+1]; michael@0: if (!full_fname) { michael@0: // couldn't allocate memory? michael@0: return nullptr; michael@0: } michael@0: michael@0: // now actually grab it michael@0: SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname, michael@0: nullptr); michael@0: return full_fname; michael@0: } michael@0: michael@0: // No builtin function to find the last character matching a set michael@0: static wchar_t* lastslash(wchar_t* s, int len) michael@0: { michael@0: for (wchar_t* c = s + len - 1; c >= s; --c) { michael@0: if (*c == L'\\' || *c == L'/') { michael@0: return c; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static NTSTATUS NTAPI michael@0: patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle) michael@0: { michael@0: // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion michael@0: #define DLLNAME_MAX 128 michael@0: char dllName[DLLNAME_MAX+1]; michael@0: wchar_t *dll_part; michael@0: char *dot; michael@0: DllBlockInfo *info; michael@0: michael@0: int len = moduleFileName->Length / 2; michael@0: wchar_t *fname = moduleFileName->Buffer; michael@0: nsAutoArrayPtr full_fname; michael@0: michael@0: // The filename isn't guaranteed to be null terminated, but in practice michael@0: // it always will be; ensure that this is so, and bail if not. michael@0: // This is done instead of the more robust approach because of bug 527122, michael@0: // where lots of weird things were happening when we tried to make a copy. michael@0: if (moduleFileName->MaximumLength < moduleFileName->Length+2 || michael@0: fname[len] != 0) michael@0: { michael@0: #ifdef DEBUG michael@0: printf_stderr("LdrLoadDll: non-null terminated string found!\n"); michael@0: #endif michael@0: goto continue_loading; michael@0: } michael@0: michael@0: dll_part = lastslash(fname, len); michael@0: if (dll_part) { michael@0: dll_part = dll_part + 1; michael@0: len -= dll_part - fname; michael@0: } else { michael@0: dll_part = fname; michael@0: } michael@0: michael@0: #ifdef DEBUG_very_verbose michael@0: printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len); michael@0: #endif michael@0: michael@0: // if it's too long, then, we assume we won't want to block it, michael@0: // since DLLNAME_MAX should be at least long enough to hold the longest michael@0: // entry in our blocklist. michael@0: if (len > DLLNAME_MAX) { michael@0: #ifdef DEBUG michael@0: printf_stderr("LdrLoadDll: len too long! %d\n", len); michael@0: #endif michael@0: goto continue_loading; michael@0: } michael@0: michael@0: // copy over to our char byte buffer, lowercasing ASCII as we go michael@0: for (int i = 0; i < len; i++) { michael@0: wchar_t c = dll_part[i]; michael@0: michael@0: if (c > 0x7f) { michael@0: // welp, it's not ascii; if we need to add non-ascii things to michael@0: // our blocklist, we'll have to remove this limitation. michael@0: goto continue_loading; michael@0: } michael@0: michael@0: // ensure that dll name is all lowercase michael@0: if (c >= 'A' && c <= 'Z') michael@0: c += 'a' - 'A'; michael@0: michael@0: dllName[i] = (char) c; michael@0: } michael@0: michael@0: dllName[len] = 0; michael@0: michael@0: #ifdef DEBUG_very_verbose michael@0: printf_stderr("LdrLoadDll: dll name '%s'\n", dllName); michael@0: #endif michael@0: michael@0: // Block a suspicious binary that uses various 12-digit hex strings michael@0: // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138) michael@0: dot = strchr(dllName, '.'); michael@0: if (dot && (strchr(dot+1, '.') == dot+13)) { michael@0: char * end = nullptr; michael@0: _strtoui64(dot+1, &end, 16); michael@0: if (end == dot+13) { michael@0: return STATUS_DLL_NOT_FOUND; michael@0: } michael@0: } michael@0: michael@0: // then compare to everything on the blocklist michael@0: info = &sWindowsDllBlocklist[0]; michael@0: while (info->name) { michael@0: if (strcmp(info->name, dllName) == 0) michael@0: break; michael@0: michael@0: info++; michael@0: } michael@0: michael@0: if (info->name) { michael@0: bool load_ok = false; michael@0: michael@0: #ifdef DEBUG_very_verbose michael@0: printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name); michael@0: #endif michael@0: michael@0: if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) && michael@0: !IsWin8OrLater()) { michael@0: goto continue_loading; michael@0: } michael@0: michael@0: if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) && michael@0: IsWin2003OrLater()) { michael@0: goto continue_loading; michael@0: } michael@0: michael@0: unsigned long long fVersion = ALL_VERSIONS; michael@0: michael@0: if (info->maxVersion != ALL_VERSIONS) { michael@0: ReentrancySentinel sentinel(dllName); michael@0: if (sentinel.BailOut()) { michael@0: goto continue_loading; michael@0: } michael@0: michael@0: full_fname = getFullPath(filePath, fname); michael@0: if (!full_fname) { michael@0: // uh, we couldn't find the DLL at all, so... michael@0: printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); michael@0: return STATUS_DLL_NOT_FOUND; michael@0: } michael@0: michael@0: if (info->flags & DllBlockInfo::USE_TIMESTAMP) { michael@0: fVersion = GetTimestamp(full_fname); michael@0: if (fVersion > info->maxVersion) { michael@0: load_ok = true; michael@0: } michael@0: } else { michael@0: DWORD zero; michael@0: DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero); michael@0: michael@0: // If we failed to get the version information, we block. michael@0: michael@0: if (infoSize != 0) { michael@0: nsAutoArrayPtr infoData(new unsigned char[infoSize]); michael@0: VS_FIXEDFILEINFO *vInfo; michael@0: UINT vInfoLen; michael@0: michael@0: if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) && michael@0: VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen)) michael@0: { michael@0: fVersion = michael@0: ((unsigned long long)vInfo->dwFileVersionMS) << 32 | michael@0: ((unsigned long long)vInfo->dwFileVersionLS); michael@0: michael@0: // finally do the version check, and if it's greater than our block michael@0: // version, keep loading michael@0: if (fVersion > info->maxVersion) michael@0: load_ok = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!load_ok) { michael@0: printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName); michael@0: DllBlockSet::Add(info->name, fVersion); michael@0: return STATUS_DLL_NOT_FOUND; michael@0: } michael@0: } michael@0: michael@0: continue_loading: michael@0: #ifdef DEBUG_very_verbose michael@0: printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer); michael@0: #endif michael@0: michael@0: if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) { michael@0: // Check to ensure that the DLL has ASLR. michael@0: full_fname = getFullPath(filePath, fname); michael@0: if (!full_fname) { michael@0: // uh, we couldn't find the DLL at all, so... michael@0: printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); michael@0: return STATUS_DLL_NOT_FOUND; michael@0: } michael@0: michael@0: if (IsVistaOrLater() && !CheckASLR(full_fname)) { michael@0: printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName); michael@0: return STATUS_DLL_NOT_FOUND; michael@0: } michael@0: } michael@0: michael@0: return stub_LdrLoadDll(filePath, flags, moduleFileName, handle); michael@0: } michael@0: michael@0: WindowsDllInterceptor NtDllIntercept; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: NS_EXPORT void michael@0: DllBlocklist_Initialize() michael@0: { michael@0: if (GetModuleHandleA("user32.dll")) { michael@0: sUser32BeforeBlocklist = true; michael@0: } michael@0: michael@0: NtDllIntercept.Init("ntdll.dll"); michael@0: michael@0: ReentrancySentinel::InitializeStatics(); michael@0: michael@0: // We specifically use a detour, because there are cases where external michael@0: // code also tries to hook LdrLoadDll, and doesn't know how to relocate our michael@0: // nop space patches. (Bug 951827) michael@0: bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast(patched_LdrLoadDll), (void**) &stub_LdrLoadDll); michael@0: michael@0: if (!ok) { michael@0: sBlocklistInitFailed = true; michael@0: #ifdef DEBUG michael@0: printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: NS_EXPORT void michael@0: DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread) michael@0: { michael@0: if (inXPCOMLoadOnMainThread) { michael@0: MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this"); michael@0: sThreadLoadingXPCOMModule = GetCurrentThreadId(); michael@0: } else { michael@0: sThreadLoadingXPCOMModule = 0; michael@0: } michael@0: } michael@0: michael@0: NS_EXPORT void michael@0: DllBlocklist_WriteNotes(HANDLE file) michael@0: { michael@0: DWORD nBytes; michael@0: michael@0: WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr); michael@0: DllBlockSet::Write(file); michael@0: WriteFile(file, "\n", 1, &nBytes, nullptr); michael@0: michael@0: if (sBlocklistInitFailed) { michael@0: WriteFile(file, kBlocklistInitFailedParameter, michael@0: kBlocklistInitFailedParameterLen, &nBytes, nullptr); michael@0: } michael@0: michael@0: if (sUser32BeforeBlocklist) { michael@0: WriteFile(file, kUser32BeforeBlocklistParameter, michael@0: kUser32BeforeBlocklistParameterLen, &nBytes, nullptr); michael@0: } michael@0: }