枚举所有句柄的方法
由于windows并没有给出枚举所有句柄所用到的API,和进程所拥有的句柄相关的只有GetProcessHandleCount这个函数,然而这个函数只能获取到和进程相关的句柄数,不能获取到实际的句柄,要获得句柄,我们必须使用未公开的Native API才可以。
PS:网上有很多关于这类的方法,但几乎都是抄来抄去,很多连编译都过不了就直接放上去了(囧)。我整理了一下方法,实测在win10和win7都可以用。
NTSTATUS WINAPI NtQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
枚举的关键是使用NtQuerySystemInformation(旧版本是ZwQuerySystemInformation)这个方法
但MSDN上并没有把这个函数的第一个参数的枚举值给全,而且Windows API所定义的枚举值也是MSDN上面列的几个而已,如下:
typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45, SystemPolicyInformation = 134, } SYSTEM_INFORMATION_CLASS;
这几个值的定义在MSDN上都有,具体可以在上面给的链接的文档查到,这里就不列了。这些枚举值都是可以拿来指定NtQuerySystemInformation所查询的内容
然而所给到的枚举值并没有我们想要的枚举句柄的功能,这个时候只能求助于强大的搜索引擎了,我们可以查到比较完整的SYSTEM_INFORMATION_CLASS的定义是下面的列表
typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, SystemPathInformation, SystemProcessInformation, SystemCallCountInformation, SystemDeviceInformation, SystemProcessorPerformanceInformation, SystemFlagsInformation, SystemCallTimeInformation, SystemModuleInformation, SystemLocksInformation, SystemStackTraceInformation, SystemPagedPoolInformation, SystemNonPagedPoolInformation, SystemHandleInformation, SystemObjectInformation, SystemPageFileInformation, SystemVdmInstemulInformation, SystemVdmBopInformation, SystemFileCacheInformation, SystemPoolTagInformation, SystemInterruptInformation, SystemDpcBehaviorInformation, SystemFullMemoryInformation, SystemLoadGdiDriverInformation, SystemUnloadGdiDriverInformation, SystemTimeAdjustmentInformation, SystemSummaryMemoryInformation, SystemMirrorMemoryInformation, SystemPerformanceTraceInformation, SystemObsolete0, SystemExceptionInformation, SystemCrashDumpStateInformation, SystemKernelDebuggerInformation, SystemContextSwitchInformation, SystemRegistryQuotaInformation, SystemExtendServiceTableInformation, SystemPrioritySeperation, SystemVerifierAddDriverInformation, SystemVerifierRemoveDriverInformation, SystemProcessorIdleInformation, SystemLegacyDriverInformation, SystemCurrentTimeZoneInformation, SystemLookasideInformation, SystemTimeSlipNotification, SystemSessionCreate, SystemSessionDetach, SystemSessionInformation, SystemRangeStartInformation, SystemVerifierInformation, SystemVerifierThunkExtend, SystemSessionProcessInformation, SystemLoadGdiDriverInSystemSpace, SystemNumaProcessorMap, SystemPrefetcherInformation, SystemExtendedProcessInformation, SystemRecommendedSharedDataAlignment, SystemComPlusPackage, SystemNumaAvailableMemory, SystemProcessorPowerInformation, SystemEmulationBasicInformation, SystemEmulationProcessorInformation, SystemExtendedHandleInformation, SystemLostDelayedWriteInformation, SystemBigPoolInformation, SystemSessionPoolTagInformation, SystemSessionMappedViewInformation, SystemHotpatchInformation, SystemObjectSecurityMode, SystemWatchdogTimerHandler, SystemWatchdogTimerInformation, SystemLogicalProcessorInformation, SystemWow64SharedInformation, SystemRegisterFirmwareTableInformationHandler, SystemFirmwareTableInformation, SystemModuleInformationEx, SystemVerifierTriageInformation, SystemSuperfetchInformation, SystemMemoryListInformation, SystemFileCacheInformationEx, MaxSystemInfoClass // MaxSystemInfoClass should always be the last enum } SYSTEM_INFORMATION_CLASS;
注意看到第17项(枚举值16)的SystemHandleInformation,这就是我们想要的东西,通过这个值传入NtQuerySystemInformation,然后在NtQuerySystemInformation的函数的第二项放入缓存空间长度,第三项放入缓存空间的指针即可。
现在我们要确定缓存区的类型了。如果我们传入SystemHandleInformation,那么NtQuerySystemInformation将会返回下面这个结构
typedef struct _SYSTEM_HANDLE_INFORMATION_EX { ULONG NumberOfHandles; SYSTEM_HANDLE_INFORMATION Information[655360];//注意655360这个值是我自己定义的,你们可以自己定义其他的常量值 }SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
其中,SYSTEM_HANDLE_INFORMATION的定义是:
typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG ProcessId;//进程标识符 UCHAR ObjectTypeNumber;//打开的对象的类型 UCHAR Flags;//句柄属性标志 USHORT Handle;//句柄数值,在进程打开的句柄中唯一标识某个句柄 PVOID Object;//这个就是句柄对应的EPROCESS的地址 ACCESS_MASK GrantedAccess;//句柄对象的访问权限 }SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
注意到这里有个很奇怪的地方,就是Handle值的类型是USHORT的,然而HANDLE的定义是void *,在32位上是4个字节,在64位上是8个字节。这就告诉我们,我们只能枚举到一个进程的0~65535个句柄值, 但是我们从Windows Internals这本书里面可以知道,现在Windows每个进程的句柄值已经可以到16000000以上了(虽然不会用到那么多)。
这样我们就可以根据ProcessId来获取我们感兴趣的进程的句柄值了,但是这里也有一个问题,虽然ProcessId的类型是ULONG,但是也只能枚举到16位以下的ProcessId的值,也就是枚举到的最大ProcessId值是65535,如果我们的程序的ProcessId大于65535,我们就不能枚举到这些进程所创建的句柄了。(这就是Undocument的函数不可靠的体现了)。
ObjectTypeNumber是我们想要的句柄值的类型,但是我暂时还无法找到关于这个值的详细资料,只是在windows driver里面查到了这个东西ObReferenceObjectByHandle,但是我现在想在ring3层把问题解决,暂时先不考虑驱动。事实上我们可以在Process Explorer里面根据把句柄值一个一个对应然后找到相应类型,比如下图:
我们可以看到获知句柄值为0x4时,对应的值是Type是Key
我们再来看下我们取到的Info的ObjectType是7,也就是说,我们的Key的类型值就是7
同理我们也可以找到File类型的ObjectType是35
我无法确定这个值是否会跟着系统不同而改变,如果要使用这个方法,请一定要先测试一下。
接下来的事情就很好办了,我们只要把NtQuerySystemInformation从NtDll.dll里面拉出来就可以了,我们可以写出这样的代码:
//头文件引#include <winternl.h> #define SystemHandleInformation 0x10 typedef DWORD(WINAPI *NTQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD); HMODULE hNtDll = LoadLibrary(L"ntdll.dll"); NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, "NtQuerySystemInformation"); ULONG cbBuffer = sizeof(SYSTEM_HANDLE_INFORMATION_EX); LPVOID pBuffer = (LPVOID)malloc(cbBuffer); if (pBuffer) { NtQuerySystemInformation(SystemHandleInformation, pBuffer, cbBuffer, NULL); PSYSTEM_HANDLE_INFORMATION_EX pInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer; for (ULONG r = 0; r < pInfo->NumberOfHandles; r++) { //dosomething } free(pBuffer); } FreeModule(hNtDll);
Native API其他用法举例:查找文件句柄文件名的方法
有时候我们想查找句柄的所对应的信息,比如我之前就遇到了一个需要查找文件句柄所对应文件名的方法,这个时候也需要Native API来完成,需要用到ZwQueryInformationFile这个方法
NTSTATUS ZwQueryInformationFile( _In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock, _Out_ PVOID FileInformation, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass );
其中第一个参数是我们要查询的文件句柄,信息可以存放在FileInformation指向的内存中,这个内存的长度由Length指定。
现在我们来看下第二个参数IoStatusBlock的类型IO_STATUS_BLOCK
typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; PVOID Pointer; }; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
Pointer是个保留项,第一项就是指定IRP‘s I/O的返回值,这个值我们可以不用
最后一个参数是FILE_INFORMATION_CLASS类型的一个数,这个数可以拿来指定ZwQueryInformationFile所查询的内容(就像NtQuerySystemInformation的那一堆枚举值也是用来指定NtQuerySystemInformation所查询的内容一样),这个值在MSDN有完整的定义,但在VS自带的SDK里面却没有。
typedef enum _FILE_INFORMATION_CLASS { FileDirectoryInformation = 1, FileFullDirectoryInformation, FileBothDirectoryInformation, FileBasicInformation, FileStandardInformation, FileInternalInformation, FileEaInformation, FileAccessInformation, FileNameInformation, FileRenameInformation, FileLinkInformation, FileNamesInformation, FileDispositionInformation, FilePositionInformation, FileFullEaInformation, FileModeInformation, FileAlignmentInformation, FileAllInformation, FileAllocationInformation, FileEndOfFileInformation, FileAlternateNameInformation, FileStreamInformation, FilePipeInformation, FilePipeLocalInformation, FilePipeRemoteInformation, FileMailslotQueryInformation, FileMailslotSetInformation, FileCompressionInformation, FileObjectIdInformation, FileCompletionInformation, FileMoveClusterInformation, FileQuotaInformation, FileReparsePointInformation, FileNetworkOpenInformation, FileAttributeTagInformation, FileTrackingInformation, FileIdBothDirectoryInformation, FileIdFullDirectoryInformation, FileValidDataLengthInformation, FileShortNameInformation, FileIoCompletionNotificationInformation, FileIoStatusBlockRangeInformation, FileIoPriorityHintInformation, FileSfioReserveInformation, FileSfioVolumeInformation, FileHardLinkInformation, FileProcessIdsUsingFileInformation, FileNormalizedNameInformation, FileNetworkPhysicalNameInformation, FileIdGlobalTxDirectoryInformation, FileIsRemoteDeviceInformation, FileUnusedInformation, FileNumaNodeInformation, FileStandardLinkInformation, FileRemoteProtocolInformation, FileRenameInformationBypassAccessCheck, FileLinkInformationBypassAccessCheck, FileVolumeNameInformation, FileIdInformation, FileIdExtdDirectoryInformation, FileReplaceCompletionInformation, FileHardLinkFullIdInformation, FileIdExtdBothDirectoryInformation, FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
在我们的例子中,我们需要用到的枚举值是FileNameInformation(枚举值9)
具体使用也像 一样,先从ntdll.dll里面导出来,然后把参数往里面填就好了,在这里我先定义一个缓冲区:
typedef struct _NM_INFO { HANDLE hFile; FILE_NAME_INFORMATION Info; } NM_INFO, *PNM_INFO;
FILE_NAME_INFORMATION结构:
typedef struct _FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[256];//256我自己定的,可以改成其他的,只要够放位置就行 } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
调用API:
NM_INFO nmInfo = { 0 }; nmInfo.hFile = hFile; PNM_INFO NmInfo = (PNM_INFO)lpParameter; IO_STATUS_BLOCK IoStatus; ZWQUERYINFORMATIONFILE ZwQueryInformationFile = (ZWQUERYINFORMATIONFILE)GetProcAddress(hNtDll, "ZwQueryInformationFile"); ZwQueryInformationFile(NmInfo->hFile, &IoStatus, &NmInfo->Info, 256, FILE_INFORMATION_CLASS::FileNameInformation);
注意查询出来的FileNameLength是字节数,而不是WCHAR数组的最大偏移值,在这里WCHAR的最大偏移值应该是512,因为WCHAR是两个字节的
例子:找出所有文件句柄,并且把对应文件名含有ABC的句柄关掉
注意我把FILE_INFORMATION_CLASS改了个名字,改成了RFILE_INFORMATION_CLASS,这是因为SDK里面已经有了一个FILE_INFORMATION_CLASS了,而且我还把第一个枚举值给改了个名字,如果用以前的那个不知道为啥VS不给编译通过。
#include <afx.h> #include <winternl.h> typedef DWORD(WINAPI *NTQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD); typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG ProcessId; UCHAR ObjectTypeNumber; UCHAR Flags; USHORT Handle; PVOID Object; ACCESS_MASK GrantedAccess; }SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; #define STATUS_INFO_LENGTH_MISMATCH 0x004 typedef struct _SYSTEM_HANDLE_INFORMATION_EX { ULONG NumberOfHandles; SYSTEM_HANDLE_INFORMATION Information[165536]; }SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; #define SystemHandleInformation 0x10 // 16 typedef struct _FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[256]; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; typedef struct _NM_INFO { HANDLE hFile; FILE_NAME_INFORMATION Info; } NM_INFO, *PNM_INFO; typedef enum _RFILE_INFORMATION_CLASS { FileDirectoryInformation1 = 1, FileFullDirectoryInformation, FileBothDirectoryInformation, FileBasicInformation, FileStandardInformation, FileInternalInformation, FileEaInformation, FileAccessInformation, FileNameInformation, FileRenameInformation, FileLinkInformation, FileNamesInformation, FileDispositionInformation, FilePositionInformation, FileFullEaInformation, FileModeInformation, FileAlignmentInformation, FileAllInformation, FileAllocationInformation, FileEndOfFileInformation, FileAlternateNameInformation, FileStreamInformation, FilePipeInformation, FilePipeLocalInformation, FilePipeRemoteInformation, FileMailslotQueryInformation, FileMailslotSetInformation, FileCompressionInformation, FileObjectIdInformation, FileCompletionInformation, FileMoveClusterInformation, FileQuotaInformation, FileReparsePointInformation, FileNetworkOpenInformation, FileAttributeTagInformation, FileTrackingInformation, FileIdBothDirectoryInformation, FileIdFullDirectoryInformation, FileValidDataLengthInformation, FileShortNameInformation, FileIoCompletionNotificationInformation, FileIoStatusBlockRangeInformation, FileIoPriorityHintInformation, FileSfioReserveInformation, FileSfioVolumeInformation, FileHardLinkInformation, FileProcessIdsUsingFileInformation, FileNormalizedNameInformation, FileNetworkPhysicalNameInformation, FileIdGlobalTxDirectoryInformation, FileIsRemoteDeviceInformation, FileUnusedInformation, FileNumaNodeInformation, FileStandardLinkInformation, FileRemoteProtocolInformation, FileRenameInformationBypassAccessCheck, FileLinkInformationBypassAccessCheck, FileVolumeNameInformation, FileIdInformation, FileIdExtdDirectoryInformation, FileReplaceCompletionInformation, FileHardLinkFullIdInformation, FileIdExtdBothDirectoryInformation, FileMaximumInformation } RFILE_INFORMATION_CLASS, *PRFILE_INFORMATION_CLASS; typedef NTSTATUS(WINAPI *ZWQUERYINFORMATIONFILE)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, RFILE_INFORMATION_CLASS); CString GetFileName(HMODULE hNtDll, PNM_INFO lpParameter) { PNM_INFO NmInfo = (PNM_INFO)lpParameter; IO_STATUS_BLOCK IoStatus; ZWQUERYINFORMATIONFILE ZwQueryInformationFile = (ZWQUERYINFORMATIONFILE)GetProcAddress(hNtDll, "ZwQueryInformationFile"); ZwQueryInformationFile(NmInfo->hFile, &IoStatus, &NmInfo->Info, 256, RFILE_INFORMATION_CLASS::FileNameInformation); if (NmInfo->Info.FileNameLength != 0) { CString str; str.Append(NmInfo->Info.FileName, NmInfo->Info.FileNameLength / sizeof(WCHAR)); return str; } return CString(); } extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int nShowCmd) { HMODULE hNtDll = LoadLibrary(L"ntdll.dll"); NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, "ZwQuerySystemInformation"); ULONG cbBuffer = sizeof(SYSTEM_HANDLE_INFORMATION_EX); LPVOID pBuffer = (LPVOID)malloc(cbBuffer); auto id= GetCurrentProcessId(); if (pBuffer) { NtQuerySystemInformation(SystemHandleInformation, pBuffer, cbBuffer, NULL); PSYSTEM_HANDLE_INFORMATION_EX pInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer; for (ULONG r = 0; r < pInfo->NumberOfHandles; r++) { if (pInfo->Information[r].ObjectTypeNumber == 35) { NM_INFO nmInfo = { 0 }; nmInfo.hFile = (HANDLE)pInfo->Information[r].Handle; CString fileName = GetFileName(hNtDll, &nmInfo); if (!fileName.IsEmpty()) { if (fileName.Find(L"ABC") != -1) { CloseHandle(nmInfo.hFile); } } } } free(pBuffer); } FreeModule(hNtDll); return 0; }