Develop a firewall in Windows 2000/XP

Source: Internet
Author: User
  • Author:Jes ús O.
  • Address: http://www.codeproject.com/internet/drvfltip.asp
  • Translation: zerray
  • Download the firewall program-47.7 KB
  • Download source code-35.2 KB

Introduction

If you decide to develop a firewall in Linux, you can find a lot of materials and source code, all for free. However, it is difficult for people who are interested in firewalls on Windows platforms to find information and free source code!

So I decided to write this article to briefly describe how to develop a firewall in Windows 2000/XP to help those who are interested in it.

Background

In Windows 2000 DDK, Microsoft includes a new network driver called filter-hook driver ). With this function, you can create a function to filter all data packets that arrive or exit the interface.

Because there are few documents on this topic and there are no examples, I wrote this article to introduce the steps required to successfully use it. I hope this article will help you understand this simple method.

Filter hook driver

As I mentioned earlier, the filter hook driver was introduced by Microsoft in Windows 2000 DDK. In fact, it is not a new network driver class, it is just a way to extend the IP Filter Driver (included in Windows 2000 and later versions) function.

In fact, the filter hook driver is not a network driver, but a kernel-mode driver. Basically, we implement a callback function in the filter hook driver, and then register this callback function with the IP Filter Driver. After this is done, the IP address filter driver will call our callback function when a data packet is sent or accepted. So ...... What are the main steps for doing so?

I have summarized the following five steps:

  1. Create a filter hook driver. In this step, you must create a kernel-mode driver and select a name, DOS name, and other driver characters. There are no special requirements, but I suggest you use descriptive names.
  2. To install the filter function, you must first obtain a pointer pointing to the IP Filter Driver. Therefore, this is the second step.
  3. We already have pointers. Now we can install the filter function. We can do this by sending the specified IRP. The transmitted "message" data contains pointers to the filter function.
  4. Filter data packets !!!
  5. When we decide to stop filtering, We must deregister the filtering function. We can deregister by registering a filter function with a null pointer.

Oh, there are only five steps, and it looks very simple, ...... How to build a kernel-mode driver? How to obtain a pointer pointing to the IP address filter driver? Why ...... Yes. Please wait. I will explain these steps now: P. The source code example is displayed.

Build a kernel-mode driver

The filter hook driver is a kernel-mode driver, so if we want to do this, we have to make a kernel-mode driver. This article does not "Learn How to Develop kernel-mode drivers in five minutes", so I assume that readers already have this knowledge.

The structure of the filter hook driver is a typical kernel-mode driver structure:

  1. Create a driver entry for a device (Note: setting up a device is the attribute of the driver entry), and set a standard routine to process IRP (dispatch, load, unload, create ......) And establish a symbolic connection to communicate with other applications.
  2. Standard routines for managing IRP. Before writing code, I suggest you consider which IOCTL you want to extract from the device driver to the application. In my example, I implemented four IOCTL codes: start_ip_hook (register the filter function), stop_ip_hook (deregister the filter function), and add_filter (install the new rule) and clear_filter (clear all rules ).
  3. We must also implement another function for our driver: filter function.

I suggest you use a program to generate the kernel-mode driver structure, and then you just need to add the code to the generated function. For example, the quicksys
.

You can see the self-implemented driver structure. The Code is as follows:

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);
}
We have completed the main driver code, and the next step is to filter the hook-driven code.
Register a filter function

In the above Code, you have seen a function called setfilterfunction. This function is used to register the function to the IP address filter driver. There are the following steps:

  1. First, we must obtain a pointer to the IP address filter driver. This requires that the IP Filter Driver has been installed and runs. My user application loads and starts the IP Filter Driver before loading the driver to ensure this.
  2. Second, we must create an IRP to specify ioctl_pf_set_extension_pointer as the IO control code. We must pass a pf_set_extension_hook_info structure in the form of parameters, which contains pointers to the filter function. If you want to uninstall this function, you must follow the same steps and use null to replace the pointer pointing to the filter function.
  3. Send the established IRP to the device driver.

Here, there is a bigger problem with this driver. Only one filter function can be installed, so if one is installed in other applications, you cannot install your function.

The code for this function is as follows:

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;
}

You will find that when we complete the process of creating a filter function, we must abolish the file object we get when we get a pointer to the device driver. I used an event that will be prompted when the IP address filter driver completes IRP processing.

Filter Functions

We have seen how to develop the driver and install the filter function, but we do not know what the filter function is.

I have already said that this function is always called when the host accepts or sends a packet. Based on the return value of this function, the system determines what to do to this package.

The prototype of this function must be as follows:

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 is an enumeration type and can be set:

  • PF_FORWARD

    The specified IP Filter Driver immediately returns a forward response to the IP stack. For local packages, IP addresses directly import them into the stack. If the destination of the package is another computer and routes are allowed, IP addresses route the packages accordingly.

  • PF_DROP

    The specified IP Filter Driver immediately returns a drop response to the IP stack. IP address should discard data packets.

  • PF_PASS

    Specify the IP address filter driver to filter the packet and return the result to the IP stack. How the IP address filter driver processes the data packet filtering depends on the data packet filtering API settings.

    If the filter hook decides not to process the package and is sent to the IP Filter Driver to filter the package, the pass response is returned.

Although the DDK document only contains these three values, you can viewPfhook. h
(Filter the hook driver to include) and you will see another value. The value is pf_icmp_on_drop. I guess this value corresponds to the discarded packet and an ICMP packet is used to feedback the error message.

As you can see in the definition of the filter function, data packets and their headers are transmitted by pointers. Therefore, you can modify the header or load and then pass the packet. This is useful, for example, Network Address Translation (NAT ). If you change the target address, the IP address routes the package.

In my implementation, the filter function compares each package with a list of rules introduced by the user application. This list is implemented using a linked list, which is created by each start_ip_hook IOCTL at runtime. You can see this in my code.

Code

The first version of this article contains a simple example, But I updated it into a more complex example because someone wanted me to help him develop the actual program. In the new example, a small data packet filtering program is used. With this new program, you can implement your own filtering rules, just as you do in some commercial firewall software.

In the first version, the program contains two components:

  • User Application: an MFC application is used to manage filtering rules. This application sends rules to the driver and determines when the driver starts filtering. Data Filtering involves three steps:

    • Define the rules you need. You can use the add and delete commands to add or delete filter rules.
    • Install rules. When you have defined the rules, click the Install button to send them to the driver.
    • Start filtering. You only need to click the start button to start filtering.
  • Filter hook DRIVER: filters IP data packets based on the filter rules received by the user application.

The filter hook driver must be in the same directory as the executable file of your application.

Why do I use this method to develop a firewall?

This is not the only way to develop a firewall in windows. There are other firewall such as NDIS firewall, TDI firewall, Winsock layer firewall, and data packet filtering API ,...... So I have summarized some advantages and disadvantages of the filter hook driver for your reference in the future.

  • This method provides great flexibility. You can filter all IP data packets (and upper-layer data packets ). However, you cannot filter lower-layer headers. For example, you cannot filter Ethernet frames. You need to use NDIS Filtering for implementation, which is more complex and flexible.
  • This is a simple method. Installing the firewall and implementing the filter function are simple. However, the data packet filtering API is simpler, although not flexible. You cannot access the content of a data packet or modify it using the data packet filtering API.

Result: The filter hook driver is not the best, but it has no bad features. But why is this method not used in commercial products?

The answer is simple. Although this driver has no bad features, it has a major disadvantage. As I mentioned earlier, only one filter function can be installed at a time. We can develop a powerful firewall that may be downloaded and installed by thousands of users. However, if other applications use filtering (and filter functions have been installed before ), our program can't do anything.

This method has another disadvantage that is not mentioned in Microsoft's documentation. Although the DDK document says that you can access the data packet content in the filter function, it is not true. You can access the content of the received packet, but for the sent packet, you can only read IP and TCP, UDP or ICMP headers. I don't understand why ......

In Windows XP, Microsoft introduces a firewall hook driver that does not have this restriction. Its installation is very similar, but Microsoft does not recommend it to "it consumes too many network stacks ". The driver may disappear in later Windows versions.

Conclusion

Okay, it's the end. This is not the best way to develop a firewall (I mentioned earlier ). But I think this is a good start for people who are looking for information and interested in it.

I hope you can get some help and start to develop a powerful firewall.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.