• 全局句柄表


     Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

    全局句柄表

    全局句柄表中只存两种对象,进程_EPROCESS与线程_ETHREAD,下面分析全局句柄表的结构

    1. 通过ID来获取句柄

      只要有过Windows编程经验的人,都会知道进程id与进程句柄的概念,其使用OpenProcess函数,传入一个进程id,其会帮你打开一个进程并返回该进程的句柄。

      注意,在Windows操作系统中,进程id、线程id、句柄都是4的倍数,不信可以打开任务管理器验证我们的说法。

      

      1)进程id在内核对象的位置

        其进程id保存在 _EPROCESS+0x84 UniqueProcessId,可以通过Windbg验证这说法。

        我们在内核中都使用过一个函数 PsLookupProcessByProcessId(),其可以通过进程Id来获取进程_EPROCESS。

        如下图该函数简单分析,现在我们得知其通过Pid从PspClidTable这张表中获取_EPROCESS。

        

      2)_HANDLE_TABLE

        PspCidTable其是一个_HANDLE_TABLE的数据结构,我们可以通过windbg查看有关成员。

        

         TableCode的分析

          TableCode指向的就是句柄表,但又不完全指向句柄表(末位0、1、3的区别)。

          注意:TableCode末尾是要分析清楚,一个句柄表以页为单位4KB,句柄表8个为一个单位,但是进程尤其在服务器中,其数量可以高过512个。

          

     

    2. 通过ID来获取句柄

      我们现在来实验一下通过进程id通过全局句柄表PspCidTable来找到_EPROCESS结构体。

      1)查看一个进程Id,我们以csrss.exe来进行实验

        csrss.exe的Pid为576,我们之前说过其都是为4的整数倍,然后576/4 = 144(0x90)来获取其索引

        

      2)通过PspCidTable查看其全局句柄表

        kd> dd PspCidTable

        80562460  e10008c0

        我们知道该地址e10008c0指向一个_HANDLE_TABLE的数据结构,我们遍历获取其TableCode e10008c0

        kd> dt _HANDLE_TABLE e10008c0
          nt!_HANDLE_TABLE
          +0x000 TableCode        : 0xe1003000
          +0x004 QuotaProcess     : (null)
          +0x008 UniqueProcessId  : (null)

      3)全局句柄表的元素为8字节一个单位,因此计算

        kd> dq  0xe1003000+0x90*8
        e1003480  00000000`81ef7ab1 000003d0`00000000
        e1003490  00000000`81b45021 00000000`81b453c1
        e10034a0  00000000`81a88441 00000000`81db1d11
        获取地址 81ef7ab1,注意其最后一位为属性位,减1即可 81ef7ab0(如果是9则变为8,不要抹零)。

      4)查看其_EPROCESS结构

        dt _EPROCESS 81ef7ab0

        

      注释:如果其为两层表,则如果索引xx>512,则查找第二张表 dq (xx-512)*8  这种方式来进行进程。

    3. 如何判断从全局句柄表中获得的是进程还是线程

      无论进程还是线程,其都存储在全局句柄表中,现在有一个问题,如何判断其取出来的是进程还是线程呢?

      我们看下面一个结构,Object_Header,任何对象上面都有这样一个,其Type指向一个_Object_Type结构,在该结构中有个name成员,

      通过这个Name成员可以判断是Process还是Thread,这个技巧是要明确的。

       

      

    4. 遍历全局句柄表

      其存储在全局句柄表中只有线程和进程,我们现在写一个简单的demo来遍历出相关全局句柄表,当然其只支持单个句柄表遍历,只有有详细寻求在继续往上添加即可。

    NTSTATUS TraverseGlobalHandleTable() {
        // 来遍历全局句柄表
        // 1.获取 PspCidTable 地址
        // 2.
        
        // 获取PspClidTable地址
        FindCode findcodes2[1] = { 0 };
        initFindCodeStruct(&findcodes2[0],
            "FF35****E8****8BD885DBC745*****74*578B3B80**75*83BF*****74*8BCFE8****84C074*8B45*8365**893853",
            0, 0);
        PUCHAR f = (PUCHAR)FindAddressByCode(findcodes2, 1);
        PULONG PspCidTable = *(PULONG)(*(PULONG)((PUCHAR)f + 2));
        PULONG TableCode = *PspCidTable;
    
        // 判断链表的是否是单层,目前只支持单层
        if (((ULONG)TableCode & 1) != 0) {
            DbgPrint("句柄表存在多级
    ");
            return STATUS_UNSUCCESSFUL;
        }
    
        // 获取数量
        ULONG HandleCount = *(PULONG)((PUCHAR)PspCidTable + 0x03c);
        //DbgPrint("该句柄表中的句柄个数:%x
    ", HandleCount);
    
    
        // 开始遍历句柄表
        PULONG p;  // _指向EPROCESS或ETHREAD
        PULONG pObjHeader; // 指向对象头
        PULONG pType;
        for (ULONG i = 0; i < HandleCount; i++) {
            p = *(PULONG)((PUCHAR)TableCode + i * 8) ; //  获取值
            p = (ULONG)p & 0xFFFFFFFE; // 将最后1bit清零
            if (MmIsAddressValid(p)) { // 当该地址为有效地址时
                pObjHeader = (PULONG)((PUCHAR)p - 0x18);  // 获取对象头
                pType = *(PULONG)((PUCHAR)pObjHeader + 0x8);  // 获取 TYPE_OBJECT
                //DbgPrint("名字:%x
    ", (ULONG)((PUCHAR)pType + 0x40)); // TYPE_OBJECT.name
                DbgPrint("%wZ", (PULONG)((PUCHAR)pType + 0x40));    
            }
        
        }
    
        return STATUS_SUCCESS;
    
    }

     

  • 相关阅读:
    nodejs中的全局函数setTimeout/clearTimeout,setInterval/clearInterval,unref/ref
    nodejs的核心对象console
    创建一个服务器,解析当前的url并根据url并作出相应的响应
    nodejs创建服务并加载一个html文件
    nodejs读文件
    Get和Post的区别
    ui-grid 网格布局--jQueryMobile
    web开发常见问题
    全选和全不选
    微信小程序-调用工具js文件/utils文件中的函数/变量
  • 原文地址:https://www.cnblogs.com/onetrainee/p/12774937.html
Copyright © 2020-2023  润新知