本次实验的测试环境为Windows Server 2008 R2 X64下。
为了解决例如系统关键目录或者业务敏感目录被放入恶意的可执行程序或者网页文件等,一些安全软件会使用文件过滤驱动的技术结合一定的检测规则来达到保护系统和业务安全的一些目的。
简单地来看下Minifilter技术的介绍:
Filter管理器随Windows一起被安装,但它只在一个minifilter驱动被加载时才会起作用。Filter管理器绑定到目标卷的文件系统栈上。
Minifilter驱动为它要过滤的I/O操作而通过向filter管理器注册来间接绑定到文件系统栈上。Filter管理器随Windows一起被安装,但它只在一个minifilter驱动被加载时才会起作用。
Filter管理器绑定到目标卷的文件系统栈上。Minifilter驱动为它要过滤的I/O操作而通过向filter管理器注册来间接绑定到文件系统栈上。
微软的WDK实例中存在通过使用REPARSE重定向来完成跨盘的重定向,以及通过Minifilter使用IoCreateFileSpecifyDeviceObjectHint来完成的非跨盘重定向。
所以,我们的目标是先通过Windows部分内核代码来看看为何Reparse能够完成文件重定向的功能,再通过实现一个能够简易配置监视路径和重定向后路径的驱动程序和应用控制程序。
通过源码查看文件创建源码的关键部分
虽然是Windows Server 2008 x64系统,但是为了避免过多的逆向分析,直接以WRK为目标(由于无法过多引用,所以尽量以文字描述),看看是否可以得到关于Reparse重定向的结论。
创建文件的入口为IoCreateFile时,其主要工作由IopCreateFile的ObOpenObjectByName来完成,其为一个对象管理的内核函数。
ObOpenObjectByName函数中主要完成的2个工作是调用ObpLookupObjectName函数即对应我们真正的文件内核对象,并通过ObpCreateHandle完成内核对象到指定句柄表的插入。
其实最后的答案就在这个ObpLookupObjectName函数内,由于函数存在可选输入参数RootDirectoryHandle作为搜索的目录对象,但是类似地我们以不提供该参数的流程分析其中工作。
第一步,它会使用ObpRootDirectoryObject为搜索的根目录对象,处理了以“\??\”开头的对象名,以当前进程的ProcessMap为父目录的查找。
接着进入循环依次解析对象名中的每个段,在访问前非KernelMode的访问必须经过安全性检查,进入ObpLookupDirectoryEntry中其对当前层对象名计算其Hash值后与Hash数组大小37取余。
其存在后继链表当获取到的对象的OBJECT_HEADER_NAME_INFO和提供的名称一致后就返回这个这个对象的地址。
直接调用其对象类型的ParseProcedure(进入IopParseFile->IopParseDevice从而向对应的卷对象发送请求)。
阅读后发现返回了STATUS_REPARSE后会转入ParseFromRoot处,完成从前面描述的DeviceMap处处理的重新解析。
这说明了STATUS_REPARSE确实可以帮助我们完成一个文件位置到另一个文件位置的重定向(比如某类目录总是STATUS_REPARSE,而且STATUS_REPARSE在ObpLookupObjectName有次数限制),第二由于交还给对象管理器时会完完整整地重新解析,所以这确实可以完成跨盘的操作。
实现可配置文件的文件系统跨盘重定向
我们目标是实现一个应用程序将监控配置发送给驱动,驱动将需要重定向文件的结果报告给应用程序。其中驱动完成将在监控文件夹内的新建文件全部转移到目标文件夹下的操作。
首先我们主要关心如何截获特定路径下的文件创建操作。我们先完成对卷实例的绑定工作。这个回调例程在新的卷被挂载后以及注册后已经挂载卷后被调用。
在这个回调中主要通过FltGetVolumeName和完成卷的设备名称和卷标名称的获取,并通过FltAllocateContext从内存池或者lookaside表种得到获取保存上下文的内存,FltSetInstanceContext来完成设置到实例中。
为了满足程序能够跨盘重定向后以DOS风格名称发送给应用程序故加入双链表中以便获取其他盘的信息。
对于绑定后InstanceQueryTeardownCallback,InstanceTeardownStartCallback,InstanceTeardownCompleteCallback3类回调的处理,值得注意的是InstanceQueryTeardownCallback,因为其对应手工的解绑定,我们简单地直接返回STATUS_FLT_DO_NOT_DETACH以拒绝。
现在我们来看对应主功能号为IRP_MJ_CREATE的处理,而主功能号(Minifilter中)为IRP_MJ_NETWORK_QUERY_OPEN的请求,我们也请求处理到了这里,由于其是一个Fast I/O操作,所以返回FLT_PREOP_DISALLOW_FASTIO来拒绝。
我们使用了FltGetFileNameInformation和FltParseFileNameInformation来得到其文件名信息,后者是为了方便获得卷设备名以转换成DOS风格名。
这个过程中,我们根据实例上下文链表及配置生成2个名称,一个是DOS风格文件名及完整文件名,前者以发送给应用程序,后者设置到FileObject中(使用IoReplaceFileObjectName),并在I/O请求包中返回STATUS_REPARSE以完成第一部分分析让这个创建操作再重新根据新名称重新从对象管理处开始处理。
对于回调函数返回FLT_PREOP_COMPLETE来完成这个I/O请求,并不往下发送请求。
IRP_MJ_CREATE的操作IRP_MJ_SET_INFORMATION的FileInformationClass为FileRenameInformation的处理,因为对于非跨盘的移动会通过这个请求来完成操作,所以使用同IRP_MJ_CREATE一样的操作。
最后提一下Minifilter中由应用程序向驱动的信息发送和驱动主动向应用程序发送的处理。应用程序以通过FilterConnectCommunicationPort向命名的通信端口请求连接。
驱动内部通过PFLT_MESSAGE_NOTIFY MessageNotifyCallback回调接受收监控的目录和重定向后的目录。最后,完成重定向后驱动使用FltSendMessage发送消息,应用程序使用FilterGetMessage获取。
本次试验资源可以通过链接:http://pan.baidu.com/s/1o766JtK密码:jnsk下载。