找回密码
 立即注册
首页 业界区 业界 驱动开发:内核封装WFP防火墙入门

驱动开发:内核封装WFP防火墙入门

寨重 2025-6-6 19:49:58
WFP框架是微软推出来替代TDIHOOK传输层驱动接口网络通信的方案,其默认被设计为分层结构,该框架分别提供了用户态与内核态相同的AIP函数,在两种模式下均可以开发防火墙产品,以下代码我实现了一个简单的驱动过滤防火墙。
WFP 框架分为两大层次模块,用户态基础过滤引擎BFE (BaseFilteringEngine) ,以及内核态过滤引擎 KMFE (KMFilteringEngine),基础过滤引擎对上提供C语言调用方式的API以及RPC接口,这些接口都被封装在FWPUCLNT.dll模块中,开发时可以调用该模块中的导出函数.

  • WFP程序工作流程:
  • 使用 FwpmEngineOpen() 开启 WFP 引擎,获得WFP使用句柄
  • 使用 FwpmTransactionBegin() 设置对网络通信内容的过滤权限 (只读/允许修改)
  • 使用 FwpsCalloutRegister(),FwpmCalloutAdd(),FwpmFilterAdd() 选择要过滤的内容,并添加过滤器对象和回调函数.
  • 使用 FwpmTransactionCommit() 确认刚才的内容,让刚才添加的回调函数开始生效.
  • 使用 FwpmFilterDeleteById(),FwpmCalloutDeleteById(),FwpsCalloutUnregisterById()函数撤销对象和回调函数.
  • 使用 FwpmEngineClose() 关闭WFP引擎类句柄.
默认情况下WFP一次需要注册3个回调函数,只有一个是事前回调,另外两个是事后回调,通常情况下我们只关注事前回调即可,此外WFP能过滤很对内容,我们需要指定过滤条件标志来输出我们所需要的数据.

  • 一般可设置为FWPM_LAYER_ALE_AUTH_CONNECT_V4意思是设置IPV4过滤.
  • 还需要设置一个GUID值,该值可随意设置,名称为GUID_ALE_AUTH_CONNECT_CALLOUT_V4宏.
首先我们通过上方的流程实现一个简单的网络控制驱动,该驱动运行后可对自身机器访问指定地址端口进行控制,例如实现指定应用断网,禁止指定页面被访问等,在配置WFP开发环境时需要在链接器选项卡中的附加依赖项中增加fwpkclnt.lib,uuid.lib这两个库文件,并且需要使用WDM开发模板,否则编译将不通过。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #define NDIS_SUPPORT_NDIS6 1
  6. #define DEV_NAME L"\\Device\\MY_WFP_DEV_NAME"
  7. #define SYM_NAME L"\\DosDevices\\MY_WFP_SYM_NAME"
  8. #include <ntifs.h>
  9. #include <fwpsk.h>
  10. #include <fwpmk.h>
  11. #include <stdio.h>
  12. // 过滤器引擎句柄
  13. HANDLE g_hEngine;
  14. // 过滤器引擎中的callout的运行时标识符
  15. ULONG32 g_AleConnectCalloutId;
  16. // 过滤器的运行时标识符
  17. ULONG64 g_AleConnectFilterId;
  18. // 指定唯一UUID值(只要不冲突即可,内容可随意)
  19. GUID GUID_ALE_AUTH_CONNECT_CALLOUT_V4 = { 0x6812fc83, 0x7d3e, 0x499a, 0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b };
  20. // ------------------------------------------------------------------------------
  21. // 头部函数声明
  22. // ------------------------------------------------------------------------------
  23. // 注册Callout并设置过滤点
  24. NTSTATUS RegisterCalloutForLayer(
  25.         IN PDEVICE_OBJECT pDevObj,
  26.         IN const GUID *layerKey,
  27.         IN const GUID *calloutKey,
  28.         IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  29.         IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  30.         IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  31.         OUT ULONG32 *calloutId,
  32.         OUT ULONG64 *filterId,
  33.         OUT HANDLE *engine);
  34. // 注册Callout
  35. NTSTATUS RegisterCallout(
  36.         PDEVICE_OBJECT pDevObj,
  37.         IN const GUID *calloutKey,
  38.         IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  39.         IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  40.         IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  41.         OUT ULONG32 *calloutId);
  42. // 设置过滤点
  43. NTSTATUS SetFilter(
  44.         IN const GUID *layerKey,
  45.         IN const GUID *calloutKey,
  46.         OUT ULONG64 *filterId,
  47.         OUT HANDLE *engine);
  48. // Callout函数 flowDeleteFn
  49. VOID NTAPI flowDeleteFn(
  50.         _In_ UINT16 layerId,
  51.         _In_ UINT32 calloutId,
  52.         _In_ UINT64 flowContext
  53.         );
  54. // Callout函数 classifyFn
  55. #if (NTDDI_VERSION >= NTDDI_WIN8)
  56. VOID NTAPI classifyFn(
  57.         _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  58.         _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  59.         _Inout_opt_ void* layerData,
  60.         _In_opt_ const void* classifyContext,
  61.         _In_ const FWPS_FILTER2* filter,
  62.         _In_ UINT64 flowContext,
  63.         _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  64.         );
  65. #elif (NTDDI_VERSION >= NTDDI_WIN7)                       
  66. VOID NTAPI classifyFn(
  67.         _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  68.         _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  69.         _Inout_opt_ void* layerData,
  70.         _In_opt_ const void* classifyContext,
  71.         _In_ const FWPS_FILTER1* filter,
  72.         _In_ UINT64 flowContext,
  73.         _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  74.         );
  75. #else
  76. VOID NTAPI classifyFn(
  77.         _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  78.         _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  79.         _Inout_opt_ void* layerData,
  80.         _In_ const FWPS_FILTER0* filter,
  81.         _In_ UINT64 flowContext,
  82.         _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  83.         );
  84. #endif
  85. // Callout函数 notifyFn
  86. #if (NTDDI_VERSION >= NTDDI_WIN8)
  87. NTSTATUS NTAPI notifyFn(
  88.         _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  89.         _In_ const GUID* filterKey,
  90.         _Inout_ FWPS_FILTER2* filter
  91.         );
  92. #elif (NTDDI_VERSION >= NTDDI_WIN7)
  93. NTSTATUS NTAPI notifyFn(
  94.         _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  95.         _In_ const GUID* filterKey,
  96.         _Inout_ FWPS_FILTER1* filter
  97.         );
  98. #else
  99. NTSTATUS NTAPI notifyFn(
  100.         _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  101.         _In_ const GUID* filterKey,
  102.         _Inout_ FWPS_FILTER0* filter
  103.         );
  104. #endif
  105. // ------------------------------------------------------------------------------
  106. // 函数实现部分
  107. // ------------------------------------------------------------------------------
  108. // 协议判断
  109. NTSTATUS ProtocalIdToName(UINT16 protocalId, PCHAR lpszProtocalName)
  110. {
  111.         NTSTATUS status = STATUS_SUCCESS;
  112.         switch (protocalId)
  113.         {
  114.         case 1:
  115.         {
  116.                 // ICMP
  117.                 RtlCopyMemory(lpszProtocalName, "ICMP", 5);
  118.                 break;
  119.         }
  120.         case 2:
  121.         {
  122.                 // IGMP
  123.                 RtlCopyMemory(lpszProtocalName, "IGMP", 5);
  124.                 break;
  125.         }
  126.         case 6:
  127.         {
  128.                 // TCP
  129.                 RtlCopyMemory(lpszProtocalName, "TCP", 4);
  130.                 break;
  131.         }
  132.         case 17:
  133.         {
  134.                 // UDP
  135.                 RtlCopyMemory(lpszProtocalName, "UDP", 4);
  136.                 break;
  137.         }
  138.         case 27:
  139.         {
  140.                 // RDP
  141.                 RtlCopyMemory(lpszProtocalName, "RDP", 6);
  142.                 break;
  143.         }
  144.         default:
  145.         {
  146.                 // UNKNOW
  147.                 RtlCopyMemory(lpszProtocalName, "UNKNOWN", 8);
  148.                 break;
  149.         }
  150.         }
  151.         return status;
  152. }
  153. // 启动WFP
  154. NTSTATUS WfpLoad(PDEVICE_OBJECT pDevObj)
  155. {
  156.         NTSTATUS status = STATUS_SUCCESS;
  157.         // 注册Callout并设置过滤点
  158.         // classifyFn, notifyFn, flowDeleteFn 注册三个回调函数,一个事前回调,两个事后回调
  159.         status = RegisterCalloutForLayer(pDevObj, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, &GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
  160.                 classifyFn, notifyFn, flowDeleteFn, &g_AleConnectCalloutId, &g_AleConnectFilterId, &g_hEngine);
  161.         if (!NT_SUCCESS(status))
  162.         {
  163.                 DbgPrint("注册回调失败 \n");
  164.                 return status;
  165.         }
  166.         return status;
  167. }
  168. // 卸载WFP
  169. NTSTATUS WfpUnload()
  170. {
  171.         if (NULL != g_hEngine)
  172.         {
  173.                 // 删除FilterId
  174.                 FwpmFilterDeleteById(g_hEngine, g_AleConnectFilterId);
  175.                 // 删除CalloutId
  176.                 FwpmCalloutDeleteById(g_hEngine, g_AleConnectCalloutId);
  177.                 // 清空Filter
  178.                 g_AleConnectFilterId = 0;
  179.                 // 反注册CalloutId
  180.                 FwpsCalloutUnregisterById(g_AleConnectCalloutId);
  181.                 // 清空CalloutId
  182.                 g_AleConnectCalloutId = 0;
  183.                 // 关闭引擎
  184.                 FwpmEngineClose(g_hEngine);
  185.                 g_hEngine = NULL;
  186.         }
  187.         return STATUS_SUCCESS;
  188. }
  189. // 注册Callout并设置过滤点
  190. NTSTATUS RegisterCalloutForLayer(IN PDEVICE_OBJECT pDevObj, IN const GUID *layerKey, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId, OUT ULONG64 *filterId, OUT HANDLE *engine)
  191. {
  192.         NTSTATUS status = STATUS_SUCCESS;
  193.         // 注册Callout
  194.         status = RegisterCallout(pDevObj, calloutKey, classifyFn, notifyFn, flowDeleteNotifyFn, calloutId);
  195.         if (!NT_SUCCESS(status))
  196.         {
  197.                 return status;
  198.         }
  199.         // 设置过滤点
  200.         status = SetFilter(layerKey, calloutKey, filterId, engine);
  201.         if (!NT_SUCCESS(status))
  202.         {
  203.                 return status;
  204.         }
  205.         return status;
  206. }
  207. // 注册Callout
  208. NTSTATUS RegisterCallout(PDEVICE_OBJECT pDevObj, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId)
  209. {
  210.         NTSTATUS status = STATUS_SUCCESS;
  211.         FWPS_CALLOUT sCallout = { 0 };
  212.         // 设置Callout
  213.         sCallout.calloutKey = *calloutKey;
  214.         sCallout.classifyFn = classifyFn;
  215.         sCallout.flowDeleteFn = flowDeleteNotifyFn;
  216.         sCallout.notifyFn = notifyFn;
  217.         // 注册Callout
  218.         status = FwpsCalloutRegister(pDevObj, &sCallout, calloutId);
  219.         if (!NT_SUCCESS(status))
  220.         {
  221.                 DbgPrint("注册Callout失败 \n");
  222.                 return status;
  223.         }
  224.         return status;
  225. }
  226. // 设置过滤点
  227. NTSTATUS SetFilter(IN const GUID *layerKey, IN const GUID *calloutKey, OUT ULONG64 *filterId, OUT HANDLE *engine)
  228. {
  229.         HANDLE hEngine = NULL;
  230.         NTSTATUS status = STATUS_SUCCESS;
  231.         FWPM_SESSION session = { 0 };
  232.         FWPM_FILTER mFilter = { 0 };
  233.         FWPM_CALLOUT mCallout = { 0 };
  234.         FWPM_DISPLAY_DATA mDispData = { 0 };
  235.         // 创建Session
  236.         session.flags = FWPM_SESSION_FLAG_DYNAMIC;
  237.         status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &hEngine);
  238.         if (!NT_SUCCESS(status))
  239.         {
  240.                 return status;
  241.         }
  242.         // 开始事务
  243.         status = FwpmTransactionBegin(hEngine, 0);
  244.         if (!NT_SUCCESS(status))
  245.         {
  246.                 return status;
  247.         }
  248.         // 设置Callout参数
  249.         mDispData.name = L"MY WFP LyShark";
  250.         mDispData.description = L"WORLD OF DEMON";
  251.         mCallout.applicableLayer = *layerKey;
  252.         mCallout.calloutKey = *calloutKey;
  253.         mCallout.displayData = mDispData;
  254.         // 添加Callout到Session中
  255.         status = FwpmCalloutAdd(hEngine, &mCallout, NULL, NULL);
  256.         if (!NT_SUCCESS(status))
  257.         {
  258.                 return status;
  259.         }
  260.         // 设置过滤器参数
  261.         mFilter.action.calloutKey = *calloutKey;
  262.         mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
  263.         mFilter.displayData.name = L"MY WFP LyShark";
  264.         mFilter.displayData.description = L"WORLD OF DEMON";
  265.         mFilter.layerKey = *layerKey;
  266.         mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
  267.         mFilter.weight.type = FWP_EMPTY;
  268.         // 添加过滤器
  269.         status = FwpmFilterAdd(hEngine, &mFilter, NULL, filterId);
  270.         if (!NT_SUCCESS(status))
  271.         {
  272.                 return status;
  273.         }
  274.         // 提交事务
  275.         status = FwpmTransactionCommit(hEngine);
  276.         if (!NT_SUCCESS(status))
  277.         {
  278.                 return status;
  279.         }
  280.         *engine = hEngine;
  281.         return status;
  282. }
  283. // Callout函数 classifyFn 事前回调函数
  284. VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
  285. {
  286.         // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  287.         WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  288.         // 定义本机地址与本机端口
  289.         ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  290.         UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  291.         // 定义对端地址与对端端口
  292.         ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  293.         UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  294.         // 获取当前进程IRQ
  295.         KIRQL kCurrentIrql = KeGetCurrentIrql();
  296.         // 获取进程ID
  297.         ULONG64 processId = inMetaValues->processId;
  298.         UCHAR szProcessPath[256] = { 0 };
  299.         CHAR szProtocalName[256] = { 0 };
  300.         RtlZeroMemory(szProcessPath, 256);
  301.         // 获取进程路径
  302.         for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  303.         {
  304.                 // 里面是宽字符存储的
  305.                 szProcessPath[i] = inMetaValues->processPath->data[i];
  306.         }
  307.         // 获取当前协议类型
  308.         ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);
  309.         // 设置默认规则 允许连接
  310.         classifyOut->actionType = FWP_ACTION_PERMIT;
  311.         // 禁止指定进程网络连接
  312.         if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
  313.         {
  314.                 // 设置拒绝规则 拒绝连接
  315.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  316.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  317.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  318.                 DbgPrint("[LyShark.com] 拦截IE网络链接请求... \n");
  319.         }
  320.         // 输出对端地址字符串 并阻断链接
  321.         char szRemoteAddress[256] = { 0 };
  322.         char szRemotePort[128] = { 0 };
  323.         char szLocalAddress[256] = { 0 };
  324.         char szLocalPort[128] = { 0 };
  325.         sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  326.         sprintf(szRemotePort, "%d", uRemotePort);
  327.         sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  328.         sprintf(szLocalPort, "%d", uLocalPort);
  329.         // DbgPrint("本端: %s : %s --> 对端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);
  330.         // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  331.         if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  332.         {
  333.                 DbgPrint("[LyShark.com] 拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  334.                 // 设置拒绝规则 拒绝连接
  335.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  336.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  337.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  338.         }
  339.         else if (strcmp(szRemotePort, "0") == 0)
  340.         {
  341.                 DbgPrint("[LyShark.com] 拦截Ping访问请求 --> %s \n", szRemoteAddress);
  342.                 // 设置拒绝规则 拒绝连接
  343.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  344.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  345.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  346.         }
  347.         // 显示
  348.         DbgPrint("[LyShark.com] 方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  349.         wDirection,
  350.         szProtocalName,
  351.         (ulLocalIp >> 24) & 0xFF,
  352.         (ulLocalIp >> 16) & 0xFF,
  353.         (ulLocalIp >> 8) & 0xFF,
  354.         (ulLocalIp)& 0xFF,
  355.         uLocalPort,
  356.         (ulRemoteIp >> 24) & 0xFF,
  357.         (ulRemoteIp >> 16) & 0xFF,
  358.         (ulRemoteIp >> 8) & 0xFF,
  359.         (ulRemoteIp)& 0xFF,
  360.         uRemotePort,
  361.         kCurrentIrql,
  362.         processId,
  363.         (PWCHAR)szProcessPath);
  364. }
  365. // Callout函数 notifyFn 事后回调函数
  366. NTSTATUS NTAPI notifyFn(_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, _In_ const GUID* filterKey, _Inout_ FWPS_FILTER2* filter)
  367. {
  368.         NTSTATUS status = STATUS_SUCCESS;
  369.         return status;
  370. }
  371. // Callout函数 flowDeleteFn 事后回调函数
  372. VOID NTAPI flowDeleteFn(_In_ UINT16 layerId, _In_ UINT32 calloutId, _In_ UINT64 flowContext)
  373. {
  374.         return;
  375. }
  376. // 默认派遣函数
  377. NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  378. {
  379.         NTSTATUS status = STATUS_SUCCESS;
  380.         pIrp->IoStatus.Status = status;
  381.         pIrp->IoStatus.Information = 0;
  382.         IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  383.         return status;
  384. }
  385. // 创建设备
  386. NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
  387. {
  388.         NTSTATUS status = STATUS_SUCCESS;
  389.         PDEVICE_OBJECT pDevObj = NULL;
  390.         UNICODE_STRING ustrDevName, ustrSymName;
  391.         RtlInitUnicodeString(&ustrDevName, DEV_NAME);
  392.         RtlInitUnicodeString(&ustrSymName, SYM_NAME);
  393.         status = IoCreateDevice(pDriverObject, 0, &ustrDevName, FILE_DEVICE_NETWORK, 0, FALSE, &pDevObj);
  394.         if (!NT_SUCCESS(status))
  395.         {
  396.                 return status;
  397.         }
  398.         status = IoCreateSymbolicLink(&ustrSymName, &ustrDevName);
  399.         if (!NT_SUCCESS(status))
  400.         {
  401.                 return status;
  402.         }
  403.         return status;
  404. }
  405. // 卸载驱动
  406. VOID UnDriver(PDRIVER_OBJECT driver)
  407. {
  408.         // 删除回调函数和过滤器,关闭引擎
  409.         WfpUnload();
  410.         UNICODE_STRING ustrSymName;
  411.         RtlInitUnicodeString(&ustrSymName, SYM_NAME);
  412.         IoDeleteSymbolicLink(&ustrSymName);
  413.         if (driver->DeviceObject)
  414.         {
  415.                 IoDeleteDevice(driver->DeviceObject);
  416.         }
  417. }
  418. // 驱动入口
  419. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  420. {
  421.         NTSTATUS status = STATUS_SUCCESS;
  422.         Driver->DriverUnload = UnDriver;
  423.         for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
  424.         {
  425.                 Driver->MajorFunction[i] = DriverDefaultHandle;
  426.         }
  427.         // 创建设备
  428.         CreateDevice(Driver);
  429.         // 启动WFP
  430.         WfpLoad(Driver->DeviceObject);
  431.         Driver->DriverUnload = UnDriver;
  432.         return STATUS_SUCCESS;
  433. }
复制代码
上方代码是一个最基本的WFP过滤框架头部函数,声明部分来源于微软的定义此处不做解释,需要注意GUID_ALE_AUTH_CONNECT_CALLOUT_V4代表的是一个随机UUID值,该值可以任意定义只要不一致即可,驱动程序运行后会率先执行WfpLoad()这个函数,该函数内部通过RegisterCalloutForLayer()注册了一个过滤点,此处我们必须要注意三个回调函数,classifyFn, notifyFn, flowDeleteFn 他们分别的功能时,事前回调,事后回调,事后回调,而WFP框架中我们最需要注意的也就是对这三个函数进行重定义,也就是需要重写函数来实现我们特定的功能。
  1. NTSTATUS RegisterCalloutForLayer
  2. (
  3.     IN const GUID* layerKey,
  4.     IN const GUID* calloutKey,
  5.     IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  6.     IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  7.     IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  8.     OUT UINT32* calloutId,
  9.     OUT UINT64* filterId
  10. }
复制代码
既然是防火墙那么必然classifyFn事前更重要一些,如果需要监控网络流量则需要在事前函数中做处理,而如果是监视则可以在事后做处理,既然要在事前进行处理,那么我们就来看看事前是如何处理的流量。
  1. // Callout函数 classifyFn 事前回调函数
  2. VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
  3. {
  4.         // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  5.         WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  6.         // 定义本机地址与本机端口
  7.         ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  8.         UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  9.         // 定义对端地址与对端端口
  10.         ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  11.         UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  12.         // 获取当前进程IRQ
  13.         KIRQL kCurrentIrql = KeGetCurrentIrql();
  14.         // 获取进程ID
  15.         ULONG64 processId = inMetaValues->processId;
  16.         UCHAR szProcessPath[256] = { 0 };
  17.         CHAR szProtocalName[256] = { 0 };
  18.         RtlZeroMemory(szProcessPath, 256);
  19.         // 获取进程路径
  20.         for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  21.         {
  22.                 // 里面是宽字符存储的
  23.                 szProcessPath[i] = inMetaValues->processPath->data[i];
  24.         }
  25.         // 获取当前协议类型
  26.         ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);
  27.         // 设置默认规则 允许连接
  28.         classifyOut->actionType = FWP_ACTION_PERMIT;
  29.         // 禁止指定进程网络连接
  30.         if (NULL != wcsstr((PWCHAR)szProcessPath, L"qq.exe"))
  31.         {
  32.                 // 设置拒绝规则 拒绝连接
  33.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  34.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  35.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  36.         }
  37.         // 输出对端地址字符串 并阻断链接
  38.         char szRemoteAddress[256] = { 0 };
  39.         char szRemotePort[128] = { 0 };
  40.         char szLocalAddress[256] = { 0 };
  41.         char szLocalPort[128] = { 0 };
  42.         sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  43.         sprintf(szRemotePort, "%d", uRemotePort);
  44.         sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  45.         sprintf(szLocalPort, "%d", uLocalPort);
  46.         // DbgPrint("本端: %s : %s --> 对端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);
  47.         // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  48.         if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  49.         {
  50.                 DbgPrint("拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  51.                 // 设置拒绝规则 拒绝连接
  52.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  53.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  54.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  55.         }
  56.         else if (strcmp(szRemotePort, "0") == 0)
  57.         {
  58.                 DbgPrint("拦截Ping访问请求 --> %s \n", szRemoteAddress);
  59.                 // 设置拒绝规则 拒绝连接
  60.                 classifyOut->actionType = FWP_ACTION_BLOCK;
  61.                 classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  62.                 classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  63.         }
  64.         /*
  65.         // 显示
  66.         DbgPrint("方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  67.         wDirection,
  68.         szProtocalName,
  69.         (ulLocalIp >> 24) & 0xFF,
  70.         (ulLocalIp >> 16) & 0xFF,
  71.         (ulLocalIp >> 8) & 0xFF,
  72.         (ulLocalIp)& 0xFF,
  73.         uLocalPort,
  74.         (ulRemoteIp >> 24) & 0xFF,
  75.         (ulRemoteIp >> 16) & 0xFF,
  76.         (ulRemoteIp >> 8) & 0xFF,
  77.         (ulRemoteIp)& 0xFF,
  78.         uRemotePort,
  79.         kCurrentIrql,
  80.         processId,
  81.         (PWCHAR)szProcessPath);
  82.         */
  83. }
复制代码
当有新的网络数据包路由到事前函数时,程序中会通过如下案例直接得到我们所需要的数据包头,ProtocalIdToName函数则是一个将特定类型数字转为字符串的转换函数。
  1. // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  2. WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  3. // 定义本机地址与本机端口
  4. ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  5. UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  6. // 定义对端地址与对端端口
  7. ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  8. UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  9. // 获取当前进程IRQ
  10. KIRQL kCurrentIrql = KeGetCurrentIrql();
  11. // 获取进程ID
  12. ULONG64 processId = inMetaValues->processId;
  13. UCHAR szProcessPath[256] = { 0 };
  14. CHAR szProtocalName[256] = { 0 };
  15. RtlZeroMemory(szProcessPath, 256);
  16. // 获取进程路径
  17. for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  18. {
  19.         // 里面是宽字符存储的
  20.         szProcessPath[i] = inMetaValues->processPath->data[i];
  21. }
  22. // 获取当前协议类型
  23. ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);
复制代码
拦截浏览器上网: 防火墙的默认规则我们将其改为放行所有classifyOut->actionType = FWP_ACTION_PERMIT;,当我们需要拦截特定进程上网时则只需要判断调用原,如果时特定进程则直接设置拒绝网络访问。
  1. // 设置默认规则 允许连接
  2. classifyOut->actionType = FWP_ACTION_PERMIT;
  3. // 禁止指定进程网络连接
  4. if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
  5. {
  6.         // 设置拒绝规则 拒绝连接
  7.         classifyOut->actionType = FWP_ACTION_BLOCK;
  8.         classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  9.         classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  10.         DbgPrint("[LyShark.com] 拦截IE网络链接请求... \n");
  11. }
复制代码
当这段驱动程序被加载后,则用户使用IE访问任何页面都将提示无法访问。
1.png

拦截指定IP地址: 防火墙的另一个重要功能就是拦截主机自身访问特定网段,此功能只需要增加过滤条件即可实现,如下当用户访问8.141.58.64这个IP地址是则会被拦截,如果监测到用户时Ping请求则也会被拦截。
  1. // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  2. if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  3. {
  4.         DbgPrint("拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  5.         // 设置拒绝规则 拒绝连接
  6.         classifyOut->actionType = FWP_ACTION_BLOCK;
  7.         classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  8.         classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  9. }
  10. else if (strcmp(szRemotePort, "0") == 0)
  11. {
  12.         DbgPrint("拦截Ping访问请求 --> %s \n", szRemoteAddress);
  13.         // 设置拒绝规则 拒绝连接
  14.         classifyOut->actionType = FWP_ACTION_BLOCK;
  15.         classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  16.         classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  17. }
复制代码
当这段驱动程序被加载后,则用户主机无法访问8.141.58.64且无法使用ping命令。
2.png

抓取底层数据包: 如果仅仅只是想要输出流经自身主机的数据包,则只需要对特定数据包进行解码即可得到原始数据。
  1. // 输出对端地址字符串 并阻断链接
  2. char szRemoteAddress[256] = { 0 };
  3. char szRemotePort[128] = { 0 };
  4. char szLocalAddress[256] = { 0 };
  5. char szLocalPort[128] = { 0 };
  6. sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  7. sprintf(szRemotePort, "%d", uRemotePort);
  8. sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  9. sprintf(szLocalPort, "%d", uLocalPort);
  10. // 显示
  11. DbgPrint("[LyShark.com] 方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  12. wDirection,
  13. szProtocalName,
  14. (ulLocalIp >> 24) & 0xFF,
  15. (ulLocalIp >> 16) & 0xFF,
  16. (ulLocalIp >> 8) & 0xFF,
  17. (ulLocalIp)& 0xFF,
  18. uLocalPort,
  19. (ulRemoteIp >> 24) & 0xFF,
  20. (ulRemoteIp >> 16) & 0xFF,
  21. (ulRemoteIp >> 8) & 0xFF,
  22. (ulRemoteIp)& 0xFF,
  23. uRemotePort,
  24. kCurrentIrql,
  25. processId,
  26. (PWCHAR)szProcessPath);
复制代码
当这段驱动程序被加载后,则用户可看到流经本机的所有数据包。
3.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册