WFP驱动监控网络

WFP 是微软推出来替代 TDI HOOK、NDIS HOOK 等拦截网络通信的方案,WFP 的框架非常庞大,在 RING3 和 RING0 各有一套类似的函数,令人兴奋的是,即使在 R3 使用 WFP,也可以做到全局拦截访问网络。由于 WFP 的范围太广,实在难以一言概括,感兴趣的朋友可以自行到 MSDN 上查看微软对它的官方概述。本文的目的,是给大家理顺 WFP 的框架,并利用 WFP 拦截指定进程访问网络,或拦截对指定 IP 地址/端口的访问。

一个标准的 WFP 程序大体是这样子的: 首先使用 FwpmEngineOpen  开启 WFP  引擎 (获得个 一个 WFP  的 使用 句柄) )用 ,然后用 FwpmTransactionBegin  设置 对网络通信 内容 的 过滤 权限(是只读) 还是允许修改) , 然后用 FwpsCalloutRegister 、FwpmCalloutAdd 、FwpmFilterAdd  选择你 要过滤的内容, 并 添加 过滤器对象 和 回调函数, 最后用 用 FwpmTransactionCommit  确认刚才的内容, 让 刚才添加的 回调函数 开始生效 。 当用 你不用 WFP  的 时候, 就要用FwpmFilterDeleteById 、FwpmCalloutDeleteById 、FwpsCalloutUnregisterById  把你刚才添加的过滤器对象和回调函数删除掉,然后用 用 FwpmEngineClose  关闭 WFP 擎 引擎 ( 类似于 关闭句柄) )。

一个概述已经是这样子了,实现起来就更加麻烦了。为了方便大家学习,作者(胡文亮)已经把微软的 WFP 实例进行了最大简化,并把核心的注册回调功能封装成了一个函数。下面一步一步进行分析。在具体分析之前,有一些重要的内容需要先说明,否则后面的内容肯定会让大家一头雾水。一、WFP  一次性 要 注册的回调函数 不是 1  个 ,而是 3  个 。 但只有 1  个 是 “ 事前 ”回调, 另外 2  个 都是 “ 事后 ” 回调 (一般 只 使用“ 事前 ” 回调 ,不使用“ 事后 ”回调) )。二、WFP  能过滤 的内容很多,你必须选择一个 感兴趣的内容。 这个“ 感 兴趣 的 内容” 用官话来说叫做 “过滤 条件标志 ”, 这货 其实 是一个 常量 , 由于我们是 要过滤进程联网, 而且 一般都是用 IPV4  协议,所以 “过滤 条件标志 ”为 为 FWPM_LAYER_ALE_AUTH_CONNECT_V4 ( 在 这个

页面 可以的 查到所有的 “过滤 条件标志” ”) )。三、 你个 必须为这个 “过滤 条件标志 ”指定一个 GUID ,当然 GUID  的 值 随便设置 就行 ,只要 在 系统范围内不重复。 。 代码中的 GUID  就是 随意设定的,它的名称为:GUID_ALE_AUTH_CONNECT_CALLOUT_V4。

我用的VS2015+wdk10开发的,VS15里面需要设置这些东西:

添加库:ntoskrnl.lib ndis.lib fwpkclnt.lib uuid.lib

添加宏定义:NDIS_SUPPORT_NDIS6

注册的代码:

NTSTATUS RegisterCalloutForLayer
(
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 UINT32* calloutId,
OUT UINT64* filterId
)
{
NTSTATUS status = STATUS_SUCCESS;
FWPS_CALLOUT sCallout = {0};
FWPM_FILTER mFilter = {0};
FWPM_FILTER_CONDITION mFilter_condition[1] = {0};
FWPM_CALLOUT mCallout = {0};
FWPM_DISPLAY_DATA mDispData = {0};
BOOLEAN bCalloutRegistered = FALSE;
sCallout.calloutKey = *calloutKey;
sCallout.classifyFn = classifyFn;
sCallout.flowDeleteFn = flowDeleteNotifyFn;
sCallout.notifyFn = notifyFn;
//要使用哪个设备对象注册
status = FwpsCalloutRegister( gDevObj,&sCallout,calloutId );
if( !NT_SUCCESS(status))
goto exit;
bCalloutRegistered = TRUE;
mDispData.name = L"WFP TEST";
mDispData.description = L"TESLA.ANGELA's WFP TEST";
//你感兴趣的内容
mCallout.applicableLayer = *layerKey;
//你感兴趣的内容的GUID
mCallout.calloutKey = *calloutKey;
mCallout.displayData = mDispData;
//添加回调函数
status = FwpmCalloutAdd( gEngineHandle,&mCallout,NULL,NULL);
if( !NT_SUCCESS(status))
goto exit;
mFilter.action.calloutKey = *calloutKey;
//在callout里决定
mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
mFilter.displayData.name = L"WFP TEST";
mFilter.displayData.description = L"TESLA.ANGELA's WFP TEST";
mFilter.layerKey = *layerKey;
mFilter.numFilterConditions = 0;
mFilter.filterCondition = mFilter_condition;
mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
mFilter.weight.type = FWP_EMPTY;
//添加过滤器
status = FwpmFilterAdd( gEngineHandle,&mFilter,NULL,filterId );
if( !NT_SUCCESS( status))
goto exit;
exit:
if( !NT_SUCCESS(status))
{
if( bCalloutRegistered )
{
FwpsCalloutUnregisterById( *calloutId );
}
}
return status;
}
NTSTATUS WallRegisterCallouts()
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN bInTransaction = FALSE;
BOOLEAN bEngineOpened = FALSE;
FWPM_SESSION session = {0};
session.flags = FWPM_SESSION_FLAG_DYNAMIC;
//开启WFP引擎
status = FwpmEngineOpen( NULL,
RPC_C_AUTHN_WINNT,
NULL,
&session,
&gEngineHandle );
if( !NT_SUCCESS(status))
goto exit;
bEngineOpened = TRUE;
//确认过滤权限
status = FwpmTransactionBegin( gEngineHandle,0 );
if( !NT_SUCCESS(status))
goto exit;
bInTransaction = TRUE;
//注册回调函数
status = RegisterCalloutForLayer(
&FWPM_LAYER_ALE_AUTH_CONNECT_V4,
&GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
WallALEConnectClassify,
WallNotifyFn,
WallFlowDeleteFn,
&gAleConnectCalloutId,
&gAleConnectFilterId);
if( !NT_SUCCESS(status))
{
DbgPrint("RegisterCalloutForLayer-FWPM_LAYER_ALE_AUTH_CONNECT_V4 failed!\n");
goto exit;
}
//确认所有内容并提交,让回调函数正式发挥作用
status = FwpmTransactionCommit(gEngineHandle );
if( !NT_SUCCESS(status))
goto exit;
bInTransaction = FALSE;
exit:
if( !NT_SUCCESS(status))
{
if( bInTransaction)
{
FwpmTransactionAbort( gEngineHandle );
}
if( bEngineOpened )
{
FwpmEngineClose( gEngineHandle );
gEngineHandle = 0;
}
}
return status;
}

注销的代码:

NTSTATUS WallUnRegisterCallouts()
{
if( gEngineHandle != 0 )
{
//删除FilterId
FwpmFilterDeleteById( gEngineHandle,gAleConnectFilterId );
//删除CalloutId
FwpmCalloutDeleteById( gEngineHandle,gAleConnectCalloutId );
//清空FilterId
gAleConnectFilterId = 0;
//反注册CalloutId
FwpsCalloutUnregisterById( gAleConnectCalloutId );
//清空CalloutId
gAleConnectCalloutId = 0;
//关闭引擎
FwpmEngineClose( gEngineHandle );
gEngineHandle = 0;
}
return STATUS_SUCCESS;
}

三个回调函数以及其他:

#include <ntddk.h>
#pragma warning(push)
#pragma warning(disable:4201) // unnamed struct/union
#pragma warning(disable:4995)
#include <ndis.h>
#include <fwpsk.h>
#pragma warning(pop)
#include <fwpmk.h>
#include <limits.h>
#include <ws2ipdef.h>
#include <in6addr.h>
#include <ip2string.h>
#include <strsafe.h>
#include <wdm.h> #define INITGUID
#include <guiddef.h>
#define bool BOOLEAN
#define true TRUE
#define false FALSE
#define DEVICE_NAME L"\\Device\\MyDriver"
#define DEVICE_DOSNAME L"\\DosDevices\\MyDriver"
#define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
#define kfree(_p) ExFreePool(_p) DEFINE_GUID // {6812FC83-7D3E-499a-A012-55E0D85F348B}
(
GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
0x6812fc83,
0x7d3e,
0x499a,
0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b
); PDEVICE_OBJECT gDevObj; HANDLE gEngineHandle = 0;
HANDLE gInjectHandle = 0;
//CalloutId
UINT32 gAleConnectCalloutId = 0;
//FilterId
UINT64 gAleConnectFilterId = 0; /*
以下两个回调函数没啥用
*/
NTSTATUS NTAPI WallNotifyFn
(
IN FWPS_CALLOUT_NOTIFY_TYPE notifyType,
IN const GUID *filterKey,
IN const FWPS_FILTER *filter
)
{
return STATUS_SUCCESS;
} VOID NTAPI WallFlowDeleteFn
(
IN UINT16 layerId,
IN UINT32 calloutId,
IN UINT64 flowContext
)
{
return;
} //协议代码转为名称
char* ProtocolIdToName(UINT16 id)
{
char *ProtocolName=kmalloc(16);
switch(id) //http://www.ietf.org/rfc/rfc1700.txt
{
case 1:
strcpy_s(ProtocolName,4+1,"ICMP");
break;
case 2:
strcpy_s(ProtocolName,4+1,"IGMP");
break;
case 6:
strcpy_s(ProtocolName,3+1,"TCP");
break;
case 17:
strcpy_s(ProtocolName,3+1,"UDP");
break;
case 27:
strcpy_s(ProtocolName,3+1,"RDP");
break;
default:
strcpy_s(ProtocolName,7+1,"UNKNOWN");
break;
}
return ProtocolName;
} //最重要的过滤函数
//http://msdn.microsoft.com/en-us/library/windows/hardware/ff551238(v=vs.85).aspx
void NTAPI WallALEConnectClassify
(
IN const FWPS_INCOMING_VALUES0* inFixedValues,
IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
IN OUT void* layerData,
IN const void* classifyContext,
IN const FWPS_FILTER* filter,
IN UINT64 flowContext,
OUT FWPS_CLASSIFY_OUT* classifyOut
)
{
char *ProtocolName=NULL;
DWORD LocalIp,RemoteIP;
LocalIp=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
RemoteIP=inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
ProtocolName=ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16);
DbgPrint("[WFP]IRQL=%d;PID=%ld;Path=%S;Local=%u.%u.%u.%u:%d;Remote=%u.%u.%u.%u:%d;Protocol=%s\n",
(USHORT)KeGetCurrentIrql(),
(DWORD)(inMetaValues->processId),
(PWCHAR)inMetaValues->processPath->data, //NULL,//
(LocalIp>>24)&0xFF,(LocalIp>>16)&0xFF,(LocalIp>>8)&0xFF,LocalIp&0xFF,
inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16,
(RemoteIP>>24)&0xFF,(RemoteIP>>16)&0xFF,(RemoteIP>>8)&0xFF,RemoteIP&0xFF,
inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16,
ProtocolName);
kfree(ProtocolName);
classifyOut->actionType = FWP_ACTION_PERMIT;//允许连接 //禁止IE联网(设置“行动类型”为FWP_ACTION_BLOCK)
// if(wcsstr((PWCHAR)inMetaValues->processPath->data,L"iexplore.exe"))
// {
// classifyOut->actionType = FWP_ACTION_BLOCK;
// classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
// classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
// }
return;
}

执行结果,进行网络监控,并且禁止ie访问网络:

刚刚看有一个哥们写的也不错,留在这方便以后用的时候一起查看:

http://blog.csdn.net/rodney443220/article/details/37653793

还有一点要注意。回调函数执行的时候IRQL不一定是0,还可能是2,看上面图片输出。

Win64 驱动内核编程-16.WFP网络监控驱动(防火墙)的更多相关文章

  1. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  2. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  3. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  4. Win64 驱动内核编程-18.SSDT

    SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...

  5. Win64 驱动内核编程-13.回调监控模块加载

    回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...

  6. Win64 驱动内核编程-11.回调监控进线程句柄操作

    无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...

  7. Win64 驱动内核编程-14.回调监控文件

    回调监控文件 使用 ObRegisterCallbacks 实现保护进程,其实稍微 PATCH 下内核,这个函数还能实现文件操作监视.但可惜只能在 WIN7X64 上用.因为在 WIN7X64 上 P ...

  8. Win64 驱动内核编程-15.回调监控注册表

    回调监控注册表 在 WIN32 平台上,监控注册表的手段通常是 SSDT HOOK.不过用 SSDT HOOK 的方式监控注册表实在是太麻烦了,要 HOOK 一大堆函数,还要处理一些 NT6 系统有而 ...

  9. Win64 驱动内核编程-12.回调监控进线程创建和退出

    回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine   进程回调PsSetCreateThreadNotifyRoutine    线程回调分 ...

随机推荐

  1. Java的特性和优势以及不同版本的分类,jdk,jre,jvm的联系与区别,javadoc的生成

    Java 1.Java的特性和优势 Write Once,Run Anywhere 简单性 面向对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性 2.Java的三大版本 JavaSE:标 ...

  2. Windows常用快捷键和基本dos命令

    Windows常用快捷键 键盘功能键:Tab,Shift,Ctrl,Alt,空格,Enter,Window... 键盘快捷键: 全选:Ctrl+A 复制: Ctrl+C 粘贴: Ctrl+V 撤销: ...

  3. FreeBSD安装xorg + xfce 4

    FreeBSD安装xorg,以及xfce 安装xorg 可选包 -xorg 完整xorg环境包 -xorg-minimal xorg最小化包 ports安装 cd /usr/ports/x11/xor ...

  4. 选择 FreeBSD 而不是 Linux 的技术性原因3

    选择 FreeBSD 而不是 Linux 的技术性原因3 jail FreeBSD Jails 系统是另一个惊人的工程壮举. 在 2000 年 3 月 14 日的 4.0 版本中,FreeBSD 引入 ...

  5. 选择 FreeBSD 而不是 Linux 的技术性原因2

    ZFSZFS 文件系统是 FreeBSD 上的一等公民.这不仅意味着可以在 ZFS 上安装根目录,安装程序也支持这一点,而且还意味着很多基础系统工具都已经紧密地集成或构建了对 ZFS 的支持.在 Fr ...

  6. 目标检测入门论文YOLOV1精读以及pytorch源码复现(yolov1)

    结果展示 其中绿线是我绘制的图像划分网格. 这里的loss是我训练的 0.77 ,由于损失函数是我自己写的,所以可能跟大家的不太一样,这个不重要,重要的是学习思路. 重点提示 yolov1是一个目标检 ...

  7. 在CentOS上安装Nginx配置HTTPS并设置系统服务和开机启动(最全教程)

    友情提示:全部配完大约需要20分钟,本教程配合 xshell 和 xftp 使用更佳. 系统配置:CentOS 7.5 本教程 摘繁华 版权所有. 操作按键 常用按键: 复制操作:Shift+Ins ...

  8. 一文搞懂如何实现 Go 超时控制

    为什么需要超时控制? 请求时间过长,用户侧可能已经离开本页面了,服务端还在消耗资源处理,得到的结果没有意义 过长时间的服务端处理会占用过多资源,导致并发能力下降,甚至出现不可用事故 Go 超时控制必要 ...

  9. Azure App Service(一)利用Azure DevOps Pipeline 构建镜像,部署应用程序

    一,引言 起因是前两天项目上做测试,需要我把写好的基于.NET 5 的 Web 测试程序作成 Docker 镜像.当我在本地验证完功能后,准备利用 Docker 构建应用程序镜像的时候,发现系统不支持 ...

  10. 懵了!女朋友突然问我MVCC实现原理

    前言 都知道事务的可重复读级别实现原理是使用MVCC实现的,那么你对MVCC的底层实现原理知道多少呢?面试高频点,你值得拥有. 一.MVCC到底是什么? MVCC即多版本控制器,其特点就是在同一时间, ...