chrome_elf_init_win.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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 "base/bind.h" 6#include "base/files/file_path.h" 7#include "base/metrics/field_trial.h" 8#include "base/metrics/histogram.h" 9#include "base/metrics/sparse_histogram.h" 10#include "base/strings/string_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/win/registry.h" 13#include "chrome/browser/chrome_elf_init_win.h" 14#include "chrome/browser/install_verification/win/module_info.h" 15#include "chrome/browser/install_verification/win/module_verification_common.h" 16#include "chrome_elf/blacklist/blacklist.h" 17#include "chrome_elf/chrome_elf_constants.h" 18#include "chrome_elf/dll_hash/dll_hash.h" 19#include "components/variations/variations_associated_data.h" 20#include "content/public/browser/browser_thread.h" 21#include "version.h" // NOLINT 22 23const char kBrowserBlacklistTrialName[] = "BrowserBlacklist"; 24const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist"; 25 26namespace { 27 28// How long to wait, in seconds, before reporting for the second (and last 29// time), what dlls were blocked from the browser process. 30const int kBlacklistReportingDelaySec = 600; 31 32// This enum is used to define the buckets for an enumerated UMA histogram. 33// Hence, 34// (a) existing enumerated constants should never be deleted or reordered, and 35// (b) new constants should only be appended in front of 36// BLACKLIST_SETUP_EVENT_MAX. 37enum BlacklistSetupEventType { 38 // The blacklist beacon has placed to enable the browser blacklisting. 39 BLACKLIST_SETUP_ENABLED = 0, 40 41 // The blacklist was successfully enabled. 42 BLACKLIST_SETUP_RAN_SUCCESSFULLY, 43 44 // The blacklist setup code failed to execute. 45 BLACKLIST_SETUP_FAILED, 46 47 // The blacklist thunk setup code failed. This is probably an indication 48 // that something else patched that code first. 49 BLACKLIST_THUNK_SETUP_FAILED, 50 51 // Deprecated. The blacklist interception code failed to execute. 52 BLACKLIST_INTERCEPTION_FAILED, 53 54 // The blacklist was disabled for this run (after it failed too many times). 55 BLACKLIST_SETUP_DISABLED, 56 57 // Always keep this at the end. 58 BLACKLIST_SETUP_EVENT_MAX, 59}; 60 61void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) { 62 UMA_HISTOGRAM_ENUMERATION("Blacklist.Setup", 63 blacklist_setup_event, 64 BLACKLIST_SETUP_EVENT_MAX); 65} 66 67// Report which DLLs were prevented from being loaded. 68void ReportSuccessfulBlocks() { 69 // Figure out how many dlls were blocked. 70 int num_blocked_dlls = 0; 71 blacklist::SuccessfullyBlocked(NULL, &num_blocked_dlls); 72 73 if (num_blocked_dlls == 0) 74 return; 75 76 // Now retrieve the list of blocked dlls. 77 std::vector<const wchar_t*> blocked_dlls(num_blocked_dlls); 78 blacklist::SuccessfullyBlocked(&blocked_dlls[0], &num_blocked_dlls); 79 80 // Send up the hashes of the blocked dlls via UMA. 81 for (size_t i = 0; i < blocked_dlls.size(); ++i) { 82 std::string dll_name_utf8; 83 base::WideToUTF8(blocked_dlls[i], wcslen(blocked_dlls[i]), &dll_name_utf8); 84 int uma_hash = DllNameToHash(dll_name_utf8); 85 86 UMA_HISTOGRAM_SPARSE_SLOWLY("Blacklist.Blocked", uma_hash); 87 } 88} 89 90} // namespace 91 92void InitializeChromeElf() { 93 if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) == 94 kBrowserBlacklistTrialDisabledGroupName) { 95 // Disable the blacklist for all future runs by removing the beacon. 96 base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER); 97 blacklist_registry_key.DeleteKey(blacklist::kRegistryBeaconPath); 98 } else { 99 AddFinchBlacklistToRegistry(); 100 BrowserBlacklistBeaconSetup(); 101 } 102 103 // Report all successful blacklist interceptions. 104 ReportSuccessfulBlocks(); 105 106 // Schedule another task to report all sucessful interceptions later. 107 // This time delay should be long enough to catch any dlls that attempt to 108 // inject after Chrome has started up. 109 content::BrowserThread::PostDelayedTask( 110 content::BrowserThread::UI, 111 FROM_HERE, 112 base::Bind(&ReportSuccessfulBlocks), 113 base::TimeDelta::FromSeconds(kBlacklistReportingDelaySec)); 114} 115 116// Note that running multiple chrome instances with distinct user data 117// directories could lead to deletion (and/or replacement) of the finch 118// blacklist registry data in one instance before the second has a chance to 119// read those values. 120void AddFinchBlacklistToRegistry() { 121 base::win::RegKey finch_blacklist_registry_key( 122 HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE); 123 124 // No point in trying to continue if the registry key isn't valid. 125 if (!finch_blacklist_registry_key.Valid()) 126 return; 127 128 // Delete and recreate the key to clear the registry. 129 finch_blacklist_registry_key.DeleteKey(L""); 130 finch_blacklist_registry_key.Create( 131 HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE); 132 133 std::map<std::string, std::string> params; 134 variations::GetVariationParams(kBrowserBlacklistTrialName, ¶ms); 135 136 for (std::map<std::string, std::string>::iterator it = params.begin(); 137 it != params.end(); 138 ++it) { 139 std::wstring name = base::UTF8ToWide(it->first); 140 std::wstring val = base::UTF8ToWide(it->second); 141 142 finch_blacklist_registry_key.WriteValue(name.c_str(), val.c_str()); 143 } 144} 145 146void BrowserBlacklistBeaconSetup() { 147 base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER, 148 blacklist::kRegistryBeaconPath, 149 KEY_QUERY_VALUE | KEY_SET_VALUE); 150 151 // No point in trying to continue if the registry key isn't valid. 152 if (!blacklist_registry_key.Valid()) 153 return; 154 155 // Record the results of the last blacklist setup. 156 DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; 157 blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state); 158 159 if (blacklist_state == blacklist::BLACKLIST_ENABLED) { 160 // The blacklist setup didn't crash, so we report if it was enabled or not. 161 if (blacklist::IsBlacklistInitialized()) { 162 RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY); 163 } else { 164 // The only way for the blacklist to be enabled, but not fully 165 // initialized is if the thunk setup failed. See blacklist.cc 166 // for more details. 167 RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED); 168 } 169 170 // Regardless of if the blacklist was fully enabled or not, report how many 171 // times we had to try to set it up. 172 DWORD attempt_count = 0; 173 blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount, 174 &attempt_count); 175 UMA_HISTOGRAM_COUNTS_100("Blacklist.RetryAttempts.Success", attempt_count); 176 } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) { 177 // We can set the state to disabled without checking that the maximum number 178 // of attempts was exceeded because blacklist.cc has already done this. 179 RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED); 180 blacklist_registry_key.WriteValue(blacklist::kBeaconState, 181 blacklist::BLACKLIST_DISABLED); 182 } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) { 183 RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED); 184 } 185 186 // Find the last recorded blacklist version. 187 base::string16 blacklist_version; 188 blacklist_registry_key.ReadValue(blacklist::kBeaconVersion, 189 &blacklist_version); 190 191 if (blacklist_version != TEXT(CHROME_VERSION_STRING)) { 192 // The blacklist hasn't been enabled for this version yet, so enable it 193 // and reset the failure count to zero. 194 LONG set_version = blacklist_registry_key.WriteValue( 195 blacklist::kBeaconVersion, 196 TEXT(CHROME_VERSION_STRING)); 197 198 LONG set_state = blacklist_registry_key.WriteValue( 199 blacklist::kBeaconState, 200 blacklist::BLACKLIST_ENABLED); 201 202 blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount, 203 static_cast<DWORD>(0)); 204 205 // Only report the blacklist as getting setup when both registry writes 206 // succeed, since otherwise the blacklist wasn't properly setup. 207 if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS) 208 RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED); 209 } 210} 211 212bool GetLoadedBlacklistedModules(std::vector<base::string16>* module_names) { 213 DCHECK(module_names); 214 215 std::set<ModuleInfo> module_info_set; 216 if (!GetLoadedModules(&module_info_set)) 217 return false; 218 219 std::set<ModuleInfo>::const_iterator module_iter(module_info_set.begin()); 220 for (; module_iter != module_info_set.end(); ++module_iter) { 221 base::string16 module_file_name(StringToLowerASCII( 222 base::FilePath(module_iter->name).BaseName().value())); 223 if (blacklist::GetBlacklistIndex(module_file_name.c_str()) != -1) { 224 module_names->push_back(module_iter->name); 225 } 226 } 227 228 return true; 229} 230