OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/browser/enumerate_modules_model_win.h" | 5 #include "chrome/browser/enumerate_modules_model_win.h" |
6 | 6 |
7 #include <Tlhelp32.h> | 7 #include <Tlhelp32.h> |
8 #include <wintrust.h> | 8 #include <wintrust.h> |
9 | 9 |
10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
11 #include "app/win_util.h" | 11 #include "app/win_util.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/environment.h" | 13 #include "base/environment.h" |
14 #include "base/file_path.h" | 14 #include "base/file_path.h" |
15 #include "base/file_version_info_win.h" | 15 #include "base/file_version_info_win.h" |
| 16 #include "base/metrics/histogram.h" |
16 #include "base/scoped_handle.h" | 17 #include "base/scoped_handle.h" |
17 #include "base/sha2.h" | 18 #include "base/sha2.h" |
18 #include "base/string_number_conversions.h" | 19 #include "base/string_number_conversions.h" |
19 #include "base/string_util.h" | 20 #include "base/string_util.h" |
| 21 #include "base/time.h" |
20 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
21 #include "base/values.h" | 23 #include "base/values.h" |
22 #include "base/version.h" | 24 #include "base/version.h" |
23 #include "base/win/registry.h" | 25 #include "base/win/registry.h" |
24 #include "chrome/browser/net/service_providers_win.h" | 26 #include "chrome/browser/net/service_providers_win.h" |
25 #include "chrome/common/chrome_constants.h" | 27 #include "chrome/common/chrome_constants.h" |
26 #include "chrome/common/chrome_switches.h" | 28 #include "chrome/common/chrome_switches.h" |
27 #include "chrome/common/notification_service.h" | 29 #include "chrome/common/notification_service.h" |
28 #include "grit/generated_resources.h" | 30 #include "grit/generated_resources.h" |
29 | 31 |
(...skipping 16 matching lines...) Expand all Loading... |
46 | 48 |
47 return a.location < b.location; | 49 return a.location < b.location; |
48 } | 50 } |
49 | 51 |
50 namespace { | 52 namespace { |
51 | 53 |
52 // Used to protect the LoadedModuleVector which is accessed | 54 // Used to protect the LoadedModuleVector which is accessed |
53 // from both the UI thread and the FILE thread. | 55 // from both the UI thread and the FILE thread. |
54 Lock* lock = NULL; | 56 Lock* lock = NULL; |
55 | 57 |
| 58 // A struct to help de-duping modules before adding them to the enumerated |
| 59 // modules vector. |
| 60 struct FindModule { |
| 61 public: |
| 62 explicit FindModule(const ModuleEnumerator::Module& x) |
| 63 : module(x) {} |
| 64 bool operator()(ModuleEnumerator::Module module_in) const { |
| 65 return (module.type == module_in.type) && |
| 66 (module.location == module_in.location) && |
| 67 (module.name == module_in.name); |
| 68 } |
| 69 |
| 70 const ModuleEnumerator::Module& module; |
| 71 }; |
| 72 |
56 } | 73 } |
57 | 74 |
58 // The browser process module blacklist. This lists all modules that are known | 75 // The browser process module blacklist. This lists all modules that are known |
59 // to cause compatibility issues within the browser process. When adding to this | 76 // to cause compatibility issues within the browser process. When adding to this |
60 // list, make sure that all paths are lower-case, in long pathname form, end | 77 // list, make sure that all paths are lower-case, in long pathname form, end |
61 // with a slash and use environments variables (or just look at one of the | 78 // with a slash and use environments variables (or just look at one of the |
62 // comments below and keep it consistent with that). When adding an entry with | 79 // comments below and keep it consistent with that). When adding an entry with |
63 // an environment variable not currently used in the list below, make sure to | 80 // an environment variable not currently used in the list below, make sure to |
64 // update the list in PreparePathMappings. Filename, Description/Signer, and | 81 // update the list in PreparePathMappings. Filename, Description/Signer, and |
65 // Location must be entered as hashes (see GenerateHash). Filename is mandatory. | 82 // Location must be entered as hashes (see GenerateHash). Filename is mandatory. |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 | 236 |
220 void ModuleEnumerator::ScanNow(ModulesVector* list) { | 237 void ModuleEnumerator::ScanNow(ModulesVector* list) { |
221 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 238 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
222 enumerated_modules_ = list; | 239 enumerated_modules_ = list; |
223 BrowserThread::PostTask( | 240 BrowserThread::PostTask( |
224 BrowserThread::FILE, FROM_HERE, | 241 BrowserThread::FILE, FROM_HERE, |
225 NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread)); | 242 NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread)); |
226 } | 243 } |
227 | 244 |
228 void ModuleEnumerator::ScanOnFileThread() { | 245 void ModuleEnumerator::ScanOnFileThread() { |
| 246 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 247 |
229 enumerated_modules_->clear(); | 248 enumerated_modules_->clear(); |
230 | 249 |
231 // Make sure the path mapping vector is setup so we can collapse paths. | 250 // Make sure the path mapping vector is setup so we can collapse paths. |
232 PreparePathMappings(); | 251 PreparePathMappings(); |
233 | 252 |
| 253 base::TimeTicks checkpoint = base::TimeTicks::Now(); |
234 EnumerateLoadedModules(); | 254 EnumerateLoadedModules(); |
| 255 HISTOGRAM_TIMES("Conflicts.EnumerateLoadedModules", |
| 256 base::TimeTicks::Now() - checkpoint); |
| 257 |
| 258 checkpoint = base::TimeTicks::Now(); |
235 EnumerateShellExtensions(); | 259 EnumerateShellExtensions(); |
236 EnumerateWinsockModule(); | 260 HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions", |
| 261 base::TimeTicks::Now() - checkpoint); |
| 262 |
| 263 checkpoint = base::TimeTicks::Now(); |
| 264 EnumerateWinsockModules(); |
| 265 HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", |
| 266 base::TimeTicks::Now() - checkpoint); |
237 | 267 |
238 MatchAgainstBlacklist(); | 268 MatchAgainstBlacklist(); |
239 | 269 |
240 std::sort(enumerated_modules_->begin(), | 270 std::sort(enumerated_modules_->begin(), |
241 enumerated_modules_->end(), ModuleSort); | 271 enumerated_modules_->end(), ModuleSort); |
242 | 272 |
243 // Send a reply back on the UI thread. | 273 // Send a reply back on the UI thread. |
244 BrowserThread::PostTask( | 274 BrowserThread::PostTask( |
245 callback_thread_id_, FROM_HERE, | 275 callback_thread_id_, FROM_HERE, |
246 NewRunnableMethod(this, &ModuleEnumerator::ReportBack)); | 276 NewRunnableMethod(this, &ModuleEnumerator::ReportBack)); |
| 277 |
| 278 HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", |
| 279 base::TimeTicks::Now() - start_time); |
247 } | 280 } |
248 | 281 |
249 void ModuleEnumerator::EnumerateLoadedModules() { | 282 void ModuleEnumerator::EnumerateLoadedModules() { |
250 // Get all modules in the current process. | 283 // Get all modules in the current process. |
251 ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | 284 ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, |
252 ::GetCurrentProcessId())); | 285 ::GetCurrentProcessId())); |
253 if (!snap.Get()) | 286 if (!snap.Get()) |
254 return; | 287 return; |
255 | 288 |
256 // Walk the module list. | 289 // Walk the module list. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 } | 329 } |
297 clsid.Close(); | 330 clsid.Close(); |
298 | 331 |
299 Module entry; | 332 Module entry; |
300 entry.type = SHELL_EXTENSION; | 333 entry.type = SHELL_EXTENSION; |
301 entry.location = dll; | 334 entry.location = dll; |
302 PopulateModuleInformation(&entry); | 335 PopulateModuleInformation(&entry); |
303 | 336 |
304 NormalizeModule(&entry); | 337 NormalizeModule(&entry); |
305 CollapsePath(&entry); | 338 CollapsePath(&entry); |
306 enumerated_modules_->push_back(entry); | 339 AddToListWithoutDuplicating(entry); |
307 | 340 |
308 ++registration; | 341 ++registration; |
309 } | 342 } |
310 } | 343 } |
311 | 344 |
312 void ModuleEnumerator::EnumerateWinsockModule() { | 345 void ModuleEnumerator::EnumerateWinsockModules() { |
313 // Add to this list the Winsock LSP DLLs. | 346 // Add to this list the Winsock LSP DLLs. |
314 WinsockLayeredServiceProviderList layered_providers; | 347 WinsockLayeredServiceProviderList layered_providers; |
315 GetWinsockLayeredServiceProviders(&layered_providers); | 348 GetWinsockLayeredServiceProviders(&layered_providers); |
316 for (size_t i = 0; i < layered_providers.size(); ++i) { | 349 for (size_t i = 0; i < layered_providers.size(); ++i) { |
317 Module entry; | 350 Module entry; |
318 entry.type = WINSOCK_MODULE_REGISTRATION; | 351 entry.type = WINSOCK_MODULE_REGISTRATION; |
319 entry.status = NOT_MATCHED; | 352 entry.status = NOT_MATCHED; |
320 entry.normalized = false; | 353 entry.normalized = false; |
321 entry.location = layered_providers[i].path; | 354 entry.location = layered_providers[i].path; |
322 entry.description = layered_providers[i].name; | 355 entry.description = layered_providers[i].name; |
323 entry.recommended_action = NONE; | 356 entry.recommended_action = NONE; |
| 357 entry.duplicate_count = 0; |
324 | 358 |
325 wchar_t expanded[MAX_PATH]; | 359 wchar_t expanded[MAX_PATH]; |
326 DWORD size = ExpandEnvironmentStrings( | 360 DWORD size = ExpandEnvironmentStrings( |
327 entry.location.c_str(), expanded, MAX_PATH); | 361 entry.location.c_str(), expanded, MAX_PATH); |
328 if (size != 0 && size <= MAX_PATH) { | 362 if (size != 0 && size <= MAX_PATH) { |
329 entry.digital_signer = | 363 entry.digital_signer = |
330 GetSubjectNameFromDigitalSignature(FilePath(expanded)); | 364 GetSubjectNameFromDigitalSignature(FilePath(expanded)); |
331 } | 365 } |
332 entry.version = base::IntToString16(layered_providers[i].version); | 366 entry.version = base::IntToString16(layered_providers[i].version); |
333 | 367 |
334 // Paths have already been collapsed. | 368 // Paths have already been collapsed. |
335 NormalizeModule(&entry); | 369 NormalizeModule(&entry); |
336 enumerated_modules_->push_back(entry); | 370 AddToListWithoutDuplicating(entry); |
337 } | 371 } |
338 } | 372 } |
339 | 373 |
340 void ModuleEnumerator::PopulateModuleInformation(Module* module) { | 374 void ModuleEnumerator::PopulateModuleInformation(Module* module) { |
341 module->status = NOT_MATCHED; | 375 module->status = NOT_MATCHED; |
| 376 module->duplicate_count = 0; |
342 module->normalized = false; | 377 module->normalized = false; |
343 module->digital_signer = | 378 module->digital_signer = |
344 GetSubjectNameFromDigitalSignature(FilePath(module->location)); | 379 GetSubjectNameFromDigitalSignature(FilePath(module->location)); |
345 module->recommended_action = NONE; | 380 module->recommended_action = NONE; |
346 scoped_ptr<FileVersionInfo> version_info( | 381 scoped_ptr<FileVersionInfo> version_info( |
347 FileVersionInfo::CreateFileVersionInfo(FilePath(module->location))); | 382 FileVersionInfo::CreateFileVersionInfo(FilePath(module->location))); |
348 if (version_info.get()) { | 383 if (version_info.get()) { |
349 FileVersionInfoWin* version_info_win = | 384 FileVersionInfoWin* version_info_win = |
350 static_cast<FileVersionInfoWin*>(version_info.get()); | 385 static_cast<FileVersionInfoWin*>(version_info.get()); |
351 | 386 |
352 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); | 387 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); |
353 if (fixed_file_info) { | 388 if (fixed_file_info) { |
354 module->description = version_info_win->file_description(); | 389 module->description = version_info_win->file_description(); |
355 module->version = version_info_win->file_version(); | 390 module->version = version_info_win->file_version(); |
356 module->product_name = version_info_win->product_name(); | 391 module->product_name = version_info_win->product_name(); |
357 } | 392 } |
358 } | 393 } |
359 } | 394 } |
360 | 395 |
| 396 void ModuleEnumerator::AddToListWithoutDuplicating(const Module& module) { |
| 397 DCHECK(module.normalized); |
| 398 // These are registered modules, not loaded modules so the same module |
| 399 // can be registered multiple times, often dozens of times. There is no need |
| 400 // to list each registration, so we just increment the count for each module |
| 401 // that is counted multiple times. |
| 402 ModulesVector::iterator iter; |
| 403 iter = std::find_if(enumerated_modules_->begin(), |
| 404 enumerated_modules_->end(), |
| 405 FindModule(module)); |
| 406 if (iter != enumerated_modules_->end()) |
| 407 iter->duplicate_count++; |
| 408 else |
| 409 enumerated_modules_->push_back(module); |
| 410 } |
| 411 |
361 void ModuleEnumerator::PreparePathMappings() { | 412 void ModuleEnumerator::PreparePathMappings() { |
362 path_mapping_.clear(); | 413 path_mapping_.clear(); |
363 | 414 |
364 scoped_ptr<base::Environment> environment(base::Environment::Create()); | 415 scoped_ptr<base::Environment> environment(base::Environment::Create()); |
365 std::vector<string16> env_vars; | 416 std::vector<string16> env_vars; |
366 env_vars.push_back(L"LOCALAPPDATA"); | 417 env_vars.push_back(L"LOCALAPPDATA"); |
367 env_vars.push_back(L"ProgramFiles"); | 418 env_vars.push_back(L"ProgramFiles"); |
368 env_vars.push_back(L"USERPROFILE"); | 419 env_vars.push_back(L"USERPROFILE"); |
369 env_vars.push_back(L"SystemRoot"); | 420 env_vars.push_back(L"SystemRoot"); |
370 env_vars.push_back(L"TEMP"); | 421 env_vars.push_back(L"TEMP"); |
| 422 env_vars.push_back(L"TMP"); |
371 for (std::vector<string16>::const_iterator variable = env_vars.begin(); | 423 for (std::vector<string16>::const_iterator variable = env_vars.begin(); |
372 variable != env_vars.end(); ++variable) { | 424 variable != env_vars.end(); ++variable) { |
373 std::string path; | 425 std::string path; |
374 if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { | 426 if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { |
375 path_mapping_.push_back( | 427 path_mapping_.push_back( |
376 std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", | 428 std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", |
377 L"%" + l10n_util::ToLower(*variable) + L"%")); | 429 L"%" + l10n_util::ToLower(*variable) + L"%")); |
378 } | 430 } |
379 } | 431 } |
380 } | 432 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 #endif | 465 #endif |
414 | 466 |
415 ModuleStatus status = Match(*module, kModuleBlacklist[i]); | 467 ModuleStatus status = Match(*module, kModuleBlacklist[i]); |
416 if (status != NOT_MATCHED) { | 468 if (status != NOT_MATCHED) { |
417 // We have a match against the blacklist. Mark it as such. | 469 // We have a match against the blacklist. Mark it as such. |
418 module->status = status; | 470 module->status = status; |
419 module->recommended_action = kModuleBlacklist[i].help_tip; | 471 module->recommended_action = kModuleBlacklist[i].help_tip; |
420 break; | 472 break; |
421 } | 473 } |
422 } | 474 } |
| 475 |
| 476 // Modules loaded from these locations are frequently malicious |
| 477 // and notorious for changing frequently so they are not good candidates |
| 478 // for blacklising individually. Mark them as suspicious if we haven't |
| 479 // classified them as bad yet. |
| 480 if (module->status == NOT_MATCHED || module->status == GOOD) { |
| 481 if (StartsWith(module->location, L"%temp%", false) || |
| 482 StartsWith(module->location, L"%tmp%", false)) { |
| 483 module->status = SUSPECTED_BAD; |
| 484 } |
| 485 } |
423 } | 486 } |
424 } | 487 } |
425 | 488 |
426 void ModuleEnumerator::ReportBack() { | 489 void ModuleEnumerator::ReportBack() { |
427 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_)); | 490 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_)); |
428 observer_->DoneScanning(); | 491 observer_->DoneScanning(); |
429 } | 492 } |
430 | 493 |
431 string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature( | 494 string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature( |
432 const FilePath& filename) { | 495 const FilePath& filename) { |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
639 module != enumerated_modules_.end(); ++module) { | 702 module != enumerated_modules_.end(); ++module) { |
640 if (module->status == ModuleEnumerator::CONFIRMED_BAD) | 703 if (module->status == ModuleEnumerator::CONFIRMED_BAD) |
641 ++confirmed_bad_modules_detected_; | 704 ++confirmed_bad_modules_detected_; |
642 if (module->status == ModuleEnumerator::SUSPECTED_BAD) | 705 if (module->status == ModuleEnumerator::SUSPECTED_BAD) |
643 ++suspected_bad_modules_detected_; | 706 ++suspected_bad_modules_detected_; |
644 } | 707 } |
645 | 708 |
646 scanning_ = false; | 709 scanning_ = false; |
647 lock->Release(); | 710 lock->Release(); |
648 | 711 |
| 712 HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", |
| 713 suspected_bad_modules_detected_); |
| 714 HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", |
| 715 confirmed_bad_modules_detected_); |
| 716 |
649 NotificationService::current()->Notify( | 717 NotificationService::current()->Notify( |
650 NotificationType::MODULE_LIST_ENUMERATED, | 718 NotificationType::MODULE_LIST_ENUMERATED, |
651 Source<EnumerateModulesModel>(this), | 719 Source<EnumerateModulesModel>(this), |
652 NotificationService::NoDetails()); | 720 NotificationService::NoDetails()); |
653 | 721 |
654 if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) { | 722 if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) { |
655 bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0; | 723 bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0; |
656 NotificationService::current()->Notify( | 724 NotificationService::current()->Notify( |
657 NotificationType::MODULE_INCOMPATIBILITY_DETECTED, | 725 NotificationType::MODULE_INCOMPATIBILITY_DETECTED, |
658 Source<EnumerateModulesModel>(this), | 726 Source<EnumerateModulesModel>(this), |
(...skipping 11 matching lines...) Expand all Loading... |
670 GenerateHash(WideToUTF8(module.name), &filename); | 738 GenerateHash(WideToUTF8(module.name), &filename); |
671 GenerateHash(WideToUTF8(module.location), &location); | 739 GenerateHash(WideToUTF8(module.location), &location); |
672 GenerateHash(WideToUTF8(module.description), &description); | 740 GenerateHash(WideToUTF8(module.description), &description); |
673 GenerateHash(WideToUTF8(module.digital_signer), &signer); | 741 GenerateHash(WideToUTF8(module.digital_signer), &signer); |
674 | 742 |
675 string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS, | 743 string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS, |
676 ASCIIToWide(filename), ASCIIToWide(location), | 744 ASCIIToWide(filename), ASCIIToWide(location), |
677 ASCIIToWide(description), ASCIIToWide(signer)); | 745 ASCIIToWide(description), ASCIIToWide(signer)); |
678 return GURL(WideToUTF8(url)); | 746 return GURL(WideToUTF8(url)); |
679 } | 747 } |
OLD | NEW |