Skip to content

Commit

Permalink
win7dep: Remove WMI AV code not called on Win10+
Browse files Browse the repository at this point in the history
On Win7, Chrome used WMI to determine what AV software is installed,
and record metrics. This is no longer required.

I'll look at removing WMI.Antivirus metrics in a follow-on CL.

Bug: 1413064
Change-Id: Iaa8f82c711916c0a9e633342e1c84dd4a61a0ce9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4223116
Reviewed-by: Sebastien Lalancette <seblalancette@chromium.org>
Commit-Queue: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Noel Gordon <noel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1103037}
  • Loading branch information
David Bienvenu authored and Chromium LUCI CQ committed Feb 9, 2023
1 parent fb5c2f4 commit 7129f9f
Show file tree
Hide file tree
Showing 9 changed files with 1 addition and 452 deletions.
5 changes: 0 additions & 5 deletions chrome/services/system_signals/win/metrics_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ void LogWscAvResponse(const device_signals::WscAvProductsResponse& response) {
response.query_error, response.parsing_errors);
}

void LogWmiAvResponse(const device_signals::WmiAvProductsResponse& response) {
LogResponse(".WMI.AntiVirus", response.av_products.size(),
response.query_error, response.parsing_errors);
}

void LogWmiHotfixResponse(const device_signals::WmiHotfixesResponse& response) {
LogResponse(".WMI.Hotfixes", response.hotfixes.size(), response.query_error,
response.parsing_errors);
Expand Down
4 changes: 0 additions & 4 deletions chrome/services/system_signals/win/metrics_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ namespace system_signals {
// `response`.
void LogWscAvResponse(const device_signals::WscAvProductsResponse& response);

// Logs UMA metrics related to the number of items and errors contained in
// `response`.
void LogWmiAvResponse(const device_signals::WmiAvProductsResponse& response);

// Logs UMA metrics related to the number of items and errors contained in
// `response`.
void LogWmiHotfixResponse(const device_signals::WmiHotfixesResponse& response);
Expand Down
163 changes: 1 addition & 162 deletions chrome/services/util_win/av_products.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <iwscapi.h>
#include <objbase.h>
#include <stddef.h>
#include <wbemidl.h>
#include <windows.h>
#include <wrl/client.h>
#include <wscapi.h>
Expand All @@ -30,29 +29,11 @@
#include "base/threading/scoped_blocking_call.h"
#include "base/win/com_init_util.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_variant.h"
#include "base/win/windows_version.h"
#include "components/variations/hashing.h"

namespace {

// This is an undocumented structure returned from querying the "productState"
// uint32 from the AntiVirusProduct in WMI.
// http://neophob.com/2010/03/wmi-query-windows-securitycenter2/ gives a good
// summary and testing was also done with a variety of AV products to determine
// these values as accurately as possible.
#pragma pack(push)
#pragma pack(1)
struct PRODUCT_STATE {
uint8_t unknown_1 : 4;
uint8_t definition_state : 4; // 1 = Out of date, 0 = Up to date.
uint8_t unknown_2 : 4;
uint8_t security_state : 4; // 0 = Inactive, 1 = Active, 2 = Snoozed.
uint8_t security_provider; // matches WSC_SECURITY_PROVIDER in wscapi.h.
uint8_t unknown_3;
};
#pragma pack(pop)

// Filter any part of a product string that looks like it might be a version
// number. Returns true if the part should be removed from the product name.
bool ShouldFilterPart(const std::string& str) {
Expand Down Expand Up @@ -221,142 +202,6 @@ internal::ResultCode FillAntiVirusProductsFromWSC(
return internal::ResultCode::kSuccess;
}

internal::ResultCode FillAntiVirusProductsFromWMI(
bool report_full_names,
std::vector<AvProduct>* products) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);

std::vector<AvProduct> result_list;

Microsoft::WRL::ComPtr<IWbemLocator> wmi_locator;
HRESULT hr =
::CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&wmi_locator));
if (FAILED(hr))
return internal::ResultCode::kFailedToCreateInstance;

Microsoft::WRL::ComPtr<IWbemServices> wmi_services;
hr = wmi_locator->ConnectServer(
base::win::ScopedBstr(L"ROOT\\SecurityCenter2").Get(), nullptr, nullptr,
nullptr, 0, nullptr, nullptr, &wmi_services);
if (FAILED(hr))
return internal::ResultCode::kFailedToConnectToWMI;

hr = ::CoSetProxyBlanket(wmi_services.Get(), RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (FAILED(hr))
return internal::ResultCode::kFailedToSetSecurityBlanket;

// This interface is available on Windows Vista and above, and is officially
// undocumented.
base::win::ScopedBstr query_language(L"WQL");
base::win::ScopedBstr query(L"SELECT * FROM AntiVirusProduct");
Microsoft::WRL::ComPtr<IEnumWbemClassObject> enumerator;

hr = wmi_services->ExecQuery(
query_language.Get(), query.Get(),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
&enumerator);
if (FAILED(hr))
return internal::ResultCode::kFailedToExecWMIQuery;

// Iterate over the results of the WMI query. Each result will be an
// AntiVirusProduct instance.
while (true) {
Microsoft::WRL::ComPtr<IWbemClassObject> class_object;
ULONG items_returned = 0;
hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
if (FAILED(hr))
return internal::ResultCode::kFailedToIterateResults;

if (hr == WBEM_S_FALSE || items_returned == 0)
break;

AvProduct av_product;
av_product.set_product_state(
metrics::SystemProfileProto::AntiVirusState::
SystemProfileProto_AntiVirusState_STATE_ON);

// See definition of PRODUCT_STATE structure above for how this is being
// used.
base::win::ScopedVariant product_state;
hr = class_object->Get(L"productState", 0, product_state.Receive(), 0, 0);

if (FAILED(hr) || product_state.type() != VT_I4)
return internal::ResultCode::kFailedToGetProductState;

LONG state_val = V_I4(product_state.ptr());
PRODUCT_STATE product_state_struct;
std::copy(reinterpret_cast<const char*>(&state_val),
reinterpret_cast<const char*>(&state_val) + sizeof state_val,
reinterpret_cast<char*>(&product_state_struct));
// Map the values from product_state_struct to the proto values.
switch (product_state_struct.security_state) {
case 0:
av_product.set_product_state(
metrics::SystemProfileProto::AntiVirusState::
SystemProfileProto_AntiVirusState_STATE_OFF);
break;
case 1:
av_product.set_product_state(
metrics::SystemProfileProto::AntiVirusState::
SystemProfileProto_AntiVirusState_STATE_ON);
break;
case 2:
av_product.set_product_state(
metrics::SystemProfileProto::AntiVirusState::
SystemProfileProto_AntiVirusState_STATE_SNOOZED);
break;
default:
// unknown state.
return internal::ResultCode::kProductStateInvalid;
}

base::win::ScopedVariant display_name;
hr = class_object->Get(L"displayName", 0, display_name.Receive(), 0, 0);

if (FAILED(hr) || display_name.type() != VT_BSTR)
return internal::ResultCode::kFailedToGetProductName;

// Owned by ScopedVariant.
BSTR temp_bstr = V_BSTR(display_name.ptr());
std::string name = internal::TrimVersionOfAvProductName(base::SysWideToUTF8(
std::wstring(temp_bstr, ::SysStringLen(temp_bstr))));

if (report_full_names)
av_product.set_product_name(name);
av_product.set_product_name_hash(variations::HashName(name));

base::win::ScopedVariant exe_path;
hr = class_object->Get(L"pathToSignedProductExe", 0, exe_path.Receive(), 0,
0);

if (FAILED(hr) || exe_path.type() != VT_BSTR)
return internal::ResultCode::kFailedToGetRemediationPath;

temp_bstr = V_BSTR(exe_path.ptr());
std::wstring path_str(temp_bstr, ::SysStringLen(temp_bstr));

std::string product_version;
// Not a failure if the product version cannot be read from the file on
// disk.
if (GetProductVersion(&path_str, &product_version)) {
if (report_full_names)
av_product.set_product_version(product_version);
av_product.set_product_version_hash(
variations::HashName(product_version));
}

result_list.push_back(av_product);
}

*products = std::move(result_list);

return internal::ResultCode::kSuccess;
}

void MaybeAddTrusteerEndpointProtection(bool report_full_names,
std::vector<AvProduct>* products) {
// Trusteer Rapport does not register with WMI or Security Center so do some
Expand Down Expand Up @@ -517,13 +362,7 @@ std::vector<AvProduct> GetAntiVirusProducts(bool report_full_names) {
if (os_info->version_type() == base::win::SUITE_SERVER) {
result = internal::ResultCode::kWSCNotAvailable;
} else {
// The WSC interface is preferred here as it's fully documented, but only
// available on Windows 8 and above, so instead use the undocumented WMI
// interface on Windows 7 and below.
if (os_info->version() >= base::win::Version::WIN8)
result = FillAntiVirusProductsFromWSC(report_full_names, &av_products);
else
result = FillAntiVirusProductsFromWMI(report_full_names, &av_products);
result = FillAntiVirusProductsFromWSC(report_full_names, &av_products);
}

MaybeAddUnregisteredAntiVirusProducts(report_full_names, &av_products);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class MockWmiClient : public WmiClient {
MockWmiClient();
~MockWmiClient() override;

MOCK_METHOD(WmiAvProductsResponse, GetAntiVirusProducts, (), (override));
MOCK_METHOD(WmiHotfixesResponse, GetInstalledHotfixes, (), (override));
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@

namespace device_signals {

WmiAvProductsResponse::WmiAvProductsResponse() = default;

WmiAvProductsResponse::WmiAvProductsResponse(
const WmiAvProductsResponse& other) = default;

WmiAvProductsResponse::~WmiAvProductsResponse() = default;

WmiHotfixesResponse::WmiHotfixesResponse() = default;

WmiHotfixesResponse::WmiHotfixesResponse(const WmiHotfixesResponse& other) =
Expand Down
16 changes: 0 additions & 16 deletions components/device_signals/core/system_signals/win/wmi_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,6 @@ enum class WmiParsingError {
kMaxValue = kFailedToGetId
};

// Response object for calls to retrieve information about installed AntiVirus
// software.
struct WmiAvProductsResponse {
WmiAvProductsResponse();
~WmiAvProductsResponse();

WmiAvProductsResponse(const WmiAvProductsResponse& other);

std::vector<AvProduct> av_products;
absl::optional<base::win::WmiError> query_error;
std::vector<WmiParsingError> parsing_errors;
};

// Response object for calls to retrieve information about installed hotfix
// updates.
struct WmiHotfixesResponse {
Expand All @@ -59,9 +46,6 @@ class WmiClient {
public:
virtual ~WmiClient() = default;

// Will retrieve information about installed AntiVirus software.
virtual WmiAvProductsResponse GetAntiVirusProducts() = 0;

// Will retrieve information about installed hotfix updates.
virtual WmiHotfixesResponse GetInstalledHotfixes() = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,98 +57,6 @@ WmiClientImpl::WmiClientImpl(RunWmiQueryCallback run_query_callback)

WmiClientImpl::~WmiClientImpl() = default;

WmiAvProductsResponse WmiClientImpl::GetAntiVirusProducts() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
ComPtr<IEnumWbemClassObject> enumerator;
auto error_code =
run_query_callback_.Run(base::win::kSecurityCenter2ServerName,
L"SELECT * FROM AntiVirusProduct", &enumerator);

WmiAvProductsResponse response;
if (error_code.has_value()) {
response.query_error = error_code.value();
return response;
}

// Iterate over the results of the WMI query. Each result will be an
// AntiVirusProduct instance.
HRESULT hr;
while (true) {
ComPtr<IWbemClassObject> class_object;
ULONG items_returned = 0U;
hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);

if (hr == WBEM_S_FALSE || items_returned == 0) {
// Reached the end of the enumerator.
break;
}

// Something went wrong and it wasn't the end of the enumerator.
if (FAILED(hr)) {
response.parsing_errors.push_back(
WmiParsingError::kFailedToIterateResults);
continue;
}

base::win::ScopedVariant product_state;
hr = class_object->Get(L"productState", 0, product_state.Receive(), 0, 0);

if (FAILED(hr) || product_state.type() != VT_I4) {
response.parsing_errors.push_back(WmiParsingError::kFailedToGetState);
continue;
}

LONG state_val = V_I4(product_state.ptr());
internal::PRODUCT_STATE product_state_struct;
std::copy(reinterpret_cast<const char*>(&state_val),
reinterpret_cast<const char*>(&state_val) + sizeof state_val,
reinterpret_cast<char*>(&product_state_struct));
// Map the values from product_state_struct to the av struct values.
AvProduct av_product;
switch (product_state_struct.security_state) {
case 0:
av_product.state = AvProductState::kOff;
break;
case 1:
av_product.state = AvProductState::kOn;
break;
case 2:
av_product.state = AvProductState::kSnoozed;
break;
default:
// Unknown state.
response.parsing_errors.push_back(WmiParsingError::kStateInvalid);
continue;
}

absl::optional<std::string> display_name =
ParseString(L"displayName", class_object);
if (!display_name.has_value()) {
response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
continue;
}

av_product.display_name = display_name.value();

absl::optional<std::string> product_id =
ParseString(L"instanceGuid", class_object);
if (!product_id.has_value()) {
response.parsing_errors.push_back(WmiParsingError::kFailedToGetId);
continue;
}

av_product.product_id = product_id.value();

// If all values were parsed properly, add `av_product` into the response
// vector. If any value could not be parsed properly, the item was
// discarded.
response.av_products.push_back(av_product);
}

return response;
}

WmiHotfixesResponse WmiClientImpl::GetInstalledHotfixes() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
Expand Down

0 comments on commit 7129f9f

Please sign in to comment.