首页
社区
课程
招聘
[原创]打造自己的HOOK引擎 之一 --- SSDT HOOK引擎
发表于: 2008-12-22 21:50 20807

[原创]打造自己的HOOK引擎 之一 --- SSDT HOOK引擎

2008-12-22 21:50
20807
打造自己的HOOK引擎 之一 --- SSDT HOOK引擎

SSDT HOOK已经是老生常谈的话题了,这里我就不对其进行介绍了,其实HOOK技术已经被大牛们分析的淋漓尽致,无以复加了,我也只是个初学者,失误之处,还望大牛们多多指教

读者可以通过后面的参考连接得到更多的关于SSDT HOOK的信息
我就相当于在这里对论坛的关于SSDT的精华贴做个总结了 呵呵

【原创】扫盲贴,HOOK SSDT 短文一篇。
【原创】分享比较完整的ROOTKIT DEMO! 原来Shadow Hook和SSDT Hook一样容易!
【原创】SSDT Hook的妙用-对抗ring0 inline hook
【原创】寻找原始表,恢复 ssdt 表
【原创】RootKit hook之[二] SSDT hook
【原创】用DDDK编写驱动,修改SSDT表HOOK NTDebugActiveProcess函数
【转帖】城里城外看SSDT

关于HOOK的文章实在是太多了,但是不同的文章由于作者的不同,导致文章的写作风格不同,代码的风格也各不相同,这样就导致了同样的技术,不同的作者用代码表现出来的形式却大相径庭,这样对我们初学者来讲实在是很不方便,于是便有了打造自己的HOOK引擎的想法,将技术细节封装起来,因为这个本来就不用去修改,需要修改的仅仅是用户自定义的HOOK例程

这个也就是我的HOOK引擎的基本思想,将HOOK的具体实现封装在引擎中,用户想HOOK某个函数时,不用去想HOOK的技术细节了,直接编写自己的HOOK函数然后调用引擎提供的某个接口即可

因为是第一次写,就写个最简单的,SSDT的HOOK
譬如如果用户想把NtOpenProcess HOOK成自己的MyNtOpenProcess
用户只需要在DriverEntry中调用HookService((ULONG)ZwOpenProcess, (ULONG)MyNtOpenProcess)
然后编写自己的钩子函数即可 接口很简单也易于实现

这里是个调用的例子

// Unload例程 卸载钩子
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
	KdPrint(("Unload Routine.\n"));
	UnHookService((ULONG)ZwSetInformationFile);
	UnHookService((ULONG)ZwOpenProcess);
}

// DriverEntry例程 初始化并安装钩子
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
					 IN PUNICODE_STRING RegistryPath)
{
	DriverObject->DriverUnload = Unload;
	InitServicesTable();
	HookService((ULONG)ZwSetInformationFile, (ULONG)MyZwSetInformationFile);
	HookService((ULONG)ZwOpenProcess, (ULONG)MyNtOpenProcess);

	return STATUS_SUCCESS;
}


然后只需要关注自己的函数了,如下编写了两个示例,第一个例子HOOK了ZwSetInformationFile保护test.txt文件不被删除,第二个例子HOOK了NtOpenProcess保护PID大于1000的进程不被结束,代码都是参考前人的,我只是在这里做个总结罢了

// 定义HOOK的函数原型
typedef
NTSTATUS
(__stdcall *ZWSETINFORMATIONFILE)(IN HANDLE FileHandle,
				  OUT PIO_STATUS_BLOCK IoStatusBlock,
				  IN PVOID FileInformation,
				  IN ULONG Length,
				  IN FILE_INFORMATION_CLASS FileInformationClass);
typedef
NTSTATUS
(__stdcall *NTOPENPROCESS)( OUT PHANDLE ProcessHandle,
			    IN ACCESS_MASK AccessMask,                                           
			    IN POBJECT_ATTRIBUTES ObjectAttributes,                                           
			    IN PCLIENT_ID ClientId);

// 对于ntddk.h中未定义的函数
// 可以根据<<Undocument>>一书在这里给出定义
NTSYSAPI
NTSTATUS
NTAPI 
ZwOpenProcess( OUT PHANDLE ProcessHandle,
	       IN ACCESS_MASK AccessMask,                                           
	       IN POBJECT_ATTRIBUTES ObjectAttributes,
               IN PCLIENT_ID ClientId);

// ==============================================================
// 用户自定义HOOK例程
NTSTATUS MyZwSetInformationFile(IN HANDLE FileHandle,
				OUT PIO_STATUS_BLOCK IoStatusBlock,
				IN PVOID FileInformation,
				IN ULONG Length,
				IN FILE_INFORMATION_CLASS FileInformationClass)
{
	PFILE_OBJECT pFileObject;

	// 在OldServiceAddressTable中取出原服务函数地址
	ZWSETINFORMATIONFILE OldZwSetInformationFile = 
		(ZWSETINFORMATIONFILE)OldServiceAddressTable[SERVICE_ID(ZwSetInformationFile)];
	
	NTSTATUS ret = ObReferenceObjectByHandle(FileHandle, 
						 GENERIC_READ,
						 *IoFileObjectType, 
						 KernelMode, 
						 (PVOID*)&pFileObject, 
						 0); 
	if(NT_SUCCESS(ret))
	{
		KdPrint(("%S opened.\n", pFileObject->FileName.Buffer));
		if (wcsstr(pFileObject->FileName.Buffer, L"test.txt"))
		{
			KdPrint(("test.txt opened. Deny it.\n"));
			return STATUS_ACCESS_DENIED;
		}
	}
      [color=red]ObDereferenceObject(pFileObject);[/color]	
	// 调用原服务函数
	return OldZwSetInformationFile( FileHandle, IoStatusBlock, FileInformation, 
					Length, FileInformationClass);
}

NTSTATUS MyNtOpenProcess(OUT PHANDLE ProcessHandle,
			 IN ACCESS_MASK DesiredAccess,
			 IN POBJECT_ATTRIBUTES ObjectAttributes,
			 IN PCLIENT_ID ClientId )
{
	NTSTATUS rc;
	ULONG PID;
[color=red]
	KPROCESSOR_MODE PreMode;
[/color]
	
	NTOPENPROCESS OldNtOpenProcess = 
		(NTOPENPROCESS)OldServiceAddressTable[SERVICE_ID(ZwOpenProcess)];
[color=red]
	PreMode = ExGetPreviousMode();
	if(PreMode != KernelMode)
	{
		__try
		{
			ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			return GetExceptionCode();
		}
	}
[/color]
	if(ClientId != NULL)
	{
		PID = (ULONG)ClientId->UniqueProcess;
        if(PID > 1000)
        {
			return STATUS_ACCESS_DENIED;
        }
	}
	return OldNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}


上面的代码都是对前人的代码进行了一些整合而得到的,仅仅是对引擎做个测试而已
然后说明一点,就是HookService()的第一个参数都应该是以Zw开头的函数,因为代码是根据这个函数来计算服务ID的
还有就是在调用HookService()之前应该先调用InitServicesTalbe()来对SSDT进行一次性的保存,避免后面多次HOOK就要保存多次

附件中是全部代码 写的很烂 还望大大们批评指正。

[课程]FART 脱壳王!加量不加价!FART作者讲授!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (29)
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
2
这也太...
ssdt hook还需要引擎啊??? 就是替换一个表.....
2008-12-22 22:15
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
  
本来就是写着方便自己学习的 大米见笑了。。。
其实就是一个HOOK接口 方便自己调用 呵呵
SSDT的是简单了点 Inline就复杂一点了 类型多种多样
2008-12-22 22:36
0
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
4
ssdt hook lib
2008-12-22 22:40
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
5
ssdt hook怎么不需要引擎了?
需要考虑到多个模块HOOK,即钩即用,随时卸载,方便管理,一个引擎是不可避免的
但其实“HOOK”不是关键,怎么做好分发、管理、稳定和灵活,才是关键
HOOK这件事本身是不需要一个引擎的,也不能用一个引擎来框起来

看看微软在VISTA ,WIN7内核里新引入的CALLBACK等等,就明白了
2008-12-22 22:59
0
雪    币: 217
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
这个完全同意,一个好的hook引擎主要是处理hook chain的dispatch,还有针对不同参数的的hookee的处理,还要随时能够安全的append to chain, pop out of chain,during call。
2008-12-22 23:38
0
雪    币: 203
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
有点意思,但代码中有一个低级错误。
2008-12-23 07:15
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
ssdt hook怎么不需要引擎了?
需要考虑到多个模块HOOK,即钩即用,随时卸载,方便管理,一个引擎是不可避免的
但其实“HOOK”不是关键,怎么做好分发、管理、稳定和灵活,才是关键
HOOK这件事本身是不需要一个引擎的,也不能用一个引擎来框起来

看看微软在VISTA ,WIN7内核里新引入的CALLBACK等等,就明白了


这个完全同意,一个好的hook引擎主要是处理hook chain的dispatch,还有针对不同参数的的hookee的处理,还要随时能够安全的append to chain, pop out of chain,during call。


感谢指点 学习之。。。

有点意思,但代码中有一个低级错误。


请问哪里出现了错误,希望网上勇士能提出来 谢谢了
2008-12-23 11:45
0
雪    币: 152
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
终于见到stone“胸”在看雪的ID了,还是精华贴!
2008-12-23 12:16
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感觉自己是用不上了
2008-12-23 12:35
0
雪    币: 147
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
7楼说的是这个?ObReferenceObjectByHandle

If the call succeeds, a pointer to the object body is returned to the caller and the pointer reference count is incremented. Incrementing this count prevents the object from being deleted while the pointer is being referenced. The caller must decrement the reference count with ObDereferenceObject as soon as it is done with the object.
2008-12-23 14:21
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
12
粗看一下,问题至少有三处

(1).(ULONG)ClientId->UniqueProcess;

未经检查就使用了ClientId,发送0x00000001或者0x80000000等地址系统即BSOD

(2).NTSTATUS ret = ObReferenceObjectByHandle(FileHandle,
             GENERIC_READ,
             *IoFileObjectType,
             KernelMode,
             (PVOID*)&pFileObject,
             0);

完成后未降低引用计数

(3). return OldNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);

return OldZwSetInformationFile( FileHandle, IoStatusBlock, FileInformation,
          Length, FileInformationClass);

直接运行了函数开始时取到的地址,如果你的处理过程中发生了线程切换,当前SSDT表中的其他hook卸载了他们钩子,就会BSOD
2008-12-23 14:30
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
感谢qihoocom和halfsoul的指点!

第一点还不是很明白,对ClientID不是做了判空检查了吗?还需要什么其他的检查吗?我看WRK的源码也只是用ARGUMENT_PRESENT做了下判空检查而已。。。不懂

第二点的确是个低级错误,现已修正。这两段代码都是我从网上拷过来的,做了点小的修改,对引擎做个测试用的。测试通过了也就没有注意这么多了。。

第三点倒没有想到,的确会出现严重的问题。我是在干净的虚拟机系统做的实验,所以没有出现什么问题。我目前想到的解决方法是,在InitSerices()中检查SSDT,对于已挂钩的表项不做处理,HookService的时候直接返回错误,不对这些做HOOK。这种方法应该比较安全。不知道有没有更好的方法?
2008-12-23 17:16
0
雪    币: 152
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
2G的前64K和最后64K永远不会分配,对他们的访问是会挂球的
2008-12-23 18:39
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
15
1.可能传一个无效BUFFER,例如0x00000001

3.可以在调用之前再做检查,或者在调用前检查下,减少几率,但100%保证安全是不可能的
因为世界上还有微点和瑞星的驱动程序员这么挫的人
2008-12-23 18:47
0
雪    币: 217
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
第1个,要防止恶意传错误地址。先判断PreviousMode,是UserMode就ProbeForRead,如果是KernelMode就MmIsAddressValid。
第3个,要绝对安全,很麻烦。简单点的做法是在你调用原函数之前检查当前SSDT项,如果不是你hook函数地址就不调用,直接返回;或者再次hook,更新原函数地址。
2008-12-23 18:55
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
17
第一,kernel mode不用管的
第三,楼上的理解有错误,应该是判断你保留的原始地址是否正确,如果不正确,从当前SSDT里取
2008-12-23 19:53
0
雪    币: 217
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
的确有误了,不好意思,这样在有新的module处理的时候会有问题。不过"判断你保留的原始地址是否正确"也不保险,他unhook并不表示会unload。

所以,最保险的就是你自己hook之前先恢复所有的hook,或者直接查ntoskrnl.exe文件里面的地址,保证你取到的原始地址是NtXxx函数地址。
2008-12-23 20:04
0
雪    币: 203
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
ULONG OldServiceAddressTable[1024];               

你认为应该放在头文件里,还是源文件里。
2008-12-23 21:24
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
[QUOTE=网上勇士;555011]ULONG OldServiceAddressTable[1024];               

你认为应该放在头文件里,还是源文件里。[/QUOTE]

这个应该没问题吧
只是一个数组而已嘛 用来保存原SSDT的
ssdt_hook_function.c 和 hook_sample.c都会用到这个数组的
2008-12-23 21:41
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
21
奇怪。。。
添加了对ClientId的检测代码后
PreMode = KeGetPreviousMode();
	if(PreMode != KernelMode)
	{
		__try
		{
			ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			return GetExceptionCode();
		}
	}


出现编译错误:error C4013: 'KeGetPreviousMode' undefined; assuming extern returning int
KeGetPreviousMode明明就在ntddk.h中定义了嘛
2008-12-23 21:45
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
22
用ExGetPreviousMode
2008-12-23 21:55
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
23
恢复HOOK和调用原始函数,可能导致一些安全软件出问题,如卡巴

一些垃圾驱动,例如NP之类,则可能BSOD
2008-12-23 21:55
0
雪    币: 27
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
24

用ExGetPreviousMode


恩 已作修改。感谢~~!


恢复HOOK和调用原始函数,可能导致一些安全软件出问题,如卡巴

一些垃圾驱动,例如NP之类,则可能BSOD


最好的方法就是对于已HOOK的表项就不HOOK了。。。  暂时就这样实现了
2008-12-23 22:15
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
25
不HOOK了很多功能无法实现~
碰上SSM那种所有函数都HOOK的你就废了~
2008-12-23 22:27
0
游客
登录 | 注册 方可回帖
返回
//