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 
9 #pragma warning(push)
10 #pragma warning(disable : 4275 4530)  // See msvc-stl-wrapper.template.h
11 #include <map>
12 #pragma warning(pop)
13 
14 #include "Authenticode.h"
15 #include "BaseProfiler.h"
16 #include "nsWindowsDllInterceptor.h"
17 #include "mozilla/CmdLineAndEnvUtils.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/StackWalk_windows.h"
20 #include "mozilla/TimeStamp.h"
21 #include "mozilla/UniquePtr.h"
22 #include "mozilla/Vector.h"
23 #include "mozilla/WindowsProcessMitigations.h"
24 #include "mozilla/WindowsVersion.h"
25 #include "mozilla/WinHeaderOnlyUtils.h"
26 #include "nsWindowsHelpers.h"
27 #include "WindowsDllBlocklist.h"
28 #include "mozilla/AutoProfilerLabel.h"
29 #include "mozilla/glue/Debug.h"
30 #include "mozilla/glue/WindowsDllServices.h"
31 #include "mozilla/glue/WinUtils.h"
32 
33 // Start new implementation
34 #include "LoaderObserver.h"
35 #include "ModuleLoadFrame.h"
36 #include "mozilla/glue/WindowsUnicode.h"
37 
38 namespace mozilla {
39 
40 glue::Win32SRWLock gDllServicesLock;
41 glue::detail::DllServicesBase* gDllServices;
42 
43 }  // namespace mozilla
44 
45 using namespace mozilla;
46 
47 using CrashReporter::Annotation;
48 using CrashReporter::AnnotationWriter;
49 
50 #define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__},
51 #define DLL_BLOCKLIST_STRING_TYPE const char*
52 #include "mozilla/WindowsDllBlocklistLegacyDefs.h"
53 
54 // define this for very verbose dll load debug spew
55 #undef DEBUG_very_verbose
56 
57 static uint32_t sInitFlags;
58 static bool sBlocklistInitAttempted;
59 static bool sBlocklistInitFailed;
60 static bool sUser32BeforeBlocklist;
61 
62 typedef MOZ_NORETURN_PTR void(__fastcall* BaseThreadInitThunk_func)(
63     BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
64 static WindowsDllInterceptor::FuncHookType<BaseThreadInitThunk_func>
65     stub_BaseThreadInitThunk;
66 
67 typedef NTSTATUS(NTAPI* LdrLoadDll_func)(PWCHAR filePath, PULONG flags,
68                                          PUNICODE_STRING moduleFileName,
69                                          PHANDLE handle);
70 static WindowsDllInterceptor::FuncHookType<LdrLoadDll_func> stub_LdrLoadDll;
71 
72 #ifdef _M_AMD64
73 typedef decltype(RtlInstallFunctionTableCallback)*
74     RtlInstallFunctionTableCallback_func;
75 static WindowsDllInterceptor::FuncHookType<RtlInstallFunctionTableCallback_func>
76     stub_RtlInstallFunctionTableCallback;
77 
78 extern uint8_t* sMsMpegJitCodeRegionStart;
79 extern size_t sMsMpegJitCodeRegionSize;
80 
patched_RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,DWORD64 BaseAddress,DWORD Length,PGET_RUNTIME_FUNCTION_CALLBACK Callback,PVOID Context,PCWSTR OutOfProcessCallbackDll)81 BOOLEAN WINAPI patched_RtlInstallFunctionTableCallback(
82     DWORD64 TableIdentifier, DWORD64 BaseAddress, DWORD Length,
83     PGET_RUNTIME_FUNCTION_CALLBACK Callback, PVOID Context,
84     PCWSTR OutOfProcessCallbackDll) {
85   // msmpeg2vdec.dll sets up a function table callback for their JIT code that
86   // just terminates the process, because their JIT doesn't have unwind info.
87   // If we see this callback being registered, record the region address, so
88   // that StackWalk.cpp can avoid unwinding addresses in this region.
89   //
90   // To keep things simple I'm not tracking unloads of msmpeg2vdec.dll.
91   // Worst case the stack walker will needlessly avoid a few pages of memory.
92 
93   // Tricky: GetModuleHandleExW adds a ref by default; GetModuleHandleW doesn't.
94   HMODULE callbackModule = nullptr;
95   DWORD moduleFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
96                       GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
97 
98   // These GetModuleHandle calls enter a critical section on Win7.
99   AutoSuppressStackWalking suppress;
100 
101   if (GetModuleHandleExW(moduleFlags, (LPWSTR)Callback, &callbackModule) &&
102       GetModuleHandleW(L"msmpeg2vdec.dll") == callbackModule) {
103     sMsMpegJitCodeRegionStart = (uint8_t*)BaseAddress;
104     sMsMpegJitCodeRegionSize = Length;
105   }
106 
107   return stub_RtlInstallFunctionTableCallback(TableIdentifier, BaseAddress,
108                                               Length, Callback, Context,
109                                               OutOfProcessCallbackDll);
110 }
111 #endif
112 
113 template <class T>
114 struct RVAMap {
RVAMapRVAMap115   RVAMap(HANDLE map, DWORD offset) {
116     SYSTEM_INFO info;
117     GetSystemInfo(&info);
118 
119     DWORD alignedOffset =
120         (offset / info.dwAllocationGranularity) * info.dwAllocationGranularity;
121 
122     MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
123 
124     mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
125                                 sizeof(T) + (offset - alignedOffset));
126 
127     mMappedView =
128         mRealView
129             ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset))
130             : nullptr;
131   }
~RVAMapRVAMap132   ~RVAMap() {
133     if (mRealView) {
134       ::UnmapViewOfFile(mRealView);
135     }
136   }
operator const T*RVAMap137   operator const T*() const { return mMappedView; }
operator ->RVAMap138   const T* operator->() const { return mMappedView; }
139 
140  private:
141   const T* mMappedView;
142   void* mRealView;
143 };
144 
GetTimestamp(const wchar_t * path)145 static DWORD GetTimestamp(const wchar_t* path) {
146   DWORD timestamp = 0;
147 
148   HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr,
149                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
150   if (file != INVALID_HANDLE_VALUE) {
151     HANDLE map =
152         ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
153     if (map) {
154       RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
155       if (peHeader) {
156         RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
157         if (ntHeader) {
158           timestamp = ntHeader->FileHeader.TimeDateStamp;
159         }
160       }
161       ::CloseHandle(map);
162     }
163     ::CloseHandle(file);
164   }
165 
166   return timestamp;
167 }
168 
169 // This lock protects both the reentrancy sentinel and the crash reporter
170 // data structures.
171 static CRITICAL_SECTION sLock;
172 
173 /**
174  * Some versions of Windows call LoadLibraryEx to get the version information
175  * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
176  * itself and cause infinite recursion and a stack-exhaustion crash. We protect
177  * against reentrancy by allowing recursive loads of the same DLL.
178  *
179  * Note that we don't use __declspec(thread) because that doesn't work in DLLs
180  * loaded via LoadLibrary and there can be a limited number of TLS slots, so
181  * we roll our own.
182  */
183 class ReentrancySentinel {
184  public:
ReentrancySentinel(const char * dllName)185   explicit ReentrancySentinel(const char* dllName) {
186     DWORD currentThreadId = GetCurrentThreadId();
187     AutoCriticalSection lock(&sLock);
188     mPreviousDllName = (*sThreadMap)[currentThreadId];
189 
190     // If there is a DLL currently being loaded and it has the same name
191     // as the current attempt, we're re-entering.
192     mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
193     (*sThreadMap)[currentThreadId] = dllName;
194   }
195 
~ReentrancySentinel()196   ~ReentrancySentinel() {
197     DWORD currentThreadId = GetCurrentThreadId();
198     AutoCriticalSection lock(&sLock);
199     (*sThreadMap)[currentThreadId] = mPreviousDllName;
200   }
201 
BailOut() const202   bool BailOut() const { return mReentered; };
203 
InitializeStatics()204   static void InitializeStatics() {
205     InitializeCriticalSection(&sLock);
206     sThreadMap = new std::map<DWORD, const char*>;
207   }
208 
209  private:
210   static std::map<DWORD, const char*>* sThreadMap;
211 
212   const char* mPreviousDllName;
213   bool mReentered;
214 };
215 
216 std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
217 
218 class WritableBuffer {
219  public:
WritableBuffer()220   WritableBuffer() : mBuffer{0}, mLen(0) {}
221 
Write(const char * aData,size_t aLen)222   void Write(const char* aData, size_t aLen) {
223     size_t writable_len = std::min(aLen, Available());
224     memcpy(mBuffer + mLen, aData, writable_len);
225     mLen += writable_len;
226   }
227 
Length()228   size_t const Length() { return mLen; }
Data()229   const char* Data() { return mBuffer; }
230 
231  private:
Available()232   size_t const Available() { return sizeof(mBuffer) - mLen; }
233 
234   char mBuffer[1024];
235   size_t mLen;
236 };
237 
238 /**
239  * This is a linked list of DLLs that have been blocked. It doesn't use
240  * mozilla::LinkedList because this is an append-only list and doesn't need
241  * to be doubly linked.
242  */
243 class DllBlockSet {
244  public:
245   static void Add(const char* name, unsigned long long version);
246 
247   // Write the list of blocked DLLs to a WritableBuffer object. This method is
248   // run after a crash occurs and must therefore not use the heap, etc.
249   static void Write(WritableBuffer& buffer);
250 
251  private:
DllBlockSet(const char * name,unsigned long long version)252   DllBlockSet(const char* name, unsigned long long version)
253       : mName(name), mVersion(version), mNext(nullptr) {}
254 
255   const char* mName;  // points into the gWindowsDllBlocklist string
256   unsigned long long mVersion;
257   DllBlockSet* mNext;
258 
259   static DllBlockSet* gFirst;
260 };
261 
262 DllBlockSet* DllBlockSet::gFirst;
263 
Add(const char * name,unsigned long long version)264 void DllBlockSet::Add(const char* name, unsigned long long version) {
265   AutoCriticalSection lock(&sLock);
266   for (DllBlockSet* b = gFirst; b; b = b->mNext) {
267     if (0 == strcmp(b->mName, name) && b->mVersion == version) {
268       return;
269     }
270   }
271   // Not already present
272   DllBlockSet* n = new DllBlockSet(name, version);
273   n->mNext = gFirst;
274   gFirst = n;
275 }
276 
Write(WritableBuffer & buffer)277 void DllBlockSet::Write(WritableBuffer& buffer) {
278   // It would be nicer to use AutoCriticalSection here. However, its destructor
279   // might not run if an exception occurs, in which case we would never leave
280   // the critical section. (MSVC warns about this possibility.) So we
281   // enter and leave manually.
282   ::EnterCriticalSection(&sLock);
283 
284   // Because this method is called after a crash occurs, and uses heap memory,
285   // protect this entire block with a structured exception handler.
286   MOZ_SEH_TRY {
287     for (DllBlockSet* b = gFirst; b; b = b->mNext) {
288       // write name[,v.v.v.v];
289       buffer.Write(b->mName, strlen(b->mName));
290       if (b->mVersion != DllBlockInfo::ALL_VERSIONS) {
291         buffer.Write(",", 1);
292         uint16_t parts[4];
293         parts[0] = b->mVersion >> 48;
294         parts[1] = (b->mVersion >> 32) & 0xFFFF;
295         parts[2] = (b->mVersion >> 16) & 0xFFFF;
296         parts[3] = b->mVersion & 0xFFFF;
297         for (int p = 0; p < 4; ++p) {
298           char buf[32];
299           _ltoa_s(parts[p], buf, sizeof(buf), 10);
300           buffer.Write(buf, strlen(buf));
301           if (p != 3) {
302             buffer.Write(".", 1);
303           }
304         }
305       }
306       buffer.Write(";", 1);
307     }
308   }
309   MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {}
310 
311   ::LeaveCriticalSection(&sLock);
312 }
313 
getFullPath(PWCHAR filePath,wchar_t * fname)314 static UniquePtr<wchar_t[]> getFullPath(PWCHAR filePath, wchar_t* fname) {
315   // In Windows 8, the first parameter seems to be used for more than just the
316   // path name.  For example, its numerical value can be 1.  Passing a non-valid
317   // pointer to SearchPathW will cause a crash, so we need to check to see if we
318   // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
319   PWCHAR sanitizedFilePath = nullptr;
320   if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) {
321     sanitizedFilePath = filePath;
322   }
323 
324   // figure out the length of the string that we need
325   DWORD pathlen =
326       SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, nullptr);
327   if (pathlen == 0) {
328     return nullptr;
329   }
330 
331   auto full_fname = MakeUnique<wchar_t[]>(pathlen + 1);
332   if (!full_fname) {
333     // couldn't allocate memory?
334     return nullptr;
335   }
336 
337   // now actually grab it
338   SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(),
339               nullptr);
340   return full_fname;
341 }
342 
343 // No builtin function to find the last character matching a set
lastslash(wchar_t * s,int len)344 static wchar_t* lastslash(wchar_t* s, int len) {
345   for (wchar_t* c = s + len - 1; c >= s; --c) {
346     if (*c == L'\\' || *c == L'/') {
347       return c;
348     }
349   }
350   return nullptr;
351 }
352 
patched_LdrLoadDll(PWCHAR filePath,PULONG flags,PUNICODE_STRING moduleFileName,PHANDLE handle)353 static NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR filePath, PULONG flags,
354                                          PUNICODE_STRING moduleFileName,
355                                          PHANDLE handle) {
356   // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename
357   // portion
358 #define DLLNAME_MAX 128
359   char dllName[DLLNAME_MAX + 1];
360   wchar_t* dll_part;
361   char* dot;
362 
363   int len = moduleFileName->Length / 2;
364   wchar_t* fname = moduleFileName->Buffer;
365   UniquePtr<wchar_t[]> full_fname;
366 
367   // The filename isn't guaranteed to be null terminated, but in practice
368   // it always will be; ensure that this is so, and bail if not.
369   // This is done instead of the more robust approach because of bug 527122,
370   // where lots of weird things were happening when we tried to make a copy.
371   if (moduleFileName->MaximumLength < moduleFileName->Length + 2 ||
372       fname[len] != 0) {
373 #ifdef DEBUG
374     printf_stderr("LdrLoadDll: non-null terminated string found!\n");
375 #endif
376     goto continue_loading;
377   }
378 
379   dll_part = lastslash(fname, len);
380   if (dll_part) {
381     dll_part = dll_part + 1;
382     len -= dll_part - fname;
383   } else {
384     dll_part = fname;
385   }
386 
387 #ifdef DEBUG_very_verbose
388   printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
389 #endif
390 
391   // if it's too long, then, we assume we won't want to block it,
392   // since DLLNAME_MAX should be at least long enough to hold the longest
393   // entry in our blocklist.
394   if (len > DLLNAME_MAX) {
395 #ifdef DEBUG
396     printf_stderr("LdrLoadDll: len too long! %d\n", len);
397 #endif
398     goto continue_loading;
399   }
400 
401   // copy over to our char byte buffer, lowercasing ASCII as we go
402   for (int i = 0; i < len; i++) {
403     wchar_t c = dll_part[i];
404 
405     if (c > 0x7f) {
406       // welp, it's not ascii; if we need to add non-ascii things to
407       // our blocklist, we'll have to remove this limitation.
408       goto continue_loading;
409     }
410 
411     // ensure that dll name is all lowercase
412     if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
413 
414     dllName[i] = (char)c;
415   }
416 
417   dllName[len] = 0;
418 
419 #ifdef DEBUG_very_verbose
420   printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
421 #endif
422 
423   if (!(sInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
424     // Block a suspicious binary that uses various 12-digit hex strings
425     // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
426     dot = strchr(dllName, '.');
427     if (dot && (strchr(dot + 1, '.') == dot + 13)) {
428       char* end = nullptr;
429       _strtoui64(dot + 1, &end, 16);
430       if (end == dot + 13) {
431         return STATUS_DLL_NOT_FOUND;
432       }
433     }
434     // Block binaries where the filename is at least 16 hex digits
435     if (dot && ((dot - dllName) >= 16)) {
436       char* current = dllName;
437       while (current < dot && isxdigit(*current)) {
438         current++;
439       }
440       if (current == dot) {
441         return STATUS_DLL_NOT_FOUND;
442       }
443     }
444 
445     // then compare to everything on the blocklist
446     DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
447     while (info->mName) {
448       if (strcmp(info->mName, dllName) == 0) break;
449 
450       info++;
451     }
452 
453     if (info->mName) {
454       bool load_ok = false;
455 
456 #ifdef DEBUG_very_verbose
457       printf_stderr("LdrLoadDll: info->mName: '%s'\n", info->mName);
458 #endif
459 
460       if (info->mFlags & DllBlockInfo::REDIRECT_TO_NOOP_ENTRYPOINT) {
461         printf_stderr(
462             "LdrLoadDll: "
463             "Ignoring the REDIRECT_TO_NOOP_ENTRYPOINT flag\n");
464       }
465 
466       if ((info->mFlags & DllBlockInfo::BLOCK_WIN8_AND_OLDER) &&
467           IsWin8Point1OrLater()) {
468         goto continue_loading;
469       }
470 
471       if ((info->mFlags & DllBlockInfo::BLOCK_WIN7_AND_OLDER) &&
472           IsWin8OrLater()) {
473         goto continue_loading;
474       }
475 
476       if ((info->mFlags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
477           !(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
478         goto continue_loading;
479       }
480 
481       if ((info->mFlags & DllBlockInfo::BROWSER_PROCESS_ONLY) &&
482           (sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
483         goto continue_loading;
484       }
485 
486       unsigned long long fVersion = DllBlockInfo::ALL_VERSIONS;
487 
488       if (info->mMaxVersion != DllBlockInfo::ALL_VERSIONS) {
489         ReentrancySentinel sentinel(dllName);
490         if (sentinel.BailOut()) {
491           goto continue_loading;
492         }
493 
494         full_fname = getFullPath(filePath, fname);
495         if (!full_fname) {
496           // uh, we couldn't find the DLL at all, so...
497           printf_stderr(
498               "LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find "
499               "it?)\n",
500               dllName);
501           return STATUS_DLL_NOT_FOUND;
502         }
503 
504         if (info->mFlags & DllBlockInfo::USE_TIMESTAMP) {
505           fVersion = GetTimestamp(full_fname.get());
506           if (fVersion > info->mMaxVersion) {
507             load_ok = true;
508           }
509         } else {
510           LauncherResult<ModuleVersion> version =
511               GetModuleVersion(full_fname.get());
512           // If we failed to get the version information, we block.
513           if (version.isOk()) {
514             load_ok = !info->IsVersionBlocked(version.unwrap());
515           }
516         }
517       }
518 
519       if (!load_ok) {
520         printf_stderr(
521             "LdrLoadDll: Blocking load of '%s' -- see "
522             "http://www.mozilla.com/en-US/blocklist/\n",
523             dllName);
524         DllBlockSet::Add(info->mName, fVersion);
525         return STATUS_DLL_NOT_FOUND;
526       }
527     }
528   }
529 
530 continue_loading:
531 #ifdef DEBUG_very_verbose
532   printf_stderr("LdrLoadDll: continuing load... ('%S')\n",
533                 moduleFileName->Buffer);
534 #endif
535 
536   glue::ModuleLoadFrame loadFrame(moduleFileName);
537 
538   NTSTATUS ret;
539   HANDLE myHandle;
540 
541   ret = stub_LdrLoadDll(filePath, flags, moduleFileName, &myHandle);
542 
543   if (handle) {
544     *handle = myHandle;
545   }
546 
547   loadFrame.SetLoadStatus(ret, myHandle);
548 
549   return ret;
550 }
551 
552 #if defined(NIGHTLY_BUILD)
553 // Map of specific thread proc addresses we should block. In particular,
554 // LoadLibrary* APIs which indicate DLL injection
555 static void* gStartAddressesToBlock[4];
556 #endif  // defined(NIGHTLY_BUILD)
557 
ShouldBlockThread(void * aStartAddress)558 static bool ShouldBlockThread(void* aStartAddress) {
559   // Allows crashfirefox.exe to continue to work. Also if your threadproc is
560   // null, this crash is intentional.
561   if (aStartAddress == nullptr) return false;
562 
563 #if defined(NIGHTLY_BUILD)
564   for (auto p : gStartAddressesToBlock) {
565     if (p == aStartAddress) {
566       return true;
567     }
568   }
569 #endif
570 
571   bool shouldBlock = false;
572   MEMORY_BASIC_INFORMATION startAddressInfo = {0};
573   if (VirtualQuery(aStartAddress, &startAddressInfo,
574                    sizeof(startAddressInfo))) {
575     shouldBlock |= startAddressInfo.State != MEM_COMMIT;
576     shouldBlock |= startAddressInfo.Protect != PAGE_EXECUTE_READ;
577   }
578 
579   return shouldBlock;
580 }
581 
582 // Allows blocked threads to still run normally through BaseThreadInitThunk, in
583 // case there's any magic there that we shouldn't skip.
NopThreadProc(void *)584 static DWORD WINAPI NopThreadProc(void* /* aThreadParam */) { return 0; }
585 
patched_BaseThreadInitThunk(BOOL aIsInitialThread,void * aStartAddress,void * aThreadParam)586 static MOZ_NORETURN void __fastcall patched_BaseThreadInitThunk(
587     BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam) {
588   if (ShouldBlockThread(aStartAddress)) {
589     aStartAddress = (void*)NopThreadProc;
590   }
591 
592   stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
593 }
594 
595 static WindowsDllInterceptor NtDllIntercept;
596 static WindowsDllInterceptor Kernel32Intercept;
597 
598 static void GetNativeNtBlockSetWriter();
599 
600 static glue::LoaderObserver gMozglueLoaderObserver;
601 static nt::WinLauncherFunctions gWinLauncherFunctions;
602 
DllBlocklist_Initialize(uint32_t aInitFlags)603 MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) {
604   if (sBlocklistInitAttempted) {
605     return;
606   }
607   sBlocklistInitAttempted = true;
608 
609   sInitFlags = aInitFlags;
610 
611   glue::ModuleLoadFrame::StaticInit(&gMozglueLoaderObserver,
612                                     &gWinLauncherFunctions);
613 
614 #ifdef _M_AMD64
615   if (!IsWin8OrLater()) {
616     Kernel32Intercept.Init("kernel32.dll");
617 
618     // The crash that this hook works around is only seen on Win7.
619     stub_RtlInstallFunctionTableCallback.Set(
620         Kernel32Intercept, "RtlInstallFunctionTableCallback",
621         &patched_RtlInstallFunctionTableCallback);
622   }
623 #endif
624 
625   if (aInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
626     GetNativeNtBlockSetWriter();
627     return;
628   }
629 
630   // There are a couple of exceptional cases where we skip user32.dll check.
631   // - If the the process was bootstrapped by the launcher process, AppInit
632   //   DLLs will be intercepted by the new DllBlockList.  No need to check
633   //   here.
634   // - The code to initialize the base profiler loads winmm.dll which
635   //   statically links user32.dll on an older Windows.  This means if the base
636   //   profiler is active before coming here, we cannot fully intercept AppInit
637   //   DLLs.  Given that the base profiler is used outside the typical use
638   //   cases, it's ok not to check user32.dll in this scenario.
639   const bool skipUser32Check =
640       (sInitFlags & eDllBlocklistInitFlagWasBootstrapped)
641 #ifdef MOZ_GECKO_PROFILER
642       ||
643       (!IsWin10AnniversaryUpdateOrLater() && baseprofiler::profiler_is_active())
644 #endif
645       ;
646 
647   // In order to be effective against AppInit DLLs, the blocklist must be
648   // initialized before user32.dll is loaded into the process (bug 932100).
649   if (!skipUser32Check && GetModuleHandleW(L"user32.dll")) {
650     sUser32BeforeBlocklist = true;
651 #ifdef DEBUG
652     printf_stderr("DLL blocklist was unable to intercept AppInit DLLs.\n");
653 #endif
654   }
655 
656   NtDllIntercept.Init("ntdll.dll");
657 
658   ReentrancySentinel::InitializeStatics();
659 
660   // We specifically use a detour, because there are cases where external
661   // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
662   // nop space patches. (Bug 951827)
663   bool ok = stub_LdrLoadDll.SetDetour(NtDllIntercept, "LdrLoadDll",
664                                       &patched_LdrLoadDll);
665 
666   if (!ok) {
667     sBlocklistInitFailed = true;
668 #ifdef DEBUG
669     printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
670 #endif
671   }
672 
673   // If someone injects a thread early that causes user32.dll to load off the
674   // main thread this causes issues, so load it as soon as we've initialized
675   // the block-list. (See bug 1400637)
676   if (!sUser32BeforeBlocklist && !IsWin32kLockedDown()) {
677     ::LoadLibraryW(L"user32.dll");
678   }
679 
680   Kernel32Intercept.Init("kernel32.dll");
681 
682   // Bug 1361410: WRusr.dll will overwrite our hook and cause a crash.
683   // Workaround: If we detect WRusr.dll, don't hook.
684   if (!GetModuleHandleW(L"WRusr.dll")) {
685     if (!stub_BaseThreadInitThunk.SetDetour(Kernel32Intercept,
686                                             "BaseThreadInitThunk",
687                                             &patched_BaseThreadInitThunk)) {
688 #ifdef DEBUG
689       printf_stderr("BaseThreadInitThunk hook failed\n");
690 #endif
691     }
692   }
693 
694 #if defined(NIGHTLY_BUILD)
695   // Populate a list of thread start addresses to block.
696   HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
697   if (hKernel) {
698     void* pProc;
699 
700     pProc = (void*)GetProcAddress(hKernel, "LoadLibraryA");
701     gStartAddressesToBlock[0] = pProc;
702 
703     pProc = (void*)GetProcAddress(hKernel, "LoadLibraryW");
704     gStartAddressesToBlock[1] = pProc;
705 
706     pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExA");
707     gStartAddressesToBlock[2] = pProc;
708 
709     pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExW");
710     gStartAddressesToBlock[3] = pProc;
711   }
712 #endif
713 }
714 
715 #ifdef DEBUG
DllBlocklist_Shutdown()716 MFBT_API void DllBlocklist_Shutdown() {}
717 #endif  // DEBUG
718 
InternalWriteNotes(AnnotationWriter & aWriter)719 static void InternalWriteNotes(AnnotationWriter& aWriter) {
720   WritableBuffer buffer;
721   DllBlockSet::Write(buffer);
722 
723   aWriter.Write(Annotation::BlockedDllList, buffer.Data(), buffer.Length());
724 
725   if (sBlocklistInitFailed) {
726     aWriter.Write(Annotation::BlocklistInitFailed, "1");
727   }
728 
729   if (sUser32BeforeBlocklist) {
730     aWriter.Write(Annotation::User32BeforeBlocklist, "1");
731   }
732 }
733 
734 using WriterFn = void (*)(AnnotationWriter&);
735 static WriterFn gWriterFn = &InternalWriteNotes;
736 
GetNativeNtBlockSetWriter()737 static void GetNativeNtBlockSetWriter() {
738   auto nativeWriter = reinterpret_cast<WriterFn>(
739       ::GetProcAddress(::GetModuleHandleW(nullptr), "NativeNtBlockSet_Write"));
740   if (nativeWriter) {
741     gWriterFn = nativeWriter;
742   }
743 }
744 
DllBlocklist_WriteNotes(AnnotationWriter & aWriter)745 MFBT_API void DllBlocklist_WriteNotes(AnnotationWriter& aWriter) {
746   MOZ_ASSERT(gWriterFn);
747   gWriterFn(aWriter);
748 }
749 
DllBlocklist_CheckStatus()750 MFBT_API bool DllBlocklist_CheckStatus() {
751   if (sBlocklistInitFailed || sUser32BeforeBlocklist) return false;
752   return true;
753 }
754 
755 // ============================================================================
756 // This section is for DLL Services
757 // ============================================================================
758 
759 namespace mozilla {
760 Authenticode* GetAuthenticode();
761 }  // namespace mozilla
762 
763 /**
764  * Please note that DllBlocklist_SetFullDllServices is called with
765  * aSvc = nullptr to de-initialize the resources even though they
766  * have been initialized via DllBlocklist_SetBasicDllServices.
767  */
DllBlocklist_SetFullDllServices(mozilla::glue::detail::DllServicesBase * aSvc)768 MFBT_API void DllBlocklist_SetFullDllServices(
769     mozilla::glue::detail::DllServicesBase* aSvc) {
770   glue::AutoExclusiveLock lock(gDllServicesLock);
771   if (aSvc) {
772     aSvc->SetAuthenticodeImpl(GetAuthenticode());
773     aSvc->SetWinLauncherFunctions(gWinLauncherFunctions);
774     gMozglueLoaderObserver.Forward(aSvc);
775   }
776 
777   gDllServices = aSvc;
778 }
779 
DllBlocklist_SetBasicDllServices(mozilla::glue::detail::DllServicesBase * aSvc)780 MFBT_API void DllBlocklist_SetBasicDllServices(
781     mozilla::glue::detail::DllServicesBase* aSvc) {
782   if (!aSvc) {
783     return;
784   }
785 
786   aSvc->SetAuthenticodeImpl(GetAuthenticode());
787   gMozglueLoaderObserver.Disable();
788 }
789