| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome_elf/blacklist/blacklist.h" | 5 #include "chrome_elf/blacklist/blacklist.h" |
| 6 | 6 |
| 7 #include <assert.h> | 7 #include <assert.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 | 9 |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 // an extra allocation at run time. | 52 // an extra allocation at run time. |
| 53 #pragma section(".crthunk",read,execute) | 53 #pragma section(".crthunk",read,execute) |
| 54 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage; | 54 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage; |
| 55 | 55 |
| 56 namespace { | 56 namespace { |
| 57 | 57 |
| 58 // Record if the blacklist was successfully initialized so processes can easily | 58 // Record if the blacklist was successfully initialized so processes can easily |
| 59 // determine if the blacklist is enabled for them. | 59 // determine if the blacklist is enabled for them. |
| 60 bool g_blacklist_initialized = false; | 60 bool g_blacklist_initialized = false; |
| 61 | 61 |
| 62 // Record that the thunk setup completed succesfully and close the registry | 62 // Helper to set DWORD registry values. |
| 63 // key handle since it is no longer needed. | 63 DWORD SetDWValue(HKEY* key, const wchar_t* property, DWORD value) { |
| 64 void RecordSuccessfulThunkSetup(HKEY* key) { | 64 return ::RegSetValueEx(*key, |
| 65 if (key != NULL) { | 65 property, |
| 66 DWORD blacklist_state = blacklist::BLACKLIST_SETUP_RUNNING; | 66 0, |
| 67 ::RegSetValueEx(*key, | 67 REG_DWORD, |
| 68 blacklist::kBeaconState, | 68 reinterpret_cast<LPBYTE>(&value), |
| 69 0, | 69 sizeof(value)); |
| 70 REG_DWORD, | 70 } |
| 71 reinterpret_cast<LPBYTE>(&blacklist_state), | 71 |
| 72 sizeof(blacklist_state)); | 72 bool GenerateStateFromBeaconAndAttemptCount(HKEY* key, DWORD blacklist_state) { |
| 73 ::RegCloseKey(*key); | 73 LONG result = 0; |
| 74 key = NULL; | 74 if (blacklist_state == blacklist::BLACKLIST_SETUP_RUNNING) { |
| 75 // Some part of the blacklist setup failed last time. If this has occured |
| 76 // blacklist::kBeaconMaxAttempts times in a row we switch the state to |
| 77 // failed and skip setting up the blacklist. |
| 78 DWORD attempt_count = 0; |
| 79 DWORD attempt_count_size = sizeof(attempt_count); |
| 80 result = ::RegQueryValueEx(*key, |
| 81 blacklist::kBeaconAttemptCount, |
| 82 0, |
| 83 NULL, |
| 84 reinterpret_cast<LPBYTE>(&attempt_count), |
| 85 &attempt_count_size); |
| 86 |
| 87 if (result == ERROR_FILE_NOT_FOUND) |
| 88 attempt_count = 0; |
| 89 else if (result != ERROR_SUCCESS) |
| 90 return false; |
| 91 |
| 92 ++attempt_count; |
| 93 SetDWValue(key, blacklist::kBeaconAttemptCount, attempt_count); |
| 94 |
| 95 if (attempt_count >= blacklist::kBeaconMaxAttempts) { |
| 96 blacklist_state = blacklist::BLACKLIST_SETUP_FAILED; |
| 97 SetDWValue(key, blacklist::kBeaconState, blacklist_state); |
| 98 return false; |
| 99 } |
| 100 } else if (blacklist_state == blacklist::BLACKLIST_ENABLED) { |
| 101 // If the blacklist succeeded on the previous run reset the failure |
| 102 // counter. |
| 103 result = |
| 104 SetDWValue(key, blacklist::kBeaconAttemptCount, static_cast<DWORD>(0)); |
| 105 if (result != ERROR_SUCCESS) { |
| 106 return false; |
| 107 } |
| 75 } | 108 } |
| 109 return true; |
| 76 } | 110 } |
| 77 | 111 |
| 78 } // namespace | 112 } // namespace |
| 79 | 113 |
| 80 namespace blacklist { | 114 namespace blacklist { |
| 81 | 115 |
| 82 #if defined(_WIN64) | 116 #if defined(_WIN64) |
| 83 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. | 117 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. |
| 84 #pragma section(".oldntmap",write,read) | 118 #pragma section(".oldntmap",write,read) |
| 85 __declspec(allocate(".oldntmap")) | 119 __declspec(allocate(".oldntmap")) |
| 86 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; | 120 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; |
| 87 #endif | 121 #endif |
| 88 | 122 |
| 89 bool LeaveSetupBeacon() { | 123 bool LeaveSetupBeacon() { |
| 90 HKEY key = NULL; | 124 HKEY key = NULL; |
| 91 DWORD disposition = 0; | 125 DWORD disposition = 0; |
| 92 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | 126 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, |
| 93 kRegistryBeaconPath, | 127 kRegistryBeaconPath, |
| 94 0, | 128 0, |
| 95 NULL, | 129 NULL, |
| 96 REG_OPTION_NON_VOLATILE, | 130 REG_OPTION_NON_VOLATILE, |
| 97 KEY_QUERY_VALUE | KEY_SET_VALUE, | 131 KEY_QUERY_VALUE | KEY_SET_VALUE, |
| 98 NULL, | 132 NULL, |
| 99 &key, | 133 &key, |
| 100 &disposition); | 134 &disposition); |
| 101 if (result != ERROR_SUCCESS) | 135 if (result != ERROR_SUCCESS) |
| 102 return false; | 136 return false; |
| 103 | 137 |
| 104 // Retrieve the current blacklist state. | 138 // Retrieve the current blacklist state. |
| 105 DWORD blacklist_state = BLACKLIST_DISABLED; | 139 DWORD blacklist_state = BLACKLIST_STATE_MAX; |
| 106 DWORD blacklist_state_size = sizeof(blacklist_state); | 140 DWORD blacklist_state_size = sizeof(blacklist_state); |
| 107 DWORD type = 0; | 141 DWORD type = 0; |
| 108 result = ::RegQueryValueEx(key, | 142 result = ::RegQueryValueEx(key, |
| 109 kBeaconState, | 143 kBeaconState, |
| 110 0, | 144 0, |
| 111 &type, | 145 &type, |
| 112 reinterpret_cast<LPBYTE>(&blacklist_state), | 146 reinterpret_cast<LPBYTE>(&blacklist_state), |
| 113 &blacklist_state_size); | 147 &blacklist_state_size); |
| 114 | 148 |
| 115 if (blacklist_state != BLACKLIST_ENABLED || | 149 if (blacklist_state == BLACKLIST_DISABLED || result != ERROR_SUCCESS || |
| 116 result != ERROR_SUCCESS || type != REG_DWORD) { | 150 type != REG_DWORD) { |
| 117 ::RegCloseKey(key); | 151 ::RegCloseKey(key); |
| 118 return false; | 152 return false; |
| 119 } | 153 } |
| 120 | 154 |
| 121 // Mark the blacklist setup code as running so if it crashes the blacklist | 155 if (!GenerateStateFromBeaconAndAttemptCount(&key, blacklist_state)) { |
| 122 // won't be enabled for the next run. | 156 ::RegCloseKey(key); |
| 123 blacklist_state = BLACKLIST_SETUP_RUNNING; | 157 return false; |
| 124 result = ::RegSetValueEx(key, | 158 } |
| 125 kBeaconState, | 159 |
| 126 0, | 160 result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING); |
| 127 REG_DWORD, | |
| 128 reinterpret_cast<LPBYTE>(&blacklist_state), | |
| 129 sizeof(blacklist_state)); | |
| 130 ::RegCloseKey(key); | 161 ::RegCloseKey(key); |
| 131 | 162 |
| 132 return (result == ERROR_SUCCESS); | 163 return (result == ERROR_SUCCESS); |
| 133 } | 164 } |
| 134 | 165 |
| 135 bool ResetBeacon() { | 166 bool ResetBeacon() { |
| 136 HKEY key = NULL; | 167 HKEY key = NULL; |
| 137 DWORD disposition = 0; | 168 DWORD disposition = 0; |
| 138 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | 169 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, |
| 139 kRegistryBeaconPath, | 170 kRegistryBeaconPath, |
| 140 0, | 171 0, |
| 141 NULL, | 172 NULL, |
| 142 REG_OPTION_NON_VOLATILE, | 173 REG_OPTION_NON_VOLATILE, |
| 143 KEY_QUERY_VALUE | KEY_SET_VALUE, | 174 KEY_QUERY_VALUE | KEY_SET_VALUE, |
| 144 NULL, | 175 NULL, |
| 145 &key, | 176 &key, |
| 146 &disposition); | 177 &disposition); |
| 147 if (result != ERROR_SUCCESS) | 178 if (result != ERROR_SUCCESS) |
| 148 return false; | 179 return false; |
| 149 | 180 |
| 150 DWORD blacklist_state = BLACKLIST_ENABLED; | 181 DWORD blacklist_state = BLACKLIST_STATE_MAX; |
| 151 result = ::RegSetValueEx(key, | 182 DWORD blacklist_state_size = sizeof(blacklist_state); |
| 152 kBeaconState, | 183 DWORD type = 0; |
| 153 0, | 184 result = ::RegQueryValueEx(key, |
| 154 REG_DWORD, | 185 kBeaconState, |
| 155 reinterpret_cast<LPBYTE>(&blacklist_state), | 186 0, |
| 156 sizeof(blacklist_state)); | 187 &type, |
| 188 reinterpret_cast<LPBYTE>(&blacklist_state), |
| 189 &blacklist_state_size); |
| 190 |
| 191 if (result != ERROR_SUCCESS || type != REG_DWORD) { |
| 192 ::RegCloseKey(key); |
| 193 return false; |
| 194 } |
| 195 |
| 196 // Reaching this point with the setup running state means the setup |
| 197 // succeeded and so we reset to enabled. Any other state indicates that setup |
| 198 // was skipped; in that case we leave the state alone for later recording. |
| 199 if (blacklist_state == BLACKLIST_SETUP_RUNNING) |
| 200 result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED); |
| 201 |
| 157 ::RegCloseKey(key); | 202 ::RegCloseKey(key); |
| 158 | |
| 159 return (result == ERROR_SUCCESS); | 203 return (result == ERROR_SUCCESS); |
| 160 } | 204 } |
| 161 | 205 |
| 162 int BlacklistSize() { | 206 int BlacklistSize() { |
| 163 int size = -1; | 207 int size = -1; |
| 164 while (blacklist::g_troublesome_dlls[++size] != NULL) {} | 208 while (blacklist::g_troublesome_dlls[++size] != NULL) {} |
| 165 | 209 |
| 166 return size; | 210 return size; |
| 167 } | 211 } |
| 168 | 212 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 | 290 |
| 247 bool Initialize(bool force) { | 291 bool Initialize(bool force) { |
| 248 // Check to see that we found the functions we need in ntdll. | 292 // Check to see that we found the functions we need in ntdll. |
| 249 if (!InitializeInterceptImports()) | 293 if (!InitializeInterceptImports()) |
| 250 return false; | 294 return false; |
| 251 | 295 |
| 252 // Check to see if this is a non-browser process, abort if so. | 296 // Check to see if this is a non-browser process, abort if so. |
| 253 if (IsNonBrowserProcess()) | 297 if (IsNonBrowserProcess()) |
| 254 return false; | 298 return false; |
| 255 | 299 |
| 256 // Check to see if a beacon is present, abort if so. | 300 // Check to see if the blacklist beacon is still set to running (indicating a |
| 301 // failure) or disabled, and abort if so. |
| 257 if (!force && !LeaveSetupBeacon()) | 302 if (!force && !LeaveSetupBeacon()) |
| 258 return false; | 303 return false; |
| 259 | 304 |
| 260 // It is possible for other dlls to have already patched code by now and | 305 // It is possible for other dlls to have already patched code by now and |
| 261 // attempting to patch their code might result in crashes. | 306 // attempting to patch their code might result in crashes. |
| 262 const bool kRelaxed = false; | 307 const bool kRelaxed = false; |
| 263 | 308 |
| 264 // Create a thunk via the appropriate ServiceResolver instance. | 309 // Create a thunk via the appropriate ServiceResolver instance. |
| 265 sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); | 310 sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); |
| 266 | 311 |
| 267 // Don't try blacklisting on unsupported OS versions. | 312 // Don't try blacklisting on unsupported OS versions. |
| 268 if (!thunk) | 313 if (!thunk) |
| 269 return false; | 314 return false; |
| 270 | 315 |
| 271 // Record that we are starting the thunk setup code. | |
| 272 HKEY key = NULL; | |
| 273 DWORD disposition = 0; | |
| 274 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | |
| 275 kRegistryBeaconPath, | |
| 276 0, | |
| 277 NULL, | |
| 278 REG_OPTION_NON_VOLATILE, | |
| 279 KEY_QUERY_VALUE | KEY_SET_VALUE, | |
| 280 NULL, | |
| 281 &key, | |
| 282 &disposition); | |
| 283 if (result == ERROR_SUCCESS) { | |
| 284 DWORD blacklist_state = BLACKLIST_THUNK_SETUP; | |
| 285 ::RegSetValueEx(key, | |
| 286 kBeaconState, | |
| 287 0, | |
| 288 REG_DWORD, | |
| 289 reinterpret_cast<LPBYTE>(&blacklist_state), | |
| 290 sizeof(blacklist_state)); | |
| 291 } else { | |
| 292 key = NULL; | |
| 293 } | |
| 294 | |
| 295 BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); | 316 BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); |
| 296 | 317 |
| 297 // Mark the thunk storage as readable and writeable, since we | 318 // Mark the thunk storage as readable and writeable, since we |
| 298 // ready to write to it. | 319 // ready to write to it. |
| 299 DWORD old_protect = 0; | 320 DWORD old_protect = 0; |
| 300 if (!VirtualProtect(&g_thunk_storage, | 321 if (!VirtualProtect(&g_thunk_storage, |
| 301 sizeof(g_thunk_storage), | 322 sizeof(g_thunk_storage), |
| 302 PAGE_EXECUTE_READWRITE, | 323 PAGE_EXECUTE_READWRITE, |
| 303 &old_protect)) { | 324 &old_protect)) { |
| 304 RecordSuccessfulThunkSetup(&key); | |
| 305 return false; | 325 return false; |
| 306 } | 326 } |
| 307 | 327 |
| 308 thunk->AllowLocalPatches(); | 328 thunk->AllowLocalPatches(); |
| 309 | 329 |
| 310 // We declare this early so it can be used in the 64-bit block below and | 330 // We declare this early so it can be used in the 64-bit block below and |
| 311 // still work on 32-bit build when referenced at the end of the function. | 331 // still work on 32-bit build when referenced at the end of the function. |
| 312 BOOL page_executable = false; | 332 BOOL page_executable = false; |
| 313 | 333 |
| 314 // Replace the default NtMapViewOfSection with our patched version. | 334 // Replace the default NtMapViewOfSection with our patched version. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 | 366 |
| 347 // Record if we have initialized the blacklist. | 367 // Record if we have initialized the blacklist. |
| 348 g_blacklist_initialized = NT_SUCCESS(ret); | 368 g_blacklist_initialized = NT_SUCCESS(ret); |
| 349 | 369 |
| 350 // Mark the thunk storage as executable and prevent any future writes to it. | 370 // Mark the thunk storage as executable and prevent any future writes to it. |
| 351 page_executable = page_executable && VirtualProtect(&g_thunk_storage, | 371 page_executable = page_executable && VirtualProtect(&g_thunk_storage, |
| 352 sizeof(g_thunk_storage), | 372 sizeof(g_thunk_storage), |
| 353 PAGE_EXECUTE_READ, | 373 PAGE_EXECUTE_READ, |
| 354 &old_protect); | 374 &old_protect); |
| 355 | 375 |
| 356 RecordSuccessfulThunkSetup(&key); | |
| 357 | |
| 358 AddDllsFromRegistryToBlacklist(); | 376 AddDllsFromRegistryToBlacklist(); |
| 359 | 377 |
| 360 return NT_SUCCESS(ret) && page_executable; | 378 return NT_SUCCESS(ret) && page_executable; |
| 361 } | 379 } |
| 362 | 380 |
| 363 bool AddDllsFromRegistryToBlacklist() { | 381 bool AddDllsFromRegistryToBlacklist() { |
| 364 HKEY key = NULL; | 382 HKEY key = NULL; |
| 365 LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER, | 383 LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER, |
| 366 kRegistryFinchListPath, | 384 kRegistryFinchListPath, |
| 367 0, | 385 0, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 394 } | 412 } |
| 395 | 413 |
| 396 // Delete the finch registry key to clear the values. | 414 // Delete the finch registry key to clear the values. |
| 397 result = ::RegDeleteKey(key, L""); | 415 result = ::RegDeleteKey(key, L""); |
| 398 | 416 |
| 399 ::RegCloseKey(key); | 417 ::RegCloseKey(key); |
| 400 return result == ERROR_SUCCESS; | 418 return result == ERROR_SUCCESS; |
| 401 } | 419 } |
| 402 | 420 |
| 403 } // namespace blacklist | 421 } // namespace blacklist |
| OLD | NEW |