私有句柄表
属于一个程序独有的 每个程序都有属于自己程序的句柄表 里面存储了所有本程序打开的内核对象
进程创建 或打开内核对象的时候 将获得句柄,通过句柄访问内核对象
句柄存在的目的是为了避免在应用层直接修改内核对象 应用层要是可以修改 如果修改成无效的地址在传送到0环 操作系统就会崩溃 所以操作系统并没有像用户层公开这个结构体 而是创建了一个句柄表
每个进程都有自己的这样的私有的表 这个表存储在0环 而你的进程里创建几个句柄 表中就用几项 而句柄则是内核句柄表的索引
创建和打开是两种概念
创建:windos会为刚创建的内核对象在0环分配一个结构体
打开:返回之前创建的结构体的地址
句柄表的地址
打开100次内核对象 并不是打开一次给你创建一个结构体 一个内核对象只有一个结构体 打开一次只会增加计数器
拷贝到虚拟机 找到当前进程的句柄表
使用!process 0 0指令 遍历进程 找到我们的进程
找到进程结构体
找到成员
第一个成员 指向一张表 tablecode的低两位 有特殊含义 最后解释
Dd到这个地址
随便拿一个下标 找到真正句柄值的计算方法168 / 4 = 5a
因为句柄表一个成员是8字节
这就是0x168所对应的句柄的值 我们观察下 看这块内存值都是一样的因为他们都是同一个内核对象
这0x0000003a 87bf168b八个字节具体含义是什么哪
代码更改 在看下有什么变化 SetHandleInformation修改的肯定是最后一个句柄因为循环出来最后打开的那个句柄
1c4 / 4 = 71 句柄表里面的最后一项
为了和之前的句柄对比 现指令更改成如下 可以看到和之前的句柄值都不同
后四字节........~97bf168b 最后三位 b 1 0 1 1 第二位表示当前的句柄可不可以被继承 像我们当前的句柄是可以被继承的所以是1
而这个句柄值真正指向内核对象的地址是97bf1688 最后三位清0
真正的内核对象的开头都是_OBJECT_HEADER这个结构体
这个句柄表里面值指向的其实是这个完整的结构 我们打开的是进程 地址+18才是EPROCESS 0X16C 就是当前的镜像名称
验证
作用:在反调试的时候 遍历别人的句柄表 看别人的句柄表里有没有我们的句柄 有的话就说明这个程序打开了我
低2位为0 一个页4kb 一个成员8字节 最多512个
低2位为1 表示你当前的句柄表结构为2级的 前1级为地址 一个成员4字节 一共1024个成员 而每个成员所指向的才是句柄的信息
低2位为2 就是3级 前两级是地址 第三级才是内核对象信息
这三个函数在遍历的时候 遍历的其实就是这个表
根据pid的值 在PspCidTable中找到内核对象
首地址 也是HANDLE_TABLE结构的
此时的tablecode的值最后两位为1 说明是两级的 第一级的每个成员为4个字节
PID为十进制的转为16进制就是E88
tablecode低两位为1 E88/4 = 3A2
因为一个表成员个数为512(0x200)
(3A2 / 200)可以判断在哪个表中 在第二个表中
(3A2 % 200)可以判断在第二个表中的下标1a2
1A2就是第二个表的偏移
通过_HANDLE_TABLE_ENTRY结构查看.
也可以使用DQ查看
因为是全局句柄表和私有的句柄表有差异 所以地址直接指向了_EPROCESS