各种方案简介 在XP系统下,实现网络过滤可以使用 TDI
IPHOOK
NDIS5
几种方案。 其中 TDI
过滤可以获取到IP数据包与进程之间的关系,而 IPHOOK
和 NDIS5
两种无法获知进程名。 在 NDIS5
中,因为是最底层,可以抓取到所有类型的数据包,前边两种只能抓取IP数据包。
在VISTA及以上的系统中,实现网络过滤可以使用 WFP
NDIS6
几种方案,虽然 TDI
和 NDIS5
仍然 有效,但是微软已经不提倡再使用。其中 WFP
过滤对 TDI
进行了封装,可以获取到IP数据包与进程 之间的关系。NDIS6
对 NDIS5
进行了封装,可以抓取到所有类型的数据包。
以上几种方案中,以 NDIS
难度和复杂度最高,因为所有的细节操作,都需要自行处理,比如自行组装 数据包,自行进行转发数据。所以只要有一个处理不当,就会导致所有用 NDIS
的同类程序受到影响。
IPHOOK框架使用 这是最简单的框架,所有相关信息在 pfhook.h
头文件中定义,向 "\\Device\\IPFILTERDRIVER"
设备 发送 IOCTL_PF_SET_EXTENSION_POINTER
控制码来进行注册,其输入参数为处理过滤数据的回调函数
1 2 3 4 5 6 7 8 typedef PF_FORWARD_ACTION (*PacketFilterExtensionPtr) ( IN unsigned char *PacketHeader, IN unsigned char *Packet, IN unsigned int PacketLength, IN unsigned int RecvInterfaceIndex, IN unsigned int SendInterfaceIndex, IN IPAddr RecvLinkNextHop, IN IPAddr SendLinkNextHop) ;
以下为注册IPHOOK
框架的代码,注意由于只能存在一个过滤函数,所以需要先设置过滤函数为 NULL
进行 清除操作,然后再注册我们的过滤函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 NTSTATUS SetFilterFunction (PacketFilterExtensionPtr FilterFun) { PIRP pIrp = NULL ; KEVENT Event = { 0 }; IO_STATUS_BLOCK IoStatus = { 0 }; NTSTATUS Status = STATUS_SUCCESS; PFILE_OBJECT FileObject = NULL ; PDEVICE_OBJECT DeviceObject = NULL ; PF_SET_EXTENSION_HOOK_INFO HookInfo = { 0 }; UNICODE_STRING IpFilterDriver = RTL_CONSTANT_STRING(L"\\Device\\IPFILTERDRIVER" ); Status = IoGetDeviceObjectPointer( &IpFilterDriver, FILE_ALL_ACCESS, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } HookInfo.ExtensionPointer = FilterFun; KeInitializeEvent(&Event, NotificationEvent, FALSE); pIrp = IoBuildDeviceIoControlRequest( IOCTL_PF_SET_EXTENSION_POINTER, DeviceObject, (PVOID)&HookInfo, sizeof (PF_SET_EXTENSION_HOOK_INFO), NULL , 0 , FALSE, &Event, &IoStatus); if (pIrp == NULL ) { ObDereferenceObject(FileObject); return STATUS_INSUFFICIENT_RESOURCES; } Status = IoCallDriver(DeviceObject, pIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL ); } ObDereferenceObject(FileObject); return IoStatus.Status; }
以下为处理过滤数据回调函数的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 PF_FORWARD_ACTION FilterPackets ( unsigned char *PacketHeader, unsigned char *Packet, unsigned int PacketLength, unsigned int RecvInterfaceIndex, unsigned int SendInterfaceIndex, IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop) { PIP_HEADER pIpHeader = (PIP_HEADER)PacketHeader; PTCP_HEADER pTcpHeader = (PTCP_HEADER)Packet; PUDP_HEADER pUdpHeader = (PUDP_HEADER)Packet; PKGFLT_STATUS fStatus = STATUS_PASS; UNREFERENCED_PARAMETER(PacketLength); UNREFERENCED_PARAMETER(RecvLinkNextHop); UNREFERENCED_PARAMETER(SendLinkNextHop); if (PacketHeader == NULL ) return PF_FORWARD; (RecvInterfaceIndex == INVALID_PF_IF_INDEX); (SendInterfaceIndex == INVALID_PF_IF_INDEX); if (fStatus == STATUS_DROP) return PF_DROP; return PF_FORWARD; }
因为全都是已经组装好的数据,不需要额外的再做其他处理,所以稳定性极高。
防火墙策略的设计 除了数据包信息的处理,我们还需要配置用以拦截数据包的策略。 防火墙策略包含 规则
协议
源IP
源端口
目的IP
目的端口
进程名
几项基本配置, 更复杂的策略可以进行深层协议识别,比如 DNS
HTTP
SSL
等,甚至对 数据关键字
进行过滤。
以 IPHOOK
为基础,我们设计的策略如下
名称
说明
示例
Rule
规则
0(放行)1(拦截)
Protocol
协议
0(ANY)1(ICMP)6(TCP)17(UDP)
SourceIP
源IP段
192.168.3.100-192.168.3.120
SourcePort
源端口段
30500-30540
DestIP
目的IP段
192.168.1.55-192.168.1.90
DestPort
目的端口段
5000-5080
发给驱动要先进行数据处理,取值为区间的参数,拆分为为 Begin
和 End
两个数据,并转换字符为整数, 为了方便进行策略比对,要先用 ntohl
和 ntohs
转换IP和端口的数据为 host
类型,结构体定义如下
1 2 3 4 5 6 7 8 9 10 11 12 typedef struct _POLICY_INFO { UCHAR Rule; UCHAR Protocol; UINT32 SrcIPBegin; UINT32 SrcIPEnd; USHORT SrcPortBegin; USHORT SrcPortEnd; UINT32 DstIPBegin; UINT32 DstIPEnd; USHORT DstPortBegin; USHORT DstPortEnd; } POLICY_INFO, *PPOLICY_INFO;
多个策略之间的协同处理,采用自上而下的方式,依照发送到驱动的先后顺序进行匹配,始终以靠后的策略 的匹配结果为结果,比如同时满足两条策略,前边的结果是拦截,后边的结果是放行,最终结果是放行。