前面简单提过,^^^^际上很类似虚拟内存页管理机制,釆用3级表结构实现句柄的分配. 系统根据TableLevel = (ULONG) (TableCode & 3),判断当前句柄级数,若级数 为0,则需要申请一个二级表项,并同时增加一级表,使新增加的二级表项指向一级表; 若级数为1,则已经是二级表,此时需要判断当前一级表个数是否小于二级表最大容 量,若小于二级表最大容量,可直接分配一个新的句柄表项,判断方式如下: i = HandleTable->NextHandleNeedingPool / SizeOfHandle(LOW_LEVEL_ENTRIES); 反之,则需要再申请一个三级表等。这里就不在赘述三级表分配策略,读者感兴趣的可参考 http://doxygen.reactos.org/de/d51/ntoskrnl 2ex 2handle 8c a8e7b0c9ba21950182ff7 lc2a091d99e9. html. 句柄销毁 对象管理器使用ObpCloseHandleTableEntry函数销毁对象句柄值,函数首先做一系列的检 查如是否允许关闭以及关闭的对象是否正确等,然后调用£10即^0711&1^16()完成句柄的 销毁动作,主要步骤: (1) 句柄值清零: Inter1ockedExchangePointer((PVOID*)&HandleTableEntry->Object, NULL); (2) 标识句柄描述符为?作6,同时清除[001^ flag. (3) 将原句柄值?^1^1』某个可用的空闲列表中。 其中,步骤(3)主要通过函数£1口[^611&1^161&1316£1^17实现,具体代码如下: VOID NTAPI ExpFreeHandleTableEntry ( IN PHANDLE_TABLE HandleTable, IN EXHANDLE Handle, IN PHAM)LE_TABLE_ENTRY HandleTableEntry ) { ULONG 01dYalue, NewValue, *Free; ULONG i; PAGED_CODE(); /*合^性校验*/ ASSERT(Hand1eTab1eEntry->Ob ject == NULL); ASSERT(HandleTableEntry == ExpLookupHandleTab1eEntry(Hand1eTab1e, Handle)); /*句柄引用数自减*/ InterlockedDecrement(&HandleTab1e->HandleCount); / *标识句柄值为?^6*/ NewValue = (ULONG)Handle. Value & ^(SizeOfHandle(1) - 1); / *校验句柄是否设置31^1(^[正0*/ if (!HandleTable->StrictFIFO){ /* Select a lock index */ i = (NewValue » 2) % 4; /*判断句柄锁定是否成功,若成功则[166指向了[化8〖[作6*/ Free = (HandleTable->HandleTableLock[i]. Locked) ? &HandleTable->FirstFree : &HandleTable->LastFree; } else{ / *卩166指向[&8七卩166*/ Free = &HandleTable->LastFree; } for (;;) { /*读取?^6所指向的值,即当前[^61^1^161181的队列头*/ 01dValue = *Free; /*将目标塵01£^^^£一£^1^的如11[1661&1^6£1^17指向原来的队列头*/ HandleTableEntry->NextFreeTableEntry = (ULONG)01dValue; /*将目标句柄£11^7插入到1&81?166队列的头部*/ if (InterlockedCompareExchange((PLONG) Free, NewValue, 01dValue)== 01dValue){ /* Break out, we’re done. Make sure the handle value makes sense */ ASSERT((01dValue & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool); break; 上述代码中,我们主要关注3化1(^[1?0是否设置,gStrictFIF0=0,则将?^6指向 HandleTable->LastFree;反之,Free = &1^1^161&1^6-儿&81[166;而后的步骤是一致的, 即将待销毁的£的^插入到空闲队列中,而且都是插入到空闲队列的头部,只不过选择的队 列分别为[1181[166和1&8丨[166而已。上面的三行代码: 01dValue = *Free; HandleTableEntry->NextFreeTableEntry = (ULONG)01dValue; InterlockedCompareExchange((PLONG) Free, NewValue, 01dValue) 完全类似于单链表中头结点插入法,即将待插入节点口->1^1〖=Free->next,然后再 #Free->next = 口;实现节点的头插入。故,31^1(^[1阳是否设置十分重要,不仅影响待销 毁句柄项的插入,同时也进一步影响空闲句柄的分配,显然,3^^(^[正0=0时,句柄£11^^ 在队列中是匕1?0,即后进先出;而3化1(^[1[0=1时,则为[1卩0. 安全隐患 考虑以下场景:运行在^1^(^8驱动服务器上的用户服务进程提供了“^个公共的进程间 通信接口,且此接口可以被系统下的任意程序使用。则通过灸“^^享了三种可能的调用 方法: (1) OpenFile ( PCHAR FileName)—利用服务打#Nfe殊文件的句柄,然后将句柄 保存到与当前会话相关的内部结构中。 (2) CloseFile ()——关闭当前已打开的文件句柄。 (3) WriteToFile (LPBYTE Data)——对实现打开的文件写入二进制数据。 为了防止未授权的文件访问攻击,0?6沾116函数在真正打开指定文件之前,会调用 财如?6^00&161^^&4来模拟客户端。此时,对应的服务将无法打开一个没有访问客户端上 下文权限的文件。然而,在处理^^^亦丨^请求时,系统并不会确认客户端文件句柄是否 处于活动状态(例如:此文件句柄先前未调用010%1^1^16关闭)。此时系统可能会认为客户 端在合法地写一个事先具有特殊权限令牌的合法客户端打开的文件,但实际上,执行的句柄 操作却是已经关闭的句柄。 |