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