mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 09:58:54 +00:00
core/windows/wmi: Add ability to exec methods on WMI results (#5504)
This commit is contained in:
parent
b5bddbef87
commit
826d1f3b44
@ -96,6 +96,9 @@ function(generateOsqueryCore)
|
||||
add_test(NAME osquery_core_tests_querytests-test COMMAND osquery_core_tests_querytests-test)
|
||||
add_test(NAME osquery_core_tests_processtests-test COMMAND osquery_core_tests_processtests-test)
|
||||
|
||||
if(DEFINED PLATFORM_WINDOWS)
|
||||
add_test(NAME osquery_core_tests_wmitests-test COMMAND osquery_core_tests_wmitests-test)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
osqueryCoreMain()
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
load("//tools/build_defs/oss/osquery:cxx.bzl", "osquery_cxx_test")
|
||||
load("//tools/build_defs/oss/osquery:native.bzl", "osquery_target")
|
||||
load("//tools/build_defs/oss/osquery:platforms.bzl", "POSIX")
|
||||
load("//tools/build_defs/oss/osquery:platforms.bzl", "POSIX", "WINDOWS")
|
||||
|
||||
osquery_cxx_test(
|
||||
name = "flags_tests",
|
||||
@ -137,3 +137,24 @@ osquery_cxx_test(
|
||||
osquery_target("specs:tables"),
|
||||
],
|
||||
)
|
||||
|
||||
osquery_cxx_test(
|
||||
name = "wmi_tests",
|
||||
platform_srcs = [
|
||||
(
|
||||
WINDOWS,
|
||||
[
|
||||
"windows/wmi_tests.cpp",
|
||||
],
|
||||
),
|
||||
],
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
osquery_target("osquery/config/tests:test_utils"),
|
||||
osquery_target("osquery/core:core"),
|
||||
osquery_target("osquery/remote/enroll:tls_enroll"),
|
||||
osquery_target("osquery/sql/tests:sql_test_utils"),
|
||||
osquery_target("osquery/utils/info:info"),
|
||||
osquery_target("tests:helper"),
|
||||
],
|
||||
)
|
||||
|
@ -11,6 +11,10 @@ function(osqueryCoreTestsMain)
|
||||
generateOsqueryCoreTestsWatcherpermissionstestsTest()
|
||||
generateOsqueryCoreTestsQuerytestsTest()
|
||||
generateOsqueryCoreTestsProcesstestsTest()
|
||||
|
||||
if(DEFINED PLATFORM_WINDOWS)
|
||||
generateOsqueryCoreTestsWmitestsTest()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(generateOsqueryCoreTestsFlagstestsTest)
|
||||
@ -139,6 +143,21 @@ function(generateOsqueryCoreTestsQuerytestsTest)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(generateOsqueryCoreTestsWmitestsTest)
|
||||
add_osquery_executable(osquery_core_tests_wmitests-test windows/wmi_tests.cpp)
|
||||
|
||||
target_link_libraries(osquery_core_tests_wmitests-test PRIVATE
|
||||
osquery_cxx_settings
|
||||
osquery_core
|
||||
osquery_config_tests_testutils
|
||||
osquery_remote_enroll_tlsenroll
|
||||
osquery_sql_tests_sqltestutils
|
||||
osquery_utils_info
|
||||
tests_helper
|
||||
thirdparty_googletest
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(generateOsqueryCoreTestsProcesstestsTest)
|
||||
add_osquery_executable(osquery_core_tests_processtests-test process_tests.cpp)
|
||||
|
||||
|
123
osquery/core/tests/windows/wmi_tests.cpp
Normal file
123
osquery/core/tests/windows/wmi_tests.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under both the Apache 2.0 license (found in the
|
||||
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||||
* in the COPYING file in the root directory of this source tree).
|
||||
* You may select, at your option, one of the above-listed licenses.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <osquery/config/tests/test_utils.h>
|
||||
#include <osquery/core.h>
|
||||
#include <osquery/system.h>
|
||||
#include <osquery/utils/system/env.h>
|
||||
|
||||
#include "osquery/core/windows/wmi.h"
|
||||
|
||||
namespace osquery {
|
||||
|
||||
class WmiTests : public testing::Test {
|
||||
protected:
|
||||
void SetUp() {
|
||||
Initializer::platformSetup();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(WmiTests, test_methodcall_inparams) {
|
||||
auto windir = getEnvVar("WINDIR");
|
||||
EXPECT_TRUE(windir);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "SELECT * FROM Win32_Directory WHERE Name = \"" << *windir << "\"";
|
||||
|
||||
auto query = ss.str();
|
||||
|
||||
// This is dirty, we need to escape the WINDIR path
|
||||
boost::replace_all(query, "\\", "\\\\");
|
||||
|
||||
WmiRequest req(query);
|
||||
const auto& wmiResults = req.results();
|
||||
|
||||
EXPECT_EQ(wmiResults.size(), 1);
|
||||
|
||||
WmiMethodArgs args;
|
||||
WmiResultItem out;
|
||||
|
||||
// Setting in-parameter Permissions to 1 (FILE_LIST_DIRECTORY)
|
||||
// The odd part here is that despite Permissions requiring a uint32 in
|
||||
// MSDN documentation, this is actually a VT_BSTR...
|
||||
args.Put<std::string>("Permissions", "1");
|
||||
|
||||
// Get the first item off the result vector since we should only have one.
|
||||
auto& resultItem = wmiResults.front();
|
||||
auto status = req.ExecMethod(resultItem, "GetEffectivePermission", args, out);
|
||||
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
bool retval = false;
|
||||
|
||||
// The return value is stored in the ReturnValue key within the out-parameter
|
||||
// object
|
||||
status = out.GetBool("ReturnValue", retval);
|
||||
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
// As both Administrator and normal user, we should be able to
|
||||
// FILE_LIST_DIRECTORY on WINDIR
|
||||
EXPECT_TRUE(retval);
|
||||
}
|
||||
|
||||
TEST_F(WmiTests, test_methodcall_outparams) {
|
||||
WmiRequest req("SELECT * FROM Win32_Process WHERE Name = \"wininit.exe\"");
|
||||
const auto& wmiResults = req.results();
|
||||
|
||||
// We should expect only one wininit.exe instance?
|
||||
EXPECT_EQ(wmiResults.size(), 1);
|
||||
|
||||
WmiMethodArgs args;
|
||||
WmiResultItem out;
|
||||
|
||||
// Get the first item off the result vector since we should only have one.
|
||||
auto& resultItem = wmiResults.front();
|
||||
auto status = req.ExecMethod(resultItem, "GetOwner", args, out);
|
||||
|
||||
// We use this check to make debugging errors faster
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
long retval;
|
||||
|
||||
// For some reason, this is a VT_I4
|
||||
status = out.GetLong("ReturnValue", retval);
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
// Make sure the return value is successful
|
||||
EXPECT_EQ(retval, 0);
|
||||
|
||||
std::string user_name;
|
||||
std::string domain_name;
|
||||
|
||||
status = out.GetString("User", user_name);
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
status = out.GetString("Domain", domain_name);
|
||||
EXPECT_EQ(status.getMessage(), "OK");
|
||||
EXPECT_TRUE(status.ok());
|
||||
|
||||
// Only NT AUTHORITY\SYSTEM should be running wininit.exe
|
||||
EXPECT_EQ(user_name, "SYSTEM");
|
||||
EXPECT_EQ(domain_name, "NT AUTHORITY");
|
||||
}
|
||||
|
||||
} // namespace osquery
|
@ -15,6 +15,62 @@
|
||||
|
||||
namespace osquery {
|
||||
|
||||
WmiMethodArgs::WmiMethodArgs(WmiMethodArgs&& src) {
|
||||
std::swap(arguments, src.arguments);
|
||||
}
|
||||
|
||||
WmiMethodArgs::~WmiMethodArgs() {
|
||||
for (const auto& p : arguments) {
|
||||
auto var = p.second;
|
||||
|
||||
// BSTR variant types have a raw pointer we need to clean up
|
||||
if (var.vt == VT_BSTR && var.bstrVal != nullptr) {
|
||||
SysFreeString(var.bstrVal);
|
||||
var.bstrVal = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
Status WmiMethodArgs::Put<unsigned int>(const std::string& name,
|
||||
const unsigned int& value) {
|
||||
VARIANT var;
|
||||
var.vt = VT_UI4;
|
||||
var.ulVal = value;
|
||||
|
||||
arguments.insert(std::pair<std::string, VARIANT>(name, var));
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
template <>
|
||||
Status WmiMethodArgs::Put<std::string>(const std::string& name,
|
||||
const std::string& value) {
|
||||
auto wide_value = stringToWstring(value);
|
||||
|
||||
VARIANT var;
|
||||
var.vt = VT_BSTR;
|
||||
var.bstrVal = SysAllocString(wide_value.c_str());
|
||||
if (var.bstrVal == nullptr) {
|
||||
return Status::failure("Out of memory");
|
||||
}
|
||||
|
||||
arguments.insert(std::pair<std::string, VARIANT>(name, var));
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
/// Utility function to help turn a property value into BSTR
|
||||
inline BSTR WbemClassObjectPropToBSTR(const WmiResultItem& item,
|
||||
const std::string& property) {
|
||||
std::string value;
|
||||
auto status = item.GetString(property, value);
|
||||
if (!status.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wstr_value = stringToWstring(value);
|
||||
return SysAllocString(wstr_value.c_str());
|
||||
}
|
||||
|
||||
void WmiResultItem::PrintType(const std::string& name) const {
|
||||
std::wstring property_name = stringToWstring(name);
|
||||
VARIANT value;
|
||||
@ -38,15 +94,15 @@ Status WmiResultItem::GetBool(const std::string& name, bool& ret) const {
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_BOOL) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.boolVal == VARIANT_TRUE ? true : false;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetDateTime(const std::string& name,
|
||||
@ -57,12 +113,12 @@ Status WmiResultItem::GetDateTime(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving datetime from WMI query result.");
|
||||
return Status::failure("Error retrieving datetime from WMI query result.");
|
||||
}
|
||||
|
||||
if (value.vt != VT_BSTR) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Expected VT_BSTR, got something else.");
|
||||
return Status::failure("Expected VT_BSTR, got something else.");
|
||||
}
|
||||
|
||||
ISWbemDateTime* dt = nullptr;
|
||||
@ -70,7 +126,7 @@ Status WmiResultItem::GetDateTime(const std::string& name,
|
||||
CLSID_SWbemDateTime, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dt));
|
||||
if (!SUCCEEDED(hr)) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Failed to create SWbemDateTime object.");
|
||||
return Status::failure("Failed to create SWbemDateTime object.");
|
||||
}
|
||||
|
||||
hr = dt->put_Value(value.bstrVal);
|
||||
@ -78,14 +134,14 @@ Status WmiResultItem::GetDateTime(const std::string& name,
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dt->Release();
|
||||
return Status(-1, "Failed to set SWbemDateTime value.");
|
||||
return Status::failure("Failed to set SWbemDateTime value.");
|
||||
}
|
||||
|
||||
BSTR filetime_str = {0};
|
||||
hr = dt->GetFileTime(is_local ? VARIANT_TRUE : VARIANT_FALSE, &filetime_str);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dt->Release();
|
||||
return Status(-1, "GetFileTime failed.");
|
||||
return Status::failure("GetFileTime failed.");
|
||||
}
|
||||
|
||||
ULARGE_INTEGER ui = {};
|
||||
@ -97,7 +153,7 @@ Status WmiResultItem::GetDateTime(const std::string& name,
|
||||
SysFreeString(filetime_str);
|
||||
dt->Release();
|
||||
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetUChar(const std::string& name,
|
||||
@ -106,15 +162,15 @@ Status WmiResultItem::GetUChar(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_UI1) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.bVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetUnsignedShort(const std::string& name,
|
||||
@ -124,15 +180,15 @@ Status WmiResultItem::GetUnsignedShort(const std::string& name,
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_UI2) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.uiVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetUnsignedInt32(const std::string& name,
|
||||
@ -142,15 +198,15 @@ Status WmiResultItem::GetUnsignedInt32(const std::string& name,
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_UINT) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.uiVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetLong(const std::string& name, long& ret) const {
|
||||
@ -158,15 +214,15 @@ Status WmiResultItem::GetLong(const std::string& name, long& ret) const {
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_I4) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.lVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetUnsignedLong(const std::string& name,
|
||||
@ -175,15 +231,15 @@ Status WmiResultItem::GetUnsignedLong(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_UI4) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.lVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetLongLong(const std::string& name,
|
||||
@ -192,15 +248,15 @@ Status WmiResultItem::GetLongLong(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_I8) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.lVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetUnsignedLongLong(const std::string& name,
|
||||
@ -209,15 +265,15 @@ Status WmiResultItem::GetUnsignedLongLong(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_UI8) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = value.lVal;
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetString(const std::string& name,
|
||||
@ -227,16 +283,16 @@ Status WmiResultItem::GetString(const std::string& name,
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
ret = "";
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != VT_BSTR) {
|
||||
ret = "";
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
ret = bstrToString(value.bstrVal);
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status WmiResultItem::GetVectorOfStrings(const std::string& name,
|
||||
@ -245,11 +301,11 @@ Status WmiResultItem::GetVectorOfStrings(const std::string& name,
|
||||
VARIANT value;
|
||||
HRESULT hr = result_->Get(property_name.c_str(), 0, &value, nullptr, nullptr);
|
||||
if (hr != S_OK) {
|
||||
return Status(-1, "Error retrieving data from WMI query.");
|
||||
return Status::failure("Error retrieving data from WMI query.");
|
||||
}
|
||||
if (value.vt != (VT_BSTR | VT_ARRAY)) {
|
||||
VariantClear(&value);
|
||||
return Status(-1, "Invalid data type returned.");
|
||||
return Status::failure("Invalid data type returned.");
|
||||
}
|
||||
long lbound, ubound;
|
||||
SafeArrayGetLBound(value.parray, 1, &lbound);
|
||||
@ -264,7 +320,7 @@ Status WmiResultItem::GetVectorOfStrings(const std::string& name,
|
||||
}
|
||||
SafeArrayUnaccessData(value.parray);
|
||||
VariantClear(&value);
|
||||
return Status(0);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
WmiRequest::WmiRequest(const std::string& query, BSTR nspace) {
|
||||
@ -272,7 +328,7 @@ WmiRequest::WmiRequest(const std::string& query, BSTR nspace) {
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
IWbemLocator *locator = nullptr;
|
||||
IWbemLocator* locator = nullptr;
|
||||
hr = ::CoInitializeSecurity(nullptr,
|
||||
-1,
|
||||
nullptr,
|
||||
@ -290,22 +346,23 @@ WmiRequest::WmiRequest(const std::string& query, BSTR nspace) {
|
||||
if (hr != S_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
locator_.reset(locator);
|
||||
|
||||
IWbemServices *services = nullptr;
|
||||
IWbemServices* services = nullptr;
|
||||
hr = locator_->ConnectServer(
|
||||
nspace, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &services);
|
||||
if (hr != S_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
services_.reset(services);
|
||||
|
||||
IEnumWbemClassObject *wbem_enum = nullptr;
|
||||
IEnumWbemClassObject* wbem_enum = nullptr;
|
||||
|
||||
hr = services_->ExecQuery(
|
||||
(BSTR)L"WQL", (BSTR)wql.c_str(), WBEM_FLAG_FORWARD_ONLY, nullptr, &wbem_enum);
|
||||
hr = services_->ExecQuery((BSTR)L"WQL",
|
||||
(BSTR)wql.c_str(),
|
||||
WBEM_FLAG_FORWARD_ONLY,
|
||||
nullptr,
|
||||
&wbem_enum);
|
||||
if (hr != S_OK) {
|
||||
return;
|
||||
}
|
||||
@ -319,14 +376,109 @@ WmiRequest::WmiRequest(const std::string& query, BSTR nspace) {
|
||||
|
||||
hr = enum_->Next(WBEM_INFINITE, 1, &result, &result_count);
|
||||
if (SUCCEEDED(hr) && result_count > 0) {
|
||||
results_.push_back(WmiResultItem(result));
|
||||
results_.emplace_back(result);
|
||||
}
|
||||
}
|
||||
|
||||
status_ = Status(0);
|
||||
}
|
||||
|
||||
WmiRequest::~WmiRequest() {
|
||||
results_.clear();
|
||||
}
|
||||
Status WmiRequest::ExecMethod(const WmiResultItem& object,
|
||||
const std::string& method,
|
||||
const WmiMethodArgs& args,
|
||||
WmiResultItem& out_result) const {
|
||||
std::wstring property_name = stringToWstring(method);
|
||||
|
||||
IWbemClassObject* raw = nullptr;
|
||||
|
||||
std::unique_ptr<IWbemClassObject, impl::WmiObjectDeleter> in_def{nullptr};
|
||||
std::unique_ptr<IWbemClassObject, impl::WmiObjectDeleter> class_obj{nullptr};
|
||||
|
||||
BSTR wmi_class_name = WbemClassObjectPropToBSTR(object, "__CLASS");
|
||||
if (wmi_class_name == nullptr) {
|
||||
return Status::failure("Class name out of memory");
|
||||
}
|
||||
|
||||
// GetObject obtains a CIM Class definition object
|
||||
HRESULT hr = services_->GetObject(wmi_class_name, 0, nullptr, &raw, nullptr);
|
||||
SysFreeString(wmi_class_name);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return Status::failure("Failed to GetObject");
|
||||
}
|
||||
|
||||
class_obj.reset(raw);
|
||||
raw = nullptr;
|
||||
|
||||
// GetMethod only works on CIM class definition objects. This is why
|
||||
// we don't use result_
|
||||
hr = class_obj->GetMethod(property_name.c_str(), 0, &raw, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return Status::failure("Failed to GetMethod");
|
||||
}
|
||||
|
||||
in_def.reset(raw);
|
||||
raw = nullptr;
|
||||
|
||||
std::unique_ptr<IWbemClassObject, impl::WmiObjectDeleter> args_inst{nullptr};
|
||||
|
||||
// in_def can be nullptr if the chosen method has no in-parameters
|
||||
if (in_def != nullptr) {
|
||||
hr = in_def->SpawnInstance(0, &raw);
|
||||
if (FAILED(hr)) {
|
||||
return Status::failure("Failed to SpawnInstance");
|
||||
}
|
||||
args_inst.reset(raw);
|
||||
|
||||
// Build up the WMI method call arguments
|
||||
for (const auto& p : args.GetArguments()) {
|
||||
const auto& name = p.first;
|
||||
auto pVal = p.second;
|
||||
|
||||
auto args_name = stringToWstring(name);
|
||||
|
||||
hr = args_inst->Put(args_name.c_str(), 0, &pVal, 0);
|
||||
if (FAILED(hr)) {
|
||||
return Status::failure("Failed to Put arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In order to execute a WMI method, we need to know the specific object name
|
||||
// and method name
|
||||
IWbemClassObject* out_params = nullptr;
|
||||
|
||||
auto wmi_meth_name = SysAllocString(property_name.c_str());
|
||||
if (wmi_meth_name == nullptr) {
|
||||
return Status::failure("Out of memory");
|
||||
}
|
||||
|
||||
auto wmi_obj_path = WbemClassObjectPropToBSTR(object, "__PATH");
|
||||
if (wmi_obj_path == nullptr) {
|
||||
SysFreeString(wmi_meth_name);
|
||||
return Status::failure("Out of memory");
|
||||
}
|
||||
|
||||
// Execute the WMI method, the return value and out-params all exist in
|
||||
// out_params
|
||||
hr = services_->ExecMethod(wmi_obj_path,
|
||||
wmi_meth_name,
|
||||
0,
|
||||
nullptr,
|
||||
args_inst.get(),
|
||||
&out_params,
|
||||
nullptr);
|
||||
|
||||
SysFreeString(wmi_meth_name);
|
||||
SysFreeString(wmi_obj_path);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return Status::failure("Failed to ExecMethod");
|
||||
}
|
||||
|
||||
out_result = std::move(WmiResultItem(out_params));
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
} // namespace osquery
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <osquery/utils/system/system.h>
|
||||
@ -21,22 +23,63 @@
|
||||
|
||||
namespace osquery {
|
||||
|
||||
namespace impl{
|
||||
using WmiMethodArgsMap = std::unordered_map<std::string, VARIANT>;
|
||||
|
||||
const auto wmiObjectDeleter = [](auto *ptr) {
|
||||
ptr->Release();
|
||||
namespace impl {
|
||||
|
||||
struct WmiObjectDeleter {
|
||||
void operator()(IUnknown* ptr) {
|
||||
if (ptr != nullptr) {
|
||||
ptr->Release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
/**
|
||||
* @brief Helper class to construct and hold the arguments of a WMI method call
|
||||
*
|
||||
* This class is used somewhat exclusively with WmiResultItem::ExecMethod. It
|
||||
* simplifies the construction of a WMI method argument
|
||||
*/
|
||||
class WmiMethodArgs {
|
||||
public:
|
||||
WmiMethodArgs() {}
|
||||
|
||||
WmiMethodArgs(WmiMethodArgs&& src);
|
||||
WmiMethodArgs(WmiMethodArgs&) = delete;
|
||||
|
||||
~WmiMethodArgs();
|
||||
|
||||
/**
|
||||
* @brief Helper function to add items to the arguments of a WMI method call
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
template <typename T>
|
||||
Status Put(const std::string& name, const T& value);
|
||||
|
||||
/**
|
||||
* @brief Getter method for argument dictionary
|
||||
*
|
||||
* @returns Map containing name, value pairs of the arguments
|
||||
*/
|
||||
const WmiMethodArgsMap& GetArguments() const {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private:
|
||||
WmiMethodArgsMap arguments{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper class to hold 1 result object from a WMI request
|
||||
*
|
||||
* This class is used to return to the user just the base type
|
||||
* and value requested from WMI. The class is largely used by
|
||||
* the WmiRequest class defined below
|
||||
*/
|
||||
* @brief Helper class to hold 1 result object from a WMI request
|
||||
*
|
||||
* This class is used to return to the user just the base type
|
||||
* and value requested from WMI. The class is largely used by
|
||||
* the WmiRequest class defined below
|
||||
*/
|
||||
class WmiResultItem {
|
||||
public:
|
||||
explicit WmiResultItem() {}
|
||||
@ -47,19 +90,25 @@ class WmiResultItem {
|
||||
|
||||
WmiResultItem(WmiResultItem&& src) = default;
|
||||
|
||||
WmiResultItem& operator=(WmiResultItem&& src) {
|
||||
result_ = std::move(src.result_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to print the type associated with results
|
||||
*
|
||||
* @returns None.
|
||||
*/
|
||||
* @brief Windows WMI Helper function to print the type associated with
|
||||
* results
|
||||
*
|
||||
* @returns None.
|
||||
*/
|
||||
void PrintType(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve a bool result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve a bool result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetBool(const std::string& name, bool& ret) const;
|
||||
|
||||
/**
|
||||
@ -81,109 +130,118 @@ class WmiResultItem {
|
||||
Status GetUChar(const std::string& name, unsigned char& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned Short from WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indiciating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned Short from WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indiciating the success of the query
|
||||
*/
|
||||
Status GetUnsignedShort(const std::string& name, unsigned short& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned 32 bit integer
|
||||
* from a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned 32 bit integer
|
||||
* from a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetUnsignedInt32(const std::string& name, unsigned int& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve a Long result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve a Long result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetLong(const std::string& name, long& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned Long result from
|
||||
* a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve an unsigned Long result from
|
||||
* a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetUnsignedLong(const std::string& name, unsigned long& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve a Long Long result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve a Long Long result from a
|
||||
* WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetLongLong(const std::string& name, long long& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve an Unsigned Long Long result
|
||||
* from a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve an Unsigned Long Long result
|
||||
* from a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetUnsignedLongLong(const std::string& name,
|
||||
unsigned long long& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve a String result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve a String result from a WMI
|
||||
* query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetString(const std::string& name, std::string& ret) const;
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to retrieve a vector of String result
|
||||
* from
|
||||
* a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
* @brief Windows WMI Helper function to retrieve a vector of String result
|
||||
* from
|
||||
* a WMI query
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status GetVectorOfStrings(const std::string& name,
|
||||
std::vector<std::string>& ret) const;
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<IWbemClassObject, decltype(impl::wmiObjectDeleter)> result_{nullptr, impl::wmiObjectDeleter};
|
||||
std::unique_ptr<IWbemClassObject, impl::WmiObjectDeleter> result_{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Windows wrapper class for querying WMI
|
||||
*
|
||||
* This class abstracts away the WMI querying logic and
|
||||
* will return WMI results given a query string.
|
||||
*/
|
||||
* @brief Windows wrapper class for querying WMI
|
||||
*
|
||||
* This class abstracts away the WMI querying logic and
|
||||
* will return WMI results given a query string.
|
||||
*/
|
||||
class WmiRequest {
|
||||
public:
|
||||
explicit WmiRequest(const std::string& query,
|
||||
BSTR nspace = (BSTR)L"ROOT\\CIMV2");
|
||||
WmiRequest(WmiRequest&& src) = default;
|
||||
~WmiRequest();
|
||||
|
||||
const std::vector<WmiResultItem>& results() const {
|
||||
return results_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Getter for retrieving the status of a WMI Request
|
||||
*
|
||||
* @returns the status of the WMI request.
|
||||
*/
|
||||
* @brief Getter for retrieving the status of a WMI Request
|
||||
*
|
||||
* @returns the status of the WMI request.
|
||||
*/
|
||||
Status getStatus() const {
|
||||
return status_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Windows WMI Helper function to execute a WMI method call on
|
||||
* the given object (wrapped in a result)
|
||||
*
|
||||
* @returns Status indicating the success of the query
|
||||
*/
|
||||
Status ExecMethod(const WmiResultItem& object,
|
||||
const std::string& method,
|
||||
const WmiMethodArgs& args,
|
||||
WmiResultItem& out_result) const;
|
||||
|
||||
private:
|
||||
Status status_;
|
||||
std::vector<WmiResultItem> results_;
|
||||
|
||||
std::unique_ptr<IWbemLocator, decltype(impl::wmiObjectDeleter)> locator_{nullptr, impl::wmiObjectDeleter};
|
||||
std::unique_ptr<IEnumWbemClassObject, decltype(impl::wmiObjectDeleter)> enum_{nullptr, impl::wmiObjectDeleter};
|
||||
std::unique_ptr<IWbemServices, decltype(impl::wmiObjectDeleter)> services_{nullptr, impl::wmiObjectDeleter};
|
||||
std::unique_ptr<IEnumWbemClassObject, impl::WmiObjectDeleter> enum_{nullptr};
|
||||
std::unique_ptr<IWbemLocator, impl::WmiObjectDeleter> locator_{nullptr};
|
||||
std::unique_ptr<IWbemServices, impl::WmiObjectDeleter> services_{nullptr};
|
||||
};
|
||||
}
|
||||
} // namespace osquery
|
||||
|
Loading…
Reference in New Issue
Block a user