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