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