windows handle manager

原创 2016年08月31日 14:30:56

前导

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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Windows 消息分流器-HANDLE_MSG【转自别人,附上自己的实验代码】

windows消息分析器的实现很好理 解,windows操作系统使用消息处理机制,那么,我们所设计的程序如何才能分辨和处理系统中的各种消息呢?这就是消息分析器的作用.          简单来说,消息...

Windows HANDLE是什么

描述了windows中为什么要定义HANDLE

Windows客户端开发--必须清楚HWND、HANDLE、HMODULE、HINSTANCE的区别

我先不说话,就看看几个代码片段1 使用HWND 查找窗口:HWND h_wnd = ::FindWindow(_T("MainForm"), NULL);创建窗口:HWND hwnd = Windo...

Windows 2008之Server Manager应用

Windows Server 2008给我们带来许多新鲜的知识点,在学习过程中总是惊喜不断,接下来我把我在学习过程中所了解到的Sever Manager一些运用和大家一起分享。   当我们打开电...

Windows Heap Manager

Windows堆方面微软一直没有公布技术细节的,不过经过界内N多牛人的研究,已经放出些好资料。最近一直在研究Windows堆,略懂一点了。深感资料的缺乏,把这方面的好文的连接发出来。《Windows堆...
  • whf727
  • whf727
  • 2011-02-01 11:38
  • 1600

windows安装Apache,注册服务出现“(OS 5)拒绝访问。 : AH00369: Failed to open the WinNT service manager..."

我下的是压缩版的apache,而不是msi的,所以需要自己注册服务,结果在cmd命令行输入服务命令的时候出现了如下的错误: 百思不得其解,最后看错误提示:你可能没有用管理员身份运行。 所以我选择用...

Apache 安装出现 Failed to open the Windows service manager, perhaps you forgot to log in as Adminstrator

Apache 安装出现 Failed to open the Windows service manager, perhaps you forgot to log in as Adminstrator...

linux net-snmp agent manager windows snmp

这段时间一直在弄snmp相关的东西,

Linux rpm 命令参数使用详解:RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序”

Linux rpm 命令参数使用详解:RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序”
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)