首页
论坛
课程
招聘
[原创]#30天写作挑战#炒冷饭之Minifilter学习
2020-9-7 19:07 3870

[原创]#30天写作挑战#炒冷饭之Minifilter学习

2020-9-7 19:07
3870

#1 前言:

毕业已经有几个月了,迷茫了一阵子最近下定决心走windows内核开发的方向。趁着看雪论坛30天写作挑战活动,记录一下自己在学习过程中的成果。本文关于Minifilter的学习主要是依赖《Windows内核编程》的第十三章的内容

#2 Minifilter简介及基本结构

Minifilter驱动是通过向过Filter Manager驱动进行注册自己需要过滤的一些操作,提供指定格式的回调函数让过滤管理器来进行调用。所以它更注重功能实现,不注重更深层的IRP之类的操控。因此整个Minifilter驱动的框架可以简洁的表示为:
DriverEntry->FltRegisterFilter(注册过滤器)->FltStartFiltering(启动过滤器)

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

DriverEntry代码如下:

NTSTATUS
DriverEntry (
    _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.2Callback设置


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

4.3回调函数

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

FLT_PREOP_CALLBACK_STATUS
FsFilter1PreOperation (          //FsFilter1是驱动名称
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )

FLT_POSTOP_CALLBACK_STATUS
FsFilter1PostOperation (
    _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_STATUS
MyMinifiterPreOperation (
    _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_STATUS
MyMinifiterPreOperation (
    _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。新人初次发帖,难免会有瑕疵,还请各位大佬多多指正。


【公告】【iPhone 13、ipad、iWatch】11月15日中午12:00,看雪·众安 2021 KCTF秋季赛 正式开赛【攻击篇】!!!文末有惊喜~

收藏
点赞5
打赏
分享
最新回复 (6)
雪    币: 72
活跃值: 活跃值 (413)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
killpy 活跃值 2 2020-9-8 16:15
2
0
666
雪    币: 15940
活跃值: 活跃值 (1537)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
KevinsBobo 活跃值 8 2020-9-8 19:46
3
0
感谢分享!
雪    币: 14753
活跃值: 活跃值 (23659)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-9-9 10:19
4
0

感谢分享!

欢迎更多的小伙伴参与到 #30天写作挑战#中来!活动详情:https://bbs.pediy.com/thread-261705.htm


雪    币: 14753
活跃值: 活跃值 (23659)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-9-9 10:19
5
0
点赞收藏关注 三连走起!
雪    币: 42
活跃值: 活跃值 (17)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
nevergone 活跃值 3 2020-9-25 18:31
6
0
支持一下,现在学minifilter的人应该不多了
UNICODE_STRING应该是不保证以0结尾,
使用wcsstr来搜索UNICODE_STRING可能会有问题的
雪    币: 639
活跃值: 活跃值 (1107)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 活跃值 2020-9-28 11:15
7
0
nevergone 支持一下,现在学minifilter的人应该不多了 UNICODE_STRING应该是不保证以0结尾, 使用wcsstr来搜索UNICODE_STRING可能会有问题的
楼上正解,这个是内核基础编程常见的问题。
游客
登录 | 注册 方可回帖
返回