You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows平台下如何用C++列出进程使用的所有句柄?

用C++枚举Windows进程句柄的实现方案

要实现类似Sysinternals Handle.exe或Process Explorer的进程句柄枚举功能,核心是调用未公开的NtQuerySystemInformation API——这也是Sysinternals工具的底层实现方式。下面是具体的实现步骤和代码示例:

1. 必备的结构体与类型定义

NtQuerySystemInformation属于Windows内核未公开API,需要自行定义所需的结构体和枚举值:

#include <windows.h>
#include <winternl.h>
#include <psapi.h>
#include <vector>
#include <iostream>

// 系统信息类枚举,此处用到SystemHandleInformation
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;

// 单个句柄的详细信息结构体
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
    USHORT UniqueProcessId;
    USHORT CreatorBackTraceIndex;
    UCHAR ObjectTypeIndex;
    UCHAR HandleAttributes;
    USHORT HandleValue;
    PVOID Object;
    ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

// 系统句柄信息的顶层结构体
typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

// 声明NtQuerySystemInformation函数指针
typedef NTSTATUS(WINAPI* PNtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);

2. 提升调试权限(关键步骤)

要枚举其他进程的句柄,必须启用SeDebugPrivilege权限:

BOOL EnableDebugPrivilege() {
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        return FALSE;
    }

    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
    CloseHandle(hToken);

    return (GetLastError() == ERROR_SUCCESS);
}

3. 枚举句柄的核心逻辑

void EnumProcessHandles() {
    if (!EnableDebugPrivilege()) {
        std::cerr << "Failed to enable debug privilege" << std::endl;
        return;
    }

    // 加载ntdll.dll并获取NtQuerySystemInformation函数地址
    HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
    if (!hNtdll) {
        std::cerr << "Failed to load ntdll.dll" << std::endl;
        return;
    }

    PNtQuerySystemInformation NtQuerySystemInformation = 
        reinterpret_cast<PNtQuerySystemInformation>(GetProcAddress(hNtdll, "NtQuerySystemInformation"));
    if (!NtQuerySystemInformation) {
        std::cerr << "Failed to get NtQuerySystemInformation address" << std::endl;
        return;
    }

    ULONG bufferSize = 0x10000; // 初始缓冲区大小
    std::vector<BYTE> buffer(bufferSize);
    NTSTATUS status;

    // 第一次调用获取所需的缓冲区大小
    status = NtQuerySystemInformation(SystemHandleInformation, buffer.data(), bufferSize, &bufferSize);
    if (status == STATUS_INFO_LENGTH_MISMATCH) {
        buffer.resize(bufferSize);
        status = NtQuerySystemInformation(SystemHandleInformation, buffer.data(), bufferSize, &bufferSize);
    }

    if (!NT_SUCCESS(status)) {
        std::cerr << "NtQuerySystemInformation failed with status: 0x" << std::hex << status << std::endl;
        return;
    }

    PSYSTEM_HANDLE_INFORMATION handleInfo = reinterpret_cast<PSYSTEM_HANDLE_INFORMATION>(buffer.data());
    for (ULONG i = 0; i < handleInfo->NumberOfHandles; ++i) {
        SYSTEM_HANDLE_TABLE_ENTRY_INFO& handle = handleInfo->Handles[i];
        // 可选:过滤当前进程的句柄
        if (handle.UniqueProcessId == GetCurrentProcessId()) {
            continue;
        }

        // 打开目标进程,需要PROCESS_DUP_HANDLE权限
        HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, handle.UniqueProcessId);
        if (!hProcess) {
            continue;
        }

        // 复制句柄到当前进程,以便查询对象详情
        HANDLE hDupHandle;
        if (DuplicateHandle(hProcess, reinterpret_cast<HANDLE>(handle.HandleValue),
            GetCurrentProcess(), &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
            
            // 示例:查询文件句柄对应的路径
            WCHAR fileName[MAX_PATH] = {0};
            if (GetFinalPathNameByHandle(hDupHandle, fileName, MAX_PATH, VOLUME_NAME_DOS)) {
                std::wcout << L"PID: " << handle.UniqueProcessId 
                           << L", Handle: 0x" << std::hex << handle.HandleValue
                           << L", Path: " << fileName << std::endl;
            }

            CloseHandle(hDupHandle);
        }

        CloseHandle(hProcess);
    }
}

4. 主函数调用示例

int main() {
    EnumProcessHandles();
    return 0;
}

关键说明

  • NtQuerySystemInformation返回的SYSTEM_HANDLE_INFORMATION包含系统中所有进程的句柄列表,遍历即可获取每个句柄的基本属性(所属PID、句柄值、权限等)。
  • 复制句柄是为了在当前进程中调用GetFinalPathNameByHandle等API查询对象详情,若仅需句柄基础属性,可跳过复制步骤。
  • 若未启用SeDebugPrivilege,仅能枚举当前进程的句柄,无法访问其他进程的句柄。

内容的提问来源于stack exchange,提问作者anitarazafi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:22:44