|
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include <windows.h> |
|
7 #include <winternl.h> |
|
8 #include <io.h> |
|
9 |
|
10 #pragma warning( push ) |
|
11 #pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h |
|
12 #include <map> |
|
13 #pragma warning( pop ) |
|
14 |
|
15 #define MOZ_NO_MOZALLOC |
|
16 #include "nsAutoPtr.h" |
|
17 |
|
18 #include "nsWindowsDllInterceptor.h" |
|
19 #include "mozilla/WindowsVersion.h" |
|
20 #include "nsWindowsHelpers.h" |
|
21 |
|
22 using namespace mozilla; |
|
23 |
|
24 #define ALL_VERSIONS ((unsigned long long)-1LL) |
|
25 |
|
26 // DLLs sometimes ship without a version number, particularly early |
|
27 // releases. Blocking "version <= 0" has the effect of blocking unversioned |
|
28 // DLLs (since the call to get version info fails), but not blocking |
|
29 // any versioned instance. |
|
30 #define UNVERSIONED ((unsigned long long)0LL) |
|
31 |
|
32 // Convert the 4 (decimal) components of a DLL version number into a |
|
33 // single unsigned long long, as needed by the blocklist |
|
34 #define MAKE_VERSION(a,b,c,d)\ |
|
35 ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL) |
|
36 |
|
37 struct DllBlockInfo { |
|
38 // The name of the DLL -- in LOWERCASE! It will be compared to |
|
39 // a lowercase version of the DLL name only. |
|
40 const char *name; |
|
41 |
|
42 // If maxVersion is ALL_VERSIONS, we'll block all versions of this |
|
43 // dll. Otherwise, we'll block all versions less than or equal to |
|
44 // the given version, as queried by GetFileVersionInfo and |
|
45 // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields. |
|
46 // |
|
47 // Note that the version is usually 4 components, which is A.B.C.D |
|
48 // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity), |
|
49 // but it's not required to be of that format. |
|
50 // |
|
51 // If the USE_TIMESTAMP flag is set, then we use the timestamp from |
|
52 // the IMAGE_FILE_HEADER in lieu of a version number. |
|
53 unsigned long long maxVersion; |
|
54 |
|
55 enum { |
|
56 FLAGS_DEFAULT = 0, |
|
57 BLOCK_WIN8PLUS_ONLY = 1, |
|
58 BLOCK_XP_ONLY = 2, |
|
59 USE_TIMESTAMP = 4, |
|
60 } flags; |
|
61 }; |
|
62 |
|
63 static DllBlockInfo sWindowsDllBlocklist[] = { |
|
64 // EXAMPLE: |
|
65 // { "uxtheme.dll", ALL_VERSIONS }, |
|
66 // { "uxtheme.dll", 0x0000123400000000ULL }, |
|
67 // The DLL name must be in lowercase! |
|
68 |
|
69 // NPFFAddon - Known malware |
|
70 { "npffaddon.dll", ALL_VERSIONS}, |
|
71 |
|
72 // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted |
|
73 {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)}, |
|
74 |
|
75 // calc.dll - Suspected malware |
|
76 {"calc.dll", MAKE_VERSION(1,0,0,1)}, |
|
77 |
|
78 // hook.dll - Suspected malware |
|
79 {"hook.dll", ALL_VERSIONS}, |
|
80 |
|
81 // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances |
|
82 // of this DLL cause crashes |
|
83 {"googledesktopnetwork3.dll", UNVERSIONED}, |
|
84 |
|
85 // rdolib.dll - Suspected malware |
|
86 {"rdolib.dll", MAKE_VERSION(6,0,88,4)}, |
|
87 |
|
88 // fgjk4wvb.dll - Suspected malware |
|
89 {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)}, |
|
90 |
|
91 // radhslib.dll - Naomi internet filter - unmaintained since 2006 |
|
92 {"radhslib.dll", UNVERSIONED}, |
|
93 |
|
94 // Music download filter for vkontakte.ru - old instances |
|
95 // of this DLL cause crashes |
|
96 {"vksaver.dll", MAKE_VERSION(2,2,2,0)}, |
|
97 |
|
98 // Topcrash in Firefox 4.0b1 |
|
99 {"rlxf.dll", MAKE_VERSION(1,2,323,1)}, |
|
100 |
|
101 // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox |
|
102 // Adobe photoshop library, now redundant in later installations |
|
103 {"psicon.dll", ALL_VERSIONS}, |
|
104 |
|
105 // Topcrash in Firefox 4 betas (bug 618899) |
|
106 {"accelerator.dll", MAKE_VERSION(3,2,1,6)}, |
|
107 |
|
108 // Topcrash with Roboform in Firefox 8 (bug 699134) |
|
109 {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)}, |
|
110 {"roboform.dll", MAKE_VERSION(7,6,1,0)}, |
|
111 |
|
112 // Topcrash with Babylon Toolbar on FF16+ (bug 721264) |
|
113 {"babyfox.dll", ALL_VERSIONS}, |
|
114 |
|
115 // sprotector.dll crashes, bug 957258 |
|
116 {"sprotector.dll", ALL_VERSIONS}, |
|
117 |
|
118 // Topcrash with Websense Endpoint, bug 828184 |
|
119 {"qipcap.dll", MAKE_VERSION(7, 6, 815, 1)}, |
|
120 |
|
121 // leave these two in always for tests |
|
122 { "mozdllblockingtest.dll", ALL_VERSIONS }, |
|
123 { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL }, |
|
124 |
|
125 // Windows Media Foundation FLAC decoder and type sniffer (bug 839031). |
|
126 { "mfflac.dll", ALL_VERSIONS }, |
|
127 |
|
128 // Older Relevant Knowledge DLLs cause us to crash (bug 904001). |
|
129 { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) }, |
|
130 { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) }, |
|
131 { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) }, |
|
132 { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) }, |
|
133 |
|
134 // Older belgian ID card software causes Firefox to crash or hang on |
|
135 // shutdown, bug 831285 and 918399. |
|
136 { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) }, |
|
137 |
|
138 // bug 925459, bitguard crashes |
|
139 { "bitguard.dll", ALL_VERSIONS }, |
|
140 |
|
141 // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed |
|
142 // Software is discontinued/unsupported |
|
143 { "atkdx11disp.dll", ALL_VERSIONS }, |
|
144 |
|
145 // Topcrash with Conduit SearchProtect, bug 944542 |
|
146 { "spvc32.dll", ALL_VERSIONS }, |
|
147 |
|
148 // XP topcrash with F-Secure, bug 970362 |
|
149 { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY }, |
|
150 |
|
151 // Topcrash with V-bates, bug 1002748 and bug 1023239 |
|
152 { "libinject.dll", UNVERSIONED }, |
|
153 { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP }, |
|
154 { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP }, |
|
155 |
|
156 // Crashes with RoboForm2Go written against old SDK, bug 988311 |
|
157 { "rf-firefox-22.dll", ALL_VERSIONS }, |
|
158 |
|
159 { nullptr, 0 } |
|
160 }; |
|
161 |
|
162 #ifndef STATUS_DLL_NOT_FOUND |
|
163 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L) |
|
164 #endif |
|
165 |
|
166 // define this for very verbose dll load debug spew |
|
167 #undef DEBUG_very_verbose |
|
168 |
|
169 static const char kBlockedDllsParameter[] = "BlockedDllList="; |
|
170 static const int kBlockedDllsParameterLen = |
|
171 sizeof(kBlockedDllsParameter) - 1; |
|
172 |
|
173 static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n"; |
|
174 static const int kBlocklistInitFailedParameterLen = |
|
175 sizeof(kBlocklistInitFailedParameter) - 1; |
|
176 |
|
177 static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n"; |
|
178 static const int kUser32BeforeBlocklistParameterLen = |
|
179 sizeof(kUser32BeforeBlocklistParameter) - 1; |
|
180 |
|
181 static DWORD sThreadLoadingXPCOMModule; |
|
182 static bool sBlocklistInitFailed; |
|
183 static bool sUser32BeforeBlocklist; |
|
184 |
|
185 // Duplicated from xpcom glue. Ideally this should be shared. |
|
186 void |
|
187 printf_stderr(const char *fmt, ...) |
|
188 { |
|
189 if (IsDebuggerPresent()) { |
|
190 char buf[2048]; |
|
191 va_list args; |
|
192 va_start(args, fmt); |
|
193 vsnprintf(buf, sizeof(buf), fmt, args); |
|
194 buf[sizeof(buf) - 1] = '\0'; |
|
195 va_end(args); |
|
196 OutputDebugStringA(buf); |
|
197 } |
|
198 |
|
199 FILE *fp = _fdopen(_dup(2), "a"); |
|
200 if (!fp) |
|
201 return; |
|
202 |
|
203 va_list args; |
|
204 va_start(args, fmt); |
|
205 vfprintf(fp, fmt, args); |
|
206 va_end(args); |
|
207 |
|
208 fclose(fp); |
|
209 } |
|
210 |
|
211 namespace { |
|
212 |
|
213 typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle); |
|
214 |
|
215 static LdrLoadDll_func stub_LdrLoadDll = 0; |
|
216 |
|
217 template <class T> |
|
218 struct RVAMap { |
|
219 RVAMap(HANDLE map, DWORD offset) { |
|
220 SYSTEM_INFO info; |
|
221 GetSystemInfo(&info); |
|
222 |
|
223 DWORD alignedOffset = (offset / info.dwAllocationGranularity) * |
|
224 info.dwAllocationGranularity; |
|
225 |
|
226 MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf"); |
|
227 |
|
228 mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset, |
|
229 sizeof(T) + (offset - alignedOffset)); |
|
230 |
|
231 mMappedView = mRealView ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset)) : |
|
232 nullptr; |
|
233 } |
|
234 ~RVAMap() { |
|
235 if (mRealView) { |
|
236 ::UnmapViewOfFile(mRealView); |
|
237 } |
|
238 } |
|
239 operator const T*() const { return mMappedView; } |
|
240 const T* operator->() const { return mMappedView; } |
|
241 private: |
|
242 const T* mMappedView; |
|
243 void* mRealView; |
|
244 }; |
|
245 |
|
246 bool |
|
247 CheckASLR(const wchar_t* path) |
|
248 { |
|
249 bool retval = false; |
|
250 |
|
251 HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, |
|
252 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, |
|
253 nullptr); |
|
254 if (file != INVALID_HANDLE_VALUE) { |
|
255 HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, |
|
256 nullptr); |
|
257 if (map) { |
|
258 RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0); |
|
259 if (peHeader) { |
|
260 RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew); |
|
261 if (ntHeader) { |
|
262 // If the DLL has no code, permit it regardless of ASLR status. |
|
263 if (ntHeader->OptionalHeader.SizeOfCode == 0) { |
|
264 retval = true; |
|
265 } |
|
266 // Check to see if the DLL supports ASLR |
|
267 else if ((ntHeader->OptionalHeader.DllCharacteristics & |
|
268 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) { |
|
269 retval = true; |
|
270 } |
|
271 } |
|
272 } |
|
273 ::CloseHandle(map); |
|
274 } |
|
275 ::CloseHandle(file); |
|
276 } |
|
277 |
|
278 return retval; |
|
279 } |
|
280 |
|
281 DWORD |
|
282 GetTimestamp(const wchar_t* path) |
|
283 { |
|
284 DWORD timestamp = 0; |
|
285 |
|
286 HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, |
|
287 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, |
|
288 nullptr); |
|
289 if (file != INVALID_HANDLE_VALUE) { |
|
290 HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, |
|
291 nullptr); |
|
292 if (map) { |
|
293 RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0); |
|
294 if (peHeader) { |
|
295 RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew); |
|
296 if (ntHeader) { |
|
297 timestamp = ntHeader->FileHeader.TimeDateStamp; |
|
298 } |
|
299 } |
|
300 ::CloseHandle(map); |
|
301 } |
|
302 ::CloseHandle(file); |
|
303 } |
|
304 |
|
305 return timestamp; |
|
306 } |
|
307 |
|
308 // This lock protects both the reentrancy sentinel and the crash reporter |
|
309 // data structures. |
|
310 static CRITICAL_SECTION sLock; |
|
311 |
|
312 /** |
|
313 * Some versions of Windows call LoadLibraryEx to get the version information |
|
314 * for a DLL, which causes our patched LdrLoadDll implementation to re-enter |
|
315 * itself and cause infinite recursion and a stack-exhaustion crash. We protect |
|
316 * against reentrancy by allowing recursive loads of the same DLL. |
|
317 * |
|
318 * Note that we don't use __declspec(thread) because that doesn't work in DLLs |
|
319 * loaded via LoadLibrary and there can be a limited number of TLS slots, so |
|
320 * we roll our own. |
|
321 */ |
|
322 class ReentrancySentinel |
|
323 { |
|
324 public: |
|
325 explicit ReentrancySentinel(const char* dllName) |
|
326 { |
|
327 DWORD currentThreadId = GetCurrentThreadId(); |
|
328 AutoCriticalSection lock(&sLock); |
|
329 mPreviousDllName = (*sThreadMap)[currentThreadId]; |
|
330 |
|
331 // If there is a DLL currently being loaded and it has the same name |
|
332 // as the current attempt, we're re-entering. |
|
333 mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName); |
|
334 (*sThreadMap)[currentThreadId] = dllName; |
|
335 } |
|
336 |
|
337 ~ReentrancySentinel() |
|
338 { |
|
339 DWORD currentThreadId = GetCurrentThreadId(); |
|
340 AutoCriticalSection lock(&sLock); |
|
341 (*sThreadMap)[currentThreadId] = mPreviousDllName; |
|
342 } |
|
343 |
|
344 bool BailOut() const |
|
345 { |
|
346 return mReentered; |
|
347 }; |
|
348 |
|
349 static void InitializeStatics() |
|
350 { |
|
351 InitializeCriticalSection(&sLock); |
|
352 sThreadMap = new std::map<DWORD, const char*>; |
|
353 } |
|
354 |
|
355 private: |
|
356 static std::map<DWORD, const char*>* sThreadMap; |
|
357 |
|
358 const char* mPreviousDllName; |
|
359 bool mReentered; |
|
360 }; |
|
361 |
|
362 std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap; |
|
363 |
|
364 /** |
|
365 * This is a linked list of DLLs that have been blocked. It doesn't use |
|
366 * mozilla::LinkedList because this is an append-only list and doesn't need |
|
367 * to be doubly linked. |
|
368 */ |
|
369 class DllBlockSet |
|
370 { |
|
371 public: |
|
372 static void Add(const char* name, unsigned long long version); |
|
373 |
|
374 // Write the list of blocked DLLs to a file HANDLE. This method is run after |
|
375 // a crash occurs and must therefore not use the heap, etc. |
|
376 static void Write(HANDLE file); |
|
377 |
|
378 private: |
|
379 DllBlockSet(const char* name, unsigned long long version) |
|
380 : mName(name) |
|
381 , mVersion(version) |
|
382 , mNext(nullptr) |
|
383 { |
|
384 } |
|
385 |
|
386 const char* mName; // points into the sWindowsDllBlocklist string |
|
387 unsigned long long mVersion; |
|
388 DllBlockSet* mNext; |
|
389 |
|
390 static DllBlockSet* gFirst; |
|
391 }; |
|
392 |
|
393 DllBlockSet* DllBlockSet::gFirst; |
|
394 |
|
395 void |
|
396 DllBlockSet::Add(const char* name, unsigned long long version) |
|
397 { |
|
398 AutoCriticalSection lock(&sLock); |
|
399 for (DllBlockSet* b = gFirst; b; b = b->mNext) { |
|
400 if (0 == strcmp(b->mName, name) && b->mVersion == version) { |
|
401 return; |
|
402 } |
|
403 } |
|
404 // Not already present |
|
405 DllBlockSet* n = new DllBlockSet(name, version); |
|
406 n->mNext = gFirst; |
|
407 gFirst = n; |
|
408 } |
|
409 |
|
410 void |
|
411 DllBlockSet::Write(HANDLE file) |
|
412 { |
|
413 AutoCriticalSection lock(&sLock); |
|
414 DWORD nBytes; |
|
415 |
|
416 // Because this method is called after a crash occurs, and uses heap memory, |
|
417 // protect this entire block with a structured exception handler. |
|
418 MOZ_SEH_TRY { |
|
419 for (DllBlockSet* b = gFirst; b; b = b->mNext) { |
|
420 // write name[,v.v.v.v]; |
|
421 WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr); |
|
422 if (b->mVersion != -1) { |
|
423 WriteFile(file, ",", 1, &nBytes, nullptr); |
|
424 uint16_t parts[4]; |
|
425 parts[0] = b->mVersion >> 48; |
|
426 parts[1] = (b->mVersion >> 32) & 0xFFFF; |
|
427 parts[2] = (b->mVersion >> 16) & 0xFFFF; |
|
428 parts[3] = b->mVersion & 0xFFFF; |
|
429 for (int p = 0; p < 4; ++p) { |
|
430 char buf[32]; |
|
431 ltoa(parts[p], buf, 10); |
|
432 WriteFile(file, buf, strlen(buf), &nBytes, nullptr); |
|
433 if (p != 3) { |
|
434 WriteFile(file, ".", 1, &nBytes, nullptr); |
|
435 } |
|
436 } |
|
437 } |
|
438 WriteFile(file, ";", 1, &nBytes, nullptr); |
|
439 } |
|
440 } |
|
441 MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { } |
|
442 } |
|
443 |
|
444 static |
|
445 wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) |
|
446 { |
|
447 // In Windows 8, the first parameter seems to be used for more than just the |
|
448 // path name. For example, its numerical value can be 1. Passing a non-valid |
|
449 // pointer to SearchPathW will cause a crash, so we need to check to see if we |
|
450 // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW. |
|
451 PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? nullptr : filePath; |
|
452 |
|
453 // figure out the length of the string that we need |
|
454 DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, |
|
455 nullptr); |
|
456 if (pathlen == 0) { |
|
457 return nullptr; |
|
458 } |
|
459 |
|
460 wchar_t* full_fname = new wchar_t[pathlen+1]; |
|
461 if (!full_fname) { |
|
462 // couldn't allocate memory? |
|
463 return nullptr; |
|
464 } |
|
465 |
|
466 // now actually grab it |
|
467 SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname, |
|
468 nullptr); |
|
469 return full_fname; |
|
470 } |
|
471 |
|
472 // No builtin function to find the last character matching a set |
|
473 static wchar_t* lastslash(wchar_t* s, int len) |
|
474 { |
|
475 for (wchar_t* c = s + len - 1; c >= s; --c) { |
|
476 if (*c == L'\\' || *c == L'/') { |
|
477 return c; |
|
478 } |
|
479 } |
|
480 return nullptr; |
|
481 } |
|
482 |
|
483 static NTSTATUS NTAPI |
|
484 patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle) |
|
485 { |
|
486 // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion |
|
487 #define DLLNAME_MAX 128 |
|
488 char dllName[DLLNAME_MAX+1]; |
|
489 wchar_t *dll_part; |
|
490 char *dot; |
|
491 DllBlockInfo *info; |
|
492 |
|
493 int len = moduleFileName->Length / 2; |
|
494 wchar_t *fname = moduleFileName->Buffer; |
|
495 nsAutoArrayPtr<wchar_t> full_fname; |
|
496 |
|
497 // The filename isn't guaranteed to be null terminated, but in practice |
|
498 // it always will be; ensure that this is so, and bail if not. |
|
499 // This is done instead of the more robust approach because of bug 527122, |
|
500 // where lots of weird things were happening when we tried to make a copy. |
|
501 if (moduleFileName->MaximumLength < moduleFileName->Length+2 || |
|
502 fname[len] != 0) |
|
503 { |
|
504 #ifdef DEBUG |
|
505 printf_stderr("LdrLoadDll: non-null terminated string found!\n"); |
|
506 #endif |
|
507 goto continue_loading; |
|
508 } |
|
509 |
|
510 dll_part = lastslash(fname, len); |
|
511 if (dll_part) { |
|
512 dll_part = dll_part + 1; |
|
513 len -= dll_part - fname; |
|
514 } else { |
|
515 dll_part = fname; |
|
516 } |
|
517 |
|
518 #ifdef DEBUG_very_verbose |
|
519 printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len); |
|
520 #endif |
|
521 |
|
522 // if it's too long, then, we assume we won't want to block it, |
|
523 // since DLLNAME_MAX should be at least long enough to hold the longest |
|
524 // entry in our blocklist. |
|
525 if (len > DLLNAME_MAX) { |
|
526 #ifdef DEBUG |
|
527 printf_stderr("LdrLoadDll: len too long! %d\n", len); |
|
528 #endif |
|
529 goto continue_loading; |
|
530 } |
|
531 |
|
532 // copy over to our char byte buffer, lowercasing ASCII as we go |
|
533 for (int i = 0; i < len; i++) { |
|
534 wchar_t c = dll_part[i]; |
|
535 |
|
536 if (c > 0x7f) { |
|
537 // welp, it's not ascii; if we need to add non-ascii things to |
|
538 // our blocklist, we'll have to remove this limitation. |
|
539 goto continue_loading; |
|
540 } |
|
541 |
|
542 // ensure that dll name is all lowercase |
|
543 if (c >= 'A' && c <= 'Z') |
|
544 c += 'a' - 'A'; |
|
545 |
|
546 dllName[i] = (char) c; |
|
547 } |
|
548 |
|
549 dllName[len] = 0; |
|
550 |
|
551 #ifdef DEBUG_very_verbose |
|
552 printf_stderr("LdrLoadDll: dll name '%s'\n", dllName); |
|
553 #endif |
|
554 |
|
555 // Block a suspicious binary that uses various 12-digit hex strings |
|
556 // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138) |
|
557 dot = strchr(dllName, '.'); |
|
558 if (dot && (strchr(dot+1, '.') == dot+13)) { |
|
559 char * end = nullptr; |
|
560 _strtoui64(dot+1, &end, 16); |
|
561 if (end == dot+13) { |
|
562 return STATUS_DLL_NOT_FOUND; |
|
563 } |
|
564 } |
|
565 |
|
566 // then compare to everything on the blocklist |
|
567 info = &sWindowsDllBlocklist[0]; |
|
568 while (info->name) { |
|
569 if (strcmp(info->name, dllName) == 0) |
|
570 break; |
|
571 |
|
572 info++; |
|
573 } |
|
574 |
|
575 if (info->name) { |
|
576 bool load_ok = false; |
|
577 |
|
578 #ifdef DEBUG_very_verbose |
|
579 printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name); |
|
580 #endif |
|
581 |
|
582 if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) && |
|
583 !IsWin8OrLater()) { |
|
584 goto continue_loading; |
|
585 } |
|
586 |
|
587 if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) && |
|
588 IsWin2003OrLater()) { |
|
589 goto continue_loading; |
|
590 } |
|
591 |
|
592 unsigned long long fVersion = ALL_VERSIONS; |
|
593 |
|
594 if (info->maxVersion != ALL_VERSIONS) { |
|
595 ReentrancySentinel sentinel(dllName); |
|
596 if (sentinel.BailOut()) { |
|
597 goto continue_loading; |
|
598 } |
|
599 |
|
600 full_fname = getFullPath(filePath, fname); |
|
601 if (!full_fname) { |
|
602 // uh, we couldn't find the DLL at all, so... |
|
603 printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); |
|
604 return STATUS_DLL_NOT_FOUND; |
|
605 } |
|
606 |
|
607 if (info->flags & DllBlockInfo::USE_TIMESTAMP) { |
|
608 fVersion = GetTimestamp(full_fname); |
|
609 if (fVersion > info->maxVersion) { |
|
610 load_ok = true; |
|
611 } |
|
612 } else { |
|
613 DWORD zero; |
|
614 DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero); |
|
615 |
|
616 // If we failed to get the version information, we block. |
|
617 |
|
618 if (infoSize != 0) { |
|
619 nsAutoArrayPtr<unsigned char> infoData(new unsigned char[infoSize]); |
|
620 VS_FIXEDFILEINFO *vInfo; |
|
621 UINT vInfoLen; |
|
622 |
|
623 if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) && |
|
624 VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen)) |
|
625 { |
|
626 fVersion = |
|
627 ((unsigned long long)vInfo->dwFileVersionMS) << 32 | |
|
628 ((unsigned long long)vInfo->dwFileVersionLS); |
|
629 |
|
630 // finally do the version check, and if it's greater than our block |
|
631 // version, keep loading |
|
632 if (fVersion > info->maxVersion) |
|
633 load_ok = true; |
|
634 } |
|
635 } |
|
636 } |
|
637 } |
|
638 |
|
639 if (!load_ok) { |
|
640 printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName); |
|
641 DllBlockSet::Add(info->name, fVersion); |
|
642 return STATUS_DLL_NOT_FOUND; |
|
643 } |
|
644 } |
|
645 |
|
646 continue_loading: |
|
647 #ifdef DEBUG_very_verbose |
|
648 printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer); |
|
649 #endif |
|
650 |
|
651 if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) { |
|
652 // Check to ensure that the DLL has ASLR. |
|
653 full_fname = getFullPath(filePath, fname); |
|
654 if (!full_fname) { |
|
655 // uh, we couldn't find the DLL at all, so... |
|
656 printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); |
|
657 return STATUS_DLL_NOT_FOUND; |
|
658 } |
|
659 |
|
660 if (IsVistaOrLater() && !CheckASLR(full_fname)) { |
|
661 printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName); |
|
662 return STATUS_DLL_NOT_FOUND; |
|
663 } |
|
664 } |
|
665 |
|
666 return stub_LdrLoadDll(filePath, flags, moduleFileName, handle); |
|
667 } |
|
668 |
|
669 WindowsDllInterceptor NtDllIntercept; |
|
670 |
|
671 } // anonymous namespace |
|
672 |
|
673 NS_EXPORT void |
|
674 DllBlocklist_Initialize() |
|
675 { |
|
676 if (GetModuleHandleA("user32.dll")) { |
|
677 sUser32BeforeBlocklist = true; |
|
678 } |
|
679 |
|
680 NtDllIntercept.Init("ntdll.dll"); |
|
681 |
|
682 ReentrancySentinel::InitializeStatics(); |
|
683 |
|
684 // We specifically use a detour, because there are cases where external |
|
685 // code also tries to hook LdrLoadDll, and doesn't know how to relocate our |
|
686 // nop space patches. (Bug 951827) |
|
687 bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll); |
|
688 |
|
689 if (!ok) { |
|
690 sBlocklistInitFailed = true; |
|
691 #ifdef DEBUG |
|
692 printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n"); |
|
693 #endif |
|
694 } |
|
695 } |
|
696 |
|
697 NS_EXPORT void |
|
698 DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread) |
|
699 { |
|
700 if (inXPCOMLoadOnMainThread) { |
|
701 MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this"); |
|
702 sThreadLoadingXPCOMModule = GetCurrentThreadId(); |
|
703 } else { |
|
704 sThreadLoadingXPCOMModule = 0; |
|
705 } |
|
706 } |
|
707 |
|
708 NS_EXPORT void |
|
709 DllBlocklist_WriteNotes(HANDLE file) |
|
710 { |
|
711 DWORD nBytes; |
|
712 |
|
713 WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr); |
|
714 DllBlockSet::Write(file); |
|
715 WriteFile(file, "\n", 1, &nBytes, nullptr); |
|
716 |
|
717 if (sBlocklistInitFailed) { |
|
718 WriteFile(file, kBlocklistInitFailedParameter, |
|
719 kBlocklistInitFailedParameterLen, &nBytes, nullptr); |
|
720 } |
|
721 |
|
722 if (sUser32BeforeBlocklist) { |
|
723 WriteFile(file, kUser32BeforeBlocklistParameter, |
|
724 kUser32BeforeBlocklistParameterLen, &nBytes, nullptr); |
|
725 } |
|
726 } |