| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/metrics/antivirus_metrics_provider_win.h" | 5 #include "chrome/browser/metrics/antivirus_metrics_provider_win.h" |
| 6 | 6 |
| 7 #include <iwscapi.h> | 7 #include <iwscapi.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <wbemidl.h> | |
| 10 #include <windows.h> | 9 #include <windows.h> |
| 11 #include <wscapi.h> | 10 #include <wscapi.h> |
| 12 | 11 |
| 13 #include <string> | 12 #include <string> |
| 14 | 13 |
| 15 #include "base/bind.h" | 14 #include "base/bind.h" |
| 16 #include "base/callback.h" | 15 #include "base/callback.h" |
| 17 #include "base/feature_list.h" | 16 #include "base/feature_list.h" |
| 18 #include "base/file_version_info_win.h" | 17 #include "base/file_version_info_win.h" |
| 19 #include "base/files/file_path.h" | 18 #include "base/files/file_path.h" |
| 20 #include "base/files/file_util.h" | 19 #include "base/files/file_util.h" |
| 21 #include "base/metrics/field_trial.h" | 20 #include "base/metrics/field_trial.h" |
| 22 #include "base/metrics/histogram.h" | 21 #include "base/metrics/histogram.h" |
| 23 #include "base/path_service.h" | 22 #include "base/path_service.h" |
| 24 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 25 #include "base/strings/sys_string_conversions.h" | 24 #include "base/strings/sys_string_conversions.h" |
| 26 #include "base/task_runner_util.h" | 25 #include "base/task_runner_util.h" |
| 27 #include "base/threading/thread_restrictions.h" | 26 #include "base/threading/thread_restrictions.h" |
| 28 #include "base/version.h" | 27 #include "base/version.h" |
| 29 #include "base/win/scoped_bstr.h" | 28 #include "base/win/scoped_bstr.h" |
| 30 #include "base/win/scoped_com_initializer.h" | 29 #include "base/win/scoped_com_initializer.h" |
| 31 #include "base/win/scoped_comptr.h" | 30 #include "base/win/scoped_comptr.h" |
| 32 #include "base/win/scoped_variant.h" | |
| 33 #include "base/win/windows_version.h" | 31 #include "base/win/windows_version.h" |
| 34 #include "chrome/common/channel_info.h" | 32 #include "chrome/common/channel_info.h" |
| 35 #include "components/metrics/proto/system_profile.pb.h" | 33 #include "components/metrics/proto/system_profile.pb.h" |
| 36 #include "components/variations/metrics_util.h" | 34 #include "components/variations/metrics_util.h" |
| 37 #include "components/version_info/version_info.h" | 35 #include "components/version_info/version_info.h" |
| 38 | 36 |
| 39 namespace { | 37 namespace { |
| 40 | 38 |
| 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() { | 39 bool ShouldReportFullNames() { |
| 61 // The expectation is that this will be disabled for the majority of users, | 40 // 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 | 41 // 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 | 42 // a large percentage of hashes collected on these channels that are not |
| 64 // resolved to names previously collected on Canary channel. | 43 // resolved to names previously collected on Canary channel. |
| 65 bool enabled = base::FeatureList::IsEnabled( | 44 bool enabled = base::FeatureList::IsEnabled( |
| 66 AntiVirusMetricsProvider::kReportNamesFeature); | 45 AntiVirusMetricsProvider::kReportNamesFeature); |
| 67 | 46 |
| 68 if (chrome::GetChannel() == version_info::Channel::CANARY) | 47 if (chrome::GetChannel() == version_info::Channel::CANARY) |
| 69 return true; | 48 return true; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 base::Bind(&AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread), | 130 base::Bind(&AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread), |
| 152 base::Bind(&AntiVirusMetricsProvider::GotAntiVirusProducts, | 131 base::Bind(&AntiVirusMetricsProvider::GotAntiVirusProducts, |
| 153 weak_ptr_factory_.GetWeakPtr(), done_callback)); | 132 weak_ptr_factory_.GetWeakPtr(), done_callback)); |
| 154 } | 133 } |
| 155 | 134 |
| 156 // static | 135 // static |
| 157 std::vector<AntiVirusMetricsProvider::AvProduct> | 136 std::vector<AntiVirusMetricsProvider::AvProduct> |
| 158 AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread() { | 137 AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread() { |
| 159 std::vector<AvProduct> av_products; | 138 std::vector<AvProduct> av_products; |
| 160 | 139 |
| 161 ResultCode result = RESULT_GENERIC_FAILURE; | 140 ResultCode result = FillAntiVirusProducts(&av_products); |
| 162 | |
| 163 // The WSC interface is preferred here as it's fully documented, but this code | |
| 164 // will fall-back to the undocumented WMI interface on Windows 7 and below. | |
| 165 if (base::win::GetVersion() >= base::win::VERSION_WIN8) | |
| 166 result = FillAntiVirusProductsFromWSC(&av_products); | |
| 167 else | |
| 168 result = FillAntiVirusProductsFromWMI(&av_products); | |
| 169 | 141 |
| 170 UMA_HISTOGRAM_ENUMERATION("UMA.AntiVirusMetricsProvider.Result", | 142 UMA_HISTOGRAM_ENUMERATION("UMA.AntiVirusMetricsProvider.Result", |
| 171 result, | 143 result, |
| 172 RESULT_COUNT); | 144 RESULT_COUNT); |
| 173 | 145 |
| 174 return av_products; | 146 return av_products; |
| 175 } | 147 } |
| 176 | 148 |
| 177 void AntiVirusMetricsProvider::GotAntiVirusProducts( | 149 void AntiVirusMetricsProvider::GotAntiVirusProducts( |
| 178 const base::Closure& done_callback, | 150 const base::Closure& done_callback, |
| 179 const std::vector<AvProduct>& av_products) { | 151 const std::vector<AvProduct>& av_products) { |
| 180 DCHECK(thread_checker_.CalledOnValidThread()); | 152 DCHECK(thread_checker_.CalledOnValidThread()); |
| 181 av_products_ = av_products; | 153 av_products_ = av_products; |
| 182 done_callback.Run(); | 154 done_callback.Run(); |
| 183 } | 155 } |
| 184 | 156 |
| 185 // static | 157 // static |
| 186 AntiVirusMetricsProvider::ResultCode | 158 AntiVirusMetricsProvider::ResultCode |
| 187 AntiVirusMetricsProvider::FillAntiVirusProductsFromWSC( | 159 AntiVirusMetricsProvider::FillAntiVirusProducts( |
| 188 std::vector<AvProduct>* products) { | 160 std::vector<AvProduct>* products) { |
| 189 std::vector<AvProduct> result_list; | 161 std::vector<AvProduct> result_list; |
| 190 base::ThreadRestrictions::AssertIOAllowed(); | 162 base::ThreadRestrictions::AssertIOAllowed(); |
| 191 base::win::ScopedCOMInitializer com_initializer; | 163 base::win::ScopedCOMInitializer com_initializer; |
| 192 | 164 |
| 193 if (!com_initializer.succeeded()) | 165 if (!com_initializer.succeeded()) |
| 194 return RESULT_FAILED_TO_INITIALIZE_COM; | 166 return RESULT_FAILED_TO_INITIALIZE_COM; |
| 195 | 167 |
| 196 base::win::ScopedComPtr<IWSCProductList> product_list; | 168 base::win::ScopedComPtr<IWSCProductList> product_list; |
| 197 HRESULT result = | 169 HRESULT result = |
| 198 CoCreateInstance(__uuidof(WSCProductList), nullptr, CLSCTX_INPROC_SERVER, | 170 CoCreateInstance(__uuidof(WSCProductList), NULL, CLSCTX_INPROC_SERVER, |
| 199 __uuidof(IWSCProductList), product_list.ReceiveVoid()); | 171 __uuidof(IWSCProductList), product_list.ReceiveVoid()); |
| 200 if (FAILED(result)) | 172 if (FAILED(result)) |
| 201 return RESULT_FAILED_TO_CREATE_INSTANCE; | 173 return RESULT_FAILED_TO_CREATE_INSTANCE; |
| 202 | 174 |
| 203 result = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS); | 175 result = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS); |
| 204 if (FAILED(result)) | 176 if (FAILED(result)) |
| 205 return RESULT_FAILED_TO_INITIALIZE_PRODUCT_LIST; | 177 return RESULT_FAILED_TO_INITIALIZE_PRODUCT_LIST; |
| 206 | 178 |
| 207 LONG product_count; | 179 LONG product_count; |
| 208 result = product_list->get_Count(&product_count); | 180 result = product_list->get_Count(&product_count); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 av_product.set_product_version_hash(metrics::HashName(product_version)); | 248 av_product.set_product_version_hash(metrics::HashName(product_version)); |
| 277 } | 249 } |
| 278 | 250 |
| 279 result_list.push_back(av_product); | 251 result_list.push_back(av_product); |
| 280 } | 252 } |
| 281 | 253 |
| 282 *products = std::move(result_list); | 254 *products = std::move(result_list); |
| 283 | 255 |
| 284 return RESULT_SUCCESS; | 256 return RESULT_SUCCESS; |
| 285 } | 257 } |
| 286 | |
| 287 AntiVirusMetricsProvider::ResultCode | |
| 288 AntiVirusMetricsProvider::FillAntiVirusProductsFromWMI( | |
| 289 std::vector<AvProduct>* products) { | |
| 290 std::vector<AvProduct> result_list; | |
| 291 base::ThreadRestrictions::AssertIOAllowed(); | |
| 292 base::win::ScopedCOMInitializer com_initializer; | |
| 293 | |
| 294 if (!com_initializer.succeeded()) | |
| 295 return RESULT_FAILED_TO_INITIALIZE_COM; | |
| 296 | |
| 297 base::win::ScopedComPtr<IWbemLocator> wmi_locator; | |
| 298 HRESULT hr = wmi_locator.CreateInstance(CLSID_WbemLocator, nullptr, | |
| 299 CLSCTX_INPROC_SERVER); | |
| 300 if (FAILED(hr)) | |
| 301 return RESULT_FAILED_TO_CREATE_INSTANCE; | |
| 302 | |
| 303 base::win::ScopedComPtr<IWbemServices> wmi_services; | |
| 304 hr = wmi_locator->ConnectServer( | |
| 305 base::win::ScopedBstr(L"ROOT\\SecurityCenter2"), nullptr, nullptr, | |
| 306 nullptr, 0, nullptr, nullptr, wmi_services.Receive()); | |
| 307 if (FAILED(hr)) | |
| 308 return RESULT_FAILED_TO_CONNECT_TO_WMI; | |
| 309 | |
| 310 hr = ::CoSetProxyBlanket(wmi_services.get(), RPC_C_AUTHN_WINNT, | |
| 311 RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, | |
| 312 RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); | |
| 313 if (FAILED(hr)) | |
| 314 return RESULT_FAILED_TO_SET_SECURITY_BLANKET; | |
| 315 | |
| 316 // This interface is available on Windows Vista and above, and is officially | |
| 317 // undocumented. | |
| 318 base::win::ScopedBstr query_language(L"WQL"); | |
| 319 base::win::ScopedBstr query(L"SELECT * FROM AntiVirusProduct"); | |
| 320 base::win::ScopedComPtr<IEnumWbemClassObject> enumerator; | |
| 321 | |
| 322 hr = wmi_services->ExecQuery( | |
| 323 query_language, query, | |
| 324 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, | |
| 325 enumerator.Receive()); | |
| 326 if (FAILED(hr)) | |
| 327 return RESULT_FAILED_TO_EXEC_WMI_QUERY; | |
| 328 | |
| 329 // Iterate over the results of the WMI query. Each result will be an | |
| 330 // AntiVirusProduct instance. | |
| 331 while (true) { | |
| 332 base::win::ScopedComPtr<IWbemClassObject> class_object; | |
| 333 ULONG items_returned = 0; | |
| 334 hr = enumerator->Next(WBEM_INFINITE, 1, class_object.Receive(), | |
| 335 &items_returned); | |
| 336 if (FAILED(hr)) | |
| 337 return RESULT_FAILED_TO_ITERATE_RESULTS; | |
| 338 | |
| 339 if (hr == WBEM_S_FALSE || items_returned == 0) | |
| 340 break; | |
| 341 | |
| 342 AvProduct av_product; | |
| 343 av_product.set_product_state( | |
| 344 metrics::SystemProfileProto::AntiVirusState:: | |
| 345 SystemProfileProto_AntiVirusState_STATE_ON); | |
| 346 | |
| 347 // See definition of PRODUCT_STATE structure above for how this is being | |
| 348 // used. | |
| 349 base::win::ScopedVariant product_state; | |
| 350 hr = class_object->Get(L"productState", 0, product_state.Receive(), 0, 0); | |
| 351 | |
| 352 if (FAILED(hr) || product_state.type() != VT_I4) | |
| 353 return RESULT_FAILED_TO_GET_PRODUCT_STATE; | |
| 354 | |
| 355 LONG state_val = V_I4(product_state.ptr()); | |
| 356 // Map the values from product_state to the proto values. | |
| 357 switch (reinterpret_cast<PRODUCT_STATE*>(&state_val)->security_state) { | |
| 358 case 0: | |
| 359 av_product.set_product_state( | |
| 360 metrics::SystemProfileProto::AntiVirusState:: | |
| 361 SystemProfileProto_AntiVirusState_STATE_OFF); | |
| 362 break; | |
| 363 case 1: | |
| 364 av_product.set_product_state( | |
| 365 metrics::SystemProfileProto::AntiVirusState:: | |
| 366 SystemProfileProto_AntiVirusState_STATE_ON); | |
| 367 break; | |
| 368 case 2: | |
| 369 av_product.set_product_state( | |
| 370 metrics::SystemProfileProto::AntiVirusState:: | |
| 371 SystemProfileProto_AntiVirusState_STATE_SNOOZED); | |
| 372 break; | |
| 373 default: | |
| 374 // unknown state. | |
| 375 return RESULT_PRODUCT_STATE_INVALID; | |
| 376 break; | |
| 377 } | |
| 378 | |
| 379 base::win::ScopedVariant display_name; | |
| 380 hr = class_object->Get(L"displayName", 0, display_name.Receive(), 0, 0); | |
| 381 | |
| 382 if (FAILED(hr) || display_name.type() != VT_BSTR) | |
| 383 return RESULT_FAILED_TO_GET_PRODUCT_NAME; | |
| 384 | |
| 385 // Owned by ScopedVariant. | |
| 386 BSTR temp_bstr = V_BSTR(display_name.ptr()); | |
| 387 std::string name(base::SysWideToUTF8( | |
| 388 std::wstring(temp_bstr, ::SysStringLen(temp_bstr)))); | |
| 389 | |
| 390 if (ShouldReportFullNames()) | |
| 391 av_product.set_product_name(name); | |
| 392 av_product.set_product_name_hash(metrics::HashName(name)); | |
| 393 | |
| 394 base::win::ScopedVariant exe_path; | |
| 395 hr = class_object->Get(L"pathToSignedProductExe", 0, exe_path.Receive(), 0, | |
| 396 0); | |
| 397 | |
| 398 if (FAILED(hr) || exe_path.type() != VT_BSTR) | |
| 399 return RESULT_FAILED_TO_GET_REMEDIATION_PATH; | |
| 400 | |
| 401 temp_bstr = V_BSTR(exe_path.ptr()); | |
| 402 std::wstring path_str(temp_bstr, ::SysStringLen(temp_bstr)); | |
| 403 | |
| 404 std::string product_version; | |
| 405 // Not a failure if the product version cannot be read from the file on | |
| 406 // disk. | |
| 407 if (GetProductVersion(&path_str, &product_version)) { | |
| 408 if (ShouldReportFullNames()) | |
| 409 av_product.set_product_version(product_version); | |
| 410 av_product.set_product_version_hash(metrics::HashName(product_version)); | |
| 411 } | |
| 412 | |
| 413 result_list.push_back(av_product); | |
| 414 } | |
| 415 | |
| 416 *products = std::move(result_list); | |
| 417 | |
| 418 return RESULT_SUCCESS; | |
| 419 } | |
| OLD | NEW |