Develop a firewall for Windows 2000/XP
Author: Jes ús o
Translation: powercpp
Download the source code
Introduction
If you decide to develop a firewall under Linux, you will find a lot of free information and source code. However, it may be difficult to develop a firewall on the Windows platform. It is impossible to find the relevant information and code.
Therefore, I decided to write this article to introduce a simple way to develop a firewall under Windows 2000/XP.
Background
In Windows 2000 DDK, Microsoft includes a new network driver called filter-hook driver. You can use it to filter data from all inbound and outbound interfaces.
Because there is very little code in this document, I will write the successful method of using it into the article, hoping to help you understand this simple method.
Filter-hook driver
As I mentioned earlier, the filter-hook driver is introduced in Microsoft Windows 2000 DDK. In fact, it is not a new network driver, it only extends the IP Filter Driver function.
In fact, the filter-hook driver is not a network driver. It is a kernel mode driver ). in general, we provide a callback function in the filter-hook driver, and then use the IP Filter Driver to register the callback function. In this way, when a data packet is sent and received, the IP Filter Driver calls the callback function. So how do we implement these steps? Summary:
1) create a filter-hook driver. We must create a kernel-mode driver. You can select a name, DOS name, and other driver features. These are not required, but I recommend using a description name.
2) if we want to install the filter function, we must first obtain the pointer to the IP Filter Driver, which is the second step.
3) We have obtained the pointer. Now we can install the filter function by sending a special IRP. The data transmitted by the message contains the pointer of the filter function.
4) filter data packets !!!
5) when we want to end filtering, we must cancel the filtering function. This is achieved by passing a null pointer as a filter function pointer.
Oh, there are only five steps, which seems very easy, but... how to generate the kernel-mode driver? How to get the IP Filter Driver pointer and how to... yes, please wait. I will explain these steps and provide the source code: P
Create a kernel mode driver)
Filter-hook driver is a kernel-mode driver, so we need to create a kernel-mode driver. This article is not a guide like "How to Develop the kernel mode driver in just five minutes". Therefore, I assume that the reader has knowledge about this.
The filter-hook driver structure is a typical kernel-mode driver structure:
1) A driver portal for device creation, a standard routine for creating symbolic connections and processing IRPs (dispatch, load, unload, and create...) for communication.
2) Manage IRPs in standard routines. Before coding, I suggest you first think about which IOCTL you need to expose to applications from the device driver. 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 a new filter rule ), clear_filter (clear all rules ).
3) For our drivers, we must implement multiple functions for filtering.
I recommend that you use a tool program to generate the basic kernel driver framework, so that you only need to add code to it. For example, quicksys is used in the project.
The following is the implementation code of the driver structure:
Ntstatus DriverEntry (in pdriver_object driverobject, in punicode_string registrypath ){//.... dprintf ("drvfltip. SYS: Entering DriverEntry/N "); // You must create the rtlinitunicodestring (& devicenameunicodestring, nt_device_name); ntstatus = iocreatedevice (driverobject, 0, & devicenameunicodestring, success, 0, false, & deviceobject); If (nt_success (ntstatus) {// create a symbolic connection so that the Win32 application can process the driver and the device rtlinit Unicodestring (& devicelinkunicodestring, dos_device_name); ntstatus = iocreatesymboliclink (& devicelinkunicodestring, & devicenameunicodestring );//.... // create a dispatch pointer driverobject for control, creation, and closure-> majorfunction [irp_mj_create] = driverobject-> majorfunction [irp_mj_close] = driverobject-> majorfunction [destroy] = 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 ){//.... 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) {// enable the ioctl code for filtering case start_ip_hook: {setfilterfunction (cbfilterfunction); break;} // disable the ioctl case stop_ip_hook: {setfilterfunction (null); break;} // IOCTL case add_filter: {If (inputbufferlength = sizeof (ipfilter) {ipfilter * NF; NF = (ipfilter *) iobuffer; addfiltertolist (NF);} break;} // release the ioctl case clear_filter: {clearfilterlist (); break;} default: IRP-> iostatus. status = status_invalid_parameter; dprintf ("drvfltip. SYS: Unknown irp_mj_device_control/N "); break;} ntstatus = IRP-> iostatus. status; iocompleterequest (IRP, io_no_increment); // we do not have any pending operations, so we always return the status code return ntstatus;} void drvunload (in pdriver_object driverobject) {unicode_string devicelinkunicodestring; dprintf ("drvfltip. SYS: unloading/N "); setfilterfunction (null); // release all resources clearfilterlist (); // delete a symbolic connection rtlinitunicodestring (& devicelinkunicodestring, dos_device_name ); iodeletesymboliclink (& devicelinkunicodestring); // Delete the device object iodeletedevice (driverobject-> deviceobject );}
We have compiled the main driver code. Next we will continue to implement the filter-hook driver
Register a filter function
In the above Code, you have seen the setfilterfunction (...) function. I will execute this function in the IP Filter Driver to register the filter function. The steps are as follows:
1) first, we must obtain the IP Filter Driver pointer, which requires that the driver has been installed and executed. To ensure that the IP Filter Driver has been installed and executed, load and start the IP Filter Driver in my user program before loading the driver.
2) in step 2, we must establish an IRP that uses ioctl_pf_set_extension_pointer as the control code. We must pass the pf_set_extension_hook_info parameter, which contains a pointer to the filter function. If you want to uninstall the function, you must pass null as the filter function pointer in the same step.
3) Send an IRP to the device driver. There is a big problem here. Only one filter function can be installed. Therefore, if another application has installed a filter function, you can no longer install it.
The code for setting the filter function is as follows:
Ntstatus setfilterfunction (effecfilterfunction) {ntstatus status = success, waitstatus = success; unicode_string filtername; pdevice_object ipdeviceobject = NULL; pfile_object ipfileobject = NULL; Disable filterdata; kevent event; Disable iostatus; pirp; dprintf ("getting pointer to ipfilterdriver/N"); // first, we need to obtain the ipfilterdriver device pointer rtli. Nitunicodestring (& filtername, dd_ipfltrdrvr_device_name); status = iogetdeviceobjectpointer (& filtername, standard_rights_all, & ipfileobject, & ipdeviceobject); If (nt_success (Status )) {// use the filter function as the parameter to initialize the pf_set_extension_hook_info structure filterdata. extensionpointer = filterfunction; // We Need to initialize the event to notify us of keinitializeevent (& event, icationicationevent, false) after the work is completed; // create the IRP = iobuilddeviceiocontrolreque used to set up the filter function ST (ioctl_pf_set_extension_pointer, ipdeviceobject, if (IRP! = NULL) {// send IRP status = iocalldriver (ipdeviceobject, IRP); // then wait for the response from the 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 space cannot be allocated, the corresponding error code status = status_insufficient_resources is returned; 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 ;}
After creating a filter function, you must release the file object after obtaining the pointer of the device driver. I use events to notify the ipfilter driver that IRP processing has been completed.
Filter Functions
We already know how to develop a driver and install a filter function, but we do not know anything in the filter function. When the host receives or sends a data packet, the filter function is always called and the system determines how to process the data packet based on the return value of the function.
The function prototype is as follows:
Typedef pf_forward_action (* packetfilterextensionptr) (// ip packet header in unsigned char * packetheader, // packet length excluding the header in unsigned char * packet, // packet length. It does not include the length of the IP header in unsigned int packetlength, // The number of the interface adapter that receives data in unsigned int recvinterfaceindex, // The number of the interface adapter that sends data in unsigned int sendinterfaceindex, // the IP address of the adapter that receives the data packet in ipaddr recvlinknexthop, // the IP address of the adapter that sends the data packet in ipaddr sendlinknexthop );
Pf_forward_action is of the enumeration type and may have the following values:
Pf_forward indicates that the IP Filter Driver transmits data to the IP stack immediately. For local data packets, the IP address is sent to the stack. If the target machine is another machine and the route is allowed, it will be sent through IP routing.
Pf_drop indicates that the IP Filter Driver discards the IP packet.
Pf_pass indicates the IP Filter Driver to filter the data packet. If the IP Filter Driver processes the data packet, it depends on the packet filtering API settings. The response of the pass returned by the filter hook indicates that it does not process the data packet and the IP Filter Driver filters the data packet.
Although the DDK document only contains these three values, if you view the pfhook. H (the header file required by the filter-hook driver) you can find another pf_icmp_on_drop file. I guess this value is used to discard ICMP packets.
In the definition of the filter function, the pointer to the data header is passed in. Therefore, you can modify the data header and send it. This is very useful for Nat translation. You can modify the destination address of the data packet and select an IP route.
In my implementation, the filter function compares each package with the rule list according to the requirements of the user program. This list is the connection list and is created using IOCTL of start_ip_hook during running. You can see this in the code.
Source code
In the first version, I only included simple examples. As many friends asked me to help them develop practical applications, I improved this example. The new example is the data packet filtering program. In this new program, you can add your filtering rules like commercial firewalls.
In the first version, an application consists of two parts:
(1) user application: This is the MFC Application for managing filtering rules. The program sends a rule to the driver and determines when to start filtering. There are three steps:
1) define the required rules. You can add or delete custom rules.
2) install rules. After the rules are defined, click Install to send them to the driver.
3) Enable filtering. You can click the start button to start filtering.
(2) filter-hook DRIVER: filters data packets according to the filter rules received from the user application. The filter-hook driver must be in the same directory as the user application.
Why use this method to develop a firewall?
In Windows, this is not the only way to develop a firewall. Other methods include NDIS firewall, TDI firewall, Winsock layered firewall, and packet filtering API ,... therefore, I will explain the advantages and disadvantages of the filter-hook driver so that you can decide whether to adopt this driver when developing the firewall in the future.
1) the elasticity of This method enables you to filter all IP layer (or above) communication. However, you cannot filter lower-level header data. For example, you cannot filter ethereframe data. You need to use the NDIS filter. Although development is difficult, it is more flexible.
2) This is a simple method. It is very easy to install the firewall and perform the filtering function. However, the packet filtering API is easier to use, although it lacks elasticity. For example, you cannot process the package content, and you cannot use the packet filtering API to filter the modified content.
Conclusion: The filter-hook driver is not the best but not bad. But why is it not used in commercial products?
The answer is simple: although this type of driver is not bad, it has a huge disadvantage: As I mentioned earlier, only one filter function can be installed at a time. We have developed a powerful firewall that can be downloaded by thousands of users. If other applications already use a filter (with a filter function installed), our program will not work normally.
Another disadvantage of this method is that it is not supported by Microsoft's official plaintext. Although the DDK document says that you can process the package content in the filter function, this is not the case. You can process packet content when receiving data packets, but you can only read IP, TCP, UDP, or ICMP data packets when sending packets. I don't know why ....
Microsoft introduced that another driver in Windows XP does not have such restrictions, that is, firewall hook driver. However, Microsoft does not recommend it because "it ran too high in the network stack ". The driver may disappear in later Windows versions.
End:
Okay, I know this is not the best way to develop a firewall (I have mentioned its huge shortcomings ), but I think this is a good start for people who are studying this or interested in it. I hope you can understand and develop a powerful firewall.