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