Home | History | Annotate | Download | only in blacklist
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome_elf/blacklist/blacklist.h"
      6 
      7 #include <assert.h>
      8 #include <string.h>
      9 
     10 #include <vector>
     11 
     12 #include "base/basictypes.h"
     13 #include "chrome_elf/blacklist/blacklist_interceptions.h"
     14 #include "chrome_elf/chrome_elf_constants.h"
     15 #include "chrome_elf/chrome_elf_util.h"
     16 #include "chrome_elf/thunk_getter.h"
     17 #include "sandbox/win/src/interception_internal.h"
     18 #include "sandbox/win/src/internal_types.h"
     19 #include "sandbox/win/src/service_resolver.h"
     20 
     21 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
     22 extern "C" IMAGE_DOS_HEADER __ImageBase;
     23 
     24 namespace blacklist{
     25 
     26 // The DLLs listed here are known (or under strong suspicion) of causing crashes
     27 // when they are loaded in the browser. DLLs should only be added to this list
     28 // if there is nothing else Chrome can do to prevent those crashes.
     29 // For more information about how this list is generated, and how to get off
     30 // of it, see:
     31 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers
     32 const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {
     33   L"activedetect32.dll",                // Lenovo One Key Theater.
     34                                         // See crbug.com/379218.
     35   L"activedetect64.dll",                // Lenovo One Key Theater.
     36   L"bitguard.dll",                      // Unknown (suspected malware).
     37   L"cespy.dll",                         // CovenantEyes.
     38   L"chrmxtn.dll",                       // Unknown (keystroke logger).
     39   L"cplushook.dll",                     // Unknown (suspected malware).
     40   L"datamngr.dll",                      // Unknown (suspected adware).
     41   L"hk.dll",                            // Unknown (keystroke logger).
     42   L"libapi2hook.dll",                   // V-Bates.
     43   L"libinject.dll",                     // V-Bates.
     44   L"libinject2.dll",                    // V-Bates.
     45   L"libredir2.dll",                     // V-Bates.
     46   L"libsvn_tsvn32.dll",                 // TortoiseSVN.
     47   L"libwinhook.dll",                    // V-Bates.
     48   L"lmrn.dll",                          // Unknown.
     49   L"minisp.dll",                        // Unknown (suspected malware).
     50   L"scdetour.dll",                      // Quick Heal Antivirus.
     51                                         // See crbug.com/382561.
     52   L"systemk.dll",                       // Unknown (suspected adware).
     53   L"windowsapihookdll32.dll",           // Lenovo One Key Theater.
     54                                         // See crbug.com/379218.
     55   L"windowsapihookdll64.dll",           // Lenovo One Key Theater.
     56   // Keep this null pointer here to mark the end of the list.
     57   NULL,
     58 };
     59 
     60 bool g_blocked_dlls[kTroublesomeDllsMaxCount] = {};
     61 int g_num_blocked_dlls = 0;
     62 
     63 }  // namespace blacklist
     64 
     65 // Allocate storage for thunks in a page of this module to save on doing
     66 // an extra allocation at run time.
     67 #pragma section(".crthunk",read,execute)
     68 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage;
     69 
     70 namespace {
     71 
     72 // Record if the blacklist was successfully initialized so processes can easily
     73 // determine if the blacklist is enabled for them.
     74 bool g_blacklist_initialized = false;
     75 
     76 // Helper to set DWORD registry values.
     77 DWORD SetDWValue(HKEY* key, const wchar_t* property, DWORD value) {
     78   return ::RegSetValueEx(*key,
     79                          property,
     80                          0,
     81                          REG_DWORD,
     82                          reinterpret_cast<LPBYTE>(&value),
     83                          sizeof(value));
     84 }
     85 
     86 bool GenerateStateFromBeaconAndAttemptCount(HKEY* key, DWORD blacklist_state) {
     87   LONG result = 0;
     88   if (blacklist_state == blacklist::BLACKLIST_SETUP_RUNNING) {
     89     // Some part of the blacklist setup failed last time.  If this has occured
     90     // blacklist::kBeaconMaxAttempts times in a row we switch the state to
     91     // failed and skip setting up the blacklist.
     92     DWORD attempt_count = 0;
     93     DWORD attempt_count_size = sizeof(attempt_count);
     94     result = ::RegQueryValueEx(*key,
     95                                blacklist::kBeaconAttemptCount,
     96                                0,
     97                                NULL,
     98                                reinterpret_cast<LPBYTE>(&attempt_count),
     99                                &attempt_count_size);
    100 
    101     if (result == ERROR_FILE_NOT_FOUND)
    102       attempt_count = 0;
    103     else if (result != ERROR_SUCCESS)
    104       return false;
    105 
    106     ++attempt_count;
    107     SetDWValue(key, blacklist::kBeaconAttemptCount, attempt_count);
    108 
    109     if (attempt_count >= blacklist::kBeaconMaxAttempts) {
    110       blacklist_state = blacklist::BLACKLIST_SETUP_FAILED;
    111       SetDWValue(key, blacklist::kBeaconState, blacklist_state);
    112       return false;
    113     }
    114   } else if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
    115     // If the blacklist succeeded on the previous run reset the failure
    116     // counter.
    117     result =
    118         SetDWValue(key, blacklist::kBeaconAttemptCount, static_cast<DWORD>(0));
    119     if (result != ERROR_SUCCESS) {
    120       return false;
    121     }
    122   }
    123   return true;
    124 }
    125 
    126 }  // namespace
    127 
    128 namespace blacklist {
    129 
    130 #if defined(_WIN64)
    131   // Allocate storage for the pointer to the old NtMapViewOfSectionFunction.
    132 #pragma section(".oldntmap",write,read)
    133   __declspec(allocate(".oldntmap"))
    134     NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL;
    135 #endif
    136 
    137 bool LeaveSetupBeacon() {
    138   HKEY key = NULL;
    139   DWORD disposition = 0;
    140   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
    141                                  kRegistryBeaconPath,
    142                                  0,
    143                                  NULL,
    144                                  REG_OPTION_NON_VOLATILE,
    145                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
    146                                  NULL,
    147                                  &key,
    148                                  &disposition);
    149   if (result != ERROR_SUCCESS)
    150     return false;
    151 
    152   // Retrieve the current blacklist state.
    153   DWORD blacklist_state = BLACKLIST_STATE_MAX;
    154   DWORD blacklist_state_size = sizeof(blacklist_state);
    155   DWORD type = 0;
    156   result = ::RegQueryValueEx(key,
    157                              kBeaconState,
    158                              0,
    159                              &type,
    160                              reinterpret_cast<LPBYTE>(&blacklist_state),
    161                              &blacklist_state_size);
    162 
    163   if (blacklist_state == BLACKLIST_DISABLED || result != ERROR_SUCCESS ||
    164       type != REG_DWORD) {
    165     ::RegCloseKey(key);
    166     return false;
    167   }
    168 
    169   if (!GenerateStateFromBeaconAndAttemptCount(&key, blacklist_state)) {
    170     ::RegCloseKey(key);
    171     return false;
    172   }
    173 
    174   result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING);
    175   ::RegCloseKey(key);
    176 
    177   return (result == ERROR_SUCCESS);
    178 }
    179 
    180 bool ResetBeacon() {
    181   HKEY key = NULL;
    182   DWORD disposition = 0;
    183   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
    184                                  kRegistryBeaconPath,
    185                                  0,
    186                                  NULL,
    187                                  REG_OPTION_NON_VOLATILE,
    188                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
    189                                  NULL,
    190                                  &key,
    191                                  &disposition);
    192   if (result != ERROR_SUCCESS)
    193     return false;
    194 
    195   DWORD blacklist_state = BLACKLIST_STATE_MAX;
    196   DWORD blacklist_state_size = sizeof(blacklist_state);
    197   DWORD type = 0;
    198   result = ::RegQueryValueEx(key,
    199                              kBeaconState,
    200                              0,
    201                              &type,
    202                              reinterpret_cast<LPBYTE>(&blacklist_state),
    203                              &blacklist_state_size);
    204 
    205   if (result != ERROR_SUCCESS || type != REG_DWORD) {
    206     ::RegCloseKey(key);
    207     return false;
    208   }
    209 
    210   // Reaching this point with the setup running state means the setup did not
    211   // crash, so we reset to enabled.  Any other state indicates that setup was
    212   // skipped; in that case we leave the state alone for later recording.
    213   if (blacklist_state == BLACKLIST_SETUP_RUNNING)
    214     result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED);
    215 
    216   ::RegCloseKey(key);
    217   return (result == ERROR_SUCCESS);
    218 }
    219 
    220 int BlacklistSize() {
    221   int size = -1;
    222   while (blacklist::g_troublesome_dlls[++size] != NULL) {}
    223 
    224   return size;
    225 }
    226 
    227 bool IsBlacklistInitialized() {
    228   return g_blacklist_initialized;
    229 }
    230 
    231 bool AddDllToBlacklist(const wchar_t* dll_name) {
    232   int blacklist_size = BlacklistSize();
    233   // We need to leave one space at the end for the null pointer.
    234   if (blacklist_size + 1 >= kTroublesomeDllsMaxCount)
    235     return false;
    236   for (int i = 0; i < blacklist_size; ++i) {
    237     if (!_wcsicmp(g_troublesome_dlls[i], dll_name))
    238       return true;
    239   }
    240 
    241   // Copy string to blacklist.
    242   wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1];
    243   wcscpy(str_buffer, dll_name);
    244 
    245   g_troublesome_dlls[blacklist_size] = str_buffer;
    246   g_blocked_dlls[blacklist_size] = false;
    247   return true;
    248 }
    249 
    250 bool RemoveDllFromBlacklist(const wchar_t* dll_name) {
    251   int blacklist_size = BlacklistSize();
    252   for (int i = 0; i < blacklist_size; ++i) {
    253     if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) {
    254       // Found the thing to remove. Delete it then replace it with the last
    255       // element.
    256       delete[] g_troublesome_dlls[i];
    257       g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1];
    258       g_troublesome_dlls[blacklist_size - 1] = NULL;
    259 
    260       // Also update the stats recording if we have blocked this dll or not.
    261       if (g_blocked_dlls[i])
    262         --g_num_blocked_dlls;
    263       g_blocked_dlls[i] = g_blocked_dlls[blacklist_size - 1];
    264       return true;
    265     }
    266   }
    267   return false;
    268 }
    269 
    270 // TODO(csharp): Maybe store these values in the registry so we can
    271 // still report them if Chrome crashes early.
    272 void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size) {
    273   if (size == NULL)
    274     return;
    275 
    276   // If the array isn't valid or big enough, just report the size it needs to
    277   // be and return.
    278   if (blocked_dlls == NULL && *size < g_num_blocked_dlls) {
    279     *size = g_num_blocked_dlls;
    280     return;
    281   }
    282 
    283   *size = g_num_blocked_dlls;
    284 
    285   int strings_to_fill = 0;
    286   for (int i = 0; strings_to_fill < g_num_blocked_dlls && g_troublesome_dlls[i];
    287        ++i) {
    288     if (g_blocked_dlls[i]) {
    289       blocked_dlls[strings_to_fill] = g_troublesome_dlls[i];
    290       ++strings_to_fill;
    291     }
    292   }
    293 }
    294 
    295 void BlockedDll(size_t blocked_index) {
    296   assert(blocked_index < kTroublesomeDllsMaxCount);
    297 
    298   if (!g_blocked_dlls[blocked_index] &&
    299       blocked_index < kTroublesomeDllsMaxCount) {
    300     ++g_num_blocked_dlls;
    301     g_blocked_dlls[blocked_index] = true;
    302   }
    303 }
    304 
    305 bool Initialize(bool force) {
    306   // Check to see that we found the functions we need in ntdll.
    307   if (!InitializeInterceptImports())
    308     return false;
    309 
    310   // Check to see if this is a non-browser process, abort if so.
    311   if (IsNonBrowserProcess())
    312     return false;
    313 
    314   // Check to see if the blacklist beacon is still set to running (indicating a
    315   // failure) or disabled, and abort if so.
    316   if (!force && !LeaveSetupBeacon())
    317     return false;
    318 
    319   // It is possible for other dlls to have already patched code by now and
    320   // attempting to patch their code might result in crashes.
    321   const bool kRelaxed = false;
    322 
    323   // Create a thunk via the appropriate ServiceResolver instance.
    324   sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed);
    325 
    326   // Don't try blacklisting on unsupported OS versions.
    327   if (!thunk)
    328     return false;
    329 
    330   BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage);
    331 
    332   // Mark the thunk storage as readable and writeable, since we
    333   // ready to write to it.
    334   DWORD old_protect = 0;
    335   if (!VirtualProtect(&g_thunk_storage,
    336                       sizeof(g_thunk_storage),
    337                       PAGE_EXECUTE_READWRITE,
    338                       &old_protect)) {
    339     return false;
    340   }
    341 
    342   thunk->AllowLocalPatches();
    343 
    344   // We declare this early so it can be used in the 64-bit block below and
    345   // still work on 32-bit build when referenced at the end of the function.
    346   BOOL page_executable = false;
    347 
    348   // Replace the default NtMapViewOfSection with our patched version.
    349 #if defined(_WIN64)
    350   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
    351                               reinterpret_cast<void*>(&__ImageBase),
    352                               "NtMapViewOfSection",
    353                               NULL,
    354                               &blacklist::BlNtMapViewOfSection64,
    355                               thunk_storage,
    356                               sizeof(sandbox::ThunkData),
    357                               NULL);
    358 
    359   // Keep a pointer to the original code, we don't have enough space to
    360   // add it directly to the call.
    361   g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>(
    362       thunk_storage);
    363 
    364   // Ensure that the pointer to the old function can't be changed.
    365  page_executable = VirtualProtect(&g_nt_map_view_of_section_func,
    366                                   sizeof(g_nt_map_view_of_section_func),
    367                                   PAGE_EXECUTE_READ,
    368                                   &old_protect);
    369 #else
    370   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
    371                               reinterpret_cast<void*>(&__ImageBase),
    372                               "NtMapViewOfSection",
    373                               NULL,
    374                               &blacklist::BlNtMapViewOfSection,
    375                               thunk_storage,
    376                               sizeof(sandbox::ThunkData),
    377                               NULL);
    378 #endif
    379   delete thunk;
    380 
    381   // Record if we have initialized the blacklist.
    382   g_blacklist_initialized = NT_SUCCESS(ret);
    383 
    384   // Mark the thunk storage as executable and prevent any future writes to it.
    385   page_executable = page_executable && VirtualProtect(&g_thunk_storage,
    386                                                       sizeof(g_thunk_storage),
    387                                                       PAGE_EXECUTE_READ,
    388                                                       &old_protect);
    389 
    390   AddDllsFromRegistryToBlacklist();
    391 
    392   return NT_SUCCESS(ret) && page_executable;
    393 }
    394 
    395 bool AddDllsFromRegistryToBlacklist() {
    396   HKEY key = NULL;
    397   LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER,
    398                                kRegistryFinchListPath,
    399                                0,
    400                                KEY_QUERY_VALUE | KEY_SET_VALUE,
    401                                &key);
    402 
    403   if (result != ERROR_SUCCESS)
    404     return false;
    405 
    406   // We add dlls from the registry to the blacklist, and then clear registry.
    407   DWORD value_len;
    408   DWORD name_len = MAX_PATH;
    409   std::vector<wchar_t> name_buffer(name_len);
    410   for (int i = 0; result == ERROR_SUCCESS; ++i) {
    411     name_len = MAX_PATH;
    412     value_len = 0;
    413     result = ::RegEnumValue(
    414         key, i, &name_buffer[0], &name_len, NULL, NULL, NULL, &value_len);
    415     name_len = name_len + 1;
    416     value_len = value_len + 1;
    417     std::vector<wchar_t> value_buffer(value_len);
    418     result = ::RegEnumValue(key, i, &name_buffer[0], &name_len, NULL, NULL,
    419                             reinterpret_cast<BYTE*>(&value_buffer[0]),
    420                             &value_len);
    421     value_buffer[value_len - 1] = L'\0';
    422 
    423     if (result == ERROR_SUCCESS) {
    424       AddDllToBlacklist(&value_buffer[0]);
    425     }
    426   }
    427 
    428   // Delete the finch registry key to clear the values.
    429   result = ::RegDeleteKey(key, L"");
    430 
    431   ::RegCloseKey(key);
    432   return result == ERROR_SUCCESS;
    433 }
    434 
    435 }  // namespace blacklist
    436