Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Side by Side Diff: chrome/browser/metrics/antivirus_metrics_provider_win.cc

Issue 2136423003: Merge M52: Add AntiVirus information to the system profile. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@2743
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "chrome/browser/metrics/antivirus_metrics_provider_win.h"
6
7 #include <iwscapi.h>
8 #include <stddef.h>
9 #include <wbemidl.h>
10 #include <windows.h>
11 #include <wscapi.h>
12
13 #include <string>
14
15 #include "base/bind.h"
16 #include "base/callback.h"
17 #include "base/feature_list.h"
18 #include "base/file_version_info_win.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/metrics/field_trial.h"
22 #include "base/metrics/histogram.h"
23 #include "base/path_service.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/sys_string_conversions.h"
26 #include "base/task_runner_util.h"
27 #include "base/threading/thread_restrictions.h"
28 #include "base/version.h"
29 #include "base/win/scoped_bstr.h"
30 #include "base/win/scoped_com_initializer.h"
31 #include "base/win/scoped_comptr.h"
32 #include "base/win/scoped_variant.h"
33 #include "base/win/windows_version.h"
34 #include "chrome/common/channel_info.h"
35 #include "components/metrics/proto/system_profile.pb.h"
36 #include "components/variations/metrics_util.h"
37 #include "components/version_info/version_info.h"
38
39 namespace {
40
41 // This is an undocumented structure returned from querying the "productState"
42 // uint32 from the AntiVirusProduct in WMI.
43 // http://neophob.com/2010/03/wmi-query-windows-securitycenter2/ gives a good
44 // summary and testing was also done with a variety of AV products to determine
45 // these values as accurately as possible.
46 #pragma pack(push)
47 #pragma pack(1)
48 struct PRODUCT_STATE {
49 uint8_t unknown_1 : 4;
50 uint8_t definition_state : 4; // 1 = Out of date, 0 = Up to date.
51 uint8_t unknown_2 : 4;
52 uint8_t security_state : 4; // 0 = Inactive, 1 = Active, 2 = Snoozed.
53 uint8_t security_provider; // matches WSC_SECURITY_PROVIDER in wscapi.h.
54 uint8_t unknown_3;
55 };
56 #pragma pack(pop)
57
58 static_assert(sizeof(PRODUCT_STATE) == 4, "Wrong packing!");
59
60 bool ShouldReportFullNames() {
61 // The expectation is that this will be disabled for the majority of users,
62 // but this allows a small group to be enabled on other channels if there are
63 // a large percentage of hashes collected on these channels that are not
64 // resolved to names previously collected on Canary channel.
65 bool enabled = base::FeatureList::IsEnabled(
66 AntiVirusMetricsProvider::kReportNamesFeature);
67
68 if (chrome::GetChannel() == version_info::Channel::CANARY)
69 return true;
70
71 return enabled;
72 }
73
74 // Helper function for expanding all environment variables in |path|.
75 std::wstring ExpandEnvironmentVariables(const std::wstring& path) {
76 static const DWORD kMaxBuffer = 32 * 1024; // Max according to MSDN.
77 std::wstring path_expanded;
78 DWORD path_len = MAX_PATH;
79 do {
80 DWORD result = ExpandEnvironmentStrings(
81 path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
82 if (!result) {
83 // Failed to expand variables. Return the original string.
84 DPLOG(ERROR) << path;
85 break;
86 }
87 if (result <= path_len)
88 return path_expanded.substr(0, result - 1);
89 path_len = result;
90 } while (path_len < kMaxBuffer);
91
92 return path;
93 }
94
95 // Helper function to take a |path| to a file, that might contain environment
96 // strings, and read the file version information in |product_version|. Returns
97 // true if it was possible to extract the file information correctly.
98 bool GetProductVersion(std::wstring* path, std::string* product_version) {
99 base::FilePath full_path(ExpandEnvironmentVariables(*path));
100
101 #if !defined(_WIN64)
102 if (!base::PathExists(full_path)) {
103 // On 32-bit builds, path might contain C:\Program Files (x86) instead of
104 // C:\Program Files.
105 base::ReplaceFirstSubstringAfterOffset(path, 0, L"%ProgramFiles%",
106 L"%ProgramW6432%");
107 full_path = base::FilePath(ExpandEnvironmentVariables(*path));
108 }
109 #endif // !defined(_WIN64)
110 std::unique_ptr<FileVersionInfo> version_info(
111 FileVersionInfo::CreateFileVersionInfo(full_path));
112
113 // It is not an error if the product version cannot be read, so continue in
114 // this case.
115 if (version_info.get()) {
116 FileVersionInfoWin* version_info_win =
117 static_cast<FileVersionInfoWin*>(version_info.get());
118 std::string version_str =
119 base::SysWideToUTF8(version_info_win->product_version());
120
121 *product_version = std::move(version_str);
122 return true;
123 }
124
125 return false;
126 }
127
128 } // namespace
129
130 constexpr base::Feature AntiVirusMetricsProvider::kReportNamesFeature;
131
132 AntiVirusMetricsProvider::AntiVirusMetricsProvider(
133 scoped_refptr<base::TaskRunner> task_runner)
134 : task_runner_(task_runner), weak_ptr_factory_(this) {}
135
136 AntiVirusMetricsProvider::~AntiVirusMetricsProvider() {}
137
138 void AntiVirusMetricsProvider::ProvideSystemProfileMetrics(
139 metrics::SystemProfileProto* system_profile_proto) {
140 for (const auto& av_product : av_products_) {
141 metrics::SystemProfileProto_AntiVirusProduct* product =
142 system_profile_proto->add_antivirus_product();
143 *product = av_product;
144 }
145 }
146
147 void AntiVirusMetricsProvider::GetAntiVirusMetrics(
148 const base::Closure& done_callback) {
149 base::PostTaskAndReplyWithResult(
150 task_runner_.get(), FROM_HERE,
151 base::Bind(&AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread),
152 base::Bind(&AntiVirusMetricsProvider::GotAntiVirusProducts,
153 weak_ptr_factory_.GetWeakPtr(), done_callback));
154 }
155
156 // static
157 std::vector<AntiVirusMetricsProvider::AvProduct>
158 AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread() {
159 std::vector<AvProduct> av_products;
160
161 ResultCode result = RESULT_GENERIC_FAILURE;
162
163 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
164
165 // Windows Security Center APIs are not available on Server products.
166 // See https://msdn.microsoft.com/en-us/library/bb432506.aspx.
167 if (os_info->version_type() == base::win::SUITE_SERVER) {
168 result = RESULT_WSC_NOT_AVAILABLE;
169 } else {
170 // The WSC interface is preferred here as it's fully documented, but only
171 // available on Windows 8 and above, so instead use the undocumented WMI
172 // interface on Windows 7 and below.
173 if (os_info->version() >= base::win::VERSION_WIN8)
174 result = FillAntiVirusProductsFromWSC(&av_products);
175 else
176 result = FillAntiVirusProductsFromWMI(&av_products);
177 }
178
179 UMA_HISTOGRAM_ENUMERATION("UMA.AntiVirusMetricsProvider.Result",
180 result,
181 RESULT_COUNT);
182
183 return av_products;
184 }
185
186 void AntiVirusMetricsProvider::GotAntiVirusProducts(
187 const base::Closure& done_callback,
188 const std::vector<AvProduct>& av_products) {
189 DCHECK(thread_checker_.CalledOnValidThread());
190 av_products_ = av_products;
191 done_callback.Run();
192 }
193
194 // static
195 AntiVirusMetricsProvider::ResultCode
196 AntiVirusMetricsProvider::FillAntiVirusProductsFromWSC(
197 std::vector<AvProduct>* products) {
198 std::vector<AvProduct> result_list;
199 base::ThreadRestrictions::AssertIOAllowed();
200 base::win::ScopedCOMInitializer com_initializer;
201
202 if (!com_initializer.succeeded())
203 return RESULT_FAILED_TO_INITIALIZE_COM;
204
205 base::win::ScopedComPtr<IWSCProductList> product_list;
206 HRESULT result =
207 CoCreateInstance(__uuidof(WSCProductList), nullptr, CLSCTX_INPROC_SERVER,
208 __uuidof(IWSCProductList), product_list.ReceiveVoid());
209 if (FAILED(result))
210 return RESULT_FAILED_TO_CREATE_INSTANCE;
211
212 result = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS);
213 if (FAILED(result))
214 return RESULT_FAILED_TO_INITIALIZE_PRODUCT_LIST;
215
216 LONG product_count;
217 result = product_list->get_Count(&product_count);
218 if (FAILED(result))
219 return RESULT_FAILED_TO_GET_PRODUCT_COUNT;
220
221 for (LONG i = 0; i < product_count; i++) {
222 IWscProduct* product = nullptr;
223 result = product_list->get_Item(i, &product);
224 if (FAILED(result))
225 return RESULT_FAILED_TO_GET_ITEM;
226
227 static_assert(metrics::SystemProfileProto::AntiVirusState::
228 SystemProfileProto_AntiVirusState_STATE_ON ==
229 static_cast<metrics::SystemProfileProto::AntiVirusState>(
230 WSC_SECURITY_PRODUCT_STATE_ON),
231 "proto and API values must be the same");
232 static_assert(metrics::SystemProfileProto::AntiVirusState::
233 SystemProfileProto_AntiVirusState_STATE_OFF ==
234 static_cast<metrics::SystemProfileProto::AntiVirusState>(
235 WSC_SECURITY_PRODUCT_STATE_OFF),
236 "proto and API values must be the same");
237 static_assert(metrics::SystemProfileProto::AntiVirusState::
238 SystemProfileProto_AntiVirusState_STATE_SNOOZED ==
239 static_cast<metrics::SystemProfileProto::AntiVirusState>(
240 WSC_SECURITY_PRODUCT_STATE_SNOOZED),
241 "proto and API values must be the same");
242 static_assert(metrics::SystemProfileProto::AntiVirusState::
243 SystemProfileProto_AntiVirusState_STATE_EXPIRED ==
244 static_cast<metrics::SystemProfileProto::AntiVirusState>(
245 WSC_SECURITY_PRODUCT_STATE_EXPIRED),
246 "proto and API values must be the same");
247
248 AvProduct av_product;
249 WSC_SECURITY_PRODUCT_STATE product_state;
250 result = product->get_ProductState(&product_state);
251 if (FAILED(result))
252 return RESULT_FAILED_TO_GET_PRODUCT_STATE;
253
254 if (!metrics::SystemProfileProto_AntiVirusState_IsValid(product_state))
255 return RESULT_PRODUCT_STATE_INVALID;
256
257 av_product.set_product_state(
258 static_cast<metrics::SystemProfileProto::AntiVirusState>(
259 product_state));
260
261 base::win::ScopedBstr product_name;
262 result = product->get_ProductName(product_name.Receive());
263 if (FAILED(result))
264 return RESULT_FAILED_TO_GET_PRODUCT_NAME;
265 std::string name =
266 base::SysWideToUTF8(std::wstring(product_name, product_name.Length()));
267 product_name.Release();
268 if (ShouldReportFullNames())
269 av_product.set_product_name(name);
270 av_product.set_product_name_hash(metrics::HashName(name));
271
272 base::win::ScopedBstr remediation_path;
273 result = product->get_RemediationPath(remediation_path.Receive());
274 if (FAILED(result))
275 return RESULT_FAILED_TO_GET_REMEDIATION_PATH;
276 std::wstring path_str(remediation_path, remediation_path.Length());
277 remediation_path.Release();
278
279 std::string product_version;
280 // Not a failure if the product version cannot be read from the file on
281 // disk.
282 if (GetProductVersion(&path_str, &product_version)) {
283 if (ShouldReportFullNames())
284 av_product.set_product_version(product_version);
285 av_product.set_product_version_hash(metrics::HashName(product_version));
286 }
287
288 result_list.push_back(av_product);
289 }
290
291 *products = std::move(result_list);
292
293 return RESULT_SUCCESS;
294 }
295
296 AntiVirusMetricsProvider::ResultCode
297 AntiVirusMetricsProvider::FillAntiVirusProductsFromWMI(
298 std::vector<AvProduct>* products) {
299 std::vector<AvProduct> result_list;
300 base::ThreadRestrictions::AssertIOAllowed();
301 base::win::ScopedCOMInitializer com_initializer;
302
303 if (!com_initializer.succeeded())
304 return RESULT_FAILED_TO_INITIALIZE_COM;
305
306 base::win::ScopedComPtr<IWbemLocator> wmi_locator;
307 HRESULT hr = wmi_locator.CreateInstance(CLSID_WbemLocator, nullptr,
308 CLSCTX_INPROC_SERVER);
309 if (FAILED(hr))
310 return RESULT_FAILED_TO_CREATE_INSTANCE;
311
312 base::win::ScopedComPtr<IWbemServices> wmi_services;
313 hr = wmi_locator->ConnectServer(
314 base::win::ScopedBstr(L"ROOT\\SecurityCenter2"), nullptr, nullptr,
315 nullptr, 0, nullptr, nullptr, wmi_services.Receive());
316 if (FAILED(hr))
317 return RESULT_FAILED_TO_CONNECT_TO_WMI;
318
319 hr = ::CoSetProxyBlanket(wmi_services.get(), RPC_C_AUTHN_WINNT,
320 RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
321 RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
322 if (FAILED(hr))
323 return RESULT_FAILED_TO_SET_SECURITY_BLANKET;
324
325 // This interface is available on Windows Vista and above, and is officially
326 // undocumented.
327 base::win::ScopedBstr query_language(L"WQL");
328 base::win::ScopedBstr query(L"SELECT * FROM AntiVirusProduct");
329 base::win::ScopedComPtr<IEnumWbemClassObject> enumerator;
330
331 hr = wmi_services->ExecQuery(
332 query_language, query,
333 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
334 enumerator.Receive());
335 if (FAILED(hr))
336 return RESULT_FAILED_TO_EXEC_WMI_QUERY;
337
338 // Iterate over the results of the WMI query. Each result will be an
339 // AntiVirusProduct instance.
340 while (true) {
341 base::win::ScopedComPtr<IWbemClassObject> class_object;
342 ULONG items_returned = 0;
343 hr = enumerator->Next(WBEM_INFINITE, 1, class_object.Receive(),
344 &items_returned);
345 if (FAILED(hr))
346 return RESULT_FAILED_TO_ITERATE_RESULTS;
347
348 if (hr == WBEM_S_FALSE || items_returned == 0)
349 break;
350
351 AvProduct av_product;
352 av_product.set_product_state(
353 metrics::SystemProfileProto::AntiVirusState::
354 SystemProfileProto_AntiVirusState_STATE_ON);
355
356 // See definition of PRODUCT_STATE structure above for how this is being
357 // used.
358 base::win::ScopedVariant product_state;
359 hr = class_object->Get(L"productState", 0, product_state.Receive(), 0, 0);
360
361 if (FAILED(hr) || product_state.type() != VT_I4)
362 return RESULT_FAILED_TO_GET_PRODUCT_STATE;
363
364 LONG state_val = V_I4(product_state.ptr());
365 // Map the values from product_state to the proto values.
366 switch (reinterpret_cast<PRODUCT_STATE*>(&state_val)->security_state) {
367 case 0:
368 av_product.set_product_state(
369 metrics::SystemProfileProto::AntiVirusState::
370 SystemProfileProto_AntiVirusState_STATE_OFF);
371 break;
372 case 1:
373 av_product.set_product_state(
374 metrics::SystemProfileProto::AntiVirusState::
375 SystemProfileProto_AntiVirusState_STATE_ON);
376 break;
377 case 2:
378 av_product.set_product_state(
379 metrics::SystemProfileProto::AntiVirusState::
380 SystemProfileProto_AntiVirusState_STATE_SNOOZED);
381 break;
382 default:
383 // unknown state.
384 return RESULT_PRODUCT_STATE_INVALID;
385 break;
386 }
387
388 base::win::ScopedVariant display_name;
389 hr = class_object->Get(L"displayName", 0, display_name.Receive(), 0, 0);
390
391 if (FAILED(hr) || display_name.type() != VT_BSTR)
392 return RESULT_FAILED_TO_GET_PRODUCT_NAME;
393
394 // Owned by ScopedVariant.
395 BSTR temp_bstr = V_BSTR(display_name.ptr());
396 std::string name(base::SysWideToUTF8(
397 std::wstring(temp_bstr, ::SysStringLen(temp_bstr))));
398
399 if (ShouldReportFullNames())
400 av_product.set_product_name(name);
401 av_product.set_product_name_hash(metrics::HashName(name));
402
403 base::win::ScopedVariant exe_path;
404 hr = class_object->Get(L"pathToSignedProductExe", 0, exe_path.Receive(), 0,
405 0);
406
407 if (FAILED(hr) || exe_path.type() != VT_BSTR)
408 return RESULT_FAILED_TO_GET_REMEDIATION_PATH;
409
410 temp_bstr = V_BSTR(exe_path.ptr());
411 std::wstring path_str(temp_bstr, ::SysStringLen(temp_bstr));
412
413 std::string product_version;
414 // Not a failure if the product version cannot be read from the file on
415 // disk.
416 if (GetProductVersion(&path_str, &product_version)) {
417 if (ShouldReportFullNames())
418 av_product.set_product_version(product_version);
419 av_product.set_product_version_hash(metrics::HashName(product_version));
420 }
421
422 result_list.push_back(av_product);
423 }
424
425 *products = std::move(result_list);
426
427 return RESULT_SUCCESS;
428 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698