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