首页
社区
课程
招聘
[原创]x64逆向热身练习: nt!ObRegisterCallbacks回调的检测与摘除
发表于: 2020-11-17 20:59 10650

[原创]x64逆向热身练习: nt!ObRegisterCallbacks回调的检测与摘除

2020-11-17 20:59
10650

     以前自学了<<C++反汇编与逆向分析揭秘>>,书中讲得很不错,不过:里面全是x86的讲解。x64上,寄存器位数进行了扩充,C/C++函数传参也与x86有所不同, 但其中的分析思路基本上还是一致的。目前毕竟用64位操作系统的机器太多了,所以有必要从32位逆向过渡到64位。目前,我就以逆向内核API:nt! ObRegisterCallbacks分析其中的数据结构和实现逻辑,然后实现对 nt!ObRegisterCallbacks回调的检测与摘除, 对x64逆向进行一次热身练习。

一. 目标操作系统: Win7 X64 内核版本号:7600 (其它系统分析方法大同小异,这里仅仅是练习)

二. 对nt!ObRegisterCallbacks的逆向分析过程:

msdn中已给出函数原型:

2. 逆向分析过程,为了不断提高对汇编代码的阅读能力,我这里不用F5,而是IDA Pro静态反汇编分析,如果遇到疑问再动态调试。

(1)调试中相关的数据结构:

(2)对ObRegisterCallbacks的内部实现的静态分析过程

(3)通过细细阅读,可以逆推出RegistratioHandle所指向的缓冲区数据结构:


其中OBP_CALLBACK_ENTRY为回调项信息,数据结构如下:

整个RegistratioHandle所指向的缓冲区尾部的最后CallbackRegistration->Altitude->Length字节保存: pObpCallbackInfo->Altitude->Buffer所指向的UNICODE串缓冲区


(4)ObRegisterCallbacks等价的C实现:

这里:我产生了一个小小的疑问:

为什么是: pObpCallbackInfo->ObpCallbackEntries[i].pListObjectTypeEntry=CallbackRegistaion->OperationRegistration[i].OjbectType->TypeList.Flink?而不是:  pObpCallbackInfo->ObpCallbackEntries[i].pListObjectTypeEntry=&CallbackRegistaion->OperationRegistration[i].OjbectType->TypeList?难道Flink还指向得有其它对象,或者就是指向ObjectType自身?通过对nt!ObRegisterCallbacks调试观察,找到了答案:


果然是第2种猜测: pObpCallbackInfo->ObpCallbackEntries[i].pListObjectTypeEntry=&CallbackRegistaion->OperationRegistration[i].OjbectType->TypeList,而TypeList是OBJECT_TYPE的第1个成员,所以: 实际上就是pObpCallbackInfo->ObpCallbackEntries[i].pListObjectTypeEntry就是指向OBJECT_TYPE自身


(5)要弄清楚回调项 pObpCallbackInfo->ObpCallbackEntries[i]是如何插入OBJECT_TYPE中的,需要对函数nt!ObpInsertCallbackByAltitude进行逆向分析:

可以看出: 内部会遍历ObjectType->CallbackList按Altitude降序排列的 链,比较Altitude,如果Altitude不重复:就把pObpCallbackEntry插入ObjectType->CallbackLis链,并且始终保持ObjectType->CallbackList链相关的回调的Altitude按降序排列,最终返回STATUS_SUCCESS,如果重复了,最终会返回: STATUS_FLT_INSTANCE_NAME_COLLISION. 整个原子操作过程是由:ObjectType->TypeLock锁来控制的,返回前会检查锁的状态,在必要时会释放锁,

然后,检查是否需要递送内核模式APC,在必要时会递送,最后返回。

(6)nt!ObpInsertCallbackByAltitude对应的C实现:

三. ObRegisterCallbacks回调的检测及摘除:

通过前面逆向分析,已经弄清楚了WIN7 X64 7600上ObRegisterCallbacks的ObjectType->CallbackList回调项数据结构及插入过程,现在来实现两个功能:

通过遍历*PsProcessType,*PsThreadType对象类型的CallbackList链,检测系统中ObRegisterCallbacks已注册的回调


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

最后于 2020-11-28 11:25 被低调putchar编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (13)
雪    币: 13493
活跃值: (6909)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
写的不错,其实枚举回调链表还能写的更简单一点,因为通过正规ObRegisterCallback注册的回调数组的ObjectType需要一致性,也就是说PsProcessType里的链表只能是PsProcessType类型的回调,PsThreadType的同理。个人感觉 if (pObjType == *PsProcessType) 这句判断完全可以剔除
2020-11-18 13:34
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
hhkqqs 写的不错,其实枚举回调链表还能写的更简单一点,因为通过正规ObRegisterCallback注册的回调数组的ObjectType需要一致性,也就是说PsProcessType里的链表只能是PsPro ...
嗯!我主要考率到: nt!ObpInsertCallbackByAltitude内部是把 &pObpCallbackInfo->>ObpCallbackEntries[j].CallbackLinks这个PLIST_ENTRY插入了(*PsProcessType)->CallbackList或者(*PsThreadType)->CallbackList,所以通过ObjectType->CallbackList的CONTAINING_RECORD:得到的仅仅是: &pObpCallbackInfo->>ObpCallbackEntries[j],其中的对象类型毫无疑问:与*PsProcessTypeh或*PsThread是一致的。

不过pObpCallbackInfo->ObpCallbackEntries[j].pSelf指向的就是:pObpCallbackInfo了,即:RegistrationHandle, pObpCallbackInfo->Inserted是数组ObpCallbackEntries[]个数, 如果为2的话,
索引不同的ObpCallbackEntries[]元素就会有不同的对象类型:*PsProcessType,*PsThreadType了(它们具有相同的RegistrationHandle和Altitude名,分别监控进程和线程)。所以用了嵌套循环:代码就显得复杂了。

其实要用单循环也可以做到,只是与用户层交互的数据结构就要调整下!这样可以把内核层的算法时间复杂度从o(n^2)降低到o(n),另外也便于理解。
练习嘛!我循序渐进,逐步提高!
2020-11-18 14:18
0
雪    币: 13493
活跃值: (6909)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
低调putchar 嗯!我主要考率到: nt!ObpInsertCallbackByAltitude内部是把 &pObpCallbackInfo->>ObpCallbackEntries[j].Cal ...

~

最后于 2020-11-18 16:05 被hhkqqs编辑 ,原因:
2020-11-18 14:39
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
hhkqqs 你误解了我的意思,我是指ObpCallbackEntries不可能同时存在PsProcessType和PsThreadType,因为ObRegisterCallback中OB_OPERATION_RE ...

明白你的意思了.OK! 我再跟一下!

最后于 2020-11-20 15:49 被低调putchar编辑 ,原因:
2020-11-18 15:02
0
雪    币: 13493
活跃值: (6909)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
低调putchar 明白你的意思了,也就说:OB_OPERATION_REGISTRATION数组中每个元素的:ObjectType成员必须一致才符合规范。OK! 我再跟一下![em_69]

~

最后于 2020-11-18 15:58 被hhkqqs编辑 ,原因:
2020-11-18 15:16
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
hhkqqs 低调putchar 明白你的意思了,也就说:OB_OPERATION_REGISTRATION数组中每个元素的:ObjectType成员必须一致才符合规范。OK! ...

在虚拟机里调试过了!WIN7 X64 7600上没有这个现象。

数组中分别指定了: *PsProcessType, *PsThreadType

1. 进程过滤


2.线程过滤:



应该是系统不同的原因,毕竟WIN10系统后面做得很烂,坑太多了, 而且这些坑文档中根本就查不到。

前段时间忙项目又装了Center OS虚拟机, 我这台笔记本硬盘空间快不够了,所以我只有把太占空间的

WIN10系统虚拟机移其它机器上了。以后,我准备用另一台笔记本再调试WIN10。


2020-11-18 16:00
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
8
系统版本太多!有坑也正常。
2020-11-18 16:04
0
雪    币: 13493
活跃值: (6909)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
低调putchar 明白你的意思了,也就说:OB_OPERATION_REGISTRATION数组中每个元素的:ObjectType成员必须一致才符合规范。OK! 我再跟一下![em_69]
我好像知道你的枚举应该怎么简化了,我看到你是尝试把所有的handle枚举出来,我刚看了下巨硬的示例代码是允许handle的回调数组存在不同ObjectType的,所以你这个思路本身没问题,但是如果有很多handle都注册了不同ObjectType的数组,你依次遍历Process和Thread的Callback链表就会存在不少重复枚举的判断,影响效率。我觉得可以不考虑使用handle的信息,直接遍历Process和Thread的CallbackList就够了,在用户层按handle值排序就能看出每个handle分别注册了什么样的回调
2020-11-18 16:04
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
10
hhkqqs 我好像知道你的枚举应该怎么简化了,我看到你是尝试把所有的handle枚举出来,我刚看了下巨硬的示例代码是允许handle的回调数组存在不同ObjectType的,所以你这个思路本身没问题,但是如果有很 ...
对!这样内核层运行效率更高!练习嘛!我逐步提高!
2020-11-18 16:07
0
雪    币: 13493
活跃值: (6909)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
低调putchar 对!这样内核层运行效率更高!练习嘛!我逐步提高![em_65]
我又在win10下试了一次,好像是某杀软注册的线程回调和我的有冲突,win10下ObRegisterCallbacks的实现几乎和win7一模一样不可能有bug的
2020-11-18 18:02
0
雪    币: 2674
活跃值: (2304)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
12
hhkqqs 我又在win10下试了一次,好像是某杀软注册的线程回调和我的有冲突,win10下ObRegisterCallbacks的实现几乎和win7一模一样不可能有bug的[em_10]
不同内核版本的系统未公开的数据结构不能保证完全相同,所以,很多的Rookit恶意代码一般都要对系统及内核版本进行判断的,在支持的系统上就开干。不过,我这里纯粹是感兴趣,以技术学习为目的,不干坏事的。再说,要加驱的话现在的HIPS都要拦截报警的!
2020-11-18 20:13
0
雪    币: 8472
活跃值: (4996)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13

没有打补丁,代码无法在win7sp1下运行,我开启测试模式禁用签名都不行

win7sp1   callbacklist偏移也是0xc0但是打印出回调是不正确的,还有那里有变化了么?

最后于 2022-10-5 23:16 被大道在我编辑 ,原因:
2022-10-5 22:49
0
雪    币: 226
活跃值: (105)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
图文并茂,深入浅出,刻苦学习!
2022-10-8 01:40
0
游客
登录 | 注册 方可回帖
返回