4

[原创]ObCallback 回调钩子检测

Fypher 2013-12-20 13:58 11679
在 PatchGuard 的摧残下,通过 ObRegisterCallbacks 函数注册回调钩子已经成了 RK/ARK 中的主流技术之一。注册回调钩子的具体做法可以参考MSDN上的示例代码:http://code.msdn.microsoft.com/windowshardware/ObCallback-Sample-67a47841

今天研究了一下检测这类回调钩子的方法,发出来跟大家分享。本人水平有限,错漏之处还请大家指正。

首先关注的还是注册回调钩子时调用的 ObRegisterCallbacks 函数,MSDN上的函数声明如下:

NTSTATUS ObRegisterCallbacks(
  _In_   POB_CALLBACK_REGISTRATION CallBackRegistration,
  _Out_  PVOID *RegistrationHandle
);


也就是说调用该函数后,我们会得到一个神奇的 RegistrationHandle 指针,而该指针指向的数据结构必然用于描述本次注册的回调钩子(否则 ObUnRegisterCallbacks 就摘不掉钩子了)。

经过一番逆向和调试,发现这个数据结构由三个部分顺序组织在内存中:

Part1:
钩子数据结构的头部信息,如版本号,CallbackOperator 个数等

Part2:(这个部分的长度取决于该回调钩子包含多少个CallbackOperator)
钩子数据结构的CallbackOperator1 相关信息
钩子数据结构的CallbackOperator2 相关信息
……

Part3:
钩子数据结构的额外信息,如 Altitude 等


具体来讲,用 C 语言可以描述如下:

//
// RegistrationHandle 指向的CallbackNode数据结构定义
//
struct CALLBACK_NODE {
	// 版本号,目前是 0x100, 可通过 ObGetFilterVersion 获取该值
	USHORT		usVersion;

	// 本节点上有多少个 CallbackOperator,即多少个 CallbackBodies
	USHORT		usCallbackBodyCount;

	// 注册回调时设定的 OB_CALLBACK_REGISTRATION.RegistrationContext
	// 即 PreCallbackRoutine 和 PostCallbackRoutine 的参数
	PVOID		pContext;

	// 这里似乎是 0x000C000C/0x00080008 一类的值
	ULONG		ulXXX_000X000X;

	// 指向 Altitude 字符串
	// 注意这个字符串因为最大长度是恒定的,所以不一定以 NULL 结尾
	PWCHAR		wcsAltitude;

	// CALLBACK_BODY 数组,其元素个数为 ulCallbackCount
	struct CALLBACK_BODY CallbackBodies[1];
	
	//
	// CALLBACK_BODY数组后,紧接着 Altitude 字符串和一些其它数据
	//
};


其中的 struct CALLBACK_BODY CallbackBodies[] 数组就是描述该 CallbackNode 上的各个 CallbackOperator 的数据结构:

struct CALLBACK_BODY {

	// 所有的 CALLBACK_NODE 通过这个链表串在一起
	LIST_ENTRY	ListEntry;

	// 注册回调时设定的 OB_OPERATION_REGISTRATION.Operations,
	// 即 OB_OPERATION_HANDLE_CREATE 这些东西
	ULONG		ulOperations;

	// 这里似乎始终是 1
	ULONG		ulXXX_1;

	// 指向该 CallbackBody 对应的 CallbackNode
	struct CALLBACK_NODE* pCallbackNode;

	// 注册回调时设定的 OB_OPERATION_REGISTRATION.pObjectType
	// 即 PsProcessType,PsThreadType 这些东西
	PVOID		pObjectType;

	// 这就是注册回调时设定的 PreCallbackRoutine 函数地址
	PVOID		pPreCallbackRoutine;

	// 这就是注册回调时设定的 PostCallbackRoutine 函数地址
	PVOID		pPostCallbackRoutine;

	// 这里似乎是个引用计数
	ULONG		ulRefCount;

};


注意这里面的 CALLBACK_BODY.ListEntry 链表很有意思,它将系统中的其它 CALLBACK_NODE.CallbackBodies 都链在了一起。因此我们就可以通过遍历这个链表检测系统中的其它回调钩子。

检测的大致思路是,先自己调用ObRegisterCallbacks注册一个假钩子,这样就得到了一个节点,然后用这个节点开始遍历。

写了一个测试代码,在64位Win8系统上测试截图如下:

图中 CallbackDetect.sys 中钩子是我的检测模块注册的假钩子;bd0001.sys 中的钩子是百度卫士注册的钩子;其它是360注册的钩子。

完整测试代码下载(64位/32位系统通用): CallbackDetect.zip

快讯:[看雪招聘]十八年来,看雪平台输出了大量安全人才,影响三代安全人才!

上传的附件:
最新回复 (18)
Speeday 2013-12-20 14:40
2
占位学习下。
永远的神话 2013-12-20 18:11
3
PG,PG。好东西,坏东西
tianyahai 2013-12-20 18:12
4
学习学习@
gossip 2013-12-20 18:37
5
学习学习
AioliaSky 2013-12-20 18:52
6
来留个mark
dnybz 2013-12-20 22:37
7
留个mark
watchsky 2013-12-21 09:07
8
看楼主帖子清晰易懂
zquchs 2013-12-21 10:06
9
值得学习啊
xiaoyang 2013-12-21 10:15
10
留个mark
wildochen 2013-12-21 10:30
11
DAY DAY UP
枫清淡 2013-12-21 12:48
12
不错的思路,学习了
正happy 2013-12-21 12:54
13
不用注册一个假钩子,你可以直接找到PsProcessType,
kd> dt _OBJECT_TYPE
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY
   +0x008 Name             : _UNICODE_STRING
   +0x010 DefaultObject    : Ptr32 Void
   +0x014 Index            : UChar
   +0x018 TotalNumberOfObjects : Uint4B
   +0x01c TotalNumberOfHandles : Uint4B
   +0x020 HighWaterNumberOfObjects : Uint4B
   +0x024 HighWaterNumberOfHandles : Uint4B
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : Uint4B
   +0x080 CallbackList     : _LIST_ENTRY
然后遍历CallbackList就行了~
FlowerCode 2013-12-22 00:59
14
楼主逆向出来的那两个结构体是不是 OB_CALLBACK_REGISTRATIONOB_OPERATION_REGISTRATION
Fypher 2013-12-22 01:24
15
内容差不多,CALLBACK_NODE描述的就是OB_CALLBACK_REGISTRATION,CALLBACK_BODY 描述的就是OB_OPERATION_REGISTRATION,只不过个别域的顺序不太一样,另外多了几个域,比如链表、ulXXX_000X000X 和 ulRefCount。

FC大牛睡得真晚……
Fypher 2013-12-22 01:26
16
这个不错,改天试试!学习了!
FlowerCode 2013-12-22 02:28
17
综合这里的信息,那个相邻的 ULONG 和 PWCHAR 应该确实是 OB_CALLBACK_REGISTRATION 里的那个 UNICODE_STRING,BODY 中间那个是个开关,结尾是个 EX_RUNDOWN_REF。

typedef struct _CALLBACK_BODY {
    LIST_ENTRY                  CallbackList;
    OB_OPERATION                Operations;
    ULONG                       Active;
    OB_HANDLE                   Handle;
    POBJECT_TYPE                ObjectType;
    POB_PRE_OPERATION_CALLBACK  PreOperation;
    POB_POST_OPERATION_CALLBACK PostOperation;
    EX_RUNDOWN_REF              RundownProtection;
} CALLBACK_BODY, *PCALLBACK_BODY;

typedef struct _CALLBACK_NODE {
    USHORT         Version;
    USHORT         OperationRegistrationCount;
    PVOID          RegistrationContext;
    UNICODE_STRING Altitude;
    CALLBACK_BODY  Entries[1];
} CALLBACK_NODE, *PCALLBACK_NODE;
Fypher 2013-12-22 03:09
18
是的,这下就完整了
cqzj70 2014-1-16 15:03
19
国标 到此一游
返回