当前进程句柄检测仅返回自身句柄,求代码修复方案
问题分析与解决方案
你的核心问题在于仅枚举了当前进程自身的句柄,而ProcessExplorer这类工具的原理是遍历系统中所有进程,再检查每个进程持有的句柄是否指向目标进程。以下是具体修改方案:
关键思路
- 启用Debug权限:必须正确启用
SeDebugPrivilege,否则无法访问系统进程(如csrss、lsass)的句柄。 - 遍历系统所有句柄:通过Native API
NtQuerySystemInformation的SystemHandleInformation类,获取系统中所有打开的句柄信息。 - 筛选目标句柄:对每个句柄,复制到当前进程后查询其对象类型,判断是否为进程句柄,再验证其指向的进程ID是否为目标进程。
修改后的完整代码
#include <windows.h> #include <winternl.h> #include <stdio.h> #include <malloc.h> #include <wchar.h> // Native API 结构体定义 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; typedef struct _OBJECT_TYPE_INFORMATION { UNICODE_STRING Name; ULONG TotalNumberOfObjects; ULONG TotalNumberOfHandles; ULONG TotalPagedPoolUsage; ULONG TotalNonPagedPoolUsage; ULONG TotalNamePoolUsage; ULONG TotalHandleTableUsage; ULONG HighWaterNumberOfObjects; ULONG HighWaterNumberOfHandles; ULONG HighWaterPagedPoolUsage; ULONG HighWaterNonPagedPoolUsage; ULONG HighWaterNamePoolUsage; ULONG HighWaterHandleTableUsage; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccess; BOOLEAN SecurityRequired; BOOLEAN MaintainHandleCount; USHORT MaintainTypeList; POOL_TYPE PoolType; ULONG PagedPoolUsage; ULONG NonPagedPoolUsage; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; // Native API 函数指针 typedef NTSTATUS(WINAPI* PNtQuerySystemInformation)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); typedef NTSTATUS(WINAPI* PNtQueryObject)( HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength ); // 启用SeDebugPrivilege权限 BOOL EnableDebugPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; } if (!LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &luid)) { CloseHandle(hToken); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); BOOL result = (GetLastError() == ERROR_SUCCESS); CloseHandle(hToken); return result; } // 枚举所有指向目标进程的句柄 void EnumTargetProcessHandles(DWORD targetPid) { PNtQuerySystemInformation NtQuerySystemInformation = (PNtQuerySystemInformation)GetProcAddress( GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation"); PNtQueryObject NtQueryObject = (PNtQueryObject)GetProcAddress( GetModuleHandleW(L"ntdll.dll"), "NtQueryObject"); if (!NtQuerySystemInformation || !NtQueryObject) { printf("Failed to load native APIs\n"); return; } // 动态分配缓冲区以容纳所有句柄信息 ULONG bufferSize = 0x10000; PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(bufferSize); NTSTATUS status; while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, bufferSize, &bufferSize)) == STATUS_INFO_LENGTH_MISMATCH) { free(handleInfo); handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(bufferSize); } if (!NT_SUCCESS(status)) { printf("NtQuerySystemInformation failed: 0x%X\n", status); free(handleInfo); return; } printf("Found %lu handles pointing to PID %lu:\n", handleInfo->NumberOfHandles, targetPid); for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) { SYSTEM_HANDLE_TABLE_ENTRY_INFO& entry = handleInfo->Handles[i]; // 打开句柄所属的进程 HANDLE hOwnerProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, entry.UniqueProcessId); if (!hOwnerProcess) continue; // 将句柄复制到当前进程,否则无法查询其信息 HANDLE hDupHandle = NULL; if (!DuplicateHandle(hOwnerProcess, (HANDLE)entry.HandleValue, GetCurrentProcess(), &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { CloseHandle(hOwnerProcess); continue; } // 查询句柄对应的对象类型 ULONG objInfoSize = 0x1000; POBJECT_TYPE_INFORMATION objTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(objInfoSize); status = NtQueryObject(hDupHandle, ObjectTypeInformation, objTypeInfo, objInfoSize, &objInfoSize); if (NT_SUCCESS(status) && wcscmp(objTypeInfo->Name.Buffer, L"Process") == 0) { // 验证该进程句柄是否指向目标进程 DWORD pid = GetProcessId(hDupHandle); if (pid == targetPid) { printf("Process PID %d holds handle 0x%X to target process\n", entry.UniqueProcessId, entry.HandleValue); } } free(objTypeInfo); CloseHandle(hDupHandle); CloseHandle(hOwnerProcess); } free(handleInfo); } int main() { if (!EnableDebugPrivilege()) { printf("Failed to enable debug privilege\n"); return 1; } DWORD targetPid = GetCurrentProcessId(); EnumTargetProcessHandles(targetPid); return 0; }
核心修改点说明
- Native API 使用:
NtQuerySystemInformation是获取系统全局句柄的唯一途径,Win32 API没有提供直接接口。 - 句柄复制:其他进程的句柄无法被当前进程直接访问,必须通过
DuplicateHandle复制到当前进程空间后才能查询其对象信息。 - 权限处理:
EnableDebugPrivilege函数确保当前进程拥有访问系统进程的权限,这是枚举csrss、lsass等进程句柄的必要条件。 - 动态缓冲区:系统中句柄数量可能很大,初始缓冲区不足时需要重新分配,避免枚举不完整。
内容的提问来源于stack exchange,提问作者nok util
相关产品推荐
相关产品推荐

