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