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