查看原文
其他

Minifilter学习的那些事

0xC5 看雪学苑 2022-07-01

本文为看雪论坛优秀文章

看雪论坛作者ID:0xC5





1 前言


毕业已经有几个月了,迷茫了一阵子最近下定决心走windows内核开发的方向。趁着看雪论坛30天写作挑战活动,记录一下自己在学习过程中的成果。

本文关于Minifilter的学习主要是依赖《Windows内核编程》的第十三章的内容。




2 Minifilter简介及基本结构


Minifilter驱动是通过向过Filter Manager驱动进行注册自己需要过滤的一些操作,提供指定格式的回调函数让过滤管理器来进行调用。

所以它更注重功能实现,不注重更深层的IRP之类的操控。因此整个Minifilter驱动的框架可以简洁的表示为:

DriverEntry->FltRegisterFilter(注册过滤器)->FltStartFiltering(启动过滤器)





3 Minifilter重要数据结构
及代码实现



DriverEntry代码如下:


NTSTATUSDriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ){ NTSTATUS status; UNREFERENCED_PARAMETER( RegistryPath ); PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, ("MyMinifiter!DriverEntry: Entered\n") ); //若注册成功则返回微过滤器的句柄 status = FltRegisterFilter( DriverObject,## &FilterRegistration,//这是一个重要的数据结构,包含着我们过滤操作的回调函数 &gFilterHandle ); FLT_ASSERT( NT_SUCCESS( status ) ); if (NT_SUCCESS( status )) { //依据注册成功的句柄开启过滤 status = FltStartFiltering( gFilterHandle ); if (!NT_SUCCESS( status )) FltUnregisterFilter( gFilterHandle ); } } return status;}

FilterRegistration参数的声明如下,具体的结构体的定义在这里就不贴出了。


CONST FLT_REGISTRATION FilterRegistration = { sizeof( FLT_REGISTRATION ), //结构的大小 FLT_REGISTRATION_VERSION, //版本 0, //微过滤器标志位 NULL, //Context Callbacks, //回调函数 MyMinifiterUnload, //卸载回调函数 MyMinifiterInstanceSetup, //安装实例回调 MyMinifiterInstanceQueryTeardown, //控制实例销毁函数 MyMinifiterInstanceTeardownStart, //实例解绑定函数 MyMinifiterInstanceTeardownComplete, // 实例解绑定完成函数
NULL, //生成文件名回调 NULL, //格式化名字组件回调 NULL //格式化上下文清理回调};


重点关注Callbacks,里面包含着处理请求的过滤函数,分为两种,一是在请求之前进行处理(Pre-Operation Fun),另一种是在请求完成之后处理(Post-Operation Fun)。下面是一个例子:


CONST FLT_OPERATION_REGISTRATION Callbacks[] = { { IRP_MJ_CREATE, //Request的功能号 0, //标志位,置0表示仅对/读写回调有用 MyMinifiterPreOperation, //Pre-Operation Fun MyMinifiterPostOperation }, //Post-Operation Fun
{ IRP_MJ_CREATE_NAMED_PIPE, 0, MyMinifiterPreOperation, MyMinifiterPostOperation },
{ IRP_MJ_CLOSE, 0, MyMinifiterPreOperation, MyMinifiterPostOperation },
{ IRP_MJ_READ, 0, MyMinifiterPreOperation, MyMinifiterPostOperation }, { IRP_MJ_OPERATION_END } //最后一个元素必须存在否则过滤管理器不知道有多少个回调参数




4 编写一个自己的Minifilter


查资料发现,Vs2015及以上版本安装WDK组件过后都可以选择创建Minifilter项目,VS会自动生成框架,但其中还有一些需要设置的地方。接下来会详细介绍:

4.1 inf文件

其中Class ClassGuid是需要设置的,当然也可以用注释中的缺省值。

倒数第二行的Altitude值是微过滤器的层级码,具体内容可以参照上面的链接这里可以设置为370030表示层中的位置。


4.2 Callback设置

Vs生成的框架默认是没有注册任何回调的,可以根据自己需要在//TODO注释后声明自己要过滤的操作和回调函数。也可以偷懒直接吧if 0 修改成if 1 直接过滤所有操作。当然此声明中Vs提供了默认的回调函数,也可以自行选择修改。


4.3 回调函数


Vs已经为我们生成好了默认的回调函数:

FLT_PREOP_CALLBACK_STATUSFsFilter1PreOperation ( //FsFilter1是驱动名称 _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext )
FLT_POSTOP_CALLBACK_STATUSFsFilter1PostOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags )

至此,一个简单的Mnifilter驱动就已经能够成功生成并能够进行加载了(BTW,x64还得自行签名或者强制禁用驱动签名)。





5 实现简单的过滤操作


5.1 过滤文件


废话不多说,直接上代码!

FLT_PREOP_CALLBACK_STATUSMyMinifiterPreOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ){ NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo;
UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); PAGED_CODE(); __try { status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); if (NT_SUCCESS(status)) { FltParseFileNameInformation(nameInfo); if (wcsstr(nameInfo->Name.Buffer, L"calc.exe") //过滤计算器 Data->IoStatus.Status = STATUS_ACCESS_DENIED;//填写拒绝 Data->IoStatus.Information = 0; FltReleaseFileNameInformation(nameInfo); return FLT_PREOP_COMPLETE;
//release resource FltReleaseFileNameInformation(nameInfo); } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("EXCEPTION_EXECUTE_HANDLER\n"); }
return FLT_PREOP_SUCCESS_WITH_CALLBACK;}

这样 calc.exe 就不能读取、删除、覆盖、重命名和运行了,这是基于书中的代码实现的功能,也是一个简单的过滤操作的实现。


5.2 文件重定向


也是查阅了相关资料,自己实现了打开读写1.txt实际上会重定向另一目录下的2.txt。

FLT_PREOP_CALLBACK_STATUSMyMinifiterPreOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ){ NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo; UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); UNICODE_STRING desFilename; RtlCreateUnicodeString(&desFilename, L"\\DEVICE\\HARDDISKVOLUME3\\USERS\\User\\DESKTOP\\2.TXT");//为了方便测试路径我写死了,实际操作也很简单仅仅只是字符串的拼接 PFILE_OBJECT FileObject; PAGED_CODE(); __try { status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); if (NT_SUCCESS(status)) { FltParseFileNameInformation(nameInfo); if (wcsstr(nameInfo->Name.Buffer, L"1.txt")) { FileObject = Data->Iopb->TargetFileObject; FileObject->FileName = desFilename;//替换成目标文件的路径。 Data->IoStatus.Information = IO_REPARSE; Data->IoStatus.Status = STATUS_REPARSE; Data->Iopb->TargetFileObject->RelatedFileObject = NULL; FltSetCallbackDataDirty(Data); FltReleaseFileNameInformation(nameInfo); return FLT_PREOP_COMPLETE; } //release resource FltReleaseFileNameInformation(nameInfo); } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("EXCEPTION_EXECUTE_HANDLER\n"); } return FLT_PREOP_SUCCESS_WITH_CALLBACK;}




6 Some interesting thing


在上面两个功能的过滤中,我都采用了if(wcsstr(nameInfo->Name.Buffer, L"1.txt")的判断,我只判定子串中是否含有“1.txt”字符串,以前搞过web安全的我突然想起来当年IIS 6.0的解析漏洞,文件夹的命名是可以使用1.txt这样的名称的,如果开启过滤,那会对目录产生怎样的影响?对此我进行了实验。

实验的结果是,整个目录包括目录里的文件操作都被过滤掉了。





7 结语


至此,是我对Minifilter的初步学习,不足之处是对Minifilter与应用层的通信还未曾涉及,在今后的时间里会不断学习各种过滤操作,学习应用层通讯,打造一个完善的Minifilter。

新人初次发帖,难免会有瑕疵,还请各位大佬多多指正。



- End -



看雪ID:0xC5

https://bbs.pediy.com/user-home-768770.htm

  *本文由看雪论坛 0xC5 原创,转载请注明来自看雪社区。





本文参与了#看雪30天发帖打卡挑战#活动。


发帖见证成长,坚持见证不凡。


不仅可以收获进步,还可赢取物质奖励哦!


想要了解更多活动详情,戳 ↓



另外,贴心提示本次再印刷还没购买0day安全正书籍的小伙伴抓紧时间啦!


目前满500本,可安排啦!


点击以下小程序即可预购!


推荐文章++++

* 反汇编代码还原之除数为2的幂

* 用麒麟框架深入分析实模式二进制文件

* 反汇编代码还原之加减乘

* 基于inlinehook免重打包实现持久化NativeHook

* 反汇编代码还原之优化方式







公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



求分享

求点赞

求在看


“阅读原文”一起来充电吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存