前导
reactos 句柄的设计位于ex执行体中,一般情况下句柄的使用都或多或少的伴随着对象的使用。目前认为句柄是用户层对系统层引用。
内核句柄表的创建
/* Create kernel handle table */ PsGetCurrentProcess()->ObjectTable = ExCreateHandleTable(NULL); ObpKernelHandleTable = PsGetCurrentProcess()->ObjectTable;
在内核对象管理器初始化时即ObInitSystem,在这个函数中,我们创建了内核句柄表,使用ExCreateHandleTable函数,这个函数返回句柄表的指针。并将内核句柄表设置成ob管理器句柄表全局结构。
PHANDLE_TABLE NTAPI ExCreateHandleTable(IN PEPROCESS Process OPTIONAL)
这个函数返回句柄表,先是调用ExpAllocateHandleTable来申请句柄,然后将这个句柄表插入到HandleTableListHead全局句柄表中。接着我们来看内部函数如何来分配句柄表。
PHANDLE_TABLE NTAPI ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL, IN BOOLEAN NewTable) { PHANDLE_TABLE HandleTable; PHANDLE_TABLE_ENTRY HandleTableTable, HandleEntry; ULONG i; PAGED_CODE(); /* Allocate the table */ HandleTable = ExAllocatePoolWithTag(PagedPool, sizeof(HANDLE_TABLE), TAG_OBJECT_TABLE); if (!HandleTable) return NULL; /* Check if we have a process */ if (Process) { /* FIXME: Charge quota */ } /* Clear the table */ RtlZeroMemory(HandleTable, sizeof(HANDLE_TABLE)); /* Now allocate the first level structures */ HandleTableTable = ExpAllocateTablePagedPoolNoZero(Process, PAGE_SIZE); if (!HandleTableTable) { /* Failed, free the table */ ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE); return NULL; } /* Write the pointer to our first level structures */ HandleTable->TableCode = (ULONG_PTR)HandleTableTable; /* Initialize the first entry */ HandleEntry = &HandleTableTable[0]; HandleEntry->NextFreeTableEntry = -2; HandleEntry->Value = 0; /* Check if this is a new table */ if (NewTable) { /* Go past the root entry */ HandleEntry++; /* Loop every low level entry */ for (i = 1; i < (LOW_LEVEL_ENTRIES - 1); i++) { /* Set up the free data */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = (i + 1) * SizeOfHandle(1); /* Move to the next entry */ HandleEntry++; } /* Terminate the last entry */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = 0; HandleTable->FirstFree = SizeOfHandle(1); } /* Set the next handle needing pool after our allocated page from above */ HandleTable->NextHandleNeedingPool = LOW_LEVEL_ENTRIES * SizeOfHandle(1); /* Setup the rest of the handle table data */ HandleTable->QuotaProcess = Process; HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId; HandleTable->Flags = 0; /* Loop all the handle table locks */ for (i = 0; i < 4; i++) { /* Initialize the handle table lock */ ExInitializePushLock(&HandleTable->HandleTableLock[i]); } /* Initialize the contention event lock and return the lock */ ExInitializePushLock(&HandleTable->HandleContentionEvent); return HandleTable; }
在这个函数中我们首先为HandleTable申请空间,函数返回后会将其记录在进程结构体EPROCESS中,进程通过这个结构体内部成员就可以获取到句柄表。
然后我们会相应的填写HandleTable内部数据,来描述当前的进程句柄表的情况如上图所示。其结构如下:
typedef struct _HANDLE_TABLE { #if (NTDDI_VERSION >= NTDDI_WINXP) ULONG_PTR TableCode; #else PHANDLE_TABLE_ENTRY **Table; #endif PEPROCESS QuotaProcess; PVOID UniqueProcessId; #if (NTDDI_VERSION >= NTDDI_WINXP) EX_PUSH_LOCK HandleTableLock[4]; LIST_ENTRY HandleTableList; EX_PUSH_LOCK HandleContentionEvent; #else ERESOURCE HandleLock; LIST_ENTRY HandleTableList; KEVENT HandleContentionEvent; #endif PHANDLE_TRACE_DEBUG_INFO DebugInfo; LONG ExtraInfoPages; #if (NTDDI_VERSION >= NTDDI_LONGHORN) union { ULONG Flags; UCHAR StrictFIFO:1; }; LONG FirstFreeHandle; PHANDLE_TABLE_ENTRY LastFreeHandleEntry; LONG HandleCount; ULONG NextHandleNeedingPool; #else ULONG FirstFree; ULONG LastFree; ULONG NextHandleNeedingPool; LONG HandleCount; union { ULONG Flags; UCHAR StrictFIFO:1; }; #endif } HANDLE_TABLE, *PHANDLE_TABLE;
然后我们首先为一级表申请空间 HandleTableTable = ExpAllocateTablePagedPoolNoZero(Process, PAGE_SIZE);很显然我们创建了一页句柄表,得到页的开始地址。这个HandleTableTable是一个如下结构的表项。
typedef struct _HANDLE_TABLE_ENTRY { union { PVOID Object; ULONG_PTR ObAttributes; PHANDLE_TABLE_ENTRY_INFO InfoTable; ULONG_PTR Value; }; union { ULONG GrantedAccess; struct { USHORT GrantedAccessIndex; USHORT CreatorBackTraceIndex; }; LONG NextFreeTableEntry; }; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
当一级表满时,我们接着创建二级表。会在HandleTable中记录下指向。
/* Write the pointer to our first level structures */ HandleTable->TableCode = (ULONG_PTR)HandleTableTable;
我们在申请的一级表中弄一块空间,由HANDLE_TABLE_ENTRY定义,这是一个句柄表项,于是我们初始化这个表项,填写一些值。
然后在EXPROCESS中的handleTable中记录一些值,这些值记录着改变。
申请句柄前期准备工作
当我们创建进程时,有一个时间我们要在全局Cid中创建进程的CID句柄,以这个为例,我们来学习在句柄表中如何创建得到一个句柄。
Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);
ExCreateHandle的定义如下,在指定的句柄表中创建句柄。
HANDLE NTAPI ExCreateHandle(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry)
函数内部使用ExpAllocateHandleTableEntry来申请句柄表项。
/* Allocate a new entry */ NewEntry = ExpAllocateHandleTableEntry(HandleTable, &Handle);
我们首先介绍一下关于句柄的一些数据结构,然后介绍申请句柄时的逻辑。
ex执行体中我们使用EXHANDLE来表示一个句柄
typedef union _EXHANDLE { struct { ULONG_PTR TagBits:2; ULONG_PTR Index:29; }; struct { ULONG_PTR TagBits2:2; ULONG_PTR LowIndex:HANDLE_LOW_BITS; ULONG_PTR MidIndex:HANDLE_HIGH_BITS; ULONG_PTR HighIndex:HANDLE_HIGH_BITS; ULONG_PTR KernelFlag:KERNEL_FLAG_BITS; }; HANDLE GenericHandleOverlay; ULONG_PTR Value; } EXHANDLE, *PEXHANDLE;
而应用层我们定义句柄如下:
/* Handle Type */ typedef void *HANDLE, **PHANDLE;
应用层的句柄定义竟如此简单,以至于我们一直疑惑句柄的意义和价值。 而ex中也仅仅是将Handle.GenericHandleOverlay赋值给HANDLE
而在具体句柄表中申请句柄项,其申请函数如下。
PHANDLE_TABLE_ENTRY NTAPI ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, OUT PEXHANDLE NewHandle)
检查空闲句柄:
1.首先检查句柄表是否有空闲。
2.如果没有空闲项剩余,就锁定表再检查一下
3.如果还是没有,移除空闲句柄,使得剩余出可用句柄
4.如果还是没有,我们调用ExpAllocateHandleTableEntrySlow来实际申请句柄
最后我们记录下得到的句柄 Handle.Value = (OldValue & FREE_HANDLE_MASK);
使用这个句柄值查询句柄表 Entry = ExpLookupHandleTableEntry(HandleTable, Handle);,获得对应的句柄项。之后递增句柄表的句柄数目,返回句柄。
实际的句柄申请函数
BOOLEAN NTAPI ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable, IN BOOLEAN DoInit) { ULONG i, j, Index; PHANDLE_TABLE_ENTRY Low = NULL, *Mid, **High, *SecondLevel, **ThirdLevel; ULONG NewFree, FirstFree; PVOID Value; ULONG_PTR TableCode = HandleTable->TableCode; ULONG_PTR TableBase = TableCode & ~3; ULONG TableLevel = (ULONG)(TableCode & 3); PAGED_CODE(); /* Check how many levels we already have */ if (TableLevel == 0) { /* Allocate a mid level, since we only have a low level */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) return FALSE; /* Link up the tables */ Mid[1] = Mid[0]; Mid[0] = (PVOID)TableBase; /* Write the new level and attempt to change the table code */ TableBase = ((ULONG_PTR)Mid) | 1; Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase); } else if (TableLevel == 1) { /* Setup the 2nd level table */ SecondLevel = (PVOID)TableBase; /* Get if the next index can fit in the table */ i = HandleTable->NextHandleNeedingPool / SizeOfHandle(LOW_LEVEL_ENTRIES); if (i < MID_LEVEL_ENTRIES) { /* We need to allocate a new table */ Low = ExpAllocateLowLevelTable(HandleTable, DoInit); if (!Low) return FALSE; /* Update the table */ Value = InterlockedExchangePointer((PVOID*)&SecondLevel[i], Low); ASSERT(Value == NULL); } else { /* We need a new high level table */ High = ExpAllocateTablePagedPool(HandleTable->QuotaProcess, SizeOfHandle(HIGH_LEVEL_ENTRIES)); if (!High) return FALSE; /* Allocate a new mid level table as well */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) { /* We failed, free the high level table as welll */ ExpFreeTablePagedPool(HandleTable->QuotaProcess, High, SizeOfHandle(HIGH_LEVEL_ENTRIES)); return FALSE; } /* Link up the tables */ High[0] = (PVOID)TableBase; High[1] = Mid; /* Write the new table and change the table code */ TableBase = ((ULONG_PTR)High) | 2; Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase); } } else if (TableLevel == 2) { /* Setup the 3rd level table */ ThirdLevel = (PVOID)TableBase; /* Get the index and check if it can fit */ i = HandleTable->NextHandleNeedingPool / SizeOfHandle(MAX_MID_INDEX); if (i >= HIGH_LEVEL_ENTRIES) return FALSE; /* Check if there's no mid-level table */ if (!ThirdLevel[i]) { /* Allocate a new mid level table */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) return FALSE; /* Update the table pointer */ Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i], Mid); ASSERT(Value == NULL); } else { /* We have one, check at which index we should insert our entry */ Index = (HandleTable->NextHandleNeedingPool / SizeOfHandle(1)) - i * MAX_MID_INDEX; j = Index / LOW_LEVEL_ENTRIES; /* Allocate a new low level */ Low = ExpAllocateLowLevelTable(HandleTable, DoInit); if (!Low) return FALSE; /* Update the table pointer */ Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i][j], Low); ASSERT(Value == NULL); } } else { /* Something is really broken */ ASSERT(FALSE); } /* Update the index of the next handle */ Index = InterlockedExchangeAdd((PLONG) &HandleTable->NextHandleNeedingPool, SizeOfHandle(LOW_LEVEL_ENTRIES)); /* Check if need to initialize the table */ if (DoInit) { /* Create a new index number */ Index += SizeOfHandle(1); /* Start free index change loop */ for (;;) { /* Setup the first free index */ FirstFree = HandleTable->FirstFree; Low[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = FirstFree; /* Change the index */ NewFree = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, Index, FirstFree); if (NewFree == FirstFree) break; } } /* All done */ return TRUE; }
这函数来实际申请一个句柄,函数中根据需要创建一级和二级或是三级句柄表,然后设置空闲句柄项。
根据句柄值获得句柄项
比较简单,使用handle中的索引,按着句柄表看是三级二级或是一级查询即可
PHANDLE_TABLE_ENTRY NTAPI ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN EXHANDLE Handle) { ULONG TableLevel; ULONG_PTR TableBase; PHANDLE_TABLE_ENTRY HandleArray, Entry; PVOID *PointerArray; /* Clear the tag bits */ Handle.TagBits = 0; /* Check if the handle is in the allocated range */ if (Handle.Value >= HandleTable->NextHandleNeedingPool) { return NULL; } /* Get the table code */ TableBase = HandleTable->TableCode; /* Extract the table level and actual table base */ TableLevel = (ULONG)(TableBase & 3); TableBase &= ~3; PointerArray = (PVOID*)TableBase; HandleArray = (PHANDLE_TABLE_ENTRY)TableBase; /* Check what level we're running at */ switch (TableLevel) { case 2: /* Get the mid level pointer array */ PointerArray = PointerArray[Handle.HighIndex]; /* Fall through */ case 1: /* Get the handle array */ HandleArray = PointerArray[Handle.MidIndex]; /* Fall through */ case 0: /* Get the entry using the low index */ Entry = &HandleArray[Handle.LowIndex]; /* All done */ break; default: NT_ASSERT(FALSE); Entry = NULL; } /* Return the handle entry */ return Entry; }