一.介绍
1.什么是句柄
句柄windows应用程序用来表示资源的一个符号,几乎所有的资源对于应用程序来说都表示为一个句柄。比如文件,用CreateFile打开一个文件,成功则会得到一个句柄,其实就是一个32位无符号整数。“当一个进程根据名称来创建或者打开一个对象时,他收到一个句柄,然后通过此举并来访问该对象。”(深入解析windows操作系统,P135)。这里HANDLE类型的作用已经很清楚了。C语言fopen函数打开文件时候,会返回一个FILE类型指针,其实这东西就可以认为也是个句柄。
2.HANDLE有什么好处呢?
a.效率:“使用句柄访问对象要比使用名字访问对象快的多,因为对象管理器可以跳过名称查找过程,直接找到目标对象。”(深入解析windows操作系统,P135)。这个问题属于对象管理器范畴,不细说了。
b.封装:句柄是系统资源的一个间接指针,使得用户模式应用程序不用直接和系统数据结构打交道。windows应用程序看来,文件,进程,线程等资源统统都是一个句柄。开发者只需要获得这个句柄,然后将句柄作为参数来做有关调用。
下面这段代码就是很好的例子:
vector<wstring> vecProcessNames;
PROCESSENTRY32 pe32;
HANDLE hTool;
pe32.dwSize = sizeof(PROCESSENTRY32);
hTool = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); //获得句柄
if(hTool==INVALID_HANDLE_VALUE)
{
MessageBox(NULL, _T("获得Toolhelp32Snapshot句柄失败"), _T("错误"), MB_OK);
return 1;
}
if(Process32First(hTool, &pe32) == TRUE) //第一个参数就是获得的句柄
{
while(Process32Next(hTool,&pe32))
{
vecProcessNames.push_back(pe32.szExeFile);
}
}
CloseHandle(hTool);//关闭句柄
c.统一的资源接口:如上所说,文件,进程,线程,内存段等资源都以HANDLE出现。
d.安全性:对喜爱能够管理器可以完全监视应用程序访问的资源。
3.什么是句柄表
句柄表是windows对象管理器用来管理句柄的结构,每个进程都包含一个执行体进程结构块(_EPROCESS),而该结构中的的ObjectTable字段就是句柄表的指针。
通过windbg内核调试器找到一个进程的句柄表地址:
1.找到指定程序的process id。这里我自己写了一个程序,打开一个文件,显示当前文件句柄和当前进程号。2928 = 0xb7
2.windbg内核调试器连接到虚拟机,这个不说了,去网上赵windbg的用法吧。
3.使用!process 0xb70 7命令,获得该进程相关信息。第一个参数0xb70就是process id。
从这张图中,我们可以得到很多信息:
EPROCESS结构地址:0x884a65d0
ObjectTable指针:0x949b1cc0
4.使用dt _EPROCESS 0x884a65d0即可查看到当前进程的EPROCESS结构
这里也可以看到,了ObjectTable指针是0x949b1cc0。我的调试机器是win7 32位旗舰版,ObjectTable在EPROCESS中的偏移是0xf4。别的系统可能不同。
二.句柄表结构
1._HANDLE_TABLE结构
WRK 1.2中该结构定义为:
typedef struct _HANDLE_TABLE {
//
// A pointer to the top level handle table tree node.
// 一个指向顶层句柄表树节点的指针(句柄表是一个树结构,分三层)
ULONG_PTR TableCode;
//
// The process who is being charged quota for this handle table and a
// unique process id to use in our callbacks
// 所属进程的EPROCESS结构,和所属进程的id
struct _EPROCESS *QuotaProcess;
HANDLE UniqueProcessId;
//
// These locks are used for table expansion and preventing the A-B-A problem
// on handle allocate.
// 这些锁
#define HANDLE_TABLE_LOCKS 4
EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS];
//
// The list of global handle tables. This field is protected by a global
// lock.
//
LIST_ENTRY HandleTableList;
//
// Define a field to block on if a handle is found locked.
//
EX_PUSH_LOCK HandleContentionEvent;
//
// Debug info. Only allocated if we are debugging handles
//
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
//
// The number of pages for additional info.
// This counter is used to improve the performance
// in ExGetHandleInfo
//
LONG ExtraInfoPages;
//
// This is a singly linked list of free table entries. We don't actually
// use pointers, but have each store the index of the next free entry
// in the list. The list is managed as a lifo list. We also keep track
// of the next index that we have to allocate pool to hold.
//
ULONG FirstFree;
//
// We free handles to this list when handle debugging is on or if we see
// that a thread has this handles bucket lock held. The allows us to delay reuse
// of handles to get a better chance of catching offenders
//
ULONG LastFree;
//
// This is the next handle index needing a pool allocation. Its also used as a bound
// for good handles.
//
ULONG NextHandleNeedingPool;
//
// The number of handle table entries in use.
//
LONG HandleCount;
//
// Define a flags field
//
union {
ULONG Flags;
//
// For optimization we reuse handle values quickly. This can be a problem for
// some usages of handles and makes debugging a little harder. If this
// bit is set then we always use FIFO handle allocation.
//
BOOLEAN StrictFIFO : 1;
};
} HANDLE_TABLE, *PHANDLE_TABLE;