Created
September 23, 2023 16:05
-
-
Save kbridge/ec56ef93fed44483e74259a29b71bec2 to your computer and use it in GitHub Desktop.
Query Installed Anti-Virus Software on Windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt | |
// https://source.chromium.org/chromium/chromium/src/+/main:base/win/scoped_com_initializer.h | |
// https://source.chromium.org/chromium/chromium/src/+/main:base/win/scoped_bstr.h | |
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/services/util_win/av_products.cc (FillAntiVirusProductsFromWSC) | |
// C | |
#include <cassert> | |
#include <clocale> | |
#include <cstdlib> | |
#include <iostream> | |
#include <limits> | |
#include <sstream> | |
#include <string> | |
#include <string_view> | |
// Windows | |
#define _WIN32_WINNT _WIN32_WINNT_WIN10 | |
#define NOMINMAX | |
#include <iwscapi.h> | |
#include <windows.h> | |
#include <wrl/client.h> | |
#include <wscapi.h> | |
void Die(std::string_view reason) | |
{ | |
std::cerr << "ERROR" << reason << "\n"; | |
exit(1); | |
} | |
namespace std | |
{ | |
std::string to_string(WSC_SECURITY_PRODUCT_STATE state) | |
{ | |
switch (state) { | |
case WSC_SECURITY_PRODUCT_STATE_ON: | |
return "ON"; | |
case WSC_SECURITY_PRODUCT_STATE_OFF: | |
return "OFF"; | |
case WSC_SECURITY_PRODUCT_STATE_SNOOZED: | |
return "SNOOZED"; | |
case WSC_SECURITY_PRODUCT_STATE_EXPIRED: | |
return "EXPIRED"; | |
default: | |
{ | |
std::stringstream ss; | |
ss << "UNKNOWN (" << state << ")"; | |
return ss.str(); | |
} | |
} | |
} | |
} | |
class ScopedCOMInitializer final | |
{ | |
public: | |
ScopedCOMInitializer() | |
: hr_(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)) | |
{ | |
} | |
~ScopedCOMInitializer() | |
{ | |
if (Succeeded()) { | |
CoUninitialize(); | |
} | |
} | |
bool Succeeded() const | |
{ | |
return SUCCEEDED(hr_); | |
} | |
ScopedCOMInitializer(const ScopedCOMInitializer &) = delete; | |
ScopedCOMInitializer &operator=(const ScopedCOMInitializer &) = delete; | |
private: | |
HRESULT hr_; | |
}; | |
class ScopedBSTR final | |
{ | |
public: | |
ScopedBSTR() = default; | |
explicit ScopedBSTR(std::wstring_view wsv) | |
: bstr_(AllocateBSTROrDie(wsv)) | |
{ | |
} | |
// SysFreeString() handles null. | |
~ScopedBSTR() | |
{ | |
SysFreeString(bstr_); | |
} | |
BSTR Get() const | |
{ | |
return bstr_; | |
} | |
void Reset(BSTR bstr) | |
{ | |
if (bstr != bstr_) { | |
SysFreeString(bstr_); | |
bstr_ = bstr; | |
} | |
} | |
BSTR Release() | |
{ | |
BSTR bstr = bstr_; | |
bstr_ = nullptr; | |
return bstr_; | |
} | |
BSTR Allocate(std::wstring_view wsv) | |
{ | |
Reset(AllocateBSTROrDie(wsv)); | |
return bstr_; | |
} | |
BSTR AllocateBytes(size_t bytes) | |
{ | |
Reset(AllocateBSTRBytesOrDie(bytes)); | |
return bstr_; | |
} | |
BSTR *Receive() | |
{ | |
assert(bstr_ == nullptr); | |
return &bstr_; | |
} | |
size_t Length() const | |
{ | |
return SysStringLen(bstr_); | |
} | |
size_t ByteLength() const | |
{ | |
return SysStringByteLen(bstr_); | |
} | |
bool operator==(const ScopedBSTR &other) const = delete; | |
bool operator!=(const ScopedBSTR &other) const = delete; | |
private: | |
BSTR bstr_ = nullptr; | |
static UINT CheckedCastToUINT(size_t n) | |
{ | |
assert(n <= std::numeric_limits<UINT>::max()); // must define NOMINMAX | |
return static_cast<UINT>(n); | |
} | |
static BSTR AllocateBSTROrDie(std::wstring_view wsv) | |
{ | |
BSTR result = SysAllocStringLen(wsv.data(), CheckedCastToUINT(wsv.length())); | |
if (result == nullptr) { | |
abort(); | |
} | |
return result; | |
} | |
static BSTR AllocateBSTRBytesOrDie(size_t num_bytes) | |
{ | |
BSTR result = SysAllocStringByteLen(nullptr, CheckedCastToUINT(num_bytes)); | |
if (result == nullptr) { | |
abort(); | |
} | |
return result; | |
} | |
}; | |
int main() | |
{ | |
setlocale(LC_ALL, "en_US.UTF-8"); | |
ScopedCOMInitializer com_initializer; | |
if (!com_initializer.Succeeded()) | |
Die("com initialization failed"); | |
Microsoft::WRL::ComPtr<IWSCProductList> product_list; | |
{ | |
HRESULT hr = CoCreateInstance( | |
__uuidof(WSCProductList), | |
nullptr, | |
CLSCTX_INPROC_SERVER, | |
IID_PPV_ARGS(&product_list)); | |
if (FAILED(hr)) | |
Die("create instance failed"); | |
} | |
if (FAILED(product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS))) | |
Die("IWSCProductList::Initialize() failed"); | |
LONG product_count; | |
if (FAILED(product_list->get_Count(&product_count))) | |
Die("IWSCProductList::get_Count() failed"); | |
std::cout << "total " << product_count << "\n"; | |
for (LONG i = 0; i < product_count; i++) { | |
std::cout << "\n"; | |
std::cout << "-- item " << i << "\n"; | |
Microsoft::WRL::ComPtr<IWscProduct> product; | |
if (FAILED(product_list->get_Item(i, &product))) | |
Die("IWSCProductList::get_Item() failed"); | |
WSC_SECURITY_PRODUCT_STATE product_state; | |
if (FAILED(product->get_ProductState(&product_state))) | |
Die("IWSCProduct:get_ProductState() failed"); | |
std::cout << "product state: " << std::to_string(product_state) << "\n"; | |
std::wstring product_name; | |
{ | |
ScopedBSTR bstr; | |
if (FAILED(product->get_ProductName(bstr.Receive()))) | |
Die("IWSCProduct::get_ProductName() failed"); | |
product_name.assign(bstr.Get(), bstr.Length()); | |
} | |
std::wcout << "product name: " << product_name << "\n"; | |
std::wstring remediation_path; | |
{ | |
ScopedBSTR bstr; | |
if (FAILED(product->get_RemediationPath(bstr.Receive()))) | |
Die("IWSCProduct::get_RemediationPath() failed"); | |
remediation_path.assign(bstr.Get(), bstr.Length()); | |
} | |
std::wcout << "remediation path: " << remediation_path << "\n"; | |
} | |
std::cout << "\n"; | |
std::cout << "done\n"; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment