Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port commit from wazuh/wazuh - WMI, WMA APIs Integration for Retrieving Installed Windows Updates #372

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/common/data_provider/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_definitions(-DWIN32=1 -DWIN_EXPORT)
else()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
Expand Down Expand Up @@ -168,7 +169,7 @@ add_library(sysinfo STATIC
target_include_directories(sysinfo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/)

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_link_libraries(sysinfo PUBLIC psapi iphlpapi ws2_32)
target_link_libraries(sysinfo PUBLIC psapi iphlpapi ws2_32 wbemuuid uuid wuguid ole32 oleaut32)
elseif(APPLE)
find_library(iokit_lib IOKit)
if(NOT iokit_lib)
Expand Down
37 changes: 37 additions & 0 deletions src/common/data_provider/src/sysInfoWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@
#include "packages/packagesPYPI.hpp"
#include "packages/packagesNPM.hpp"
#include "packages/modernPackageDataRetriever.hpp"
#include "utilsWrapperWin.hpp"


constexpr auto CENTRAL_PROCESSOR_REGISTRY {"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"};
const std::string UNINSTALL_REGISTRY{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"};
constexpr auto SYSTEM_IDLE_PROCESS_NAME {"System Idle Process"};
constexpr auto SYSTEM_PROCESS_NAME {"System"};


static const std::map<std::string, DWORD> gs_firmwareTableProviderSignature
{
{"ACPI", 0x41435049},
Expand Down Expand Up @@ -912,9 +915,43 @@ void SysInfo::getPackages(std::function<void(nlohmann::json&)> callback) const

ModernFactoryPackagesCreator<HAS_STDFILESYSTEM>::getPackages(searchPaths, callback);
}

nlohmann::json SysInfo::getHotfixes() const
{
std::set<std::string> hotfixes;
std::ostringstream oss;
ComHelper comHelper;

// Initialize COM
HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);

if (SUCCEEDED(hres))
{
try
{
// Query hotfixes using WMI
QueryWMIHotFixes(hotfixes, comHelper);
}
catch (...)
{
// Ignore the error. The OS does not support WMI API.
}


try
{
// Query hotfixes using Windows Update API
QueryWUHotFixes(hotfixes, comHelper);
}
catch (...)
{
// Ignore the error. The OS does not support WUA API.
}

// Uninitialize COM
CoUninitialize();
}

PackageWindowsHelper::getHotFixFromReg(HKEY_LOCAL_MACHINE, PackageWindowsHelper::WIN_REG_HOTFIX, hotfixes);
PackageWindowsHelper::getHotFixFromRegNT(HKEY_LOCAL_MACHINE, PackageWindowsHelper::VISTA_REG_HOTFIX, hotfixes);
PackageWindowsHelper::getHotFixFromRegWOW(HKEY_LOCAL_MACHINE, PackageWindowsHelper::WIN_REG_WOW_HOTFIX, hotfixes);
Expand Down
254 changes: 254 additions & 0 deletions src/common/data_provider/src/utilsWrapperWin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* Wazuh SysInfo
* Copyright (C) 2015, Wazuh Inc.
* December 22, 2021.
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License (version 2) as published by the FSF - Free Software
* Foundation
*/

#include <regex>
#include <sstream> // For std::ostringstream
#include <iomanip> // For std::hex
#include <stdexcept> // For std::runtime_error
#include <string> // For std::string and std::wstring
#include <locale> // For localization utilities (if needed for string conversion)

#include "utilsWrapperWin.hpp"


// Implement WMI functions
HRESULT ComHelper::CreateWmiLocator(IWbemLocator*& pLoc)
{
return CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
}

HRESULT ComHelper::ConnectToWmiServer(IWbemLocator* pLoc, IWbemServices*& pSvc)
{
return pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, 0, 0, 0, &pSvc);
}

HRESULT ComHelper::SetProxyBlanket(IWbemServices* pSvc)
{
return CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
}

HRESULT ComHelper::ExecuteWmiQuery(IWbemServices* pSvc, IEnumWbemClassObject*& pEnumerator)
{
return pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_QuickFixEngineering"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
}

// Implement Windows Update API functions
HRESULT ComHelper::CreateUpdateSearcher(IUpdateSearcher*& pUpdateSearcher)
{
return CoCreateInstance(CLSID_UpdateSearcher, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSearcher, (LPVOID*)&pUpdateSearcher);
}

HRESULT ComHelper::GetTotalHistoryCount(IUpdateSearcher* pUpdateSearcher, LONG& count)
{
return pUpdateSearcher->GetTotalHistoryCount(&count);
}

HRESULT ComHelper::QueryHistory(IUpdateSearcher* pUpdateSearcher, IUpdateHistoryEntryCollection*& pHistory, LONG& count)
{
return pUpdateSearcher->QueryHistory(0, count, &pHistory);
}

HRESULT ComHelper::GetCount(IUpdateHistoryEntryCollection* pHistory, LONG& count)
{
return pHistory->get_Count(&count);
}

HRESULT ComHelper::GetItem(IUpdateHistoryEntryCollection* pHistory, LONG index, IUpdateHistoryEntry** pEntry)
{
return pHistory->get_Item(index, pEntry);
}

HRESULT ComHelper::GetTitle(IUpdateHistoryEntry* pEntry, BSTR& title)
{
return pEntry->get_Title(&title);
}

// This function provides a minimal implementation for converting C strings to BSTRs,
// avoiding the need to include a full COM library. This can be useful when you only need this specific functionality
// and want to minimize dependencies.
namespace _com_util
{
BSTR ConvertStringToBSTR(const char* str)
{
int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
BSTR bstr = SysAllocStringLen(0, len);
MultiByteToWideChar(CP_ACP, 0, str, -1, bstr, len);
return bstr;
}
}

// The BstrToString function takes a Windows-specific string (BSTR) and converts it into
// a standard C++ string (std::string) that can be more easily used in the application.
std::string BstrToString(BSTR bstr)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wstr(bstr, SysStringLen(bstr));
return converter.to_bytes(wstr);
}

void QueryWMIHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper)
{
HRESULT hres;
IWbemLocator* pLoc = NULL;
IWbemServices* pSvc = NULL;
std::ostringstream oss;

hres = comHelper.CreateWmiLocator(pLoc);

if (FAILED(hres))
{
oss << "WMI: Error creating IWbemLocator. Code: " << std::hex << hres;
throw std::runtime_error(oss.str());
}

hres = comHelper.ConnectToWmiServer(pLoc, pSvc);

if (FAILED(hres))
{
if (pLoc) pLoc->Release();

oss << "WMI: connection failed. Code: " << std::hex << hres;
throw std::runtime_error(oss.str());
}

hres = comHelper.SetProxyBlanket(pSvc);

if (FAILED(hres))
{
if (pSvc) pSvc->Release();

if (pLoc) pLoc->Release();

oss << "WMI: security error. Code: " << std::hex << hres;
throw std::runtime_error(oss.str());
}

IEnumWbemClassObject* pEnumerator = NULL;
hres = comHelper.ExecuteWmiQuery(pSvc, pEnumerator);

if (FAILED(hres))
{
if (pLoc) pLoc->Release();

if (pLoc) pSvc->Release();

oss << "WMI: query error. Code: " << std::hex << hres;
throw std::runtime_error(oss.str());
}

IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;

while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

if (0 == uReturn)
{
break;
}

VARIANT vtProp;
hr = pclsObj->Get(L"HotFixID", 0, &vtProp, 0, 0);

if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
{
std::string hotfix = BstrToString(vtProp.bstrVal);

if (hotfixSet.find(hotfix) == hotfixSet.end())
{
// New HotFix found
hotfixSet.insert(hotfix);
}
}

VariantClear(&vtProp);
pclsObj->Release();
}

pSvc->Release();
pLoc->Release();
pEnumerator->Release();
}

void QueryWUHotFixes(std::set<std::string>& hotfixSet, IComHelper& comHelper)
{
HRESULT hres;
IUpdateSearcher* pUpdateSearcher = NULL;
IUpdateHistoryEntryCollection* pHistory = NULL;
std::regex hotfixRegex("KB[0-9]+");
std::ostringstream oss;

hres = comHelper.CreateUpdateSearcher(pUpdateSearcher);

if (FAILED(hres))
{
oss << "WUA: UpdateSearcher error. Code: " << std::hex << hres;
throw std::runtime_error(oss.str());
}

LONG count;
hres = comHelper.GetTotalHistoryCount(pUpdateSearcher, count);

if (FAILED(hres))
{
if (pUpdateSearcher) pUpdateSearcher->Release();

oss << "WUA: Error getting total update history count. Code: 0x" << std::hex << hres;
throw std::runtime_error(oss.str());
}

hres = comHelper.QueryHistory(pUpdateSearcher, pHistory, count);

if (FAILED(hres))
{
if (pUpdateSearcher) pUpdateSearcher->Release();

oss << "WUA: Error querying update history. Code: 0x" << std::hex << hres;
throw std::runtime_error(oss.str());
}

LONG historyCount;
hres = comHelper.GetCount(pHistory, historyCount);

for (LONG i = 0; i < historyCount; ++i)
{
IUpdateHistoryEntry* pEntry = NULL;
comHelper.GetItem(pHistory, i, &pEntry);
BSTR title;
comHelper.GetTitle(pEntry, title);
std::string titleStr = BstrToString(title);

std::smatch match;

if (std::regex_search(titleStr, match, hotfixRegex))
{
std::string hotfix = match[0];

if (hotfixSet.find(hotfix) == hotfixSet.end())
{
// New HotFix found
hotfixSet.insert(hotfix);
}
}

if (pEntry) pEntry->Release();

SysFreeString(title);
}

if (pHistory) pHistory->Release();

if (pUpdateSearcher) pUpdateSearcher->Release();
}

Loading