1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mozglue/build/WindowsDllBlocklist.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,726 @@ 1.4 +/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <windows.h> 1.10 +#include <winternl.h> 1.11 +#include <io.h> 1.12 + 1.13 +#pragma warning( push ) 1.14 +#pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h 1.15 +#include <map> 1.16 +#pragma warning( pop ) 1.17 + 1.18 +#define MOZ_NO_MOZALLOC 1.19 +#include "nsAutoPtr.h" 1.20 + 1.21 +#include "nsWindowsDllInterceptor.h" 1.22 +#include "mozilla/WindowsVersion.h" 1.23 +#include "nsWindowsHelpers.h" 1.24 + 1.25 +using namespace mozilla; 1.26 + 1.27 +#define ALL_VERSIONS ((unsigned long long)-1LL) 1.28 + 1.29 +// DLLs sometimes ship without a version number, particularly early 1.30 +// releases. Blocking "version <= 0" has the effect of blocking unversioned 1.31 +// DLLs (since the call to get version info fails), but not blocking 1.32 +// any versioned instance. 1.33 +#define UNVERSIONED ((unsigned long long)0LL) 1.34 + 1.35 +// Convert the 4 (decimal) components of a DLL version number into a 1.36 +// single unsigned long long, as needed by the blocklist 1.37 +#define MAKE_VERSION(a,b,c,d)\ 1.38 + ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL) 1.39 + 1.40 +struct DllBlockInfo { 1.41 + // The name of the DLL -- in LOWERCASE! It will be compared to 1.42 + // a lowercase version of the DLL name only. 1.43 + const char *name; 1.44 + 1.45 + // If maxVersion is ALL_VERSIONS, we'll block all versions of this 1.46 + // dll. Otherwise, we'll block all versions less than or equal to 1.47 + // the given version, as queried by GetFileVersionInfo and 1.48 + // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields. 1.49 + // 1.50 + // Note that the version is usually 4 components, which is A.B.C.D 1.51 + // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity), 1.52 + // but it's not required to be of that format. 1.53 + // 1.54 + // If the USE_TIMESTAMP flag is set, then we use the timestamp from 1.55 + // the IMAGE_FILE_HEADER in lieu of a version number. 1.56 + unsigned long long maxVersion; 1.57 + 1.58 + enum { 1.59 + FLAGS_DEFAULT = 0, 1.60 + BLOCK_WIN8PLUS_ONLY = 1, 1.61 + BLOCK_XP_ONLY = 2, 1.62 + USE_TIMESTAMP = 4, 1.63 + } flags; 1.64 +}; 1.65 + 1.66 +static DllBlockInfo sWindowsDllBlocklist[] = { 1.67 + // EXAMPLE: 1.68 + // { "uxtheme.dll", ALL_VERSIONS }, 1.69 + // { "uxtheme.dll", 0x0000123400000000ULL }, 1.70 + // The DLL name must be in lowercase! 1.71 + 1.72 + // NPFFAddon - Known malware 1.73 + { "npffaddon.dll", ALL_VERSIONS}, 1.74 + 1.75 + // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted 1.76 + {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)}, 1.77 + 1.78 + // calc.dll - Suspected malware 1.79 + {"calc.dll", MAKE_VERSION(1,0,0,1)}, 1.80 + 1.81 + // hook.dll - Suspected malware 1.82 + {"hook.dll", ALL_VERSIONS}, 1.83 + 1.84 + // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances 1.85 + // of this DLL cause crashes 1.86 + {"googledesktopnetwork3.dll", UNVERSIONED}, 1.87 + 1.88 + // rdolib.dll - Suspected malware 1.89 + {"rdolib.dll", MAKE_VERSION(6,0,88,4)}, 1.90 + 1.91 + // fgjk4wvb.dll - Suspected malware 1.92 + {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)}, 1.93 + 1.94 + // radhslib.dll - Naomi internet filter - unmaintained since 2006 1.95 + {"radhslib.dll", UNVERSIONED}, 1.96 + 1.97 + // Music download filter for vkontakte.ru - old instances 1.98 + // of this DLL cause crashes 1.99 + {"vksaver.dll", MAKE_VERSION(2,2,2,0)}, 1.100 + 1.101 + // Topcrash in Firefox 4.0b1 1.102 + {"rlxf.dll", MAKE_VERSION(1,2,323,1)}, 1.103 + 1.104 + // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox 1.105 + // Adobe photoshop library, now redundant in later installations 1.106 + {"psicon.dll", ALL_VERSIONS}, 1.107 + 1.108 + // Topcrash in Firefox 4 betas (bug 618899) 1.109 + {"accelerator.dll", MAKE_VERSION(3,2,1,6)}, 1.110 + 1.111 + // Topcrash with Roboform in Firefox 8 (bug 699134) 1.112 + {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)}, 1.113 + {"roboform.dll", MAKE_VERSION(7,6,1,0)}, 1.114 + 1.115 + // Topcrash with Babylon Toolbar on FF16+ (bug 721264) 1.116 + {"babyfox.dll", ALL_VERSIONS}, 1.117 + 1.118 + // sprotector.dll crashes, bug 957258 1.119 + {"sprotector.dll", ALL_VERSIONS}, 1.120 + 1.121 + // Topcrash with Websense Endpoint, bug 828184 1.122 + {"qipcap.dll", MAKE_VERSION(7, 6, 815, 1)}, 1.123 + 1.124 + // leave these two in always for tests 1.125 + { "mozdllblockingtest.dll", ALL_VERSIONS }, 1.126 + { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL }, 1.127 + 1.128 + // Windows Media Foundation FLAC decoder and type sniffer (bug 839031). 1.129 + { "mfflac.dll", ALL_VERSIONS }, 1.130 + 1.131 + // Older Relevant Knowledge DLLs cause us to crash (bug 904001). 1.132 + { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) }, 1.133 + { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) }, 1.134 + { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) }, 1.135 + { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) }, 1.136 + 1.137 + // Older belgian ID card software causes Firefox to crash or hang on 1.138 + // shutdown, bug 831285 and 918399. 1.139 + { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) }, 1.140 + 1.141 + // bug 925459, bitguard crashes 1.142 + { "bitguard.dll", ALL_VERSIONS }, 1.143 + 1.144 + // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed 1.145 + // Software is discontinued/unsupported 1.146 + { "atkdx11disp.dll", ALL_VERSIONS }, 1.147 + 1.148 + // Topcrash with Conduit SearchProtect, bug 944542 1.149 + { "spvc32.dll", ALL_VERSIONS }, 1.150 + 1.151 + // XP topcrash with F-Secure, bug 970362 1.152 + { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY }, 1.153 + 1.154 + // Topcrash with V-bates, bug 1002748 and bug 1023239 1.155 + { "libinject.dll", UNVERSIONED }, 1.156 + { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP }, 1.157 + { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP }, 1.158 + 1.159 + // Crashes with RoboForm2Go written against old SDK, bug 988311 1.160 + { "rf-firefox-22.dll", ALL_VERSIONS }, 1.161 + 1.162 + { nullptr, 0 } 1.163 +}; 1.164 + 1.165 +#ifndef STATUS_DLL_NOT_FOUND 1.166 +#define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L) 1.167 +#endif 1.168 + 1.169 +// define this for very verbose dll load debug spew 1.170 +#undef DEBUG_very_verbose 1.171 + 1.172 +static const char kBlockedDllsParameter[] = "BlockedDllList="; 1.173 +static const int kBlockedDllsParameterLen = 1.174 + sizeof(kBlockedDllsParameter) - 1; 1.175 + 1.176 +static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n"; 1.177 +static const int kBlocklistInitFailedParameterLen = 1.178 + sizeof(kBlocklistInitFailedParameter) - 1; 1.179 + 1.180 +static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n"; 1.181 +static const int kUser32BeforeBlocklistParameterLen = 1.182 + sizeof(kUser32BeforeBlocklistParameter) - 1; 1.183 + 1.184 +static DWORD sThreadLoadingXPCOMModule; 1.185 +static bool sBlocklistInitFailed; 1.186 +static bool sUser32BeforeBlocklist; 1.187 + 1.188 +// Duplicated from xpcom glue. Ideally this should be shared. 1.189 +void 1.190 +printf_stderr(const char *fmt, ...) 1.191 +{ 1.192 + if (IsDebuggerPresent()) { 1.193 + char buf[2048]; 1.194 + va_list args; 1.195 + va_start(args, fmt); 1.196 + vsnprintf(buf, sizeof(buf), fmt, args); 1.197 + buf[sizeof(buf) - 1] = '\0'; 1.198 + va_end(args); 1.199 + OutputDebugStringA(buf); 1.200 + } 1.201 + 1.202 + FILE *fp = _fdopen(_dup(2), "a"); 1.203 + if (!fp) 1.204 + return; 1.205 + 1.206 + va_list args; 1.207 + va_start(args, fmt); 1.208 + vfprintf(fp, fmt, args); 1.209 + va_end(args); 1.210 + 1.211 + fclose(fp); 1.212 +} 1.213 + 1.214 +namespace { 1.215 + 1.216 +typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle); 1.217 + 1.218 +static LdrLoadDll_func stub_LdrLoadDll = 0; 1.219 + 1.220 +template <class T> 1.221 +struct RVAMap { 1.222 + RVAMap(HANDLE map, DWORD offset) { 1.223 + SYSTEM_INFO info; 1.224 + GetSystemInfo(&info); 1.225 + 1.226 + DWORD alignedOffset = (offset / info.dwAllocationGranularity) * 1.227 + info.dwAllocationGranularity; 1.228 + 1.229 + MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf"); 1.230 + 1.231 + mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset, 1.232 + sizeof(T) + (offset - alignedOffset)); 1.233 + 1.234 + mMappedView = mRealView ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset)) : 1.235 + nullptr; 1.236 + } 1.237 + ~RVAMap() { 1.238 + if (mRealView) { 1.239 + ::UnmapViewOfFile(mRealView); 1.240 + } 1.241 + } 1.242 + operator const T*() const { return mMappedView; } 1.243 + const T* operator->() const { return mMappedView; } 1.244 +private: 1.245 + const T* mMappedView; 1.246 + void* mRealView; 1.247 +}; 1.248 + 1.249 +bool 1.250 +CheckASLR(const wchar_t* path) 1.251 +{ 1.252 + bool retval = false; 1.253 + 1.254 + HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, 1.255 + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 1.256 + nullptr); 1.257 + if (file != INVALID_HANDLE_VALUE) { 1.258 + HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, 1.259 + nullptr); 1.260 + if (map) { 1.261 + RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0); 1.262 + if (peHeader) { 1.263 + RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew); 1.264 + if (ntHeader) { 1.265 + // If the DLL has no code, permit it regardless of ASLR status. 1.266 + if (ntHeader->OptionalHeader.SizeOfCode == 0) { 1.267 + retval = true; 1.268 + } 1.269 + // Check to see if the DLL supports ASLR 1.270 + else if ((ntHeader->OptionalHeader.DllCharacteristics & 1.271 + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) { 1.272 + retval = true; 1.273 + } 1.274 + } 1.275 + } 1.276 + ::CloseHandle(map); 1.277 + } 1.278 + ::CloseHandle(file); 1.279 + } 1.280 + 1.281 + return retval; 1.282 +} 1.283 + 1.284 +DWORD 1.285 +GetTimestamp(const wchar_t* path) 1.286 +{ 1.287 + DWORD timestamp = 0; 1.288 + 1.289 + HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, 1.290 + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 1.291 + nullptr); 1.292 + if (file != INVALID_HANDLE_VALUE) { 1.293 + HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, 1.294 + nullptr); 1.295 + if (map) { 1.296 + RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0); 1.297 + if (peHeader) { 1.298 + RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew); 1.299 + if (ntHeader) { 1.300 + timestamp = ntHeader->FileHeader.TimeDateStamp; 1.301 + } 1.302 + } 1.303 + ::CloseHandle(map); 1.304 + } 1.305 + ::CloseHandle(file); 1.306 + } 1.307 + 1.308 + return timestamp; 1.309 +} 1.310 + 1.311 +// This lock protects both the reentrancy sentinel and the crash reporter 1.312 +// data structures. 1.313 +static CRITICAL_SECTION sLock; 1.314 + 1.315 +/** 1.316 + * Some versions of Windows call LoadLibraryEx to get the version information 1.317 + * for a DLL, which causes our patched LdrLoadDll implementation to re-enter 1.318 + * itself and cause infinite recursion and a stack-exhaustion crash. We protect 1.319 + * against reentrancy by allowing recursive loads of the same DLL. 1.320 + * 1.321 + * Note that we don't use __declspec(thread) because that doesn't work in DLLs 1.322 + * loaded via LoadLibrary and there can be a limited number of TLS slots, so 1.323 + * we roll our own. 1.324 + */ 1.325 +class ReentrancySentinel 1.326 +{ 1.327 +public: 1.328 + explicit ReentrancySentinel(const char* dllName) 1.329 + { 1.330 + DWORD currentThreadId = GetCurrentThreadId(); 1.331 + AutoCriticalSection lock(&sLock); 1.332 + mPreviousDllName = (*sThreadMap)[currentThreadId]; 1.333 + 1.334 + // If there is a DLL currently being loaded and it has the same name 1.335 + // as the current attempt, we're re-entering. 1.336 + mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName); 1.337 + (*sThreadMap)[currentThreadId] = dllName; 1.338 + } 1.339 + 1.340 + ~ReentrancySentinel() 1.341 + { 1.342 + DWORD currentThreadId = GetCurrentThreadId(); 1.343 + AutoCriticalSection lock(&sLock); 1.344 + (*sThreadMap)[currentThreadId] = mPreviousDllName; 1.345 + } 1.346 + 1.347 + bool BailOut() const 1.348 + { 1.349 + return mReentered; 1.350 + }; 1.351 + 1.352 + static void InitializeStatics() 1.353 + { 1.354 + InitializeCriticalSection(&sLock); 1.355 + sThreadMap = new std::map<DWORD, const char*>; 1.356 + } 1.357 + 1.358 +private: 1.359 + static std::map<DWORD, const char*>* sThreadMap; 1.360 + 1.361 + const char* mPreviousDllName; 1.362 + bool mReentered; 1.363 +}; 1.364 + 1.365 +std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap; 1.366 + 1.367 +/** 1.368 + * This is a linked list of DLLs that have been blocked. It doesn't use 1.369 + * mozilla::LinkedList because this is an append-only list and doesn't need 1.370 + * to be doubly linked. 1.371 + */ 1.372 +class DllBlockSet 1.373 +{ 1.374 +public: 1.375 + static void Add(const char* name, unsigned long long version); 1.376 + 1.377 + // Write the list of blocked DLLs to a file HANDLE. This method is run after 1.378 + // a crash occurs and must therefore not use the heap, etc. 1.379 + static void Write(HANDLE file); 1.380 + 1.381 +private: 1.382 + DllBlockSet(const char* name, unsigned long long version) 1.383 + : mName(name) 1.384 + , mVersion(version) 1.385 + , mNext(nullptr) 1.386 + { 1.387 + } 1.388 + 1.389 + const char* mName; // points into the sWindowsDllBlocklist string 1.390 + unsigned long long mVersion; 1.391 + DllBlockSet* mNext; 1.392 + 1.393 + static DllBlockSet* gFirst; 1.394 +}; 1.395 + 1.396 +DllBlockSet* DllBlockSet::gFirst; 1.397 + 1.398 +void 1.399 +DllBlockSet::Add(const char* name, unsigned long long version) 1.400 +{ 1.401 + AutoCriticalSection lock(&sLock); 1.402 + for (DllBlockSet* b = gFirst; b; b = b->mNext) { 1.403 + if (0 == strcmp(b->mName, name) && b->mVersion == version) { 1.404 + return; 1.405 + } 1.406 + } 1.407 + // Not already present 1.408 + DllBlockSet* n = new DllBlockSet(name, version); 1.409 + n->mNext = gFirst; 1.410 + gFirst = n; 1.411 +} 1.412 + 1.413 +void 1.414 +DllBlockSet::Write(HANDLE file) 1.415 +{ 1.416 + AutoCriticalSection lock(&sLock); 1.417 + DWORD nBytes; 1.418 + 1.419 + // Because this method is called after a crash occurs, and uses heap memory, 1.420 + // protect this entire block with a structured exception handler. 1.421 + MOZ_SEH_TRY { 1.422 + for (DllBlockSet* b = gFirst; b; b = b->mNext) { 1.423 + // write name[,v.v.v.v]; 1.424 + WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr); 1.425 + if (b->mVersion != -1) { 1.426 + WriteFile(file, ",", 1, &nBytes, nullptr); 1.427 + uint16_t parts[4]; 1.428 + parts[0] = b->mVersion >> 48; 1.429 + parts[1] = (b->mVersion >> 32) & 0xFFFF; 1.430 + parts[2] = (b->mVersion >> 16) & 0xFFFF; 1.431 + parts[3] = b->mVersion & 0xFFFF; 1.432 + for (int p = 0; p < 4; ++p) { 1.433 + char buf[32]; 1.434 + ltoa(parts[p], buf, 10); 1.435 + WriteFile(file, buf, strlen(buf), &nBytes, nullptr); 1.436 + if (p != 3) { 1.437 + WriteFile(file, ".", 1, &nBytes, nullptr); 1.438 + } 1.439 + } 1.440 + } 1.441 + WriteFile(file, ";", 1, &nBytes, nullptr); 1.442 + } 1.443 + } 1.444 + MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { } 1.445 +} 1.446 + 1.447 +static 1.448 +wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) 1.449 +{ 1.450 + // In Windows 8, the first parameter seems to be used for more than just the 1.451 + // path name. For example, its numerical value can be 1. Passing a non-valid 1.452 + // pointer to SearchPathW will cause a crash, so we need to check to see if we 1.453 + // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW. 1.454 + PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? nullptr : filePath; 1.455 + 1.456 + // figure out the length of the string that we need 1.457 + DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, 1.458 + nullptr); 1.459 + if (pathlen == 0) { 1.460 + return nullptr; 1.461 + } 1.462 + 1.463 + wchar_t* full_fname = new wchar_t[pathlen+1]; 1.464 + if (!full_fname) { 1.465 + // couldn't allocate memory? 1.466 + return nullptr; 1.467 + } 1.468 + 1.469 + // now actually grab it 1.470 + SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname, 1.471 + nullptr); 1.472 + return full_fname; 1.473 +} 1.474 + 1.475 +// No builtin function to find the last character matching a set 1.476 +static wchar_t* lastslash(wchar_t* s, int len) 1.477 +{ 1.478 + for (wchar_t* c = s + len - 1; c >= s; --c) { 1.479 + if (*c == L'\\' || *c == L'/') { 1.480 + return c; 1.481 + } 1.482 + } 1.483 + return nullptr; 1.484 +} 1.485 + 1.486 +static NTSTATUS NTAPI 1.487 +patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle) 1.488 +{ 1.489 + // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion 1.490 +#define DLLNAME_MAX 128 1.491 + char dllName[DLLNAME_MAX+1]; 1.492 + wchar_t *dll_part; 1.493 + char *dot; 1.494 + DllBlockInfo *info; 1.495 + 1.496 + int len = moduleFileName->Length / 2; 1.497 + wchar_t *fname = moduleFileName->Buffer; 1.498 + nsAutoArrayPtr<wchar_t> full_fname; 1.499 + 1.500 + // The filename isn't guaranteed to be null terminated, but in practice 1.501 + // it always will be; ensure that this is so, and bail if not. 1.502 + // This is done instead of the more robust approach because of bug 527122, 1.503 + // where lots of weird things were happening when we tried to make a copy. 1.504 + if (moduleFileName->MaximumLength < moduleFileName->Length+2 || 1.505 + fname[len] != 0) 1.506 + { 1.507 +#ifdef DEBUG 1.508 + printf_stderr("LdrLoadDll: non-null terminated string found!\n"); 1.509 +#endif 1.510 + goto continue_loading; 1.511 + } 1.512 + 1.513 + dll_part = lastslash(fname, len); 1.514 + if (dll_part) { 1.515 + dll_part = dll_part + 1; 1.516 + len -= dll_part - fname; 1.517 + } else { 1.518 + dll_part = fname; 1.519 + } 1.520 + 1.521 +#ifdef DEBUG_very_verbose 1.522 + printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len); 1.523 +#endif 1.524 + 1.525 + // if it's too long, then, we assume we won't want to block it, 1.526 + // since DLLNAME_MAX should be at least long enough to hold the longest 1.527 + // entry in our blocklist. 1.528 + if (len > DLLNAME_MAX) { 1.529 +#ifdef DEBUG 1.530 + printf_stderr("LdrLoadDll: len too long! %d\n", len); 1.531 +#endif 1.532 + goto continue_loading; 1.533 + } 1.534 + 1.535 + // copy over to our char byte buffer, lowercasing ASCII as we go 1.536 + for (int i = 0; i < len; i++) { 1.537 + wchar_t c = dll_part[i]; 1.538 + 1.539 + if (c > 0x7f) { 1.540 + // welp, it's not ascii; if we need to add non-ascii things to 1.541 + // our blocklist, we'll have to remove this limitation. 1.542 + goto continue_loading; 1.543 + } 1.544 + 1.545 + // ensure that dll name is all lowercase 1.546 + if (c >= 'A' && c <= 'Z') 1.547 + c += 'a' - 'A'; 1.548 + 1.549 + dllName[i] = (char) c; 1.550 + } 1.551 + 1.552 + dllName[len] = 0; 1.553 + 1.554 +#ifdef DEBUG_very_verbose 1.555 + printf_stderr("LdrLoadDll: dll name '%s'\n", dllName); 1.556 +#endif 1.557 + 1.558 + // Block a suspicious binary that uses various 12-digit hex strings 1.559 + // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138) 1.560 + dot = strchr(dllName, '.'); 1.561 + if (dot && (strchr(dot+1, '.') == dot+13)) { 1.562 + char * end = nullptr; 1.563 + _strtoui64(dot+1, &end, 16); 1.564 + if (end == dot+13) { 1.565 + return STATUS_DLL_NOT_FOUND; 1.566 + } 1.567 + } 1.568 + 1.569 + // then compare to everything on the blocklist 1.570 + info = &sWindowsDllBlocklist[0]; 1.571 + while (info->name) { 1.572 + if (strcmp(info->name, dllName) == 0) 1.573 + break; 1.574 + 1.575 + info++; 1.576 + } 1.577 + 1.578 + if (info->name) { 1.579 + bool load_ok = false; 1.580 + 1.581 +#ifdef DEBUG_very_verbose 1.582 + printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name); 1.583 +#endif 1.584 + 1.585 + if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) && 1.586 + !IsWin8OrLater()) { 1.587 + goto continue_loading; 1.588 + } 1.589 + 1.590 + if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) && 1.591 + IsWin2003OrLater()) { 1.592 + goto continue_loading; 1.593 + } 1.594 + 1.595 + unsigned long long fVersion = ALL_VERSIONS; 1.596 + 1.597 + if (info->maxVersion != ALL_VERSIONS) { 1.598 + ReentrancySentinel sentinel(dllName); 1.599 + if (sentinel.BailOut()) { 1.600 + goto continue_loading; 1.601 + } 1.602 + 1.603 + full_fname = getFullPath(filePath, fname); 1.604 + if (!full_fname) { 1.605 + // uh, we couldn't find the DLL at all, so... 1.606 + printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); 1.607 + return STATUS_DLL_NOT_FOUND; 1.608 + } 1.609 + 1.610 + if (info->flags & DllBlockInfo::USE_TIMESTAMP) { 1.611 + fVersion = GetTimestamp(full_fname); 1.612 + if (fVersion > info->maxVersion) { 1.613 + load_ok = true; 1.614 + } 1.615 + } else { 1.616 + DWORD zero; 1.617 + DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero); 1.618 + 1.619 + // If we failed to get the version information, we block. 1.620 + 1.621 + if (infoSize != 0) { 1.622 + nsAutoArrayPtr<unsigned char> infoData(new unsigned char[infoSize]); 1.623 + VS_FIXEDFILEINFO *vInfo; 1.624 + UINT vInfoLen; 1.625 + 1.626 + if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) && 1.627 + VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen)) 1.628 + { 1.629 + fVersion = 1.630 + ((unsigned long long)vInfo->dwFileVersionMS) << 32 | 1.631 + ((unsigned long long)vInfo->dwFileVersionLS); 1.632 + 1.633 + // finally do the version check, and if it's greater than our block 1.634 + // version, keep loading 1.635 + if (fVersion > info->maxVersion) 1.636 + load_ok = true; 1.637 + } 1.638 + } 1.639 + } 1.640 + } 1.641 + 1.642 + if (!load_ok) { 1.643 + printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName); 1.644 + DllBlockSet::Add(info->name, fVersion); 1.645 + return STATUS_DLL_NOT_FOUND; 1.646 + } 1.647 + } 1.648 + 1.649 +continue_loading: 1.650 +#ifdef DEBUG_very_verbose 1.651 + printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer); 1.652 +#endif 1.653 + 1.654 + if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) { 1.655 + // Check to ensure that the DLL has ASLR. 1.656 + full_fname = getFullPath(filePath, fname); 1.657 + if (!full_fname) { 1.658 + // uh, we couldn't find the DLL at all, so... 1.659 + printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); 1.660 + return STATUS_DLL_NOT_FOUND; 1.661 + } 1.662 + 1.663 + if (IsVistaOrLater() && !CheckASLR(full_fname)) { 1.664 + printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName); 1.665 + return STATUS_DLL_NOT_FOUND; 1.666 + } 1.667 + } 1.668 + 1.669 + return stub_LdrLoadDll(filePath, flags, moduleFileName, handle); 1.670 +} 1.671 + 1.672 +WindowsDllInterceptor NtDllIntercept; 1.673 + 1.674 +} // anonymous namespace 1.675 + 1.676 +NS_EXPORT void 1.677 +DllBlocklist_Initialize() 1.678 +{ 1.679 + if (GetModuleHandleA("user32.dll")) { 1.680 + sUser32BeforeBlocklist = true; 1.681 + } 1.682 + 1.683 + NtDllIntercept.Init("ntdll.dll"); 1.684 + 1.685 + ReentrancySentinel::InitializeStatics(); 1.686 + 1.687 + // We specifically use a detour, because there are cases where external 1.688 + // code also tries to hook LdrLoadDll, and doesn't know how to relocate our 1.689 + // nop space patches. (Bug 951827) 1.690 + bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll); 1.691 + 1.692 + if (!ok) { 1.693 + sBlocklistInitFailed = true; 1.694 +#ifdef DEBUG 1.695 + printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n"); 1.696 +#endif 1.697 + } 1.698 +} 1.699 + 1.700 +NS_EXPORT void 1.701 +DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread) 1.702 +{ 1.703 + if (inXPCOMLoadOnMainThread) { 1.704 + MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this"); 1.705 + sThreadLoadingXPCOMModule = GetCurrentThreadId(); 1.706 + } else { 1.707 + sThreadLoadingXPCOMModule = 0; 1.708 + } 1.709 +} 1.710 + 1.711 +NS_EXPORT void 1.712 +DllBlocklist_WriteNotes(HANDLE file) 1.713 +{ 1.714 + DWORD nBytes; 1.715 + 1.716 + WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr); 1.717 + DllBlockSet::Write(file); 1.718 + WriteFile(file, "\n", 1, &nBytes, nullptr); 1.719 + 1.720 + if (sBlocklistInitFailed) { 1.721 + WriteFile(file, kBlocklistInitFailedParameter, 1.722 + kBlocklistInitFailedParameterLen, &nBytes, nullptr); 1.723 + } 1.724 + 1.725 + if (sUser32BeforeBlocklist) { 1.726 + WriteFile(file, kUser32BeforeBlocklistParameter, 1.727 + kUser32BeforeBlocklistParameterLen, &nBytes, nullptr); 1.728 + } 1.729 +}