- Author: Jes ús O.
- Address: http://www.codeproject.com/internet/FwHookDrv.asp
- Translation: zerray
- Download source file-103 Kb
- Download the sample program-20.5 KB
Introduction
Perhaps, the firewall hook driver is the least documented method for developing a data packet filtering program in windows. Microsoft didn't provide any documentation about it. The only thing you can find is the DDK header file (ipfirewall. h ). In fact, when I installed Windows 2000 DDK, I was very surprised to find this. h file (and its content) because no documentation mentioned the existence of firewall hooks. In the next version of DDK, Microsoft added some documents about it: "This method exists but is not recommended ".
However, because it is a simple way to implement the firewall, I think it is interesting to understand how the firewall hook driver works.
Firewall hook driver
I don't understand why Microsoft doesn't recommend using the firewall hook driver for development. I do not recommend developing a complete firewall solution, but it is a good choice for small programs. Basically, the firewall hook driver can be used as a filter hook Driver (see my article "Developing a firewall in Windows 2000/XP".
.
You may remember that the filter hook function allows only one filter function to be installed in the system. If a program already uses this function, your program will be useless. You will not encounter this problem when using the firewall hook driver. You can install all the filter functions you need. Each filter function is assigned a priority, so the system calls the filter function one by one (in order of priority) to know that a function returns "Drop packet (discard packet )". If all functions return "allow packet", the package is allowed to pass. You can think of it as a filter function chain. When one of them returns the "Drop packet" chain, it is cut off. The order of each function in a chain is given by its priority value.
In, I represent the following process:
- Your host receives a package. The IP driver has a list of filter functions sorted by priority (filter function 1 is a function with a higher priority ).
- First, the IP driver passes the packet to the filtering function with the highest priority and waits for the returned value.
- Filter Function 1 returns "allow packet ".
- Because filter function 1 allows packets, the IP driver passes the packet to the next filter function: filter function 2.
- In the figure below, filter function 2 returns "Drop packet ". Therefore, the IP driver discards the package and does not continue to call the next filter function.
Another problem that you will find when filtering the hook driver is sending packets. You cannot access the data content in the package. However, you can use firewall hooks to drive access to all data. The data structure received by the firewall hook filter function is more complex than that of the hook filter driver. It is closer to the structure of the NDIS driver, and the entire package is composed of a buffer chain. But please be patient and you will see more later.
Like the filter hook driver, the firewall hook driver is only a kernel-mode driver used to install the callback function (but the firewall hook driver installs the callback function in the IP driver ). In fact, the process of installing the firewall hook driver is similar to installing the filter hook driver. In the ipfirewall. h file, you will see the following content:
typedef struct _IP_SET_FIREWALL_HOOK_INFO
{
// Packet filter callout.
IPPacketFirewallPtr FirewallPtr;
// Priority of the hook
UINT Priority;
// if TRUE then ADD else DELETE
BOOLEAN Add;
} IP_SET_FIREWALL_HOOK_INFO, *PIP_SET_FIREWALL_HOOK_INFO;
#define DD_IP_DEVICE_NAME L//Device//Ip
#define _IP_CTL_CODE(function, method, access) /
CTL_CODE(FSCTL_IP_BASE, function, method, access)
#define IOCTL_IP_SET_FIREWALL_HOOK /
_IP_CTL_CODE(12, METHOD_BUFFERED, FILE_WRITE_ACCESS)
These lines of code tell you how to install the callback function. You only need to use the data of your callback function to fill in the ip_set_firewall_hook_info structure and install it to send IOCTL ioctl_ip_set_firewall_hook to the IP device. Simple: If you have dealt with the driver or used the documented filter hook function. An important parameter of this structure is the priority field. Each priority field contains the priority of the filter function, and its value increases.
PDEVICE_OBJECT ipDeviceObject=NULL;
IP_SET_FIREWALL_HOOK_INFO filterData;
//.....
// Init structure filterData.
FirewallPtr = filterFunction;
filterData.Priority = 1;
filterData.Add = TRUE;
//....
// Send the commando to ip driver
IoCallDriver(ipDeviceObject, irp);
If you want to uninstall the filter function, you can use the same code, as long as you change the value of filterdata. Add to false.
Filter Functions
The filter function of the firewall hook driver is more complex than that of the filter hook driver. Because there is no document on the function and its parameters, the complexity is increased. This function has the following features:
FORWARD_ACTION cbFilterFunction(VOID **pData,
UINT RecvInterfaceIndex,
UINT *pSendInterfaceIndex,
UCHAR *pDestinationType,
VOID *pContext,
UINT ContextLength,
struct IPRcvBuf **pRcvBuf);
With patience and debugging methods (and explanation of parameter names), I got the following information about the parameters:
pData |
* Pdata points to a struct iprcvbuf * structure.
|
RecvInterfaceIndex |
The interface for receiving data. |
pSendInterfaceIndex |
A pointer to an unsigned integer that contains the index value sent by the data. Although it is a pointer, changing its value does not change the path of the package :(. |
pDestinationType |
A pointer to an unsigned integer, indicating the target type: local network, remote, broadcast, multicasting, and so on. |
pContext |
Pointer to the firewall_context_t structure, so that you can get the package information like inbound and outbound packages. |
ContextLength |
The size of the buffer. The value is always sizeof (firewall_context_t ). |
pRcvBuf |
* Prcvbuf always points to null. |
This information may be changed in future Windows versions because no official documentation is available. I can only ensure that this is the meaning of these domains I have tested in Windows 2000 and Windows XP.
For each package, our function will be called and, depending on its return value, the package will be discarded or released. In the filter function, you can return the following values:
FORWARD |
Packages are allowed. |
DROP |
Package discarded. |
ICMP_ON_DROP |
The packet is discarded and an ICMP packet is sent to the remote host. |
Release Buffer
In the firewall hook filter function, you do not directly receive a buffer with a header and content as you did in the filter hook driver. After some tests, I figured out the internal structure of the buffer. As I mentioned earlier, the sending/receiving package is passed to the pdata parameter. * Pdata points to an iprcvbuf structure:
struct IPRcvBuf
{
// Point to the next buffer in the chain
struct IPRcvBuf *ipr_next;
// Always 0
UINT ipr_owner;
// Buffer data
UCHAR *ipr_buffer;
// Buffer data size
UINT ipr_size;
// In my tests always a pointer to NULL.
// Maybe the system could use MDLs instead of IPRcvBuf structures (but
// i never have seen it).
PMDL ipr_pMdl;
// Always a pointer to NULL.
UINT *ipr_pClientCnt;
// Always a pointer to NULL.
UCHAR *ipr_RcvContext;
// Always 0. I suppose this field is a offset into buffer data
// but because I haven't a value different from 0, I can affirm it.
UINT ipr_RcvOffset;
// In Windows 2003 DDK the name of this field have changed to flags.
// In my tests I always get 0 value for local traffic and 2 for remote.
// It's the only thing I can tell you about this field.
ULONG ipr_promiscuous;
};
According to our intention, we only need to know the domain ipr_next, ipr_buffer and ipr_size. The ipr_buffer field contains the packet's ipr_size bytes. However, the entire package does not have to be in one buffer, and the system can link Multiple buffers. For this reason, the ipr_next field is used. This field points to the next structure with package data. When ipr_next in the data structure points to null, we get the entire data packet. Therefore, we found the link Buffer Structure in the firewall hook driver, as seen in the NDIS driver. In my tests, for all received packets, the function only receives one structure with all the data in its buffer. For the sent package, I found several links buffer, each containing information about a protocol. I mean, for example, when I send an ICMP packet, there will be three link buffers: one with an IP header, one with an ICMP header, and the other with data. However, just like what we do with the NDIS driver, we cannot rely on how the system fills in These buffers.
In the figure below, you can see how to build data packets in the firewall hook DRIVER:
The following code shows how to obtain an orthodox buffer with packet content from the link Buffer:
char *pPacket = NULL;
int iBufferSize;
struct IPRcvBuf *pBuffer = (struct IPRcvBuf *) *pData;
// First, I calculate the total size of the packet
iBufferSize = buffer->ipr_size;
while(pBuffer->ipr_next != NULL)
{
pBuffer = pBuffer->ipr_next;
iBufferSize += pBuffer->ipr_size;
}
// Reserve memory to the lineal buffer.
pPacket = (char *) ExAllocatePool(NonPagedPool, iBufferSize);
if(pPacket != NULL)
{
unsigned int iOffset = 0;
pBuffer = (struct IPRcvBuf *) *pData;
// we are going to copy each buffer of the chain in the lineal buffer.
memcpy(pPacket, pBuffer->ipr_buffer, pBuffer->ipr_size);
while(pBuffer->ipr_next != NULL)
{
iOffset += pBuffer->ipr_size;
pBuffer = pBbuffer->ipr_next;
memcpy(pPacket + iOffset, pBuffer->ipr_buffer,
pBbuffer->ipr_size);
}
}
Also, for all curious people (before you ask me: P), you can modify the package data and take the risk on your own. There is no tool for such software. To do something similar, I suggest you implement an NDIS im driver or TDI filter driver. I didn't perform much tests, but I don't trust the firewall hook driver to change the stability of the packet content. Why? Because we don't know how the IP driver manages these buffering and how much risk it takes to modify them. In a word, I suggest you do not touch it.
It's time to combine!
Now we know the syntax of the filter function and the format of the package passed to it. Now, we need to know how to combine these two things into a data packet filtering program. The method I used is to define a filter function similar to that used in the filter hook driver, because it is easier to understand. Because the parameters passed to the filter function are different in the two drivers, for the firewall hook, I implemented an intermediate function (the actual firewall hook filter function) to wrap the filter function. I mean, in the firewall hook filter function, I use a function to process the package, copy it to an orthodox buffer, and pass it to the filter function. With the following code, I think you can better understand it:
FORWARD_ACTION cbFilterFunction(VOID **pData,
UINT RecvInterfaceIndex,
UINT *pSendInterfaceIndex,
UCHAR *pDestinationType,
VOID *pContext,
UINT ContextLength,
struct IPRcvBuf **pRcvBuf)
{
FORWARD_ACTION result = FORWARD;
char *pPacket = NULL;
int iBufferSize;
struct IPRcvBuf *pBbuffer =(struct IPRcvBuf *) *pData;
PFIREWALL_CONTEXT_T fwContext = (PFIREWALL_CONTEXT_T)pContext;
IPHeader *pIpHeader;
// Convert chained buffer to lineal buffer as we see before.
// This won't be the fastest code but
// will help us to understand better the method.
// ...........
pIpHeader = (IPHeader *)pPacket;
// Call the real filter function and return result
result = FilterPacket(pPacket,
// length in bytes = ipp->headerLength * (32 bits/8)
pPacket + (pIpHeader ->headerLength * 4),
iBufferSize - (pIpHeader ->headerLength * 4),
(fwContext != NULL) ? fwContext->Direction: 0,
RecvInterfaceIndex,
(pSendInterfaceIndex != NULL) ? *pSendInterfaceIndex : 0);
return result;
}
Code
You can quickly recognize the program of this article. Yes, the GUI is exactly the same as I used in the filter hook driver. Why? Because I wrote a simple packet filtering program to test all the firewall methods I wrote. In this way, I have a general graphical interface for them to provide the same functions, but at the underlying layer, they work differently. I have different versions of this program (only make the smallest changes) to test my filter hook driver, firewall hook driver, LSP DLL, TDI filter driver, NDIS driver ...... Therefore, I think this method is easy to understand. The graphic interface is almost unchanged, so you only want to understand the new method used.
Like other articles, this program only filters packets. Many people ask me to add more features, such as package record, installation as a service ...... But I want to follow the idea of providing methods rather than answering. If you want to add some of these features, I'm very happy :). You can contact me to ask all the questions you want.
Conclusion
Well, once I finish writing this article, it will be yours. I hope you can learn as many things as you can. Enjoy it !!