首页
社区
课程
招聘
[原创]X86内核笔记_5_句柄
发表于: 2021-12-9 17:11 15546

[原创]X86内核笔记_5_句柄

2021-12-9 17:11
15546

在实际对抗中,攻击者往往第一件事就是一个OpenProcess,然后执行注入、内存读写等操作。OpenProcess会返回一个叫做句柄的东西。而防守方通常对某些进程的句柄非常敏感,通过各种手段使OpenProcess失败或对返回的句柄进行处理,从而将恶意行为扼杀在摇篮里。本章通过学习句柄的一系列知识来体会下句柄防护在实际攻防对抗中的重要性。

当调用OpenProcess时,会传入进程ID、权限等参数。此时系统会通过进程ID去全局句柄表中搜索进程对象,然后把找到的进程对象插入到调用OpenProcess的进程私有句柄表中,再将插入后的索引返回给调用者,这个索引就是句柄

线程的句柄获取方式与进程相同。

全局句柄表中存储着当前系统所有进程及线程的对象信息。且全局句柄表中仅存储进程、线程信息。

进程ID、线程ID,每一个ID对应全局句柄表中一个成员,每个ID都是4的整数倍。但全局句柄表每个成员占8字节。宏观体现如下:(假设全局句柄表地址为0x12345670)

image-20211207154746596

当进程、线程数量过多时,一张表的512项显然不够,这时全局句柄表地址最后一位会变成1,代表额外增加一层,宏观体现如下:(全局句柄表地址0x12345671,实际地址为0x12345670,最后一位仅代表层级)

image-20211207155559537

当双层结构也不够存储时,又会增加一层。结构如下:

image-20211207160132932

在虚拟机中随便打开个进程,查看该进程的PID,如dbgview:540(十进制)

image-20211207160403498

全局句柄表存在名为PspCidTable的全局变量中,windbg中查看该变量。

句柄表实质是个名为_HANDLE_TABLE的结构,windbg中查看该结构。

第一个成员TableCode值为0x9a8d6001,最后一位为1,说明有两层结构。第一层表中每个成员占4字节,每个成员都指向一张表。windbg查看第一层。

上文dbgview进程ID为540,每个进程、线程ID都是4的倍数,因此540除以4等于135,索引从0开始,也就是第136个。每个表存储521个进程、线程信息,因此dbgview存在第一张表中。使用windbg命令查看第一张表索引为135的成员:

找到的成员为0000000087fd0571,最后3个二进制位需要清空,就变成了0000000087fd0570,这个地址就是dbgview进程结构的地址:

索引为135的8字节成员同时也是一个名为 _HANDLE_TABLE_ENTRY 的结构,使用dt _HANDLE_TABLE_ENTRY 8d405000+8*0x87 也能得到进程结构体地址。这里直接看低4字节数据是因为全局句柄表中没有权限划分,因此高4字节必然为0。

image-20211207161847950

上文通过指定进程PID查询到了进程结构体,但全局句柄表中既有进程也有线程,反向查询时单凭一个结构体首地址无法得知这个结构体是进程的还是线程的。

每个内核对象地址前面都有一个对象头结构,描述了这个对象的类型。对象头结构占0x18字节。

在windbg中查看全局句柄表中索引为1的对象头信息:

其中TypeIndex成员指明了这个内核对象的类型,这个成员是内核对象类型表的索引值,内核对象类型表存储在全局变量ObTypeIndexTable中,每个成员占4个字节,每个成员都是一个名为_OBJECT_TYPE的结构体。使用windbg查看该内核对象具体类型信息:

思路:

效果:

无法打开目标进程。

全局句柄表中仅存储所有进程和线程。类似互斥体、事件、信号量这些东西的句柄都存储在调用者进程自身的私有句柄表中。私有句柄表中还存储OpenProcess、OpenThread返回的句柄和从父进程继承的句柄。

使用windbg随便查看一个进程的EPROCESS结构,在偏移0xF4处有一个成员名为ObjectTable,其类型为 _HANDLE_TABLE 。与全局句柄表类型结构一致,查找方式也一致。这个句柄表就是该进程的私有句柄表。

image-20211209111243659

该句柄表结构

image-20211209111343484

该句柄表结构成员信息:

查看句柄表内每个句柄:

第一个句柄为-2,代表当前线程。在3环调用GetCurrentProcess会返回-1代表当前进程,GetCurrentThread返回-2代表当前线程。

继续查看第二个句柄结构:

一些公司为了防止自身进程被操作,会遍历系统中所有进程的私有句柄表。如果发现某个进程的私有句柄表有自己的进程(判断进程名、进程ID、CR3、进程结构体地址等手段),就清空该私有句柄表的自身句柄的权限位(_HANDLE_TABLE_ENTRY->GrantedAccess)。这样导致该句柄不再具有读写等权限,从而保护自身进程。

由于我们的进程打开目标进程后,私有句柄表内的句柄权限被循环清空,导致无法读写目标进程的内存或只能查看很短的时间。因此我们需要进行句柄的替换。

需注意句柄结构中Object成员包含0x18大小的对象头部数据,在复制时要将目标进程的对象头结构一同复制。

 
 
 
 
 
 
 
 
kd> dd PspCidTable
83f7bf34  8d404008 00000000 80000020 00000101
83f7bf44  80000320 80000024 00000000 00000000
83f7bf54  00000000 00000000 00000000 00000113
83f7bf64  00000000 00000000 83f2c35a 00000000
83f7bf74  00000000 00000000 00000000 00000008
83f7bf84  00000000 83f7bf88 83f7bf88 00000000
83f7bf94  00000000 00000000 00000000 00000000
83f7bfa4  00000000 807c8c38 807c4c38 00000000
kd> dd PspCidTable
83f7bf34  8d404008 00000000 80000020 00000101
83f7bf44  80000320 80000024 00000000 00000000
83f7bf54  00000000 00000000 00000000 00000113
83f7bf64  00000000 00000000 83f2c35a 00000000
83f7bf74  00000000 00000000 00000000 00000008
83f7bf84  00000000 83f7bf88 83f7bf88 00000000
83f7bf94  00000000 00000000 00000000 00000000
83f7bfa4  00000000 807c8c38 807c4c38 00000000
kd> dt _HANDLE_TABLE 8d404008
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0x9a8d6001
   +0x004 QuotaProcess     : (null)
   +0x008 UniqueProcessId  : (null)
   +0x00c HandleLock       : _EX_PUSH_LOCK
   +0x010 HandleTableList  : _LIST_ENTRY [ 0x8d404018 - 0x8d404018 ]
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : (null)
   +0x020 ExtraInfoPages   : 0n0
   +0x024 Flags            : 1
   +0x024 StrictFIFO       : 0y1
   +0x028 FirstFreeHandle  : 0xf98
   +0x02c LastFreeHandleEntry : 0x9a8d7808 _HANDLE_TABLE_ENTRY
   +0x030 HandleCount      : 0x255
   +0x034 NextHandleNeedingPool : 0x1000
   +0x038 HandleCountHighWatermark : 0x304
kd> dt _HANDLE_TABLE 8d404008
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0x9a8d6001
   +0x004 QuotaProcess     : (null)
   +0x008 UniqueProcessId  : (null)
   +0x00c HandleLock       : _EX_PUSH_LOCK
   +0x010 HandleTableList  : _LIST_ENTRY [ 0x8d404018 - 0x8d404018 ]
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : (null)
   +0x020 ExtraInfoPages   : 0n0
   +0x024 Flags            : 1
   +0x024 StrictFIFO       : 0y1
   +0x028 FirstFreeHandle  : 0xf98
   +0x02c LastFreeHandleEntry : 0x9a8d7808 _HANDLE_TABLE_ENTRY
   +0x030 HandleCount      : 0x255
   +0x034 NextHandleNeedingPool : 0x1000
   +0x038 HandleCountHighWatermark : 0x304
kd> dd 0x9a8d6000
ReadVirtual: 9a8d6000 not properly sign extended
9a8d6000  8d405000 9a8d7000 00000000 00000000
9a8d6010  00000000 00000000 00000000 00000000
9a8d6020  00000000 00000000 00000000 00000000
9a8d6030  00000000 00000000 00000000 00000000
9a8d6040  00000000 00000000 00000000 00000000
9a8d6050  00000000 00000000 00000000 00000000
9a8d6060  00000000 00000000 00000000 00000000
9a8d6070  00000000 00000000 00000000 00000000
kd> dd 0x9a8d6000
ReadVirtual: 9a8d6000 not properly sign extended
9a8d6000  8d405000 9a8d7000 00000000 00000000
9a8d6010  00000000 00000000 00000000 00000000
9a8d6020  00000000 00000000 00000000 00000000
9a8d6030  00000000 00000000 00000000 00000000
9a8d6040  00000000 00000000 00000000 00000000
9a8d6050  00000000 00000000 00000000 00000000
9a8d6060  00000000 00000000 00000000 00000000
9a8d6070  00000000 00000000 00000000 00000000
kd> dq 8d405000+8*0x87
ReadVirtual: 8d405438 not properly sign extended
8d405438  00000000`87fd0571 00000000`87ac9b31
8d405448  00000000`87ac9609 00000000`87ac7cc1
8d405458  00000000`87ad1971 00000000`87ad21c1
8d405468  00000000`87ad8bf9 00000000`87ad8281
8d405478  00000000`87ad98b1 00000000`87ad5031
8d405488  0000090c`00000000 00000000`877a7b01
8d405498  000003bc`00000000 000003f4`00000000
8d4054a8  00000af4`00000000 00000000`87b51031
kd> dq 8d405000+8*0x87
ReadVirtual: 8d405438 not properly sign extended
8d405438  00000000`87fd0571 00000000`87ac9b31
8d405448  00000000`87ac9609 00000000`87ac7cc1
8d405458  00000000`87ad1971 00000000`87ad21c1
8d405468  00000000`87ad8bf9 00000000`87ad8281
8d405478  00000000`87ad98b1 00000000`87ad5031
8d405488  0000090c`00000000 00000000`877a7b01
8d405498  000003bc`00000000 000003f4`00000000
8d4054a8  00000af4`00000000 00000000`87b51031
 
 
kd> dq 8d405000    //0~511句柄表
ReadVirtual: 8d405000 not properly sign extended
8d405000  fffffffe`00000000 00000000`865e8739
8d405010  00000000`865e8461 00000000`86617021
8d405020  00000000`8661f021 00000000`865e9a49
8d405030  00000000`86637d49 00000000`86637a71
8d405040  00000000`86633d49 00000000`86633a71
8d405050  00000000`8662fd49 00000000`8662fa71
8d405060  00000000`8661fd49 00000000`8661fa71
8d405070  00000000`8661bd49 00000000`8661ba71
 
kd> dt _OBJECT_HEADER 865e8738-0x18  //记得清空后三个二进制位
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n149
   +0x004 HandleCount      : 0n3
   +0x004 NextToFree       : 0x00000003 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0x7 ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0 ''
   +0x00f Flags            : 0x2 ''
   +0x010 ObjectCreateInfo : 0x83f6fcc0 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x83f6fcc0 Void
   +0x014 SecurityDescriptor : 0x8d404d96 Void
   +0x018 Body             : _QUAD
kd> dq 8d405000    //0~511句柄表
ReadVirtual: 8d405000 not properly sign extended
8d405000  fffffffe`00000000 00000000`865e8739
8d405010  00000000`865e8461 00000000`86617021
8d405020  00000000`8661f021 00000000`865e9a49
8d405030  00000000`86637d49 00000000`86637a71
8d405040  00000000`86633d49 00000000`86633a71
8d405050  00000000`8662fd49 00000000`8662fa71
8d405060  00000000`8661fd49 00000000`8661fa71
8d405070  00000000`8661bd49 00000000`8661ba71
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (6)
雪    币: 950
活跃值: (9946)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
2
谢谢大佬分享.写的详细不错.
2021-12-9 17:30
0
雪    币: 246
活跃值: (4507)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
3
marik
2021-12-9 18:06
0
雪    币: 14
活跃值: (948)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
为啥我替换了对象类型会随机变化呢?cr3一起替换还会蓝屏
2023-4-28 22:48
0
雪    币: 14
活跃值: (948)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
妮可 为啥我替换了对象类型会随机变化呢?cr3一起替换还会蓝屏
知道了 TypeIndex是根据object加密的,单纯复制头部数据不行,要重新计算TypeIndex的值写入
2023-4-29 01:24
0
雪    币: 2
活跃值: (392)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
妮可 知道了 TypeIndex是根据object加密的,单纯复制头部数据不行,要重新计算TypeIndex的值写入
我也这样伪造句柄表了。但是用CE附加的时候就会蓝屏,读写数据是正常的,兄弟你遇见这种情况没有
2023-12-25 17:52
0
雪    币: 300
活跃值: (2532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
mark
2023-12-25 19:23
0
游客
登录 | 注册 方可回帖
返回
//