题外话:搞安全驱动也有些日子了,从当年XP下肆无忌惮的HOOK,到现今win7 64位系统的微软的种种限制,忽然发觉我们的自由被严重的束缚。我们是做安全产品的,很显然是不会采取某种措施关闭掉微软的这些所谓的为了“安全”而做出的限制。好在,微软还是手下留情了,提供了另外一种HOOK的方式----基于微软规范的框架。
今天,我们就来一起探讨下win7下这让人又爱又恨的框架。
据我所知,现在很多公司都开始或者已经针对内网安全领域开发自己的产品,作为产品,那么稳定性是第一位的,而追求更加的底层就显得不是那么的重要了。因此越来越多的公司选择了基于微软框架的开发方式---保证高稳定性。
现今的内网安全涉及以下几个:进程,文件,注册表,网络
现在我就基于微软框架,详细的介绍下:
1,进程
在XP时代,你完全可以HOOK NtOpenProcess 甚至inline其内部的一些对象函数,比如 NtReferenceObjectByName。而现在,我们必须使用ObRegisterCallbacks函数来注册一个进程操作的回调。这和HOOK NtOpenProcess是等效的。同时,微软还会传递给你一堆的信息让你来判断。 (注册的步骤,下面奉上)
2,注册表
在XP时代,注册表的监控和保护需要HOOK N多个函数,而现在就不要那么麻烦了。只要有注册表的操作,微软都会乖乖的告诉你触发了什么样的操作等一堆的信息。这是你就可以基于这些信息来判断。当然,前提是你必须注册注册表回调,使用ObUnRegisterCallbacks。
下面是进程,注册表回调的总代码:
NTSTATUS register_process_register_protect(PDRIVER_OBJECT pdriverobj) //注册进程,注册表 保护回调
{
NTSTATUS status=STATUS_SUCCESS;
OB_CALLBACK_REGISTRATION CallBackRegistration;
UNICODE_STRING Altitude;
OB_OPERATION_REGISTRATION OperationRegistration;
PROCESS_CONTEXT ProcessContextStruct;
USHORT FilterVersion=ObGetFilterVersion();
RtlInitUnicodeString(&Altitude,L"ProcessCallBack");
RtlInitUnicodeString(&(process_register_hook.Altitude2),L"RegisterCallBack");
memset(&CallBackRegistration,0,sizeof(OB_CALLBACK_REGISTRATION));
memset(&OperationRegistration,0,sizeof(OB_OPERATION_REGISTRATION));
memset(&ProcessContextStruct,0,sizeof(PROCESS_CONTEXT));
ProcessContextStruct.ulindex=1;
ProcessContextStruct.Version=120;
if (FilterVersion == OB_FLT_REGISTRATION_VERSION)
{
CallBackRegistration.Version=OB_FLT_REGISTRATION_VERSION;
CallBackRegistration.OperationRegistrationCount = ProcessCallBackEntry;
CallBackRegistration.Altitude=Altitude;
CallBackRegistration.RegistrationContext=&ProcessContextStruct;
OperationRegistration.ObjectType=PsProcessType;
OperationRegistration.Operations=OB_OPERATION_HANDLE_CREATE;
OperationRegistration.PreOperation=ProcessPreCallBackX;
OperationRegistration.PostOperation=ProcessPostCallBackX;
CallBackRegistration.OperationRegistration=&OperationRegistration;
status=ObRegisterCallbacks(&CallBackRegistration,&(process_register_hook.RegistrationProcessHandle));
if (status==STATUS_SUCCESS) //如果进程注册成功,那么继续注册注册表保护回调。此驱动必须保证进程通知,进程保护,注册表保护3个回调全部成功,则加载才会成功
{
process_register_hook.RegProcessCallBackOrNot = TRUE;
status=CmRegisterCallbackEx((PEX_CALLBACK_FUNCTION)RegistryCallback,&(process_register_hook.Altitude2),pdriverobj,NULL,&(process_register_hook.RegistrationRegisterCookie),NULL);
if (!NT_SUCCESS(status)){
process_register_hook.RegRegisterCallBackOrNot = FALSE;
ObUnRegisterCallbacks(process_register_hook.RegistrationProcessHandle); //如果注册表注册失败,则同时反注册进程保护回调
return STATUS_UNSUCCESSFUL;
}else{
process_register_hook.RegRegisterCallBackOrNot = TRUE; //此时,注册表保护回调也已经注册成功
}
}
}else
{
status = STATUS_UNSUCCESSFUL;
}
return status;
}
其中ProcessPreCallBackX 为进程相关回调函数(你自己提供的函数,函数类型请MSDN上找)
其中RegistryCallback为注册表回调函数,同上,自己找函数类型定义
///以上就是注册表,进程的HOOK框架///
3,文件相关
在以往,文件方面要么HOOK ntoskrnl里的文件函数外就是基于文件系统过滤框架的驱动了。网上也有非常全面的源码,里面涉及到设备的挂载等一系列的名词,至少需要一定基础的人研究一段时间才能够开发出稳定的产品。关于常规的HOOK文件内核函数的做法,只能够做到基本的文件保护功能,也不够的底层。如果涉及到文件的透明加解密,那么还是要选择基于文件系统过滤框架来做。正如上面所说,比较复杂,开发周期比较长。微软似乎也感觉到这个问题,因此微软就发明了一个框架--文件系统微过滤框架。
你在使用之前也必须注册:(代码如下)
status = FltRegisterFilter(pDriverObject,&FilterRegistration,&gFilterHandle); //此函数调用成功的前提必须使用正确的INF配置
if (NT_SUCCESS(status))
{
status = FltStartFiltering( gFilterHandle );
if (!NT_SUCCESS( status )){
FltUnregisterFilter( gFilterHandle );
}
}else{
return status;
}
注册倒是不难,寒江独钓这本书讲的再清楚不过。但是,在使用的时候,MSDN上写得就不够清楚了,让我着实郁闷了几天。下面我就说下关于文件的读和写方面的保护。
当有文件读写操作的时候,你的回调函数就会被触发。其中第一个参数:Data非常的关键。它里面有3个地方描述了这此HOOK截获到的操作类型,分别是:
Data->Iopb->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess
Data->Iopb->Parameters.Create.SecurityContext->AccessState->OriginalDesiredAccess
Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess
加入,HOOK到的对文件的操作是读写,而你只打算写(拒绝读文件)。那么你必须3个一起“去读操作”。代码如下:
Data->Iopb->Parameters.Create.SecurityContext->AccessState->OriginalDesiredAccess &=~ FILE_READ_DATA;
Data->Iopb->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &=~ FILE_READ_DATA;
Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &=~ FILE_READ_DATA;
没错,效果达到了。而在拒绝文件写入的时候,就必须注意了。除了按常规除去FILE_WRITE_DATA,还必须同时除去FILE_APPEND_DATA。不然,文件的写保护是起不了效果的。就这事,郁闷了我几天。
Data->Iopb->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &=~ FILE_WRITE_DATA;
Data->Iopb->Parameters.Create.SecurityContext->AccessState->OriginalDesiredAccess &=~ FILE_WRITE_DATA;
Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &=~ FILE_WRITE_DATA;
Data->Iopb->Parameters.Create.SecurityContext->AccessState->OriginalDesiredAccess &=~ FILE_APPEND_DATA;
Data->Iopb->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &=~ FILE_APPEND_DATA;
Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &=~ FILE_APPEND_DATA;
其他的没什么好说的。仔细研究MSDN
需要说明的是,以上3个回调的注册操作,前提是你的驱动必须带有数字签名。如不然,你需要一些操作才可以避免注册失败。经过逆向分析。我们需要在DriverEntry函数里加上:
PLDR_DATA_TABLE_ENTRY pldr = NULL
DriverSection = pDriverObject->DriverSection;
pldr = (PLDR_DATA_TABLE_ENTRY)DriverSection;
pldr->Flags |= 0x20;
这样一来,回调函数的注册就不会失败了。这点很重要。
4,网络
网络过滤框架有TDI,WFP,NDIS这3类框架,其中TDI属于要淘汰的产品,不晓得win7上还能不用使用,所以作为产品级的软件,肯定不能选用。代替而来的就是WFP,相关代码在WDK文档里有源码,由于篇幅的问题,就不介绍了。自己去研究代码。
NDIS。NDIS框架由来已早,像市面上的sniffer,还有个很多软件都需要绑定的那个网络插件(忘记什么名字了,N多抓包软件都提示要安装)他们都基于NDIS框架。其中呢,这个框架分一下几类:NDIS协议驱动,NDIS过滤驱动,NDIS小端口驱动
分别说下作用:
NDIS协议驱动,和TCP/IP协议一个道理,你可以自由的封包,可以直接受应用层的控制。基于这样的原理,你可以开发自己的一套协议,从安全角度来讲,即使黑客截获到你的包,那么也需要很长时间来分析你的包的结构。NDIS协议驱动构建完包后,就可以通过NDIS内部函数进行发包操作,此时整个数据报将会交给NDIS小端口驱动来进行最终的发送
NDIS过滤驱动,介于协议驱动和小端口驱动之间,其实他就是个过滤层驱动,整个驱动需要提供2套接口,一套接口是向上面协议驱动公开的,另一套接口向下层的小端口驱动公开的。学习过滤驱动的时候最好先学习协议驱动和小端口驱动。重点是他们之间配合的流程。
NDIS小端口驱动。时间比较长了,具体忘记了。所以就不多说什么。他的应用其实很广。有兴趣的朋友可以自己找下相关资料
在企业应用方面,WFP涉及到比如监听,外连,连入等操作的控制。而NDIS大都涉及到网络数据的透明加解密技术还有数据报的截获。后者是基于端口,IP这些裸信息的。而前者相对比较高层,可以截获某某进程对某某IP地址+端口的某某操作。应用不同,则按需选择。
好了,说了这一大堆,希望给做企业级产品的朋友带来帮助。同时,恳请发给我邀请码。谢谢~~
[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!