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 Sysinternals Handle Viewer

  • 2016年08月22日 10:40
  • 416KB
  • 下载

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

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

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

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

Windows HANDLE是什么

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

Windows程序设计(4):根据PID,获取句柄Handle

如题。
  • qcyfred
  • qcyfred
  • 2017年11月11日 20:46
  • 75

Windows 2008之Server Manager应用

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

Windows Heap Manager

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

RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序”

RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包 二进制包(Binary)以及源代码包(Source...

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

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

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

我下的是压缩版的apache,而不是msi的,所以需要自己注册服务,结果在cmd命令行输入服务命令的时候出现了如下的错误: 百思不得其解,最后看错误提示:你可能没有用管理员身份运行。 所以我选择用...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:windows handle manager
举报原因:
原因补充:

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