目前正在做的项目进行性能测试,被通知进程句柄数已经到达2600多,心中甚是一惊,用Procexp查看句柄表得知,为类型为Key的句柄,名称为\Registry\HKLM\Microsoft\CTF\AssemblyItem....,基本上得知是输入法关联的注册表项,而后用ProcMon监视注册表项操作关联的线程栈,确认句柄为Sogou输入法组件打开,为什么句柄没有关闭,原因不得而知,本人并没有深入的研究,或许是Sogou的bug,或者我们的注册表过滤驱动导致。也没有时间去协调人员去检查,于是我打算在用户层枚举句柄,而后关闭这些句柄。经过Google,查到了一下方法
http://forum.sysinternals.com/howto-enumerate-handles_topic18892.html
于是本人写了个枚举句柄的类
#pragma once
#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2
#define STATUS_SUCCESS 0x00000000
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
typedef NTSTATUS (WINAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (WINAPI *_NtQueryObject)(
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING TypeName;
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 ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
class CProcHandles
{
public:
CProcHandles(void);
~CProcHandles(void);
public:
BOOL GetUndocumentedFunctionAddress();
BOOL QueryHandleInfomation(DWORD PID=-1);
private:
_NtQuerySystemInformation m_pfunNtQuerySystemInformation;
_NtQueryObject m_pfunNtQueryObject;
};
#include "StdAfx.h"
#include "ProcHandles.h"
#define ONEPAGESIZE 0x1000
CProcHandles::CProcHandles(void)
{
}
CProcHandles::~CProcHandles(void)
{
}
BOOL CProcHandles::GetUndocumentedFunctionAddress()
{
m_pfunNtQuerySystemInformation=(_NtQuerySystemInformation)GetProcAddress(
GetModuleHandle(_T("ntdll.dll")),
"NtQuerySystemInformation");
m_pfunNtQueryObject=(_NtQueryObject)GetProcAddress(
GetModuleHandle(_T("ntdll.dll")),
"NtQueryObject");
return (m_pfunNtQuerySystemInformation!=NULL && m_pfunNtQueryObject!=NULL);
}
BOOL CProcHandles::QueryHandleInfomation(DWORD PID)
{
PSYSTEM_HANDLE_INFORMATION pSysHandleInfo=NULL;
size_t HandleInfoSize=0x1000; //4K
NTSTATUS NtStatus;
pSysHandleInfo=(PSYSTEM_HANDLE_INFORMATION)malloc(HandleInfoSize);
do
{
NtStatus=m_pfunNtQuerySystemInformation(SystemHandleInformation,
pSysHandleInfo,HandleInfoSize,NULL);
if (NtStatus==STATUS_INFO_LENGTH_MISMATCH)
{
HandleInfoSize*=2;
pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(pSysHandleInfo, HandleInfoSize);
}
else
{
break;
}
} while (TRUE);
for (int i=0;i<pSysHandleInfo->HandleCount;i++)
{
PSYSTEM_HANDLE SystemHandle=&pSysHandleInfo->Handles[i];
if (SystemHandle->ProcessId==PID)
{
//HANDLE DuplicatedHandle = NULL;
//if(!DuplicateHandle(GetCurrentProcess(),
// (HANDLE)SystemHandle->Handle,
// GetCurrentProcess(),
// &DuplicatedHandle,0,FALSE,0))
//{
// CString strMsg;
// strMsg.Format(_T("DuplicateHandle failed. code=%x\n"),GetLastError());
// ::OutputDebugString((LPCTSTR)strMsg);
// continue;
//}
/* Query the object type. */
POBJECT_TYPE_INFORMATION objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(ONEPAGESIZE);
NtStatus = m_pfunNtQueryObject((HANDLE)SystemHandle->Handle,
ObjectTypeInformation,
objectTypeInfo,
ONEPAGESIZE,
NULL
);
if(NtStatus!=STATUS_SUCCESS)
{
CString strMsg;
strMsg.Format(_T("[%#x] Error!, NtQueryObject return %x"),SystemHandle->Handle, NtStatus);
//CloseHandle(DuplicatedHandle);
continue;
}
/* Query the object name (unless it has an access of
0x0012019f, on which NtQueryObject could hang. */
if (SystemHandle->GrantedAccess == 0x0012019F)
{
/* We have the type, so display that. */
CString strMsg;
strMsg.Format(_T("[%#x] %s: (did not get name)\n"),SystemHandle->Handle,objectTypeInfo->TypeName.Buffer);
::OutputDebugString((LPCTSTR)strMsg);
free(objectTypeInfo);
//CloseHandle(DuplicatedHandle);
continue;
}
PVOID objectNameInfo;
objectNameInfo = malloc(ONEPAGESIZE);
ULONG retLength;
if (m_pfunNtQueryObject((HANDLE)SystemHandle->Handle,ObjectNameInformation,objectNameInfo,ONEPAGESIZE,&retLength)!=STATUS_SUCCESS)
{
objectNameInfo = realloc(objectNameInfo,retLength);
if (m_pfunNtQueryObject((HANDLE)SystemHandle->Handle,ObjectNameInformation,objectNameInfo,ONEPAGESIZE,&retLength)!=STATUS_SUCCESS)
{
/* We have the type name, so just display that. */
CString strMsg;
strMsg.Format(_T("[%#x] %s: (could not get name)\n"),SystemHandle->Handle,objectTypeInfo->TypeName.Buffer);
::OutputDebugString((LPCTSTR)strMsg);
free(objectTypeInfo);
free(objectNameInfo);
//CloseHandle(DuplicatedHandle);
continue;
}
}
/* Cast our buffer into an UNICODE_STRING. */
UNICODE_STRING objectName;
objectName = *(PUNICODE_STRING)objectNameInfo;
/* Print the information! */
if (objectName.Length)
{
/* The object has a name. */
CString strMsg;
strMsg.Format(_T("[%#x] %s: %s\n"),SystemHandle->Handle,objectTypeInfo->TypeName.Buffer,objectName.Buffer);
::OutputDebugString((LPCTSTR)strMsg);
}
else
{
/* Print something else. */
CString strMsg;
strMsg.Format(_T("[%#x] %s: (unnamed)\n"),SystemHandle->Handle,objectTypeInfo->TypeName.Buffer);
::OutputDebugString((LPCTSTR)strMsg);
}
CString strType=objectTypeInfo->TypeName.Buffer;
if (strType.CompareNoCase(_T("Key"))==0)
{
//CString strName=objectName.Buffer;
//if (strName.CompareNoCase(_T("\\REGISTRY\\MACHINE\\SOFTWARE\\Wow6432Node"))==0)
//{
// CloseHandle((HANDLE)SystemHandle->Handle);
//}
//CloseHandle((HANDLE)SystemHandle->Handle);
}
free(objectTypeInfo);
free(objectNameInfo);
//CloseHandle(DuplicatedHandle);
}
}
free(pSysHandleInfo);
return TRUE;
}
1. 本人写的和人家的源代码有点区别,我并没有复制句柄,但是觉得这样写是有道理的,防止程序其它部分代码关闭句柄后,导致查询句柄信息失败。
2. 我最开始想不管三七二十一把所有的注册表句柄都关闭,这样做从逻辑上是不正确的,例如程序的例程A刚刚创建注册表句柄,还没使用,就被运行在其它线程中的这段代码关闭,造成异常。另外我还发现,把所有的注册表句柄都关闭,其中包括一个句柄名称为HKLM的句柄,关闭这个句柄后,会导致后继的操作
RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Software"),0,KEY_READ,&hKey)失败,失败代码为ERROR_INVALID_HANDLE。
所以最后我还是匹配了句柄类型,句柄名称(匹配\Registry\HKLM\Microsoft\CTF\AssemblyItem),句柄存在时长等条件去关闭注册表句柄。