• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2010 The Chromium Authors
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/win/wmi.h"
6 
7 #include <windows.h>
8 
9 #include <objbase.h>
10 #include <stdint.h>
11 
12 #include <string_view>
13 #include <utility>
14 
15 #include "base/location.h"
16 #include "base/no_destructor.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/scoped_thread_priority.h"
20 #include "base/win/scoped_bstr.h"
21 #include "base/win/scoped_variant.h"
22 
23 using Microsoft::WRL::ComPtr;
24 
25 namespace base {
26 namespace win {
27 
28 const wchar_t kCimV2ServerName[] = L"ROOT\\CIMV2";
29 
30 const wchar_t kSecurityCenter2ServerName[] = L"ROOT\\SecurityCenter2";
31 
32 namespace {
33 
34 constexpr wchar_t kSerialNumberQuery[] = L"SELECT SerialNumber FROM Win32_Bios";
35 
36 // Instantiates `wmi_services` with a connection to `server_name` in WMI. Will
37 // set a security blanket if `set_blanket` is true.
CreateLocalWmiConnection(bool set_blanket,const std::wstring & server_name,ComPtr<IWbemServices> * wmi_services)38 absl::optional<WmiError> CreateLocalWmiConnection(
39     bool set_blanket,
40     const std::wstring& server_name,
41     ComPtr<IWbemServices>* wmi_services) {
42   DCHECK(wmi_services);
43   ComPtr<IWbemLocator> wmi_locator;
44   HRESULT hr =
45       ::CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
46                          IID_PPV_ARGS(&wmi_locator));
47   if (FAILED(hr))
48     return WmiError::kFailedToCreateInstance;
49 
50   ComPtr<IWbemServices> wmi_services_r;
51   hr = wmi_locator->ConnectServer(base::win::ScopedBstr(server_name).Get(),
52                                   nullptr, nullptr, nullptr, 0, nullptr,
53                                   nullptr, &wmi_services_r);
54   if (FAILED(hr))
55     return WmiError::kFailedToConnectToWMI;
56 
57   if (set_blanket) {
58     hr = ::CoSetProxyBlanket(wmi_services_r.Get(), RPC_C_AUTHN_WINNT,
59                              RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
60                              RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
61     if (FAILED(hr))
62       return WmiError::kFailedToSetSecurityBlanket;
63   }
64 
65   *wmi_services = std::move(wmi_services_r);
66   return absl::nullopt;
67 }
68 
69 // Runs `query` through `wmi_services` and sets the results' `enumerator`.
TryRunQuery(const std::wstring & query,const ComPtr<IWbemServices> & wmi_services,ComPtr<IEnumWbemClassObject> * enumerator)70 bool TryRunQuery(const std::wstring& query,
71                  const ComPtr<IWbemServices>& wmi_services,
72                  ComPtr<IEnumWbemClassObject>* enumerator) {
73   DCHECK(enumerator);
74   base::win::ScopedBstr query_language(L"WQL");
75   base::win::ScopedBstr query_bstr(query);
76 
77   ComPtr<IEnumWbemClassObject> enumerator_r;
78   HRESULT hr = wmi_services->ExecQuery(
79       query_language.Get(), query_bstr.Get(),
80       WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
81       &enumerator_r);
82 
83   if (FAILED(hr))
84     return false;
85 
86   *enumerator = std::move(enumerator_r);
87   return true;
88 }
89 
90 }  // namespace
91 
RunWmiQuery(const std::wstring & server_name,const std::wstring & query,ComPtr<IEnumWbemClassObject> * enumerator)92 absl::optional<WmiError> RunWmiQuery(const std::wstring& server_name,
93                                      const std::wstring& query,
94                                      ComPtr<IEnumWbemClassObject>* enumerator) {
95   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
96 
97   DCHECK(enumerator);
98 
99   ComPtr<IWbemServices> wmi_services;
100   auto error = CreateLocalWmiConnection(/*set_blanket=*/true, server_name,
101                                         &wmi_services);
102 
103   if (error.has_value())
104     return error;
105 
106   if (!TryRunQuery(query, wmi_services, enumerator))
107     return WmiError::kFailedToExecWMIQuery;
108 
109   return absl::nullopt;
110 }
111 
CreateLocalWmiConnection(bool set_blanket,ComPtr<IWbemServices> * wmi_services)112 bool CreateLocalWmiConnection(bool set_blanket,
113                               ComPtr<IWbemServices>* wmi_services) {
114   // Mitigate the issues caused by loading DLLs on a background thread
115   // (http://crbug/973868).
116   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
117 
118   auto error =
119       CreateLocalWmiConnection(set_blanket, kCimV2ServerName, wmi_services);
120   return !error.has_value();
121 }
122 
CreateWmiConnection(bool set_blanket,const std::wstring & resource)123 ComPtr<IWbemServices> CreateWmiConnection(bool set_blanket,
124                                           const std::wstring& resource) {
125   // Mitigate the issues caused by loading DLLs on a background thread
126   // (http://crbug/973868).
127   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
128 
129   ComPtr<IWbemServices> wmi_services = nullptr;
130   auto error = CreateLocalWmiConnection(set_blanket, resource, &wmi_services);
131   if (error.has_value())
132     return nullptr;
133   return wmi_services;
134 }
135 
CreateWmiClassMethodObject(IWbemServices * wmi_services,std::wstring_view class_name,std::wstring_view method_name,ComPtr<IWbemClassObject> * class_instance)136 bool CreateWmiClassMethodObject(IWbemServices* wmi_services,
137                                 std::wstring_view class_name,
138                                 std::wstring_view method_name,
139                                 ComPtr<IWbemClassObject>* class_instance) {
140   // We attempt to instantiate a COM object that represents a WMI object plus
141   // a method rolled into one entity.
142   ScopedBstr b_class_name(class_name);
143   ScopedBstr b_method_name(method_name);
144   ComPtr<IWbemClassObject> class_object;
145   HRESULT hr;
146   hr = wmi_services->GetObject(b_class_name.Get(), 0, nullptr, &class_object,
147                                nullptr);
148   if (FAILED(hr))
149     return false;
150 
151   ComPtr<IWbemClassObject> params_def;
152   hr = class_object->GetMethod(b_method_name.Get(), 0, &params_def, nullptr);
153   if (FAILED(hr))
154     return false;
155 
156   if (!params_def.Get()) {
157     // You hit this special case if the WMI class is not a CIM class. MSDN
158     // sometimes tells you this. Welcome to WMI hell.
159     return false;
160   }
161 
162   hr = params_def->SpawnInstance(0, &(*class_instance));
163   return SUCCEEDED(hr);
164 }
165 
166 // The code in Launch() basically calls the Create Method of the Win32_Process
167 // CIM class is documented here:
168 // http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx
169 // NOTE: The documentation for the Create method suggests that the ProcessId
170 // parameter and return value are of type uint32_t, but when we call the method
171 // the values in the returned out_params, are VT_I4, which is int32_t.
WmiLaunchProcess(const std::wstring & command_line,int * process_id)172 bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) {
173   ComPtr<IWbemServices> wmi_local;
174   if (!CreateLocalWmiConnection(true, &wmi_local))
175     return false;
176 
177   static constexpr wchar_t class_name[] = L"Win32_Process";
178   static constexpr wchar_t method_name[] = L"Create";
179   ComPtr<IWbemClassObject> process_create;
180   if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name,
181                                   &process_create)) {
182     return false;
183   }
184 
185   ScopedVariant b_command_line(command_line.c_str());
186 
187   if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(),
188                                  0))) {
189     return false;
190   }
191 
192   ComPtr<IWbemClassObject> out_params;
193   HRESULT hr = wmi_local->ExecMethod(
194       ScopedBstr(class_name).Get(), ScopedBstr(method_name).Get(), 0, nullptr,
195       process_create.Get(), &out_params, nullptr);
196   if (FAILED(hr))
197     return false;
198 
199   // We're only expecting int32_t or uint32_t values, so no need for
200   // ScopedVariant.
201   VARIANT ret_value = {{{VT_EMPTY}}};
202   hr = out_params->Get(L"ReturnValue", 0, &ret_value, nullptr, nullptr);
203   if (FAILED(hr) || V_I4(&ret_value) != 0)
204     return false;
205 
206   VARIANT pid = {{{VT_EMPTY}}};
207   hr = out_params->Get(L"ProcessId", 0, &pid, nullptr, nullptr);
208   if (FAILED(hr) || V_I4(&pid) == 0)
209     return false;
210 
211   if (process_id)
212     *process_id = V_I4(&pid);
213 
214   return true;
215 }
216 
217 // static
Get()218 WmiComputerSystemInfo WmiComputerSystemInfo::Get() {
219   static const base::NoDestructor<WmiComputerSystemInfo> static_info([] {
220     WmiComputerSystemInfo info;
221     ComPtr<IEnumWbemClassObject> enumerator_bios;
222     auto error =
223         RunWmiQuery(kCimV2ServerName, kSerialNumberQuery, &enumerator_bios);
224     if (!error.has_value())
225       info.PopulateSerialNumber(enumerator_bios);
226     return info;
227   }());
228   return *static_info;
229 }
230 
PopulateSerialNumber(const ComPtr<IEnumWbemClassObject> & enumerator_bios)231 void WmiComputerSystemInfo::PopulateSerialNumber(
232     const ComPtr<IEnumWbemClassObject>& enumerator_bios) {
233   ComPtr<IWbemClassObject> class_obj;
234   ULONG items_returned = 0;
235   HRESULT hr =
236       enumerator_bios->Next(WBEM_INFINITE, 1, &class_obj, &items_returned);
237   if (FAILED(hr) || !items_returned)
238     return;
239 
240   ScopedVariant serial_number;
241   hr = class_obj->Get(L"SerialNumber", 0, serial_number.Receive(), nullptr,
242                       nullptr);
243   if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) {
244     serial_number_.assign(V_BSTR(serial_number.ptr()),
245                           ::SysStringLen(V_BSTR(serial_number.ptr())));
246   }
247 }
248 
249 }  // namespace win
250 }  // namespace base
251