无法直接用enumhandleinformation获取进程句柄,因该接口仅限驱动或高权限系统工具调用;用户态程序应使用ntquerysysteminformation配合systemextendedhandleinformation(win10 1607+)或systemhandleinformation(兼容旧版),并确保启用sedebugprivilege权限及同架构运行。

如何用 EnumHandleInformation 获取进程所有句柄(Windows)
Windows 没有公开的 API 直接返回“句柄类型名”,EnumHandleInformation 是 Windows 10 1809+ 新增的内核态接口,但仅限驱动或高权限系统工具调用;普通用户态程序无法直接使用。实际开发中,必须走 NtQuerySystemInformation + SystemHandleInformation(已废弃但仍可用)或更稳定的 SystemExtendedHandleInformation(Win10 1607+)。
关键点:你需要以 SeDebugPrivilege 权限打开目标进程,并确保调用线程拥有调试权限——否则 NtQuerySystemInformation 返回 STATUS_ACCESS_DENIED 或只返回部分句柄。
- 必须用
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, pid)打开目标进程 - 调用前需通过
AdjustTokenPrivileges启用SE_DEBUG_NAME -
SystemExtendedHandleInformation返回的每个SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX中,ObjectTypeIndex是类型索引,不是字符串;真实类型名需查系统全局对象类型表(ObGetObjectType不可用,得靠硬编码映射或读取ntdll!ObTypeIndexTable——但该符号未导出,稳定做法是用已知偏移+特征扫描,或直接依赖Psapi.dll的GetProcessImageFileName辅助判断
为什么 GetFileType 和 GetHandleInformation 不能用于句柄类型分类
这两个函数常被误用:GetFileType 只对文件/设备句柄有效,对事件、互斥体、节(section)、注册表键等返回 FILE_TYPE_UNKNOWN;GetHandleInformation 只返回标志位(如 HANDLE_FLAG_INHERIT),不包含类型信息。
典型错误现象:遍历句柄后对每个句柄调用 GetFileType,结果 80% 句柄都返回 FILE_TYPE_UNKNOWN,误以为“不是文件”,其实它可能是 Event 或 Section。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
GetFileType本质是向对象发送IRP_MJ_QUERY_INFORMATION,仅对FileObject类型响应,其余多数对象直接失败 -
GetHandleInformation读的是句柄表项的Flags字段,和对象类型完全无关 - 唯一能间接推断类型的轻量方式:用
DuplicateHandle尝试复制为EVENT_ALL_ACCESS/MUTANT_ALL_ACCESS等,根据GetLastError()判断——但开销大、易触发安全软件拦截,仅适合离线分析
实战:用 NtQuerySystemInformation 枚举并映射句柄类型(C++ 示例核心逻辑)
下面这段逻辑可嵌入任意 Win32 C++ 工程(需链接 ntdll.lib,头文件自行声明 NtQuerySystemInformation):
// 假设已获取 target_pid 和 hProcess
ULONG size = 0;
NTSTATUS status = NtQuerySystemInformation(SystemExtendedHandleInformation, nullptr, 0, &size);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
auto info = std::make_unique<byte>(size);
status = NtQuerySystemInformation(SystemExtendedHandleInformation, info.get(), size, &size);
if (NT_SUCCESS(status)) {
auto p = reinterpret_cast<psystem_handle_information_ex>(info.get());
for (ULONG i = 0; i NumberOfHandles; ++i) {
auto h = &p->Handles[i];
if (h->UniqueProcessId == target_pid) {
// h->ObjectTypeIndex 是关键:查表得类型名
const char* type_name = GetTypeNameByIndex(h->ObjectTypeIndex); // 自定义查表函数
printf("Handle: %p, Type: %s, Attributes: %x\n",
(void*)h->HandleValue, type_name, h->HandleAttributes);
}
}
}
}
</psystem_handle_information_ex></byte>
GetTypeNameByIndex 需维护一个静态数组,对应 Windows 内核中 ObTypeIndexTable 的常见索引(如 0x19=Event, 0x1A=Mutant, 0x1E=Section, 0x20=Key)。这个表在不同 Windows 版本间有微小变动,建议从 ntoskrnl.exe 符号或 WinDbg 中提取最新版,而非硬编码全部 100+ 项。
- 务必检查
h->HandleValue是否为合法值(非 0、非 0xffffffff) -
h->HandleAttributes的HANDLE_FLAG_PROTECT_FROM_CLOSE位可用于识别受保护句柄,避免误关 - 枚举结果不含句柄所指对象的名称(如文件路径、事件名),要获取名称需额外调用
NtQueryObject,且成功率取决于对象是否支持ObjectNameInformation
容易被忽略的兼容性与权限陷阱
很多代码在开发机上跑通,上线后失败,问题几乎全出在权限和版本适配上。
- Windows Server 2016+ 默认禁用
SeDebugPrivilege给非 SYSTEM 进程,需在组策略中启用“调试程序”权限,或改用RunAs启动(不推荐生产环境) -
SystemExtendedHandleInformation在 Windows 7 上不可用,若需向下兼容,必须 fallback 到SystemHandleInformation(返回结构无ObjectTypeIndex,只能靠Object地址低字节启发式猜测类型,准确率低于 70%) - 32 位程序在 64 位系统上枚举 64 位进程句柄会失败(
STATUS_INVALID_HANDLE),必须匹配架构,或改用WOW64兼容层 +NtWow64QueryInformationProcess64(极复杂,通常直接要求同架构) - 杀毒软件/EDR 常 Hook
NtQuerySystemInformation,返回截断数据或伪造句柄,此时应结合Toolhelp32(仅限模块/线程)和内存扫描(如搜索EPROCESS对象)交叉验证
真正稳定的句柄类型统计,从来不是单次 API 调用能搞定的事——它依赖权限控制、版本适配、错误容忍和多源验证。别指望一个 for 循环就输出完美报表。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










