| 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 |