枚举并删除系统上Minifilter回调

Coquettish

发布日期: 2019-05-20 15:55:10 浏览量: 924
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

背景

我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。

既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。

本文要介绍的是枚举并删除系统上 Minifilter 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。

函数介绍

FltEnumerateFilters 函数

列举系统中所有注册的 Minifilter 驱动程序。

函数声明

  1. NTSTATUS FltEnumerateFilters(
  2. _Out_ PFLT_FILTER *FilterList,
  3. _In_ ULONG FilterListSize,
  4. _Out_ PULONG NumberFiltersReturned
  5. );

参数

  • FilterList [out]
    指向调用者分配的缓冲区的指针,该缓冲区接收不透明的过滤器指针数组。此参数是可选的,如果FilterListSize参数的值为零,则该参数可以为NULL。如果FilterListSize在输入上为零,并且FilterList为NULL,则NumberFiltersReturned参数将接收找到的 Minifilter 驱动程序的数量。
  • FilterListSize [in]
    FilterList参数指向的缓冲区可以容纳的不透明过滤器指针数。该参数是可选的,可以为零。如果FilterListSize在输入上为零,并且FilterList为NULL,则NumberFiltersReturned参数将接收找到的 Minifilter 驱动程序的数量。
  • NumberFiltersReturned [out]
    指向调用者分配的变量,该变量接收FilterList参数指向的数组中返回的不透明过滤器指针数。如果FilterListSize参数值太小,并且FilterList在输入上不为NULL,FltEnumerateFilters将返回STATUS_BUFFER_TOO_SMALL,并将NumberFiltersReturn设置为指向找到的minifilter驱动程序的数量。此参数是必需的,不能为NULL。

返回值

  • 成功,则返回 STATUS_SUCCESS;失败,则返回其它 NTSTATUS 错误码。

实现原理

枚举 Minifilter 驱动程序的回调,并不像枚举进程回调、线程回调、模块加载回调、注册表回调、对象回调那样,需要我们自己逆向寻找数组或是链表的地址,因为,Minifilter 驱动程序提供了 FltEnumerateFilters 内核函数给我们,用来获取系统上所有注册成功的 Minifilter 回调。

FltEnumerateFilters 函数可以获取系统上所有注册成功的 Minifilter 的过滤器对象指针数组 PFLT_FILTER *。PFLT_FILTER 数据类型在不同的系统上,它的定义是不同的。下面是我们使用 WinDbg 获取 Win10 x64 上的结构定义:

  1. lkd> dt fltmgr!_FLT_FILTER
  2. +0x000 Base : _FLT_OBJECT
  3. +0x030 Frame : Ptr64 _FLTP_FRAME
  4. +0x038 Name : _UNICODE_STRING
  5. +0x048 DefaultAltitude : _UNICODE_STRING
  6. +0x058 Flags : _FLT_FILTER_FLAGS
  7. +0x060 DriverObject : Ptr64 _DRIVER_OBJECT
  8. +0x068 InstanceList : _FLT_RESOURCE_LIST_HEAD
  9. +0x0e8 VerifierExtension : Ptr64 _FLT_VERIFIER_EXTENSION
  10. +0x0f0 VerifiedFiltersLink : _LIST_ENTRY
  11. +0x100 FilterUnload : Ptr64 long
  12. +0x108 InstanceSetup : Ptr64 long
  13. +0x110 InstanceQueryTeardown : Ptr64 long
  14. +0x118 InstanceTeardownStart : Ptr64 void
  15. +0x120 InstanceTeardownComplete : Ptr64 void
  16. +0x128 SupportedContextsListHead : Ptr64 _ALLOCATE_CONTEXT_HEADER
  17. +0x130 SupportedContexts : [7] Ptr64 _ALLOCATE_CONTEXT_HEADER
  18. +0x168 PreVolumeMount : Ptr64 _FLT_PREOP_CALLBACK_STATUS
  19. +0x170 PostVolumeMount : Ptr64 _FLT_POSTOP_CALLBACK_STATUS
  20. +0x178 GenerateFileName : Ptr64 long
  21. +0x180 NormalizeNameComponent : Ptr64 long
  22. +0x188 NormalizeNameComponentEx : Ptr64 long
  23. +0x190 NormalizeContextCleanup : Ptr64 void
  24. +0x198 KtmNotification : Ptr64 long
  25. +0x1a0 SectionNotification : Ptr64 long
  26. +0x1a8 Operations : Ptr64 _FLT_OPERATION_REGISTRATION
  27. +0x1b0 OldDriverUnload : Ptr64 void
  28. +0x1b8 ActiveOpens : _FLT_MUTEX_LIST_HEAD
  29. +0x208 ConnectionList : _FLT_MUTEX_LIST_HEAD
  30. +0x258 PortList : _FLT_MUTEX_LIST_HEAD
  31. +0x2a8 PortLock : _EX_PUSH_LOCK

其中,成员 Operations 就存储着 Minifilter 过滤器对象对应的回调信息,数据类型是 FLT_OPERATION_REGISTRATION,该结构是固定的。在头文件 fltKernel.h 里有 FLT_OPERATION_REGISTRATION 结构体定义:

  1. typedef struct _FLT_OPERATION_REGISTRATION
  2. {
  3. UCHAR MajorFunction;
  4. FLT_OPERATION_REGISTRATION_FLAGS Flags;
  5. PFLT_PRE_OPERATION_CALLBACK PreOperation;
  6. PFLT_POST_OPERATION_CALLBACK PostOperation;
  7. PVOID Reserved1;
  8. } FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;

从结构体里面可知,从中可获取 Minifilter 驱动程序的消息类型 MajorFunction,操作前回调函数地址 PreOperation,操作后回调函数地址 PostOperation 等信息。

所以,遍历系统上所有的 Minifilter 回调,原理就是:

  1. 调用 FltEnumerateFilters 内核函数获取系统上注册成功的 Minifilter 驱动程序的过滤器对象指针数组 PFLT_FILTER *。
  2. 然后,我们遍历过滤器对象指针 PFLT_FILTER,从中可以获取 Operations 成员的数据,数据类型为 FLT_OPERATION_REGISTRATION,可以从中获取 Minifilter 回调信息。

要注意的是,由于不同的系统,FLT_FILTER 数据结构的定义都不相同,所以成员 Operations 在数据结构中的偏移也是不固定的。下面是我使用 WinDbg 逆向各个系统中 FLT_FILTER 的数据结构定义,总结出来的 Operations 偏移大小:

Win 7 Win 8.1 Win 10
32 位 0xCC 0xD4 0xE4
64 位 0x188 0x198 0x1A8

删除回调

我们可以通过上述介绍的方法,枚举系统中的回调函数。其中,我们不能调用 FltUnregisterFilter 函数删除 Minifilter 回调,因为微软规定 FltUnregisterFilter 函数只能在 Minifilter 自身的驱动程序中调用,不能在其它的驱动程序中调用使用。所以,要删除回调函数可以有 2 种方式。

  1. 直接修改 FLT_OPERATION_REGISTRATION 数据结构中的操作前回调函数和操作后回调函数的地址数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数。
  2. 修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作。

编码实现

声明头文件 fltKernel.h:

  1. #include <fltKernel.h>

导入库文件 FltMgr.lib:

  1. 右击项目“属性” --> 链接器 --> 输入 --> 在“附加依赖项”中添加 FltMgr.lib

遍历 Minifilter 回调

  1. // 遍历回调
  2. BOOLEAN EnumCallback()
  3. {
  4. NTSTATUS status = STATUS_SUCCESS;
  5. ULONG ulFilterListSize = 0;
  6. PFLT_FILTER *ppFilterList = NULL;
  7. ULONG i = 0;
  8. LONG lOperationsOffset = 0;
  9. PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL;
  10. // 获取 Minifilter 过滤器Filter 的数量
  11. FltEnumerateFilters(NULL, 0, &ulFilterListSize);
  12. // 申请内存
  13. ppFilterList = (PFLT_FILTER *)ExAllocatePool(NonPagedPool, ulFilterListSize *sizeof(PFLT_FILTER));
  14. if (NULL == ppFilterList)
  15. {
  16. DbgPrint("ExAllocatePool Error!\n");
  17. return FALSE;
  18. }
  19. // 获取 Minifilter 中所有过滤器Filter 的信息
  20. status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize);
  21. if (!NT_SUCCESS(status))
  22. {
  23. DbgPrint("FltEnumerateFilters Error![0x%X]\n", status);
  24. return FALSE;
  25. }
  26. DbgPrint("ulFilterListSize=%d\n", ulFilterListSize);
  27. // 获取 PFLT_FILTER 中 Operations 偏移
  28. lOperationsOffset = GetOperationsOffset();
  29. if (0 == lOperationsOffset)
  30. {
  31. DbgPrint("GetOperationsOffset Error\n");
  32. return FALSE;
  33. }
  34. // 开始遍历 Minifilter 中各个过滤器Filter 的信息
  35. __try
  36. {
  37. for (i = 0; i < ulFilterListSize; i++)
  38. {
  39. // 获取 PFLT_FILTER 中 Operations 成员地址
  40. pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID *)((PUCHAR)ppFilterList[i] + lOperationsOffset));
  41. __try
  42. {
  43. // 同一过滤器下的回调信息
  44. DbgPrint("-------------------------------------------------------------------------------\n");
  45. while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction)
  46. {
  47. if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction) // MajorFunction ID Is: 0~27
  48. {
  49. // 显示
  50. DbgPrint("[Filter=%p]IRP=%d, PreFunc=0x%p, PostFunc=0x%p\n", ppFilterList[i], pFltOperationRegistration->MajorFunction,
  51. pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation);
  52. }
  53. // 获取下一个消息回调信息
  54. pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof(FLT_OPERATION_REGISTRATION));
  55. }
  56. DbgPrint("-------------------------------------------------------------------------------\n");
  57. }
  58. __except (EXCEPTION_EXECUTE_HANDLER)
  59. {
  60. DbgPrint("[2_EXCEPTION_EXECUTE_HANDLER]\n");
  61. }
  62. }
  63. }
  64. __except (EXCEPTION_EXECUTE_HANDLER)
  65. {
  66. DbgPrint("[1_EXCEPTION_EXECUTE_HANDLER]\n");
  67. }
  68. // 释放内存
  69. ExFreePool(ppFilterList);
  70. ppFilterList = NULL;
  71. return TRUE;
  72. }

移除 Minifilter 回调

  1. // 移除回调
  2. NTSTATUS RemoveCallback(PFLT_FILTER pFilter)
  3. {
  4. LONG lOperationsOffset = 0;
  5. PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL;
  6. // 开始遍历 过滤器Filter 的信息
  7. // 获取 PFLT_FILTER 中 Operations 成员地址
  8. pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID *)((PUCHAR)pFilter + lOperationsOffset));
  9. __try
  10. {
  11. // 同一过滤器下的回调信息
  12. while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction)
  13. {
  14. if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction) // MajorFunction ID Is: 0~27
  15. {
  16. // 替换回调函数
  17. pFltOperationRegistration->PreOperation = New_MiniFilterPreOperation;
  18. pFltOperationRegistration->PostOperation = New_MiniFilterPostOperation;
  19. // 显示
  20. DbgPrint("[Filter=%p]IRP=%d, PreFunc=0x%p, PostFunc=0x%p\n", pFilter, pFltOperationRegistration->MajorFunction,
  21. pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation);
  22. }
  23. // 获取下一个消息回调信息
  24. pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof(FLT_OPERATION_REGISTRATION));
  25. }
  26. }
  27. __except (EXCEPTION_EXECUTE_HANDLER)
  28. {
  29. DbgPrint("[EXCEPTION_EXECUTE_HANDLER]\n");
  30. }
  31. return STATUS_SUCCESS;
  32. }

获取 Operations 偏移

  1. // 获取 Operations 偏移
  2. LONG GetOperationsOffset()
  3. {
  4. RTL_OSVERSIONINFOW osInfo = { 0 };
  5. LONG lOperationsOffset = 0;
  6. // 获取系统版本信息, 判断系统版本
  7. RtlGetVersion(&osInfo);
  8. if (6 == osInfo.dwMajorVersion)
  9. {
  10. if (1 == osInfo.dwMinorVersion)
  11. {
  12. // Win7
  13. #ifdef _WIN64
  14. // 64 位
  15. // 0x188
  16. lOperationsOffset = 0x188;
  17. #else
  18. // 32 位
  19. // 0xCC
  20. lOperationsOffset = 0xCC;
  21. #endif
  22. }
  23. else if (2 == osInfo.dwMinorVersion)
  24. {
  25. // Win8
  26. #ifdef _WIN64
  27. // 64 位
  28. #else
  29. // 32 位
  30. #endif
  31. }
  32. else if (3 == osInfo.dwMinorVersion)
  33. {
  34. // Win8.1
  35. #ifdef _WIN64
  36. // 64 位
  37. // 0x198
  38. lOperationsOffset = 0x198;
  39. #else
  40. // 32 位
  41. // 0xD4
  42. lOperationsOffset = 0xD4;
  43. #endif
  44. }
  45. }
  46. else if (10 == osInfo.dwMajorVersion)
  47. {
  48. // Win10
  49. #ifdef _WIN64
  50. // 64 位
  51. // 0x1A8
  52. lOperationsOffset = 0x1A8;
  53. #else
  54. // 32 位
  55. // 0xE4
  56. lOperationsOffset = 0xE4;
  57. #endif
  58. }
  59. return lOperationsOffset;
  60. }

程序测试

在 Win7 32 位系统下,驱动程序正常执行:

在 Win8.1 32 位系统下,驱动程序正常执行:

在 Win10 32 位系统下,驱动程序正常执行:

在 Win7 64 位系统下,驱动程序正常执行:

在 Win8.1 64 位系统下,驱动程序正常执行:

在 Win10 64 位系统下,驱动程序正常执行:

总结

我们可以调用 FltEnumerateFilters 来获取系统上所有 Minifilter 驱动程序的过滤器对象,并从中 PFLT_FILTER 经过一定的偏移获取 Operations 成员数据,里面存储着回调信息。其中,不同统统的 FLT_FILTER 定义都不同,所以,Operations 成员的偏移也不相同。大家也不用记忆这些偏移大小,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。

删除回调常用就有 2 种方式,自己根据需要选择一种使用即可。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download Minifilter_Enum_Remove_Test.7z ( 12.24kb, 1次下载 )

发送私信

童心未泯,是一件值得骄傲的事情

28
文章数
18
评论数
最近文章
eject