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