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, &params);
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