• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event)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.
ReportSuccessfulBlocks()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 
InitializeChromeElf()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 
AddFinchBlacklistToRegistry()112 void AddFinchBlacklistToRegistry() {
113   base::win::RegKey finch_blacklist_registry_key(
114       HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE);
115 
116   // No point in trying to continue if the registry key isn't valid.
117   if (!finch_blacklist_registry_key.Valid())
118     return;
119 
120   std::map<std::string, std::string> params;
121   chrome_variations::GetVariationParams(kBrowserBlacklistTrialName, &params);
122 
123   for (std::map<std::string, std::string>::iterator it = params.begin();
124        it != params.end();
125        ++it) {
126     std::wstring name = base::UTF8ToWide(it->first);
127     std::wstring val = base::UTF8ToWide(it->second);
128 
129     finch_blacklist_registry_key.WriteValue(name.c_str(), val.c_str());
130   }
131 }
132 
BrowserBlacklistBeaconSetup()133 void BrowserBlacklistBeaconSetup() {
134   base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER,
135                                            blacklist::kRegistryBeaconPath,
136                                            KEY_QUERY_VALUE | KEY_SET_VALUE);
137 
138   // No point in trying to continue if the registry key isn't valid.
139   if (!blacklist_registry_key.Valid())
140     return;
141 
142   // Record the results of the last blacklist setup.
143   DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
144   blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state);
145 
146   if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
147     // The blacklist setup didn't crash, so we report if it was enabled or not.
148     if (blacklist::IsBlacklistInitialized()) {
149       RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY);
150     } else {
151       // The only way for the blacklist to be enabled, but not fully
152       // initialized is if the thunk setup failed. See blacklist.cc
153       // for more details.
154       RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED);
155     }
156 
157     // Regardless of if the blacklist was fully enabled or not, report how many
158     // times we had to try to set it up.
159     DWORD attempt_count = 0;
160     blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount,
161                                        &attempt_count);
162     UMA_HISTOGRAM_COUNTS_100("Blacklist.RetryAttempts.Success", attempt_count);
163   } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) {
164     // We can set the state to disabled without checking that the maximum number
165     // of attempts was exceeded because blacklist.cc has already done this.
166     RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED);
167     blacklist_registry_key.WriteValue(blacklist::kBeaconState,
168                                       blacklist::BLACKLIST_DISABLED);
169   } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) {
170     RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED);
171   }
172 
173   // Find the last recorded blacklist version.
174   base::string16 blacklist_version;
175   blacklist_registry_key.ReadValue(blacklist::kBeaconVersion,
176                                    &blacklist_version);
177 
178   if (blacklist_version != TEXT(CHROME_VERSION_STRING)) {
179     // The blacklist hasn't been enabled for this version yet, so enable it
180     // and reset the failure count to zero.
181     LONG set_version = blacklist_registry_key.WriteValue(
182         blacklist::kBeaconVersion,
183         TEXT(CHROME_VERSION_STRING));
184 
185     LONG set_state = blacklist_registry_key.WriteValue(
186         blacklist::kBeaconState,
187         blacklist::BLACKLIST_ENABLED);
188 
189     blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount,
190                                       static_cast<DWORD>(0));
191 
192     // Only report the blacklist as getting setup when both registry writes
193     // succeed, since otherwise the blacklist wasn't properly setup.
194     if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
195       RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED);
196   }
197 }
198