If you decide to develop a firewall for Linux, you will find a lot of information and source code, all free. However, people interested in a firewall for Windows platforms have a little more difficulty, not only with finding information, as finding free source code is a task almost impossible!! So, I decided to write this article that describes a simple method for developing firewalls for Windows 2000/XP to help people interested in this subject.
In the Windows 2000 DDK, Microsoft includes a new type of network driver denominated Filter-Hook Driver. With it, you can establish a function to filter all traffic that arrives/leaves the interfaces. Because the documentation about this topic is small and doesn't include samples, I write in this article the steps needed to use it successfully. I hope this article will help you to understand this easy method.
The Filter-Hook Driver
As I said before, the Filter-Hook Driver was introduced by Microsoft in the Windows 2000 DDK. In fact, it is not a new network driver class; it is only a way to extend IP Filter Driver (included with Windows 2000 and ¿later?) functionality. In fact, Filter-Hook Driver isn't a Network driver; it is a Kernel Mode Driver. Basically, in this Filter-Hook Driver, we implement a callback function and then we register this callback with the IP Filter Driver. When we do this, the IP Filter Driver calls our callback function when a packet has been sent or received. Then... what are the main steps to do this? We can summarize them in the following steps:
- Create a Filter-Hook Driver. For this, you must create a Kernel Mode Driver. You choose the name, DOS name and other driver characteristics, nothing obligatory but I recommend using descriptive names.
- If we want to install the filter function, first we must get a pointer to IP Filter Driver. So, It will be the second step.
- We already have the pointer, so now we can install the filter function. We can do it by sending a specific IRP. The data passed in this "message" includes a pointer to the filter function.
- Filtering packets!!!!
- When we decide to finish filtering, we must deregister the filter function. We can do it by "registering" as a filter function the null pointer.
Oh, oh, five steps only and it seem very easy, but... how can I make a Kernel mode driver? How can I get a pointer to the IP Filter Driver? How can I ..... yessssssss, one moment please, I will explain all these steps now :P, showing the source code sample.
Create the Kernel Mode Driver
Filter-Hook driver is a Kernel Mode Driver, so if we want to do one, we have to make a Kernel Mode Driver. This article isn't a "How to develop Kernel Mode drivers in 5 minutes" guide, so I assume that the reader has some knowledge on the subject. The structure of the Filter-Hook driver is the typical Kernel Mode Driver structure:
- A driver entry where we create the device, set the standard routines in order to process IRPs (Dispatch, load, unload, create....) and create the symbolic link for communication with user applications.
- The standard routines to manage IRPs. Before you begin to code, I recommend, think what IOCTL you "export" to applications from a device driver. In my sample, I implement four IOCTL Codes:
START_IP_HOOK (registers the filter function),
STOP_IP_HOOK (deregisters the filter function),
ADD_FILTER (installs a new rule) and
CLEAR_FILTER (frees all rules).
- For our driver, we must implement one more function: the filter function.
I recommended you to use a program that generates the structure of a Kernel Mode Driver, so you only have to put code into the generate functions. For example, I have used QuickSYS in this project. You can see my own implementation of the structure of the Driver in the following code:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
dprintf("DrvFltIp.SYS: entering DriverEntry\n");
ntStatus = IoCreateDevice(DriverObject,
if ( NT_SUCCESS(ntStatus) )
ntStatus = IoCreateSymbolicLink(&deviceLinkUnicodeString,
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
DriverObject->DriverUnload = DrvUnload;
if ( !NT_SUCCESS(ntStatus) )
dprintf("Error in initialization. Unloading...");
NTSTATUS DrvDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
if(inputBufferLength == sizeof(IPFilter))
nf = (IPFilter *)ioBuffer;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
dprintf("DrvFltIp.SYS: unknown IRP_MJ_DEVICE_CONTROL\n");
ntStatus = Irp->IoStatus.Status;
VOID DrvUnload(IN PDRIVER_OBJECT DriverObject)
We have already made the driver main code, so we follow with code of the Filter-Hook Driver.
Registering a Filter Function
In the above code, you have seen a function called
SetFilterFunction(..). I implemented this function to register a function in the IP Filter Driver. I will describe the steps followed:
- First, we must get a pointer to the IP Filter Driver. This requires that the driver be installed and executed. My user application loads and starts IP Filter Driver before loading this driver, in order to assure this.
- Second, we must build an IRP specifying
IOCTL_PF_SET_EXTENSION_POINTER as IO Control Code. We must pass as a parameter a
PF_SET_EXTENSION_HOOK_INFO structure that has information about the pointer to filter function. If you want to uninstall the function, you have to follow the same steps, but passing
NULL as the pointer to filter function.
- Send the build IRP to the device driver.
Here there is one of the bigger problems of this driver. Only one filter function can be installed, so if other applications installed one, you can't install your function. I will show in the following lines the code of this function:
NTSTATUS status = STATUS_SUCCESS, waitStatus=STATUS_SUCCESS;
dprintf("Getting pointer to IpFilterDriver\n");
status = IoGetDeviceObjectPointer(&filterName,STANDARD_RIGHTS_ALL,
filterData.ExtensionPointer = filterFunction;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,
if(irp != NULL)
status = IoCallDriver(ipDeviceObject, irp);
if (status == STATUS_PENDING)
waitStatus = KeWaitForSingleObject(&event,
Executive, KernelMode, FALSE, NULL);
if (waitStatus != STATUS_SUCCESS )
dprintf("Error waiting for IpFilterDriver response.");
status = ioStatus.Status;
dprintf("Error, IO error with ipFilterDriver\n");
status = STATUS_INSUFFICIENT_RESOURCES;
dprintf("Error building IpFilterDriver IRP\n");
if(ipFileObject != NULL)
ipFileObject = NULL;
ipDeviceObject = NULL;
dprintf("Error while getting the pointer\n");
You can see that when we finish the process of establishing the filter function, we must de-reference the file object obtained when we get a pointer to the device driver. I use an event to be notified when IpFilter Driver finishes the processes of the IRP.
The Filter Function
We have seen how we can develop the driver and how to install the filter function, but we don't know anything about this function yet. I already said that this function is always called when the host receives or sends a packet. Depending on the return value of this function, the system decides what to do with the packet. The prototype of this function must be:
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
PF_FORWARD_ACTION is an enumerated type that can value (in Microsoft Words):
Specifies for the IP filter driver to immediately return the forward response to the IP stack. For local packets, IP forwards them up the stack. If the destination for packets is another computer and routing is enabled, IP routes them accordingly.
Specifies for the IP filter driver to immediately return the drop response to the IP stack. IP should drop the packet.
Specifies for the IP filter driver to filter packets and return the resulting response to the IP stack. How the IP filter driver proceeds to filter packets is determined by how it was set with the Packet Filtering API. The filter hook returns this pass response if it determines that it should not process the packet, but should allow the IP filter driver to filter the packet.
Although DDK documentation only includes these 3 values, if you look into pfhook.h (include needed for Filter-Hook Driver), you can see one more. This value is
PF_ICMP_ON_DROP. I suppose this value corresponds with dropping the packet and informing source for errors with an ICMP packet. As you can see in the definition of the filter function, the packet and its header are passed as pointers. So, you can modify header or payload and then forward the packets. This is very useful, for example, to do Network Address Translation (NAT). If we change destination address, IP routes the packets. In my implementation, the filter function compares each packet with a list of rules introduced by the user application. This list is implemented as a linked list that is built in runtime with each
START_IP_HOOK IOCTL. You can see this in my source code.
In the first version of this article, I included a simple example and -- because some people requested me to help them to develop real applications -- I updated it with a more complex one. The new example is a little packet filtering application. With this new program, you can establish your filter rules as you can do in some commercial firewalls. As the first version, this application has two components:
- User Application: it's an MFC application that manages the filter rules. This application sends the rules to the application and decides when the driver must begin to filter. Three steps for filtering the traffic:
- Define the rules you need. With the Add and Delete commands, you can add or delete filter rules.
- Install Rules. When you define the rules, click the install button to send them to the driver.
- Start Filtering. You only have to click the start button in order to begin filtering.
- Filter-Hook Driver: Driver that filter IP Traffic based in the filter rules received from the user application.
The Filter-Hook Driver must be in the same directory as the user application executable.
Why Use this Method to Develop a Firewall?
It isn't the unique method to developing firewalls for Windows. There are others such as NDIS Firewall, TDI Firewall, Winsock Layered Firewall, Packet Filtering API,.... so I will mention some advantages and disadvantages of Filter-Hook Driver in order for you to decide if your future firewall must use this driver.
- You have much flexibility filtering with this method. You can filter all IP traffic (and above). However, you can't filter lower layer headers. For example, you can't filter Ethernet frames. You need an NDIS filter to do that, which is more complicated to develop but more flexible.
- It is an easy method. Installing a firewall and implementation of the filter function are easy procedures with this method. However, the Packet Filtering API is more easy yet, although it is less flexible. You can't access packet content and you can't modify this with Packet Filtering API.
Result: Filter-Hook Driver isn't the best in anything, but it hasn't any bad characteristics. However, why is this method not used in commercial products? The answer is simple. Although this driver hasn't any bad characteristics, it has a great disadvantage, too. As I mentioned before, only one filter function can be installed each time. We can develop a great firewall and it can be downloaded and installed by thousands of users, but if other applications use this filter (and installed the filter function before) our program won't do anything.
This method has another disadvantage not documented by Microsoft. Although DDK documentation says that you can access packet content in a filter function, it's not real. You can access packet content for received packets, but for sent packets you can only read IP and TCP, UDP or ICMP headers. I don't understand why... Microsoft introduced another type of driver without this limitation in Windows XP: the firewall-hook driver. Its installation is very similar, but Microsoft doesn't recommend its use because "it ran too high in the network stack." Maybe this driver will disappear in later Windows versions.
Ok, this is the finish. I know this is not the best method for developing firewalls (I mentioned the great disadvantage before), but I think this is a good beginning for interested people who are searching for information, or for people who are interested now. I hope you understood something and that you want to develop a great firewall now.
- 21 December, 2002 -- Original version posted
- 4 November, 2003 -- Update