前导
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;
}