potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++&Win API NtQuerySystemInformation関数とSystemExtendedHandleInformation (0x40)でカーネルオブジェクトハンドルの一覧取得

公開された非公開関数NtQuerySystemInformationと非公開の定数SystemExtendedHandleInformation (0x40)を使うとカーネルオブジェクトハンドルの一覧を取得できます。固定長構造体のみなので前回投稿と比べれば遥かに扱いが楽です。なお、NtQuerySystemInformationSystemExtendedHandleInformationは非後方互換性が明記された関数と非公開定数の組み合わせです。今後のWindowsアップデート次第では使えなくなる可能性があります。

追記∶C++では一見正常に動作しましたが、C#へ移植したところ範囲外アクセスエラーが発生しました。NtQuerySystemInformationの2〜3回目の呼び出しの間にカーネルオブジェクトが増加したことが要因と思われます。適当なマージンを加えつつ、戻り値がメモリ不足情報(STATUS_INFO_LENGTH_MISMATCH)の場合は処理を繰り返しても良いかもしれません。

#include <bit>
#include <vector>
#include <ranges>
#include <format>
#include <iostream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")

// 参考
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/query.htm
static const auto SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)0x40;

// 参考
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_table_entry_ex.htm?tx=60&ts=0,313
typedef struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    PVOID Object;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR HandleValue;
    ULONG GrantedAccess;
    USHORT CreatorBackTraceIndex;
    USHORT ObjectTypeIndex;
    ULONG HandleAttributes;
    ULONG Reserved;
} *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;

// 参考
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_ex.htm?tx=60
typedef struct SYSTEM_HANDLE_INFORMATION_EX
{
    ULONG_PTR NumberOfHandles;
    ULONG_PTR Reserved;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
} *PSYSTEM_HANDLE_INFORMATION_EX;

static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleTableEntryInfosEx()
{
    // NtQuerySystemInformationはnullptrと0を渡すと56(64ビット環境)、
    // サイズ56のバッファーを渡すと実際に必要なサイズを返す。
    DWORD required;
    ::NtQuerySystemInformation(SystemExtendedHandleInformation, nullptr, 0, &required);

    std::vector<BYTE> buffer(required);
    ::NtQuerySystemInformation(SystemExtendedHandleInformation, buffer.data(), required, &required);

    buffer.resize(required);
    if (!NT_SUCCESS(::NtQuerySystemInformation(SystemExtendedHandleInformation, buffer.data(), required, nullptr)))
        throw std::exception();

    auto pinfos = std::bit_cast<PSYSTEM_HANDLE_INFORMATION_EX>(buffer.data());
    return std::vector(pinfos->Handles, pinfos->Handles + pinfos->NumberOfHandles);
}

int wmain()
{
    auto entries = GetSystemHandleTableEntryInfosEx();
    for (const auto& [i, entry] : entries | std::views::take(1000) | std::views::enumerate)
    {
        std::wcout << std::format(L"{}: {} ({})\n", i, entry.Object, entry.ObjectTypeIndex);
    }
    if (entries.size() > 1000)
        std::wcout << L"...\n";

    return 0;
}

参考