逆向ObRegisterCallbacks 分析回调表结构

栏目: 数据库 · 发布时间: 5年前

内容简介:1. 因为本人水平有限,某些地方写的可能是错的,也请各位大佬指正,不胜感激。2. 为什么要发这个帖子?

题外话

1. 因为本人水平有限,某些地方写的可能是错的,也请各位大佬指正,不胜感激。

2. 为什么要发这个帖子?

① 因为后期如果我们想对进程线 程搞一些事情的话,需要了解 ObRegisterCallbacks 表的结构。

② 当我去查 ObRegisterCallbacks 表结构资料的时候,全网搜了半小时,也没找到啥能用的,当然这可能是我搜索的姿势不对。

正文

用到的文件: win7 sp1 x64 内核文件ntoskrnl.exe 

用到的工具: IDA 7.0

阅读提醒:

① 以下所有被命名为 p_oft_xx 都表示在缓冲区  offset  为xx位置的变量。

② 为了更清晰的表示结构体在结构体成员前面也加了oft_xx 表示偏移。

ObRegisterCallbacks 函数故名思意,是一个注册对象钩子的函数。

可以让用户定义多个自定义的回调函数,在某些事件即将发生,或者已经发生的时候被触发。

比如进程线程句柄的创建,句柄的拷贝。在时下,经常被用作进程监测,进程防杀。

函数定义

NTSTATUS ObRegisterCallbacks(
POB_CALLBACK_REGISTRATION CallbackRegistration,
PVOID *RegistrationHandle
)
;

参数结构体相关

typedef struct _OB_CALLBACK_REGISTRATION {
USHORT Version;
USHORT OperationRegistrationCount;
UNICODE_STRING Altitude;
PVOID RegistrationContext;
OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
<br>
typedef struct _OB_OPERATION_REGISTRATION {
POBJECT_TYPE *ObjectType;
OB_OPERATION Operations;
POB_PRE_OPERATION_CALLBACK PreOperation;
POB_POST_OPERATION_CALLBACK PostOperation;
} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;

_OB_CALLBACK_REGISTRATION 结构体中,有一个需要注意的成员 OperationRegistrationCount。

这个成员表示注册回调函数的个数。

逆向ObRegisterCallbacks 分析回调表结构

学过内核的大佬们知道,内核有一些表是全局的数组,因为是全局的,所以对表项数目会有要求,比如最多只能注册8项之类的。

那么,看到微软官方的解释,并没有限制注册的数量,而且,注册条目这里类型是USHORT。

也就是说,理论上我们可以注册 65536个回调函数。如果用全局数组,并不现实,推测使用了链表之类的数据结构。

所以,逆向的时候,我从这个角度入手,重点找内存申请的相关操作,确定表结构。

定位到关键位置,开始逆向。

逆向ObRegisterCallbacks 分析回调表结构

首先看一下整个函数的规模,也不是特别大,挺好挺好。

F5一下,看看反汇编的结果。

逆向ObRegisterCallbacks 分析回调表结构

看到这个就有点辣眼睛了,为了让IDA 能够完全发挥功能,定义一波用到的结构体先。

00000000 _OB_CALLBACK_REGISTRATION struc ; (sizeof=0x14, mappedto_1310)
00000000 Version dw ?
00000002 OperationRegistrationCount dw ?
00000004 Altitude UNICODE_STRING ?
0000000C p_RegistrationContext dd ?
00000010 p_OperationRegistration dd ?
00000014 _OB_CALLBACK_REGISTRATION ends
00000014

00000000 _OB_OPERATION_REGISTRATION struc ; (sizeof=0x10, mappedto_1309)
00000000 p_ObjectType dd ?
00000004 Operations dd ?
00000008 PreOperation dd ?
0000000C PostOperation dd ?
00000010 _OB_OPERATION_REGISTRATION ends

继续分析函数功能:

逆向ObRegisterCallbacks 分析回调表结构

和大部分函数一样,上来就是一波参数检查。

检测Version成员,OperationRegistrationCount成员的合法性。

接下来我们看到了一波申请内存的操作。

n_alloc_size = 36 * u_reg_cnt + CallbackRegistration->Altitude.Length + 16;

结合上面的观点,整个表很可能是一个链表之类的数据结构,那么这块内存很可能就是数据成员的大小了。

变量解释:

n_alloc_size: 申请内存的大小

u_reg_cnt: 要注册的回调函数条目数量

CallbackRegistration->Altitude.Length: 参数结构体中unicode字符串的长度

所以上面这个式子用文字来表达就是:

申请内存大小 =36 * 要注册的回调函数条目数量+参数结构体中 unicode 字符串的长度+16

这个很可能是数据结构中,单个数据成员占用内存空间,我们先继续向后看。

逆向ObRegisterCallbacks 分析回调表结构

可以看到上面来了一波赋值而且都是针对刚申请的缓冲区的赋值。

为了方便阅读,我把上图的代码整理了下,如下:

*(_WORD *)p_alloc_buf1 = 256; //OB_FLT_REGISTRATION_VERSION 这里是version成员
*((_DWORD *)p_alloc_buf1 + 1) = CallbackRegistration->p_RegistrationContext;

//这里是一个 UNICODE_STRING 结构体
n_str_len = CallbackRegistration->Altitude.Length;
*((_WORD *)p_alloc_buf1 + 4) = n_str_len; // UNICODE_STRING Length 成员
*((_WORD *)p_alloc_buf1 + 5) = n_str_len; // UNICODE_STRING MaximumLength成员
p_u_str = (char *)p_alloc_buf1 + n_alloc_size - n_str_len; // 在缓冲区的最后部分存字符串
*((_DWORD *)p_alloc_buf1 + 3) = p_u_str; // UNICODE_STRING Buffer 成员

n_str_len1 = *((unsigned __int16 *)p_alloc_buf1 + 4);
memcpy(p_u_str, CallbackRegistration->Altitude.Buffer, n_str_len1); // 拷贝字符串到内存

变量解释:

p_alloc_buf1: 刚刚申请的内存首地址

n_str_len: 参数中unicode字符串长度

p_u_str:unicode 字符串缓冲区的地址

那么,如果这个缓冲区存储的是结构体数据的话,部分成员已经被分析出来了,我把这个这个结构体命名为  St_Call_Back_Table ,如下:

struct St_Call_Back_Table
{
oft_0: USHORT Version; // _OB_CALLBACK_REGISTRATION结构体Version成员
oft_2: unknow //成员未知
oft_4: PVOID RegistrationContext; //_OB_CALLBACK_REGISTRATION结构体 RegistrationContext

//MaximumLength 和 Length 数值一样 都是字符串长度
oft_8: UNICODE_STRING Altitude //_OB_CALLBACK_REGISTRATION结构体 Altitude 成员

oft_16 --- oft_last unknow //成员未知

//在缓冲区最后 sizeof(p_alloc_buf1) - altitude.length的位置
oft_last: wchar_t[xx]; //unicode字符串 当前结构体中 Altitude.buffer 成员指向的位置
}

逆向ObRegisterCallbacks 分析回调表结构

上面一波赋值完之后,有一个if语句块,针对 oft_2  oft_28 两个成员进行了操作,暂时先不理,看下面 else块的代码。

逆向ObRegisterCallbacks 分析回调表结构

下面是上面图片的完整代码:

else
{
v23 = 0;
p_oft_48 = (char *)p_alloc_buf1 + 48;

while ( 1 )
{
p_oper_reg = (_OB_OPERATION_REGISTRATION *)(v23 + p_call_back_reg->p_OperationRegistration);
p_oper_reg1 = (_OB_OPERATION_REGISTRATION *)(v23 + p_call_back_reg->p_OperationRegistration);//

// 检测数据成员是否合法
if ( !p_oper_reg->Operations || !(*(_BYTE *)(*(_DWORD *)p_oper_reg->p_ObjectType + 42) & 0x40) )
break;

// 检测回调函数 调用前 指针是否合法
if ( p_oper_reg->PreOperation )
{
if ( !MmVerifyCallbackFunction(p_oper_reg->PreOperation) )
goto LABEL_21;
p_oper_reg = p_oper_reg1;
}

// 检测调用后指针是否合法
else if ( !p_oper_reg->PostOperation )
{
break;
}
if ( p_oper_reg->PostOperation )
{
if ( !MmVerifyCallbackFunction(p_oper_reg->PostOperation) )
{
LABEL_21:
u_ret = -1073741790;
goto LABEL_22;
}
p_oper_reg = p_oper_reg1;
}
.......
.......
}

else 块中进行了一波检测,检测了成员的合法性,只要有一个条件不满足就跳出。

逆向ObRegisterCallbacks 分析回调表结构

首先我把上面的图一步步进行拆解分析。

首先我们看到了一波赋值,这里赋值了36个字节的数据:

36

36

36

这个数字是不是很熟悉?

申请内存大小 = 36 *  要注册的回调函数条目数量 +  参数结构体中unicode字符串的长度 + 16;

结合上下文的汇编,得出这是个单独的结构体,具体分析代码稍后写出来。当前结构体在缓冲区 oft_16 的位置处, p_oft_48 - sizeof(DWORD) * 8 = p_oft_48 - 32 = p_oft_16

//这里是对缓冲区的又一波赋值 p_oft_48 表示在缓冲区首地址 + 48 的位置
// 这里的 p_oft_48定义为一个 DWORD* 指针 所以 - 1 就是 - sizeof(DWORD) 请知悉
//p_oft_48 - 8 是表示 p_oft_48 - sizeof(DWORD) * 8

*p_oft_48 = 0; //固定为0
*(p_oft_48 - 7) = p_oft_48 - 8; //指向 本结构首地址
*(p_oft_48 - 8) = p_oft_48 - 8; //指向 本结构首地址
*(p_oft_48 - 6) = p_oper_reg->Operations; // _OB_OPERATION_REGISTRATION Operations成员
*(p_oft_48 - 4) = p_alloc_buf1; //缓冲区首地址
*(p_oft_48 - 3) = *(_DWORD *)p_oper_reg->p_ObjectType; // _OB_OPERATION_REGISTRATION ObjectType 成员
*(p_oft_48 - 2) = p_oper_reg->PreOperation; //_OB_OPERATION_REGISTRATION PreOperation 成员
*(p_oft_48 - 1) = p_oper_reg->PostOperation; // _OB_OPERATION_REGISTRATION PostOperation成员

仔细观察这里有一个成员没有被赋值 *(p_oft_48 - 5) 也就是缓冲区 oft_28在这里没有出现,前面if 块中有对这个成员的操作 *(p_oft_48 - 5) 就是 if 块里面的变量, p_oft_28 稍后分析。

逆向ObRegisterCallbacks 分析回调表结构

我把这个结构命名为 St_Reg_Info

//结构体大小36字节 9个成员 有些成员的功能会在下面进一步分析

struct St_Reg_Info
{
oft_0: PUNCHAR p_self0; //指向本结构首地址 怀疑跟LIST_ENTRY有关
oft_4: PUNCHAR p_self1; //指向本结构首地址
oft_8: OB_OPERATION Operations; //_OB_OPERATION_REGISTRATION Operations成员
oft_12: DWORD dw_flag; //待分析
oft_16: PUCHAR p_call_back_self; //指向缓冲区 St_Call_Back_Table 首地址
oft_20: POBJECT_TYPE ObjectType; //_OB_OPERATION_REGISTRATION ObjectType成员
oft_24: POB_PRE_OPERATION_CALLBACK PreOperation; //_OB_OPERATION_REGISTRATION PreOperation成员
oft_28: POB_POST_OPERATION_CALLBACK PostOperation; //_OB_OPERATION_REGISTRATION PostOperation成员
oft_32: DWORD zero_flag; //直接赋值为0 含义暂时不知道
}

赋值完一波结构体后,调用了一个参数 ObpInsertCallbackByAltitude, 顾名思义是一个插入链表的函数,

arg1   St_Reg_Info::p_self0

arg2  *(St_Reg_Info:: p_ObjectType)

//插入链表 
u_ret = ObpInsertCallbackByAltitude(p_oft_48 - 8, *(p_oft_48 - 3));

if ( u_ret < 0 )
{
goto LABEL_22;
}

进去函数看看

逆向ObRegisterCallbacks 分析回调表结构

看到了一个教科书般的链表插入动作。

由此可以判定St_Reg_Info 结构体中p_self实际上是个LIST_ENTRY结构体。

更新一下分析结果:

//结构体大小36字节 9个成员 有些成员的功能会在下面进一步分析

struct St_Reg_Info
{
oft_0: LIST_ENTRY list_entry; //回调函数链表项
oft_8: OB_OPERATION Operations; //_OB_OPERATION_REGISTRATION Operations成员
oft_12: DWORD dw_flag; //待分析
oft_16: PUCHAR p_call_back_self; //指向缓冲区 St_Call_Back_Table 首地址
oft_20: POBJECT_TYPE ObjectType; //_OB_OPERATION_REGISTRATION ObjectType成员
oft_24: POB_PRE_OPERATION_CALLBACK PreOperation; //_OB_OPERATION_REGISTRATION PreOperation成员
oft_28: POB_POST_OPERATION_CALLBACK PostOperation; //_OB_OPERATION_REGISTRATION PostOperation成员
oft_32: DWORD zero_flag; //直接赋值为0 含义暂时不知道
}

链表成功插入后oft_2 的位置进行了一个递增操作,猜测是表示成功,因为缓冲区在开始的时候被赋值为0,所以这个值是从0开始累积的。

++*((_WORD *)p_alloc_buf1 + 1); // 缓冲区偏移为2的数递增

逆向ObRegisterCallbacks 分析回调表结构

接下来指针向后轮询,开始新一轮的插入操作。

逆向ObRegisterCallbacks 分析回调表结构

此刻在结构体中还有一个成员未分析 ,St_Reg_Info 偏移为12位置的  dw_flag;

逆向ObRegisterCallbacks 分析回调表结构

根据 if 块的代码分析,可以知道dw_flag成员在一个循环里,只要注册成功,这个值都会最低位置1 。所以猜测是一个标志位,表示是否有效。

更新一波分析到的结构体:

struct St_Call_Back_Table
{
oft_0: USHORT Version; // _OB_CALLBACK_REGISTRATION结构体Version成员
oft_2: USHORT u_valid_cnt; //有效注册条目 注册失败的条目不算
oft_4: PVOID RegistrationContext; //_OB_CALLBACK_REGISTRATION结构体 RegistrationContext

//MaximumLength 和 Length 数值一样 都被赋值为字符串长度
oft_8: UNICODE_STRING Altitude //_OB_CALLBACK_REGISTRATION结构体 Altitude 成员

//以下结构主要看 注册条目有几个 结构大小为36个字节
//n_reg_cnt 表示注册条目 注意这里不是 n_valid_cnt 有效条目
oft_16: St_Reg_Info st_reg_info[u_reg_cnt];

//在缓冲区最后 sizeof(p_alloc_buf1) - altitude.length的位置
oft_last: wchar_t[xx]; //unicode字符串 当前结构体中 Altitude.buffer 成员指向的位置
}

struct St_Reg_Info
{
oft_0: LIST_ENTRY list_entry; //回调函数链表项
oft_8: OB_OPERATION Operations; //_OB_OPERATION_REGISTRATION Operations成员
oft_12: DWORD dw_flag; //标志位 最低位为1 表示有效
oft_16: PUCHAR p_call_back_self; //指向缓冲区 St_Call_Back_Table 首地址
oft_20: POBJECT_TYPE ObjectType; //_OB_OPERATION_REGISTRATION ObjectType成员
oft_24: POB_PRE_OPERATION_CALLBACK PreOperation; //_OB_OPERATION_REGISTRATION PreOperation成员
oft_28: POB_POST_OPERATION_CALLBACK PostOperation; //_OB_OPERATION_REGISTRATION PostOperation成员
oft_32: DWORD dw_zero_flag; //直接赋值为0 含义暂时不知道
}

最后做一波验证

申请内存大小=36*要注册的回调函数条目数量 +参数结构体中 unicode 字符串的长度+16;

这里36 是结构体 St_Reg_Info 的大小,16是结构体 St_Call_Back_Table 中前4个成员占用的总字节数。

Bingo!!!!验证成功!

虽然下面还有一些针对成员的操作,但是时间原因不多分析了(主要原因是懒)。

本来是想上传IDB文件的,但是压缩了后还是有14M多,附件最多8M,所以表示遗憾了。

如果帖子有什么错误也请各位大佬指出明示,毕竟我自己水平有限。

希望本贴可以抛砖引玉,能有更多的人加入到对这个表的分析也是好的。

2019.5.9 于武汉

- End -

逆向ObRegisterCallbacks 分析回调表结构

看雪ID:上海刘一刀     

https://bbs.pediy.com/user-807993.htm

本文由看雪论坛  上海刘一刀    原创

转载请注明来自看雪社区

热门图书推荐

逆向ObRegisterCallbacks 分析回调表结构   立即购买!

逆向ObRegisterCallbacks 分析回调表结构

公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com

点击下方“阅读原文”,查看更多干货


以上所述就是小编给大家介绍的《逆向ObRegisterCallbacks 分析回调表结构》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

《电脑报》2005年合订本(上下)

《电脑报》2005年合订本(上下)

电脑报社主编 / 西南师范大学出版社 / 2006-1 / 45.00元

全套上、下两册,浓缩2005年电脑报精华文章;附录包含70余篇简明IT应用指南,涵盖软件、硬件、数码、网络四大领域,配赠权威实用的2005-2006中国计算机年鉴光盘,近1.4GB海量信息与资源超值奉献,提供2005-2006全系列硬件、数码产品资讯,兼具知识性与资料性,连结购买每年《电脑报合订本》,你将拥有一套完整的实用大型电脑文库。一起来看看 《《电脑报》2005年合订本(上下)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具