xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 Xcon English Version

基于Filter-Hook Driver(使用ipfirewall.h)的IP过滤驱动


创建时间:2007-02-27
文章属性:原创
文章提交:fleshwound (fleshwound_at_126.com)

Author:  fleshwound
Email:   fleshwound@126.com
Homepage:http://www.smatrix.org
IP过滤驱动可以广泛的应用于网络安全产品的研发,NDIS和TDI的驱动资料很多,有比较成熟的代码可以参考,但是使用IPFIREWALL.h开发的IP过滤驱动的资料非常少,这次自己做一个软件的时候参考VCKBASE上的一篇文章《开发Windows 2000/XP下的防火墙》(作者:Jesús O)的基础上,写了一个驱动,代码都做了详细的注释了,只要稍微有点驱动设计基础的都可以看得懂,我把自己的特殊的回调函数去掉了,保留了基本的完整框架,牛人就不需要看了,初学者都可以在此基础上继续快速开发。
1 SmatrixIPDiv.cpp文件
2  protocol.h头文件
3  SmatrixIPDiv.h头文件

* Copyright (c) 2007,安全矩阵(Security Matrix)
* All rights reserved.
*
* 文件名称:SmatrixIPDiv.cpp
* 文件标识:S
* 摘    要:IP过滤驱动,利用ipfirewall捕获包、分析包、过滤包
* 开始时间:2006年12月26日
*
* 当前版本:1.0
* 作    者:fleshwound@126.com
* 相关信息:http://www.smatrix.org
* 完成日期:2007年1月2日
*/
extern "C"
{
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ntddk.h>
    #include <ntddndis.h>
    #include <pfhook.h>
    #include <ndis.h>
    #include <ipfirewall.h>

}

#include "SmatrixIPDiv.h"
#include "protocol.h"

///////////////////////// 自定义函数的声明///////////////////////
//关闭打开驱动函数
NTSTATUS DispatchCreateClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);

//驱动卸载函数
void DriverUnload(PDRIVER_OBJECT pDriverObj);

//IO控制派遣函数(内核消息处理)
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp);


// 向过滤列表中添加一个过滤规则
NTSTATUS AddFilterToList(CIPFilter* pFilter);

//清除过滤列表
void ClearFilterList();

// 注册钩子回调函数
NTSTATUS SetFilterFunction(IPPacketFirewallPtr filterFunction, BOOLEAN load);

//包过滤函数
FORWARD_ACTION FilterPacket(unsigned char *PacketHeader,
                  unsigned char *Packet,
                  unsigned int PacketLength,
                  DIRECTION_E direction,
                  unsigned int RecvInterfaceIndex,
                  unsigned int SendInterfaceIndex);

//IP过滤器函数
FORWARD_ACTION IPFilterFunction(VOID         **pData,
                    UINT         RecvInterfaceIndex,
                    UINT         *pSendInterfaceIndex,
                    UCHAR         *pDestinationType,
                    VOID         *pContext,
                    UINT         ContextLength,
                    struct IPRcvBuf **pRcvBuf);
// 过滤列表首地址
struct CFilterList* g_pHeader = NULL;


// 驱动内部名称和符号连接名称
#define DEVICE_NAME L"\\Device\\DevSMFltIP"
#define LINK_NAME L"\\DosDevices\\DrvSMFltIp"

//驱动入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
    NTSTATUS status = STATUS_SUCCESS;

    // 初始化各个派遣例程
    pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
    pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
    pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
    pDriverObj->DriverUnload = DriverUnload;

        // 创建、初始化设备对象
    // 设备名称
    UNICODE_STRING ustrDevName;
    RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
    // 创建设备对象
    PDEVICE_OBJECT pDevObj;
    status = IoCreateDevice(pDriverObj,
                0,
                &ustrDevName,
                FILE_DEVICE_DRVFLTIP,
                0,
                FALSE,
                &pDevObj);
    if(!NT_SUCCESS(status))
    {
        return status;
    }

        // 创建符号连接名称
    // 符号连接名称
    UNICODE_STRING ustrLinkName;
    RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
    // 创建关联
    status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
    if(!NT_SUCCESS(status))
    {
        IoDeleteDevice(pDevObj);
        return status;
    }
    
    return STATUS_SUCCESS;
}


void DriverUnload(PDRIVER_OBJECT pDriverObj)
{    
    // 卸载过滤函数
    SetFilterFunction(IPFilterFunction,FALSE);
    // 释放所有资源
    ClearFilterList();

    // 删除符号连接名称
    UNICODE_STRING strLink;
    RtlInitUnicodeString(&strLink, LINK_NAME);
    IoDeleteSymbolicLink(&strLink);

    // 删除设备对象
    IoDeleteDevice(pDriverObj->DeviceObject);
}

// 处理IRP_MJ_CREATE、IRP_MJ_CLOSE功能代码
NTSTATUS DispatchCreateClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
    pIrp->IoStatus.Status = STATUS_SUCCESS;
//    pIrp->IoStatus.Information = 0;
    // 完成此请求
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}


// I/O控制派遣例程
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
    NTSTATUS status = STATUS_SUCCESS;

    // 取得此IRP(pIrp)的I/O堆栈指针
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

    // 取得I/O控制代码
    ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    // 取得I/O缓冲区指针和它的长度
    PVOID pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    ULONG uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    
    // 响应用户的命令
    switch(uIoControlCode)
    {
    case START_IP_HOOK:    // 开始过滤
        status = SetFilterFunction(IPFilterFunction,TRUE);
        break;

    case STOP_IP_HOOK:    // 停止过滤
        status = SetFilterFunction(IPFilterFunction,FALSE);
        break;

    case ADD_FILTER:    // 添加一个过滤规则
        if(uInSize == sizeof(CIPFilter))
            status = AddFilterToList((CIPFilter*)pIoBuffer);
        else
            status = STATUS_INVALID_DEVICE_REQUEST;
        break;

    case CLEAR_FILTER:    // 释放过滤规则列表
        ClearFilterList();
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }
    
    // 完成请求
    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    return status;
}



///////////////////////////////////////////////////////////////////
//过滤列表

// 向过滤列表中添加一个过滤规则
NTSTATUS AddFilterToList(CIPFilter* pFilter)
{
    // 为新的过滤规则申请内存空间
    CFilterList* pNew = (CFilterList*)ExAllocatePool(NonPagedPool, sizeof(CFilterList));
    if(pNew == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    // 填充这块内存
    RtlCopyMemory(&pNew->ipf, pFilter, sizeof(CIPFilter));
    
    // 连接到过滤列表中
    pNew->pNext = g_pHeader;
    g_pHeader = pNew;

    return STATUS_SUCCESS;
}

// 清除过滤列表
void ClearFilterList()
{
    CFilterList* pNext;
    // 释放过滤列表占用的所有内存
    while(g_pHeader != NULL)
    {
        pNext = g_pHeader->pNext;
        // 释放内存
        ExFreePool(g_pHeader);
        g_pHeader = pNext;
    }
}



// 包过滤函数
FORWARD_ACTION FilterPacket(unsigned char *PacketHeader,
                  unsigned char *Packet,
                  unsigned int PacketLength,
                  DIRECTION_E direction,
                  unsigned int RecvInterfaceIndex,
                  unsigned int SendInterfaceIndex)
{

    // 提取IP头
    IPHeader* pIPHdr = (IPHeader*)PacketHeader;
    TCPHeader *pTCPHdr = NULL;
    UDPHeader *pUDPHdr = NULL;

    if(pIPHdr->ipProtocol == 6)  // 是TCP协议
    {
        // 提取TCP头
         pTCPHdr = (TCPHeader*)Packet;
        // 我们接受所有已经建立连接的TCP封包
        if(!(pTCPHdr->flags & 0x02))
        {
            return FORWARD;
        }
    }

    // 与过滤规则相比较,决定采取的行动
    CFilterList* pList = g_pHeader;
    while(pList != NULL)
    {
        // 比较协议
        if(pList->ipf.protocol == 0 || pList->ipf.protocol == pIPHdr->ipProtocol)
        {
            // 查看源IP地址
            if(pList->ipf.sourceIP != 0 &&
                (pList->ipf.sourceIP & pList->ipf.sourceMask) != pIPHdr->ipSource)
            {
                pList = pList->pNext;
                continue;
            }

            // 查看目标IP地址
            if(pList->ipf.destinationIP != 0 &&
                (pList->ipf.destinationIP & pList->ipf.destinationMask) != pIPHdr->ipDestination)
            {
                pList = pList->pNext;
                continue;
            }

            // 如果是TCP封包,查看端口号
            if(pIPHdr->ipProtocol == 6)
            {
                pTCPHdr = (TCPHeader*)Packet;
                if(pList->ipf.sourcePort == 0 || pList->ipf.sourcePort == pTCPHdr->sourcePort)
                {
                    if(pList->ipf.destinationPort == 0
                        || pList->ipf.destinationPort == pTCPHdr->destinationPort)
                    {
                        // 现在决定如何处理这个封包
                        if(pList->ipf.bDrop)
                            return DROP;
                        else
                            return FORWARD;
                    }
                }
            }

            // 如果是UDP封包,查看端口号
            else if(pIPHdr->ipProtocol == 17)
            {
                pUDPHdr = (UDPHeader*)Packet;
                if(pList->ipf.sourcePort == 0 || pList->ipf.sourcePort == pUDPHdr->sourcePort)
                {
                    if(pList->ipf.destinationPort == 0
                        || pList->ipf.destinationPort == pUDPHdr->destinationPort)
                    {
                        // 现在决定如何处理这个封包
                        if(pList->ipf.bDrop)
                            return DROP;
                        else
                            return FORWARD;
                    }
                }
            }
            else
            {
                // 对于其它封包,我们直接处理
                if(pList->ipf.bDrop)
                        return DROP;
                    else
                        return FORWARD;
            }
        }

        // 比较下一个规则
        pList = pList->pNext;
    }

    // 我们接受所有没有注册的封包
    return FORWARD;
}

// 注册钩子回调函数
NTSTATUS SetFilterFunction(IPPacketFirewallPtr filterFunction, BOOLEAN load)
{
    //{变量定义BEGIN}
    NTSTATUS status = STATUS_SUCCESS;           //内核状态
  
    NTSTATUS waitStatus = STATUS_SUCCESS;       //受信状态

    PDEVICE_OBJECT pDeviceObj = NULL;           //pDeviceObj变量将指向IP过滤驱动设备对象
    
    PFILE_OBJECT pFileObj = NULL;               //内核过滤器设备

    IP_SET_FIREWALL_HOOK_INFO filterData;        //IP_SET_FIREWALL_HOOK_INFO结构
    
    UNICODE_STRING ustrFilterDriver;             // IP过滤驱动的名称

    KEVENT event;                                //
    
    IO_STATUS_BLOCK ioStatus;                    //
  
    PIRP pIrp;                                   //

    //{变量定义END}

    // 初始化IP过滤驱动的名称
    RtlInitUnicodeString(&ustrFilterDriver, DD_IP_DEVICE_NAME);
  
    // 取得设备对象指针    
    status = IoGetDeviceObjectPointer(&ustrFilterDriver, STANDARD_RIGHTS_ALL, &pFileObj, &pDeviceObj);
    

    if(!NT_SUCCESS(status))
    {
        return status;
    }

    /////////// 使用到IP过滤驱动中设备对象的指针创建一个IRP///////////////////////////    
    // 填充IP_SET_FIREWALL_HOOK_INFO结构

    filterData.FirewallPtr = filterFunction;
    filterData.Priority = 1;
    filterData.Add = load;

    // 我们需要初始化一个事件对象。
    // 构建IRP时需要使用这个事件内核对象,当IP过滤取得接受到此IRP,完成工作以后会将它置位

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    // 为设备控制请求申请和构建一个IRP
    
    pIrp = IoBuildDeviceIoControlRequest(IOCTL_IP_SET_FIREWALL_HOOK,  // io control code
        pDeviceObj,
        (PVOID) &filterData,
        sizeof(IP_SET_FIREWALL_HOOK_INFO),
        NULL,
        0,
        FALSE,
        &event,
        &ioStatus);

    if(NULL==pIrp)
    {
        // 如果不能申请空间得到pIrp,返回对应的错误代码
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    ////////////////////////////////////////////////////////////////
    
    /////////////// 请求安装钩子回调函数/////////////////////////////

    // 发送此IRP到IP过滤驱动
    status = IoCallDriver(pDeviceObj, pIrp);

    // 等待IP过滤驱动的通知
    if(status == STATUS_PENDING)
    {
        waitStatus=KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        
        if (!NT_SUCCESS(waitStatus)) //受信状态不成功,返回
        {
            return waitStatus;
        }

    }

    status = ioStatus.Status;


    if(!NT_SUCCESS(status))//状态不成功,返回
    {
        return status;
    }
   /////////////////////////////////////////////////////////////////////////
    
    ////////////////////// 清除内核资源/////////////////////////////////////
    if(pFileObj != NULL)
        ObDereferenceObject(pFileObj);
    pDeviceObj = NULL;         //避免产生野指针
    pFileObj = NULL;            //避免产生野指针

    return status;

}

//IP过滤器函数
FORWARD_ACTION IPFilterFunction(VOID         **pData,
                    UINT         RecvInterfaceIndex,
                    UINT         *pSendInterfaceIndex,
                    UCHAR         *pDestinationType,
                    VOID         *pContext,
                    UINT         ContextLength,
                    struct IPRcvBuf **pRcvBuf)
{
    FORWARD_ACTION result = FORWARD;

    unsigned char *packet = NULL;

    int bufferSize = 0;

    struct IPRcvBuf *buffer =(struct IPRcvBuf *) *pData;
    
    PFIREWALL_CONTEXT_T fwContext = (PFIREWALL_CONTEXT_T)pContext;

    DIRECTION_E direction=IP_RECEIVE;
    
    //如果包指针不为空,IPRcvBuf中存在数据
    if(buffer != NULL)
    {
        bufferSize = buffer->ipr_size;
        
        while(buffer->ipr_next != NULL) //得到整个IPRcvBuf缓冲链中数据总长度
        {
            buffer = buffer->ipr_next;
            
            bufferSize += buffer->ipr_size;
        }
        
        //分配一个不分页的内存,将整个IPRcvBuf缓冲链放入其中
        packet = (unsigned char *) ExAllocatePool(NonPagedPool, bufferSize);
        if(packet != NULL)
        {
            IPHeader *ipp = (IPHeader *)packet;
            unsigned int offset = 0;
            
            buffer = (struct IPRcvBuf *) *pData;
            
            memcpy(packet, buffer->ipr_buffer, buffer->ipr_size);  
            
            while(buffer->ipr_next != NULL)
            {    
                offset += buffer->ipr_size;
                buffer = buffer->ipr_next;
                
                memcpy(packet + offset, buffer->ipr_buffer, buffer->ipr_size);
            }
            if (NULL != fwContext)
            {
                direction=fwContext->Direction;
            }
            else
            {
                direction=(DIRECTION_E)0;
            }

            //调用包检测函数,通过返回FORWARD,否则返回DROP
            result = FilterPacket(packet,
                packet + (ipp->ipHeaderLength * 4),
                bufferSize - (ipp->ipHeaderLength * 4),
                direction,
                RecvInterfaceIndex,
                (pSendInterfaceIndex != NULL) ? *pSendInterfaceIndex : 0);
            
            
        }
        
        
    }
    
    //释放分配的临时包缓存
    if(NULL != packet) ExFreePool(packet);
    
    return result;
}

2 定义常见的封包结构信息
//文件名称:protocol.h

typedef struct IPHeader
{    UCHAR      ipHeaderLength:4;      // 头长度
    UCHAR      ipVersion:4;          // 版本号
    UCHAR     ipTOS;          // 服务类型
    USHORT    ipLength;       // 封包总长度,即整个IP报的长度
    USHORT    ipID;          // 封包标识,惟一标识发送的每一个数据报
    USHORT    ipFlags;          // 标志
    UCHAR     ipTTL;          // 生存时间,就是TTL
    UCHAR     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等
    USHORT    ipChecksum;     // 校验和
    ULONG     ipSource;       // 源IP地址
    ULONG     ipDestination;  // 目标IP地址
} IPPacket;


typedef struct _TCPHeader
{
    USHORT            sourcePort;        // 源端口号
    USHORT            destinationPort;    // 目的端口号
    ULONG            sequenceNumber;        // 序号
    ULONG            acknowledgeNumber;    // 确认序号
    UCHAR            dataoffset;        // 数据指针
    UCHAR            flags;            // 标志
    USHORT            windows;        // 窗口大小
    USHORT            checksum;        // 校验和
    USHORT            urgentPointer;        // 紧急指针
} TCPHeader;

typedef struct _UDPHeader
{
    USHORT            sourcePort;        // 源端口号        
    USHORT            destinationPort;    // 目的端口号        
    USHORT            len;            // 封包长度
    USHORT            checksum;        // 校验和
} UDPHeader;
enum
  {
    IPPROTO_IP                    = 0,        // Dummy protocol for TCP.
    IPPROTO_HOPOPTS                = 0,        // IPv6 Hop-by-Hop options.  */
    IPPROTO_ICMP                = 1,        // Internet Control Message Protocol.  */
    IPPROTO_IGMP                = 2,        // Internet Group Management Protocol. */
    IPPROTO_IPIP                = 4,        // IPIP tunnels (older KA9Q tunnels use 94).  */
    IPPROTO_TCP                    = 6,        // Transmission Control Protocol.  */
    IPPROTO_EGP                    = 8,        // Exterior Gateway Protocol.  */
    IPPROTO_PUP                    = 12,        //    PUP protocol.  */
    IPPROTO_UDP                    = 17,        //    User Datagram Protocol.  */
    IPPROTO_IDP                    = 22,        //  XNS IDP protocol.  */
    IPPROTO_TP                    = 29,        //  SO Transport Protocol Class 4.  */
    IPPROTO_IPV6                = 41,        //    IPv6 header.  */
    IPPROTO_ROUTING                = 43,        //    IPv6 routing header.  */
    IPPROTO_FRAGMENT            = 44,        //    IPv6 fragmentation header.  */
    IPPROTO_RSVP                = 46,        //  Reservation Protocol.  */
    IPPROTO_GRE                    = 47,        //  General Routing Encapsulation.  */
    IPPROTO_ESP                    = 50,        // encapsulating security payload.  */
    IPPROTO_AH                    = 51,        // authentication header.  */
    IPPROTO_ICMPV6                = 58,        // ICMPv6.  */
    IPPROTO_NONE                = 59,        /* IPv6 no next header.  */
    IPPROTO_DSTOPTS                = 60,        /* IPv6 destination options.  */
    IPPROTO_MTP                    = 92,        /* Multicast Transport Protocol.  */
    IPPROTO_ENCAP                = 98,        /* Encapsulation Header.  */
    IPPROTO_PIM                    = 103,        /* Protocol Independent Multicast.  */
    IPPROTO_COMP                = 108,        /* Compression Header Protocol.  */
    IPPROTO_RAW                    = 255,        /* Raw IP packets.  */
    IPPROTO_MAX
  };
3  IP过滤驱动相关结构和宏定义
// 文件名称:SmatrixIPDiv.h
#ifndef __SMATRIXIPDIV_H__
#define __SMATRIXIPDIV_H__

// 自定义设备类型,在创建设备对象时使用
// 注意,自定义值的范围是32768-65535
#define FILE_DEVICE_DRVFLTIP  0x00654322

// 自定义的IO控制代码,用于区分不同的设备控制请求
// 注意,自定义值的范围是2048-4095
#define DRVFLTIP_IOCTL_INDEX  0x830

//
// 定义各种设备控制代码。分别是开始过滤、停止过滤、添加过滤规则、清除过滤规则
//
#define START_IP_HOOK    CTL_CODE(FILE_DEVICE_DRVFLTIP, \
                DRVFLTIP_IOCTL_INDEX, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define STOP_IP_HOOK    CTL_CODE(FILE_DEVICE_DRVFLTIP, \
                DRVFLTIP_IOCTL_INDEX+1, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define ADD_FILTER    CTL_CODE(FILE_DEVICE_DRVFLTIP, \
                DRVFLTIP_IOCTL_INDEX+2, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define CLEAR_FILTER    CTL_CODE(FILE_DEVICE_DRVFLTIP, \
                DRVFLTIP_IOCTL_INDEX+3, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 定义过滤规则的结构
struct CIPFilter
{
    USHORT protocol;        // 使用的协议

    ULONG sourceIP;            // 源IP地址
    ULONG destinationIP;        // 目标IP地址

    ULONG sourceMask;        // 源地址屏蔽码
    ULONG destinationMask;        // 目的地址屏蔽码

    USHORT sourcePort;        // 源端口号
    USHORT destinationPort;        // 目的端口号
    
    BOOLEAN bDrop;            // 是否丢弃此封包
};

// 过滤列表
struct CFilterList
{
    CIPFilter ipf;        // 过滤规则
    CFilterList* pNext;    // 指向下一个CFilterList结构
};

#endif // __SMATRIXIPDIV_H__
[注:这里感谢下sinister和驱动网若干牛人的热情指导和建议,需要驱动开发向导文件得可以到http://www.smatrix.org/bbs 下-by fleshwound]
参考文献:
1开发Windows 2000/XP下的防火墙,http://www.vckbase.com/document/viewdoc/?id=1067