249 lines
8.1 KiB
C++
249 lines
8.1 KiB
C++
|
|
// Copyright 2010 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "base/win/wmi.h"
|
||
|
|
|
||
|
|
#include <windows.h>
|
||
|
|
|
||
|
|
#include <objbase.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "base/location.h"
|
||
|
|
#include "base/no_destructor.h"
|
||
|
|
#include "base/strings/string_util.h"
|
||
|
|
#include "base/strings/utf_string_conversions.h"
|
||
|
|
#include "base/threading/scoped_thread_priority.h"
|
||
|
|
#include "base/win/scoped_bstr.h"
|
||
|
|
#include "base/win/scoped_variant.h"
|
||
|
|
|
||
|
|
using Microsoft::WRL::ComPtr;
|
||
|
|
|
||
|
|
namespace base {
|
||
|
|
namespace win {
|
||
|
|
|
||
|
|
const wchar_t kCimV2ServerName[] = L"ROOT\\CIMV2";
|
||
|
|
|
||
|
|
const wchar_t kSecurityCenter2ServerName[] = L"ROOT\\SecurityCenter2";
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
constexpr wchar_t kSerialNumberQuery[] = L"SELECT SerialNumber FROM Win32_Bios";
|
||
|
|
|
||
|
|
// Instantiates `wmi_services` with a connection to `server_name` in WMI. Will
|
||
|
|
// set a security blanket if `set_blanket` is true.
|
||
|
|
absl::optional<WmiError> CreateLocalWmiConnection(
|
||
|
|
bool set_blanket,
|
||
|
|
const std::wstring& server_name,
|
||
|
|
ComPtr<IWbemServices>* wmi_services) {
|
||
|
|
DCHECK(wmi_services);
|
||
|
|
ComPtr<IWbemLocator> wmi_locator;
|
||
|
|
HRESULT hr =
|
||
|
|
::CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
|
||
|
|
IID_PPV_ARGS(&wmi_locator));
|
||
|
|
if (FAILED(hr))
|
||
|
|
return WmiError::kFailedToCreateInstance;
|
||
|
|
|
||
|
|
ComPtr<IWbemServices> wmi_services_r;
|
||
|
|
hr = wmi_locator->ConnectServer(base::win::ScopedBstr(server_name).Get(),
|
||
|
|
nullptr, nullptr, nullptr, 0, nullptr,
|
||
|
|
nullptr, &wmi_services_r);
|
||
|
|
if (FAILED(hr))
|
||
|
|
return WmiError::kFailedToConnectToWMI;
|
||
|
|
|
||
|
|
if (set_blanket) {
|
||
|
|
hr = ::CoSetProxyBlanket(wmi_services_r.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 WmiError::kFailedToSetSecurityBlanket;
|
||
|
|
}
|
||
|
|
|
||
|
|
*wmi_services = std::move(wmi_services_r);
|
||
|
|
return absl::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Runs `query` through `wmi_services` and sets the results' `enumerator`.
|
||
|
|
bool TryRunQuery(const std::wstring& query,
|
||
|
|
const ComPtr<IWbemServices>& wmi_services,
|
||
|
|
ComPtr<IEnumWbemClassObject>* enumerator) {
|
||
|
|
DCHECK(enumerator);
|
||
|
|
base::win::ScopedBstr query_language(L"WQL");
|
||
|
|
base::win::ScopedBstr query_bstr(query);
|
||
|
|
|
||
|
|
ComPtr<IEnumWbemClassObject> enumerator_r;
|
||
|
|
HRESULT hr = wmi_services->ExecQuery(
|
||
|
|
query_language.Get(), query_bstr.Get(),
|
||
|
|
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
|
||
|
|
&enumerator_r);
|
||
|
|
|
||
|
|
if (FAILED(hr))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
*enumerator = std::move(enumerator_r);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
absl::optional<WmiError> RunWmiQuery(const std::wstring& server_name,
|
||
|
|
const std::wstring& query,
|
||
|
|
ComPtr<IEnumWbemClassObject>* enumerator) {
|
||
|
|
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
|
||
|
|
|
||
|
|
DCHECK(enumerator);
|
||
|
|
|
||
|
|
ComPtr<IWbemServices> wmi_services;
|
||
|
|
auto error = CreateLocalWmiConnection(/*set_blanket=*/true, server_name,
|
||
|
|
&wmi_services);
|
||
|
|
|
||
|
|
if (error.has_value())
|
||
|
|
return error;
|
||
|
|
|
||
|
|
if (!TryRunQuery(query, wmi_services, enumerator))
|
||
|
|
return WmiError::kFailedToExecWMIQuery;
|
||
|
|
|
||
|
|
return absl::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CreateLocalWmiConnection(bool set_blanket,
|
||
|
|
ComPtr<IWbemServices>* wmi_services) {
|
||
|
|
// Mitigate the issues caused by loading DLLs on a background thread
|
||
|
|
// (http://crbug/973868).
|
||
|
|
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
|
||
|
|
|
||
|
|
auto error =
|
||
|
|
CreateLocalWmiConnection(set_blanket, kCimV2ServerName, wmi_services);
|
||
|
|
return !error.has_value();
|
||
|
|
}
|
||
|
|
|
||
|
|
ComPtr<IWbemServices> CreateWmiConnection(bool set_blanket,
|
||
|
|
const std::wstring& resource) {
|
||
|
|
// Mitigate the issues caused by loading DLLs on a background thread
|
||
|
|
// (http://crbug/973868).
|
||
|
|
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
|
||
|
|
|
||
|
|
ComPtr<IWbemServices> wmi_services = nullptr;
|
||
|
|
auto error = CreateLocalWmiConnection(set_blanket, resource, &wmi_services);
|
||
|
|
if (error.has_value())
|
||
|
|
return nullptr;
|
||
|
|
return wmi_services;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CreateWmiClassMethodObject(IWbemServices* wmi_services,
|
||
|
|
WStringPiece class_name,
|
||
|
|
WStringPiece method_name,
|
||
|
|
ComPtr<IWbemClassObject>* class_instance) {
|
||
|
|
// We attempt to instantiate a COM object that represents a WMI object plus
|
||
|
|
// a method rolled into one entity.
|
||
|
|
ScopedBstr b_class_name(class_name);
|
||
|
|
ScopedBstr b_method_name(method_name);
|
||
|
|
ComPtr<IWbemClassObject> class_object;
|
||
|
|
HRESULT hr;
|
||
|
|
hr = wmi_services->GetObject(b_class_name.Get(), 0, nullptr, &class_object,
|
||
|
|
nullptr);
|
||
|
|
if (FAILED(hr))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
ComPtr<IWbemClassObject> params_def;
|
||
|
|
hr = class_object->GetMethod(b_method_name.Get(), 0, ¶ms_def, nullptr);
|
||
|
|
if (FAILED(hr))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
if (!params_def.Get()) {
|
||
|
|
// You hit this special case if the WMI class is not a CIM class. MSDN
|
||
|
|
// sometimes tells you this. Welcome to WMI hell.
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
hr = params_def->SpawnInstance(0, &(*class_instance));
|
||
|
|
return SUCCEEDED(hr);
|
||
|
|
}
|
||
|
|
|
||
|
|
// The code in Launch() basically calls the Create Method of the Win32_Process
|
||
|
|
// CIM class is documented here:
|
||
|
|
// http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx
|
||
|
|
// NOTE: The documentation for the Create method suggests that the ProcessId
|
||
|
|
// parameter and return value are of type uint32_t, but when we call the method
|
||
|
|
// the values in the returned out_params, are VT_I4, which is int32_t.
|
||
|
|
bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) {
|
||
|
|
ComPtr<IWbemServices> wmi_local;
|
||
|
|
if (!CreateLocalWmiConnection(true, &wmi_local))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
static constexpr wchar_t class_name[] = L"Win32_Process";
|
||
|
|
static constexpr wchar_t method_name[] = L"Create";
|
||
|
|
ComPtr<IWbemClassObject> process_create;
|
||
|
|
if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name,
|
||
|
|
&process_create)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScopedVariant b_command_line(command_line.c_str());
|
||
|
|
|
||
|
|
if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(),
|
||
|
|
0))) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
ComPtr<IWbemClassObject> out_params;
|
||
|
|
HRESULT hr = wmi_local->ExecMethod(
|
||
|
|
ScopedBstr(class_name).Get(), ScopedBstr(method_name).Get(), 0, nullptr,
|
||
|
|
process_create.Get(), &out_params, nullptr);
|
||
|
|
if (FAILED(hr))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// We're only expecting int32_t or uint32_t values, so no need for
|
||
|
|
// ScopedVariant.
|
||
|
|
VARIANT ret_value = {{{VT_EMPTY}}};
|
||
|
|
hr = out_params->Get(L"ReturnValue", 0, &ret_value, nullptr, nullptr);
|
||
|
|
if (FAILED(hr) || V_I4(&ret_value) != 0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
VARIANT pid = {{{VT_EMPTY}}};
|
||
|
|
hr = out_params->Get(L"ProcessId", 0, &pid, nullptr, nullptr);
|
||
|
|
if (FAILED(hr) || V_I4(&pid) == 0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
if (process_id)
|
||
|
|
*process_id = V_I4(&pid);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// static
|
||
|
|
WmiComputerSystemInfo WmiComputerSystemInfo::Get() {
|
||
|
|
static const base::NoDestructor<WmiComputerSystemInfo> static_info([] {
|
||
|
|
WmiComputerSystemInfo info;
|
||
|
|
ComPtr<IEnumWbemClassObject> enumerator_bios;
|
||
|
|
auto error =
|
||
|
|
RunWmiQuery(kCimV2ServerName, kSerialNumberQuery, &enumerator_bios);
|
||
|
|
if (!error.has_value())
|
||
|
|
info.PopulateSerialNumber(enumerator_bios);
|
||
|
|
return info;
|
||
|
|
}());
|
||
|
|
return *static_info;
|
||
|
|
}
|
||
|
|
|
||
|
|
void WmiComputerSystemInfo::PopulateSerialNumber(
|
||
|
|
const ComPtr<IEnumWbemClassObject>& enumerator_bios) {
|
||
|
|
ComPtr<IWbemClassObject> class_obj;
|
||
|
|
ULONG items_returned = 0;
|
||
|
|
HRESULT hr =
|
||
|
|
enumerator_bios->Next(WBEM_INFINITE, 1, &class_obj, &items_returned);
|
||
|
|
if (FAILED(hr) || !items_returned)
|
||
|
|
return;
|
||
|
|
|
||
|
|
ScopedVariant serial_number;
|
||
|
|
hr = class_obj->Get(L"SerialNumber", 0, serial_number.Receive(), nullptr,
|
||
|
|
nullptr);
|
||
|
|
if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) {
|
||
|
|
serial_number_.assign(V_BSTR(serial_number.ptr()),
|
||
|
|
::SysStringLen(V_BSTR(serial_number.ptr())));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace win
|
||
|
|
} // namespace base
|