驱动开发:内核枚举PspCidTable句柄表

在上一篇文章《驱动开发:内核枚举DpcTimer定时器》中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows 10系统下面的PspCidTable内核句柄表地址 。
首先引入一段基础概念;

  • 1.在windows下所有的资源都是用对象的方式进行管理的(文件、进程、设备等都是对象),当要访问一个对象时,如打开一个文件,系统就会创建一个对象句柄,通过这个句柄可以对这个文件进行各种操作 。
  • 2.句柄和对象的联系是通过句柄表来进行的,准确来说一个句柄就是它所对应的对象在句柄表中的索引 。
  • 3.通过句柄可以在句柄表中找到对象的指针,通过指针就可以对,对象进行操作 。
PspCidTable 就是这样的一种表(内核句柄表),表的内部存放的是进程EPROCESS和线程ETHREAD的内核对象,并通过进程PID和线程TID进行索引,ID号以4递增,内核句柄表不属于任何进程,也不连接在系统的句柄表上,通过它可以返回系统的任何对象 。
内核句柄表与普通句柄表完全一样,但它与每个进程私有的句柄表有以下不同;
  • 1.PspCidTable 中存放的对象是系统中所有的进程线程对象,其索引就是PID和TID 。
  • 2.PspCidTable 中存放的直接是对象体EPROCESS和ETHREAD,而每个进程私有的句柄表则存放的是对象头OBJECT_HEADER 。
  • 3.PspCidTable 是一个独立的句柄表,而每个进程私有的句柄表以一个双链连接起来 。
  • 4.PspCidTable 访问对象时要掩掉低三位,每个进程私有的句柄表是双链连接起来的 。
那么在Windows10系统中该如何枚举句柄表;
  • 1.首先找到PsLookupProcessByProcessId函数地址,该函数是被导出的可以动态拿到 。
  • 2.其次在PsLookupProcessByProcessId地址中搜索PspReferenceCidTableEntry函数 。
  • 3.最后在PspReferenceCidTableEntry地址中找到PspCidTable函数 。
首先第一步先要得到PspCidTable函数内存地址,输入dp PspCidTable即可得到,如果在程序中则是调用MmGetSystemRoutineAddress取到 。
驱动开发:内核枚举PspCidTable句柄表

文章插图
PspCidTable是一个HANDLE_TALBE结构,当新建一个进程时,对应的会在PspCidTable存在一个该进程和线程对应的HANDLE_TABLE_ENTRY项 。在windows10中依然采用动态扩展的方法,当句柄数少的时候就采用下层表,多的时候才启用中层表或上层表 。
接着我们解析ffffdc88-79605dc0这个内存地址,执行dt _HANDLE_TABLE 0xffffdc8879605dc0得到规范化结构体 。
驱动开发:内核枚举PspCidTable句柄表

文章插图
内核句柄表分为三层如下;
  • 下层表:是一个HANDLE_TABLE_ENTRY项的索引,整个表共有256个元素,每个元素是一个8个字节长的HANDLE_TABLE_ENTRY项及索引,HANDLE_TABLE_ENTRY项中保存着指向对象的指针,下层表可以看成是进程和线程的稠密索引 。
  • 中层表:共有256个元素,每个元素是4个字节长的指向下层表的入口指针及索引,中层表可以看成是进程和线程的稀疏索引 。
  • 上层表:共有256个元素,每个元素是4个字节长的指向中层表的入口指针及索引,上层表可以看成是中层表的稀疏索引 。
总结起来一个句柄表有一个上层表,一个上层表最多可以有256个中层表的入口指针,每个中层表最多可以有256个下层表的入口指针,每个下层表最多可以有256个进程和线程对象的指针 。PspCidTable表可以看成是HANDLE_TBALE_ENTRY项的多级索引 。

经验总结扩展阅读