对象管理---4

本文深入探讨Windows内核中的对象管理,重点关注几个关键函数,如ObReferenceObjectByHandle、ExMapHandleToPointer和ObpLookupObjectName。这些函数在Windows内核对象系统中扮演重要角色,涉及对象引用、句柄映射和对象名查找等操作。文章详细阐述了函数的工作原理,包括句柄表的三级索引结构和对象引用计数的管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

几个常用的内核函数

在Windows的内核管理层(Executive),对象管理是个很重要的机制。Windows对象管理中,有些著作喜欢将其称为"子系统",其实也就是内核管理层。下面讲介绍几个常用的内核中对象管理的函数

ObReferenceObjectByHandle

这个函数根据Handle,返回一个该Handle对应的对象。

 //
 // Converts to and from a Kernel Handle to a normal handle
 //
 #define ObKernelHandleToHandle(Handle)                  \	//将内核句柄的相应标志位去掉
     (HANDLE)((ULONG_PTR)(Handle) & ~KERNEL_HANDLE_FLAG)
 #define ObMarkHandleAsKernelHandle(Handle)              \
     (HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_FLAG)

 #define ObpGetHandleObject(x)                           \	//获取的是对象头
     ((POBJECT_HEADER)((ULONG_PTR)x->Object & ~OBJ_HANDLE_ATTRIBUTES))
NTSTATUS
NTAPI
ObReferenceObjectByHandle(IN HANDLE Handle,
                          IN ACCESS_MASK DesiredAccess,
                          IN POBJECT_TYPE ObjectType,
                          IN KPROCESSOR_MODE AccessMode,
                          OUT PVOID* Object,
                          OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
{
   
    PHANDLE_TABLE_ENTRY HandleEntry;
    POBJECT_HEADER ObjectHeader;
    ACCESS_MASK GrantedAccess;
    ULONG Attributes;
    PEPROCESS CurrentProcess;
    PVOID HandleTable;
    PETHREAD CurrentThread;
    NTSTATUS Status;
    PAGED_CODE();

    /* Assume failure */
    *Object = NULL;

    /* Check if the caller wants the current process */
    if ((Handle == NtCurrentProcess()) &&               //当Handle值是NtCurrentProcess时 特殊对待
        ((ObjectType == PsProcessType) || !(ObjectType)))
    {
   
        /* Get the current process */
        CurrentProcess = PsGetCurrentProcess();         //调用PsGetCurrentProcess获取EPROCESS结构

        /* Check if the caller wanted handle information */
        if (HandleInformation)                          //如果要求返回句柄信息的话 填写相应的字段
        {
   
            /* Return it */
            HandleInformation->HandleAttributes = 0;
            HandleInformation->GrantedAccess = PROCESS_ALL_ACCESS;
        }

        /* Reference ourselves */
        ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess); //获取对象头
        InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); //获取对象头的目的是对 Header的PointerCount 即对该对象的引用计数加1

        /* Return the pointer */
        *Object = CurrentProcess;//EPROCESS结构即要找的对象
        return STATUS_SUCCESS;
    }
    else if (Handle == NtCurrentProcess())  //若Handle值是NtCurrentProcess,但类型不匹配 说明传入的类型出现错误
    {
   
        /* The caller used this special handle value with a non-process type */
        return STATUS_OBJECT_TYPE_MISMATCH;
    }

    /* Check if the caller wants the current thread */
    if ((Handle == NtCurrentThread()) &&        //对于Handle值为当前线程而言,同样如此 
        ((ObjectType == PsThreadType) || !(ObjectType)))
    {
   
        /* Get the current thread */
        CurrentThread = PsGetCurrentThread();

        /* Check if the caller wanted handle information */
        if (HandleInformation)
        {
   
            /* Return it */
            HandleInformation->HandleAttributes = 0;
            HandleInformation->GrantedAccess = THREAD_ALL_ACCESS;
        }

        /* Reference ourselves */
        ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread);
        InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);

        /* Return the pointer */
        *Object = CurrentThread;
        return STATUS_SUCCESS;
    }
    else if (Handle == NtCurrentThread())
    {
   
        /* The caller used this special handle value with a non-thread type */
        return STATUS_OBJECT_TYPE_MISMATCH;
    }


    //下面的是一般情况
    /* Check if this is a kernel handle */
    if (ObIsKernelHandle(Handle, AccessMode))   //若该句柄是内核句柄
    {
   
        /* Use the kernel handle table and get the actual handle value */
        Handle = ObKernelHandleToHandle(Handle);    //也就是将最高位(表示此时是kernel_handle)的标志位清零,因为之后要查找
        HandleTable = ObpKernelHandleTable;         //选用内核句柄表
    }
    else
    {
   
        /* Otherwise use this process's handle table */
        HandleTable = PsGetCurrentProcess()->ObjectTable;   //选用本进程的句柄表
    }

    /* Enter a critical region while we touch the handle table */
    ASSERT(HandleTable != NULL);
    KeEnterCriticalRegion();    //进入临界区

    /* Get the handle entry */
    HandleEntry = ExMapHandleToPointer(HandleTable, Handle);    //调用ExMapHandleToPointer根据句柄表和对应的句柄获取对应的句柄表项 非常重要的一个函数
    if (HandleEntry)    //如果找到了对应的句柄表项
    {
   
        /* Get the object header and validate the type*/
        ObjectHeader = ObpGetHandleObject(HandleEntry); //根据句柄表项获取对应的对象头
        if (!(ObjectType) || (ObjectType == ObjectHeader->Type))
        {
   
            /* Get the granted access and validate it */
            GrantedAccess = HandleEntry->GrantedAccess;
            if ((AccessMode == KernelMode) ||           //若访问机制不冲突 
                !(~GrantedAccess & DesiredAccess))
            {
   
                /* Reference the object directly since we have its header */
                InterlockedIncrement(&ObjectHeader->PointerCount);  //对该对象的引用计数加一

                /* Mask out the internal attributes */
                Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES;

                /* Check if the caller wants handle information */
                if (HandleInformation)  //若需要句柄信息 进行填写
                {
   
                    /* Fill out the information */
                    HandleInformation->HandleAttributes = Attributes;
                    HandleInformation->GrantedAccess = GrantedAccess;
                }

                /* Return the pointer */
                *Object = &ObjectHeader->Body;  //返回对应对象的主体 即具体对象的数据结构

                /* Unlock the handle */
                ExUnlockHandleTableEntry(HandleTable, HandleEntry);
                KeLeaveCriticalRegion();    //离开临界区

                /* Return success */
                ASSERT(*Object != NULL);
                return STATUS_SUCCESS;
            }
            else
            {
   
                /* Requested access failed */
                Status = STATUS_ACCESS_DENIED;
            }
        }
        else
        {
   
            /* Invalid object type */
            Status = STATUS_OBJECT_TYPE_MISMATCH;
        }

        /* Unlock the entry */
        ExUnlockHandleTableEntry(HandleTable, HandleEntry);
    }
    else
    {
   
        /* Invalid handle */
        Status = STATUS_INVALID_HANDLE;
    }

    /* Return failure status */
    KeLeaveCriticalRegion();
    *Object = NULL;
    return Status;
}

对于当前进程当前线程的句柄值而言,他们是很特殊的,Windows将它们也视为一个对象!!!。其对应的当前进程和当前线程的句柄值通过NtCurrentProcess和NtCurrentThread可以获取,注意这里要跟获取EPROCESS的函数 PsGetCurrentProcess进行区分。

#define NtCurrentProcess ( (HANDLE)(ULONG_PTR) -1 )
#define NtCurrentThread  (  (HANDLE)(ULONG_PTR) -2 )

NtCurrentProcess是句柄值!!!,而PsGetCurrentProcess()则是函数IoGetCurrentProcess,它返回的是当前进程的EPROCESS数据结构的地址
但是-1 和 -2 并不是真正的句柄,所以需要特殊处理。于是如果ObReferenceObjectByHandle 给定的句柄是-1,并且ObjectType是PsProcessType或者是默认ObjectType(即传入0),那么就会进入这条控制流,通过PsGetCurrentProcess来获取对象的数据结构。

当然大部分情况是下面的情况。也就是从句柄表获取相应的句柄表项,然后获取对象地址。

  1. 其流程是先根据是否是内核句柄选择相应的句柄表,若是内核句柄,则选择内核句柄表的时候,需要将KERNEL_HANDLE_FLAG的值设置为0。
  2. 然后调用ExMapHandleToPointer,根据选择的句柄表和我们传入的Handle找到相应的句柄表,这是一个很重要的函数,我们等会会去看它。
  3. 获取句柄表项后,其句柄表项就对应着相应的对象,通过ObpGetHandleObject宏可以获取到相应的对象,注意的是这里的对象Object,其实是ObjectHeader对象头。成功获取后,需要对其对象头字段的PointerCount,即引用计数要加一,最后返回的时候要返回它的Body,也就是ObjectHeader->Body。

ExMapHandleToPointer

看下它根据句柄表句柄寻找句柄表项的主要逻辑。

ObReferenceObjectByHandle->ExMapHandleToPointer

PHANDLE_TABLE_ENTRY
NTAPI
ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
                     IN HANDLE Handle)
{
   
    EXHANDLE ExHandle;
    PHANDLE_TABLE_ENTRY HandleTableEntry;
    PAGED_CODE();

    /* Set the handle value */
    ExHandle.GenericHandleOverlay = Handle;									//可以看出这里将Handle用ExHandle包装了起来

    /* Fail if we got an invalid index */
    if (!(ExHandle.Index & (LOW_LEVEL_ENTRIES - 1))) return NULL;			//如果最低层的索引为0 表明出现问题

    /* Do the lookup */
    HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);	//最终调用ExpLookupHandleTableEntry完成查找
    if (!HandleTableEntry) return NULL;

    /* Lock it */
    if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) return NULL;

    /* Return the entry */
    return HandleTableEntry;
}

这里将Handle用ExHandle包装了起来,然后判断该Handle是否合法,最后将查找的工作留给了ExpLookupHandleTableEntry

ExHandle的结构如下

 #define LOW_LEVEL_ENTRIES   (PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
 #define MID_LEVEL_ENTRIES   (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))
 #define HIGH_LEVEL_ENTRIES  (16777216 / (LOW_LEVEL_ENTRIES * MID_LEVEL_ENTRIES))

 #define HANDLE_LOW_BITS (PAGE_SHIFT - 3)		
 #define HANDLE_HIGH_BITS (PAGE_SHIFT - 2)
 #define HANDLE_TAG_BITS (2)
 #define HANDLE_INDEX_BITS (HANDLE_LOW_BITS + 2*HANDLE_HIGH_BITS)		// 9 + 20
 #define KERNEL_FLAG_BITS (sizeof(PVOID)*8 - HANDLE_INDEX_BITS - HANDLE_TAG_BITS)
 
typedef union _EXHANDLE
 {
   
      struct
      {
   
          ULONG_PTR TagBits  :     HANDLE_TAG_BITS;		//2位标签 代表的是HandleTable的层数
          ULONG_PTR Index :       HANDLE_INDEX_BITS;	//29位 
          ULONG_PTR KernelFlag : KERNEL_FLAG_BITS;		//1位 最高位 代表是否是内核句柄
      };
      struct
      {
   
          ULONG_PTR TagBits2:    HANDLE_TAG_BITS;
          ULONG_PTR LowIndex:    HANDLE_LOW_BITS;
          ULONG_PTR MidIndex:    HANDLE_HIGH_BITS;
          ULONG_PTR HighIndex:   HANDLE_HIGH_BITS;
          ULONG_PTR KernelFlag2: KERNEL_FLAG_BITS;
      };
      HANDLE GenericHandleOverlay;
      ULONG_PTR Value;
 } EXHANDLE, *PEXHANDLE;

可以看到EXHANDLE类型是个Union结构,最低两位是个Tag标签,剩下的29位是Index代表是一个查找的下标,其实也是30位,因为最高位在之前已经被清零了。因为Tag代表的是层数,当最低9位为0的时候,说明只有一层,那么此时索引为0显然是有问题的。

看下ExpLookupHandleTableEntry

ExpLookupHandleTableEntry

ObReferenceObjectByHandle->ExMapHandleToPointer->ExpLookupHandleTableEntry

PHANDLE_TABLE_ENTRY
NTAPI
ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,
                          IN EXHANDLE LookupHandle)
{
   
    ULONG i, j, k, TableLevel, NextHandle;
    ULONG_PTR TableBase;
    PHANDLE_TABLE_ENTRY Entry = NULL;
    EXHANDLE Handle = LookupHandle;
    PUCHAR Level1, Level2, Level3;

    /* Clear the tag bits and check what the next handle is */
    Handle.TagBits = 0; //清空层数
    NextHandle = HandleTable->NextHandleNeedingPool;
    if (Handle.Value >= NextHandle) return NULL;    //若Handle的Value落在了下一个HandleTable的区间 说明有问题

    /* Get the table code */
    TableBase = (ULONG_PTR)HandleTable->TableCode;  //获取句柄表对应的下一级表地址(伪地址)

    /* Extract the table level and actual table base */
    TableLevel = (ULONG)(TableBase & 3);            //TableCode不是严格的一个地址 其最低两位代表的是句柄表的层级
    TableBase = TableBase - TableLevel;             //去掉层级后才是真正的句柄表地址

    /* Check what level we're running at */
    switch (TableLevel)                             //根据层级来
    {
   
        /* Direct index */
        case 0:                                     //为0时表示此时的TableBase指向的是一个一维数组 每一个数组元素都是一个HANDLE_TABLE_ENTRY结构 一个元素8 Bytes大小

            /* Use level 1 and just get the entry directlry */
            Level1 = (PUCHAR)TableBase;             //此时TableBase指向的是最低层的一维数组
            Entry = (PVOID)&Level1[Handle.Value *               
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值