OLD | NEW |
| (Empty) |
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 "chrome_elf/blacklist/blacklist_interceptions.h" | |
13 #include "chrome_elf/chrome_elf_constants.h" | |
14 #include "chrome_elf/chrome_elf_util.h" | |
15 #include "chrome_elf/thunk_getter.h" | |
16 #include "sandbox/win/src/interception_internal.h" | |
17 #include "sandbox/win/src/internal_types.h" | |
18 #include "sandbox/win/src/service_resolver.h" | |
19 | |
20 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx | |
21 extern "C" IMAGE_DOS_HEADER __ImageBase; | |
22 | |
23 namespace blacklist{ | |
24 | |
25 // The DLLs listed here are known (or under strong suspicion) of causing crashes | |
26 // when they are loaded in the browser. DLLs should only be added to this list | |
27 // if there is nothing else Chrome can do to prevent those crashes. | |
28 // For more information about how this list is generated, and how to get off | |
29 // of it, see: | |
30 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers | |
31 // NOTE: Please remember to update the DllHash enum in histograms.xml when | |
32 // adding a new value to the blacklist. | |
33 const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = { | |
34 L"949ba8b6a9.dll", // Coupon Time. | |
35 L"activedetect32.dll", // Lenovo One Key Theater. | |
36 // See crbug.com/379218. | |
37 L"activedetect64.dll", // Lenovo One Key Theater. | |
38 L"bitguard.dll", // Unknown (suspected malware). | |
39 L"bsvc.dll", // Unknown (suspected adware). | |
40 L"chrmxtn.dll", // Unknown (keystroke logger). | |
41 L"cplushook.dll", // Unknown (suspected malware). | |
42 L"crdli.dll", // Linkury Inc. | |
43 L"crdli64.dll", // Linkury Inc. | |
44 L"datamngr.dll", // Unknown (suspected adware). | |
45 L"dpinterface32.dll", // Unknown (suspected adware). | |
46 L"explorerex.dll", // Unknown (suspected adware). | |
47 L"hk.dll", // Unknown (keystroke logger). | |
48 L"libapi2hook.dll", // V-Bates. | |
49 L"libinject.dll", // V-Bates. | |
50 L"libinject2.dll", // V-Bates. | |
51 L"libredir2.dll", // V-Bates. | |
52 L"libsvn_tsvn32.dll", // TortoiseSVN. | |
53 L"libwinhook.dll", // V-Bates. | |
54 L"lmrn.dll", // Unknown. | |
55 L"minisp.dll", // Unknown (suspected malware). | |
56 L"minisp32.dll", // Unknown (suspected malware). | |
57 L"offerswizarddll.dll", // Unknown (suspected adware). | |
58 L"safetynut.dll", // Unknown (suspected adware). | |
59 L"smdmf.dll", // Unknown (suspected adware). | |
60 L"spappsv32.dll", // Unknown (suspected adware). | |
61 L"systemk.dll", // Unknown (suspected adware). | |
62 L"vntsrv.dll", // Virtual New Tab by APN LLC. | |
63 L"wajam_goblin_64.dll", // Wajam Internet Technologies. | |
64 L"wajam_goblin.dll", // Wajam Internet Technologies. | |
65 L"windowsapihookdll32.dll", // Lenovo One Key Theater. | |
66 // See crbug.com/379218. | |
67 L"windowsapihookdll64.dll", // Lenovo One Key Theater. | |
68 L"virtualcamera.ax", // %PROGRAMFILES%\ASUS\VirtualCamera. | |
69 // See crbug.com/422522. | |
70 L"ycwebcamerasource.ax", // CyberLink Youcam, crbug.com/424159 | |
71 // Keep this null pointer here to mark the end of the list. | |
72 NULL, | |
73 }; | |
74 | |
75 bool g_blocked_dlls[kTroublesomeDllsMaxCount] = {}; | |
76 int g_num_blocked_dlls = 0; | |
77 | |
78 } // namespace blacklist | |
79 | |
80 // Allocate storage for thunks in a page of this module to save on doing | |
81 // an extra allocation at run time. | |
82 #pragma section(".crthunk",read,execute) | |
83 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage; | |
84 | |
85 namespace { | |
86 | |
87 // Record if the blacklist was successfully initialized so processes can easily | |
88 // determine if the blacklist is enabled for them. | |
89 bool g_blacklist_initialized = false; | |
90 | |
91 // Helper to set DWORD registry values. | |
92 DWORD SetDWValue(HKEY* key, const wchar_t* property, DWORD value) { | |
93 return ::RegSetValueEx(*key, | |
94 property, | |
95 0, | |
96 REG_DWORD, | |
97 reinterpret_cast<LPBYTE>(&value), | |
98 sizeof(value)); | |
99 } | |
100 | |
101 bool GenerateStateFromBeaconAndAttemptCount(HKEY* key, DWORD blacklist_state) { | |
102 LONG result = 0; | |
103 if (blacklist_state == blacklist::BLACKLIST_ENABLED) { | |
104 // If the blacklist succeeded on the previous run reset the failure | |
105 // counter. | |
106 return (SetDWValue(key, | |
107 blacklist::kBeaconAttemptCount, | |
108 static_cast<DWORD>(0)) == ERROR_SUCCESS); | |
109 } else { | |
110 // Some part of the blacklist setup failed last time. If this has occured | |
111 // blacklist::kBeaconMaxAttempts times in a row we switch the state to | |
112 // failed and skip setting up the blacklist. | |
113 DWORD attempt_count = 0; | |
114 DWORD attempt_count_size = sizeof(attempt_count); | |
115 result = ::RegQueryValueEx(*key, | |
116 blacklist::kBeaconAttemptCount, | |
117 0, | |
118 NULL, | |
119 reinterpret_cast<LPBYTE>(&attempt_count), | |
120 &attempt_count_size); | |
121 | |
122 if (result == ERROR_FILE_NOT_FOUND) | |
123 attempt_count = 0; | |
124 else if (result != ERROR_SUCCESS) | |
125 return false; | |
126 | |
127 ++attempt_count; | |
128 SetDWValue(key, blacklist::kBeaconAttemptCount, attempt_count); | |
129 | |
130 if (attempt_count >= blacklist::kBeaconMaxAttempts) { | |
131 blacklist_state = blacklist::BLACKLIST_SETUP_FAILED; | |
132 SetDWValue(key, blacklist::kBeaconState, blacklist_state); | |
133 } | |
134 | |
135 return false; | |
136 } | |
137 } | |
138 | |
139 } // namespace | |
140 | |
141 namespace blacklist { | |
142 | |
143 #if defined(_WIN64) | |
144 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. | |
145 #pragma section(".oldntmap",write,read) | |
146 __declspec(allocate(".oldntmap")) | |
147 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; | |
148 #endif | |
149 | |
150 bool LeaveSetupBeacon() { | |
151 HKEY key = NULL; | |
152 DWORD disposition = 0; | |
153 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | |
154 kRegistryBeaconPath, | |
155 0, | |
156 NULL, | |
157 REG_OPTION_NON_VOLATILE, | |
158 KEY_QUERY_VALUE | KEY_SET_VALUE, | |
159 NULL, | |
160 &key, | |
161 &disposition); | |
162 if (result != ERROR_SUCCESS) | |
163 return false; | |
164 | |
165 // Retrieve the current blacklist state. | |
166 DWORD blacklist_state = BLACKLIST_STATE_MAX; | |
167 DWORD blacklist_state_size = sizeof(blacklist_state); | |
168 DWORD type = 0; | |
169 result = ::RegQueryValueEx(key, | |
170 kBeaconState, | |
171 0, | |
172 &type, | |
173 reinterpret_cast<LPBYTE>(&blacklist_state), | |
174 &blacklist_state_size); | |
175 | |
176 if (result != ERROR_SUCCESS || blacklist_state == BLACKLIST_DISABLED || | |
177 type != REG_DWORD) { | |
178 ::RegCloseKey(key); | |
179 return false; | |
180 } | |
181 | |
182 if (!GenerateStateFromBeaconAndAttemptCount(&key, blacklist_state)) { | |
183 ::RegCloseKey(key); | |
184 return false; | |
185 } | |
186 | |
187 result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING); | |
188 ::RegCloseKey(key); | |
189 | |
190 return (result == ERROR_SUCCESS); | |
191 } | |
192 | |
193 bool ResetBeacon() { | |
194 HKEY key = NULL; | |
195 DWORD disposition = 0; | |
196 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, | |
197 kRegistryBeaconPath, | |
198 0, | |
199 NULL, | |
200 REG_OPTION_NON_VOLATILE, | |
201 KEY_QUERY_VALUE | KEY_SET_VALUE, | |
202 NULL, | |
203 &key, | |
204 &disposition); | |
205 if (result != ERROR_SUCCESS) | |
206 return false; | |
207 | |
208 DWORD blacklist_state = BLACKLIST_STATE_MAX; | |
209 DWORD blacklist_state_size = sizeof(blacklist_state); | |
210 DWORD type = 0; | |
211 result = ::RegQueryValueEx(key, | |
212 kBeaconState, | |
213 0, | |
214 &type, | |
215 reinterpret_cast<LPBYTE>(&blacklist_state), | |
216 &blacklist_state_size); | |
217 | |
218 if (result != ERROR_SUCCESS || type != REG_DWORD) { | |
219 ::RegCloseKey(key); | |
220 return false; | |
221 } | |
222 | |
223 // Reaching this point with the setup running state means the setup did not | |
224 // crash, so we reset to enabled. Any other state indicates that setup was | |
225 // skipped; in that case we leave the state alone for later recording. | |
226 if (blacklist_state == BLACKLIST_SETUP_RUNNING) | |
227 result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED); | |
228 | |
229 ::RegCloseKey(key); | |
230 return (result == ERROR_SUCCESS); | |
231 } | |
232 | |
233 int BlacklistSize() { | |
234 int size = -1; | |
235 while (blacklist::g_troublesome_dlls[++size] != NULL) {} | |
236 | |
237 return size; | |
238 } | |
239 | |
240 bool IsBlacklistInitialized() { | |
241 return g_blacklist_initialized; | |
242 } | |
243 | |
244 int GetBlacklistIndex(const wchar_t* dll_name) { | |
245 for (int i = 0; | |
246 i < static_cast<int>(kTroublesomeDllsMaxCount) && g_troublesome_dlls[i]; | |
247 ++i) { | |
248 if (_wcsicmp(dll_name, g_troublesome_dlls[i]) == 0) | |
249 return i; | |
250 } | |
251 return -1; | |
252 } | |
253 | |
254 bool AddDllToBlacklist(const wchar_t* dll_name) { | |
255 int blacklist_size = BlacklistSize(); | |
256 // We need to leave one space at the end for the null pointer. | |
257 if (blacklist_size + 1 >= static_cast<int>(kTroublesomeDllsMaxCount)) | |
258 return false; | |
259 for (int i = 0; i < blacklist_size; ++i) { | |
260 if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) | |
261 return true; | |
262 } | |
263 | |
264 // Copy string to blacklist. | |
265 wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1]; | |
266 wcscpy(str_buffer, dll_name); | |
267 | |
268 g_troublesome_dlls[blacklist_size] = str_buffer; | |
269 g_blocked_dlls[blacklist_size] = false; | |
270 return true; | |
271 } | |
272 | |
273 bool RemoveDllFromBlacklist(const wchar_t* dll_name) { | |
274 int blacklist_size = BlacklistSize(); | |
275 for (int i = 0; i < blacklist_size; ++i) { | |
276 if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) { | |
277 // Found the thing to remove. Delete it then replace it with the last | |
278 // element. | |
279 delete[] g_troublesome_dlls[i]; | |
280 g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1]; | |
281 g_troublesome_dlls[blacklist_size - 1] = NULL; | |
282 | |
283 // Also update the stats recording if we have blocked this dll or not. | |
284 if (g_blocked_dlls[i]) | |
285 --g_num_blocked_dlls; | |
286 g_blocked_dlls[i] = g_blocked_dlls[blacklist_size - 1]; | |
287 return true; | |
288 } | |
289 } | |
290 return false; | |
291 } | |
292 | |
293 // TODO(csharp): Maybe store these values in the registry so we can | |
294 // still report them if Chrome crashes early. | |
295 void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size) { | |
296 if (size == NULL) | |
297 return; | |
298 | |
299 // If the array isn't valid or big enough, just report the size it needs to | |
300 // be and return. | |
301 if (blocked_dlls == NULL && *size < g_num_blocked_dlls) { | |
302 *size = g_num_blocked_dlls; | |
303 return; | |
304 } | |
305 | |
306 *size = g_num_blocked_dlls; | |
307 | |
308 int strings_to_fill = 0; | |
309 for (int i = 0; strings_to_fill < g_num_blocked_dlls && g_troublesome_dlls[i]; | |
310 ++i) { | |
311 if (g_blocked_dlls[i]) { | |
312 blocked_dlls[strings_to_fill] = g_troublesome_dlls[i]; | |
313 ++strings_to_fill; | |
314 } | |
315 } | |
316 } | |
317 | |
318 void BlockedDll(size_t blocked_index) { | |
319 assert(blocked_index < kTroublesomeDllsMaxCount); | |
320 | |
321 if (!g_blocked_dlls[blocked_index] && | |
322 blocked_index < kTroublesomeDllsMaxCount) { | |
323 ++g_num_blocked_dlls; | |
324 g_blocked_dlls[blocked_index] = true; | |
325 } | |
326 } | |
327 | |
328 bool Initialize(bool force) { | |
329 // Check to see that we found the functions we need in ntdll. | |
330 if (!InitializeInterceptImports()) | |
331 return false; | |
332 | |
333 // Check to see if this is a non-browser process, abort if so. | |
334 if (IsNonBrowserProcess()) | |
335 return false; | |
336 | |
337 // Check to see if the blacklist beacon is still set to running (indicating a | |
338 // failure) or disabled, and abort if so. | |
339 if (!force && !LeaveSetupBeacon()) | |
340 return false; | |
341 | |
342 // It is possible for other dlls to have already patched code by now and | |
343 // attempting to patch their code might result in crashes. | |
344 const bool kRelaxed = false; | |
345 | |
346 // Create a thunk via the appropriate ServiceResolver instance. | |
347 sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); | |
348 | |
349 // Don't try blacklisting on unsupported OS versions. | |
350 if (!thunk) | |
351 return false; | |
352 | |
353 BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); | |
354 | |
355 // Mark the thunk storage as readable and writeable, since we | |
356 // ready to write to it. | |
357 DWORD old_protect = 0; | |
358 if (!VirtualProtect(&g_thunk_storage, | |
359 sizeof(g_thunk_storage), | |
360 PAGE_EXECUTE_READWRITE, | |
361 &old_protect)) { | |
362 return false; | |
363 } | |
364 | |
365 thunk->AllowLocalPatches(); | |
366 | |
367 // We declare this early so it can be used in the 64-bit block below and | |
368 // still work on 32-bit build when referenced at the end of the function. | |
369 BOOL page_executable = false; | |
370 | |
371 // Replace the default NtMapViewOfSection with our patched version. | |
372 #if defined(_WIN64) | |
373 NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName), | |
374 reinterpret_cast<void*>(&__ImageBase), | |
375 "NtMapViewOfSection", | |
376 NULL, | |
377 &blacklist::BlNtMapViewOfSection64, | |
378 thunk_storage, | |
379 sizeof(sandbox::ThunkData), | |
380 NULL); | |
381 | |
382 // Keep a pointer to the original code, we don't have enough space to | |
383 // add it directly to the call. | |
384 g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>( | |
385 thunk_storage); | |
386 | |
387 // Ensure that the pointer to the old function can't be changed. | |
388 page_executable = VirtualProtect(&g_nt_map_view_of_section_func, | |
389 sizeof(g_nt_map_view_of_section_func), | |
390 PAGE_EXECUTE_READ, | |
391 &old_protect); | |
392 #else | |
393 NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName), | |
394 reinterpret_cast<void*>(&__ImageBase), | |
395 "NtMapViewOfSection", | |
396 NULL, | |
397 &blacklist::BlNtMapViewOfSection, | |
398 thunk_storage, | |
399 sizeof(sandbox::ThunkData), | |
400 NULL); | |
401 #endif | |
402 delete thunk; | |
403 | |
404 // Record if we have initialized the blacklist. | |
405 g_blacklist_initialized = NT_SUCCESS(ret); | |
406 | |
407 // Mark the thunk storage as executable and prevent any future writes to it. | |
408 page_executable = page_executable && VirtualProtect(&g_thunk_storage, | |
409 sizeof(g_thunk_storage), | |
410 PAGE_EXECUTE_READ, | |
411 &old_protect); | |
412 | |
413 AddDllsFromRegistryToBlacklist(); | |
414 | |
415 return NT_SUCCESS(ret) && page_executable; | |
416 } | |
417 | |
418 void AddDllsFromRegistryToBlacklist() { | |
419 HKEY key = NULL; | |
420 LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER, | |
421 kRegistryFinchListPath, | |
422 0, | |
423 KEY_QUERY_VALUE | KEY_SET_VALUE, | |
424 &key); | |
425 | |
426 if (result != ERROR_SUCCESS) | |
427 return; | |
428 | |
429 // We add dlls from the registry to the blacklist. | |
430 DWORD value_len; | |
431 DWORD name_len = MAX_PATH; | |
432 std::vector<wchar_t> name_buffer(name_len); | |
433 for (int i = 0; result == ERROR_SUCCESS; ++i) { | |
434 name_len = MAX_PATH; | |
435 value_len = 0; | |
436 result = ::RegEnumValue( | |
437 key, i, &name_buffer[0], &name_len, NULL, NULL, NULL, &value_len); | |
438 if (result != ERROR_SUCCESS) | |
439 break; | |
440 | |
441 name_len = name_len + 1; | |
442 value_len = value_len + 1; | |
443 std::vector<wchar_t> value_buffer(value_len); | |
444 result = ::RegEnumValue(key, i, &name_buffer[0], &name_len, NULL, NULL, | |
445 reinterpret_cast<BYTE*>(&value_buffer[0]), | |
446 &value_len); | |
447 if (result != ERROR_SUCCESS) | |
448 break; | |
449 value_buffer[value_len - 1] = L'\0'; | |
450 AddDllToBlacklist(&value_buffer[0]); | |
451 } | |
452 | |
453 ::RegCloseKey(key); | |
454 return; | |
455 } | |
456 | |
457 } // namespace blacklist | |
OLD | NEW |