potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20&Win API&WIL AMSIプロバイダーのCLSIDと表示名を取得する。

WindowsのAMSI(マルウェア対策スキャンインターフェイス)プロバイダーのCLSIDと表示名を取得するコードです。AMSIについてはMicrosoftの公式ドキュメントを参照ください。

#include <string>
#include <vector>

#define STRICT
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <amsi.h>

#include "wil/com.h"

// レジストリキーのサブキーの名前をすべて取得します。
// 取得中にエラーが発生した場合は無視します。
std::vector<std::wstring> GetRegistrySubKeyNames(HKEY hkey)
{
    DWORD subKeyCount, maxSubKeyLen;
    DWORD err = ::RegQueryInfoKeyW(hkey, nullptr, nullptr, nullptr,
        &subKeyCount, &maxSubKeyLen, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
    if (err != ERROR_SUCCESS)
        return {};

    std::vector<std::wstring> names;
    std::wstring buffer(maxSubKeyLen, L'\0');
    for (DWORD i = 0; i < subKeyCount; i++)
    {
        DWORD size = maxSubKeyLen + 1;
        err = ::RegEnumKeyExW(hkey, i, buffer.data(), &size, nullptr, nullptr, nullptr, nullptr);
        if (err != ERROR_SUCCESS)
            continue;
        names.emplace_back(buffer.data(), size);
    }
    return names;
}

// CLSIDを{...}形式の文字列に変換します。
std::wstring CLSIDToStlWString(const CLSID& clsid)
{
    std::wstring s(38, L'\0');
    ::StringFromGUID2(clsid, s.data(), 39);
    return s;
}

// AMSIプロバイダーのCLSIDが登録されたレジストリキーを開きます。
wil::unique_hkey OpenAMSIProviderRegistryKey(DWORD options = 0, DWORD desired = KEY_READ)
{
    wil::unique_hkey hkey;
    DWORD err = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\AMSI\\Providers", options, desired, hkey.put());
    return hkey;
}

// レジストリに登録されたAMSIプロバイダーのCLSIDをすべて取得します。
std::vector<CLSID> GetAMSIProviderCLSIDs(DWORD regOptions = 0, DWORD regDesired = 0)
{
    auto hkey = OpenAMSIProviderRegistryKey(regOptions, KEY_READ | regDesired);
    auto clsidStrs = GetRegistrySubKeyNames(hkey.get());

    std::vector<GUID> clsids;
    clsids.reserve(clsidStrs.size());
    for (size_t i = 0, l = clsidStrs.size(); i < l; i++)
    {
        CLSID clsid;
        if (::CLSIDFromString(clsidStrs[i].data(), &clsid) != NOERROR)
            continue;
        clsids.emplace_back(clsid);
    }
    clsids.shrink_to_fit();
    return clsids;
}

#include <format>
#include <iostream>

// 登録されたAMSIプロバイダーのCLSIDと表示名を表示します。
int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto coinit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED);

    for (auto amsiProviderCLSID : GetAMSIProviderCLSIDs())
    {
        auto antimalwareProvider = wil::CoCreateInstance<IAntimalwareProvider>(
            amsiProviderCLSID, CLSCTX_INPROC_SERVER);

        // 書式化したCLSIDと表示名の取得
        auto clsidStr = CLSIDToStlWString(amsiProviderCLSID);
        wil::unique_cotaskmem_string displayName;
        antimalwareProvider->DisplayName(displayName.put());

        std::wcout
            << std::format(L"{}: {}",
                clsidStr.data(),
                wil::string_get_not_null(displayName))
            << std::endl;
    }

    return 0;
}