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, | |
58 reinterpret_cast<LPBYTE>(&blacklist_state), | |
59 sizeof(blacklist_state)); | |
60 ::RegCloseKey(*key); | |
61 key = NULL; | |
62 } | |
63 } | 57 } |
64 | 58 |
65 } // namespace | 59 } // namespace |
66 | 60 |
67 namespace blacklist { | 61 namespace blacklist { |
68 | 62 |
69 #if defined(_WIN64) | 63 #if defined(_WIN64) |
70 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. | 64 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. |
71 #pragma section(".oldntmap",write,read) | 65 #pragma section(".oldntmap",write,read) |
72 __declspec(allocate(".oldntmap")) | 66 __declspec(allocate(".oldntmap")) |
73 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; | 67 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; |
74 #endif | 68 #endif |
75 | 69 |
70 bool HandlePreviousBeacon(HKEY* key, DWORD blacklist_state) { | |
csharp
2014/06/11 13:34:43
This function shouldn't be indented and can be mov
krstnmnlsn
2014/06/11 16:35:27
Done.
| |
71 LONG result; | |
72 if (blacklist_state == BLACKLIST_SETUP_RUNNING) { | |
73 // Some part of the blacklist setup failed last time. If this has occured | |
74 // some minimum number of times in a row we switch the state to failed and | |
75 // skip setting up the blacklist. | |
76 DWORD attempt_count = 0; | |
77 DWORD attempt_count_size = sizeof(attempt_count); | |
78 DWORD attempt_count_type = 0; | |
79 result = ::RegQueryValueEx(*key, | |
80 kBeaconAttemptCount, | |
81 0, | |
82 &attempt_count_type, | |
83 reinterpret_cast<LPBYTE>(&attempt_count), | |
84 &attempt_count_size); | |
85 | |
86 if (result == ERROR_FILE_NOT_FOUND) { | |
csharp
2014/06/11 13:34:43
I'm not sure we need lines 88->95. It just sets th
krstnmnlsn
2014/06/11 16:35:27
Done.
| |
87 attempt_count = 0; | |
88 attempt_count_type = REG_DWORD; | |
89 result = SetDWValue(key, kBeaconAttemptCount, attempt_count); | |
90 } | |
91 | |
92 if (result != ERROR_SUCCESS || attempt_count_type != REG_DWORD) { | |
93 ::RegCloseKey(*key); | |
csharp
2014/06/11 13:34:43
It probably is cleaner to move all the ::RegCloseK
krstnmnlsn
2014/06/11 16:35:27
Yea I realized after that might be better. Done.
| |
94 return false; | |
95 } | |
96 | |
97 attempt_count = attempt_count + 1; | |
csharp
2014/06/11 13:34:43
++attempt_count instead?
krstnmnlsn
2014/06/11 16:35:27
Done.
| |
98 result = SetDWValue(key, kBeaconAttemptCount, attempt_count); | |
99 if (result != ERROR_SUCCESS) { | |
csharp
2014/06/11 13:34:43
I wonder if we shouldn't try to abort here. If we
krstnmnlsn
2014/06/11 16:35:27
Yeah, I don't think taking it out even introduces
| |
100 ::RegCloseKey(*key); | |
101 return false; | |
102 } | |
103 | |
104 if (attempt_count >= blacklist::kBeaconMaxAttempts) { | |
105 blacklist_state = BLACKLIST_SETUP_FAILED; | |
106 SetDWValue(key, kBeaconState, blacklist_state); | |
107 ::RegCloseKey(*key); | |
108 return false; | |
109 } | |
110 } else if (blacklist_state == BLACKLIST_ENABLED) { | |
111 // If the blacklist succeeded on the previous run reset the failure | |
112 // counter. | |
113 result = SetDWValue(key, kBeaconAttemptCount, static_cast<DWORD>(0)); | |
114 if (result != ERROR_SUCCESS) { | |
115 ::RegCloseKey(*key); | |
116 return false; | |
117 } | |
118 } | |
119 return true; | |
120 } | |
121 | |
76 bool LeaveSetupBeacon() { | 122 bool LeaveSetupBeacon() { |
77 HKEY key = NULL; | 123 HKEY key = NULL; |
78 DWORD disposition = 0; | 124 DWORD disposition = 0; |
79 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | 125 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, |
80 kRegistryBeaconPath, | 126 kRegistryBeaconPath, |
81 0, | 127 0, |
82 NULL, | 128 NULL, |
83 REG_OPTION_NON_VOLATILE, | 129 REG_OPTION_NON_VOLATILE, |
84 KEY_QUERY_VALUE | KEY_SET_VALUE, | 130 KEY_QUERY_VALUE | KEY_SET_VALUE, |
85 NULL, | 131 NULL, |
86 &key, | 132 &key, |
87 &disposition); | 133 &disposition); |
88 if (result != ERROR_SUCCESS) | 134 if (result != ERROR_SUCCESS) |
89 return false; | 135 return false; |
90 | 136 |
91 // Retrieve the current blacklist state. | 137 // Retrieve the current blacklist state. |
92 DWORD blacklist_state = BLACKLIST_DISABLED; | 138 DWORD blacklist_state = BLACKLIST_STATE_MAX; |
93 DWORD blacklist_state_size = sizeof(blacklist_state); | 139 DWORD blacklist_state_size = sizeof(blacklist_state); |
94 DWORD type = 0; | 140 DWORD type = 0; |
95 result = ::RegQueryValueEx(key, | 141 result = ::RegQueryValueEx(key, |
96 kBeaconState, | 142 kBeaconState, |
97 0, | 143 0, |
98 &type, | 144 &type, |
99 reinterpret_cast<LPBYTE>(&blacklist_state), | 145 reinterpret_cast<LPBYTE>(&blacklist_state), |
100 &blacklist_state_size); | 146 &blacklist_state_size); |
101 | 147 |
102 if (blacklist_state != BLACKLIST_ENABLED || | 148 if (blacklist_state == BLACKLIST_DISABLED || result != ERROR_SUCCESS || |
103 result != ERROR_SUCCESS || type != REG_DWORD) { | 149 type != REG_DWORD) { |
104 ::RegCloseKey(key); | 150 ::RegCloseKey(key); |
105 return false; | 151 return false; |
106 } | 152 } |
107 | 153 |
108 // Mark the blacklist setup code as running so if it crashes the blacklist | 154 if (!HandlePreviousBeacon(&key, blacklist_state)) |
csharp
2014/06/11 13:34:43
This name seems a bit vague, I don't really have a
krstnmnlsn
2014/06/11 16:35:27
Yea I couldn't think of a reasonably short but als
| |
109 // won't be enabled for the next run. | 155 return false; |
110 blacklist_state = BLACKLIST_SETUP_RUNNING; | 156 |
111 result = ::RegSetValueEx(key, | 157 result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING); |
112 kBeaconState, | |
113 0, | |
114 REG_DWORD, | |
115 reinterpret_cast<LPBYTE>(&blacklist_state), | |
116 sizeof(blacklist_state)); | |
117 ::RegCloseKey(key); | 158 ::RegCloseKey(key); |
118 | 159 |
119 return (result == ERROR_SUCCESS); | 160 return (result == ERROR_SUCCESS); |
120 } | 161 } |
121 | 162 |
122 bool ResetBeacon() { | 163 bool ResetBeacon() { |
123 HKEY key = NULL; | 164 HKEY key = NULL; |
124 DWORD disposition = 0; | 165 DWORD disposition = 0; |
125 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | 166 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, |
126 kRegistryBeaconPath, | 167 kRegistryBeaconPath, |
127 0, | 168 0, |
128 NULL, | 169 NULL, |
129 REG_OPTION_NON_VOLATILE, | 170 REG_OPTION_NON_VOLATILE, |
130 KEY_QUERY_VALUE | KEY_SET_VALUE, | 171 KEY_QUERY_VALUE | KEY_SET_VALUE, |
131 NULL, | 172 NULL, |
132 &key, | 173 &key, |
133 &disposition); | 174 &disposition); |
134 if (result != ERROR_SUCCESS) | 175 if (result != ERROR_SUCCESS) |
135 return false; | 176 return false; |
136 | 177 |
137 DWORD blacklist_state = BLACKLIST_ENABLED; | 178 DWORD blacklist_state = BLACKLIST_STATE_MAX; |
138 result = ::RegSetValueEx(key, | 179 DWORD blacklist_state_size = sizeof(blacklist_state); |
139 kBeaconState, | 180 DWORD type = 0; |
140 0, | 181 result = ::RegQueryValueEx(key, |
141 REG_DWORD, | 182 kBeaconState, |
142 reinterpret_cast<LPBYTE>(&blacklist_state), | 183 0, |
143 sizeof(blacklist_state)); | 184 &type, |
185 reinterpret_cast<LPBYTE>(&blacklist_state), | |
186 &blacklist_state_size); | |
187 | |
188 if (result != ERROR_SUCCESS || type != REG_DWORD) { | |
189 ::RegCloseKey(key); | |
190 return false; | |
191 } | |
192 | |
193 // Reaching this point with the setup running state means the setup | |
194 // succeeded and so we reset to enabled. Any other state indicates that setup | |
195 // was skipped; in that case we leave the state alone for later recording. | |
196 if (blacklist_state == BLACKLIST_SETUP_RUNNING) | |
197 result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED); | |
198 | |
144 ::RegCloseKey(key); | 199 ::RegCloseKey(key); |
145 | |
146 return (result == ERROR_SUCCESS); | 200 return (result == ERROR_SUCCESS); |
147 } | 201 } |
148 | 202 |
149 int BlacklistSize() { | 203 int BlacklistSize() { |
150 int size = -1; | 204 int size = -1; |
151 while (blacklist::g_troublesome_dlls[++size] != NULL) {} | 205 while (blacklist::g_troublesome_dlls[++size] != NULL) {} |
152 | 206 |
153 return size; | 207 return size; |
154 } | 208 } |
155 | 209 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 | 287 |
234 bool Initialize(bool force) { | 288 bool Initialize(bool force) { |
235 // Check to see that we found the functions we need in ntdll. | 289 // Check to see that we found the functions we need in ntdll. |
236 if (!InitializeInterceptImports()) | 290 if (!InitializeInterceptImports()) |
237 return false; | 291 return false; |
238 | 292 |
239 // Check to see if this is a non-browser process, abort if so. | 293 // Check to see if this is a non-browser process, abort if so. |
240 if (IsNonBrowserProcess()) | 294 if (IsNonBrowserProcess()) |
241 return false; | 295 return false; |
242 | 296 |
243 // Check to see if a beacon is present, abort if so. | 297 // Check to see if the blacklist beacon is still set to running (indicating a |
298 // failure) or disabled, and abort if so. | |
244 if (!force && !LeaveSetupBeacon()) | 299 if (!force && !LeaveSetupBeacon()) |
245 return false; | 300 return false; |
246 | 301 |
247 // It is possible for other dlls to have already patched code by now and | 302 // It is possible for other dlls to have already patched code by now and |
248 // attempting to patch their code might result in crashes. | 303 // attempting to patch their code might result in crashes. |
249 const bool kRelaxed = false; | 304 const bool kRelaxed = false; |
250 | 305 |
251 // Create a thunk via the appropriate ServiceResolver instance. | 306 // Create a thunk via the appropriate ServiceResolver instance. |
252 sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); | 307 sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); |
253 | 308 |
254 // Don't try blacklisting on unsupported OS versions. | 309 // Don't try blacklisting on unsupported OS versions. |
255 if (!thunk) | 310 if (!thunk) |
256 return false; | 311 return false; |
257 | 312 |
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); | 313 BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); |
283 | 314 |
284 // Mark the thunk storage as readable and writeable, since we | 315 // Mark the thunk storage as readable and writeable, since we |
285 // ready to write to it. | 316 // ready to write to it. |
286 DWORD old_protect = 0; | 317 DWORD old_protect = 0; |
287 if (!VirtualProtect(&g_thunk_storage, | 318 if (!VirtualProtect(&g_thunk_storage, |
288 sizeof(g_thunk_storage), | 319 sizeof(g_thunk_storage), |
289 PAGE_EXECUTE_READWRITE, | 320 PAGE_EXECUTE_READWRITE, |
290 &old_protect)) { | 321 &old_protect)) { |
291 RecordSuccessfulThunkSetup(&key); | |
292 return false; | 322 return false; |
293 } | 323 } |
294 | 324 |
295 thunk->AllowLocalPatches(); | 325 thunk->AllowLocalPatches(); |
296 | 326 |
297 // We declare this early so it can be used in the 64-bit block below and | 327 // 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. | 328 // still work on 32-bit build when referenced at the end of the function. |
299 BOOL page_executable = false; | 329 BOOL page_executable = false; |
300 | 330 |
301 // Replace the default NtMapViewOfSection with our patched version. | 331 // Replace the default NtMapViewOfSection with our patched version. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 | 363 |
334 // Record if we have initialized the blacklist. | 364 // Record if we have initialized the blacklist. |
335 g_blacklist_initialized = NT_SUCCESS(ret); | 365 g_blacklist_initialized = NT_SUCCESS(ret); |
336 | 366 |
337 // Mark the thunk storage as executable and prevent any future writes to it. | 367 // Mark the thunk storage as executable and prevent any future writes to it. |
338 page_executable = page_executable && VirtualProtect(&g_thunk_storage, | 368 page_executable = page_executable && VirtualProtect(&g_thunk_storage, |
339 sizeof(g_thunk_storage), | 369 sizeof(g_thunk_storage), |
340 PAGE_EXECUTE_READ, | 370 PAGE_EXECUTE_READ, |
341 &old_protect); | 371 &old_protect); |
342 | 372 |
343 RecordSuccessfulThunkSetup(&key); | |
344 | |
345 return NT_SUCCESS(ret) && page_executable; | 373 return NT_SUCCESS(ret) && page_executable; |
346 } | 374 } |
347 | 375 |
348 } // namespace blacklist | 376 } // namespace blacklist |
OLD | NEW |