下载 DownLoadSource DownLoadFireWallApplication
介绍
如果你决定为Linux系统开发一个防火墙,你将会找到好多相关的信息及各式免费代码。但当人们想要
在Windows平台上开发防火墙时可就有点困难了,就那么可怜的一点点资料信息,至于免费代码,几乎是不
可能的。
所以我决定写这篇关于在Windows 2000/XP平台上开发一个简单功能的防火墙文章去帮助那些个对这方
面有兴趣的人。
背景
微软公司在它发布的Windows 2000 DDK中,已经包含了一个新的网络驱动类:Filter-Hook Driver。
用它你可以建立过滤所有通信的接口。
Filter-Hook Driver
正如我刚才所说的,Filter-Hook Driver是在微软的Windows 2000 DDK中被引入进来。但实际上它并
不是一个新的网络驱动类,它只是一个IP过滤功能的驱动。
事实上Filter-Hook Driver不是一个网络驱动类,它是属于核心驱模型。在这个Filter-Hook Driver
中我们只实现了一个回调函数,我们只是在Filter-Hook Driver中注册这个回调函数。当我们实现并注册
好回调函数后,IP过滤驱动就会在数据包到达和发送的时候调用它。
我们把实现步骤归纳为如下几步:
1、建立一个Filter-Hook Driver。在这里你必须建立一个核心模式驱动,你将任选名字,DOS名字以及其
它一些个属性,不一定是必须的,但我建议你这样做。
2、如果我们想安装过滤功能,首先我们必须得到一个IP过滤驱动的指针。这便是第二步。
3、我们已经得到了指针,现在我们能正确的安装我们的过滤功能函数。我们将发送一个特定的IPR,里面
将包括我们的过滤函数的指针。
4、过滤包!!!
5、当我们完成过滤后,我们必须注销过滤功能函数。我们可以注册一个空指针来替换我们的过滤函数指针
。
哦,就上面五个步骤,看起来似乎很简单,但是我如何生成一个核心模式驱动呢?如何得到IP过滤驱动的
指针呢?如何…………,稍等一下,我将会解释上面所有的步骤并列出源码例子。
建立核心模式驱动
Filter-Hook Driver是一个核心模式驱动,因此我们将要建立一个核心模式的驱动程序。但这篇文章
不是“教你在5分钟内如何开发核心驱动程序”的指南,所以我将假设读者已据有以上知识。
Filter-Hook驱动据有一个典型的核心驱动结构。
1、我们将为驱动程序建立一个典型的驱动入口(DriverEntry),为IRP设置标准分发例程(Dispatch, load,
unload, Create...),为方便与应用程序通讯建立一个符号连接。
2、标准分发例程将管理IRP。在你开使编写代码前,我建议你先建立IOCTL以便应用程序来操纵驱动。在我
的例子里,我实现了4个IOCTL编码:START_IP_HOOK(注册一个过滤功能函数),STOP_IP_HOOK(注销过滤功
能函数),ADD_FILTER(安装一个新的过滤规则),CLEAR_FILTER(清空所有过滤规则)
3、为了我们的驱动,我们必须实现一个或更多的过滤功能函数。
我建议你用一个程序来生成典型的核心驱动程序框架,因此你只须在里面填上你的功能代码既可,比
如我就用了 QuickSYS 来生成我的例子工程。
你将看到我自己产现的驱动结构,如下代码所示:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
//....
dprintf("DrvFltIp.SYS: entering DriverEntry\n");
//we have to create the device
RtlInitUnicodeString(&deviceNameUnicodeString, NT_DEVICE_NAME);
ntStatus = IoCreateDevice(DriverObject,
0,
&deviceNameUnicodeString,
FILE_DEVICE_DRVFLTIP,
0,
FALSE,
&deviceObject);
if ( NT_SUCCESS(ntStatus) )
{
// Create a symbolic link that Win32 apps can specify to gain access
// to this driver/device
RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);
ntStatus = IoCreateSymbolicLink(&deviceLinkUnicodeString,
&deviceNameUnicodeString);
//....
// Create dispatch points for device control, create, close.
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
DriverObject->DriverUnload = DrvUnload;
}
if ( !NT_SUCCESS(ntStatus) )
{
dprintf("Error in initialization. Unloading...");
DrvUnload(DriverObject);
}
return ntStatus;
}
NTSTATUS DrvDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
// ....
switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
dprintf("DrvFltIp.SYS: IRP_MJ_CREATE\n");
break;
case IRP_MJ_CLOSE:
dprintf("DrvFltIp.SYS: IRP_MJ_CLOSE\n");
break;
case IRP_MJ_DEVICE_CONTROL:
dprintf("DrvFltIp.SYS: IRP_MJ_DEVICE_CONTROL\n");
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode)
{
// ioctl code to start filtering
case START_IP_HOOK:
{
SetFilterFunction(cbFilterFunction);
break;
}
// ioctl to stop filtering
case STOP_IP_HOOK:
{
SetFilterFunction(NULL);
break;
}
// ioctl to add a filter rule
case ADD_FILTER:
{
if(inputBufferLength == sizeof(IPFilter))
{
IPFilter *nf;
nf = (IPFilter *)ioBuffer;
AddFilterToList(nf);
}
break;
}
// ioctl to free filter rule list
case CLEAR_FILTER:
{
ClearFilterList();
break;
}
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
dprintf("DrvFltIp.SYS: unknown IRP_MJ_DEVICE_CONTROL\n");
break;
}
break;
}
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
// We never have pending operation so always return the status code.
return ntStatus;
}
VOID DrvUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING deviceLinkUnicodeString;
dprintf("DrvFltIp.SYS: Unloading\n");
SetFilterFunction(NULL);
// Free any resources
ClearFilterList();
// Delete the symbolic link
RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
// Delete the device object
IoDeleteDevice(DriverObject->DeviceObject);
}
我们已经完成驱动程序主体代码,接下来将是过滤钩子代码。
注册过滤功能函数
在上面的代码中,我们已经看到了调用SetFilterFunction(...)函数,现在我们将实现这个注册IP过
虑功能函数,他将分以下几步实现。
1、首先,我们必须得到一个IP过滤驱动的指针,那需要驱动已正确安装且已经运行起来。现在假设在加载
这个驱动之前我的用户应用程序将加载并起动IP过滤驱动。
2、我们必须建立一个特定的IRP包含IOCTL_PF_SET_EXTENSION_POINTER的控制码。我们还得必须传送参数
如PF_SET_EXTENSION_HOOK_INFO结构来包含过滤函数指针。如果你要卸载函数,你得采取同样的步骤传送
一个NULL指针来取代过滤函数。
3、传送刚建立的IRP到设备驱动程序。
这里关于驱动的大问题,就是一次只能安装一个过滤功能函数。因此如果其它应用程序已安装了一个,那
么你的将不能被安装上。
接下来的代码我将出示该函数。
NTSTATUS SetFilterFunction
(PacketFilterExtensionPtr filterFunction)
{
NTSTATUS status = STATUS_SUCCESS, waitStatus=STATUS_SUCCESS;
UNICODE_STRING filterName;
PDEVICE_OBJECT ipDeviceObject=NULL;
PFILE_OBJECT ipFileObject=NULL;
PF_SET_EXTENSION_HOOK_INFO filterData;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
dprintf("Getting pointer to IpFilterDriver\n");
//first of all, we have to get a pointer to IpFilterDriver Device
RtlInitUnicodeString(&filterName, DD_IPFLTRDRVR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&filterName,STANDARD_RIGHTS_ALL,
&ipFileObject, &ipDeviceObject);
if(NT_SUCCESS(status))
{
//initialize the struct with functions parameters
filterData.ExtensionPointer = filterFunction;
//we need initialize the event used later by
//the IpFilterDriver to signal us
//when it finished its work
KeInitializeEvent(&event, NotificationEvent, FALSE);
//we build the irp needed to establish fitler function
irp = IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,
ipDeviceObject,
if(irp != NULL)
{
// we send the IRP
status = IoCallDriver(ipDeviceObject, irp);
//and finally, we wait for
//"acknowledge" of IpFilter Driver
if (status == STATUS_PENDING)
{
waitStatus = KeWaitForSingleObject(&event,
Executive, KernelMode, FALSE, NULL);
if (waitStatus != STATUS_SUCCESS )
dprintf("Error waiting for IpFilterDriver response.");
}
status = ioStatus.Status;
if(!NT_SUCCESS(status))
dprintf("Error, IO error with ipFilterDriver\n");
}
else
{
//if we cant allocate the space,
//we return the corresponding code error
status = STATUS_INSUFFICIENT_RESOURCES;
dprintf("Error building IpFilterDriver IRP\n");
}
if(ipFileObject != NULL)
ObDereferenceObject(ipFileObject);
ipFileObject = NULL;
ipDeviceObject = NULL;
}
else
dprintf("Error while getting the pointer\n");
return status;
}
当我们完成处理过滤功能函数后你能看到,我们必须引用文件对象来获取设备驱动指针。当IpFilter
驱动完成IRP处理时我使用了一个事件来通知。
过滤函数
我们明白了如何开发驱动以及如何安装过滤函数,但我们还不知道过滤函数呢。
早先我已经说过这个函数总是当传出或发送数据包时被调用。系统跟据它的返回值来决定如何处理数
据包。
下面是函数的申明
typedef PF_FORWARD_ACTION
(*PacketFilterExtensionPtr)(
// Ip Packet Header
IN unsigned char *PacketHeader,
// Packet. Don't include Header
IN unsigned char *Packet,
// Packet length. Don't Include length of ip header
IN unsigned int PacketLength,
// Index number for the interface adapter
//over which the packet arrived
IN unsigned int RecvInterfaceIndex,
// Index number for the interface adapter
//over which the packet will be transmitted
IN unsigned int SendInterfaceIndex,
//IP address for the interface
//adapter that received the packet
IN IPAddr RecvLinkNextHop,
//IP address for the interface adapter
//that will transmit the packet
IN IPAddr SendLinkNextHop
);
PF_FORWARD_ACTION是一个枚举类型
PF_FORWARD:指定IP过滤驱动立即将数据包返回到IP栈。对于送到本地的包,IP将被送到栈顶。如果
是要发送到其他机算机,IP包也将会被立即发送走。
PF_DROP:指定IP过滤驱动立即将该数据包从IP栈里丢弃掉。该IP数据包将会被丢失。
PF_PASS:指定IP过滤驱动过滤该包并返回响应结果到IP栈。IP过滤驱动如何处理该数据包取决于他对
包过滤API的设置。当过滤钩子发觉他不能处理该数据包就得交给IP过滤驱动去处理此包时就返回该参数。
虽然DDK的文只介绍了上面三个返回值,但当你查看pfhook.h文件时会发现还有另一个供返回的数值。
那就是PF_ICMP_NO_DROP。我猜想这个值与丢弃包相似并提供ICMP包错误信息。
正如你在过滤功能函数的定义中所看到的,包和他的包头都是以指针方式传递的。因此你可以修改包
头或内容然后再将其传送走。这在写比如NAT程序时是非常有用的。如果我们更改其目标地址,那么数据包
将向你所更改的方向传送。
在我的实现在,过滤功能函数会与规则链表中的每个过滤规则函数所定义的规则进行比对。这个规则
链表会在运行时收到START_IP_HOOK控制码时创建。这个你会在源码中看到它。
代码
在我的第一个版本中,我只实现了一个相对简单的版本,因此好多人都在询问我是否能帮助他们开发
一个更为实用的应用。所以我现在升级了一个相对复杂的版本。在这个新的例子中有一个小的包过滤应用
。在这个新的应用程序中,你可以加入你的一些通用防火墙的规则。
在第一个版本中,这个应用有两个部分组成。
用户应用程序:它是一个MFC应用程序来管理各种过滤规则。这个应用程序发送各种过滤规则到应用中
并决定何时开始起动过滤。过滤动作分三步
按照你的需要定一个过滤规则。发送添加或删除命令来增加或删除这个过滤规则。
安装规则。当你定义好一个过滤规则后,按下安装按扭来发送他到驱动中。
开始过滤。你只需要按下开始按扭就可以开始过滤了。
过滤驱动程序:驱动程序按照用户应用程序发来的过滤规则来过滤发送和收到的各数据包。
过滤钩子驱动程序必须和用户应用程序在同一目录中。
为什么要用这种方法来开发防火墙?
当然,它并不是Windows下开发防火墙的唯一方法,还有其它NDIS防火墙、TDI防火墙、WinSock层防火
墙、包过滤API等等从多方法。因此我将说明用过滤钩子驱动开发防火墙的一些优缺点。
基于过滤钩子方法来开发防火墙程序有较好的弹性。你可以在进入到IP栈前过滤所有的IP包。当然了
,你不能再过滤更底层的数的数据,例如网卡硬件帧。那么你需要写NDIS驱动去过滤它,那你将获得更好
的灵活性但也更难开发。
基于过滤钩子方法来开发防火墙程序不失为一个简单有效的方法。安装及实现防火墙只需几个简单的
过程。当然,包过滤API将更简单,但那样你将失去更多的灵活性,你再不能随意更改包内容了。
结论:过滤钩子驱动算不上开发防火墙的最好方法,但也不是最差的。那么为什么不能把它列为开发
商业软件的一部分呢?
回答是简单的,但这个驱动算不上最好的,但也不是最差的。我再次提及它只因为过滤钩子驱动只能
一次安装一个。我们可以开发一个更好的,它可以任意装载来为成百上千的应用程序过滤包并不会与其它
过滤程序相冲突。
在微软件的文档中,该方法还有另一个缺点。那就是虽然DDK文档说你可以对所有发送和收到的数据包
为所欲为但那并不是真的。你能任意更改所有收到的数据包,但只能对IP、TCP、UDP以及ICMP包头时行更
改。我也不知道那是为什么?
微软公司还为WindowsXP系统引入了另一个无限制的防火墙钩子驱动。它的装载非常的简单,但微软并
不建议你去用它来开发。因为它运行在网络栈的上层,太高了。可能这个驱动将会在下一版本的Windows中
消失。
结论
OK,所有的都讲完了。我知道这不是开发防火墙的最佳方法(我已经提及它的优缺点)。但我想这是为
所有对防火墙开发感性趣的朋友一个很好的起点,或是因为看了这篇文章对防火墙开发更感性趣了。
我希望你现已经有些明白了Filter-Hook Driver,并准备开发一个功能更为强大的防火墙了。