首页
社区
课程
招聘
[学习]逆向ObReferenceObjectByHandle得到EPROCESS和修改句柄权限
发表于: 2026-1-18 18:05 643

[学习]逆向ObReferenceObjectByHandle得到EPROCESS和修改句柄权限

2026-1-18 18:05
643

确定大致方向

首先先用微软官方函数获取句柄信息

验证ProcessObject可以发现+0x480的位置刚好就是ImageFileName, 说明这就是EPROCESS对象

可以正确获取EPORCESS对象和句柄的权限, 但是尝试修改权限会发现并不会被修改成功, 因为它只是将权限copy出来了, 并不是真正的权限地址

于是即可开始逆向这个函数, 目标: 找出权限是从哪里取出来的? 以及如何根据句柄取到EPROCESS对象

逆向分析如何得到OBJECT_HANDLE_INFORMATION

在此处给info下一个访问断点ba w8 info

发现在mov dword ptr [rax+4],esi断了下来, 此时查看寄存器rsi刚好等于0x12345是设置的权限, 此时需要找到是什么地方写入rsi

继续跟踪发现mov rsi,qword ptr [rsp+50h]rsi进行了写入, 并且进一步跟踪发现是由上面jne跳转过来的, 继续向上寻找

在这里发现rsirdx赋值, 然后又回到了rsi, 继续向上跟踪发现rsi来自[rax+8], 继续向上发现rax是来自nt!ExpLookupHandleTableEntry

分析nt!ExpLookupHandleTableEntry可以发现这就是取句柄信息的核心算法, 首先判断传入句柄是否合法, 其次根据句柄表后2位判断是几级结构的句柄表, 共有三个层次

1
2
3
一级结构算法: poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+handle*0x4+0x8
二级结构算法: poi(poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+(handle>>0x10)*0x8-0x1)+(handle&0x3FF)*0x4+0x8
三级结构算法: poi(poi(poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+((handle>>0xA)>>0x9)*0x8-0x2)+((handle>>0xA)&0x1FF)*0x8)+(handle&0x3FF)*0x4+0x8

根据分析结果回推可以得到三级结构的各个算法如上所示

逆向分析如何得到_EPROCESS

继续分析如何得到_EPROCESS对象, 对ProcessObject下一个访问断点

可以发现断点被触发, 在mov qword ptr [r12],raxProcessObject进行了写入, 明显当前r12=ProcessObject=EPROCESS, 继续向上跟踪发现raxlea rax,[rbx+30h]赋值, 知道EPROCESS-30刚好就回到了_OBJECT_HEADER头部, 所以rbx=_OBJECT_HEADER, 需要继续向上跟踪rbx的由来...

继续跟踪发现rbxrax赋值, 并且经过sar rbx,10hand rbx,0FFFFFFFFFFFFFFF0h运算, 这也是重要的算法, 需要一并反推记录, 继续向上追踪rax由来

raxr14赋值, 继续追踪r14

继续跟踪得知r14来自[rsp+30], [rsp+30]又来自rcx, rcx又来自[rax], 而rax来自call nt!ExpLookupHandleTableEntry, 经过上面分析ExpLookupHandleTableEntry得知[rax]+8=OBJECT_HANDLE_INFORMATION, 而对于EPROCESS则是用的[rax], 于是可以反解出算法为((poi(rax)>>0x10)&0x0FFFFFFFFFFFFFFF0)=_OBJECT_HEADER

代码实现

下面直接上代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//一级表
// poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+handle*0x4
LONG_PTR GetObjectHandleInformation0(ULONG_PTR handle)
{
    return poi(poi(poi(__readgsqword(0x188)+0x0B8)+0x418)+0x8)+handle*0x4;
}
//二级表
//poi(poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+(handle>>0x10)*0x8-0x1)+(handle&0x3FF)*0x4
LONG_PTR GetObjectHandleInformation1(ULONG_PTR handle)
{
    return poi(poi(poi(poi(__readgsqword(0x188)+0x0B8)+0x418)+0x8)+(handle>>0x10)*0x8-0x1)+(handle&0x3FF)*0x4;
}
//三级表
// poi(poi(poi(poi(poi(gs:[0x188]+0x0B8)+0x418)+0x8)+((handle>>0xA)>>0x9)*0x8-0x2)+((handle>>0xA)&0x1FF)*0x8)+(handle&0x3FF)*0x4
LONG_PTR GetObjectHandleInformation2(ULONG_PTR handle)
{
    return poi(poi(poi(poi(poi(__readgsqword(0x188)+0x0B8)+0x418)+0x8)+((handle>>0xA)>>0x9)*0x8-0x2)+((handle>>0xA)&0x1FF)*0x8)+(handle&0x3FF)*0x4;
}
//获取权限地址
void GetObjectHandleInformation(ULONG_PTR handle, PVOID** object,OBJECT_HANDLE_INFORMATION** info)
{
    //句柄表地址
    ULONG_PTR handleTable = GetHandleTable(handle);
    //表等级
    ULONG level = handleTable & 0x3;
    LONG_PTR var1 = 0;
    if (level == 0) {
        PZY_PRINT("level=0");
        var1 = GetObjectHandleInformation0(handle);
    }
    else if (level == 1) {
        PZY_PRINT("level=1");
        var1 = GetObjectHandleInformation1(handle);
    }
    else if (level == 2) {
        PZY_PRINT("level=2");
        var1 = GetObjectHandleInformation2(handle);
    }
    //处理info
    UINT_PTR tempInof = (UINT_PTR)((UCHAR*)var1 + 0x8);
    *info = tempInof;
    //处理Object
    UINT_PTR tempObject = (((INT64)poi(var1) >> 0x10) & 0x0FFFFFFFFFFFFFFF0);
    *object = tempObject;
}
1
2
3
4
5
//自己实现的获取权限地址方法
OBJECT_HANDLE_INFORMATION* myInfo= { 0 };
PVOID* EPROCESS = 0;
GetObjectHandleInformation(handle, &EPROCESS, &myInfo);
PZY_PRINT("EPROCESS=%llX GrantedAccess=%llX HandleAttributes=%llX", EPROCESS,myInfo->GrantedAccess, myInfo->HandleAttributes);

取出的地址就是储存权限的真是地址, 修改此地址权限直接影响当前句柄权限

总结

经过后续观察发现, 系统先会得到全局句柄表 方式: 获取当前线程_KTHREAD, 再由当前线程+0x220 Process找到当前进程, 当前进程+0x418 ObjectTable得到句柄表_HANDLE_TABLE, 句柄表继续偏移+0x008 TableCode得到句柄入口_HANDLE_TABLE_ENTRY, 继续偏移+0x000 InfoTable得到一个加密后的地址, 这个地址解密后是一个具体的对象, 这个具体对象是根据传入的参数而定的, 常用的有事件对象/文件对象/进程对象/线程对象, 其他的我还没摸到, 参考下图:

图片描述

这些e开头的数据就是解密前的数据, 这些都是一个个_HANDLE_TABLE_ENTRY


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2026-1-19 14:49 被mb_binusgki编辑 ,原因: 补充内容
收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 230
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
后续还有一个问题就是 gs:[188h]+0x0B8 = gs:[188h]+0x220 这两个值是相同的 也就是_KTHREAD+B8也可以取得Process, _KTHREAD+220的值等同, 并且系统也是用的B8而不是220, 但根据_KTHREAD+0x098 ApcStateFill [43] UChar来看 ApcStateFill数组中包含了Process, 就是不懂ApcStateFill是什么
2026-1-19 01:49
0
游客
登录 | 注册 方可回帖
返回