发布于2026-07-06 阅读(0)
扫一扫,手机访问
很多人以为直接用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。
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(size);
status = NtQuerySystemInformation(SystemExtendedHandleInformation, info.get(), size, &size);
if (NT_SUCCESS(status)) {
auto p = reinterpret_cast(info.get());
for (ULONG i = 0; i < p->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);
}
}
}
}
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。很多代码在开发机上跑得飞起,一上线就翻车,问题几乎全出在权限和版本适配这些细节上。
SeDebugPrivilege,得在组策略里把调试程序权限启用,或者用 RunAs 启动(但生产环境不推荐这么做)。SystemExtendedHandleInformation 在 Windows 7 上根本不存在,要向下兼容只能退回到 SystemHandleInformation,但返回的结构里没有 ObjectTypeIndex,只能靠对象地址低字节猜类型,准确率不到 70%。STATUS_INVALID_HANDLE),必须确保架构一致,或者折腾 WOW64 兼容层加 NtWow64QueryInformationProcess64,复杂得不行,通常直接要求同架构。NtQuerySystemInformation,返回截断数据或者伪造句柄,这时候就得结合 Toolhelp32 和内存扫描交叉验证。真正的稳定句柄类型统计,从来不是单次 API 调用就能搞定的——它牵涉权限控制、版本适配、错误容忍和多源验证。别指望写一个 for 循环就能输出完美报表。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8