Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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/browser/enumerate_modules_model_win.h"
      6 
      7 #include <Tlhelp32.h>
      8 #include <wintrust.h>
      9 
     10 #include "base/command_line.h"
     11 #include "base/environment.h"
     12 #include "base/file_path.h"
     13 #include "base/file_version_info_win.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/string_number_conversions.h"
     16 #include "base/string_util.h"
     17 #include "base/time.h"
     18 #include "base/utf_string_conversions.h"
     19 #include "base/values.h"
     20 #include "base/version.h"
     21 #include "base/win/registry.h"
     22 #include "base/win/scoped_handle.h"
     23 #include "crypto/sha2.h"
     24 #include "chrome/browser/net/service_providers_win.h"
     25 #include "chrome/common/chrome_constants.h"
     26 #include "chrome/common/chrome_switches.h"
     27 #include "content/common/notification_service.h"
     28 #include "grit/generated_resources.h"
     29 #include "ui/base/l10n/l10n_util.h"
     30 
     31 // The period of time (in milliseconds) to wait until checking to see if any
     32 // incompatible modules exist.
     33 static const int kModuleCheckDelayMs = 60 * 1000;
     34 
     35 // The path to the Shell Extension key in the Windows registry.
     36 static const wchar_t kRegPath[] =
     37     L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";
     38 
     39 // Short-hand for things on the blacklist you should simply get rid of.
     40 static const ModuleEnumerator::RecommendedAction kUninstallLink =
     41     static_cast<ModuleEnumerator::RecommendedAction>(
     42         ModuleEnumerator::UNINSTALL | ModuleEnumerator::SEE_LINK);
     43 
     44 // Short-hand for things on the blacklist we are investigating and have info.
     45 static const ModuleEnumerator::RecommendedAction kInvestigatingLink =
     46     static_cast<ModuleEnumerator::RecommendedAction>(
     47         ModuleEnumerator::INVESTIGATING | ModuleEnumerator::SEE_LINK);
     48 
     49 // A sort method that sorts by bad modules first, then by full name (including
     50 // path).
     51 static bool ModuleSort(const ModuleEnumerator::Module& a,
     52                        const ModuleEnumerator::Module& b) {
     53   if (a.status != b.status)
     54     return a.status > b.status;
     55 
     56   if (a.location == b.location)
     57     return a.name < b.name;
     58 
     59   return a.location < b.location;
     60 }
     61 
     62 namespace {
     63 
     64 // Used to protect the LoadedModuleVector which is accessed
     65 // from both the UI thread and the FILE thread.
     66 base::Lock* lock = NULL;
     67 
     68 // A struct to help de-duping modules before adding them to the enumerated
     69 // modules vector.
     70 struct FindModule {
     71  public:
     72   explicit FindModule(const ModuleEnumerator::Module& x)
     73     : module(x) {}
     74   bool operator()(const ModuleEnumerator::Module& module_in) const {
     75     return (module.location == module_in.location) &&
     76            (module.name == module_in.name);
     77   }
     78 
     79   const ModuleEnumerator::Module& module;
     80 };
     81 
     82 // Returns the long path name given a short path name. A short path name is a
     83 // path that follows the 8.3 convention and has ~x in it. If the path is already
     84 // a long path name, the function returns the current path without modification.
     85 bool ConvertToLongPath(const string16& short_path, string16* long_path) {
     86   wchar_t long_path_buf[MAX_PATH];
     87   DWORD return_value = GetLongPathName(short_path.c_str(), long_path_buf,
     88                                        MAX_PATH);
     89   if (return_value != 0 && return_value < MAX_PATH) {
     90     *long_path = long_path_buf;
     91     return true;
     92   }
     93 
     94   return false;
     95 }
     96 
     97 }  // namespace
     98 
     99 // The browser process module blacklist. This lists modules that are known
    100 // to cause compatibility issues within the browser process. When adding to this
    101 // list, make sure that all paths are lower-case, in long pathname form, end
    102 // with a slash and use environments variables (or just look at one of the
    103 // comments below and keep it consistent with that). When adding an entry with
    104 // an environment variable not currently used in the list below, make sure to
    105 // update the list in PreparePathMappings. Filename, Description/Signer, and
    106 // Location must be entered as hashes (see GenerateHash). Filename is mandatory.
    107 // Entries without any Description, Signer info, or Location will never be
    108 // marked as confirmed bad (only as suspicious).
    109 const ModuleEnumerator::BlacklistEntry ModuleEnumerator::kModuleBlacklist[] = {
    110   // NOTE: Please keep this list sorted by dll name, then location.
    111 
    112   // Version 3.2.1.6 seems to be implicated in most cases (and 3.2.2.2 in some).
    113   // There is a more recent version available for download.
    114   // accelerator.dll, "%programfiles%\\speedbit video accelerator\\".
    115   { "7ba9402f", "c9132d48", "", "", "", kInvestigatingLink },
    116 
    117   // apiqq0.dll, "%temp%\\".
    118   { "26134911", "59145acf", "", "", "", kUninstallLink },
    119 
    120   // arking0.dll, "%systemroot%\\system32\\".
    121   { "f5d8f549", "23d01d5b", "", "", "", kUninstallLink },
    122 
    123   // arking1.dll, "%systemroot%\\system32\\".
    124   { "c60ca062", "23d01d5b", "", "", "", kUninstallLink },
    125 
    126   // Said to belong to Killer NIC from BigFoot Networks (not verified). Versions
    127   // 6.0.0.7 and 6.0.0.10 implicated.
    128   // bfllr.dll, "%systemroot%\\system32\\".
    129   { "6bb57633", "23d01d5b", "", "", "", kInvestigatingLink },
    130 
    131   // clickpotatolitesahook.dll, "". Different version each report.
    132   { "0396e037.dll", "", "", "", "", kUninstallLink },
    133 
    134   // cvasds0.dll, "%temp%\\".
    135   { "5ce0037c", "59145acf", "", "", "", kUninstallLink },
    136 
    137   // cwalsp.dll, "%systemroot%\\system32\\".
    138   { "e579a039", "23d01d5b", "", "", "", kUninstallLink },
    139 
    140   // dsoqq0.dll, "%temp%\\".
    141   { "1c4df325", "59145acf", "", "", "", kUninstallLink },
    142 
    143   // This looks like a malware edition of a Brazilian Bank plugin, sometimes
    144   // referred to as Malware.Banc.A.
    145   // gbieh.dll, "%programfiles%\\gbplugin\\".
    146   { "4cb4f2e3", "88e4a3b1", "", "", "", kUninstallLink },
    147 
    148   // hblitesahook.dll. Each report has different version number in location.
    149   { "5d10b363", "", "", "", "", kUninstallLink },
    150 
    151   // icf.dll, "%systemroot%\\system32\\".
    152   { "303825ed", "23d01d5b", "", "", "", INVESTIGATING },
    153 
    154   // idmmbc.dll (IDM), "%systemroot%\\system32\\". See: http://crbug.com/26892/.
    155   { "b8dce5c3", "23d01d5b", "", "", "6.03",
    156       static_cast<RecommendedAction>(UPDATE | DISABLE) },
    157 
    158   // imon.dll (NOD32), "%systemroot%\\system32\\". See: http://crbug.com/21715.
    159   { "8f42f22e", "23d01d5b", "", "", "4.0",
    160       static_cast<RecommendedAction>(UPDATE | DISABLE) },
    161 
    162   // is3lsp.dll, "%commonprogramfiles%\\is3\\anti-spyware\\".
    163   { "7ffbdce9", "bc5673f2", "", "", "",
    164       static_cast<RecommendedAction>(UPDATE | DISABLE | SEE_LINK) },
    165 
    166   // jsi.dll, "%programfiles%\\profilecraze\\".
    167   { "f9555eea", "e3548061", "", "", "", kUninstallLink },
    168 
    169   // kernel.dll, "%programfiles%\\contentwatch\\internet protection\\modules\\".
    170   { "ead2768e", "4e61ce60", "", "", "", INVESTIGATING },
    171 
    172   // mgking0.dll, "%systemroot%\\system32\\".
    173   { "d0893e38", "23d01d5b", "", "", "", kUninstallLink },
    174 
    175   // mgking0.dll, "%temp%\\".
    176   { "d0893e38", "59145acf", "", "", "", kUninstallLink },
    177 
    178   // mgking1.dll, "%systemroot%\\system32\\".
    179   { "3e837222", "23d01d5b", "", "", "", kUninstallLink },
    180 
    181   // mgking1.dll, "%temp%\\".
    182   { "3e837222", "59145acf", "", "", "", kUninstallLink },
    183 
    184   // mstcipha.ime, "%systemroot%\\system32\\".
    185   { "5523579e", "23d01d5b", "", "", "", INVESTIGATING },
    186 
    187   // mwtsp.dll, "%systemroot%\\system32\\".
    188   { "9830bff6", "23d01d5b", "", "", "", kUninstallLink },
    189 
    190   // nodqq0.dll, "%temp%\\".
    191   { "b86ce04d", "59145acf", "", "", "", kUninstallLink },
    192 
    193   // nProtect GameGuard Anti-cheat system. Every report has a different
    194   // location, since it is installed into and run from a game folder. Various
    195   // versions implicated.
    196   // npggnt.des, no fixed location.
    197   { "f2c8790d", "", "", "", "", kInvestigatingLink },
    198 
    199   // nvlsp.dll,
    200   // "%programfiles%\\nvidia corporation\\networkaccessmanager\\bin32\\".
    201   { "37f907e2", "3ad0ff23", "", "", "", INVESTIGATING },
    202 
    203   // post0.dll, "%systemroot%\\system32\\".
    204   { "7405c0c8", "23d01d5b", "", "", "", kUninstallLink },
    205 
    206   // radhslib.dll (Naomi web filter), "%programfiles%\\rnamfler\\".
    207   // See http://crbug.com/12517.
    208   { "7edcd250", "0733dc3e", "", "", "", INVESTIGATING },
    209 
    210   // rlls.dll, "%programfiles%\\relevantknowledge\\".
    211   { "a1ed94a7", "ea9d6b36", "", "", "", kUninstallLink },
    212 
    213   // rooksdol.dll, "%programfiles%\\trusteer\\rapport\\bin\\".
    214   { "802aefef", "06120e13", "", "", "3.5.1008.40", INVESTIGATING },
    215 
    216   // sdata.dll, "%programdata%\\srtserv\\".
    217   { "1936d5cc", "223c44be", "", "", "", kUninstallLink },
    218 
    219   // searchtree.dll,
    220   // "%programfiles%\\contentwatch\\internet protection\\modules\\".
    221   { "f6915a31", "4e61ce60", "", "", "", INVESTIGATING },
    222 
    223   // sgprxy.dll, "%commonprogramfiles%\\is3\\anti-spyware\\".
    224   { "005965ea", "bc5673f2", "", "", "", INVESTIGATING },
    225 
    226   // swi_filter_0001.dll (Sophos Web Intelligence),
    227   // "%programfiles%\\sophos\\sophos anti-virus\\web intelligence\\".
    228   // A small random sample all showed version 1.0.5.0.
    229   { "61112d7b", "25fb120f", "", "", "", kInvestigatingLink },
    230 
    231   // twking0.dll, "%systemroot%\\system32\\".
    232   { "0355549b", "23d01d5b", "", "", "", kUninstallLink },
    233 
    234   // twking1.dll, "%systemroot%\\system32\\".
    235   { "02e44508", "23d01d5b", "", "", "", kUninstallLink },
    236 
    237   // vksaver.dll, "%systemroot%\\system32\\".
    238   { "c4a784d5", "23d01d5b", "", "", "", kUninstallLink },
    239 
    240   // vlsp.dll (Venturi Firewall?), "%systemroot%\\system32\\".
    241   { "2e4eb93d", "23d01d5b", "", "", "", INVESTIGATING },
    242 
    243   // vmn3_1dn.dll, "%appdata%\\roaming\\vmndtxtb\\".
    244   { "bba2037d", "9ab68585", "", "", "", kUninstallLink },
    245 
    246   // webanalyzer.dll,
    247   // "%programfiles%\\contentwatch\\internet protection\\modules\\".
    248   { "c70b697d", "4e61ce60", "", "", "", INVESTIGATING },
    249 
    250   // wowst0.dll, "%systemroot%\\system32\\".
    251   { "38ad9963", "23d01d5b", "", "", "", kUninstallLink },
    252 
    253   // wxbase28u_vc_cw.dll, "%systemroot%\\system32\\".
    254   { "e967210d", "23d01d5b", "", "", "", kUninstallLink },
    255 };
    256 
    257 // Generates an 8 digit hash from the input given.
    258 static void GenerateHash(const std::string& input, std::string* output) {
    259   if (input.empty()) {
    260     *output = "";
    261     return;
    262   }
    263 
    264   uint8 hash[4];
    265   crypto::SHA256HashString(input, hash, sizeof(hash));
    266   *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
    267 }
    268 
    269 // -----------------------------------------------------------------------------
    270 
    271 // static
    272 void ModuleEnumerator::NormalizeModule(Module* module) {
    273   string16 path = module->location;
    274   if (!ConvertToLongPath(path, &module->location))
    275     module->location = path;
    276 
    277   module->location = l10n_util::ToLower(module->location);
    278 
    279   // Location contains the filename, so the last slash is where the path
    280   // ends.
    281   size_t last_slash = module->location.find_last_of(L"\\");
    282   if (last_slash != string16::npos) {
    283     module->name = module->location.substr(last_slash + 1);
    284     module->location = module->location.substr(0, last_slash + 1);
    285   } else {
    286     module->name = module->location;
    287     module->location.clear();
    288   }
    289 
    290   // Some version strings have things like (win7_rtm.090713-1255) appended
    291   // to them. Remove that.
    292   size_t first_space = module->version.find_first_of(L" ");
    293   if (first_space != string16::npos)
    294     module->version = module->version.substr(0, first_space);
    295 
    296   module->normalized = true;
    297 }
    298 
    299 // static
    300 ModuleEnumerator::ModuleStatus ModuleEnumerator::Match(
    301     const ModuleEnumerator::Module& module,
    302     const ModuleEnumerator::BlacklistEntry& blacklisted) {
    303   // All modules must be normalized before matching against blacklist.
    304   DCHECK(module.normalized);
    305   // Filename is mandatory and version should not contain spaces.
    306   DCHECK(strlen(blacklisted.filename) > 0);
    307   DCHECK(!strstr(blacklisted.version_from, " "));
    308   DCHECK(!strstr(blacklisted.version_to, " "));
    309 
    310   std::string filename_hash, location_hash;
    311   GenerateHash(WideToUTF8(module.name), &filename_hash);
    312   GenerateHash(WideToUTF8(module.location), &location_hash);
    313 
    314   // Filenames are mandatory. Location is mandatory if given.
    315   if (filename_hash == blacklisted.filename &&
    316           (std::string(blacklisted.location).empty() ||
    317           location_hash == blacklisted.location)) {
    318     // We have a name match against the blacklist (and possibly location match
    319     // also), so check version.
    320     scoped_ptr<Version> module_version(
    321         Version::GetVersionFromString(UTF16ToASCII(module.version)));
    322     scoped_ptr<Version> version_min(
    323         Version::GetVersionFromString(blacklisted.version_from));
    324     scoped_ptr<Version> version_max(
    325         Version::GetVersionFromString(blacklisted.version_to));
    326     bool version_ok = !version_min.get() && !version_max.get();
    327     if (!version_ok) {
    328       bool too_low = version_min.get() &&
    329           (!module_version.get() ||
    330           module_version->CompareTo(*version_min.get()) < 0);
    331       bool too_high = version_max.get() &&
    332           (!module_version.get() ||
    333           module_version->CompareTo(*version_max.get()) >= 0);
    334       version_ok = !too_low && !too_high;
    335     }
    336 
    337     if (version_ok) {
    338       // At this point, the names match and there is no version specified
    339       // or the versions also match.
    340 
    341       std::string desc_or_signer(blacklisted.desc_or_signer);
    342       std::string signer_hash, description_hash;
    343       GenerateHash(WideToUTF8(module.digital_signer), &signer_hash);
    344       GenerateHash(WideToUTF8(module.description), &description_hash);
    345 
    346       // If signatures match (or both are empty), then we have a winner.
    347       if (signer_hash == desc_or_signer)
    348         return CONFIRMED_BAD;
    349 
    350       // If descriptions match (or both are empty) and the locations match, then
    351       // we also have a confirmed match.
    352       if (description_hash == desc_or_signer &&
    353           !location_hash.empty() && location_hash == blacklisted.location) {
    354         return CONFIRMED_BAD;
    355       }
    356 
    357       // We are not sure, but it is likely bad.
    358       return SUSPECTED_BAD;
    359     }
    360   }
    361 
    362   return NOT_MATCHED;
    363 }
    364 
    365 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer)
    366     : enumerated_modules_(NULL),
    367       observer_(observer),
    368       limited_mode_(false),
    369       callback_thread_id_(BrowserThread::ID_COUNT) {
    370 }
    371 
    372 ModuleEnumerator::~ModuleEnumerator() {
    373 }
    374 
    375 void ModuleEnumerator::ScanNow(ModulesVector* list, bool limited_mode) {
    376   enumerated_modules_ = list;
    377 
    378   limited_mode_ = limited_mode;
    379 
    380   if (!limited_mode_) {
    381     CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_));
    382     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
    383     BrowserThread::PostTask(
    384         BrowserThread::FILE, FROM_HERE,
    385             NewRunnableMethod(this, &ModuleEnumerator::ScanImpl));
    386   } else {
    387     // Run it synchronously.
    388     ScanImpl();
    389   }
    390 }
    391 
    392 void ModuleEnumerator::ScanImpl() {
    393   base::TimeTicks start_time = base::TimeTicks::Now();
    394 
    395   enumerated_modules_->clear();
    396 
    397   // Make sure the path mapping vector is setup so we can collapse paths.
    398   PreparePathMappings();
    399 
    400   // Enumerating loaded modules must happen first since the other types of
    401   // modules check for duplication against the loaded modules.
    402   base::TimeTicks checkpoint = base::TimeTicks::Now();
    403   EnumerateLoadedModules();
    404   base::TimeTicks checkpoint2 = base::TimeTicks::Now();
    405   UMA_HISTOGRAM_TIMES("Conflicts.EnumerateLoadedModules",
    406                       checkpoint2 - checkpoint);
    407 
    408   checkpoint = checkpoint2;
    409   EnumerateShellExtensions();
    410   checkpoint2 = base::TimeTicks::Now();
    411   UMA_HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions",
    412                       checkpoint2 - checkpoint);
    413 
    414   checkpoint = checkpoint2;
    415   EnumerateWinsockModules();
    416   checkpoint2 = base::TimeTicks::Now();
    417   UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules",
    418                       checkpoint2 - checkpoint);
    419 
    420   MatchAgainstBlacklist();
    421 
    422   std::sort(enumerated_modules_->begin(),
    423             enumerated_modules_->end(), ModuleSort);
    424 
    425   if (!limited_mode_) {
    426     // Send a reply back on the UI thread.
    427     BrowserThread::PostTask(
    428         callback_thread_id_, FROM_HERE,
    429         NewRunnableMethod(this, &ModuleEnumerator::ReportBack));
    430   } else {
    431     // We are on the main thread already.
    432     ReportBack();
    433   }
    434 
    435   UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime",
    436                       base::TimeTicks::Now() - start_time);
    437 }
    438 
    439 void ModuleEnumerator::EnumerateLoadedModules() {
    440   // Get all modules in the current process.
    441   base::win::ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
    442                                ::GetCurrentProcessId()));
    443   if (!snap.Get())
    444     return;
    445 
    446   // Walk the module list.
    447   MODULEENTRY32 module = { sizeof(module) };
    448   if (!::Module32First(snap.Get(), &module))
    449     return;
    450 
    451   do {
    452     // It would be weird to present chrome.exe as a loaded module.
    453     if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0)
    454       continue;
    455 
    456     Module entry;
    457     entry.type = LOADED_MODULE;
    458     entry.location = module.szExePath;
    459     PopulateModuleInformation(&entry);
    460 
    461     NormalizeModule(&entry);
    462     CollapsePath(&entry);
    463     enumerated_modules_->push_back(entry);
    464   } while (::Module32Next(snap.Get(), &module));
    465 }
    466 
    467 void ModuleEnumerator::EnumerateShellExtensions() {
    468   ReadShellExtensions(HKEY_LOCAL_MACHINE);
    469   ReadShellExtensions(HKEY_CURRENT_USER);
    470 }
    471 
    472 void ModuleEnumerator::ReadShellExtensions(HKEY parent) {
    473   base::win::RegistryValueIterator registration(parent, kRegPath);
    474   while (registration.Valid()) {
    475     std::wstring key(std::wstring(L"CLSID\\") + registration.Name() +
    476         L"\\InProcServer32");
    477     base::win::RegKey clsid;
    478     if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ) != ERROR_SUCCESS) {
    479       ++registration;
    480       continue;
    481     }
    482     string16 dll;
    483     if (clsid.ReadValue(L"", &dll) != ERROR_SUCCESS) {
    484       ++registration;
    485       continue;
    486     }
    487     clsid.Close();
    488 
    489     Module entry;
    490     entry.type = SHELL_EXTENSION;
    491     entry.location = dll;
    492     PopulateModuleInformation(&entry);
    493 
    494     NormalizeModule(&entry);
    495     CollapsePath(&entry);
    496     AddToListWithoutDuplicating(entry);
    497 
    498     ++registration;
    499   }
    500 }
    501 
    502 void ModuleEnumerator::EnumerateWinsockModules() {
    503   // Add to this list the Winsock LSP DLLs.
    504   WinsockLayeredServiceProviderList layered_providers;
    505   GetWinsockLayeredServiceProviders(&layered_providers);
    506   for (size_t i = 0; i < layered_providers.size(); ++i) {
    507     Module entry;
    508     entry.type = WINSOCK_MODULE_REGISTRATION;
    509     entry.status = NOT_MATCHED;
    510     entry.normalized = false;
    511     entry.location = layered_providers[i].path;
    512     entry.description = layered_providers[i].name;
    513     entry.recommended_action = NONE;
    514     entry.duplicate_count = 0;
    515 
    516     wchar_t expanded[MAX_PATH];
    517     DWORD size = ExpandEnvironmentStrings(
    518         entry.location.c_str(), expanded, MAX_PATH);
    519     if (size != 0 && size <= MAX_PATH) {
    520       entry.digital_signer =
    521           GetSubjectNameFromDigitalSignature(FilePath(expanded));
    522     }
    523     entry.version = base::IntToString16(layered_providers[i].version);
    524 
    525     // Paths have already been collapsed.
    526     NormalizeModule(&entry);
    527     AddToListWithoutDuplicating(entry);
    528   }
    529 }
    530 
    531 void ModuleEnumerator::PopulateModuleInformation(Module* module) {
    532   module->status = NOT_MATCHED;
    533   module->duplicate_count = 0;
    534   module->normalized = false;
    535   module->digital_signer =
    536       GetSubjectNameFromDigitalSignature(FilePath(module->location));
    537   module->recommended_action = NONE;
    538   scoped_ptr<FileVersionInfo> version_info(
    539       FileVersionInfo::CreateFileVersionInfo(FilePath(module->location)));
    540   if (version_info.get()) {
    541     FileVersionInfoWin* version_info_win =
    542         static_cast<FileVersionInfoWin*>(version_info.get());
    543 
    544     VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
    545     if (fixed_file_info) {
    546       module->description = version_info_win->file_description();
    547       module->version = version_info_win->file_version();
    548       module->product_name = version_info_win->product_name();
    549     }
    550   }
    551 }
    552 
    553 void ModuleEnumerator::AddToListWithoutDuplicating(const Module& module) {
    554   DCHECK(module.normalized);
    555   // These are registered modules, not loaded modules so the same module
    556   // can be registered multiple times, often dozens of times. There is no need
    557   // to list each registration, so we just increment the count for each module
    558   // that is counted multiple times.
    559   ModulesVector::iterator iter;
    560   iter = std::find_if(enumerated_modules_->begin(),
    561                       enumerated_modules_->end(),
    562                       FindModule(module));
    563   if (iter != enumerated_modules_->end()) {
    564     iter->duplicate_count++;
    565     iter->type = static_cast<ModuleType>(iter->type | module.type);
    566   } else {
    567     enumerated_modules_->push_back(module);
    568   }
    569 }
    570 
    571 void ModuleEnumerator::PreparePathMappings() {
    572   path_mapping_.clear();
    573 
    574   scoped_ptr<base::Environment> environment(base::Environment::Create());
    575   std::vector<string16> env_vars;
    576   env_vars.push_back(L"LOCALAPPDATA");
    577   env_vars.push_back(L"ProgramFiles");
    578   env_vars.push_back(L"ProgramData");
    579   env_vars.push_back(L"USERPROFILE");
    580   env_vars.push_back(L"SystemRoot");
    581   env_vars.push_back(L"TEMP");
    582   env_vars.push_back(L"TMP");
    583   env_vars.push_back(L"CommonProgramFiles");
    584   for (std::vector<string16>::const_iterator variable = env_vars.begin();
    585        variable != env_vars.end(); ++variable) {
    586     std::string path;
    587     if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) {
    588       path_mapping_.push_back(
    589           std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\",
    590                          L"%" + l10n_util::ToLower(*variable) + L"%"));
    591     }
    592   }
    593 }
    594 
    595 void ModuleEnumerator::CollapsePath(Module* entry) {
    596   // Take the path and see if we can use any of the substitution values
    597   // from the vector constructed above to replace c:\windows with, for
    598   // example, %systemroot%. The most collapsed path (the one with the
    599   // minimum length) wins.
    600   size_t min_length = MAXINT;
    601   string16 location = entry->location;
    602   for (PathMapping::const_iterator mapping = path_mapping_.begin();
    603        mapping != path_mapping_.end(); ++mapping) {
    604     string16 prefix = mapping->first;
    605     if (StartsWith(location, prefix, false)) {
    606       string16 new_location = mapping->second +
    607                               location.substr(prefix.length() - 1);
    608       size_t length = new_location.length() - mapping->second.length();
    609       if (length < min_length) {
    610         entry->location = new_location;
    611         min_length = length;
    612       }
    613     }
    614   }
    615 }
    616 
    617 void ModuleEnumerator::MatchAgainstBlacklist() {
    618   for (size_t m = 0; m < enumerated_modules_->size(); ++m) {
    619     // Match this module against the blacklist.
    620     Module* module = &(*enumerated_modules_)[m];
    621     module->status = GOOD;  // We change this below potentially.
    622     for (size_t i = 0; i < arraysize(kModuleBlacklist); ++i) {
    623       #if !defined(NDEBUG)
    624         // This saves time when constructing the blacklist.
    625         std::string hashes(kModuleBlacklist[i].filename);
    626         std::string hash1, hash2, hash3;
    627         GenerateHash(kModuleBlacklist[i].filename, &hash1);
    628         hashes += " - " + hash1;
    629         GenerateHash(kModuleBlacklist[i].location, &hash2);
    630         hashes += " - " + hash2;
    631         GenerateHash(kModuleBlacklist[i].desc_or_signer, &hash3);
    632         hashes += " - " + hash3;
    633       #endif
    634 
    635       ModuleStatus status = Match(*module, kModuleBlacklist[i]);
    636       if (status != NOT_MATCHED) {
    637         // We have a match against the blacklist. Mark it as such.
    638         module->status = status;
    639         module->recommended_action = kModuleBlacklist[i].help_tip;
    640         break;
    641       }
    642     }
    643 
    644     // Modules loaded from these locations are frequently malicious
    645     // and notorious for changing frequently so they are not good candidates
    646     // for blacklisting individually. Mark them as suspicious if we haven't
    647     // classified them as bad yet.
    648     if (module->status == NOT_MATCHED || module->status == GOOD) {
    649       if (StartsWith(module->location, L"%temp%", false) ||
    650           StartsWith(module->location, L"%tmp%", false)) {
    651         module->status = SUSPECTED_BAD;
    652       }
    653     }
    654   }
    655 }
    656 
    657 void ModuleEnumerator::ReportBack() {
    658   if (!limited_mode_)
    659     DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
    660   observer_->DoneScanning();
    661 }
    662 
    663 string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature(
    664     const FilePath& filename) {
    665   HCERTSTORE store = NULL;
    666   HCRYPTMSG message = NULL;
    667 
    668   // Find the crypto message for this filename.
    669   bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE,
    670                                    filename.value().c_str(),
    671                                    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
    672                                    CERT_QUERY_FORMAT_FLAG_BINARY,
    673                                    0,
    674                                    NULL,
    675                                    NULL,
    676                                    NULL,
    677                                    &store,
    678                                    &message,
    679                                    NULL);
    680   if (!result)
    681     return string16();
    682 
    683   // Determine the size of the signer info data.
    684   DWORD signer_info_size = 0;
    685   result = !!CryptMsgGetParam(message,
    686                               CMSG_SIGNER_INFO_PARAM,
    687                               0,
    688                               NULL,
    689                               &signer_info_size);
    690   if (!result)
    691     return string16();
    692 
    693   // Allocate enough space to hold the signer info.
    694   scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]);
    695   CMSG_SIGNER_INFO* signer_info =
    696       reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get());
    697 
    698   // Obtain the signer info.
    699   result = !!CryptMsgGetParam(message,
    700                               CMSG_SIGNER_INFO_PARAM,
    701                               0,
    702                               signer_info,
    703                               &signer_info_size);
    704   if (!result)
    705     return string16();
    706 
    707   // Search for the signer certificate.
    708   CERT_INFO CertInfo = {0};
    709   PCCERT_CONTEXT cert_context = NULL;
    710   CertInfo.Issuer = signer_info->Issuer;
    711   CertInfo.SerialNumber = signer_info->SerialNumber;
    712 
    713   cert_context = CertFindCertificateInStore(
    714       store,
    715       X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    716       0,
    717       CERT_FIND_SUBJECT_CERT,
    718       &CertInfo,
    719       NULL);
    720   if (!cert_context)
    721     return string16();
    722 
    723   // Determine the size of the Subject name.
    724   DWORD subject_name_size = 0;
    725   if (!(subject_name_size = CertGetNameString(cert_context,
    726                                               CERT_NAME_SIMPLE_DISPLAY_TYPE,
    727                                               0,
    728                                               NULL,
    729                                               NULL,
    730                                               0))) {
    731     return string16();
    732   }
    733 
    734   string16 subject_name;
    735   subject_name.resize(subject_name_size);
    736 
    737   // Get subject name.
    738   if (!(CertGetNameString(cert_context,
    739                           CERT_NAME_SIMPLE_DISPLAY_TYPE,
    740                           0,
    741                           NULL,
    742                           const_cast<LPWSTR>(subject_name.c_str()),
    743                           subject_name_size))) {
    744     return string16();
    745   }
    746 
    747   return subject_name;
    748 }
    749 
    750 //  ----------------------------------------------------------------------------
    751 
    752 // static
    753 EnumerateModulesModel* EnumerateModulesModel::GetInstance() {
    754   return Singleton<EnumerateModulesModel>::get();
    755 }
    756 
    757 bool EnumerateModulesModel::ShouldShowConflictWarning() const {
    758   // If the user has acknowledged the conflict notification, then we don't need
    759   // to show it again (because the scanning only happens once per the lifetime
    760   // of the process). If we were to run the scanning more than once, then we'd
    761   // need to clear the flag somewhere when we are ready to show it again.
    762   if (conflict_notification_acknowledged_)
    763     return false;
    764 
    765   return confirmed_bad_modules_detected_ > 0;
    766 }
    767 
    768 void EnumerateModulesModel::AcknowledgeConflictNotification() {
    769   if (!conflict_notification_acknowledged_) {
    770     conflict_notification_acknowledged_ = true;
    771     NotificationService::current()->Notify(
    772         NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE,
    773         Source<EnumerateModulesModel>(this),
    774         NotificationService::NoDetails());
    775   }
    776 }
    777 
    778 void EnumerateModulesModel::ScanNow() {
    779   if (scanning_)
    780     return;  // A scan is already in progress.
    781 
    782   lock->Acquire();  // Balanced in DoneScanning();
    783 
    784   scanning_ = true;
    785 
    786   // Instruct the ModuleEnumerator class to load this on the File thread.
    787   // ScanNow does not block.
    788   if (!module_enumerator_)
    789     module_enumerator_ = new ModuleEnumerator(this);
    790   module_enumerator_->ScanNow(&enumerated_modules_, limited_mode_);
    791 }
    792 
    793 ListValue* EnumerateModulesModel::GetModuleList() const {
    794   if (scanning_)
    795     return NULL;
    796 
    797   lock->Acquire();
    798 
    799   if (enumerated_modules_.empty()) {
    800     lock->Release();
    801     return NULL;
    802   }
    803 
    804   ListValue* list = new ListValue();
    805 
    806   for (ModuleEnumerator::ModulesVector::const_iterator module =
    807            enumerated_modules_.begin();
    808        module != enumerated_modules_.end(); ++module) {
    809     DictionaryValue* data = new DictionaryValue();
    810     data->SetInteger("type", module->type);
    811     string16 type_string;
    812     if ((module->type & ModuleEnumerator::LOADED_MODULE) == 0) {
    813       // Module is not loaded, denote type of module.
    814       if (module->type & ModuleEnumerator::SHELL_EXTENSION)
    815         type_string = ASCIIToWide("Shell Extension");
    816       if (module->type & ModuleEnumerator::WINSOCK_MODULE_REGISTRATION) {
    817         if (!type_string.empty())
    818           type_string += ASCIIToWide(", ");
    819         type_string += ASCIIToWide("Winsock");
    820       }
    821       // Must be one of the above type.
    822       DCHECK(!type_string.empty());
    823       if (!limited_mode_) {
    824         type_string += ASCIIToWide(" -- ");
    825         type_string += l10n_util::GetStringUTF16(IDS_CONFLICTS_NOT_LOADED_YET);
    826       }
    827     }
    828     data->SetString("type_description", type_string);
    829     data->SetInteger("status", module->status);
    830     data->SetString("location", module->location);
    831     data->SetString("name", module->name);
    832     data->SetString("product_name", module->product_name);
    833     data->SetString("description", module->description);
    834     data->SetString("version", module->version);
    835     data->SetString("digital_signer", module->digital_signer);
    836 
    837     if (!limited_mode_) {
    838       // Figure out the possible resolution help string.
    839       string16 actions;
    840       string16 separator = ASCIIToWide(" ") + l10n_util::GetStringUTF16(
    841           IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR) +
    842           ASCIIToWide(" ");
    843 
    844       if (module->recommended_action & ModuleEnumerator::NONE) {
    845         actions = l10n_util::GetStringUTF16(
    846             IDS_CONFLICTS_CHECK_INVESTIGATING);
    847       }
    848       if (module->recommended_action & ModuleEnumerator::UNINSTALL) {
    849         if (!actions.empty())
    850           actions += separator;
    851         actions = l10n_util::GetStringUTF16(
    852             IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL);
    853       }
    854       if (module->recommended_action & ModuleEnumerator::UPDATE) {
    855         if (!actions.empty())
    856           actions += separator;
    857         actions += l10n_util::GetStringUTF16(
    858             IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE);
    859       }
    860       if (module->recommended_action & ModuleEnumerator::DISABLE) {
    861         if (!actions.empty())
    862           actions += separator;
    863         actions += l10n_util::GetStringUTF16(
    864             IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE);
    865       }
    866       string16 possible_resolution = actions.empty() ? ASCIIToWide("") :
    867           l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS) +
    868           ASCIIToWide(" ") +
    869           actions;
    870       data->SetString("possibleResolution", possible_resolution);
    871       data->SetString("help_url",
    872                       ConstructHelpCenterUrl(*module).spec().c_str());
    873     }
    874 
    875     list->Append(data);
    876   }
    877 
    878   lock->Release();
    879   return list;
    880 }
    881 
    882 EnumerateModulesModel::EnumerateModulesModel()
    883     : limited_mode_(false),
    884       scanning_(false),
    885       conflict_notification_acknowledged_(false),
    886       confirmed_bad_modules_detected_(0),
    887       suspected_bad_modules_detected_(0) {
    888   const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
    889   if (cmd_line.HasSwitch(switches::kConflictingModulesCheck)) {
    890     check_modules_timer_.Start(
    891         base::TimeDelta::FromMilliseconds(kModuleCheckDelayMs),
    892         this, &EnumerateModulesModel::ScanNow);
    893   }
    894 
    895   lock = new base::Lock();
    896 }
    897 
    898 EnumerateModulesModel::~EnumerateModulesModel() {
    899   delete lock;
    900 }
    901 
    902 void EnumerateModulesModel::DoneScanning() {
    903   confirmed_bad_modules_detected_ = 0;
    904   suspected_bad_modules_detected_ = 0;
    905   for (ModuleEnumerator::ModulesVector::const_iterator module =
    906     enumerated_modules_.begin();
    907     module != enumerated_modules_.end(); ++module) {
    908       if (module->status == ModuleEnumerator::CONFIRMED_BAD)
    909         ++confirmed_bad_modules_detected_;
    910       if (module->status == ModuleEnumerator::SUSPECTED_BAD)
    911         ++suspected_bad_modules_detected_;
    912   }
    913 
    914   scanning_ = false;
    915   lock->Release();
    916 
    917   UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules",
    918                            suspected_bad_modules_detected_);
    919   UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules",
    920                            confirmed_bad_modules_detected_);
    921 
    922   // Notifications are not available in limited mode.
    923   if (limited_mode_)
    924     return;
    925 
    926   NotificationService::current()->Notify(
    927       NotificationType::MODULE_LIST_ENUMERATED,
    928       Source<EnumerateModulesModel>(this),
    929       NotificationService::NoDetails());
    930 
    931   // Command line flag must be enabled for the notification to get sent out.
    932   // Otherwise we'd get the badge (while the feature is disabled) when we
    933   // navigate to about:conflicts and find confirmed matches.
    934   const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
    935   if (!cmd_line.HasSwitch(switches::kConflictingModulesCheck))
    936     return;
    937 
    938   NotificationService::current()->Notify(
    939       NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE,
    940       Source<EnumerateModulesModel>(this),
    941       NotificationService::NoDetails());
    942 }
    943 
    944 GURL EnumerateModulesModel::ConstructHelpCenterUrl(
    945     const ModuleEnumerator::Module& module) const {
    946   if (!(module.recommended_action & ModuleEnumerator::SEE_LINK))
    947     return GURL();
    948 
    949   // Construct the needed hashes.
    950   std::string filename, location, description, signer;
    951   GenerateHash(WideToUTF8(module.name), &filename);
    952   GenerateHash(WideToUTF8(module.location), &location);
    953   GenerateHash(WideToUTF8(module.description), &description);
    954   GenerateHash(WideToUTF8(module.digital_signer), &signer);
    955 
    956   string16 url = l10n_util::GetStringFUTF16(IDS_HELP_CENTER_VIEW_CONFLICTS,
    957       ASCIIToUTF16(filename), ASCIIToUTF16(location),
    958       ASCIIToUTF16(description), ASCIIToUTF16(signer));
    959   return GURL(UTF16ToUTF8(url));
    960 }
    961