• 谈windows中的句柄


    谈windows中的句柄

     
    每当一个进程打开一个对象,系统就返回一个句柄作为凭证,由此可以想到,句柄是依赖于具体的进程的,换句话说,句柄一定属于某个进程,以后在访问这个对象时就要使用这个凭证!
        所以句柄还可以认为是一个进程和一个对象之间建立的一种连接,一座桥梁,一个上下文,就像一个门一样,已经打开。这种连接就持续存在,直到关闭。
    一个进程可打开对个对象,就会拥有多个句柄,所以每个进程都拥有一个句柄表,在进程控制块EPROCESS中有个指针ObjectTable是_HANDLE_TABLE类型,指向本进程的句柄表!
    看下_HANDLE_TABLE结构:
     1 kd> dt _handle_table
     2 nt!_HANDLE_TABLE
     3 +0x000 TableCode : Uint4B
     4 +0x004 QuotaProcess : Ptr32 _EPROCESS
     5 +0x008 UniqueProcessId : Ptr32 Void
     6 +0x00c HandleLock : _EX_PUSH_LOCK
     7 +0x010 HandleTableList : _LIST_ENTRY //句柄表双向链表
     8 +0x018 HandleContentionEvent : _EX_PUSH_LOCK
     9 +0x01c DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
    10 +0x020 ExtraInfoPages : Int4B
    11 +0x024 Flags : Uint4B
    12 +0x024 StrictFIFO : Pos 0, 1 Bit
    13 +0x028 FirstFreeHandle : Uint4B
    14 +0x02c LastFreeHandleEntry : Ptr32 _HANDLE_TABLE_ENTRY
    15 +0x030 HandleCount : Uint4B
    16 +0x034 NextHandleNeedingPool : Uint4B
    17 +0x038 HandleCountHighWatermark : Uint4B
    TableCode的低两位被用作标志位,用于表示当前句柄表的级数,0,1,2分别表示一级表,二级表,三级表。
    一级表实际上是一个_HANDLE_TABLE_ENTRY 数组,每个_HANDLE_TABLE_ENTRY 8个字节,而一级表是一个page的大小,所以一级表可以容纳2^9个_HANDLE_TABLE_ENTRY 
     1 lkd> dt _HANDLE_TABLE_ENTRY
     2 nt!_HANDLE_TABLE_ENTRY
     3 +0x000 Object : Ptr32 Void//指向对象的一个指针
     4 +0x000 ObAttributes : Uint4B
     5 +0x000 InfoTable : Ptr32 _HANDLE_TABLE_ENTRY_INFO
     6 +0x000 Value : Uint4B
     7 +0x004 GrantedAccess : Uint4B
     8 +0x004 GrantedAccessIndex : Uint2B
     9 +0x006 CreatorBackTraceIndex : Uint2B
    10 +0x004 NextFreeTableEntry : Uint4B
    而句柄以4为步进(注0),因此最大句柄为0x200*4=0x800.其中可存放的最大句柄不超过0x800(最大为0x800-4),而每个一级表的第一个HANDLE_TABLE_ENTRY的Object总是为0,因为我们都知道0是一个无效的句柄,它不指向一个有效的对象。因此,每个一级表实际存放的句柄数为511个!
     
    _HANDLE_TABLE结构中有个TableCode字段,该字段有32位,低2位作为标记是几级表,0为一级,1为2级,2为3级。当为0时,TableCode值就指向一个Page,此页就是句柄表,每个表项8个字节为一个entry,低4个字节是对象地址,注意第一个entry不代表任何对象。
    若TableCode前两位为1或者2,那么TableCode&0xfffffffd就指向二级或者三级表,每个表项四个字节,指向一个一级或者二级表,以此类推!!
    注意:由于对象体都是8字节对齐的,所以对象地址的低3位总是0,可用于标记对象的某些属性,所以在一级句柄表中的对象地址要&0xfffffff8才表示对象头的地址,加上0x18后得到对象体(这里涉及到windows内核中对象的管理,参见另一篇关于对象的文章)!
    句柄表架构如图所示:
    至于为什么句柄表的号都是4的倍数呢?


    一个进程的句柄表包含了所有已被该进程打开的那些对象的指针。对象句柄是用来检索句柄表的一个“伪索引”。对于句柄表机制,achillis <<Windows句柄表>>系列文章已经分析得很透彻了,只是对“句柄以4为步进”来源不明。经查,根源如下:

    typedef struct _EXHANDLE
    {
     union
     {
      struct
      {
       ULONG TagBits:2;
       ULONG Index:30;
      }
      HANDLE GenericHandleOverlay;
      #define HANLE_VALUE_INC 4
      ULONG_PTR Value;
     }

    }EXHANDLE,*PEXHANDLE;
    此结构正是用来定义句柄类型。低2位TagBits为标志位Windows用于其它用途,故句柄值低2位对其作为句柄表索引本身无意义,所以等于4的倍数。有了以上分析,自然,在用句柄值为索引取句柄表项时,句柄值必须/4。因此程序中用到的句柄值并不能直接用来索引句柄表,也就有了“伪索引”说法。

     
    在windows系统中,主要分为两种句柄表:
    1、单个进程的句柄表
    2、系统全局句柄表pspCidTable
    前者主要用于进程打开的各种对象,而后者用于分配全局进程PID。以任务管理器关闭某个进程为例,如果其要关闭一个进程,首先根据进程PID打开其进程并获取访问这个进程的句柄,这时,PID对应在PspHandleTable中的索引,而获得的句柄对应任务管理器的句柄表中的索引,仅仅在任务管理器的进程空间中有效,一个全局、一个局部。而解析句柄和PID的过程完全一致。
    主要区别在于全局句柄表的表项指向的是对象体而不是对象头。
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    java相关知识集锦
    adb相关基础知识集锦
    我所理解的OOP——UML六种关系
    据说每个大牛、小牛都应该有自己的库——DOM处理续
    据说每个大牛、小牛都应该有自己的库——DOM处理
    JavaScript 继承
    据说每个大牛、小牛都应该有自己的库——Event处理
    据说每个大牛、小牛都应该有自己的库——框架篇
    可拖动的DIV续
    CSS hack前传——背景图片全屏
  • 原文地址:https://www.cnblogs.com/ck1020/p/5897460.html
Copyright © 2020-2023  润新知