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, ¶ms_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