Bug 1322554: Interpose kernel32!BaseThreadInitThunk to add verification of thread start addresses; r=dmajor draft
authorAaron Klotz <aklotz@mozilla.com>
Wed, 22 Mar 2017 13:51:43 -0600
changeset 552977 8dc24c4d384cca89cd24acb0dcc7841b2d403a90
parent 552976 eb1dbf20253b633c093893dfaa9b7ec81e4e4c8b
child 552978 656d0a38542331404a859b8a868a2b3279810986
push id51530
push userbmo:ccorcoran@mozilla.com
push dateWed, 29 Mar 2017 10:46:27 +0000
reviewersdmajor
bugs1322554
milestone55.0a1
Bug 1322554: Interpose kernel32!BaseThreadInitThunk to add verification of thread start addresses; r=dmajor MozReview-Commit-ID: 4Mp9JyE9eat
mozglue/build/WindowsDllBlocklist.cpp
toolkit/xre/test/win/TestDllInterceptor.cpp
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -277,16 +277,20 @@ printf_stderr(const char *fmt, ...)
   vfprintf(fp, fmt, args);
   va_end(args);
 
   fclose(fp);
 }
 
 namespace {
 
+typedef void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
+
+static BaseThreadInitThunk_func stub_BaseThreadInitThunk = nullptr;
+
 typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
 
 static LdrLoadDll_func stub_LdrLoadDll = 0;
 
 template <class T>
 struct RVAMap {
   RVAMap(HANDLE map, DWORD offset) {
     SYSTEM_INFO info;
@@ -696,17 +700,43 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
 continue_loading:
 #ifdef DEBUG_very_verbose
   printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
 #endif
 
   return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
 }
 
+static bool
+ShouldBlockThread(void* aStartAddress, void* aThreadParam)
+{
+  bool shouldBlock = false;
+  MEMORY_BASIC_INFORMATION startAddressInfo;
+  if (VirtualQuery(aStartAddress, &startAddressInfo, sizeof(startAddressInfo))) {
+    shouldBlock |= startAddressInfo.State != MEM_COMMIT;
+    shouldBlock |= startAddressInfo.Protect != PAGE_EXECUTE_READ;
+    shouldBlock |= !(startAddressInfo.Type & MEM_IMAGE);
+  }
+
+  return shouldBlock;
+}
+
+static MOZ_NORETURN void __fastcall
+patched_BaseThreadInitThunk(BOOL aIsInitialThread, void* aStartAddress,
+                            void* aThreadParam)
+{
+  if (ShouldBlockThread(aStartAddress, aThreadParam)) {
+    ExitThread(1);
+  }
+
+  stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
+}
+
 WindowsDllInterceptor NtDllIntercept;
+WindowsDllInterceptor Kernel32DllIntercept;
 
 } // namespace
 
 MFBT_API void
 DllBlocklist_Initialize()
 {
   if (sBlocklistInitAttempted) {
     return;
@@ -732,16 +762,26 @@ DllBlocklist_Initialize()
   bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll);
 
   if (!ok) {
     sBlocklistInitFailed = true;
 #ifdef DEBUG
     printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
 #endif
   }
+
+  Kernel32DllIntercept.Init("kernel32.dll");
+  ok = Kernel32DllIntercept.AddHook("BaseThreadInitThunk",
+                                    reinterpret_cast<intptr_t>(patched_BaseThreadInitThunk),
+                                    (void**) &stub_BaseThreadInitThunk);
+  if (!ok) {
+#ifdef DEBUG
+    printf_stderr("BaseThreadInitThunk hook failed\n");
+#endif
+  }
 }
 
 MFBT_API void
 DllBlocklist_WriteNotes(HANDLE file)
 {
   DWORD nBytes;
 
   WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -463,15 +463,16 @@ int main()
       TestHook(TestGetOpenFileNameW, "comdlg32.dll", "GetOpenFileNameW") &&
 #ifdef _M_X64
       TestHook(TestGetKeyState, "user32.dll", "GetKeyState") &&    // see Bug 1316415
 #endif
       MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") &&
 #ifdef _M_IX86
       TestHook(TestSendMessageTimeoutW, "user32.dll", "SendMessageTimeoutW") &&
 #endif
+      TestDetour("kernel32.dll", "BaseThreadInitThunk") &&
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
   }
 
   return 1;
 }