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