windows handle manager

来源:互联网 发布:免费商业源码分享 编辑:程序博客网 时间:2024/05/23 11:19

前导

reactos 句柄的设计位于ex执行体中,一般情况下句柄的使用都或多或少的伴随着对象的使用。目前认为句柄是用户层对系统层引用。
这里写图片描述

内核句柄表的创建

    /* Create kernel handle table */    PsGetCurrentProcess()->ObjectTable = ExCreateHandleTable(NULL);    ObpKernelHandleTable = PsGetCurrentProcess()->ObjectTable;

在内核对象管理器初始化时即ObInitSystem,在这个函数中,我们创建了内核句柄表,使用ExCreateHandleTable函数,这个函数返回句柄表的指针。并将内核句柄表设置成ob管理器句柄表全局结构。

PHANDLE_TABLENTAPIExCreateHandleTable(IN PEPROCESS Process OPTIONAL)

这个函数返回句柄表,先是调用ExpAllocateHandleTable来申请句柄,然后将这个句柄表插入到HandleTableListHead全局句柄表中。接着我们来看内部函数如何来分配句柄表。

PHANDLE_TABLENTAPIExpAllocateHandleTable(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的定义如下,在指定的句柄表中创建句柄。

HANDLENTAPIExCreateHandle(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_ENTRYNTAPIExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,                            OUT PEXHANDLE NewHandle)

检查空闲句柄:
1.首先检查句柄表是否有空闲。
2.如果没有空闲项剩余,就锁定表再检查一下
3.如果还是没有,移除空闲句柄,使得剩余出可用句柄
4.如果还是没有,我们调用ExpAllocateHandleTableEntrySlow来实际申请句柄
最后我们记录下得到的句柄 Handle.Value = (OldValue & FREE_HANDLE_MASK);
使用这个句柄值查询句柄表 Entry = ExpLookupHandleTableEntry(HandleTable, Handle);,获得对应的句柄项。之后递增句柄表的句柄数目,返回句柄。

实际的句柄申请函数

BOOLEAN NTAPIExpAllocateHandleTableEntrySlow(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_ENTRYNTAPIExpLookupHandleTableEntry(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;}
0 0
原创粉丝点击