首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
1
1
逆向ObReferenceObjectByHandle得到EPROCESS和修改句柄权限
发表于: 2026-1-18 18:05
1827
逆向ObReferenceObjectByHandle得到EPROCESS和修改句柄权限
mb_binusgki
2026-1-18 18:05
1827
# 确定大致方向  首先先用微软官方函数获取句柄信息   验证`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`跳转过来的, 继续向上寻找   在这里发现`rsi`被`rdx`赋值, 然后又回到了`rsi`, 继续向上跟踪发现`rsi`来自`[rax+8]`, 继续向上发现`rax`是来自`nt!ExpLookupHandleTableEntry`  分析`nt!ExpLookupHandleTableEntry`可以发现这就是取句柄信息的核心算法, 首先判断传入句柄是否合法, 其次根据句柄表后2位判断是几级结构的句柄表, 共有三个层次 ``` 一级结构算法: 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],rax`对`ProcessObject`进行了写入, 明显当前`r12`=`ProcessObject`=`EPROCESS`, 继续向上跟踪发现`rax`被`lea rax,[rbx+30h]`赋值, 知道`EPROCESS`-30刚好就回到了`_OBJECT_HEADER`头部, 所以`rbx`=`_OBJECT_HEADER`, 需要继续向上跟踪`rbx`的由来...  继续跟踪发现`rbx`由`rax`赋值, 并且经过`sar rbx,10h`和`and rbx,0FFFFFFFFFFFFFFF0h`运算, 这也是重要的算法, 需要一并反推记录, 继续向上追踪`rax`由来  `rax`被`r14`赋值, 继续追踪`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` # 代码实现 下面直接上代码实现: ``` //一级表 // 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; } ``` ``` //自己实现的获取权限地址方法 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-3-15 16:30 被mb_binusgki编辑 ,原因: 补充内容
#调试逆向
#系统底层
#问题讨论
#其他内容
收藏
・
1
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
我的小拇指啊
感谢你的积极参与,期待更多精彩内容!
2026-1-22 19:54
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
mb_binusgki
雪 币:
390
能力值:
( LV1,RANK:0 )
在线值:
发帖
29
回帖
6
粉丝
22
关注
私信
mb_binusgki
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
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_binusgki
29
发帖
6
回帖
0
RANK
关注
私信
他的文章
[分享]注册表回调内核通信
379
[分享]简单编译器实现
1869
[分享]Lexer简单词法解析器入门
1233
[分享]vm解释器入门2
1288
[分享]vm解释器入门
1587
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部