全局句柄表

 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("句柄表存在多级\r\n");
        return STATUS_UNSUCCESSFUL;
    }

    // 获取数量
    ULONG HandleCount = *(PULONG)((PUCHAR)PspCidTable + 0x03c);
    //DbgPrint("该句柄表中的句柄个数:%x\r\n", 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\r\n", (ULONG)((PUCHAR)pType + 0x40)); // TYPE_OBJECT.name
            DbgPrint("%wZ", (PULONG)((PUCHAR)pType + 0x40));    
        }
    
    }

    return STATUS_SUCCESS;

}

 

 

posted @ 2020-04-25 22:33  OneTrainee  阅读(984)  评论(0编辑  收藏  举报